From 08ab083e57fabe328fbed27d6dca667bcd2dfb29 Mon Sep 17 00:00:00 2001 From: jinyu Date: Sun, 27 Mar 2016 19:14:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=97=A5=E5=BF=97=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGE.md | 4 +- weixin4j-base/CHANGE.md | 32 +- weixin4j-base/pom.xml | 6 + .../weixin4j/http/AbstractHttpClient.java | 5 + .../weixin4j/http/SimpleHttpClient.java | 15 + .../http/weixin/WeixinRequestExecutor.java | 16 +- .../logging/AbstractInternalLogger.java | 190 +++++ .../weixin4j/logging/FormattingTuple.java | 88 +++ .../weixin4j/logging/InternalLogLevel.java | 42 ++ .../weixin4j/logging/InternalLogger.java | 444 ++++++++++++ .../logging/InternalLoggerFactory.java | 88 +++ .../foxinmy/weixin4j/logging/JdkLogger.java | 647 ++++++++++++++++++ .../weixin4j/logging/JdkLoggerFactory.java | 32 + .../weixin4j/logging/MessageFormatter.java | 428 ++++++++++++ .../foxinmy/weixin4j/logging/Slf4JLogger.java | 183 +++++ .../weixin4j/logging/Slf4JLoggerFactory.java | 69 ++ .../com/foxinmy/weixin4j/util/StringUtil.java | 29 + .../weixin4j/base/test/HttpClientTest.java | 9 +- .../src/main/resources/weixin4j.properties | 2 - 19 files changed, 2313 insertions(+), 16 deletions(-) create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/AbstractInternalLogger.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/FormattingTuple.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/InternalLogLevel.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/InternalLogger.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/InternalLoggerFactory.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/JdkLogger.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/JdkLoggerFactory.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/MessageFormatter.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/Slf4JLogger.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/Slf4JLoggerFactory.java diff --git a/CHANGE.md b/CHANGE.md index f25e651e..2af9e97b 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -665,4 +665,6 @@ + weixin4j-base:支付对象优化 - + weixin4j-base:新增海关接口 \ No newline at end of file + + weixin4j-base:新增海关接口 + + + weixin4j-base:添加日志支持 \ No newline at end of file diff --git a/weixin4j-base/CHANGE.md b/weixin4j-base/CHANGE.md index f96e4f33..688ce874 100644 --- a/weixin4j-base/CHANGE.md +++ b/weixin4j-base/CHANGE.md @@ -115,4 +115,34 @@ + 新增MemoryTokenStorager(内存保存token)类 - + TokenStorager类新增evict和clear接口 \ No newline at end of file + + TokenStorager类新增evict和clear接口 + +* 2016-01-29 + + + 新增Weixin4jSettings配置类 + +* 2016-03-22 + + + 企业付款相关类更名 + +* 2016-03-25 + + + v2和v3支付改名 + + + 支持服务商版支付 + + + 签名类接口化 + + + 新增查询结算金额接口 + + + 新增查询汇率接口 + +* 2016-03-27 + + + 删除Mciro支付接口,新增MCIROPayRequest对象 + + + 支付对象优化 + + + 新增海关接口 + + + 添加日志支持 \ No newline at end of file diff --git a/weixin4j-base/pom.xml b/weixin4j-base/pom.xml index 0301d918..c1e46a25 100644 --- a/weixin4j-base/pom.xml +++ b/weixin4j-base/pom.xml @@ -45,5 +45,11 @@ 2.6.0 true + + org.slf4j + slf4j-api + 1.7.19 + true + \ No newline at end of file diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpClient.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpClient.java index 4c09a706..23cc1390 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpClient.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpClient.java @@ -5,9 +5,14 @@ import java.util.Set; import com.foxinmy.weixin4j.http.entity.FormUrlEntity; import com.foxinmy.weixin4j.http.entity.HttpEntity; +import com.foxinmy.weixin4j.logging.InternalLogger; +import com.foxinmy.weixin4j.logging.InternalLoggerFactory; public abstract class AbstractHttpClient implements HttpClient { + protected final InternalLogger logger = InternalLoggerFactory + .getInstance(getClass()); + @Override public HttpResponse get(String url) throws HttpClientException { return execute(HttpMethod.GET, url); diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpClient.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpClient.java index 203db0a0..1842c5c3 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpClient.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpClient.java @@ -117,6 +117,8 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient { if (!headers.containsKey(HttpHeaders.USER_AGENT)) { headers.set(HttpHeaders.USER_AGENT, "jdk/httpclient"); } + logger.debug("request >> " + request.getMethod() + " " + + request.getURI().toString()); for (Iterator>> headerIterator = headers .entrySet().iterator(); headerIterator.hasNext();) { Entry> header = headerIterator.next(); @@ -129,6 +131,8 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient { headerValue != null ? headerValue : ""); } } + logger.debug("headers >> " + header.getKey() + ":" + + StringUtil.join(header.getValue(), ';')); } // set inputstream HttpEntity httpEntity = request.getEntity(); @@ -150,6 +154,8 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient { connection.setRequestProperty(HttpHeaders.CONTENT_TYPE, httpEntity.getContentType().getMimeType()); } + logger.debug("entity >> " + httpEntity.getContentType() + "(" + + httpEntity.getContentLength() + "byte)"); } // connect connection.connect(); @@ -165,6 +171,15 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient { .getErrorStream() : connection.getInputStream(); byte[] content = IOUtil.toByteArray(input); response = new SimpleHttpResponse(connection, content); + logger.debug("response << " + response.getProtocol() + + response.getStatus().toString()); + for (Iterator>> headerIterator = response + .getHeaders().entrySet().iterator(); headerIterator + .hasNext();) { + Entry> header = headerIterator.next(); + logger.debug("headers << " + header.getKey() + ":" + + StringUtil.join(header.getValue(), ';')); + } input.close(); handleResponse(response); } catch (IOException e) { diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java index 58e14d64..bed6bae3 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java @@ -19,6 +19,8 @@ import com.foxinmy.weixin4j.http.entity.FormUrlEntity; import com.foxinmy.weixin4j.http.entity.HttpEntity; import com.foxinmy.weixin4j.http.entity.StringEntity; import com.foxinmy.weixin4j.http.factory.HttpClientFactory; +import com.foxinmy.weixin4j.logging.InternalLogger; +import com.foxinmy.weixin4j.logging.InternalLoggerFactory; import com.foxinmy.weixin4j.model.Consts; import com.foxinmy.weixin4j.util.StringUtil; import com.foxinmy.weixin4j.util.WeixinErrorUtil; @@ -35,6 +37,9 @@ import com.foxinmy.weixin4j.xml.XmlStream; */ public class WeixinRequestExecutor { + protected final InternalLogger logger = InternalLoggerFactory + .getInstance(getClass()); + protected final HttpClient httpClient; protected final HttpParams params; @@ -93,9 +98,14 @@ public class WeixinRequestExecutor { public WeixinResponse doRequest(HttpRequest request) throws WeixinException { request.setParams(params); try { + logger.info("weixin request >> " + request.getMethod() + " " + + request.getURI().toString()); HttpResponse httpResponse = httpClient.execute(request); HttpHeaders headers = httpResponse.getHeaders(); WeixinResponse response = new WeixinResponse(httpResponse); + logger.info("weixin response << " + httpResponse.getProtocol() + + httpResponse.getStatus().toString() + " " + + response.getAsString()); String contentType = headers.getFirst(HttpHeaders.CONTENT_TYPE); String disposition = headers .getFirst(HttpHeaders.CONTENT_DISPOSITION); @@ -161,13 +171,11 @@ public class WeixinRequestExecutor { if ("0".equals(xmlResult.getReturnCode())) { return; } - if (!Consts.SUCCESS - .equalsIgnoreCase(xmlResult.getReturnCode())) { + if (!Consts.SUCCESS.equalsIgnoreCase(xmlResult.getReturnCode())) { throw new WeixinException(xmlResult.getReturnCode(), xmlResult.getReturnMsg()); } - if (!Consts.SUCCESS - .equalsIgnoreCase(xmlResult.getResultCode())) { + if (!Consts.SUCCESS.equalsIgnoreCase(xmlResult.getResultCode())) { throw new WeixinException(xmlResult.getErrCode(), xmlResult.getErrCodeDes()); } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/AbstractInternalLogger.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/AbstractInternalLogger.java new file mode 100644 index 00000000..9cb6d8d7 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/AbstractInternalLogger.java @@ -0,0 +1,190 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project 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 com.foxinmy.weixin4j.logging; + +import java.io.ObjectStreamException; +import java.io.Serializable; + +import com.foxinmy.weixin4j.util.StringUtil; + +/** + * A skeletal implementation of {@link InternalLogger}. This class implements + * all methods that have a {@link InternalLogLevel} parameter by default to call + * specific logger methods such as {@link #info(String)} or {@link #isInfoEnabled()}. + */ +public abstract class AbstractInternalLogger implements InternalLogger, Serializable { + + private static final long serialVersionUID = -6382972526573193470L; + + private final String name; + + /** + * Creates a new instance. + */ + protected AbstractInternalLogger(String name) { + if (name == null) { + throw new NullPointerException("name"); + } + this.name = name; + } + + @Override + public String name() { + return name; + } + + @Override + public boolean isEnabled(InternalLogLevel level) { + switch (level) { + case TRACE: + return isTraceEnabled(); + case DEBUG: + return isDebugEnabled(); + case INFO: + return isInfoEnabled(); + case WARN: + return isWarnEnabled(); + case ERROR: + return isErrorEnabled(); + default: + throw new Error(); + } + } + + @Override + public void log(InternalLogLevel level, String msg, Throwable cause) { + switch (level) { + case TRACE: + trace(msg, cause); + break; + case DEBUG: + debug(msg, cause); + break; + case INFO: + info(msg, cause); + break; + case WARN: + warn(msg, cause); + break; + case ERROR: + error(msg, cause); + break; + default: + throw new Error(); + } + } + + @Override + public void log(InternalLogLevel level, String msg) { + switch (level) { + case TRACE: + trace(msg); + break; + case DEBUG: + debug(msg); + break; + case INFO: + info(msg); + break; + case WARN: + warn(msg); + break; + case ERROR: + error(msg); + break; + default: + throw new Error(); + } + } + + @Override + public void log(InternalLogLevel level, String format, Object arg) { + switch (level) { + case TRACE: + trace(format, arg); + break; + case DEBUG: + debug(format, arg); + break; + case INFO: + info(format, arg); + break; + case WARN: + warn(format, arg); + break; + case ERROR: + error(format, arg); + break; + default: + throw new Error(); + } + } + + @Override + public void log(InternalLogLevel level, String format, Object argA, Object argB) { + switch (level) { + case TRACE: + trace(format, argA, argB); + break; + case DEBUG: + debug(format, argA, argB); + break; + case INFO: + info(format, argA, argB); + break; + case WARN: + warn(format, argA, argB); + break; + case ERROR: + error(format, argA, argB); + break; + default: + throw new Error(); + } + } + + @Override + public void log(InternalLogLevel level, String format, Object... arguments) { + switch (level) { + case TRACE: + trace(format, arguments); + break; + case DEBUG: + debug(format, arguments); + break; + case INFO: + info(format, arguments); + break; + case WARN: + warn(format, arguments); + break; + case ERROR: + error(format, arguments); + break; + default: + throw new Error(); + } + } + + protected Object readResolve() throws ObjectStreamException { + return InternalLoggerFactory.getInstance(name()); + } + + @Override + public String toString() { + return StringUtil.simpleClassName(this) + '(' + name() + ')'; + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/FormattingTuple.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/FormattingTuple.java new file mode 100644 index 00000000..0dc1ec80 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/FormattingTuple.java @@ -0,0 +1,88 @@ +/* + * Copyright 2013 The Netty Project + * + * The Netty Project 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. + */ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package com.foxinmy.weixin4j.logging; + +/** + * Holds the results of formatting done by {@link MessageFormatter}. + */ +class FormattingTuple { + + static final FormattingTuple NULL = new FormattingTuple(null); + + private final String message; + private final Throwable throwable; + private final Object[] argArray; + + FormattingTuple(String message) { + this(message, null, null); + } + + FormattingTuple(String message, Object[] argArray, Throwable throwable) { + this.message = message; + this.throwable = throwable; + if (throwable == null) { + this.argArray = argArray; + } else { + this.argArray = trimmedCopy(argArray); + } + } + + static Object[] trimmedCopy(Object[] argArray) { + if (argArray == null || argArray.length == 0) { + throw new IllegalStateException("non-sensical empty or null argument array"); + } + final int trimemdLen = argArray.length - 1; + Object[] trimmed = new Object[trimemdLen]; + System.arraycopy(argArray, 0, trimmed, 0, trimemdLen); + return trimmed; + } + + public String getMessage() { + return message; + } + + public Object[] getArgArray() { + return argArray; + } + + public Throwable getThrowable() { + return throwable; + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/InternalLogLevel.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/InternalLogLevel.java new file mode 100644 index 00000000..c74e1b8d --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/InternalLogLevel.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project 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 com.foxinmy.weixin4j.logging; + +/** + * The log level that {@link InternalLogger} can log at. + */ +public enum InternalLogLevel { + /** + * 'TRACE' log level. + */ + TRACE, + /** + * 'DEBUG' log level. + */ + DEBUG, + /** + * 'INFO' log level. + */ + INFO, + /** + * 'WARN' log level. + */ + WARN, + /** + * 'ERROR' log level. + */ + ERROR +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/InternalLogger.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/InternalLogger.java new file mode 100644 index 00000000..eb739686 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/InternalLogger.java @@ -0,0 +1,444 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project 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. + */ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package com.foxinmy.weixin4j.logging; + +/** + * Internal-use-only logger used by Netty. DO NOT + * access this class outside of Netty. + */ +public interface InternalLogger { + + /** + * Return the name of this {@link InternalLogger} instance. + * + * @return name of this logger instance + */ + String name(); + + /** + * Is the logger instance enabled for the TRACE level? + * + * @return True if this Logger is enabled for the TRACE level, + * false otherwise. + */ + boolean isTraceEnabled(); + + /** + * Log a message at the TRACE level. + * + * @param msg the message string to be logged + */ + void trace(String msg); + + /** + * Log a message at the TRACE level according to the specified format + * and argument. + *

+ *

This form avoids superfluous object creation when the logger + * is disabled for the TRACE level.

+ * + * @param format the format string + * @param arg the argument + */ + void trace(String format, Object arg); + + /** + * Log a message at the TRACE level according to the specified format + * and arguments. + *

+ *

This form avoids superfluous object creation when the logger + * is disabled for the TRACE level.

+ * + * @param format the format string + * @param argA the first argument + * @param argB the second argument + */ + void trace(String format, Object argA, Object argB); + + /** + * Log a message at the TRACE level according to the specified format + * and arguments. + *

+ *

This form avoids superfluous string concatenation when the logger + * is disabled for the TRACE level. However, this variant incurs the hidden + * (and relatively small) cost of creating an {@code Object[]} before invoking the method, + * even if this logger is disabled for TRACE. The variants taking {@link #trace(String, Object) one} and + * {@link #trace(String, Object, Object) two} arguments exist solely in order to avoid this hidden cost.

+ * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + void trace(String format, Object... arguments); + + /** + * Log an exception (throwable) at the TRACE level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + void trace(String msg, Throwable t); + + /** + * Is the logger instance enabled for the DEBUG level? + * + * @return True if this Logger is enabled for the DEBUG level, + * false otherwise. + */ + boolean isDebugEnabled(); + + /** + * Log a message at the DEBUG level. + * + * @param msg the message string to be logged + */ + void debug(String msg); + + /** + * Log a message at the DEBUG level according to the specified format + * and argument. + *

+ *

This form avoids superfluous object creation when the logger + * is disabled for the DEBUG level.

+ * + * @param format the format string + * @param arg the argument + */ + void debug(String format, Object arg); + + /** + * Log a message at the DEBUG level according to the specified format + * and arguments. + *

+ *

This form avoids superfluous object creation when the logger + * is disabled for the DEBUG level.

+ * + * @param format the format string + * @param argA the first argument + * @param argB the second argument + */ + void debug(String format, Object argA, Object argB); + + /** + * Log a message at the DEBUG level according to the specified format + * and arguments. + *

+ *

This form avoids superfluous string concatenation when the logger + * is disabled for the DEBUG level. However, this variant incurs the hidden + * (and relatively small) cost of creating an {@code Object[]} before invoking the method, + * even if this logger is disabled for DEBUG. The variants taking + * {@link #debug(String, Object) one} and {@link #debug(String, Object, Object) two} + * arguments exist solely in order to avoid this hidden cost.

+ * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + void debug(String format, Object... arguments); + + /** + * Log an exception (throwable) at the DEBUG level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + void debug(String msg, Throwable t); + + /** + * Is the logger instance enabled for the INFO level? + * + * @return True if this Logger is enabled for the INFO level, + * false otherwise. + */ + boolean isInfoEnabled(); + + /** + * Log a message at the INFO level. + * + * @param msg the message string to be logged + */ + void info(String msg); + + /** + * Log a message at the INFO level according to the specified format + * and argument. + *

+ *

This form avoids superfluous object creation when the logger + * is disabled for the INFO level.

+ * + * @param format the format string + * @param arg the argument + */ + void info(String format, Object arg); + + /** + * Log a message at the INFO level according to the specified format + * and arguments. + *

+ *

This form avoids superfluous object creation when the logger + * is disabled for the INFO level.

+ * + * @param format the format string + * @param argA the first argument + * @param argB the second argument + */ + void info(String format, Object argA, Object argB); + + /** + * Log a message at the INFO level according to the specified format + * and arguments. + *

+ *

This form avoids superfluous string concatenation when the logger + * is disabled for the INFO level. However, this variant incurs the hidden + * (and relatively small) cost of creating an {@code Object[]} before invoking the method, + * even if this logger is disabled for INFO. The variants taking + * {@link #info(String, Object) one} and {@link #info(String, Object, Object) two} + * arguments exist solely in order to avoid this hidden cost.

+ * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + void info(String format, Object... arguments); + + /** + * Log an exception (throwable) at the INFO level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + void info(String msg, Throwable t); + + /** + * Is the logger instance enabled for the WARN level? + * + * @return True if this Logger is enabled for the WARN level, + * false otherwise. + */ + boolean isWarnEnabled(); + + /** + * Log a message at the WARN level. + * + * @param msg the message string to be logged + */ + void warn(String msg); + + /** + * Log a message at the WARN level according to the specified format + * and argument. + *

+ *

This form avoids superfluous object creation when the logger + * is disabled for the WARN level.

+ * + * @param format the format string + * @param arg the argument + */ + void warn(String format, Object arg); + + /** + * Log a message at the WARN level according to the specified format + * and arguments. + *

+ *

This form avoids superfluous string concatenation when the logger + * is disabled for the WARN level. However, this variant incurs the hidden + * (and relatively small) cost of creating an {@code Object[]} before invoking the method, + * even if this logger is disabled for WARN. The variants taking + * {@link #warn(String, Object) one} and {@link #warn(String, Object, Object) two} + * arguments exist solely in order to avoid this hidden cost.

+ * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + void warn(String format, Object... arguments); + + /** + * Log a message at the WARN level according to the specified format + * and arguments. + *

+ *

This form avoids superfluous object creation when the logger + * is disabled for the WARN level.

+ * + * @param format the format string + * @param argA the first argument + * @param argB the second argument + */ + void warn(String format, Object argA, Object argB); + + /** + * Log an exception (throwable) at the WARN level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + void warn(String msg, Throwable t); + + /** + * Is the logger instance enabled for the ERROR level? + * + * @return True if this Logger is enabled for the ERROR level, + * false otherwise. + */ + boolean isErrorEnabled(); + + /** + * Log a message at the ERROR level. + * + * @param msg the message string to be logged + */ + void error(String msg); + + /** + * Log a message at the ERROR level according to the specified format + * and argument. + *

+ *

This form avoids superfluous object creation when the logger + * is disabled for the ERROR level.

+ * + * @param format the format string + * @param arg the argument + */ + void error(String format, Object arg); + + /** + * Log a message at the ERROR level according to the specified format + * and arguments. + *

+ *

This form avoids superfluous object creation when the logger + * is disabled for the ERROR level.

+ * + * @param format the format string + * @param argA the first argument + * @param argB the second argument + */ + void error(String format, Object argA, Object argB); + + /** + * Log a message at the ERROR level according to the specified format + * and arguments. + *

+ *

This form avoids superfluous string concatenation when the logger + * is disabled for the ERROR level. However, this variant incurs the hidden + * (and relatively small) cost of creating an {@code Object[]} before invoking the method, + * even if this logger is disabled for ERROR. The variants taking + * {@link #error(String, Object) one} and {@link #error(String, Object, Object) two} + * arguments exist solely in order to avoid this hidden cost.

+ * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + void error(String format, Object... arguments); + + /** + * Log an exception (throwable) at the ERROR level with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + void error(String msg, Throwable t); + + /** + * Is the logger instance enabled for the specified {@code level}? + * + * @return True if this Logger is enabled for the specified {@code level}, + * false otherwise. + */ + boolean isEnabled(InternalLogLevel level); + + /** + * Log a message at the specified {@code level}. + * + * @param msg the message string to be logged + */ + void log(InternalLogLevel level, String msg); + + /** + * Log a message at the specified {@code level} according to the specified format + * and argument. + *

+ *

This form avoids superfluous object creation when the logger + * is disabled for the specified {@code level}.

+ * + * @param format the format string + * @param arg the argument + */ + void log(InternalLogLevel level, String format, Object arg); + + /** + * Log a message at the specified {@code level} according to the specified format + * and arguments. + *

+ *

This form avoids superfluous object creation when the logger + * is disabled for the specified {@code level}.

+ * + * @param format the format string + * @param argA the first argument + * @param argB the second argument + */ + void log(InternalLogLevel level, String format, Object argA, Object argB); + + /** + * Log a message at the specified {@code level} according to the specified format + * and arguments. + *

+ *

This form avoids superfluous string concatenation when the logger + * is disabled for the specified {@code level}. However, this variant incurs the hidden + * (and relatively small) cost of creating an {@code Object[]} before invoking the method, + * even if this logger is disabled for the specified {@code level}. The variants taking + * {@link #log(InternalLogLevel, String, Object) one} and + * {@link #log(InternalLogLevel, String, Object, Object) two} arguments exist solely + * in order to avoid this hidden cost.

+ * + * @param format the format string + * @param arguments a list of 3 or more arguments + */ + void log(InternalLogLevel level, String format, Object... arguments); + + /** + * Log an exception (throwable) at the specified {@code level} with an + * accompanying message. + * + * @param msg the message accompanying the exception + * @param t the exception (throwable) to log + */ + void log(InternalLogLevel level, String msg, Throwable t); +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/InternalLoggerFactory.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/InternalLoggerFactory.java new file mode 100644 index 00000000..f29d389b --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/InternalLoggerFactory.java @@ -0,0 +1,88 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project 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 com.foxinmy.weixin4j.logging; + +/** + * Creates an {@link InternalLogger} or changes the default factory + * implementation. This factory allows you to choose what logging framework + * Netty should use. The default factory is {@link Slf4JLoggerFactory}. If SLF4J + * is not available, {@link JdkLoggerFactory} is used. You can change it to your + * preferred logging framework before other Netty classes are loaded: + * + *
+ * {@link InternalLoggerFactory}.setDefaultFactory(new {@link Log4JLoggerFactory}());
+ * 
+ * + * Please note that the new default factory is effective only for the classes + * which were loaded after the default factory is changed. Therefore, + * {@link #setDefaultFactory(InternalLoggerFactory)} should be called as early + * as possible and shouldn't be called more than once. + */ +public abstract class InternalLoggerFactory { + private static volatile InternalLoggerFactory defaultFactory = newDefaultFactory(InternalLoggerFactory.class + .getName()); + + private static InternalLoggerFactory newDefaultFactory(String name) { + InternalLoggerFactory f; + try { + f = new Slf4JLoggerFactory(true); + f.newInstance(name).debug( + "Using SLF4J as the default logging framework"); + } catch (Throwable t1) { + f = new JdkLoggerFactory(); + f.newInstance(name).debug( + "Using java.util.logging as the default logging framework"); + } + return f; + } + + /** + * Returns the default factory. The initial default factory is + * {@link JdkLoggerFactory}. + */ + public static InternalLoggerFactory getDefaultFactory() { + return defaultFactory; + } + + /** + * Changes the default factory. + */ + public static void setDefaultFactory(InternalLoggerFactory defaultFactory) { + if (defaultFactory == null) { + throw new NullPointerException("defaultFactory"); + } + InternalLoggerFactory.defaultFactory = defaultFactory; + } + + /** + * Creates a new logger instance with the name of the specified class. + */ + public static InternalLogger getInstance(Class clazz) { + return getInstance(clazz.getName()); + } + + /** + * Creates a new logger instance with the specified name. + */ + public static InternalLogger getInstance(String name) { + return getDefaultFactory().newInstance(name); + } + + /** + * Creates a new logger instance with the specified name. + */ + protected abstract InternalLogger newInstance(String name); +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/JdkLogger.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/JdkLogger.java new file mode 100644 index 00000000..8813651b --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/JdkLogger.java @@ -0,0 +1,647 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project 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. + */ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package com.foxinmy.weixin4j.logging; + +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +/** + * java.util.logging + * logger. + */ +class JdkLogger extends AbstractInternalLogger { + + private static final long serialVersionUID = -1767272577989225979L; + + final transient Logger logger; + + JdkLogger(Logger logger) { + super(logger.getName()); + this.logger = logger; + } + + /** + * Is this logger instance enabled for the FINEST level? + * + * @return True if this Logger is enabled for level FINEST, false otherwise. + */ + @Override + public boolean isTraceEnabled() { + return logger.isLoggable(Level.FINEST); + } + + /** + * Log a message object at level FINEST. + * + * @param msg + * - the message object to be logged + */ + @Override + public void trace(String msg) { + if (logger.isLoggable(Level.FINEST)) { + log(SELF, Level.FINEST, msg, null); + } + } + + /** + * Log a message at level FINEST according to the specified format and + * argument. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for level FINEST. + *

+ * + * @param format + * the format string + * @param arg + * the argument + */ + @Override + public void trace(String format, Object arg) { + if (logger.isLoggable(Level.FINEST)) { + FormattingTuple ft = MessageFormatter.format(format, arg); + log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at level FINEST according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the FINEST level. + *

+ * + * @param format + * the format string + * @param argA + * the first argument + * @param argB + * the second argument + */ + @Override + public void trace(String format, Object argA, Object argB) { + if (logger.isLoggable(Level.FINEST)) { + FormattingTuple ft = MessageFormatter.format(format, argA, argB); + log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at level FINEST according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the FINEST level. + *

+ * + * @param format + * the format string + * @param argArray + * an array of arguments + */ + @Override + public void trace(String format, Object... argArray) { + if (logger.isLoggable(Level.FINEST)) { + FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); + log(SELF, Level.FINEST, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log an exception (throwable) at level FINEST with an accompanying message. + * + * @param msg + * the message accompanying the exception + * @param t + * the exception (throwable) to log + */ + @Override + public void trace(String msg, Throwable t) { + if (logger.isLoggable(Level.FINEST)) { + log(SELF, Level.FINEST, msg, t); + } + } + + /** + * Is this logger instance enabled for the FINE level? + * + * @return True if this Logger is enabled for level FINE, false otherwise. + */ + @Override + public boolean isDebugEnabled() { + return logger.isLoggable(Level.FINE); + } + + /** + * Log a message object at level FINE. + * + * @param msg + * - the message object to be logged + */ + @Override + public void debug(String msg) { + if (logger.isLoggable(Level.FINE)) { + log(SELF, Level.FINE, msg, null); + } + } + + /** + * Log a message at level FINE according to the specified format and argument. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for level FINE. + *

+ * + * @param format + * the format string + * @param arg + * the argument + */ + @Override + public void debug(String format, Object arg) { + if (logger.isLoggable(Level.FINE)) { + FormattingTuple ft = MessageFormatter.format(format, arg); + log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at level FINE according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the FINE level. + *

+ * + * @param format + * the format string + * @param argA + * the first argument + * @param argB + * the second argument + */ + @Override + public void debug(String format, Object argA, Object argB) { + if (logger.isLoggable(Level.FINE)) { + FormattingTuple ft = MessageFormatter.format(format, argA, argB); + log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at level FINE according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the FINE level. + *

+ * + * @param format + * the format string + * @param argArray + * an array of arguments + */ + @Override + public void debug(String format, Object... argArray) { + if (logger.isLoggable(Level.FINE)) { + FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); + log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log an exception (throwable) at level FINE with an accompanying message. + * + * @param msg + * the message accompanying the exception + * @param t + * the exception (throwable) to log + */ + @Override + public void debug(String msg, Throwable t) { + if (logger.isLoggable(Level.FINE)) { + log(SELF, Level.FINE, msg, t); + } + } + + /** + * Is this logger instance enabled for the INFO level? + * + * @return True if this Logger is enabled for the INFO level, false otherwise. + */ + @Override + public boolean isInfoEnabled() { + return logger.isLoggable(Level.INFO); + } + + /** + * Log a message object at the INFO level. + * + * @param msg + * - the message object to be logged + */ + @Override + public void info(String msg) { + if (logger.isLoggable(Level.INFO)) { + log(SELF, Level.INFO, msg, null); + } + } + + /** + * Log a message at level INFO according to the specified format and argument. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the INFO level. + *

+ * + * @param format + * the format string + * @param arg + * the argument + */ + @Override + public void info(String format, Object arg) { + if (logger.isLoggable(Level.INFO)) { + FormattingTuple ft = MessageFormatter.format(format, arg); + log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at the INFO level according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the INFO level. + *

+ * + * @param format + * the format string + * @param argA + * the first argument + * @param argB + * the second argument + */ + @Override + public void info(String format, Object argA, Object argB) { + if (logger.isLoggable(Level.INFO)) { + FormattingTuple ft = MessageFormatter.format(format, argA, argB); + log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at level INFO according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the INFO level. + *

+ * + * @param format + * the format string + * @param argArray + * an array of arguments + */ + @Override + public void info(String format, Object... argArray) { + if (logger.isLoggable(Level.INFO)) { + FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); + log(SELF, Level.INFO, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log an exception (throwable) at the INFO level with an accompanying + * message. + * + * @param msg + * the message accompanying the exception + * @param t + * the exception (throwable) to log + */ + @Override + public void info(String msg, Throwable t) { + if (logger.isLoggable(Level.INFO)) { + log(SELF, Level.INFO, msg, t); + } + } + + /** + * Is this logger instance enabled for the WARNING level? + * + * @return True if this Logger is enabled for the WARNING level, false + * otherwise. + */ + @Override + public boolean isWarnEnabled() { + return logger.isLoggable(Level.WARNING); + } + + /** + * Log a message object at the WARNING level. + * + * @param msg + * - the message object to be logged + */ + @Override + public void warn(String msg) { + if (logger.isLoggable(Level.WARNING)) { + log(SELF, Level.WARNING, msg, null); + } + } + + /** + * Log a message at the WARNING level according to the specified format and + * argument. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the WARNING level. + *

+ * + * @param format + * the format string + * @param arg + * the argument + */ + @Override + public void warn(String format, Object arg) { + if (logger.isLoggable(Level.WARNING)) { + FormattingTuple ft = MessageFormatter.format(format, arg); + log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at the WARNING level according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the WARNING level. + *

+ * + * @param format + * the format string + * @param argA + * the first argument + * @param argB + * the second argument + */ + @Override + public void warn(String format, Object argA, Object argB) { + if (logger.isLoggable(Level.WARNING)) { + FormattingTuple ft = MessageFormatter.format(format, argA, argB); + log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at level WARNING according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the WARNING level. + *

+ * + * @param format + * the format string + * @param argArray + * an array of arguments + */ + @Override + public void warn(String format, Object... argArray) { + if (logger.isLoggable(Level.WARNING)) { + FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); + log(SELF, Level.WARNING, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log an exception (throwable) at the WARNING level with an accompanying + * message. + * + * @param msg + * the message accompanying the exception + * @param t + * the exception (throwable) to log + */ + @Override + public void warn(String msg, Throwable t) { + if (logger.isLoggable(Level.WARNING)) { + log(SELF, Level.WARNING, msg, t); + } + } + + /** + * Is this logger instance enabled for level SEVERE? + * + * @return True if this Logger is enabled for level SEVERE, false otherwise. + */ + @Override + public boolean isErrorEnabled() { + return logger.isLoggable(Level.SEVERE); + } + + /** + * Log a message object at the SEVERE level. + * + * @param msg + * - the message object to be logged + */ + @Override + public void error(String msg) { + if (logger.isLoggable(Level.SEVERE)) { + log(SELF, Level.SEVERE, msg, null); + } + } + + /** + * Log a message at the SEVERE level according to the specified format and + * argument. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the SEVERE level. + *

+ * + * @param format + * the format string + * @param arg + * the argument + */ + @Override + public void error(String format, Object arg) { + if (logger.isLoggable(Level.SEVERE)) { + FormattingTuple ft = MessageFormatter.format(format, arg); + log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at the SEVERE level according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the SEVERE level. + *

+ * + * @param format + * the format string + * @param argA + * the first argument + * @param argB + * the second argument + */ + @Override + public void error(String format, Object argA, Object argB) { + if (logger.isLoggable(Level.SEVERE)) { + FormattingTuple ft = MessageFormatter.format(format, argA, argB); + log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log a message at level SEVERE according to the specified format and + * arguments. + * + *

+ * This form avoids superfluous object creation when the logger is disabled + * for the SEVERE level. + *

+ * + * @param format + * the format string + * @param arguments + * an array of arguments + */ + @Override + public void error(String format, Object... arguments) { + if (logger.isLoggable(Level.SEVERE)) { + FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments); + log(SELF, Level.SEVERE, ft.getMessage(), ft.getThrowable()); + } + } + + /** + * Log an exception (throwable) at the SEVERE level with an accompanying + * message. + * + * @param msg + * the message accompanying the exception + * @param t + * the exception (throwable) to log + */ + @Override + public void error(String msg, Throwable t) { + if (logger.isLoggable(Level.SEVERE)) { + log(SELF, Level.SEVERE, msg, t); + } + } + + /** + * Log the message at the specified level with the specified throwable if any. + * This method creates a LogRecord and fills in caller date before calling + * this instance's JDK14 logger. + * + * See bug report #13 for more details. + */ + private void log(String callerFQCN, Level level, String msg, Throwable t) { + // millis and thread are filled by the constructor + LogRecord record = new LogRecord(level, msg); + record.setLoggerName(name()); + record.setThrown(t); + fillCallerData(callerFQCN, record); + logger.log(record); + } + + static final String SELF = JdkLogger.class.getName(); + static final String SUPER = AbstractInternalLogger.class.getName(); + + /** + * Fill in caller data if possible. + * + * @param record + * The record to update + */ + private static void fillCallerData(String callerFQCN, LogRecord record) { + StackTraceElement[] steArray = new Throwable().getStackTrace(); + + int selfIndex = -1; + for (int i = 0; i < steArray.length; i++) { + final String className = steArray[i].getClassName(); + if (className.equals(callerFQCN) || className.equals(SUPER)) { + selfIndex = i; + break; + } + } + + int found = -1; + for (int i = selfIndex + 1; i < steArray.length; i++) { + final String className = steArray[i].getClassName(); + if (!(className.equals(callerFQCN) || className.equals(SUPER))) { + found = i; + break; + } + } + + if (found != -1) { + StackTraceElement ste = steArray[found]; + // setting the class name has the side effect of setting + // the needToInferCaller variable to false. + record.setSourceClassName(ste.getClassName()); + record.setSourceMethodName(ste.getMethodName()); + } + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/JdkLoggerFactory.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/JdkLoggerFactory.java new file mode 100644 index 00000000..81397cd6 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/JdkLoggerFactory.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project 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 com.foxinmy.weixin4j.logging; + + +import java.util.logging.Logger; + +/** + * Logger factory which creates a + * java.util.logging + * logger. + */ +public class JdkLoggerFactory extends InternalLoggerFactory { + + @Override + public InternalLogger newInstance(String name) { + return new JdkLogger(Logger.getLogger(name)); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/MessageFormatter.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/MessageFormatter.java new file mode 100644 index 00000000..cce05f0b --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/MessageFormatter.java @@ -0,0 +1,428 @@ +/* + * Copyright 2013 The Netty Project + * + * The Netty Project 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. + */ +/** + * Copyright (c) 2004-2011 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package com.foxinmy.weixin4j.logging; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; + +// contributors: lizongbo: proposed special treatment of array parameter values +// Joern Huxhorn: pointed out double[] omission, suggested deep array copy + +/** + * Formats messages according to very simple substitution rules. Substitutions + * can be made 1, 2 or more arguments. + *

+ *

+ * For example, + *

+ *

+ * MessageFormatter.format("Hi {}.", "there")
+ * 
+ *

+ * will return the string "Hi there.". + *

+ * The {} pair is called the formatting anchor. It serves to designate + * the location where arguments need to be substituted within the message + * pattern. + *

+ * In case your message contains the '{' or the '}' character, you do not have + * to do anything special unless the '}' character immediately follows '{'. For + * example, + *

+ *

+ * MessageFormatter.format("Set {1,2,3} is not equal to {}.", "1,2");
+ * 
+ *

+ * will return the string "Set {1,2,3} is not equal to 1,2.". + *

+ *

+ * If for whatever reason you need to place the string "{}" in the message + * without its formatting anchor meaning, then you need to escape the + * '{' character with '\', that is the backslash character. Only the '{' + * character should be escaped. There is no need to escape the '}' character. + * For example, + *

+ *

+ * MessageFormatter.format("Set \\{} is not equal to {}.", "1,2");
+ * 
+ *

+ * will return the string "Set {} is not equal to 1,2.". + *

+ *

+ * The escaping behavior just described can be overridden by escaping the escape + * character '\'. Calling + *

+ *

+ * MessageFormatter.format("File name is C:\\\\{}.", "file.zip");
+ * 
+ *

+ * will return the string "File name is C:\file.zip". + *

+ *

+ * The formatting conventions are different than those of {@link MessageFormat} + * which ships with the Java platform. This is justified by the fact that + * SLF4J's implementation is 10 times faster than that of {@link MessageFormat}. + * This local performance difference is both measurable and significant in the + * larger context of the complete logging processing chain. + *

+ *

+ * See also {@link #format(String, Object)}, + * {@link #format(String, Object, Object)} and + * {@link #arrayFormat(String, Object[])} methods for more details. + */ +final class MessageFormatter { + static final char DELIM_START = '{'; + static final char DELIM_STOP = '}'; + static final String DELIM_STR = "{}"; + private static final char ESCAPE_CHAR = '\\'; + + /** + * Performs single argument substitution for the 'messagePattern' passed as + * parameter. + *

+ * For example, + *

+ *

+     * MessageFormatter.format("Hi {}.", "there");
+     * 
+ *

+ * will return the string "Hi there.". + *

+ * + * @param messagePattern The message pattern which will be parsed and formatted + * @param arg The argument to be substituted in place of the formatting anchor + * @return The formatted message + */ + static FormattingTuple format(String messagePattern, Object arg) { + return arrayFormat(messagePattern, new Object[]{arg}); + } + + /** + * Performs a two argument substitution for the 'messagePattern' passed as + * parameter. + *

+ * For example, + *

+ *

+     * MessageFormatter.format("Hi {}. My name is {}.", "Alice", "Bob");
+     * 
+ *

+ * will return the string "Hi Alice. My name is Bob.". + * + * @param messagePattern The message pattern which will be parsed and formatted + * @param argA The argument to be substituted in place of the first formatting + * anchor + * @param argB The argument to be substituted in place of the second formatting + * anchor + * @return The formatted message + */ + static FormattingTuple format(final String messagePattern, + Object argA, Object argB) { + return arrayFormat(messagePattern, new Object[]{argA, argB}); + } + + static Throwable getThrowableCandidate(Object[] argArray) { + if (argArray == null || argArray.length == 0) { + return null; + } + + final Object lastEntry = argArray[argArray.length - 1]; + if (lastEntry instanceof Throwable) { + return (Throwable) lastEntry; + } + return null; + } + + /** + * Same principle as the {@link #format(String, Object)} and + * {@link #format(String, Object, Object)} methods except that any number of + * arguments can be passed in an array. + * + * @param messagePattern The message pattern which will be parsed and formatted + * @param argArray An array of arguments to be substituted in place of formatting + * anchors + * @return The formatted message + */ + static FormattingTuple arrayFormat(final String messagePattern, + final Object[] argArray) { + + Throwable throwableCandidate = getThrowableCandidate(argArray); + + if (messagePattern == null) { + return new FormattingTuple(null, argArray, throwableCandidate); + } + + if (argArray == null) { + return new FormattingTuple(messagePattern); + } + + int i = 0; + int j; + StringBuffer sbuf = new StringBuffer(messagePattern.length() + 50); + + int L; + for (L = 0; L < argArray.length; L++) { + + j = messagePattern.indexOf(DELIM_STR, i); + + if (j == -1) { + // no more variables + if (i == 0) { // this is a simple string + return new FormattingTuple(messagePattern, argArray, + throwableCandidate); + } else { // add the tail string which contains no variables and return + // the result. + sbuf.append(messagePattern.substring(i, messagePattern.length())); + return new FormattingTuple(sbuf.toString(), argArray, + throwableCandidate); + } + } else { + if (isEscapedDelimeter(messagePattern, j)) { + if (!isDoubleEscaped(messagePattern, j)) { + L--; // DELIM_START was escaped, thus should not be incremented + sbuf.append(messagePattern.substring(i, j - 1)); + sbuf.append(DELIM_START); + i = j + 1; + } else { + // The escape character preceding the delimiter start is + // itself escaped: "abc x:\\{}" + // we have to consume one backward slash + sbuf.append(messagePattern.substring(i, j - 1)); + deeplyAppendParameter(sbuf, argArray[L], new HashMap()); + i = j + 2; + } + } else { + // normal case + sbuf.append(messagePattern.substring(i, j)); + deeplyAppendParameter(sbuf, argArray[L], new HashMap()); + i = j + 2; + } + } + } + // append the characters following the last {} pair. + sbuf.append(messagePattern.substring(i, messagePattern.length())); + if (L < argArray.length - 1) { + return new FormattingTuple(sbuf.toString(), argArray, throwableCandidate); + } else { + return new FormattingTuple(sbuf.toString(), argArray, null); + } + } + + static boolean isEscapedDelimeter(String messagePattern, + int delimeterStartIndex) { + + if (delimeterStartIndex == 0) { + return false; + } + return messagePattern.charAt(delimeterStartIndex - 1) == ESCAPE_CHAR; + } + + static boolean isDoubleEscaped(String messagePattern, + int delimeterStartIndex) { + return delimeterStartIndex >= 2 && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR; + } + + // special treatment of array values was suggested by 'lizongbo' + private static void deeplyAppendParameter(StringBuffer sbuf, Object o, + Map seenMap) { + if (o == null) { + sbuf.append("null"); + return; + } + if (!o.getClass().isArray()) { + safeObjectAppend(sbuf, o); + } else { + // check for primitive array types because they + // unfortunately cannot be cast to Object[] + if (o instanceof boolean[]) { + booleanArrayAppend(sbuf, (boolean[]) o); + } else if (o instanceof byte[]) { + byteArrayAppend(sbuf, (byte[]) o); + } else if (o instanceof char[]) { + charArrayAppend(sbuf, (char[]) o); + } else if (o instanceof short[]) { + shortArrayAppend(sbuf, (short[]) o); + } else if (o instanceof int[]) { + intArrayAppend(sbuf, (int[]) o); + } else if (o instanceof long[]) { + longArrayAppend(sbuf, (long[]) o); + } else if (o instanceof float[]) { + floatArrayAppend(sbuf, (float[]) o); + } else if (o instanceof double[]) { + doubleArrayAppend(sbuf, (double[]) o); + } else { + objectArrayAppend(sbuf, (Object[]) o, seenMap); + } + } + } + + private static void safeObjectAppend(StringBuffer sbuf, Object o) { + try { + String oAsString = o.toString(); + sbuf.append(oAsString); + } catch (Throwable t) { + System.err + .println("SLF4J: Failed toString() invocation on an object of type [" + + o.getClass().getName() + ']'); + t.printStackTrace(); + sbuf.append("[FAILED toString()]"); + } + } + + private static void objectArrayAppend(StringBuffer sbuf, Object[] a, + Map seenMap) { + sbuf.append('['); + if (!seenMap.containsKey(a)) { + seenMap.put(a, null); + final int len = a.length; + for (int i = 0; i < len; i++) { + deeplyAppendParameter(sbuf, a[i], seenMap); + if (i != len - 1) { + sbuf.append(", "); + } + } + // allow repeats in siblings + seenMap.remove(a); + } else { + sbuf.append("..."); + } + sbuf.append(']'); + } + + private static void booleanArrayAppend(StringBuffer sbuf, boolean[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); + } + + private static void byteArrayAppend(StringBuffer sbuf, byte[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); + } + + private static void charArrayAppend(StringBuffer sbuf, char[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); + } + + private static void shortArrayAppend(StringBuffer sbuf, short[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); + } + + private static void intArrayAppend(StringBuffer sbuf, int[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); + } + + private static void longArrayAppend(StringBuffer sbuf, long[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); + } + + private static void floatArrayAppend(StringBuffer sbuf, float[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); + } + + private static void doubleArrayAppend(StringBuffer sbuf, double[] a) { + sbuf.append('['); + final int len = a.length; + for (int i = 0; i < len; i++) { + sbuf.append(a[i]); + if (i != len - 1) { + sbuf.append(", "); + } + } + sbuf.append(']'); + } + + private MessageFormatter() { + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/Slf4JLogger.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/Slf4JLogger.java new file mode 100644 index 00000000..45a61c56 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/Slf4JLogger.java @@ -0,0 +1,183 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project 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 com.foxinmy.weixin4j.logging; + +import org.slf4j.Logger; + +/** + * SLF4J logger. + */ +class Slf4JLogger extends AbstractInternalLogger { + + private static final long serialVersionUID = 108038972685130825L; + + private final transient Logger logger; + + Slf4JLogger(Logger logger) { + super(logger.getName()); + this.logger = logger; + } + + @Override + public boolean isTraceEnabled() { + return logger.isTraceEnabled(); + } + + @Override + public void trace(String msg) { + logger.trace(msg); + } + + @Override + public void trace(String format, Object arg) { + logger.trace(format, arg); + } + + @Override + public void trace(String format, Object argA, Object argB) { + logger.trace(format, argA, argB); + } + + @Override + public void trace(String format, Object... argArray) { + logger.trace(format, argArray); + } + + @Override + public void trace(String msg, Throwable t) { + logger.trace(msg, t); + } + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + @Override + public void debug(String msg) { + logger.debug(msg); + } + + @Override + public void debug(String format, Object arg) { + logger.debug(format, arg); + } + + @Override + public void debug(String format, Object argA, Object argB) { + logger.debug(format, argA, argB); + } + + @Override + public void debug(String format, Object... argArray) { + logger.debug(format, argArray); + } + + @Override + public void debug(String msg, Throwable t) { + logger.debug(msg, t); + } + + @Override + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + @Override + public void info(String msg) { + logger.info(msg); + } + + @Override + public void info(String format, Object arg) { + logger.info(format, arg); + } + + @Override + public void info(String format, Object argA, Object argB) { + logger.info(format, argA, argB); + } + + @Override + public void info(String format, Object... argArray) { + logger.info(format, argArray); + } + + @Override + public void info(String msg, Throwable t) { + logger.info(msg, t); + } + + @Override + public boolean isWarnEnabled() { + return logger.isWarnEnabled(); + } + + @Override + public void warn(String msg) { + logger.warn(msg); + } + + @Override + public void warn(String format, Object arg) { + logger.warn(format, arg); + } + + @Override + public void warn(String format, Object... argArray) { + logger.warn(format, argArray); + } + + @Override + public void warn(String format, Object argA, Object argB) { + logger.warn(format, argA, argB); + } + + @Override + public void warn(String msg, Throwable t) { + logger.warn(msg, t); + } + + @Override + public boolean isErrorEnabled() { + return logger.isErrorEnabled(); + } + + @Override + public void error(String msg) { + logger.error(msg); + } + + @Override + public void error(String format, Object arg) { + logger.error(format, arg); + } + + @Override + public void error(String format, Object argA, Object argB) { + logger.error(format, argA, argB); + } + + @Override + public void error(String format, Object... argArray) { + logger.error(format, argArray); + } + + @Override + public void error(String msg, Throwable t) { + logger.error(msg, t); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/Slf4JLoggerFactory.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/Slf4JLoggerFactory.java new file mode 100644 index 00000000..ca5470f0 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/logging/Slf4JLoggerFactory.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project 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 com.foxinmy.weixin4j.logging; + + +import org.slf4j.LoggerFactory; +import org.slf4j.helpers.NOPLoggerFactory; + +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; + +/** + * Logger factory which creates a SLF4J + * logger. + */ +public class Slf4JLoggerFactory extends InternalLoggerFactory { + + public Slf4JLoggerFactory() { + } + + Slf4JLoggerFactory(boolean failIfNOP) { + assert failIfNOP; // Should be always called with true. + + // SFL4J writes it error messages to System.err. Capture them so that the user does not see such a message on + // the console during automatic detection. + final StringBuffer buf = new StringBuffer(); + final PrintStream err = System.err; + try { + System.setErr(new PrintStream(new OutputStream() { + @Override + public void write(int b) { + buf.append((char) b); + } + }, true, "US-ASCII")); + } catch (UnsupportedEncodingException e) { + throw new Error(e); + } + + try { + if (LoggerFactory.getILoggerFactory() instanceof NOPLoggerFactory) { + throw new NoClassDefFoundError(buf.toString()); + } else { + err.print(buf); + err.flush(); + } + } finally { + System.setErr(err); + } + } + + @Override + public InternalLogger newInstance(String name) { + return new Slf4JLogger(LoggerFactory.getLogger(name)); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/StringUtil.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/StringUtil.java index fee0d92e..83637076 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/StringUtil.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/StringUtil.java @@ -205,4 +205,33 @@ public final class StringUtil { } return buf.toString(); } + + /** + * The shortcut to {@link #simpleClassName(Class) + * simpleClassName(o.getClass())}. + */ + public static String simpleClassName(Object o) { + if (o == null) { + return "null_object"; + } else { + return simpleClassName(o.getClass()); + } + } + + /** + * Generates a simplified name from a {@link Class}. Similar to + * {@link Class#getSimpleName()}, but it works fine with anonymous classes. + */ + public static String simpleClassName(Class clazz) { + if (clazz == null) { + return "null_class"; + } + + Package pkg = clazz.getPackage(); + if (pkg != null) { + return clazz.getName().substring(pkg.getName().length() + 1); + } else { + return clazz.getName(); + } + } } diff --git a/weixin4j-base/src/test/java/com/foxinmy/weixin4j/base/test/HttpClientTest.java b/weixin4j-base/src/test/java/com/foxinmy/weixin4j/base/test/HttpClientTest.java index bd03cd54..1f0ffdc3 100644 --- a/weixin4j-base/src/test/java/com/foxinmy/weixin4j/base/test/HttpClientTest.java +++ b/weixin4j-base/src/test/java/com/foxinmy/weixin4j/base/test/HttpClientTest.java @@ -67,14 +67,7 @@ public class HttpClientTest { } public static void main(String[] args) throws Exception { - for(int i=0;i<100000;i++){ test1(); - } - System.out.println("---------------------"); - test2(); - System.out.println("---------------------"); - test3(); - System.out.println("---------------------"); - test4(); + } } diff --git a/weixin4j-mp/src/main/resources/weixin4j.properties b/weixin4j-mp/src/main/resources/weixin4j.properties index 5d794c21..09a197d4 100644 --- a/weixin4j-mp/src/main/resources/weixin4j.properties +++ b/weixin4j-mp/src/main/resources/weixin4j.properties @@ -4,8 +4,6 @@ weixin4j.account={"id":"wx4ab8f8de58159a57","secret":"1d4eb0f4bf556aaed539f30ed05ca795",\ "mchId":"\u5fae\u4fe1\u5546\u6237\u53f7 \u5fae\u4fe1\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\ "certificateKey":"\u52a0\u8f7d\u652f\u4ed8\u8bc1\u4e66\u6587\u4ef6\u7684\u5bc6\u7801 \u5982\u679c\u4e0d\u586b\u5199\u5219\u9ed8\u8ba4\u83b7\u53d6mchId\u4f5c\u4e3a\u5bc6\u7801",\ -"partnerId":"V2\u7248\u672c\u4e0b\u7684\u8d22\u4ed8\u901a\u7684\u5546\u6237\u53f7 \u8001\u7248\u672c\u5fae\u4fe1\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\ -"partnerKey":"V2\u7248\u672c\u4e0b\u7684\u8d22\u4ed8\u901a\u5546\u6237\u6743\u9650\u5bc6\u94a5Key \u8001\u7248\u672c\u5fae\u4fe1\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\ "paySignKey":"\u5fae\u4fe1\u652f\u4ed8\u4e2d\u8c03\u7528API\u7684\u5bc6\u94a5 \u5fae\u4fe1\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165"} # weixin4j\u7684\u4e34\u65f6\u76ee\u5f55