entry : msgId2Timestamp.entrySet()) {
+ if (now - entry.getValue() > timeToLive) {
+ msgId2Timestamp.entrySet().remove(entry);
+ }
+ }
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ t.setDaemon(true);
+ t.start();
+ }
+
+ @Override
+ public boolean isDuplicate(String messageId) {
+ if (messageId == null) {
+ return false;
+ }
+ checkBackgroundProcessStarted();
+ Long timestamp = msgId2Timestamp.putIfAbsent(messageId, System.currentTimeMillis());
+ if (timestamp == null) {
+ // 第一次接收到这个消息
+ return false;
+ }
+ return true;
+ }
+
+
+}
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java
index cc233308..c97facd3 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java
@@ -10,27 +10,49 @@ import java.util.Arrays;
public class SHA1 {
/**
- * 生成SHA1签名
+ * 串接arr参数,生成sha1 digest
+ *
* @param arr
* @return
*/
public static String gen(String... arr) throws NoSuchAlgorithmException {
Arrays.sort(arr);
StringBuilder sb = new StringBuilder();
- for(String a : arr) {
+ for (String a : arr) {
sb.append(a);
}
+ return genStr(sb.toString());
+ }
+
+ /**
+ * 用&串接arr参数,生成sha1 digest
+ *
+ * @param arr
+ * @return
+ */
+ public static String genWithAmple(String... arr) throws NoSuchAlgorithmException {
+ Arrays.sort(arr);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < arr.length; i++) {
+ String a = arr[i];
+ sb.append(a);
+ if (i != arr.length - 1) {
+ sb.append('&');
+ }
+ }
+ return genStr(sb.toString());
+ }
+ public static String genStr(String str) throws NoSuchAlgorithmException {
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
- sha1.update(sb.toString().getBytes());
+ sha1.update(str.getBytes());
byte[] output = sha1.digest();
return bytesToHex(output);
}
-
protected static String bytesToHex(byte[] b) {
- char hexDigit[] = {'0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ char hexDigit[] = { '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
StringBuffer buf = new StringBuffer();
for (int j = 0; j < b.length; j++) {
buf.append(hexDigit[(b[j] >> 4) & 0x0f]);
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/res/StringManager.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/res/StringManager.java
new file mode 100644
index 00000000..4668ec25
--- /dev/null
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/res/StringManager.java
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package me.chanjar.weixin.common.util.res;
+
+import java.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An internationalization / localization helper class which reduces
+ * the bother of handling ResourceBundles and takes care of the
+ * common cases of message formating which otherwise require the
+ * creation of Object arrays and such.
+ *
+ * The StringManager operates on a package basis. One StringManager
+ * per package can be created and accessed via the getManager method
+ * call.
+ *
+ *
The StringManager will look for a ResourceBundle named by
+ * the package name given plus the suffix of "LocalStrings". In
+ * practice, this means that the localized information will be contained
+ * in a LocalStrings.properties file located in the package
+ * directory of the classpath.
+ *
+ *
Please see the documentation for java.util.ResourceBundle for
+ * more information.
+ *
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Mel Martinez [mmartinez@g1440.com]
+ * @see java.util.ResourceBundle
+ */
+public class StringManager {
+
+ private static int LOCALE_CACHE_SIZE = 10;
+
+ /**
+ * The ResourceBundle for this StringManager.
+ */
+ private final ResourceBundle bundle;
+ private final Locale locale;
+
+ /**
+ * Creates a new StringManager for a given package. This is a
+ * private method and all access to it is arbitrated by the
+ * static getManager method call so that only one StringManager
+ * per package will be created.
+ *
+ * @param packageName Name of package to create StringManager for.
+ */
+ private StringManager(String packageName, Locale locale) {
+ String bundleName = packageName + ".LocalStrings";
+ ResourceBundle bnd = null;
+ try {
+ bnd = ResourceBundle.getBundle(bundleName, locale);
+ } catch( MissingResourceException ex ) {
+ // Try from the current loader (that's the case for trusted apps)
+ // Should only be required if using a TC5 style classloader structure
+ // where common != shared != server
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ if( cl != null ) {
+ try {
+ bnd = ResourceBundle.getBundle(bundleName, locale, cl);
+ } catch(MissingResourceException ex2) {
+ // Ignore
+ }
+ }
+ }
+ bundle = bnd;
+ // Get the actual locale, which may be different from the requested one
+ if (bundle != null) {
+ Locale bundleLocale = bundle.getLocale();
+ if (bundleLocale.equals(Locale.ROOT)) {
+ this.locale = Locale.ENGLISH;
+ } else {
+ this.locale = bundleLocale;
+ }
+ } else {
+ this.locale = null;
+ }
+ }
+
+ /**
+ Get a string from the underlying resource bundle or return
+ null if the String is not found.
+
+ @param key to desired resource String
+ @return resource String matching key from underlying
+ bundle or null if not found.
+ @throws IllegalArgumentException if key is null.
+ */
+ public String getString(String key) {
+ if(key == null){
+ String msg = "key may not have a null value";
+
+ throw new IllegalArgumentException(msg);
+ }
+
+ String str = null;
+
+ try {
+ // Avoid NPE if bundle is null and treat it like an MRE
+ if (bundle != null) {
+ str = bundle.getString(key);
+ }
+ } catch(MissingResourceException mre) {
+ //bad: shouldn't mask an exception the following way:
+ // str = "[cannot find message associated with key '" + key +
+ // "' due to " + mre + "]";
+ // because it hides the fact that the String was missing
+ // from the calling code.
+ //good: could just throw the exception (or wrap it in another)
+ // but that would probably cause much havoc on existing
+ // code.
+ //better: consistent with container pattern to
+ // simply return null. Calling code can then do
+ // a null check.
+ str = null;
+ }
+
+ return str;
+ }
+
+ /**
+ * Get a string from the underlying resource bundle and format
+ * it with the given set of arguments.
+ *
+ * @param key
+ * @param args
+ */
+ public String getString(final String key, final Object... args) {
+ String value = getString(key);
+ if (value == null) {
+ value = key;
+ }
+
+ MessageFormat mf = new MessageFormat(value);
+ mf.setLocale(locale);
+ return mf.format(args, new StringBuffer(), null).toString();
+ }
+
+ /**
+ * Identify the Locale this StringManager is associated with
+ */
+ public Locale getLocale() {
+ return locale;
+ }
+
+ // --------------------------------------------------------------
+ // STATIC SUPPORT METHODS
+ // --------------------------------------------------------------
+
+ private static final Map> managers =
+ new Hashtable>();
+
+ /**
+ * Get the StringManager for a particular package. If a manager for
+ * a package already exists, it will be reused, else a new
+ * StringManager will be created and returned.
+ *
+ * @param packageName The package name
+ */
+ public static final synchronized StringManager getManager(
+ String packageName) {
+ return getManager(packageName, Locale.getDefault());
+ }
+
+ /**
+ * Get the StringManager for a particular package and Locale. If a manager
+ * for a package/Locale combination already exists, it will be reused, else
+ * a new StringManager will be created and returned.
+ *
+ * @param packageName The package name
+ * @param locale The Locale
+ */
+ public static final synchronized StringManager getManager(
+ String packageName, Locale locale) {
+
+ Map map = managers.get(packageName);
+ if (map == null) {
+ /*
+ * Don't want the HashMap to be expanded beyond LOCALE_CACHE_SIZE.
+ * Expansion occurs when size() exceeds capacity. Therefore keep
+ * size at or below capacity.
+ * removeEldestEntry() executes after insertion therefore the test
+ * for removal needs to use one less than the maximum desired size
+ *
+ */
+ map = new LinkedHashMap(LOCALE_CACHE_SIZE, 1, true) {
+ private static final long serialVersionUID = 1L;
+ @Override
+ protected boolean removeEldestEntry(
+ Map.Entry eldest) {
+ if (size() > (LOCALE_CACHE_SIZE - 1)) {
+ return true;
+ }
+ return false;
+ }
+ };
+ managers.put(packageName, map);
+ }
+
+ StringManager mgr = map.get(locale);
+ if (mgr == null) {
+ mgr = new StringManager(packageName, locale);
+ map.put(locale, mgr);
+ }
+ return mgr;
+ }
+
+ /**
+ * Retrieve the StringManager for a list of Locales. The first StringManager
+ * found will be returned.
+ *
+ * @param requestedLocales the list of Locales
+ *
+ * @return the found StringManager or the default StringManager
+ */
+ public static StringManager getManager(String packageName,
+ Enumeration requestedLocales) {
+ while (requestedLocales.hasMoreElements()) {
+ Locale locale = requestedLocales.nextElement();
+ StringManager result = getManager(packageName, locale);
+ if (result.getLocale().equals(locale)) {
+ return result;
+ }
+ }
+ // Return the default
+ return getManager(packageName);
+ }
+}
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/AdapterCDATA.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/AdapterCDATA.java
deleted file mode 100644
index c38c2795..00000000
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/AdapterCDATA.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package me.chanjar.weixin.common.util.xml;
-
-import javax.xml.bind.annotation.adapters.XmlAdapter;
-
-/**
- *
- * http://stackoverflow.com/questions/14193944/jaxb-marshalling-unmarshalling-with-cdata
- *
- * @author Daniel Qian
- *
- */
-public class AdapterCDATA extends XmlAdapter {
-
- @Override
- public String marshal(String arg0) throws Exception {
- return "";
- }
-
- @Override
- public String unmarshal(String arg0) throws Exception {
- return arg0;
- }
-
-}
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/MediaIdMarshaller.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/MediaIdMarshaller.java
deleted file mode 100644
index 10e6a3aa..00000000
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/MediaIdMarshaller.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package me.chanjar.weixin.common.util.xml;
-
-import javax.xml.bind.annotation.adapters.XmlAdapter;
-
-/**
- * @author Daniel Qian
- */
-public class MediaIdMarshaller extends XmlAdapter {
-
- @Override
- public String marshal(String arg0) throws Exception {
- return "";
- }
-
- @Override
- public String unmarshal(String arg0) throws Exception {
- // do nothing
- return arg0;
- }
-
-}
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamCDataConverter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamCDataConverter.java
new file mode 100644
index 00000000..aa948b34
--- /dev/null
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamCDataConverter.java
@@ -0,0 +1,12 @@
+package me.chanjar.weixin.common.util.xml;
+
+import com.thoughtworks.xstream.converters.basic.StringConverter;
+
+public class XStreamCDataConverter extends StringConverter {
+
+ @Override
+ public String toString(Object obj) {
+ return "";
+ }
+
+}
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamInitializer.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamInitializer.java
new file mode 100644
index 00000000..d545a796
--- /dev/null
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamInitializer.java
@@ -0,0 +1,50 @@
+package me.chanjar.weixin.common.util.xml;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.converters.basic.DoubleConverter;
+import com.thoughtworks.xstream.converters.basic.FloatConverter;
+import com.thoughtworks.xstream.converters.basic.IntConverter;
+import com.thoughtworks.xstream.core.util.QuickWriter;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
+import com.thoughtworks.xstream.io.xml.XppDriver;
+import com.thoughtworks.xstream.security.NoTypePermission;
+import com.thoughtworks.xstream.security.NullPermission;
+import com.thoughtworks.xstream.security.PrimitiveTypePermission;
+
+import java.io.Writer;
+
+public class XStreamInitializer {
+
+ public static XStream getInstance() {
+ XStream xstream = new XStream(new XppDriver() {
+
+ @Override
+ public HierarchicalStreamWriter createWriter(Writer out) {
+ return new PrettyPrintWriter(out, getNameCoder()) {
+ protected String PREFIX_CDATA = "";
+ protected String PREFIX_MEDIA_ID = "";
+ protected String SUFFIX_MEDIA_ID = "";
+ @Override
+ protected void writeText(QuickWriter writer, String text) {
+ if (text.startsWith(PREFIX_CDATA) && text.endsWith(SUFFIX_CDATA)) {
+ writer.write(text);
+ } else if (text.startsWith(PREFIX_MEDIA_ID) && text.endsWith(SUFFIX_MEDIA_ID)) {
+ writer.write(text);
+ } else {
+ super.writeText(writer, text);
+ }
+
+ }
+ };
+ }
+ });
+ xstream.ignoreUnknownElements();
+ xstream.setMode(XStream.NO_REFERENCES);
+ xstream.addPermission(NullPermission.NULL);
+ xstream.addPermission(PrimitiveTypePermission.PRIMITIVES);
+ return xstream;
+ }
+
+}
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamMediaIdConverter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamMediaIdConverter.java
new file mode 100644
index 00000000..f67a3d9d
--- /dev/null
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/xml/XStreamMediaIdConverter.java
@@ -0,0 +1,8 @@
+package me.chanjar.weixin.common.util.xml;
+
+public class XStreamMediaIdConverter extends XStreamCDataConverter {
+ @Override
+ public String toString(Object obj) {
+ return "" + super.toString(obj) + "";
+ }
+}
diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/session/SessionTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/session/SessionTest.java
new file mode 100644
index 00000000..bcb029b1
--- /dev/null
+++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/session/SessionTest.java
@@ -0,0 +1,132 @@
+package me.chanjar.weixin.common.session;
+
+import org.testng.Assert;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+@Test
+public class SessionTest {
+
+ @DataProvider
+ public Object[][] getSessionManager() {
+
+ return new Object[][] {
+ new Object[] { new StandardSessionManager() }
+ };
+
+ }
+
+
+ @Test(dataProvider = "getSessionManager", expectedExceptions = IllegalStateException.class)
+ public void testInvalidate(WxSessionManager sessionManager) {
+
+ WxSession session = sessionManager.getSession("abc");
+ session.invalidate();
+ session.getAttributeNames();
+
+ }
+
+ @Test(dataProvider = "getSessionManager")
+ public void testInvalidate2(InternalSessionManager sessionManager) {
+
+ Assert.assertEquals(sessionManager.getActiveSessions(), 0);
+ WxSession session = ((WxSessionManager) sessionManager).getSession("abc");
+ Assert.assertEquals(sessionManager.getActiveSessions(), 1);
+ session.invalidate();
+ Assert.assertEquals(sessionManager.getActiveSessions(), 0);
+
+ }
+
+ @Test(dataProvider = "getSessionManager")
+ public void testGetSession(WxSessionManager sessionManager) {
+
+ WxSession session1 = sessionManager.getSession("abc");
+ WxSession session2 = sessionManager.getSession("abc");
+ Assert.assertEquals(session1, session2);
+ Assert.assertTrue(session1 == session2);
+
+ WxSession abc1 = sessionManager.getSession("abc1");
+ Assert.assertNotEquals(session1, abc1);
+
+ WxSession abc1b = sessionManager.getSession("abc1", false);
+ Assert.assertEquals(abc1, abc1b);
+
+ WxSession def = sessionManager.getSession("def", false);
+ Assert.assertNull(def);
+
+ }
+
+ @Test(dataProvider = "getSessionManager")
+ public void testInvalidateAngGet(WxSessionManager sessionManager) {
+
+ WxSession session1 = sessionManager.getSession("abc");
+ session1.invalidate();
+ WxSession session2 = sessionManager.getSession("abc");
+ Assert.assertNotEquals(session1, session2);
+ InternalSessionManager ism = (InternalSessionManager) sessionManager;
+ Assert.assertEquals(ism.getActiveSessions(), 1);
+
+ }
+
+ @Test(dataProvider = "getSessionManager")
+ public void testBackgroundProcess(WxSessionManager sessionManager) throws InterruptedException {
+
+ InternalSessionManager ism = (InternalSessionManager) sessionManager;
+ ism.setMaxInactiveInterval(1);
+ ism.setProcessExpiresFrequency(1);
+ ism.setBackgroundProcessorDelay(1);
+
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+
+ InternalSession abc = ism.createSession("abc");
+ abc.endAccess();
+
+ Thread.sleep(2000l);
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+
+ }
+
+ @Test(dataProvider = "getSessionManager")
+ public void testBackgroundProcess2(WxSessionManager sessionManager) throws InterruptedException {
+
+ InternalSessionManager ism = (InternalSessionManager) sessionManager;
+ ism.setMaxInactiveInterval(100);
+ ism.setProcessExpiresFrequency(1);
+ ism.setBackgroundProcessorDelay(1);
+
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+
+ InternalSession abc = ism.createSession("abc");
+ abc.setMaxInactiveInterval(1);
+ abc.endAccess();
+
+ Thread.sleep(2000l);
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+
+ }
+
+ @Test(dataProvider = "getSessionManager")
+ public void testMaxActive(WxSessionManager sessionManager) throws InterruptedException {
+
+ InternalSessionManager ism = (InternalSessionManager) sessionManager;
+ ism.setMaxActiveSessions(2);
+
+ ism.createSession("abc");
+ ism.createSession("abc");
+ ism.createSession("def");
+
+ }
+
+ @Test(dataProvider = "getSessionManager", expectedExceptions = TooManyActiveSessionsException.class)
+ public void testMaxActive2(WxSessionManager sessionManager) throws InterruptedException {
+
+ InternalSessionManager ism = (InternalSessionManager) sessionManager;
+ ism.setMaxActiveSessions(2);
+
+ ism.createSession("abc");
+ ism.createSession("abc");
+ ism.createSession("def");
+ ism.createSession("xyz");
+
+ }
+}
diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/WxMessageInMemoryDuplicateCheckerTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/WxMessageInMemoryDuplicateCheckerTest.java
new file mode 100644
index 00000000..28642c44
--- /dev/null
+++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/WxMessageInMemoryDuplicateCheckerTest.java
@@ -0,0 +1,35 @@
+package me.chanjar.weixin.common.util;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+@Test
+public class WxMessageInMemoryDuplicateCheckerTest {
+
+ public void test() throws InterruptedException {
+ Long[] msgIds = new Long[] { 1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l };
+ WxMessageInMemoryDuplicateChecker checker = new WxMessageInMemoryDuplicateChecker(2000l, 1000l);
+
+ // 第一次检查
+ for (Long msgId : msgIds) {
+ boolean result = checker.isDuplicate(String.valueOf(msgId));
+ Assert.assertFalse(result);
+ }
+
+ // 过1秒再检查
+ Thread.sleep(1000l);
+ for (Long msgId : msgIds) {
+ boolean result = checker.isDuplicate(String.valueOf(msgId));
+ Assert.assertTrue(result);
+ }
+
+ // 过1.5秒再检查
+ Thread.sleep(1500l);
+ for (Long msgId : msgIds) {
+ boolean result = checker.isDuplicate(String.valueOf(msgId));
+ Assert.assertFalse(result);
+ }
+
+ }
+
+}
diff --git a/weixin-java-common/src/test/resources/logback-test.xml b/weixin-java-common/src/test/resources/logback-test.xml
new file mode 100644
index 00000000..c0d6fc3c
--- /dev/null
+++ b/weixin-java-common/src/test/resources/logback-test.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
diff --git a/weixin-java-common/src/test/resources/testng.xml b/weixin-java-common/src/test/resources/testng.xml
index f0058b53..f2222275 100644
--- a/weixin-java-common/src/test/resources/testng.xml
+++ b/weixin-java-common/src/test/resources/testng.xml
@@ -1,12 +1,14 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml
index 2ec3e36a..50d8ebe4 100644
--- a/weixin-java-cp/pom.xml
+++ b/weixin-java-cp/pom.xml
@@ -6,7 +6,7 @@
me.chanjar
weixin-java-parent
- 1.0.6
+ 1.1.0-SNAPSHOT
weixin-java-cp
diff --git a/weixin-java-cp/pom.xml.versionsBackup b/weixin-java-cp/pom.xml.versionsBackup
deleted file mode 100644
index fe491f91..00000000
--- a/weixin-java-cp/pom.xml.versionsBackup
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
- 4.0.0
-
- me.chanjar
- weixin-java-parent
- 1.0.5-SNAPSHOT
-
-
- weixin-java-cp
- WeiXin Java Tools - CP
- 微信企业号Java SDK
-
-
-
- me.chanjar
- weixin-java-common
- ${project.version}
-
-
- junit
- junit
- test
-
-
- org.testng
- testng
- 6.8.7
- test
-
-
- org.mockito
- mockito-all
- 1.9.5
- test
-
-
- com.google.inject
- guice
- 3.0
- test
-
-
- org.eclipse.jetty
- jetty-server
- 9.3.0.M0
- test
-
-
- org.eclipse.jetty
- jetty-servlet
- 9.3.0.M0
- test
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
-
- src/test/resources/testng.xml
-
-
-
-
-
-
-
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java
index 36c9cfba..44123a36 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java
@@ -9,12 +9,20 @@ import me.chanjar.weixin.common.bean.WxAccessToken;
*/
public interface WxCpConfigStorage {
+ public String getAccessToken();
+
+ public boolean isAccessTokenExpired();
+
+ /**
+ * 强制将access token过期掉
+ */
+ public void expireAccessToken();
+
public void updateAccessToken(WxAccessToken accessToken);
-
+
public void updateAccessToken(String accessToken, int expiresIn);
-
- public String getAccessToken();
-
+
+
public String getCorpId();
public String getCorpSecret();
@@ -25,7 +33,7 @@ public interface WxCpConfigStorage {
public String getAesKey();
- public int getExpiresIn();
+ public long getExpiresTime();
public String getOauth2redirectUri();
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java
index 9ff1f187..0a35fcd9 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java
@@ -16,7 +16,7 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
protected String accessToken;
protected String aesKey;
protected String agentId;
- protected int expiresIn;
+ protected long expiresTime;
protected String oauth2redirectUri;
@@ -25,17 +25,25 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
protected String http_proxy_username;
protected String http_proxy_password;
- public void updateAccessToken(WxAccessToken accessToken) {
+ public String getAccessToken() {
+ return this.accessToken;
+ }
+
+ public boolean isAccessTokenExpired() {
+ return System.currentTimeMillis() > this.expiresTime;
+ }
+
+ public void expireAccessToken() {
+ this.expiresTime = 0;
+ }
+
+ public synchronized void updateAccessToken(WxAccessToken accessToken) {
updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
}
- public void updateAccessToken(String accessToken, int expiresIn) {
+ public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
this.accessToken = accessToken;
- this.expiresIn = expiresIn;
- }
-
- public String getAccessToken() {
- return this.accessToken;
+ this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l;
}
public String getCorpId() {
@@ -50,8 +58,8 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
return this.token;
}
- public int getExpiresIn() {
- return this.expiresIn;
+ public long getExpiresTime() {
+ return this.expiresTime;
}
public void setCorpId(String corpId) {
@@ -78,8 +86,8 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
this.accessToken = accessToken;
}
- public void setExpiresIn(int expiresIn) {
- this.expiresIn = expiresIn;
+ public void setExpiresTime(long expiresTime) {
+ this.expiresTime = expiresTime;
}
public String getAgentId() {
@@ -140,7 +148,7 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
", accessToken='" + accessToken + '\'' +
", aesKey='" + aesKey + '\'' +
", agentId='" + agentId + '\'' +
- ", expiresIn=" + expiresIn +
+ ", expiresTime=" + expiresTime +
", http_proxy_host='" + http_proxy_host + '\'' +
", http_proxy_port=" + http_proxy_port +
", http_proxy_username='" + http_proxy_username + '\'' +
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageHandler.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageHandler.java
index 861c610d..f9a3c289 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageHandler.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageHandler.java
@@ -1,24 +1,29 @@
package me.chanjar.weixin.cp.api;
-import java.util.Map;
-
+import me.chanjar.weixin.common.session.WxSession;
+import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
+import java.util.Map;
+
/**
* 处理微信推送消息的处理器接口
- * @author Daniel Qian
*
+ * @author Daniel Qian
*/
public interface WxCpMessageHandler {
/**
- *
* @param wxMessage
- * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个
+ * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个
* @param wxCpService
+ * @param sessionManager
* @return xml格式的消息,如果在异步规则里处理的话,可以返回null
*/
- public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService wxCpService);
-
+ public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage,
+ Map context,
+ WxCpService wxCpService,
+ WxSessionManager sessionManager);
+
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageInterceptor.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageInterceptor.java
index c1e7ee9d..92fa41f1 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageInterceptor.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageInterceptor.java
@@ -1,23 +1,29 @@
package me.chanjar.weixin.cp.api;
-import java.util.Map;
-
+import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
+import java.util.Map;
+
/**
* 微信消息拦截器,可以用来做验证
- * @author Daniel Qian
*
+ * @author Daniel Qian
*/
public interface WxCpMessageInterceptor {
/**
* 拦截微信消息
+ *
* @param wxMessage
- * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个
+ * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个
* @param wxCpService
- * @return true代表OK,false代表不OK
+ * @param sessionManager
+ * @return true代表OK,false代表不OK
*/
- public boolean intercept(WxCpXmlMessage wxMessage, Map context, WxCpService wxCpService);
-
+ public boolean intercept(WxCpXmlMessage wxMessage,
+ Map context,
+ WxCpService wxCpService,
+ WxSessionManager sessionManager);
+
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java
index bf5bf86e..34b36ec3 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java
@@ -1,16 +1,23 @@
package me.chanjar.weixin.cp.api;
+import me.chanjar.weixin.common.session.*;
+import me.chanjar.weixin.common.util.WxMessageDuplicateChecker;
+import me.chanjar.weixin.common.util.WxMessageInMemoryDuplicateChecker;
+import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
+import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.regex.Pattern;
-import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
-import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
-
/**
*
* 微信消息路由器,通过代码化的配置,把来自微信的消息交给handler处理
@@ -41,22 +48,58 @@ import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
*/
public class WxCpMessageRouter {
- private static final int DEFAULT_THREAD_POOL_SIZE = 20;
+ protected final Logger log = LoggerFactory.getLogger(WxCpMessageRouter.class);
- private final List rules = new ArrayList();
+ private static final int DEFAULT_THREAD_POOL_SIZE = 100;
- private final ExecutorService executorService;
+ private final List rules = new ArrayList();
private final WxCpService wxCpService;
+ private ExecutorService executorService;
+
+ private WxMessageDuplicateChecker messageDuplicateChecker;
+
+ private WxSessionManager sessionManager;
+
public WxCpMessageRouter(WxCpService wxCpService) {
this.wxCpService = wxCpService;
this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE);
+ this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker();
+ this.sessionManager = new StandardSessionManager();
}
- public WxCpMessageRouter(WxCpService wxMpService, int threadPoolSize) {
- this.wxCpService = wxMpService;
- this.executorService = Executors.newFixedThreadPool(threadPoolSize);
+ /**
+ *
+ * 设置自定义的 {@link ExecutorService}
+ * 如果不调用该方法,默认使用 Executors.newFixedThreadPool(100)
+ *
+ * @param executorService
+ */
+ public void setExecutorService(ExecutorService executorService) {
+ this.executorService = executorService;
+ }
+
+ /**
+ *
+ * 设置自定义的 {@link me.chanjar.weixin.common.util.WxMessageDuplicateChecker}
+ * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.WxMessageInMemoryDuplicateChecker}
+ *
+ * @param messageDuplicateChecker
+ */
+ public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicateChecker) {
+ this.messageDuplicateChecker = messageDuplicateChecker;
+ }
+
+ /**
+ *
+ * 设置自定义的{@link me.chanjar.weixin.common.session.WxSessionManager}
+ * 如果不调用该方法,默认使用 {@linke SessionManagerImpl}
+ *
+ * @param sessionManager
+ */
+ public void setSessionManager(WxSessionManager sessionManager) {
+ this.sessionManager = sessionManager;
}
/**
@@ -64,7 +107,7 @@ public class WxCpMessageRouter {
* @return
*/
public Rule rule() {
- return new Rule(this, wxCpService);
+ return new Rule(this, wxCpService, sessionManager);
}
/**
@@ -72,6 +115,11 @@ public class WxCpMessageRouter {
* @param wxMessage
*/
public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) {
+ if (isDuplicateMessage(wxMessage)) {
+ // 如果是重复消息,那么就不做处理
+ return null;
+ }
+
final List matchRules = new ArrayList();
// 收集匹配的规则
for (final Rule rule : rules) {
@@ -88,27 +136,84 @@ public class WxCpMessageRouter {
}
WxCpXmlOutMessage res = null;
+ final List futures = new ArrayList();
for (final Rule rule : matchRules) {
// 返回最后一个非异步的rule的执行结果
if(rule.async) {
- executorService.submit(new Runnable() {
- public void run() {
- rule.service(wxMessage);
- }
- });
+ futures.add(
+ executorService.submit(new Runnable() {
+ public void run() {
+ rule.service(wxMessage);
+ }
+ })
+ );
} else {
res = rule.service(wxMessage);
+ // 在同步操作结束,session访问结束
+ log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUserName());
+ sessionEndAccess(wxMessage);
}
}
+
+ if (futures.size() > 0) {
+ executorService.submit(new Runnable() {
+ @Override
+ public void run() {
+ for (Future future : futures) {
+ try {
+ future.get();
+ log.debug("End session access: async=true, sessionId={}", wxMessage.getFromUserName());
+ // 异步操作结束,session访问结束
+ sessionEndAccess(wxMessage);
+ } catch (InterruptedException e) {
+ log.error("Error happened when wait task finish", e);
+ } catch (ExecutionException e) {
+ log.error("Error happened when wait task finish", e);
+ }
+ }
+ }
+ });
+ }
return res;
}
+ protected boolean isDuplicateMessage(WxCpXmlMessage wxMessage) {
+
+ String messageId = "";
+ if (wxMessage.getMsgId() == null) {
+ messageId = wxMessage.getFromUserName() + "-" + String.valueOf(wxMessage.getCreateTime());
+ } else {
+ messageId = String.valueOf(wxMessage.getMsgId());
+ }
+
+ if (messageDuplicateChecker.isDuplicate(messageId)) {
+ return true;
+ }
+ return false;
+
+ }
+
+ /**
+ * 对session的访问结束
+ * @param wxMessage
+ */
+ protected void sessionEndAccess(WxCpXmlMessage wxMessage) {
+
+ InternalSession session = ((InternalSessionManager)sessionManager).findSession(wxMessage.getFromUserName());
+ if (session != null) {
+ session.endAccess();
+ }
+
+ }
+
public static class Rule {
private final WxCpMessageRouter routerBuilder;
private final WxCpService wxCpService;
+ private final WxSessionManager sessionManager;
+
private boolean async = true;
private String fromUser;
@@ -131,9 +236,10 @@ public class WxCpMessageRouter {
private List interceptors = new ArrayList();
- protected Rule(WxCpMessageRouter routerBuilder, WxCpService wxCpService) {
+ protected Rule(WxCpMessageRouter routerBuilder, WxCpService wxCpService, WxSessionManager sessionManager) {
this.routerBuilder = routerBuilder;
this.wxCpService = wxCpService;
+ this.sessionManager = sessionManager;
}
/**
@@ -311,7 +417,7 @@ public class WxCpMessageRouter {
Map context = new HashMap();
// 如果拦截器不通过
for (WxCpMessageInterceptor interceptor : this.interceptors) {
- if (!interceptor.intercept(wxMessage, context, wxCpService)) {
+ if (!interceptor.intercept(wxMessage, context, wxCpService, sessionManager)) {
return null;
}
}
@@ -320,7 +426,7 @@ public class WxCpMessageRouter {
WxCpXmlOutMessage res = null;
for (WxCpMessageHandler handler : this.handlers) {
// 返回最后handler的结果
- res = handler.handle(wxMessage, context, wxCpService);
+ res = handler.handle(wxMessage, context, wxCpService, sessionManager);
}
return res;
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
index cc520f90..21669b19 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
@@ -1,17 +1,20 @@
package me.chanjar.weixin.cp.api;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-
import me.chanjar.weixin.common.bean.WxMenu;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.common.session.WxSession;
+import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.common.util.http.RequestExecutor;
-import me.chanjar.weixin.cp.bean.*;
import me.chanjar.weixin.cp.bean.WxCpDepart;
+import me.chanjar.weixin.cp.bean.WxCpMessage;
+import me.chanjar.weixin.cp.bean.WxCpTag;
import me.chanjar.weixin.cp.bean.WxCpUser;
-import me.chanjar.weixin.common.exception.WxErrorException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
/**
* 微信API的Service
@@ -42,6 +45,14 @@ public interface WxCpService {
*/
public void userAuthenticated(String userId) throws WxErrorException;
+ /**
+ * 获取access_token, 不强制刷新access_token
+ * @see #getAccessToken(boolean)
+ * @return
+ * @throws WxErrorException
+ */
+ public String getAccessToken() throws WxErrorException;
+
/**
*
* 获取access_token,本方法线程安全
@@ -50,10 +61,11 @@ public interface WxCpService {
* 程序员在非必要情况下尽量不要主动调用此方法
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token
*
- *
+ * @param forceRefresh 强制刷新
+ * @return
* @throws me.chanjar.weixin.common.exception.WxErrorException
*/
- public void accessTokenRefresh() throws WxErrorException;
+ public String getAccessToken(boolean forceRefresh) throws WxErrorException;
/**
*
@@ -343,4 +355,46 @@ public interface WxCpService {
* @param wxConfigProvider
*/
public void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider);
+
+ /**
+ *
+ * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试
+ * 默认:1000ms
+ *
+ * @param retrySleepMillis
+ */
+ void setRetrySleepMillis(int retrySleepMillis);
+
+ /**
+ *
+ * 设置当微信系统响应系统繁忙时,最大重试次数
+ * 默认:5次
+ *
+ * @param maxRetryTimes
+ */
+ void setMaxRetryTimes(int maxRetryTimes);
+
+ /**
+ * 获取某个sessionId对应的session,如果sessionId没有对应的session,则新建一个并返回。
+ * @param id id可以为任意字符串,建议使用FromUserName作为id
+ * @return
+ */
+ WxSession getSession(String id);
+
+ /**
+ * 获取某个sessionId对应的session,如果sessionId没有对应的session,若create为true则新建一个,否则返回null。
+ * @param id id可以为任意字符串,建议使用FromUserName作为id
+ * @param create
+ * @return
+ */
+ WxSession getSession(String id, boolean create);
+
+ /**
+ *
+ * 设置WxSessionManager,只有当需要使用个性化的WxSessionManager的时候才需要调用此方法,
+ * WxCpService默认使用的是{@link me.chanjar.weixin.common.session.StandardSessionManager}
+ *
+ * @param sessionManager
+ */
+ void setSessionManager(WxSessionManager sessionManager);
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
index 2e0738c7..f0da30b5 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
@@ -12,6 +12,9 @@ import me.chanjar.weixin.common.bean.WxMenu;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.common.session.StandardSessionManager;
+import me.chanjar.weixin.common.session.WxSession;
+import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.common.util.StringUtils;
import me.chanjar.weixin.common.util.crypto.SHA1;
import me.chanjar.weixin.common.util.fs.FileUtils;
@@ -34,6 +37,8 @@ import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
@@ -41,25 +46,28 @@ import java.io.InputStream;
import java.io.StringReader;
import java.util.List;
import java.util.UUID;
-import java.util.concurrent.atomic.AtomicBoolean;
public class WxCpServiceImpl implements WxCpService {
+ protected final Logger log = LoggerFactory.getLogger(WxCpServiceImpl.class);
+
/**
- * 全局的是否正在刷新Access Token的flag
- * true: 正在刷新
- * false: 没有刷新
+ * 全局的是否正在刷新access token的锁
*/
- protected static final AtomicBoolean GLOBAL_ACCESS_TOKEN_REFRESH_FLAG = new AtomicBoolean(false);
+ protected static final Object GLOBAL_ACCESS_TOKEN_REFRESH_LOCK = new Object();
protected WxCpConfigStorage wxCpConfigStorage;
- protected final ThreadLocal retryTimes = new ThreadLocal();
-
protected CloseableHttpClient httpClient;
protected HttpHost httpProxy;
+ private int retrySleepMillis = 1000;
+
+ private int maxRetryTimes = 5;
+
+ protected WxSessionManager sessionManager = new StandardSessionManager();
+
public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) {
try {
return SHA1.gen(wxCpConfigStorage.getToken(), timestamp, nonce, data).equals(msgSignature);
@@ -73,45 +81,44 @@ public class WxCpServiceImpl implements WxCpService {
execute(new SimpleGetRequestExecutor(), url, null);
}
- public void accessTokenRefresh() throws WxErrorException {
- if (!GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.getAndSet(true)) {
- try {
- String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?"
- + "&corpid=" + wxCpConfigStorage.getCorpId()
- + "&corpsecret=" + wxCpConfigStorage.getCorpSecret();
- try {
- HttpGet httpGet = new HttpGet(url);
- if (httpProxy != null) {
- RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
- httpGet.setConfig(config);
- }
- CloseableHttpClient httpclient = getHttpclient();
- CloseableHttpResponse response = httpclient.execute(httpGet);
- String resultContent = new BasicResponseHandler().handleResponse(response);
- WxError error = WxError.fromJson(resultContent);
- if (error.getErrorCode() != 0) {
- throw new WxErrorException(error);
+ public String getAccessToken() throws WxErrorException {
+ return getAccessToken(false);
+ }
+
+ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
+ if (forceRefresh) {
+ wxCpConfigStorage.expireAccessToken();
+ }
+ if (wxCpConfigStorage.isAccessTokenExpired()) {
+ synchronized (GLOBAL_ACCESS_TOKEN_REFRESH_LOCK) {
+ if (wxCpConfigStorage.isAccessTokenExpired()) {
+ String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?"
+ + "&corpid=" + wxCpConfigStorage.getCorpId()
+ + "&corpsecret=" + wxCpConfigStorage.getCorpSecret();
+ try {
+ HttpGet httpGet = new HttpGet(url);
+ if (httpProxy != null) {
+ RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
+ httpGet.setConfig(config);
+ }
+ CloseableHttpClient httpclient = getHttpclient();
+ CloseableHttpResponse response = httpclient.execute(httpGet);
+ String resultContent = new BasicResponseHandler().handleResponse(response);
+ WxError error = WxError.fromJson(resultContent);
+ if (error.getErrorCode() != 0) {
+ throw new WxErrorException(error);
+ }
+ WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
+ wxCpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
+ } catch (ClientProtocolException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
}
- WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
- wxCpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
- } catch (ClientProtocolException e) {
- throw new RuntimeException(e);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- } finally {
- GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.set(false);
- }
- } else {
- // 每隔100ms检查一下是否刷新完毕了
- while (GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.get()) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
}
}
- // 刷新完毕了,就没他什么事儿了
}
+ return wxCpConfigStorage.getAccessToken();
}
public void messageSend(WxCpMessage message) throws WxErrorException {
@@ -369,10 +376,34 @@ public class WxCpServiceImpl implements WxCpService {
* @throws WxErrorException
*/
public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException {
- if (StringUtils.isBlank(wxCpConfigStorage.getAccessToken())) {
- accessTokenRefresh();
- }
- String accessToken = wxCpConfigStorage.getAccessToken();
+ int retryTimes = 0;
+ do {
+ try {
+ return executeInternal(executor, uri, data);
+ } catch (WxErrorException e) {
+ WxError error = e.getError();
+ /**
+ * -1 系统繁忙, 1000ms后重试
+ */
+ if (error.getErrorCode() == -1) {
+ int sleepMillis = retrySleepMillis * (1 << retryTimes);
+ try {
+ log.debug("微信系统繁忙,{}ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
+ Thread.sleep(sleepMillis);
+ } catch (InterruptedException e1) {
+ throw new RuntimeException(e1);
+ }
+ } else {
+ throw e;
+ }
+ }
+ } while(++retryTimes < maxRetryTimes);
+
+ throw new RuntimeException("微信服务端异常,超出重试次数");
+ }
+
+ protected T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException {
+ String accessToken = getAccessToken(false);
String uriWithAccessToken = uri;
uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken;
@@ -387,30 +418,10 @@ public class WxCpServiceImpl implements WxCpService {
* 42001 access_token超时
*/
if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) {
- accessTokenRefresh();
+ // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token
+ wxCpConfigStorage.expireAccessToken();
return execute(executor, uri, data);
}
- /**
- * -1 系统繁忙, 1000ms后重试
- */
- if (error.getErrorCode() == -1) {
- if (retryTimes.get() == null) {
- retryTimes.set(0);
- }
- if (retryTimes.get() > 4) {
- retryTimes.set(0);
- throw new RuntimeException("微信服务端异常,超出重试次数");
- }
- int sleepMillis = 1000 * (1 << retryTimes.get());
- try {
- System.out.println("微信系统繁忙," + sleepMillis + "ms后重试");
- Thread.sleep(sleepMillis);
- retryTimes.set(retryTimes.get() + 1);
- return execute(executor, uri, data);
- } catch (InterruptedException e1) {
- throw new RuntimeException(e1);
- }
- }
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
@@ -421,7 +432,6 @@ public class WxCpServiceImpl implements WxCpService {
throw new RuntimeException(e);
}
}
-
protected CloseableHttpClient getHttpclient() {
return httpClient;
}
@@ -456,4 +466,37 @@ public class WxCpServiceImpl implements WxCpService {
}
}
+ @Override
+ public void setRetrySleepMillis(int retrySleepMillis) {
+ this.retrySleepMillis = retrySleepMillis;
+ }
+
+
+ @Override
+ public void setMaxRetryTimes(int maxRetryTimes) {
+ this.maxRetryTimes = maxRetryTimes;
+ }
+
+ @Override
+ public WxSession getSession(String id) {
+ if (sessionManager == null) {
+ return null;
+ }
+ return sessionManager.getSession(id);
+ }
+
+ @Override
+ public WxSession getSession(String id, boolean create) {
+ if (sessionManager == null) {
+ return null;
+ }
+ return sessionManager.getSession(id, create);
+ }
+
+
+ @Override
+ public void setSessionManager(WxSessionManager sessionManager) {
+ this.sessionManager = sessionManager;
+ }
+
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java
index 40f5c840..0f5c4f82 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java
@@ -1,11 +1,11 @@
package me.chanjar.weixin.cp.bean;
-import java.util.ArrayList;
-import java.util.List;
-
import me.chanjar.weixin.cp.bean.messagebuilder.*;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* 消息
* @author Daniel Qian
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java
index 90125a5d..2e8547d2 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpUser.java
@@ -21,6 +21,8 @@ public class WxCpUser {
private String tel;
private String email;
private String weiXinId;
+ private String avatar;
+ private Integer status;
private final List extAttrs = new ArrayList();
public String getUserId() {
@@ -95,6 +97,22 @@ public class WxCpUser {
this.weiXinId = weiXinId;
}
+ public String getAvatar() {
+ return avatar;
+ }
+
+ public void setAvatar(String avatar) {
+ this.avatar = avatar;
+ }
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
+
public void addExtAttr(String name, String value) {
this.extAttrs.add(new Attr(name, value));
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java
index 467c6a1f..db86e6f3 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java
@@ -1,14 +1,13 @@
package me.chanjar.weixin.cp.bean;
-import me.chanjar.weixin.common.util.xml.AdapterCDATA;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
+import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
import me.chanjar.weixin.cp.api.WxCpConfigStorage;
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil;
-import me.chanjar.weixin.cp.util.xml.XmlTransformer;
+import me.chanjar.weixin.cp.util.xml.XStreamTransformer;
import org.apache.commons.io.IOUtils;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.annotation.*;
-import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
@@ -25,103 +24,102 @@ import java.util.List;
*
* @author Daniel Qian
*/
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
+@XStreamAlias("xml")
public class WxCpXmlMessage {
///////////////////////
// 以下都是微信推送过来的消息的xml的element所对应的属性
///////////////////////
- @XmlElement(name="AgentID")
+ @XStreamAlias("AgentID")
private Integer agentId;
- @XmlElement(name = "ToUserName")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("ToUserName")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String toUserName;
- @XmlElement(name = "FromUserName")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("FromUserName")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String fromUserName;
- @XmlElement(name = "CreateTime")
+ @XStreamAlias("CreateTime")
private Long createTime;
- @XmlElement(name = "MsgType")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("MsgType")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String msgType;
- @XmlElement(name = "Content")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Content")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String content;
- @XmlElement(name = "MsgId")
+ @XStreamAlias("MsgId")
private Long msgId;
- @XmlElement(name = "PicUrl")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("PicUrl")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String picUrl;
- @XmlElement(name = "MediaId")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("MediaId")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String mediaId;
- @XmlElement(name = "Format")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Format")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String format;
- @XmlElement(name = "ThumbMediaId")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("ThumbMediaId")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String thumbMediaId;
- @XmlElement(name = "Location_X")
+ @XStreamAlias("Location_X")
private Double locationX;
- @XmlElement(name = "Location_Y")
+ @XStreamAlias("Location_Y")
private Double locationY;
- @XmlElement(name = "Scale")
+ @XStreamAlias("Scale")
private Double scale;
- @XmlElement(name = "Label")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Label")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String label;
- @XmlElement(name = "Title")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Title")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String title;
- @XmlElement(name = "Description")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Description")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String description;
- @XmlElement(name = "Url")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Url")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String url;
- @XmlElement(name = "Event")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Event")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String event;
- @XmlElement(name = "EventKey")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("EventKey")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String eventKey;
- @XmlElement(name = "Ticket")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Ticket")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String ticket;
- @XmlElement(name = "Latitude")
+ @XStreamAlias("Latitude")
private Double latitude;
- @XmlElement(name = "Longitude")
+ @XStreamAlias("Longitude")
private Double longitude;
- @XmlElement(name = "Precision")
+ @XStreamAlias("Precision")
private Double precision;
- @XmlElement(name = "Recognition")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Recognition")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String recognition;
///////////////////////////////////////
@@ -130,37 +128,37 @@ public class WxCpXmlMessage {
/**
* 群发的结果
*/
- @XmlElement(name = "Status")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Status")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String status;
/**
* group_id下粉丝数;或者openid_list中的粉丝数
*/
- @XmlElement(name = "TotalCount")
+ @XStreamAlias("TotalCount")
private Integer totalCount;
/**
* 过滤(过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数,原则上,filterCount = sentCount + errorCount
*/
- @XmlElement(name = "FilterCount")
+ @XStreamAlias("FilterCount")
private Integer filterCount;
/**
* 发送成功的粉丝数
*/
- @XmlElement(name = "SentCount")
+ @XStreamAlias("SentCount")
private Integer sentCount;
/**
* 发送失败的粉丝数
*/
- @XmlElement(name = "ErrorCount")
+ @XStreamAlias("ErrorCount")
private Integer errorCount;
- @XmlElement(name = "ScanCodeInfo")
+ @XStreamAlias("ScanCodeInfo")
private ScanCodeInfo scanCodeInfo = new ScanCodeInfo();
- @XmlElement(name = "SendPicsInfo")
+ @XStreamAlias("SendPicsInfo")
private SendPicsInfo sendPicsInfo = new SendPicsInfo();
- @XmlElement(name = "SendLocationInfo")
+ @XStreamAlias("SendLocationInfo")
private SendLocationInfo sendLocationInfo = new SendLocationInfo();
public Integer getAgentId() {
@@ -390,19 +388,11 @@ public class WxCpXmlMessage {
}
protected static WxCpXmlMessage fromXml(String xml) {
- try {
- return XmlTransformer.fromXml(WxCpXmlMessage.class, xml);
- } catch (JAXBException e) {
- throw new RuntimeException(e);
- }
+ return XStreamTransformer.fromXml(WxCpXmlMessage.class, xml);
}
protected static WxCpXmlMessage fromXml(InputStream is) {
- try {
- return XmlTransformer.fromXml(WxCpXmlMessage.class, is);
- } catch (JAXBException e) {
- throw new RuntimeException(e);
- }
+ return XStreamTransformer.fromXml(WxCpXmlMessage.class, is);
}
/**
@@ -499,16 +489,15 @@ public class WxCpXmlMessage {
this.sendLocationInfo = sendLocationInfo;
}
- @XmlRootElement(name = "ScanCodeInfo")
- @XmlAccessorType(XmlAccessType.FIELD)
+ @XStreamAlias("ScanCodeInfo")
public static class ScanCodeInfo {
- @XmlElement(name = "ScanType")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("ScanType")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String scanType;
- @XmlElement(name = "ScanResult")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("ScanResult")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String scanResult;
/**
@@ -538,15 +527,13 @@ public class WxCpXmlMessage {
}
- @XmlRootElement(name = "SendPicsInfo")
- @XmlAccessorType(XmlAccessType.FIELD)
+ @XStreamAlias("SendPicsInfo")
public static class SendPicsInfo {
- @XmlElement(name = "Count")
+ @XStreamAlias("Count")
private Long count;
- @XmlElementWrapper(name="PicList")
- @XmlElement(name = "item")
+ @XStreamAlias("PicList")
protected final List- picList = new ArrayList
- ();
public Long getCount() {
@@ -561,13 +548,11 @@ public class WxCpXmlMessage {
return picList;
}
- @XmlRootElement(name = "item")
- @XmlAccessorType(XmlAccessType.FIELD)
- @XmlType(name = "WxXmlMessage.SendPicsInfo.Item")
+ @XStreamAlias("item")
public static class Item {
- @XmlElement(name = "PicMd5Sum")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("PicMd5Sum")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String PicMd5Sum;
public String getPicMd5Sum() {
@@ -580,28 +565,27 @@ public class WxCpXmlMessage {
}
}
- @XmlRootElement(name = "SendLocationInfo")
- @XmlAccessorType(XmlAccessType.FIELD)
+ @XStreamAlias("SendLocationInfo")
public static class SendLocationInfo {
- @XmlElement(name = "Location_X")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Location_X")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String locationX;
- @XmlElement(name = "Location_Y")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Location_Y")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String locationY;
- @XmlElement(name = "Scale")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Scale")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String scale;
- @XmlElement(name = "Label")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Label")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String label;
- @XmlElement(name = "Poiname")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Poiname")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String poiname;
public String getLocationX() {
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessage.java
index 06ba88c3..9706a5aa 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutImageMessage.java
@@ -1,20 +1,15 @@
package me.chanjar.weixin.cp.bean;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.util.xml.MediaIdMarshaller;
+import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter;
-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 javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
+@XStreamAlias("xml")
public class WxCpXmlOutImageMessage extends WxCpXmlOutMessage {
- @XmlElement(name="Image")
- @XmlJavaTypeAdapter(MediaIdMarshaller.class)
+ @XStreamAlias("Image")
+ @XStreamConverter(value=XStreamMediaIdConverter.class)
private String mediaId;
public WxCpXmlOutImageMessage() {
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMessage.java
index 1ccacb00..641a572d 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutMessage.java
@@ -1,35 +1,29 @@
package me.chanjar.weixin.cp.bean;
-import me.chanjar.weixin.common.util.xml.AdapterCDATA;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
+import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
import me.chanjar.weixin.cp.api.WxCpConfigStorage;
import me.chanjar.weixin.cp.bean.outxmlbuilder.*;
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil;
-import me.chanjar.weixin.cp.util.xml.XmlTransformer;
+import me.chanjar.weixin.cp.util.xml.XStreamTransformer;
-import javax.xml.bind.JAXBException;
-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 javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+@XStreamAlias("xml")
+public abstract class WxCpXmlOutMessage {
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
-public class WxCpXmlOutMessage {
-
- @XmlElement(name="ToUserName")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("ToUserName")
+ @XStreamConverter(value=XStreamCDataConverter.class)
protected String toUserName;
- @XmlElement(name="FromUserName")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("FromUserName")
+ @XStreamConverter(value=XStreamCDataConverter.class)
protected String fromUserName;
- @XmlElement(name="CreateTime")
+ @XStreamAlias("CreateTime")
protected Long createTime;
- @XmlElement(name="MsgType")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("MsgType")
+ @XStreamConverter(value=XStreamCDataConverter.class)
protected String msgType;
public String getToUserName() {
@@ -65,11 +59,7 @@ public class WxCpXmlOutMessage {
}
protected String toXml() {
- try {
- return XmlTransformer.toXml((Class)this.getClass(), this);
- } catch (JAXBException e) {
- throw new RuntimeException(e);
- }
+ return XStreamTransformer.toXml((Class)this.getClass(), this);
}
/**
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessage.java
index 76f28e2e..79c35ba9 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutNewsMessage.java
@@ -1,22 +1,20 @@
package me.chanjar.weixin.cp.bean;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.util.xml.AdapterCDATA;
+import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
-import javax.xml.bind.annotation.*;
-import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.ArrayList;
import java.util.List;
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
+@XStreamAlias("xml")
public class WxCpXmlOutNewsMessage extends WxCpXmlOutMessage {
- @XmlElement(name = "ArticleCount")
+ @XStreamAlias("ArticleCount")
protected int articleCount;
- @XmlElementWrapper(name="Articles")
- @XmlElement(name = "item")
+ @XStreamAlias("Articles")
protected final List
- articles = new ArrayList
- ();
public WxCpXmlOutNewsMessage() {
@@ -37,24 +35,23 @@ public class WxCpXmlOutNewsMessage extends WxCpXmlOutMessage {
}
- @XmlRootElement(name = "Item")
- @XmlAccessorType(XmlAccessType.FIELD)
+ @XStreamAlias("item")
public static class Item {
- @XmlElement(name = "Title")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Title")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String Title;
- @XmlElement(name = "Description")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Description")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String Description;
- @XmlElement(name="PicUrl")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("PicUrl")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String PicUrl;
- @XmlElement(name="Url")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Url")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String Url;
public String getTitle() {
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessage.java
index d6466fd0..f2096ba8 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutTextMessage.java
@@ -1,20 +1,15 @@
package me.chanjar.weixin.cp.bean;
-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 javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.util.xml.AdapterCDATA;
+import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
+@XStreamAlias("xml")
public class WxCpXmlOutTextMessage extends WxCpXmlOutMessage {
- @XmlElement(name="Content")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Content")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String content;
public WxCpXmlOutTextMessage() {
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessage.java
index 83345858..537147b6 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVideoMessage.java
@@ -1,19 +1,14 @@
package me.chanjar.weixin.cp.bean;
-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 javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.util.xml.AdapterCDATA;
+import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
+@XStreamAlias("xml")
public class WxCpXmlOutVideoMessage extends WxCpXmlOutMessage {
- @XmlElement(name = "Video")
+ @XStreamAlias("Video")
protected final Video video = new Video();
public WxCpXmlOutVideoMessage() {
@@ -45,20 +40,19 @@ public class WxCpXmlOutVideoMessage extends WxCpXmlOutMessage {
}
- @XmlRootElement(name = "Video")
- @XmlAccessorType(XmlAccessType.FIELD)
- private static class Video {
+ @XStreamAlias("Video")
+ public static class Video {
- @XmlElement(name = "MediaId")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("MediaId")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String mediaId;
- @XmlElement(name = "Title")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Title")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String title;
- @XmlElement(name = "Description")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Description")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String description;
public String getMediaId() {
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessage.java
index a4fa91a5..54c53dc6 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlOutVoiceMessage.java
@@ -1,20 +1,15 @@
package me.chanjar.weixin.cp.bean;
-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 javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.util.xml.MediaIdMarshaller;
+import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter;
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
+@XStreamAlias("xml")
public class WxCpXmlOutVoiceMessage extends WxCpXmlOutMessage {
- @XmlElement(name="Voice")
- @XmlJavaTypeAdapter(MediaIdMarshaller.class)
+ @XStreamAlias("Voice")
+ @XStreamConverter(value=XStreamMediaIdConverter.class)
private String mediaId;
public WxCpXmlOutVoiceMessage() {
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java
index 5f0fa614..383904df 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/outxmlbuilder/NewsBuilder.java
@@ -1,11 +1,11 @@
package me.chanjar.weixin.cp.bean.outxmlbuilder;
-import java.util.ArrayList;
-import java.util.List;
-
import me.chanjar.weixin.cp.bean.WxCpXmlOutNewsMessage;
import me.chanjar.weixin.cp.bean.WxCpXmlOutNewsMessage.Item;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* 图文消息builder
* @author Daniel Qian
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpDepartGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpDepartGsonAdapter.java
index 022333f8..723d50db 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpDepartGsonAdapter.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpDepartGsonAdapter.java
@@ -8,18 +8,11 @@
*/
package me.chanjar.weixin.cp.util.json;
-import java.lang.reflect.Type;
-
+import com.google.gson.*;
import me.chanjar.weixin.common.util.json.GsonHelper;
import me.chanjar.weixin.cp.bean.WxCpDepart;
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
+import java.lang.reflect.Type;
/**
* @author Daniel Qian
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java
index 3a8b739b..32358648 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java
@@ -2,11 +2,12 @@ package me.chanjar.weixin.cp.util.json;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
-import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.bean.result.WxError;
-import me.chanjar.weixin.common.util.json.WxMediaUploadResultAdapter;
import me.chanjar.weixin.common.util.json.WxErrorAdapter;
-import me.chanjar.weixin.cp.bean.*;
+import me.chanjar.weixin.cp.bean.WxCpDepart;
+import me.chanjar.weixin.cp.bean.WxCpMessage;
+import me.chanjar.weixin.cp.bean.WxCpTag;
+import me.chanjar.weixin.cp.bean.WxCpUser;
public class WxCpGsonBuilder {
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java
index bf04d50c..4a920b54 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpUserGsonAdapter.java
@@ -8,12 +8,12 @@
*/
package me.chanjar.weixin.cp.util.json;
-import java.lang.reflect.Type;
-
import com.google.gson.*;
import me.chanjar.weixin.common.util.json.GsonHelper;
import me.chanjar.weixin.cp.bean.WxCpUser;
+import java.lang.reflect.Type;
+
/**
* @author Daniel Qian
*/
@@ -49,6 +49,8 @@ public class WxCpUserGsonAdapter implements JsonDeserializer, JsonSeri
user.setTel(GsonHelper.getString(o, "tel"));
user.setEmail(GsonHelper.getString(o, "email"));
user.setWeiXinId(GsonHelper.getString(o, "weixinid"));
+ user.setAvatar(GsonHelper.getString(o, "avatar"));
+ user.setStatus(GsonHelper.getInteger(o, "status"));
if (GsonHelper.isNotNull(o.get("extattr"))) {
JsonArray attrJsonElements = o.get("extattr").getAsJsonObject().get("attrs").getAsJsonArray();
@@ -97,6 +99,12 @@ public class WxCpUserGsonAdapter implements JsonDeserializer, JsonSeri
if (user.getWeiXinId() != null) {
o.addProperty("weixinid", user.getWeiXinId());
}
+ if (user.getAvatar() != null) {
+ o.addProperty("avatar", user.getAvatar());
+ }
+ if (user.getStatus() != null) {
+ o.addProperty("status", user.getStatus());
+ }
if (user.getExtAttrs().size() > 0) {
JsonArray attrsJsonArray = new JsonArray();
for (WxCpUser.Attr attr : user.getExtAttrs()) {
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java
new file mode 100644
index 00000000..c6c5a5f1
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XStreamTransformer.java
@@ -0,0 +1,103 @@
+package me.chanjar.weixin.cp.util.xml;
+
+import com.thoughtworks.xstream.XStream;
+import me.chanjar.weixin.common.util.xml.XStreamInitializer;
+import me.chanjar.weixin.cp.bean.*;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+public class XStreamTransformer {
+
+ protected static final Map CLASS_2_XSTREAM_INSTANCE = configXStreamInstance();
+
+ /**
+ * xml -> pojo
+ *
+ * @param clazz
+ * @param xml
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public static T fromXml(Class clazz, String xml) {
+ T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(xml);
+ return object;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T fromXml(Class clazz, InputStream is) {
+ T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is);
+ return object;
+ }
+
+ /**
+ * pojo -> xml
+ *
+ * @param clazz
+ * @param object
+ * @return
+ */
+ public static String toXml(Class clazz, T object) {
+ return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object);
+ }
+
+ private static Map configXStreamInstance() {
+ Map map = new HashMap();
+ map.put(WxCpXmlMessage.class, config_WxCpXmlMessage());
+ map.put(WxCpXmlOutNewsMessage.class, config_WxCpXmlOutNewsMessage());
+ map.put(WxCpXmlOutTextMessage.class, config_WxCpXmlOutTextMessage());
+ map.put(WxCpXmlOutImageMessage.class, config_WxCpXmlOutImageMessage());
+ map.put(WxCpXmlOutVideoMessage.class, config_WxCpXmlOutVideoMessage());
+ map.put(WxCpXmlOutVoiceMessage.class, config_WxCpXmlOutVoiceMessage());
+ return map;
+ }
+
+ private static XStream config_WxCpXmlMessage() {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxCpXmlMessage.class);
+ xstream.processAnnotations(WxCpXmlMessage.ScanCodeInfo.class);
+ xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.class);
+ xstream.processAnnotations(WxCpXmlMessage.SendPicsInfo.Item.class);
+ xstream.processAnnotations(WxCpXmlMessage.SendLocationInfo.class);
+ return xstream;
+ }
+
+ private static XStream config_WxCpXmlOutImageMessage() {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxCpXmlOutMessage.class);
+ xstream.processAnnotations(WxCpXmlOutImageMessage.class);
+ return xstream;
+ }
+
+ private static XStream config_WxCpXmlOutNewsMessage() {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxCpXmlOutMessage.class);
+ xstream.processAnnotations(WxCpXmlOutNewsMessage.class);
+ xstream.processAnnotations(WxCpXmlOutNewsMessage.Item.class);
+ return xstream;
+ }
+
+ private static XStream config_WxCpXmlOutTextMessage() {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxCpXmlOutMessage.class);
+ xstream.processAnnotations(WxCpXmlOutTextMessage.class);
+ return xstream;
+ }
+
+ private static XStream config_WxCpXmlOutVideoMessage() {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxCpXmlOutMessage.class);
+ xstream.processAnnotations(WxCpXmlOutVideoMessage.class);
+ xstream.processAnnotations(WxCpXmlOutVideoMessage.Video.class);
+ return xstream;
+ }
+
+ private static XStream config_WxCpXmlOutVoiceMessage() {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxCpXmlOutMessage.class);
+ xstream.processAnnotations(WxCpXmlOutVoiceMessage.class);
+ return xstream;
+ }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XmlTransformer.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XmlTransformer.java
deleted file mode 100644
index 21bb7a77..00000000
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/xml/XmlTransformer.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package me.chanjar.weixin.cp.util.xml;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.io.Writer;
-
-import javax.xml.bind.*;
-
-import me.chanjar.weixin.cp.bean.*;
-import org.xml.sax.InputSource;
-
-import com.sun.xml.bind.marshaller.CharacterEscapeHandler;
-
-public class XmlTransformer {
-
- protected static final JAXBContext JAXB_CONTEXT = initJAXBContext();
-
- /**
- * xml -> pojo
- *
- * @param clazz
- * @param xml
- * @return
- * @throws JAXBException
- */
- @SuppressWarnings("unchecked")
- public static T fromXml(Class clazz, String xml) throws JAXBException {
- Unmarshaller um = JAXB_CONTEXT.createUnmarshaller();
- T object = (T) um.unmarshal(new StringReader(xml));
- return object;
- }
-
- @SuppressWarnings("unchecked")
- public static T fromXml(Class clazz, InputStream is) throws JAXBException {
- Unmarshaller um = JAXB_CONTEXT.createUnmarshaller();
- InputSource inputSource = new InputSource(is);
- inputSource.setEncoding("utf-8");
- T object = (T) um.unmarshal(inputSource);
- return object;
- }
-
- /**
- * pojo -> xml
- *
- * @param clazz
- * @param object
- * @return
- * @throws JAXBException
- */
- public static String toXml(Class clazz, T object) throws JAXBException {
- StringWriter stringWriter = new StringWriter();
- toXml(clazz, object, stringWriter);
- return stringWriter.getBuffer().toString();
- }
-
- public static void toXml(Class clazz, T object, Writer writer) throws JAXBException {
- Marshaller m = JAXB_CONTEXT.createMarshaller();
- m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
- m.setProperty(CharacterEscapeHandler.class.getName(), CHAR_ESCAPE_HANDLER);
- m.setProperty(Marshaller.JAXB_FRAGMENT, true);
- m.marshal(object, writer);
- }
-
- protected static final CharacterEscapeHandler CHAR_ESCAPE_HANDLER = new CharacterUnescapeHandler();
-
- protected static class CharacterUnescapeHandler implements CharacterEscapeHandler {
- public void escape(char[] ac, int i, int j, boolean flag, Writer writer) throws IOException {
- writer.write(ac, i, j);
- }
- }
-
- private static JAXBContext initJAXBContext() {
- /*
- * JAXBContext对象是线程安全的,根据官方文档的建议将对象作为全局实例
- * https://jaxb.java.net/guide/Performance_and_thread_safety.html
- */
- try {
- return JAXBContext.newInstance(
- WxCpXmlOutMessage.class,
- WxCpXmlOutImageMessage.class,
- WxCpXmlOutNewsMessage.class,
- WxCpXmlOutTextMessage.class,
- WxCpXmlOutVideoMessage.class,
- WxCpXmlOutVoiceMessage.class,
- WxCpXmlMessage.class);
- } catch (JAXBException e) {
- throw new RuntimeException(e);
- }
- }
-
-}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java
index 00b890d9..bb3690bc 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java
@@ -1,23 +1,17 @@
package me.chanjar.weixin.cp.api;
-import java.io.InputStream;
-
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlRootElement;
-
import com.google.inject.Binder;
import com.google.inject.Module;
-import org.xml.sax.InputSource;
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import me.chanjar.weixin.common.util.xml.XStreamInitializer;
+
+import java.io.InputStream;
public class ApiTestModule implements Module {
@Override
public void configure(Binder binder) {
- try {
InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml");
WxXmlCpInMemoryConfigStorage config = fromXml(WxXmlCpInMemoryConfigStorage.class, is1);
WxCpServiceImpl wxService = new WxCpServiceImpl();
@@ -25,21 +19,16 @@ public class ApiTestModule implements Module {
binder.bind(WxCpServiceImpl.class).toInstance(wxService);
binder.bind(WxCpConfigStorage.class).toInstance(config);
- } catch (JAXBException e) {
- throw new RuntimeException(e);
- }
}
- public static T fromXml(Class clazz, InputStream is) throws JAXBException {
- Unmarshaller um = JAXBContext.newInstance(clazz).createUnmarshaller();
- InputSource inputSource = new InputSource(is);
- inputSource.setEncoding("utf-8");
- T object = (T) um.unmarshal(inputSource);
- return object;
+ public static T fromXml(Class clazz, InputStream is) {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.alias("xml", clazz);
+ xstream.processAnnotations(clazz);
+ return (T) xstream.fromXML(is);
}
- @XmlRootElement(name = "xml")
- @XmlAccessorType(XmlAccessType.FIELD)
+ @XStreamAlias("xml")
public static class WxXmlCpInMemoryConfigStorage extends WxCpInMemoryConfigStorage {
protected String userId;
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java
index 032165eb..d8757f34 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java
@@ -1,14 +1,12 @@
package me.chanjar.weixin.cp.api;
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.StringUtils;
import org.testng.Assert;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import me.chanjar.weixin.common.exception.WxErrorException;
-
-import com.google.inject.Inject;
-
/**
* 基础API测试
* @author Daniel Qian
@@ -24,7 +22,7 @@ public class WxCpBaseAPITest {
public void testRefreshAccessToken() throws WxErrorException {
WxCpConfigStorage configStorage = wxService.wxCpConfigStorage;
String before = configStorage.getAccessToken();
- wxService.accessTokenRefresh();
+ wxService.getAccessToken(false);
String after = configStorage.getAccessToken();
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java
new file mode 100644
index 00000000..cf129088
--- /dev/null
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java
@@ -0,0 +1,66 @@
+package me.chanjar.weixin.cp.api;
+
+import me.chanjar.weixin.common.bean.result.WxError;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.common.util.http.RequestExecutor;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+@Test
+public class WxCpBusyRetryTest {
+
+ @DataProvider(name="getService")
+ public Object[][] getService() {
+ WxCpService service = new WxCpServiceImpl() {
+
+ @Override
+ protected T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException {
+ WxError error = new WxError();
+ error.setErrorCode(-1);
+ throw new WxErrorException(error);
+ }
+ };
+
+ service.setMaxRetryTimes(3);
+ service.setRetrySleepMillis(500);
+ return new Object[][] {
+ new Object[] { service }
+ };
+ }
+
+ @Test(dataProvider = "getService", expectedExceptions = RuntimeException.class)
+ public void testRetry(WxCpService service) throws WxErrorException {
+ service.execute(null, null, null);
+ }
+
+ @Test(dataProvider = "getService")
+ public void testRetryInThreadPool(final WxCpService service) throws InterruptedException, ExecutionException {
+ // 当线程池中的线程复用的时候,还是能保证相同的重试次数
+ ExecutorService executorService = Executors.newFixedThreadPool(1);
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ System.out.println("=====================");
+ System.out.println(Thread.currentThread().getName() + ": testRetry");
+ service.execute(null, null, null);
+ } catch (WxErrorException e) {
+ throw new RuntimeException(e);
+ } catch (RuntimeException e) {
+ // OK
+ }
+ }
+ };
+ Future> submit1 = executorService.submit(runnable);
+ Future> submit2 = executorService.submit(runnable);
+
+ submit1.get();
+ submit2.get();
+ }
+
+}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpDepartAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpDepartAPITest.java
index 28cf15b0..28779e65 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpDepartAPITest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpDepartAPITest.java
@@ -1,15 +1,13 @@
package me.chanjar.weixin.cp.api;
-import java.util.List;
-
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpDepart;
import org.testng.Assert;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import me.chanjar.weixin.common.exception.WxErrorException;
-
-import com.google.inject.Inject;
+import java.util.List;
/**
* 测试部门接口
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java
index f04b1fd4..c67acab1 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java
@@ -1,20 +1,18 @@
package me.chanjar.weixin.cp.api;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
+import com.google.inject.Inject;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
+import me.chanjar.weixin.common.exception.WxErrorException;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import me.chanjar.weixin.common.exception.WxErrorException;
-
-import com.google.inject.Inject;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
/**
* 测试多媒体文件上传下载
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java
index 1672faaa..ee16f56f 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java
@@ -1,14 +1,12 @@
package me.chanjar.weixin.cp.api;
+import com.google.inject.Inject;
import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpMessage;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import me.chanjar.weixin.common.exception.WxErrorException;
-
-import com.google.inject.Inject;
-
/***
* 测试发送消息
* @author Daniel Qian
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageRouterTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageRouterTest.java
index 6b178f9b..7394bbfa 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageRouterTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageRouterTest.java
@@ -1,14 +1,16 @@
package me.chanjar.weixin.cp.api;
-import java.util.Map;
-
import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.session.StandardSessionManager;
+import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
+import java.util.Map;
+
/**
* 测试消息路由器
* @author Daniel Qian
@@ -67,7 +69,8 @@ public class WxCpMessageRouterTest {
final WxCpMessageRouter router = new WxCpMessageRouter(null);
router.rule().handler(new WxCpMessageHandler() {
@Override
- public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService wxCpService) {
+ public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService wxCpService,
+ WxSessionManager sessionManager) {
return null;
}
}).end();
@@ -149,11 +152,145 @@ public class WxCpMessageRouterTest {
}
@Override
- public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService wxCpService) {
+ public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService wxCpService,
+ WxSessionManager sessionManager) {
sb.append(this.echoStr).append(',');
return null;
}
}
+ @DataProvider
+ public Object[][] standardSessionManager() {
+
+ // 故意把session存活时间变短,清理更频繁
+ StandardSessionManager ism = new StandardSessionManager();
+ ism.setMaxInactiveInterval(1);
+ ism.setProcessExpiresFrequency(1);
+ ism.setBackgroundProcessorDelay(1);
+
+ return new Object[][] {
+ new Object[] { ism }
+ };
+
+ }
+
+ @Test(dataProvider = "standardSessionManager")
+ public void testSessionClean1(StandardSessionManager ism) throws InterruptedException {
+
+ // 两个同步请求,看是否处理完毕后会被清理掉
+ final WxCpMessageRouter router = new WxCpMessageRouter(null);
+ router.setSessionManager(ism);
+ router
+ .rule().async(false).handler(new WxSessionMessageHandler()).next()
+ .rule().async(false).handler(new WxSessionMessageHandler()).end();
+
+ WxCpXmlMessage msg = new WxCpXmlMessage();
+ msg.setFromUserName("abc");
+ router.route(msg);
+
+ Thread.sleep(2000l);
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+
+ }
+
+ @Test(dataProvider = "standardSessionManager")
+ public void testSessionClean2(StandardSessionManager ism) throws InterruptedException {
+
+ // 1个同步,1个异步请求,看是否处理完毕后会被清理掉
+ {
+ final WxCpMessageRouter router = new WxCpMessageRouter(null);
+ router.setSessionManager(ism);
+ router
+ .rule().async(false).handler(new WxSessionMessageHandler()).next()
+ .rule().async(true).handler(new WxSessionMessageHandler()).end();
+
+ WxCpXmlMessage msg = new WxCpXmlMessage();
+ msg.setFromUserName("abc");
+ router.route(msg);
+
+ Thread.sleep(2000l);
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+ }
+ {
+ final WxCpMessageRouter router = new WxCpMessageRouter(null);
+ router.setSessionManager(ism);
+ router
+ .rule().async(true).handler(new WxSessionMessageHandler()).next()
+ .rule().async(false).handler(new WxSessionMessageHandler()).end();
+
+ WxCpXmlMessage msg = new WxCpXmlMessage();
+ msg.setFromUserName("abc");
+ router.route(msg);
+
+ Thread.sleep(2000l);
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+ }
+
+ }
+
+ @Test(dataProvider = "standardSessionManager")
+ public void testSessionClean3(StandardSessionManager ism) throws InterruptedException {
+
+ // 2个异步请求,看是否处理完毕后会被清理掉
+ final WxCpMessageRouter router = new WxCpMessageRouter(null);
+ router.setSessionManager(ism);
+ router
+ .rule().async(true).handler(new WxSessionMessageHandler()).next()
+ .rule().async(true).handler(new WxSessionMessageHandler()).end();
+
+ WxCpXmlMessage msg = new WxCpXmlMessage();
+ msg.setFromUserName("abc");
+ router.route(msg);
+
+ Thread.sleep(2000l);
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+
+ }
+
+ @Test(dataProvider = "standardSessionManager")
+ public void testSessionClean4(StandardSessionManager ism) throws InterruptedException {
+
+ // 一个同步请求,看是否处理完毕后会被清理掉
+ {
+ final WxCpMessageRouter router = new WxCpMessageRouter(null);
+ router.setSessionManager(ism);
+ router
+ .rule().async(false).handler(new WxSessionMessageHandler()).end();
+
+ WxCpXmlMessage msg = new WxCpXmlMessage();
+ msg.setFromUserName("abc");
+ router.route(msg);
+
+ Thread.sleep(2000l);
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+ }
+
+ {
+ final WxCpMessageRouter router = new WxCpMessageRouter(null);
+ router.setSessionManager(ism);
+ router
+ .rule().async(true).handler(new WxSessionMessageHandler()).end();
+
+ WxCpXmlMessage msg = new WxCpXmlMessage();
+ msg.setFromUserName("abc");
+ router.route(msg);
+
+ Thread.sleep(2000l);
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+ }
+ }
+
+ public static class WxSessionMessageHandler implements WxCpMessageHandler {
+
+ @Override
+ public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService wxCpService,
+ WxSessionManager sessionManager) {
+ sessionManager.getSession(wxMessage.getFromUserName());
+ return null;
+ }
+
+
+ }
+
}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpUserAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpUserAPITest.java
index 9efb8a09..c444eec5 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpUserAPITest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpUserAPITest.java
@@ -1,9 +1,9 @@
package me.chanjar.weixin.cp.api;
import com.google.inject.Inject;
+import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpDepart;
import me.chanjar.weixin.cp.bean.WxCpUser;
-import me.chanjar.weixin.common.exception.WxErrorException;
import org.testng.Assert;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
@@ -26,30 +26,30 @@ public class WxCpUserAPITest {
public void testUserCreate() throws WxErrorException {
WxCpUser user = new WxCpUser();
- user.setUserId("xiaohe.yang");
- user.setName("杨宝");
+ user.setUserId("some.woman");
+ user.setName("Some Woman");
user.setDepartIds(new Integer[] { 9, 8 });
- user.setEmail("yangxiaohe@ddd.com");
+ user.setEmail("none@none.com");
user.setGender("女");
- user.setMobile("13564684979");
- user.setPosition("老婆");
+ user.setMobile("13560084979");
+ user.setPosition("woman");
user.setTel("3300393");
- user.addExtAttr("爱好", "老公");
+ user.addExtAttr("爱好", "table");
wxCpService.userCreate(user);
}
@Test(dependsOnMethods = "testUserCreate")
public void testUserUpdate() throws WxErrorException {
WxCpUser user = new WxCpUser();
- user.setUserId("xiaohe.yang");
- user.setName("杨宝");
- user.addExtAttr("爱好", "老公2");
+ user.setUserId("some.woman");
+ user.setName("Some Woman");
+ user.addExtAttr("爱好", "table2");
wxCpService.userUpdate(user);
}
@Test(dependsOnMethods = "testUserUpdate")
public void testUserGet() throws WxErrorException {
- WxCpUser user = wxCpService.userGet("xiaohe.yang");
+ WxCpUser user = wxCpService.userGet("some.woman");
Assert.assertNotNull(user);
}
@@ -61,6 +61,6 @@ public class WxCpUserAPITest {
@Test(dependsOnMethods = "testDepartGetUsers")
public void testUserDelete() throws WxErrorException {
- wxCpService.userDelete("xiaohe.yang");
+ wxCpService.userDelete("some.woman");
}
}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxMenuAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxMenuAPITest.java
index c00d73e2..ca5b5991 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxMenuAPITest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxMenuAPITest.java
@@ -1,19 +1,15 @@
package me.chanjar.weixin.cp.api;
-import javax.xml.bind.JAXBException;
-
+import com.google.inject.Inject;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.WxMenu;
+import me.chanjar.weixin.common.bean.WxMenu.WxMenuButton;
+import me.chanjar.weixin.common.exception.WxErrorException;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import com.google.inject.Inject;
-
-import me.chanjar.weixin.common.bean.WxMenu.WxMenuButton;
-import me.chanjar.weixin.common.exception.WxErrorException;
-
/**
* 测试菜单
* @author Daniel Qian
@@ -42,7 +38,7 @@ public class WxMenuAPITest {
}
@DataProvider(name="menu")
- public Object[][] getMenu() throws JAXBException {
+ public Object[][] getMenu() {
WxMenu menu = new WxMenu();
WxMenuButton button1 = new WxMenuButton();
button1.setType(WxConsts.BUTTON_CLICK);
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java
index 72169d2b..f9dbe657 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java
@@ -1,11 +1,10 @@
package me.chanjar.weixin.cp.bean;
import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.cp.bean.WxCpMessage.WxArticle;
import org.testng.Assert;
import org.testng.annotations.Test;
-import me.chanjar.weixin.cp.bean.WxCpMessage.WxArticle;
-
@Test
public class WxCpMessageTest {
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java
index 0c0d3961..52219868 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java
@@ -1,35 +1,29 @@
package me.chanjar.weixin.cp.demo;
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import me.chanjar.weixin.common.util.xml.XStreamInitializer;
import me.chanjar.weixin.cp.api.WxCpInMemoryConfigStorage;
-import org.xml.sax.InputSource;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlRootElement;
import java.io.InputStream;
/**
* @author Daniel Qian
*/
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
+@XStreamAlias("xml")
class WxCpDemoInMemoryConfigStorage extends WxCpInMemoryConfigStorage {
@Override
public String toString() {
return "SimpleWxConfigProvider [appidOrCorpid=" + corpId + ", corpSecret=" + corpSecret + ", accessToken=" + accessToken
- + ", expiresIn=" + expiresIn + ", token=" + token + ", aesKey=" + aesKey + "]";
+ + ", expiresTime=" + expiresTime + ", token=" + token + ", aesKey=" + aesKey + "]";
}
- public static WxCpDemoInMemoryConfigStorage fromXml(InputStream is) throws JAXBException {
- Unmarshaller um = JAXBContext.newInstance(WxCpDemoInMemoryConfigStorage.class).createUnmarshaller();
- InputSource inputSource = new InputSource(is);
- inputSource.setEncoding("utf-8");
- return (WxCpDemoInMemoryConfigStorage) um.unmarshal(inputSource);
+ public static WxCpDemoInMemoryConfigStorage fromXml(InputStream is) {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxCpDemoInMemoryConfigStorage.class);
+ return (WxCpDemoInMemoryConfigStorage) xstream.fromXML(is);
}
}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java
index 2a248f98..0dadee91 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoServer.java
@@ -1,5 +1,6 @@
package me.chanjar.weixin.cp.demo;
+import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.cp.api.*;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
@@ -8,7 +9,6 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
-import javax.xml.bind.JAXBException;
import java.io.InputStream;
import java.util.Map;
@@ -37,7 +37,6 @@ public class WxCpDemoServer {
}
private static void initWeixin() {
- try {
InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml");
WxCpDemoInMemoryConfigStorage config = WxCpDemoInMemoryConfigStorage.fromXml(is1);
@@ -48,7 +47,7 @@ public class WxCpDemoServer {
WxCpMessageHandler handler = new WxCpMessageHandler() {
@Override
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context,
- WxCpService wxCpService) {
+ WxCpService wxCpService, WxSessionManager sessionManager) {
WxCpXmlOutTextMessage m = WxCpXmlOutMessage
.TEXT()
.content("测试加密消息")
@@ -62,7 +61,7 @@ public class WxCpDemoServer {
WxCpMessageHandler oauth2handler = new WxCpMessageHandler() {
@Override
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context,
- WxCpService wxCpService) {
+ WxCpService wxCpService, WxSessionManager sessionManager) {
String href = "测试oauth2";
return WxCpXmlOutMessage
@@ -87,8 +86,5 @@ public class WxCpDemoServer {
.end()
;
- } catch (JAXBException e) {
- throw new RuntimeException(e);
- }
}
}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpEndpointServlet.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpEndpointServlet.java
index 320cc540..1fe8f592 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpEndpointServlet.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpEndpointServlet.java
@@ -1,20 +1,18 @@
package me.chanjar.weixin.cp.demo;
import me.chanjar.weixin.common.util.StringUtils;
-import me.chanjar.weixin.cp.api.*;
+import me.chanjar.weixin.cp.api.WxCpConfigStorage;
+import me.chanjar.weixin.cp.api.WxCpMessageRouter;
+import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
-import me.chanjar.weixin.cp.bean.WxCpXmlOutTextMessage;
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import javax.xml.bind.JAXBException;
import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map;
/**
* @author Daniel Qian
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java
index 02b1bf5d..e0800061 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpOAuth2Servlet.java
@@ -10,9 +10,6 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
-/**
- * Created by qianjia on 14/11/28.
- */
public class WxCpOAuth2Servlet extends HttpServlet {
protected WxCpService wxCpService;
diff --git a/weixin-java-cp/src/test/resources/logback-test.xml b/weixin-java-cp/src/test/resources/logback-test.xml
new file mode 100644
index 00000000..9c2ec6ae
--- /dev/null
+++ b/weixin-java-cp/src/test/resources/logback-test.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
diff --git a/weixin-java-cp/src/test/resources/test-config.sample.xml b/weixin-java-cp/src/test/resources/test-config.sample.xml
index 02c98f3f..aa99a962 100644
--- a/weixin-java-cp/src/test/resources/test-config.sample.xml
+++ b/weixin-java-cp/src/test/resources/test-config.sample.xml
@@ -5,7 +5,7 @@
企业号应用Token
企业号应用EncodingAESKey
可以不填写
- 可以不填写
+ 可以不填写
企业号通讯录里的某个userid
企业号通讯录的某个部门id
企业号通讯录里的某个tagid
diff --git a/weixin-java-cp/src/test/resources/testng.xml b/weixin-java-cp/src/test/resources/testng.xml
index 4cddddcf..ba92bc0c 100644
--- a/weixin-java-cp/src/test/resources/testng.xml
+++ b/weixin-java-cp/src/test/resources/testng.xml
@@ -1,28 +1,29 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml
index 2f757cfa..61d4972b 100644
--- a/weixin-java-mp/pom.xml
+++ b/weixin-java-mp/pom.xml
@@ -6,7 +6,7 @@
me.chanjar
weixin-java-parent
- 1.0.6
+ 1.1.0-SNAPSHOT
weixin-java-mp
WeiXin Java Tools - MP
diff --git a/weixin-java-mp/pom.xml.versionsBackup b/weixin-java-mp/pom.xml.versionsBackup
deleted file mode 100644
index 2e10c3ed..00000000
--- a/weixin-java-mp/pom.xml.versionsBackup
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
- 4.0.0
-
- me.chanjar
- weixin-java-parent
- 1.0.5-SNAPSHOT
-
- weixin-java-mp
- WeiXin Java Tools - MP
- 微信公众号Java SDK
-
-
-
- me.chanjar
- weixin-java-common
- ${project.version}
-
-
- junit
- junit
- test
-
-
- org.testng
- testng
- 6.8.7
- test
-
-
- org.mockito
- mockito-all
- 1.9.5
- test
-
-
- com.google.inject
- guice
- 3.0
- test
-
-
- org.eclipse.jetty
- jetty-server
- 9.3.0.M0
- test
-
-
- org.eclipse.jetty
- jetty-servlet
- 9.3.0.M0
- test
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
-
- src/test/resources/testng.xml
-
-
-
-
-
-
-
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
index 3ecbf155..c54a119d 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
@@ -9,21 +9,52 @@ import me.chanjar.weixin.common.bean.WxAccessToken;
*/
public interface WxMpConfigStorage {
+ public String getAccessToken();
+
+ public boolean isAccessTokenExpired();
+
+ /**
+ * 强制将access token过期掉
+ */
+ public void expireAccessToken();
+
+ /**
+ * 应该是线程安全的
+ * @param accessToken
+ */
public void updateAccessToken(WxAccessToken accessToken);
-
+
+ /**
+ * 应该是线程安全的
+ * @param accessToken
+ * @param expiresIn
+ */
public void updateAccessToken(String accessToken, int expiresIn);
-
- public String getAccessToken();
-
+
+ public String getJsapiTicket();
+
+ public boolean isJsapiTicketExpired();
+
+ /**
+ * 强制将jsapi ticket过期掉
+ */
+ public void expireJsapiTicket();
+
+ /**
+ * 应该是线程安全的
+ * @param jsapiTicket
+ */
+ public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds);
+
public String getAppId();
-
+
public String getSecret();
-
+
public String getToken();
public String getAesKey();
- public int getExpiresIn();
+ public long getExpiresTime();
public String getOauth2redirectUri();
@@ -33,6 +64,7 @@ public interface WxMpConfigStorage {
public String getHttp_proxy_username();
+
public String getHttp_proxy_password();
}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
index 82c3d621..ff272f77 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
@@ -9,12 +9,14 @@ import me.chanjar.weixin.common.bean.WxAccessToken;
*/
public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
+ private static final long l = 7000 * 1000l;
+
protected String appId;
protected String secret;
protected String token;
protected String accessToken;
protected String aesKey;
- protected int expiresIn;
+ protected long expiresTime;
protected String oauth2redirectUri;
@@ -23,17 +25,46 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
protected String http_proxy_username;
protected String http_proxy_password;
- public void updateAccessToken(WxAccessToken accessToken) {
+ protected String jsapiTicket;
+ protected long jsapiTicketExpiresTime;
+
+ public String getAccessToken() {
+ return this.accessToken;
+ }
+
+ public boolean isAccessTokenExpired() {
+ return System.currentTimeMillis() > this.expiresTime;
+ }
+
+ public synchronized void updateAccessToken(WxAccessToken accessToken) {
updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
}
- public void updateAccessToken(String accessToken, int expiresIn) {
+ public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
this.accessToken = accessToken;
- this.expiresIn = expiresIn;
+ this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l;
}
- public String getAccessToken() {
- return this.accessToken;
+ public void expireAccessToken() {
+ this.expiresTime = 0;
+ }
+
+ public String getJsapiTicket() {
+ return jsapiTicket;
+ }
+
+ public boolean isJsapiTicketExpired() {
+ return System.currentTimeMillis() > this.jsapiTicketExpiresTime;
+ }
+
+ public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
+ this.jsapiTicket = jsapiTicket;
+ // 预留200秒的时间
+ this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l;
+ }
+
+ public void expireJsapiTicket() {
+ this.jsapiTicketExpiresTime = 0;
}
public String getAppId() {
@@ -48,8 +79,8 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
return this.token;
}
- public int getExpiresIn() {
- return this.expiresIn;
+ public long getExpiresTime() {
+ return this.expiresTime;
}
public void setAppId(String appId) {
@@ -76,8 +107,8 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
this.accessToken = accessToken;
}
- public void setExpiresIn(int expiresIn) {
- this.expiresIn = expiresIn;
+ public void setExpiresTime(long expiresTime) {
+ this.expiresTime = expiresTime;
}
@Override
@@ -129,11 +160,13 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
", token='" + token + '\'' +
", accessToken='" + accessToken + '\'' +
", aesKey='" + aesKey + '\'' +
- ", expiresIn=" + expiresIn +
+ ", expiresTime=" + expiresTime +
", http_proxy_host='" + http_proxy_host + '\'' +
", http_proxy_port=" + http_proxy_port +
", http_proxy_username='" + http_proxy_username + '\'' +
", http_proxy_password='" + http_proxy_password + '\'' +
+ ", jsapiTicket='" + jsapiTicket + '\'' +
+ ", jsapiTicketExpiresTime='" + jsapiTicketExpiresTime + '\'' +
'}';
}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageHandler.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageHandler.java
index 8e6deeb6..d7c30a31 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageHandler.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageHandler.java
@@ -1,5 +1,6 @@
package me.chanjar.weixin.mp.api;
+import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
@@ -7,18 +8,21 @@ import java.util.Map;
/**
* 处理微信推送消息的处理器接口
- * @author chanjarster
*
+ * @author Daniel Qian
*/
public interface WxMpMessageHandler {
/**
- *
* @param wxMessage
- * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个
+ * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个
* @param wxMpService
+ * @param sessionManager
* @return xml格式的消息,如果在异步规则里处理的话,可以返回null
*/
- public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, WxMpService wxMpService);
-
+ public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
+ Map context,
+ WxMpService wxMpService,
+ WxSessionManager sessionManager);
+
}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageInterceptor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageInterceptor.java
index d940804f..2b68a6f2 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageInterceptor.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageInterceptor.java
@@ -1,23 +1,29 @@
package me.chanjar.weixin.mp.api;
+import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import java.util.Map;
/**
* 微信消息拦截器,可以用来做验证
- * @author qianjia
*
+ * @author Daniel Qian
*/
public interface WxMpMessageInterceptor {
/**
* 拦截微信消息
+ *
* @param wxMessage
- * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个
+ * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个
* @param wxMpService
- * @return true代表OK,false代表不OK
+ * @param sessionManager
+ * @return true代表OK,false代表不OK
*/
- public boolean intercept(WxMpXmlMessage wxMessage, Map context, WxMpService wxMpService);
-
+ public boolean intercept(WxMpXmlMessage wxMessage,
+ Map context,
+ WxMpService wxMpService,
+ WxSessionManager sessionManager);
+
}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java
index e904e41b..afd8d3fb 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java
@@ -1,14 +1,22 @@
package me.chanjar.weixin.mp.api;
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.session.*;
+import me.chanjar.weixin.common.util.WxMessageDuplicateChecker;
+import me.chanjar.weixin.common.util.WxMessageInMemoryDuplicateChecker;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.regex.Pattern;
/**
@@ -36,27 +44,61 @@ import java.util.regex.Pattern;
* router.route(message);
*
*
- * @author qianjia
- *
*/
public class WxMpMessageRouter {
- private static final int DEFAULT_THREAD_POOL_SIZE = 20;
+ protected final Logger log = LoggerFactory.getLogger(WxMpMessageRouter.class);
- private final List rules = new ArrayList();
+ private static final int DEFAULT_THREAD_POOL_SIZE = 100;
- private final ExecutorService executorService;
+ private final List rules = new ArrayList();
private final WxMpService wxMpService;
+ private ExecutorService executorService;
+
+ private WxMessageDuplicateChecker messageDuplicateChecker;
+
+ private WxSessionManager sessionManager;
+
public WxMpMessageRouter(WxMpService wxMpService) {
this.wxMpService = wxMpService;
this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE);
+ this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker();
+ this.sessionManager = new StandardSessionManager();
}
- public WxMpMessageRouter(WxMpService wxMpService, int threadPoolSize) {
- this.wxMpService = wxMpService;
- this.executorService = Executors.newFixedThreadPool(threadPoolSize);
+ /**
+ *
+ * 设置自定义的 {@link ExecutorService}
+ * 如果不调用该方法,默认使用 Executors.newFixedThreadPool(100)
+ *
+ * @param executorService
+ */
+ public void setExecutorService(ExecutorService executorService) {
+ this.executorService = executorService;
+ }
+
+ /**
+ *
+ * 设置自定义的 {@link me.chanjar.weixin.common.util.WxMessageDuplicateChecker}
+ * 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.WxMessageInMemoryDuplicateChecker}
+ *
+ * @param messageDuplicateChecker
+ */
+ public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicateChecker) {
+ this.messageDuplicateChecker = messageDuplicateChecker;
+ }
+
+ /**
+ *
+ * 设置自定义的{@link me.chanjar.weixin.common.session.WxSessionManager}
+ * 如果不调用该方法,默认使用 {@linke SessionManagerImpl}
+ *
+ * @param sessionManager
+ */
+ public void setSessionManager(WxSessionManager sessionManager) {
+ this.sessionManager = sessionManager;
}
/**
@@ -64,7 +106,7 @@ public class WxMpMessageRouter {
* @return
*/
public Rule rule() {
- return new Rule(this, wxMpService);
+ return new Rule(this, wxMpService, sessionManager);
}
/**
@@ -72,6 +114,11 @@ public class WxMpMessageRouter {
* @param wxMessage
*/
public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage) {
+ if (isDuplicateMessage(wxMessage)) {
+ // 如果是重复消息,那么就不做处理
+ return null;
+ }
+
final List matchRules = new ArrayList();
// 收集匹配的规则
for (final Rule rule : rules) {
@@ -82,33 +129,90 @@ public class WxMpMessageRouter {
}
}
}
-
+
if (matchRules.size() == 0) {
return null;
}
-
+
WxMpXmlOutMessage res = null;
+ final List futures = new ArrayList();
for (final Rule rule : matchRules) {
// 返回最后一个非异步的rule的执行结果
if(rule.async) {
- executorService.submit(new Runnable() {
- public void run() {
- rule.service(wxMessage);
- }
- });
+ futures.add(
+ executorService.submit(new Runnable() {
+ public void run() {
+ rule.service(wxMessage);
+ }
+ })
+ );
} else {
res = rule.service(wxMessage);
+ // 在同步操作结束,session访问结束
+ log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUserName());
+ sessionEndAccess(wxMessage);
}
}
+
+ if (futures.size() > 0) {
+ executorService.submit(new Runnable() {
+ @Override
+ public void run() {
+ for (Future future : futures) {
+ try {
+ future.get();
+ log.debug("End session access: async=true, sessionId={}", wxMessage.getFromUserName());
+ // 异步操作结束,session访问结束
+ sessionEndAccess(wxMessage);
+ } catch (InterruptedException e) {
+ log.error("Error happened when wait task finish", e);
+ } catch (ExecutionException e) {
+ log.error("Error happened when wait task finish", e);
+ }
+ }
+ }
+ });
+ }
return res;
}
-
+
+ protected boolean isDuplicateMessage(WxMpXmlMessage wxMessage) {
+
+ String messageId = "";
+ if (wxMessage.getMsgId() == null) {
+ messageId = wxMessage.getFromUserName() + "-" + String.valueOf(wxMessage.getCreateTime());
+ } else {
+ messageId = String.valueOf(wxMessage.getMsgId());
+ }
+
+ if (messageDuplicateChecker.isDuplicate(messageId)) {
+ return true;
+ }
+ return false;
+
+ }
+
+ /**
+ * 对session的访问结束
+ * @param wxMessage
+ */
+ protected void sessionEndAccess(WxMpXmlMessage wxMessage) {
+
+ InternalSession session = ((InternalSessionManager)sessionManager).findSession(wxMessage.getFromUserName());
+ if (session != null) {
+ session.endAccess();
+ }
+
+ }
+
public static class Rule {
private final WxMpMessageRouter routerBuilder;
private final WxMpService wxMpService;
+ private final WxSessionManager sessionManager;
+
private boolean async = true;
private String fromUser;
@@ -128,10 +232,11 @@ public class WxMpMessageRouter {
private List handlers = new ArrayList();
private List interceptors = new ArrayList();
-
- protected Rule(WxMpMessageRouter routerBuilder, WxMpService wxMpService) {
+
+ protected Rule(WxMpMessageRouter routerBuilder, WxMpService wxMpService, WxSessionManager sessionManager) {
this.routerBuilder = routerBuilder;
this.wxMpService = wxMpService;
+ this.sessionManager = sessionManager;
}
/**
@@ -297,7 +402,7 @@ public class WxMpMessageRouter {
Map context = new HashMap();
// 如果拦截器不通过
for (WxMpMessageInterceptor interceptor : this.interceptors) {
- if (!interceptor.intercept(wxMessage, context, wxMpService)) {
+ if (!interceptor.intercept(wxMessage, context, wxMpService, sessionManager)) {
return null;
}
}
@@ -306,7 +411,7 @@ public class WxMpMessageRouter {
WxMpXmlOutMessage res = null;
for (WxMpMessageHandler handler : this.handlers) {
// 返回最后handler的结果
- res = handler.handle(wxMessage, context, wxMpService);
+ res = handler.handle(wxMessage, context, wxMpService, sessionManager);
}
return res;
}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
index a6ac3c43..6bd501f3 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
@@ -2,10 +2,12 @@ package me.chanjar.weixin.mp.api;
import me.chanjar.weixin.common.bean.WxMenu;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.common.session.WxSession;
+import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.mp.bean.*;
import me.chanjar.weixin.mp.bean.result.*;
-import me.chanjar.weixin.common.exception.WxErrorException;
import java.io.File;
import java.io.IOException;
@@ -28,7 +30,15 @@ public interface WxMpService {
* @return
*/
public boolean checkSignature(String timestamp, String nonce, String signature);
-
+
+ /**
+ * 获取access_token, 不强制刷新access_token
+ * @see #getAccessToken(boolean)
+ * @return
+ * @throws WxErrorException
+ */
+ public String getAccessToken() throws WxErrorException;
+
/**
*
* 获取access_token,本方法线程安全
@@ -40,20 +50,56 @@ public interface WxMpService {
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token
*
+ * @param forceRefresh 强制刷新
+ * @return
* @throws me.chanjar.weixin.common.exception.WxErrorException
*/
- public void accessTokenRefresh() throws WxErrorException;
-
+ public String getAccessToken(boolean forceRefresh) throws WxErrorException;
+
+ /**
+ * 获得jsapi_ticket,不强制刷新jsapi_ticket
+ * @see #getJsapiTicket(boolean)
+ * @return
+ * @throws WxErrorException
+ */
+ public String getJsapiTicket() throws WxErrorException;
+
+ /**
+ *
+ * 获得jsapi_ticket
+ * 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
+ *
+ * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95
+ *
+ * @param forceRefresh 强制刷新
+ * @return
+ * @throws WxErrorException
+ */
+ public String getJsapiTicket(boolean forceRefresh) throws WxErrorException;
+
+ /**
+ *
+ * 创建调用jsapi时所需要的签名
+ *
+ * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95
+ *
+ * @param timestamp 时间戳
+ * @param noncestr 用户自己生成的随机字符串
+ * @param url url
+ * @return
+ */
+ public String createJsapiSignature(long timestamp, String noncestr, String url) throws WxErrorException;
+
/**
*
* 上传多媒体文件
- *
+ *
* 上传的多媒体文件有格式和大小限制,如下:
* 图片(image): 1M,支持JPG格式
* 语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式
* 视频(video):10MB,支持MP4格式
* 缩略图(thumb):64KB,支持JPG格式
- *
+ *
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件
*
* @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts}
@@ -412,9 +458,28 @@ public interface WxMpService {
*/
public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException;
- /**
- * 注入 {@link WxMpConfigStorage} 的实现
- * @param wxConfigProvider
- */
+ /**
+ * 注入 {@link WxMpConfigStorage} 的实现
+ * @param wxConfigProvider
+ */
public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider);
+
+ /**
+ *
+ * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试
+ * 默认:1000ms
+ *
+ * @param retrySleepMillis
+ */
+ void setRetrySleepMillis(int retrySleepMillis);
+
+ /**
+ *
+ * 设置当微信系统响应系统繁忙时,最大重试次数
+ * 默认:5次
+ *
+ * @param maxRetryTimes
+ */
+ void setMaxRetryTimes(int maxRetryTimes);
+
}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
index dbf2adc4..439c44df 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
@@ -10,6 +10,8 @@ import me.chanjar.weixin.common.bean.WxMenu;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.common.session.StandardSessionManager;
+import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.common.util.StringUtils;
import me.chanjar.weixin.common.util.crypto.SHA1;
import me.chanjar.weixin.common.util.fs.FileUtils;
@@ -31,32 +33,43 @@ import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
+import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.UUID;
-import java.util.concurrent.atomic.AtomicBoolean;
public class WxMpServiceImpl implements WxMpService {
+ protected final Logger log = LoggerFactory.getLogger(WxMpServiceImpl.class);
+
/**
- * 全局的是否正在刷新Access Token的flag
- * true: 正在刷新
- * false: 没有刷新
+ * 全局的是否正在刷新access token的锁
*/
- protected static final AtomicBoolean GLOBAL_ACCESS_TOKEN_REFRESH_FLAG = new AtomicBoolean(false);
-
+ protected final Object globalAccessTokenRefreshLock = new Object();
+
+ /**
+ * 全局的是否正在刷新jsapi_ticket的锁
+ */
+ protected final Object globalJsapiTicketRefreshLock = new Object();
+
protected WxMpConfigStorage wxMpConfigStorage;
- protected final ThreadLocal retryTimes = new ThreadLocal();
-
protected CloseableHttpClient httpClient;
protected HttpHost httpProxy;
+ private int retrySleepMillis = 1000;
+
+ private int maxRetryTimes = 5;
+
+ protected WxSessionManager sessionManager = new StandardSessionManager();
+
public boolean checkSignature(String timestamp, String nonce, String signature) {
try {
return SHA1.gen(wxMpConfigStorage.getToken(), timestamp, nonce).equals(signature);
@@ -64,49 +77,86 @@ public class WxMpServiceImpl implements WxMpService {
return false;
}
}
-
- public void accessTokenRefresh() throws WxErrorException {
- if (!GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.getAndSet(true)) {
- try {
- String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"
- + "&appid=" + wxMpConfigStorage.getAppId()
- + "&secret=" + wxMpConfigStorage.getSecret()
- ;
- try {
- HttpGet httpGet = new HttpGet(url);
- if (httpProxy != null) {
- RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
- httpGet.setConfig(config);
- }
- CloseableHttpClient httpclient = getHttpclient();
- CloseableHttpResponse response = httpclient.execute(httpGet);
- String resultContent = new BasicResponseHandler().handleResponse(response);
- WxError error = WxError.fromJson(resultContent);
- if (error.getErrorCode() != 0) {
- throw new WxErrorException(error);
+
+ public String getAccessToken() throws WxErrorException {
+ return getAccessToken(false);
+ }
+
+ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
+ if (forceRefresh) {
+ wxMpConfigStorage.expireAccessToken();
+ }
+ if (wxMpConfigStorage.isAccessTokenExpired()) {
+ synchronized (globalAccessTokenRefreshLock) {
+ if (wxMpConfigStorage.isAccessTokenExpired()) {
+ String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"
+ + "&appid=" + wxMpConfigStorage.getAppId()
+ + "&secret=" + wxMpConfigStorage.getSecret()
+ ;
+ try {
+ HttpGet httpGet = new HttpGet(url);
+ if (httpProxy != null) {
+ RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
+ httpGet.setConfig(config);
+ }
+ CloseableHttpClient httpclient = getHttpclient();
+ CloseableHttpResponse response = httpclient.execute(httpGet);
+ String resultContent = new BasicResponseHandler().handleResponse(response);
+ WxError error = WxError.fromJson(resultContent);
+ if (error.getErrorCode() != 0) {
+ throw new WxErrorException(error);
+ }
+ WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
+ wxMpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
+ } catch (ClientProtocolException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
}
- WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
- wxMpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
- } catch (ClientProtocolException e) {
- throw new RuntimeException(e);
- } catch (IOException e) {
- throw new RuntimeException(e);
}
- } finally {
- GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.set(false);
}
- } else {
- // 每隔100ms检查一下是否刷新完毕了
- while (GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.get()) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
+ }
+ return wxMpConfigStorage.getAccessToken();
+ }
+
+ public String getJsapiTicket() throws WxErrorException {
+ return getJsapiTicket(false);
+ }
+
+ public String getJsapiTicket(boolean forceRefresh) throws WxErrorException {
+ if (forceRefresh) {
+ wxMpConfigStorage.expireJsapiTicket();
+ }
+ if (wxMpConfigStorage.isJsapiTicketExpired()) {
+ synchronized (globalJsapiTicketRefreshLock) {
+ if (wxMpConfigStorage.isJsapiTicketExpired()) {
+ String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi";
+ String responseContent = execute(new SimpleGetRequestExecutor(), url, null);
+ JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
+ JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject();
+ String jsapiTicket = tmpJsonObject.get("ticket").getAsString();
+ int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt();
+ wxMpConfigStorage.updateJsapiTicket(jsapiTicket, expiresInSeconds);
}
}
- // 刷新完毕了,就没他什么事儿了
}
+ return wxMpConfigStorage.getJsapiTicket();
}
-
+
+ public String createJsapiSignature(long timestamp, String noncestr, String url) throws WxErrorException {
+ String jsapiTicket = getJsapiTicket(false);
+ try {
+ return SHA1.genWithAmple(
+ "jsapi_ticket=" + jsapiTicket,
+ "noncestr=" + noncestr,
+ "timestamp=" + timestamp,
+ "url=" + url
+ );
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
public void customMessageSend(WxMpCustomMessage message) throws WxErrorException {
String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send";
execute(new SimplePostRequestExecutor(), url, message.toJson());
@@ -399,7 +449,7 @@ public class WxMpServiceImpl implements WxMpService {
return execute(new SimplePostRequestExecutor(), url, postData);
}
- /**
+ /**
* 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求
* @param executor
* @param uri
@@ -408,10 +458,34 @@ public class WxMpServiceImpl implements WxMpService {
* @throws WxErrorException
*/
public T execute(RequestExecutor executor, String uri, E data) throws WxErrorException {
- if (StringUtils.isBlank(wxMpConfigStorage.getAccessToken())) {
- accessTokenRefresh();
- }
- String accessToken = wxMpConfigStorage.getAccessToken();
+ int retryTimes = 0;
+ do {
+ try {
+ return executeInternal(executor, uri, data);
+ } catch (WxErrorException e) {
+ WxError error = e.getError();
+ /**
+ * -1 系统繁忙, 1000ms后重试
+ */
+ if (error.getErrorCode() == -1) {
+ int sleepMillis = retrySleepMillis * (1 << retryTimes);
+ try {
+ log.debug("微信系统繁忙,{}ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
+ Thread.sleep(sleepMillis);
+ } catch (InterruptedException e1) {
+ throw new RuntimeException(e1);
+ }
+ } else {
+ throw e;
+ }
+ }
+ } while(++retryTimes < maxRetryTimes);
+
+ throw new RuntimeException("微信服务端异常,超出重试次数");
+ }
+
+ protected T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException {
+ String accessToken = getAccessToken(false);
String uriWithAccessToken = uri;
uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken;
@@ -426,30 +500,10 @@ public class WxMpServiceImpl implements WxMpService {
* 42001 access_token超时
*/
if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) {
- accessTokenRefresh();
+ // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token
+ wxMpConfigStorage.expireAccessToken();
return execute(executor, uri, data);
}
- /**
- * -1 系统繁忙, 1000ms后重试
- */
- if (error.getErrorCode() == -1) {
- if(retryTimes.get() == null) {
- retryTimes.set(0);
- }
- if (retryTimes.get() > 4) {
- retryTimes.set(0);
- throw new RuntimeException("微信服务端异常,超出重试次数");
- }
- int sleepMillis = 1000 * (1 << retryTimes.get());
- try {
- System.out.println("微信系统繁忙," + sleepMillis + "ms后重试");
- Thread.sleep(sleepMillis);
- retryTimes.set(retryTimes.get() + 1);
- return execute(executor, uri, data);
- } catch (InterruptedException e1) {
- throw new RuntimeException(e1);
- }
- }
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
@@ -495,4 +549,15 @@ public class WxMpServiceImpl implements WxMpService {
}
}
+ @Override
+ public void setRetrySleepMillis(int retrySleepMillis) {
+ this.retrySleepMillis = retrySleepMillis;
+ }
+
+
+ @Override
+ public void setMaxRetryTimes(int maxRetryTimes) {
+ this.maxRetryTimes = maxRetryTimes;
+ }
+
}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMpXmlOutImageMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMpXmlOutImageMessage.java
deleted file mode 100644
index 1db47004..00000000
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMpXmlOutImageMessage.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package me.chanjar.weixin.mp.bean;
-
-import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.util.xml.MediaIdMarshaller;
-
-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 javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
-public class WxMpMpXmlOutImageMessage extends WxMpXmlOutMessage {
-
- @XmlElement(name="Image")
- @XmlJavaTypeAdapter(MediaIdMarshaller.class)
- private String mediaId;
-
- public WxMpMpXmlOutImageMessage() {
- this.msgType = WxConsts.XML_MSG_IMAGE;
- }
-
- public String getMediaId() {
- return mediaId;
- }
-
- public void setMediaId(String mediaId) {
- this.mediaId = mediaId;
- }
-
-}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpTemplateMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpTemplateMessage.java
index cd6ac5f7..91441cd1 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpTemplateMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpTemplateMessage.java
@@ -5,9 +5,6 @@ import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
import java.util.ArrayList;
import java.util.List;
-/**
- * Created by qianjia on 14/11/1.
- */
public class WxMpTemplateMessage {
private String toUser;
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java
index 2470aff4..1996e4f2 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java
@@ -1,14 +1,13 @@
package me.chanjar.weixin.mp.bean;
-import me.chanjar.weixin.common.util.xml.AdapterCDATA;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
+import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.util.crypto.WxMpCryptUtil;
-import me.chanjar.weixin.mp.util.xml.XmlTransformer;
+import me.chanjar.weixin.mp.util.xml.XStreamTransformer;
import org.apache.commons.io.IOUtils;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.annotation.*;
-import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
@@ -25,100 +24,99 @@ import java.util.List;
*
* @author chanjarster
*/
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
+@XStreamAlias("xml")
public class WxMpXmlMessage {
///////////////////////
// 以下都是微信推送过来的消息的xml的element所对应的属性
///////////////////////
- @XmlElement(name = "ToUserName")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("ToUserName")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String toUserName;
- @XmlElement(name = "FromUserName")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("FromUserName")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String fromUserName;
- @XmlElement(name = "CreateTime")
+ @XStreamAlias("CreateTime")
private Long createTime;
- @XmlElement(name = "MsgType")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("MsgType")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String msgType;
- @XmlElement(name = "Content")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Content")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String content;
- @XmlElement(name = "MsgId")
+ @XStreamAlias("MsgId")
private Long msgId;
- @XmlElement(name = "PicUrl")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("PicUrl")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String picUrl;
- @XmlElement(name = "MediaId")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("MediaId")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String mediaId;
- @XmlElement(name = "Format")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Format")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String format;
- @XmlElement(name = "ThumbMediaId")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("ThumbMediaId")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String thumbMediaId;
- @XmlElement(name = "Location_X")
+ @XStreamAlias("Location_X")
private Double locationX;
- @XmlElement(name = "Location_Y")
+ @XStreamAlias("Location_Y")
private Double locationY;
- @XmlElement(name = "Scale")
+ @XStreamAlias("Scale")
private Double scale;
- @XmlElement(name = "Label")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Label")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String label;
- @XmlElement(name = "Title")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Title")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String title;
- @XmlElement(name = "Description")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Description")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String description;
- @XmlElement(name = "Url")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Url")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String url;
- @XmlElement(name = "Event")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Event")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String event;
- @XmlElement(name = "EventKey")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("EventKey")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String eventKey;
- @XmlElement(name = "Ticket")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Ticket")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String ticket;
- @XmlElement(name = "Latitude")
+ @XStreamAlias("Latitude")
private Double latitude;
- @XmlElement(name = "Longitude")
+ @XStreamAlias("Longitude")
private Double longitude;
- @XmlElement(name = "Precision")
+ @XStreamAlias("Precision")
private Double precision;
- @XmlElement(name = "Recognition")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Recognition")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String recognition;
///////////////////////////////////////
@@ -127,37 +125,37 @@ public class WxMpXmlMessage {
/**
* 群发的结果
*/
- @XmlElement(name = "Status")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Status")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String status;
/**
* group_id下粉丝数;或者openid_list中的粉丝数
*/
- @XmlElement(name = "TotalCount")
+ @XStreamAlias("TotalCount")
private Integer totalCount;
/**
* 过滤(过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数,原则上,filterCount = sentCount + errorCount
*/
- @XmlElement(name = "FilterCount")
+ @XStreamAlias("FilterCount")
private Integer filterCount;
/**
* 发送成功的粉丝数
*/
- @XmlElement(name = "SentCount")
+ @XStreamAlias("SentCount")
private Integer sentCount;
/**
* 发送失败的粉丝数
*/
- @XmlElement(name = "ErrorCount")
+ @XStreamAlias("ErrorCount")
private Integer errorCount;
- @XmlElement(name = "ScanCodeInfo")
+ @XStreamAlias("ScanCodeInfo")
private ScanCodeInfo scanCodeInfo = new ScanCodeInfo();
- @XmlElement(name = "SendPicsInfo")
+ @XStreamAlias("SendPicsInfo")
private SendPicsInfo sendPicsInfo = new SendPicsInfo();
- @XmlElement(name = "SendLocationInfo")
+ @XStreamAlias("SendLocationInfo")
private SendLocationInfo sendLocationInfo = new SendLocationInfo();
public String getToUserName() {
@@ -380,21 +378,11 @@ public class WxMpXmlMessage {
}
public static WxMpXmlMessage fromXml(String xml) {
- try {
- // 操蛋的微信,模板消息推送成功的消息是MsgID,其他消息推送过来是MsgId
- xml = xml.replaceAll("", "").replaceAll("", "");
- return XmlTransformer.fromXml(WxMpXmlMessage.class, xml);
- } catch (JAXBException e) {
- throw new RuntimeException(e);
- }
+ return XStreamTransformer.fromXml(WxMpXmlMessage.class, xml);
}
public static WxMpXmlMessage fromXml(InputStream is) {
- try {
- return fromXml(IOUtils.toString(is, "UTF-8"));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ return XStreamTransformer.fromXml(WxMpXmlMessage.class, is);
}
/**
@@ -491,16 +479,15 @@ public class WxMpXmlMessage {
this.sendLocationInfo = sendLocationInfo;
}
- @XmlRootElement(name = "ScanCodeInfo")
- @XmlAccessorType(XmlAccessType.FIELD)
+ @XStreamAlias("ScanCodeInfo")
public static class ScanCodeInfo {
- @XmlElement(name = "ScanType")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("ScanType")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String scanType;
- @XmlElement(name = "ScanResult")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("ScanResult")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String scanResult;
/**
@@ -530,15 +517,13 @@ public class WxMpXmlMessage {
}
- @XmlRootElement(name = "SendPicsInfo")
- @XmlAccessorType(XmlAccessType.FIELD)
+ @XStreamAlias("SendPicsInfo")
public static class SendPicsInfo {
- @XmlElement(name = "Count")
+ @XStreamAlias("Count")
private Long count;
- @XmlElementWrapper(name="PicList")
- @XmlElement(name = "item")
+ @XStreamAlias("PicList")
protected final List- picList = new ArrayList
- ();
public Long getCount() {
@@ -553,47 +538,44 @@ public class WxMpXmlMessage {
return picList;
}
- @XmlRootElement(name = "item")
- @XmlAccessorType(XmlAccessType.FIELD)
- @XmlType(name = "WxXmlMessage.SendPicsInfo.Item")
+ @XStreamAlias("item")
public static class Item {
- @XmlElement(name = "PicMd5Sum")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
- private String PicMd5Sum;
+ @XStreamAlias("PicMd5Sum")
+ @XStreamConverter(value=XStreamCDataConverter.class)
+ private String picMd5Sum;
public String getPicMd5Sum() {
- return PicMd5Sum;
+ return picMd5Sum;
}
public void setPicMd5Sum(String picMd5Sum) {
- PicMd5Sum = picMd5Sum;
+ this.picMd5Sum = picMd5Sum;
}
}
}
- @XmlRootElement(name = "SendLocationInfo")
- @XmlAccessorType(XmlAccessType.FIELD)
+ @XStreamAlias("SendLocationInfo")
public static class SendLocationInfo {
- @XmlElement(name = "Location_X")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Location_X")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String locationX;
- @XmlElement(name = "Location_Y")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Location_Y")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String locationY;
- @XmlElement(name = "Scale")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Scale")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String scale;
- @XmlElement(name = "Label")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Label")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String label;
- @XmlElement(name = "Poiname")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Poiname")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String poiname;
public String getLocationX() {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutImageMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutImageMessage.java
new file mode 100644
index 00000000..6f6510c2
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutImageMessage.java
@@ -0,0 +1,27 @@
+package me.chanjar.weixin.mp.bean;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter;
+
+@XStreamAlias("xml")
+public class WxMpXmlOutImageMessage extends WxMpXmlOutMessage {
+
+ @XStreamAlias("Image")
+ @XStreamConverter(value = XStreamMediaIdConverter.class)
+ private String mediaId;
+
+ public String getMediaId() {
+ return mediaId;
+ }
+
+ public void setMediaId(String mediaId) {
+ this.mediaId = mediaId;
+ }
+
+ public WxMpXmlOutImageMessage() {
+ this.msgType = WxConsts.XML_MSG_IMAGE;
+ }
+
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutMessage.java
index b9e4507d..a7534bde 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutMessage.java
@@ -1,35 +1,29 @@
package me.chanjar.weixin.mp.bean;
-import me.chanjar.weixin.common.util.xml.AdapterCDATA;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
+import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.bean.outxmlbuilder.*;
import me.chanjar.weixin.mp.util.crypto.WxMpCryptUtil;
-import me.chanjar.weixin.mp.util.xml.XmlTransformer;
+import me.chanjar.weixin.mp.util.xml.XStreamTransformer;
-import javax.xml.bind.JAXBException;
-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 javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+@XStreamAlias("xml")
+public abstract class WxMpXmlOutMessage {
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
-public class WxMpXmlOutMessage {
-
- @XmlElement(name="ToUserName")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("ToUserName")
+ @XStreamConverter(value=XStreamCDataConverter.class)
protected String toUserName;
- @XmlElement(name="FromUserName")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("FromUserName")
+ @XStreamConverter(value=XStreamCDataConverter.class)
protected String fromUserName;
- @XmlElement(name="CreateTime")
+ @XStreamAlias("CreateTime")
protected Long createTime;
- @XmlElement(name="MsgType")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("MsgType")
+ @XStreamConverter(value=XStreamCDataConverter.class)
protected String msgType;
public String getToUserName() {
@@ -65,11 +59,7 @@ public class WxMpXmlOutMessage {
}
public String toXml() {
- try {
- return XmlTransformer.toXml((Class)this.getClass(), this);
- } catch (JAXBException e) {
- throw new RuntimeException(e);
- }
+ return XStreamTransformer.toXml((Class) this.getClass(), this);
}
/**
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutMusicMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutMusicMessage.java
index 782b5dbb..b6fefdac 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutMusicMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutMusicMessage.java
@@ -1,19 +1,14 @@
package me.chanjar.weixin.mp.bean;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.util.xml.AdapterCDATA;
+import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
-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 javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
+@XStreamAlias("xml")
public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage {
- @XmlElement(name = "Music")
+ @XStreamAlias("Music")
protected final Music music = new Music();
public WxMpXmlOutMusicMessage() {
@@ -60,28 +55,27 @@ public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage {
music.setHqMusicUrl(hqMusicUrl);
}
- @XmlRootElement(name = "Music")
- @XmlAccessorType(XmlAccessType.FIELD)
- private static class Music {
+ @XStreamAlias("Music")
+ public static class Music {
- @XmlElement(name = "Title")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Title")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String title;
- @XmlElement(name = "Description")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Description")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String description;
- @XmlElement(name="ThumbMediaId")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("ThumbMediaId")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String thumbMediaId;
- @XmlElement(name="MusicUrl")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("MusicUrl")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String musicUrl;
- @XmlElement(name="HQMusicUrl")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("HQMusicUrl")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String hqMusicUrl;
public String getTitle() {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutNewsMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutNewsMessage.java
index 4730a99b..4b76b4f5 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutNewsMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutNewsMessage.java
@@ -1,22 +1,20 @@
package me.chanjar.weixin.mp.bean;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.util.xml.AdapterCDATA;
+import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
-import javax.xml.bind.annotation.*;
-import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.ArrayList;
import java.util.List;
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
+@XStreamAlias("xml")
public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage {
- @XmlElement(name = "ArticleCount")
+ @XStreamAlias("ArticleCount")
protected int articleCount;
- @XmlElementWrapper(name="Articles")
- @XmlElement(name = "item")
+ @XStreamAlias("Articles")
protected final List
- articles = new ArrayList
- ();
public WxMpXmlOutNewsMessage() {
@@ -37,24 +35,23 @@ public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage {
}
- @XmlRootElement(name = "Item")
- @XmlAccessorType(XmlAccessType.FIELD)
+ @XStreamAlias("item")
public static class Item {
- @XmlElement(name = "Title")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Title")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String Title;
- @XmlElement(name = "Description")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Description")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String Description;
- @XmlElement(name="PicUrl")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("PicUrl")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String PicUrl;
- @XmlElement(name="Url")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Url")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String Url;
public String getTitle() {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutTextMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutTextMessage.java
index 3542e07b..e2458a03 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutTextMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutTextMessage.java
@@ -1,20 +1,15 @@
package me.chanjar.weixin.mp.bean;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.util.xml.AdapterCDATA;
+import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
-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 javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
+@XStreamAlias("xml")
public class WxMpXmlOutTextMessage extends WxMpXmlOutMessage {
- @XmlElement(name="Content")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Content")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String content;
public WxMpXmlOutTextMessage() {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutVideoMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutVideoMessage.java
index 4b192cf7..fb0520c1 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutVideoMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutVideoMessage.java
@@ -1,19 +1,14 @@
package me.chanjar.weixin.mp.bean;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.util.xml.AdapterCDATA;
+import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
-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 javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
+@XStreamAlias("xml")
public class WxMpXmlOutVideoMessage extends WxMpXmlOutMessage {
- @XmlElement(name = "Video")
+ @XStreamAlias("Video")
protected final Video video = new Video();
public WxMpXmlOutVideoMessage() {
@@ -45,20 +40,19 @@ public class WxMpXmlOutVideoMessage extends WxMpXmlOutMessage {
}
- @XmlRootElement(name = "Video")
- @XmlAccessorType(XmlAccessType.FIELD)
- private static class Video {
+ @XStreamAlias("Video")
+ public static class Video {
- @XmlElement(name = "MediaId")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("MediaId")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String mediaId;
- @XmlElement(name = "Title")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Title")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String title;
- @XmlElement(name = "Description")
- @XmlJavaTypeAdapter(AdapterCDATA.class)
+ @XStreamAlias("Description")
+ @XStreamConverter(value=XStreamCDataConverter.class)
private String description;
public String getMediaId() {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutVoiceMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutVoiceMessage.java
index 59f164ba..29c74e90 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutVoiceMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutVoiceMessage.java
@@ -1,26 +1,21 @@
package me.chanjar.weixin.mp.bean;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamConverter;
import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.util.xml.MediaIdMarshaller;
+import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter;
-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 javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
+@XStreamAlias("xml")
public class WxMpXmlOutVoiceMessage extends WxMpXmlOutMessage {
-
- @XmlElement(name="Voice")
- @XmlJavaTypeAdapter(MediaIdMarshaller.class)
+
+ @XStreamAlias("Voice")
+ @XStreamConverter(value = XStreamMediaIdConverter.class)
private String mediaId;
public WxMpXmlOutVoiceMessage() {
this.msgType = WxConsts.XML_MSG_VOICE;
}
-
+
public String getMediaId() {
return mediaId;
}
@@ -28,5 +23,5 @@ public class WxMpXmlOutVoiceMessage extends WxMpXmlOutMessage {
public void setMediaId(String mediaId) {
this.mediaId = mediaId;
}
-
+
}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/ImageBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/ImageBuilder.java
index f127406a..e263e57f 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/ImageBuilder.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/ImageBuilder.java
@@ -1,12 +1,12 @@
package me.chanjar.weixin.mp.bean.outxmlbuilder;
-import me.chanjar.weixin.mp.bean.WxMpMpXmlOutImageMessage;
+import me.chanjar.weixin.mp.bean.WxMpXmlOutImageMessage;
/**
* 图片消息builder
* @author chanjarster
*/
-public final class ImageBuilder extends BaseBuilder {
+public final class ImageBuilder extends BaseBuilder {
private String mediaId;
@@ -15,8 +15,8 @@ public final class ImageBuilder extends BaseBuilder {
public JsonElement serialize(WxMpCustomMessage message, Type typeOfSrc, JsonSerializationContext context) {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGroupGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGroupGsonAdapter.java
index 63f0624e..58e6f98d 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGroupGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGroupGsonAdapter.java
@@ -14,11 +14,6 @@ import me.chanjar.weixin.mp.bean.WxMpGroup;
import java.lang.reflect.Type;
-/**
- *
- * @author qianjia
- *
- */
public class WxMpGroupGsonAdapter implements JsonSerializer, JsonDeserializer {
public JsonElement serialize(WxMpGroup group, Type typeOfSrc, JsonSerializationContext context) {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java
index 40854843..73083d8c 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java
@@ -4,7 +4,6 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import me.chanjar.weixin.mp.bean.*;
import me.chanjar.weixin.mp.bean.result.*;
-import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
public class WxMpGsonBuilder {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassGroupMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassGroupMessageGsonAdapter.java
index a0077469..79a9a0c6 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassGroupMessageGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassGroupMessageGsonAdapter.java
@@ -17,11 +17,6 @@ import me.chanjar.weixin.mp.bean.WxMpMassGroupMessage;
import java.lang.reflect.Type;
-/**
- *
- * @author qianjia
- *
- */
public class WxMpMassGroupMessageGsonAdapter implements JsonSerializer {
public JsonElement serialize(WxMpMassGroupMessage message, Type typeOfSrc, JsonSerializationContext context) {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassNewsGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassNewsGsonAdapter.java
index 5ba78594..1719b75a 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassNewsGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassNewsGsonAdapter.java
@@ -13,11 +13,6 @@ import me.chanjar.weixin.mp.bean.WxMpMassNews;
import java.lang.reflect.Type;
-/**
- *
- * @author qianjia
- *
- */
public class WxMpMassNewsGsonAdapter implements JsonSerializer {
public JsonElement serialize(WxMpMassNews message, Type typeOfSrc, JsonSerializationContext context) {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassOpenIdsMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassOpenIdsMessageGsonAdapter.java
index cd03addf..84063da4 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassOpenIdsMessageGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassOpenIdsMessageGsonAdapter.java
@@ -14,11 +14,6 @@ import me.chanjar.weixin.mp.bean.WxMpMassOpenIdsMessage;
import java.lang.reflect.Type;
-/**
- *
- * @author qianjia
- *
- */
public class WxMpMassOpenIdsMessageGsonAdapter implements JsonSerializer {
public JsonElement serialize(WxMpMassOpenIdsMessage message, Type typeOfSrc, JsonSerializationContext context) {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpOAuth2AccessTokenAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpOAuth2AccessTokenAdapter.java
index 7ea7a35e..6d73131a 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpOAuth2AccessTokenAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpOAuth2AccessTokenAdapter.java
@@ -1,15 +1,11 @@
package me.chanjar.weixin.mp.util.json;
import com.google.gson.*;
-import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.util.json.GsonHelper;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import java.lang.reflect.Type;
-/**
- * Created by qianjia on 14/11/28.
- */
public class WxMpOAuth2AccessTokenAdapter implements JsonDeserializer {
public WxMpOAuth2AccessToken deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpSemanticQueryResultAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpSemanticQueryResultAdapter.java
index c0d1e805..822357df 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpSemanticQueryResultAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpSemanticQueryResultAdapter.java
@@ -10,8 +10,6 @@ package me.chanjar.weixin.mp.util.json;
import com.google.gson.*;
import me.chanjar.weixin.common.util.json.GsonHelper;
-import me.chanjar.weixin.mp.bean.result.WxMpMassSendResult;
-import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult;
import java.lang.reflect.Type;
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpTemplateMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpTemplateMessageGsonAdapter.java
index ad603d3b..c34ac2c2 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpTemplateMessageGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpTemplateMessageGsonAdapter.java
@@ -8,17 +8,15 @@
*/
package me.chanjar.weixin.mp.util.json;
-import com.google.gson.*;
-import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.mp.bean.WxMpCustomMessage;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
import me.chanjar.weixin.mp.bean.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.WxMpTemplateMessage;
import java.lang.reflect.Type;
-/**
- * @author qianjia
- */
public class WxMpTemplateMessageGsonAdapter implements JsonSerializer {
public JsonElement serialize(WxMpTemplateMessage message, Type typeOfSrc, JsonSerializationContext context) {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserGsonAdapter.java
index cbbc3080..462994f6 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserGsonAdapter.java
@@ -14,11 +14,6 @@ import me.chanjar.weixin.mp.bean.result.WxMpUser;
import java.lang.reflect.Type;
-/**
- *
- * @author qianjia
- *
- */
public class WxUserGsonAdapter implements JsonDeserializer {
public WxMpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java
index ec65735e..5be3d160 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java
@@ -14,11 +14,6 @@ import me.chanjar.weixin.mp.bean.result.WxMpUserList;
import java.lang.reflect.Type;
-/**
- *
- * @author qianjia
- *
- */
public class WxUserListGsonAdapter implements JsonDeserializer {
public WxMpUserList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java
new file mode 100644
index 00000000..7b20c6f1
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java
@@ -0,0 +1,114 @@
+package me.chanjar.weixin.mp.util.xml;
+
+import com.thoughtworks.xstream.XStream;
+import me.chanjar.weixin.common.util.xml.XStreamInitializer;
+import me.chanjar.weixin.mp.bean.*;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+public class XStreamTransformer {
+
+ protected static final Map CLASS_2_XSTREAM_INSTANCE = configXStreamInstance();
+
+ /**
+ * xml -> pojo
+ *
+ * @param clazz
+ * @param xml
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public static T fromXml(Class clazz, String xml) {
+ T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(xml);
+ return object;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T fromXml(Class clazz, InputStream is) {
+ T object = (T) CLASS_2_XSTREAM_INSTANCE.get(clazz).fromXML(is);
+ return object;
+ }
+
+ /**
+ * pojo -> xml
+ *
+ * @param clazz
+ * @param object
+ * @return
+ */
+ public static String toXml(Class clazz, T object) {
+ return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object);
+ }
+
+ private static Map configXStreamInstance() {
+ Map map = new HashMap();
+ map.put(WxMpXmlMessage.class, config_WxMpXmlMessage());
+ map.put(WxMpXmlOutMusicMessage.class, config_WxMpXmlOutMusicMessage());
+ map.put(WxMpXmlOutNewsMessage.class, config_WxMpXmlOutNewsMessage());
+ map.put(WxMpXmlOutTextMessage.class, config_WxMpXmlOutTextMessage());
+ map.put(WxMpXmlOutImageMessage.class, config_WxMpXmlOutImageMessage());
+ map.put(WxMpXmlOutVideoMessage.class, config_WxMpXmlOutVideoMessage());
+ map.put(WxMpXmlOutVoiceMessage.class, config_WxMpXmlOutVoiceMessage());
+ return map;
+ }
+
+ private static XStream config_WxMpXmlMessage() {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxMpXmlMessage.class);
+ xstream.processAnnotations(WxMpXmlMessage.ScanCodeInfo.class);
+ xstream.processAnnotations(WxMpXmlMessage.SendPicsInfo.class);
+ xstream.processAnnotations(WxMpXmlMessage.SendPicsInfo.Item.class);
+ xstream.processAnnotations(WxMpXmlMessage.SendLocationInfo.class);
+
+ xstream.aliasField("MsgID", WxMpXmlMessage.class, "msgId");
+ return xstream;
+ }
+
+ private static XStream config_WxMpXmlOutImageMessage() {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxMpXmlOutMessage.class);
+ xstream.processAnnotations(WxMpXmlOutImageMessage.class);
+ return xstream;
+ }
+
+ private static XStream config_WxMpXmlOutNewsMessage() {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxMpXmlOutMessage.class);
+ xstream.processAnnotations(WxMpXmlOutNewsMessage.class);
+ xstream.processAnnotations(WxMpXmlOutNewsMessage.Item.class);
+ return xstream;
+ }
+
+ private static XStream config_WxMpXmlOutMusicMessage() {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxMpXmlOutMessage.class);
+ xstream.processAnnotations(WxMpXmlOutMusicMessage.class);
+ xstream.processAnnotations(WxMpXmlOutMusicMessage.Music.class);
+ return xstream;
+ }
+
+ private static XStream config_WxMpXmlOutTextMessage() {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxMpXmlOutMessage.class);
+ xstream.processAnnotations(WxMpXmlOutTextMessage.class);
+ return xstream;
+ }
+
+ private static XStream config_WxMpXmlOutVideoMessage() {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxMpXmlOutMessage.class);
+ xstream.processAnnotations(WxMpXmlOutVideoMessage.class);
+ xstream.processAnnotations(WxMpXmlOutVideoMessage.Video.class);
+ return xstream;
+ }
+
+ private static XStream config_WxMpXmlOutVoiceMessage() {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxMpXmlOutMessage.class);
+ xstream.processAnnotations(WxMpXmlOutVoiceMessage.class);
+ return xstream;
+ }
+
+}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XmlTransformer.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XmlTransformer.java
deleted file mode 100644
index bff84eab..00000000
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XmlTransformer.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package me.chanjar.weixin.mp.util.xml;
-
-import com.sun.xml.bind.marshaller.CharacterEscapeHandler;
-import me.chanjar.weixin.mp.bean.*;
-import org.xml.sax.InputSource;
-
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Marshaller;
-import javax.xml.bind.Unmarshaller;
-import java.io.*;
-
-public class XmlTransformer {
-
- protected static final JAXBContext JAXB_CONTEXT = initJAXBContext();
-
- /**
- * xml -> pojo
- *
- * @param clazz
- * @param xml
- * @return
- * @throws JAXBException
- */
- @SuppressWarnings("unchecked")
- public static T fromXml(Class clazz, String xml) throws JAXBException {
- Unmarshaller um = JAXB_CONTEXT.createUnmarshaller();
- T object = (T) um.unmarshal(new StringReader(xml));
- return object;
- }
-
- @SuppressWarnings("unchecked")
- public static T fromXml(Class clazz, InputStream is) throws JAXBException {
- Unmarshaller um = JAXB_CONTEXT.createUnmarshaller();
- InputSource inputSource = new InputSource(is);
- inputSource.setEncoding("utf-8");
- T object = (T) um.unmarshal(inputSource);
- return object;
- }
-
- /**
- * pojo -> xml
- *
- * @param clazz
- * @param object
- * @return
- * @throws JAXBException
- */
- public static String toXml(Class clazz, T object) throws JAXBException {
- StringWriter stringWriter = new StringWriter();
- toXml(clazz, object, stringWriter);
- return stringWriter.getBuffer().toString();
- }
-
- public static void toXml(Class clazz, T object, Writer writer) throws JAXBException {
- Marshaller m = JAXB_CONTEXT.createMarshaller();
- m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
- m.setProperty(CharacterEscapeHandler.class.getName(), CHAR_ESCAPE_HANDLER);
- m.setProperty(Marshaller.JAXB_FRAGMENT, true);
- m.marshal(object, writer);
- }
-
- protected static final CharacterEscapeHandler CHAR_ESCAPE_HANDLER = new CharacterUnescapeHandler();
-
- protected static class CharacterUnescapeHandler implements CharacterEscapeHandler {
- public void escape(char[] ac, int i, int j, boolean flag, Writer writer) throws IOException {
- writer.write(ac, i, j);
- }
- }
-
- private static JAXBContext initJAXBContext() {
- /*
- * JAXBContext对象是线程安全的,根据官方文档的建议将对象作为全局实例
- * https://jaxb.java.net/guide/Performance_and_thread_safety.html
- */
- try {
- return JAXBContext.newInstance(
- WxMpXmlOutMessage.class,
- WxMpMpXmlOutImageMessage.class,
- WxMpXmlOutNewsMessage.class,
- WxMpXmlOutMusicMessage.class,
- WxMpXmlOutTextMessage.class,
- WxMpXmlOutVideoMessage.class,
- WxMpXmlOutVoiceMessage.class,
- WxMpXmlMessage.class);
- } catch (JAXBException e) {
- throw new RuntimeException(e);
- }
- }
-
-}
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java
index ca4cbc13..e6df8e34 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java
@@ -1,46 +1,35 @@
package me.chanjar.weixin.mp.api;
-import java.io.InputStream;
-
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlRootElement;
-
import com.google.inject.Binder;
import com.google.inject.Module;
-import org.xml.sax.InputSource;
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import me.chanjar.weixin.common.util.xml.XStreamInitializer;
+
+import java.io.InputStream;
public class ApiTestModule implements Module {
@Override
public void configure(Binder binder) {
- try {
- InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml");
- WxXmlMpInMemoryConfigStorage config = fromXml(WxXmlMpInMemoryConfigStorage.class, is1);
- WxMpServiceImpl wxService = new WxMpServiceImpl();
- wxService.setWxMpConfigStorage(config);
-
- binder.bind(WxMpServiceImpl.class).toInstance(wxService);
- binder.bind(WxMpConfigStorage.class).toInstance(config);
- } catch (JAXBException e) {
- throw new RuntimeException(e);
- }
+ InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml");
+ WxXmlMpInMemoryConfigStorage config = fromXml(WxXmlMpInMemoryConfigStorage.class, is1);
+ WxMpServiceImpl wxService = new WxMpServiceImpl();
+ wxService.setWxMpConfigStorage(config);
+
+ binder.bind(WxMpServiceImpl.class).toInstance(wxService);
+ binder.bind(WxMpConfigStorage.class).toInstance(config);
}
- public static T fromXml(Class clazz, InputStream is) throws JAXBException {
- Unmarshaller um = JAXBContext.newInstance(clazz).createUnmarshaller();
- InputSource inputSource = new InputSource(is);
- inputSource.setEncoding("utf-8");
- T object = (T) um.unmarshal(inputSource);
- return object;
+ public static T fromXml(Class clazz, InputStream is) {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.alias("xml", clazz);
+ xstream.processAnnotations(clazz);
+ return (T) xstream.fromXML(is);
}
- @XmlRootElement(name = "xml")
- @XmlAccessorType(XmlAccessType.FIELD)
- public static class WxXmlMpInMemoryConfigStorage extends WxMpInMemoryConfigStorage {
+ @XStreamAlias("xml")
+ public static class WxXmlMpInMemoryConfigStorage extends WxMpInMemoryConfigStorage {
protected String openId;
@@ -53,7 +42,7 @@ public class ApiTestModule implements Module {
@Override
public String toString() {
return "SimpleWxConfigProvider [appId=" + appId + ", secret=" + secret + ", accessToken=" + accessToken
- + ", expiresIn=" + expiresIn + ", token=" + token + ", openId=" + openId + "]";
+ + ", expiresTime=" + expiresTime + ", token=" + token + ", openId=" + openId + "]";
}
}
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxBaseAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java
similarity index 92%
rename from weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxBaseAPITest.java
rename to weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java
index f87d97a4..c4210b57 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxBaseAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java
@@ -1,14 +1,12 @@
package me.chanjar.weixin.mp.api;
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.StringUtils;
import org.testng.Assert;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import me.chanjar.weixin.common.exception.WxErrorException;
-
-import com.google.inject.Inject;
-
/**
* 基础API测试
* @author chanjarster
@@ -16,7 +14,7 @@ import com.google.inject.Inject;
*/
@Test(groups = "baseAPI")
@Guice(modules = ApiTestModule.class)
-public class WxBaseAPITest {
+public class WxMpBaseAPITest {
@Inject
protected WxMpServiceImpl wxService;
@@ -24,10 +22,9 @@ public class WxBaseAPITest {
public void testRefreshAccessToken() throws WxErrorException {
WxMpConfigStorage configStorage = wxService.wxMpConfigStorage;
String before = configStorage.getAccessToken();
- wxService.accessTokenRefresh();
+ wxService.getAccessToken(false);
String after = configStorage.getAccessToken();
-
Assert.assertNotEquals(before, after);
Assert.assertTrue(StringUtils.isNotBlank(after));
}
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java
new file mode 100644
index 00000000..2e16723a
--- /dev/null
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java
@@ -0,0 +1,66 @@
+package me.chanjar.weixin.mp.api;
+
+import me.chanjar.weixin.common.bean.result.WxError;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.common.util.http.RequestExecutor;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+@Test
+public class WxMpBusyRetryTest {
+
+ @DataProvider(name="getService")
+ public Object[][] getService() {
+ WxMpService service = new WxMpServiceImpl() {
+
+ @Override
+ protected T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException {
+ WxError error = new WxError();
+ error.setErrorCode(-1);
+ throw new WxErrorException(error);
+ }
+ };
+
+ service.setMaxRetryTimes(3);
+ service.setRetrySleepMillis(500);
+ return new Object[][] {
+ new Object[] { service }
+ };
+ }
+
+ @Test(dataProvider = "getService", expectedExceptions = RuntimeException.class)
+ public void testRetry(WxMpService service) throws WxErrorException {
+ service.execute(null, null, null);
+ }
+
+ @Test(dataProvider = "getService")
+ public void testRetryInThreadPool(final WxMpService service) throws InterruptedException, ExecutionException {
+ // 当线程池中的线程复用的时候,还是能保证相同的重试次数
+ ExecutorService executorService = Executors.newFixedThreadPool(1);
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ System.out.println("=====================");
+ System.out.println(Thread.currentThread().getName() + ": testRetry");
+ service.execute(null, null, null);
+ } catch (WxErrorException e) {
+ throw new RuntimeException(e);
+ } catch (RuntimeException e) {
+ // OK
+ }
+ }
+ };
+ Future> submit1 = executorService.submit(runnable);
+ Future> submit2 = executorService.submit(runnable);
+
+ submit1.get();
+ submit2.get();
+ }
+
+}
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpCustomMessageAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpCustomMessageAPITest.java
index 04e8400e..d0d9cde2 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpCustomMessageAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpCustomMessageAPITest.java
@@ -1,14 +1,12 @@
package me.chanjar.weixin.mp.api;
+import com.google.inject.Inject;
import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.bean.WxMpCustomMessage;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import me.chanjar.weixin.common.exception.WxErrorException;
-
-import com.google.inject.Inject;
-
/***
* 测试发送客服消息
* @author chanjarster
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpGroupAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpGroupAPITest.java
index ca6f20ed..4397e057 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpGroupAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpGroupAPITest.java
@@ -1,15 +1,13 @@
package me.chanjar.weixin.mp.api;
-import java.util.List;
-
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.bean.WxMpGroup;
import org.testng.Assert;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import me.chanjar.weixin.common.exception.WxErrorException;
-
-import com.google.inject.Inject;
+import java.util.List;
/**
* 测试分组接口
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpJsAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpJsAPITest.java
new file mode 100644
index 00000000..16506caa
--- /dev/null
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpJsAPITest.java
@@ -0,0 +1,48 @@
+package me.chanjar.weixin.mp.api;
+
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.common.util.crypto.SHA1;
+import me.chanjar.weixin.mp.bean.WxMpGroup;
+import org.testng.Assert;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+
+/**
+ * 测试jsapi ticket接口
+ *
+ * @author chanjarster
+ */
+@Test(groups = "jsAPI", dependsOnGroups = "baseAPI")
+@Guice(modules = ApiTestModule.class)
+public class WxMpJsAPITest {
+
+ @Inject
+ protected WxMpServiceImpl wxService;
+
+
+ public void testJsapiTicket() throws WxErrorException {
+ String jsapiTicket = wxService.getJsapiTicket(false);
+ System.out.println(jsapiTicket);
+ Assert.assertNotNull(jsapiTicket);
+ }
+
+ public void test() throws NoSuchAlgorithmException {
+ long timestamp = 1419835025l;
+ String url = "http://omstest.vmall.com:23568/thirdparty/wechat/vcode/gotoshare?quantity=1&batchName=MATE7";
+ String noncestr = "82693e11-b9bc-448e-892f-f5289f46cd0f";
+ String jsapiTicket = "bxLdikRXVbTPdHSM05e5u4RbEYQn7pNQMPrfzl8lJNb1foLDa3HIwI3BRMkQmSO_5F64VFa75uURcq6Uz7QHgA";
+ String result = SHA1.genWithAmple(
+ "jsapi_ticket=" + jsapiTicket,
+ "noncestr=" + noncestr,
+ "timestamp=" + timestamp,
+ "url=" + url
+ );
+
+ Assert.assertEquals(result, "c6f04b64d6351d197b71bd23fb7dd2d44c0db486");
+ }
+
+}
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java
index 78a48c2f..73ebd1e2 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java
@@ -1,10 +1,9 @@
package me.chanjar.weixin.mp.api;
-import java.io.IOException;
-import java.io.InputStream;
-
+import com.google.inject.Inject;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
+import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.bean.WxMpMassGroupMessage;
import me.chanjar.weixin.mp.bean.WxMpMassNews;
import me.chanjar.weixin.mp.bean.WxMpMassOpenIdsMessage;
@@ -16,9 +15,8 @@ import org.testng.annotations.DataProvider;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import me.chanjar.weixin.common.exception.WxErrorException;
-
-import com.google.inject.Inject;
+import java.io.IOException;
+import java.io.InputStream;
/**
* 测试群发消息
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMediaAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMediaAPITest.java
index e012df40..0df7103d 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMediaAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMediaAPITest.java
@@ -1,20 +1,18 @@
package me.chanjar.weixin.mp.api;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
+import com.google.inject.Inject;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
+import me.chanjar.weixin.common.exception.WxErrorException;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import me.chanjar.weixin.common.exception.WxErrorException;
-
-import com.google.inject.Inject;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
/**
* 测试多媒体文件上传下载
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMenuAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMenuAPITest.java
index 335442b7..01acb179 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMenuAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMenuAPITest.java
@@ -1,19 +1,15 @@
package me.chanjar.weixin.mp.api;
-import javax.xml.bind.JAXBException;
-
+import com.google.inject.Inject;
import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.bean.WxMenu;
+import me.chanjar.weixin.common.bean.WxMenu.WxMenuButton;
+import me.chanjar.weixin.common.exception.WxErrorException;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import com.google.inject.Inject;
-
-import me.chanjar.weixin.common.bean.WxMenu;
-import me.chanjar.weixin.common.bean.WxMenu.WxMenuButton;
-import me.chanjar.weixin.common.exception.WxErrorException;
-
/**
* 测试菜单
* @author chanjarster
@@ -42,7 +38,7 @@ public class WxMpMenuAPITest {
}
@DataProvider(name="menu")
- public Object[][] getMenu() throws JAXBException {
+ public Object[][] getMenu() {
WxMenu menu = new WxMenu();
WxMenuButton button1 = new WxMenuButton();
button1.setType(WxConsts.BUTTON_CLICK);
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java
index b3c2460b..0b636985 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java
@@ -1,14 +1,16 @@
package me.chanjar.weixin.mp.api;
-import java.util.Map;
-
import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.session.StandardSessionManager;
+import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
+import java.util.Map;
+
/**
* 测试消息路由器
* @author chanjarster
@@ -67,7 +69,8 @@ public class WxMpMessageRouterTest {
final WxMpMessageRouter router = new WxMpMessageRouter(null);
router.rule().handler(new WxMpMessageHandler() {
@Override
- public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, WxMpService wxMpService) {
+ public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, WxMpService wxMpService,
+ WxSessionManager sessionManager) {
return null;
}
}).end();
@@ -149,11 +152,144 @@ public class WxMpMessageRouterTest {
}
@Override
- public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, WxMpService wxMpService) {
+ public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, WxMpService wxMpService,
+ WxSessionManager sessionManager) {
sb.append(this.echoStr).append(',');
return null;
}
}
+ @DataProvider
+ public Object[][] standardSessionManager() {
+
+ // 故意把session存活时间变短,清理更频繁
+ StandardSessionManager ism = new StandardSessionManager();
+ ism.setMaxInactiveInterval(1);
+ ism.setProcessExpiresFrequency(1);
+ ism.setBackgroundProcessorDelay(1);
+
+ return new Object[][] {
+ new Object[] { ism }
+ };
+
+ }
+
+ @Test(dataProvider = "standardSessionManager")
+ public void testSessionClean1(StandardSessionManager ism) throws InterruptedException {
+
+ // 两个同步请求,看是否处理完毕后会被清理掉
+ final WxMpMessageRouter router = new WxMpMessageRouter(null);
+ router.setSessionManager(ism);
+ router
+ .rule().async(false).handler(new WxSessionMessageHandler()).next()
+ .rule().async(false).handler(new WxSessionMessageHandler()).end();
+
+ WxMpXmlMessage msg = new WxMpXmlMessage();
+ msg.setFromUserName("abc");
+ router.route(msg);
+
+ Thread.sleep(2000l);
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+
+ }
+
+ @Test(dataProvider = "standardSessionManager")
+ public void testSessionClean2(StandardSessionManager ism) throws InterruptedException {
+
+ // 1个同步,1个异步请求,看是否处理完毕后会被清理掉
+ {
+ final WxMpMessageRouter router = new WxMpMessageRouter(null);
+ router.setSessionManager(ism);
+ router
+ .rule().async(false).handler(new WxSessionMessageHandler()).next()
+ .rule().async(true).handler(new WxSessionMessageHandler()).end();
+
+ WxMpXmlMessage msg = new WxMpXmlMessage();
+ msg.setFromUserName("abc");
+ router.route(msg);
+
+ Thread.sleep(2000l);
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+ }
+ {
+ final WxMpMessageRouter router = new WxMpMessageRouter(null);
+ router.setSessionManager(ism);
+ router
+ .rule().async(true).handler(new WxSessionMessageHandler()).next()
+ .rule().async(false).handler(new WxSessionMessageHandler()).end();
+
+ WxMpXmlMessage msg = new WxMpXmlMessage();
+ msg.setFromUserName("abc");
+ router.route(msg);
+
+ Thread.sleep(2000l);
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+ }
+
+ }
+
+ @Test(dataProvider = "standardSessionManager")
+ public void testSessionClean3(StandardSessionManager ism) throws InterruptedException {
+
+ // 2个异步请求,看是否处理完毕后会被清理掉
+ final WxMpMessageRouter router = new WxMpMessageRouter(null);
+ router.setSessionManager(ism);
+ router
+ .rule().async(true).handler(new WxSessionMessageHandler()).next()
+ .rule().async(true).handler(new WxSessionMessageHandler()).end();
+
+ WxMpXmlMessage msg = new WxMpXmlMessage();
+ msg.setFromUserName("abc");
+ router.route(msg);
+
+ Thread.sleep(2000l);
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+
+ }
+
+ @Test(dataProvider = "standardSessionManager")
+ public void testSessionClean4(StandardSessionManager ism) throws InterruptedException {
+
+ // 一个同步请求,看是否处理完毕后会被清理掉
+ {
+ final WxMpMessageRouter router = new WxMpMessageRouter(null);
+ router.setSessionManager(ism);
+ router
+ .rule().async(false).handler(new WxSessionMessageHandler()).end();
+
+ WxMpXmlMessage msg = new WxMpXmlMessage();
+ msg.setFromUserName("abc");
+ router.route(msg);
+
+ Thread.sleep(2000l);
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+ }
+
+ {
+ final WxMpMessageRouter router = new WxMpMessageRouter(null);
+ router.setSessionManager(ism);
+ router
+ .rule().async(true).handler(new WxSessionMessageHandler()).end();
+
+ WxMpXmlMessage msg = new WxMpXmlMessage();
+ msg.setFromUserName("abc");
+ router.route(msg);
+
+ Thread.sleep(2000l);
+ Assert.assertEquals(ism.getActiveSessions(), 0);
+ }
+ }
+
+ public static class WxSessionMessageHandler implements WxMpMessageHandler {
+
+ @Override
+ public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, WxMpService wxMpService,
+ WxSessionManager sessionManager) {
+ sessionManager.getSession(wxMessage.getFromUserName());
+ return null;
+ }
+
+ }
+
}
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpQrCodeAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpQrCodeAPITest.java
index 2a206d2e..1fa76e49 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpQrCodeAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpQrCodeAPITest.java
@@ -1,15 +1,13 @@
package me.chanjar.weixin.mp.api;
-import java.io.File;
-
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
import org.testng.Assert;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import me.chanjar.weixin.common.exception.WxErrorException;
-
-import com.google.inject.Inject;
+import java.io.File;
/**
* 测试用户相关的接口
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpShortUrlAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpShortUrlAPITest.java
index e854ed08..fb97f2d4 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpShortUrlAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpShortUrlAPITest.java
@@ -1,13 +1,11 @@
package me.chanjar.weixin.mp.api;
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.exception.WxErrorException;
import org.testng.Assert;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import me.chanjar.weixin.common.exception.WxErrorException;
-
-import com.google.inject.Inject;
-
/**
* 测试短连接
*
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpUserAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpUserAPITest.java
index c29bda83..fa978c57 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpUserAPITest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpUserAPITest.java
@@ -1,15 +1,13 @@
package me.chanjar.weixin.mp.api;
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.mp.bean.result.WxMpUser;
import me.chanjar.weixin.mp.bean.result.WxMpUserList;
import org.testng.Assert;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import me.chanjar.weixin.mp.bean.result.WxMpUser;
-import me.chanjar.weixin.common.exception.WxErrorException;
-
-import com.google.inject.Inject;
-
/**
* 测试用户相关的接口
* @author chanjarster
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpCustomMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpCustomMessageTest.java
index 5e3d5620..aa8de0df 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpCustomMessageTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpCustomMessageTest.java
@@ -1,11 +1,10 @@
package me.chanjar.weixin.mp.bean;
import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.mp.bean.WxMpCustomMessage.WxArticle;
import org.testng.Assert;
import org.testng.annotations.Test;
-import me.chanjar.weixin.mp.bean.WxMpCustomMessage.WxArticle;
-
@Test
public class WxMpCustomMessageTest {
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpXmlOutImageMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpXmlOutImageMessageTest.java
index f4b50e1d..9c92cab8 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpXmlOutImageMessageTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpXmlOutImageMessageTest.java
@@ -7,7 +7,7 @@ import org.testng.annotations.Test;
public class WxMpXmlOutImageMessageTest {
public void test() {
- WxMpMpXmlOutImageMessage m = new WxMpMpXmlOutImageMessage();
+ WxMpXmlOutImageMessage m = new WxMpXmlOutImageMessage();
m.setMediaId("ddfefesfsdfef");
m.setCreateTime(1122l);
m.setFromUserName("from");
@@ -25,7 +25,7 @@ public class WxMpXmlOutImageMessageTest {
}
public void testBuild() {
- WxMpMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build();
+ WxMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build();
String expected = ""
+ ""
+ ""
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java
index 430d327d..becfbeb4 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java
@@ -1,35 +1,29 @@
package me.chanjar.weixin.mp.demo;
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import me.chanjar.weixin.common.util.xml.XStreamInitializer;
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
-import org.xml.sax.InputSource;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlRootElement;
import java.io.InputStream;
/**
* @author Daniel Qian
*/
-@XmlRootElement(name = "xml")
-@XmlAccessorType(XmlAccessType.FIELD)
+@XStreamAlias("xml")
class WxMpDemoInMemoryConfigStorage extends WxMpInMemoryConfigStorage {
@Override
public String toString() {
return "SimpleWxConfigProvider [appId=" + appId + ", secret=" + secret + ", accessToken=" + accessToken
- + ", expiresIn=" + expiresIn + ", token=" + token + ", aesKey=" + aesKey + "]";
+ + ", expiresTime=" + expiresTime + ", token=" + token + ", aesKey=" + aesKey + "]";
}
- public static WxMpDemoInMemoryConfigStorage fromXml(InputStream is) throws JAXBException {
- Unmarshaller um = JAXBContext.newInstance(WxMpDemoInMemoryConfigStorage.class).createUnmarshaller();
- InputSource inputSource = new InputSource(is);
- inputSource.setEncoding("utf-8");
- return (WxMpDemoInMemoryConfigStorage) um.unmarshal(inputSource);
+ public static WxMpDemoInMemoryConfigStorage fromXml(InputStream is) {
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.processAnnotations(WxMpDemoInMemoryConfigStorage.class);
+ return (WxMpDemoInMemoryConfigStorage) xstream.fromXML(is);
}
}
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServer.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServer.java
index 8fc8008f..8e44d2db 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServer.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoServer.java
@@ -3,15 +3,16 @@ package me.chanjar.weixin.mp.demo;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.*;
-import me.chanjar.weixin.mp.bean.WxMpMpXmlOutImageMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.WxMpXmlOutImageMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage;
import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.servlet.*;
+import org.eclipse.jetty.servlet.ServletHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
-import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
@@ -41,7 +42,6 @@ public class WxMpDemoServer {
}
private static void initWeixin() {
- try {
InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml");
WxMpDemoInMemoryConfigStorage config = WxMpDemoInMemoryConfigStorage.fromXml(is1);
@@ -52,7 +52,7 @@ public class WxMpDemoServer {
WxMpMessageHandler textHandler = new WxMpMessageHandler() {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context,
- WxMpService wxMpService) {
+ WxMpService wxMpService, WxSessionManager sessionManager) {
WxMpXmlOutTextMessage m
= WxMpXmlOutMessage.TEXT().content("测试加密消息").fromUser(wxMessage.getToUserName())
.toUser(wxMessage.getFromUserName()).build();
@@ -63,11 +63,11 @@ public class WxMpDemoServer {
WxMpMessageHandler imageHandler = new WxMpMessageHandler() {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context,
- WxMpService wxMpService) {
+ WxMpService wxMpService, WxSessionManager sessionManager) {
try {
WxMediaUploadResult wxMediaUploadResult = wxMpService
.mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, ClassLoader.getSystemResourceAsStream("mm.jpeg"));
- WxMpMpXmlOutImageMessage m
+ WxMpXmlOutImageMessage m
= WxMpXmlOutMessage
.IMAGE()
.mediaId(wxMediaUploadResult.getMediaId())
@@ -87,7 +87,7 @@ public class WxMpDemoServer {
WxMpMessageHandler oauth2handler = new WxMpMessageHandler() {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context,
- WxMpService wxMpService) {
+ WxMpService wxMpService, WxSessionManager sessionManager) {
String href = "测试oauth2";
return WxMpXmlOutMessage
@@ -117,8 +117,5 @@ public class WxMpDemoServer {
.end()
;
- } catch (JAXBException e) {
- throw new RuntimeException(e);
- }
}
}
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpEndpointServlet.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpEndpointServlet.java
index 4a88ede9..36bc77ee 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpEndpointServlet.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpEndpointServlet.java
@@ -1,23 +1,17 @@
package me.chanjar.weixin.mp.demo;
-import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
-import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.StringUtils;
-import me.chanjar.weixin.mp.api.*;
-import me.chanjar.weixin.mp.bean.WxMpMpXmlOutImageMessage;
+import me.chanjar.weixin.mp.api.WxMpConfigStorage;
+import me.chanjar.weixin.mp.api.WxMpMessageRouter;
+import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
-import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import javax.xml.bind.JAXBException;
import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map;
/**
* @author Daniel Qian
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpOAuth2Servlet.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpOAuth2Servlet.java
index 6ddf4ad3..e0291b10 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpOAuth2Servlet.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpOAuth2Servlet.java
@@ -1,26 +1,15 @@
package me.chanjar.weixin.mp.demo;
-import me.chanjar.weixin.common.api.WxConsts;
-import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.exception.WxErrorException;
-import me.chanjar.weixin.common.util.StringUtils;
-import me.chanjar.weixin.mp.api.*;
-import me.chanjar.weixin.mp.bean.WxMpMpXmlOutImageMessage;
-import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
-import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
-import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage;
+import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
-import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import javax.xml.bind.JAXBException;
import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map;
public class WxMpOAuth2Servlet extends HttpServlet {
diff --git a/weixin-java-mp/src/test/resources/logback-test.xml b/weixin-java-mp/src/test/resources/logback-test.xml
new file mode 100644
index 00000000..ed595101
--- /dev/null
+++ b/weixin-java-mp/src/test/resources/logback-test.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
diff --git a/weixin-java-mp/src/test/resources/test-config.sample.xml b/weixin-java-mp/src/test/resources/test-config.sample.xml
index 19535f3c..6ec4825a 100644
--- a/weixin-java-mp/src/test/resources/test-config.sample.xml
+++ b/weixin-java-mp/src/test/resources/test-config.sample.xml
@@ -4,7 +4,7 @@
公众号Token
公众号EncodingAESKey
可以不填写
- 可以不填写
+ 可以不填写
某个加你公众号的用户的openId
网页授权获取用户信息回调地址
diff --git a/weixin-java-mp/src/test/resources/testng.xml b/weixin-java-mp/src/test/resources/testng.xml
index 8987f762..cf9c2d64 100644
--- a/weixin-java-mp/src/test/resources/testng.xml
+++ b/weixin-java-mp/src/test/resources/testng.xml
@@ -3,7 +3,8 @@
-
+
+
@@ -13,6 +14,7 @@
+