From aba69feb41ef79fdfa71b61f533870de0638f2f5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 12 Jan 2020 10:36:03 +0800 Subject: [PATCH 0001/1965] fixed commit cookie error when response is committed --- .../session/JbootServletRequestWrapper.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/jboot/web/session/JbootServletRequestWrapper.java b/src/main/java/io/jboot/web/session/JbootServletRequestWrapper.java index 04532a6e..a4cc1057 100644 --- a/src/main/java/io/jboot/web/session/JbootServletRequestWrapper.java +++ b/src/main/java/io/jboot/web/session/JbootServletRequestWrapper.java @@ -147,10 +147,13 @@ public class JbootServletRequestWrapper extends HttpServletRequestWrapper { */ private Cookie getCookieObject(String name) { Cookie[] cookies = originRequest.getCookies(); - if (cookies != null) - for (Cookie cookie : cookies) - if (cookie.getName().equals(name)) + if (cookies != null) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals(name)) { return cookie; + } + } + } return null; } @@ -160,14 +163,16 @@ public class JbootServletRequestWrapper extends HttpServletRequestWrapper { * @param maxAgeInSeconds */ private void setCookie(String name, String value, int maxAgeInSeconds) { - Cookie cookie = new Cookie(name, value); - cookie.setMaxAge(maxAgeInSeconds); - cookie.setPath(cookiePath); - if (cookieDomain != null) { - cookie.setDomain(cookieDomain); + if (!response.isCommitted()) { + Cookie cookie = new Cookie(name, value); + cookie.setMaxAge(maxAgeInSeconds); + cookie.setPath(cookiePath); + if (cookieDomain != null) { + cookie.setDomain(cookieDomain); + } + cookie.setHttpOnly(true); + response.addCookie(cookie); } - cookie.setHttpOnly(true); - response.addCookie(cookie); } public HttpServletRequest getOriginRequest() { -- Gitee From 5a2bd26956bde08fa8deaca9a3f40da1d99bc4ab Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 12 Feb 2020 14:38:30 +0800 Subject: [PATCH 0002/1965] add splitToSetByComma() method in StrUtil --- src/main/java/io/jboot/utils/StrUtil.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/utils/StrUtil.java b/src/main/java/io/jboot/utils/StrUtil.java index 31915fcb..712a0300 100644 --- a/src/main/java/io/jboot/utils/StrUtil.java +++ b/src/main/java/io/jboot/utils/StrUtil.java @@ -23,7 +23,7 @@ import org.apache.commons.lang3.StringUtils; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import java.util.UUID; import java.util.regex.Matcher; @@ -200,6 +200,16 @@ public class StrUtil extends StrKit { } + /** + * 根据逗号分隔为set + * + * @param src + * @return + */ + public static Set splitToSetByComma(String src) { + return splitToSet(src, ","); + } + /** * 把字符串拆分成一个set * @@ -213,7 +223,7 @@ public class StrUtil extends StrKit { } String[] strings = src.split(regex); - Set set = new HashSet<>(); + Set set = new LinkedHashSet<>(); for (String s : strings) { if (StrUtil.isBlank(s)) { continue; -- Gitee From e0c7939f428efa5937a0e948be564105307f4df0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 12 Feb 2020 14:39:05 +0800 Subject: [PATCH 0003/1965] v3.0.2 ready --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index ef042289..747d2edc 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.0.1 + 3.0.2-SNAPSHOT jar jboot @@ -47,7 +47,7 @@ 2.4 3.3.0 3.4.2 - 2.10.1 + 2.10.2 5.1.48 2.0.29.Final 1.7.29 @@ -216,7 +216,7 @@ com.alibaba druid - 1.1.20 + 1.1.21 provided @@ -565,7 +565,7 @@ net.oschina.j2cache j2cache-core - 2.7.8-release + 2.8.0-release provided -- Gitee From a68badadb493ca73bfe7dc0b2f06fe538f965f08 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 16 Feb 2020 10:10:40 +0800 Subject: [PATCH 0004/1965] upgrade dependencies --- pom.xml | 38 +++++++++++--------------------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/pom.xml b/pom.xml index 747d2edc..9088a7ae 100644 --- a/pom.xml +++ b/pom.xml @@ -50,21 +50,22 @@ 2.10.2 5.1.48 2.0.29.Final - 1.7.29 + 1.7.30 2.57 1.2.62 28.2-jre 2.2.5 2.10.2 - 1.12.1 + 1.12.2 2.10.6 3.9 2.6 1.13 4.5.10 4.4.12 - 4.1.43.Final + 4.1.45.Final 1.7.1 + 2.7.5 1.8 1.8 @@ -410,36 +411,19 @@ provided - org.apache.dubbo dubbo - 2.7.5 - - - org.springframework - spring - - - org.springframework - spring-web - - - org.springframework - spring-beans - - - org.springframework - spring-context - - + ${dubbo.version} + provided - - org.apache.curator - curator-framework - 4.2.0 + org.apache.dubbo + dubbo-dependencies-zookeeper + ${dubbo.version} + pom + provided -- Gitee From aa520faeaffc5a3100a676a25aebeb648c744d54 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 16 Feb 2020 10:31:40 +0800 Subject: [PATCH 0005/1965] optimize StrUti.isDecimal() method --- src/main/java/io/jboot/utils/StrUtil.java | 55 +++++++++++++++-------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/jboot/utils/StrUtil.java b/src/main/java/io/jboot/utils/StrUtil.java index 712a0300..fda7b845 100644 --- a/src/main/java/io/jboot/utils/StrUtil.java +++ b/src/main/java/io/jboot/utils/StrUtil.java @@ -63,12 +63,12 @@ public class StrUtil extends StrKit { return redirect; } - public static boolean areNotEmpty(String... strings) { - if (strings == null || strings.length == 0) { + public static boolean areNotEmpty(String... strs) { + if (strs == null || strs.length == 0) { return false; } - for (String string : strings) { + for (String string : strs) { if (string == null || EMPTY.equals(string)) { return false; } @@ -76,43 +76,54 @@ public class StrUtil extends StrKit { return true; } - public static String requireNonBlank(String string) { - if (isBlank(string)) { + public static String requireNonBlank(String str) { + if (isBlank(str)) { throw new NullPointerException(); } - return string; + return str; } - public static String requireNonBlank(String string, String message) { - if (isBlank(string)) { + public static String requireNonBlank(String str, String message) { + if (isBlank(str)) { throw new NullPointerException(message); } - return string; + return str; } - public static String obtainDefaultIfBlank(String string, String defaultValue) { - return isBlank(string) ? defaultValue : string; + public static String obtainDefaultIfBlank(String str, String defaultValue) { + return isBlank(str) ? defaultValue : str; } /** * 不是空数据,注意:空格不是空数据 * - * @param string + * @param str * @return */ - public static boolean isNotEmpty(String string) { - return string != null && !string.equals(""); + public static boolean isNotEmpty(String str) { + return str != null && !str.equals(""); } /** * 确保不是空白字符串 * - * @param o + * @param str + * @return + */ + public static boolean isNotBlank(Object str) { + return str == null ? false : notBlank(str.toString()); + } + + + /** + * null 或者 空内容字符串 + * + * @param str * @return */ - public static boolean isNotBlank(Object o) { - return o == null ? false : notBlank(o.toString()); + public static boolean isNullOrBlank(String str) { + return isBlank(str); } @@ -150,7 +161,7 @@ public class StrUtil extends StrKit { } /** - * 这个字符串是否是小数点 + * 这个字符串是否是可能包含小数点的数字 * * @param str * @return @@ -159,11 +170,19 @@ public class StrUtil extends StrKit { if (str == null) { return false; } + boolean hasDot = false; for (int i = str.length(); --i >= 0; ) { int chr = str.charAt(i); if ((chr < 48 || chr > 57) && chr != '.') { return false; } + if (chr == '.'){ + if (hasDot){ + return false; + }else { + hasDot = true; + } + } } return true; } -- Gitee From 69166cd99eb222868608d3e2f26dd93b48f9775b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 21 Feb 2020 15:49:21 +0800 Subject: [PATCH 0006/1965] add escapeModel() and escapeMap() methods --- src/main/java/io/jboot/utils/StrUtil.java | 42 +++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/main/java/io/jboot/utils/StrUtil.java b/src/main/java/io/jboot/utils/StrUtil.java index fda7b845..c6aa0955 100644 --- a/src/main/java/io/jboot/utils/StrUtil.java +++ b/src/main/java/io/jboot/utils/StrUtil.java @@ -18,12 +18,15 @@ package io.jboot.utils; import com.jfinal.core.JFinal; import com.jfinal.kit.StrKit; import com.jfinal.log.Log; +import com.jfinal.plugin.activerecord.Model; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.LinkedHashSet; +import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.regex.Matcher; @@ -264,5 +267,44 @@ public class StrUtil extends StrKit { return isBlank(content) ? content : StringUtils.replaceEach(content, escapeChars, htmlChars); } + public static Model escapeModel(Model model, String... ignoreAttrs) { + String[] attrNames = model._getAttrNames(); + for (String attr : attrNames) { + + if (ArrayUtils.contains(ignoreAttrs, attr)) { + continue; + } + + Object value = model.get(attr); + + if (value != null && value instanceof String) { + model.set(attr, escapeHtml(value.toString())); + } + } + + return model; + } + + public static Map escapeMap(Map map, Object... ignoreKeys) { + if (map == null || map.isEmpty()) { + return map; + } + + Set keys = map.keySet(); + for (Object key : keys) { + if (ArrayUtils.contains(ignoreKeys, key)) { + continue; + } + + Object value = map.get(key); + + if (value != null && value instanceof String) { + map.put(key, escapeHtml(value.toString())); + } + } + + return map; + } + } -- Gitee From cb3478b4d3b439f23ae2464b916de3ced20ca704 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 21 Feb 2020 16:04:43 +0800 Subject: [PATCH 0007/1965] optimize pom.xml --- pom.xml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 9088a7ae..0e93dff3 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,7 @@ 4.1.45.Final 1.7.1 2.7.5 + 1.1.0 1.8 1.8 @@ -234,18 +235,21 @@ com.alibaba.csp sentinel-core ${sentinel.version} + provided com.alibaba.csp sentinel-cluster-client-default ${sentinel.version} + provided com.alibaba.csp sentinel-transport-simple-http ${sentinel.version} + provided @@ -495,7 +499,8 @@ io.seata seata-tm - 1.0.0 + ${seata.version} + provided ch.qos.logback @@ -507,25 +512,29 @@ io.seata seata-rm-datasource - 1.0.0 + ${seata.version} + provided io.seata seata-config-core - 1.0.0 + ${seata.version} + provided io.seata seata-codec-seata 1.0.0 + provided io.seata seata-core - 1.0.0 + ${seata.version} + provided io.seata @@ -534,10 +543,12 @@ + io.seata seata-common - 1.0.0 + ${seata.version} + provided ch.qos.logback @@ -546,6 +557,7 @@ + net.oschina.j2cache j2cache-core -- Gitee From 62e7dc6995075ad8b6e1fbf38af3901940198025 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 25 Feb 2020 17:27:55 +0800 Subject: [PATCH 0008/1965] optimize FileUtil --- src/main/java/io/jboot/utils/FileUtil.java | 31 +++++++++++++--------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index cac19b57..27bf7bb1 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -16,6 +16,7 @@ package io.jboot.utils; import com.jfinal.core.JFinal; +import com.jfinal.kit.LogKit; import com.jfinal.kit.PathKit; import java.io.*; @@ -58,6 +59,7 @@ public class FileUtil { } return new String(baos.toByteArray(), JFinal.me().getConstants().getEncoding()); } catch (Exception e) { + LogKit.error(e.toString(), e); } finally { close(fis, baos); } @@ -70,35 +72,38 @@ public class FileUtil { fos = new FileOutputStream(file, false); fos.write(string.getBytes(JFinal.me().getConstants().getEncoding())); } catch (Exception e) { + LogKit.error(e.toString(), e); } finally { - close(null, fos); + close(fos); } } - private static void close(InputStream is, OutputStream os) { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - } - } - if (os != null) { - try { - os.close(); - } catch (IOException e) { + public static void close(Closeable... closeable) { + if (closeable != null || closeable.length != 0) { + for (Closeable c : closeable) { + if (c != null) { + try { + c.close(); + } catch (IOException e) { + LogKit.error(e.toString(), e); + } + } } } } + public static void unzip(String zipFilePath) throws IOException { String targetPath = zipFilePath.substring(0, zipFilePath.lastIndexOf(".")); unzip(zipFilePath, targetPath, true); } + public static void unzip(String zipFilePath, String targetPath) throws IOException { unzip(zipFilePath, targetPath, true); } + public static void unzip(String zipFilePath, String targetPath, boolean safeUnzip) throws IOException { ZipFile zipFile = new ZipFile(zipFilePath); try { @@ -131,7 +136,7 @@ public class FileUtil { } } } finally { - zipFile.close(); + close(zipFile); } } -- Gitee From f91f5342400177e68b6d5582a5660a4d13df5255 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 26 Feb 2020 10:55:07 +0800 Subject: [PATCH 0009/1965] update docs --- doc/docs/config.md | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/doc/docs/config.md b/doc/docs/config.md index f34be1a9..ffd11d06 100644 --- a/doc/docs/config.md +++ b/doc/docs/config.md @@ -9,6 +9,8 @@ - 注入配置 - 注解配置 - 配置实体类 +- 开启 Nacos 分布式配置中心 +- 开启 Apollo 分布式配置中心 - 配置内容加密解密 - 设计原因 - 常见问题 @@ -22,9 +24,10 @@ - 环境变量 - Jvm 系统属性 - 启动参数 +- 分布式配置中心(目前支持 Apollo 和 Nacos) > 注意:如果同一个属性被多处配置,那么 Jboot 读取配置的优先顺序是: -> `启动参数` > `Jvm 系统属性` > `环境变量` > `jboot.properties 配置` +> `分布式配置中心` > `启动参数` > `Jvm 系统属性` > `环境变量` > `jboot.properties 配置` @@ -86,7 +89,8 @@ public class UserServiceProvider extends UserService{ } ``` -但是,无论是 `@RequestMapping("/user")` 或者是 `@RPCBean(group="myGroup",version="myVersion",port=...)` , 其参数配置都是固定的,因此,Jboot 提供了一种动态的配置方法,可以用于读取配置文件的内容。 +但是,无论是 `@RequestMapping("/user")` 或者是 `@RPCBean(group="myGroup",version="myVersion",port=...)` , +其参数配置都是固定的,因此,Jboot 提供了一种动态的配置方法,可以用于读取配置文件的内容。 例如: @@ -160,6 +164,38 @@ Component1Config config = Jboot.config(Component1Config.class); > 备注:`@ConfigModel(prefix="component1")` 注解的含义是 `Component1Config` 的前缀是 `component1` ,因此,其属性 `host` 是来至配置文件的 `component1.host` 的值。 + + + +## 开启 Nacos 分布式配置中心 + +**第一步,启动 nacos** +相关文档在 https://nacos.io/zh-cn/ + + +**第二步,在 jboot.properties 添加如下配置** + +```java +jboot.config.nacos.enable = true +jboot.config.nacos.serverAddr = 127.0.0.1:8848 +jboot.config.nacos.dataId = jboot +jboot.config.nacos.group = jboot +``` + +## 开启 Apollo 分布式配置中心 + +**第一步,启动 Apollo** +相关文档在 https://github.com/ctripcorp/apollo/wiki/Quick-Start + +**第二步,在 jboot.properties 添加如下配置** + +``` +jboot.config.apollo.enable = true +jboot.config.apollo.appId = SampleApp +jboot.config.apollo.meta = http://106.54.227.205:8080 +``` + + ## 配置内容加密解密 为了安全起见,很多时候我们需要对配置里的一些安全和隐私内容进行加密,比如数据库的账号密码等,防止web服务器被黑客入侵时保证数据库的安全。 @@ -208,10 +244,13 @@ public MyConfigDecriptor implements JbootConfigDecryptor { > 答:和启动参数一样,只需要把 `--` 换成 `-D`,例如: java -jar -Dundertow.port=8080 -Dundertow.host=0.0.0.0 -2、如何设置系统环境变量 ? +3、如何设置系统环境变量 ? > 答:在 Docker 下,启动 Docker 容器的时候,只需要添加 -e 参数即可,例如: `docker run -e undertow.port=8080 xxxx` > Linux、Window、Mac 搜索引擎自行搜索关键字: `环境变量配置` +注意:在设置的系统环境变量的key、value中,例如:jboot.app.mode = dev 可以修改为 JBOOT_APP_MODE = dev ,其他同理把全部小写 +修改为大写,符号点(.)修改为下划线(_)。 + ## Jboot 所有配置参考 -- Gitee From 277629cd20c9923d158dd94e0c4739c2995caeab Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 26 Feb 2020 10:57:05 +0800 Subject: [PATCH 0010/1965] upgrade dependencies --- pom.xml | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index 0e93dff3..83574b9f 100644 --- a/pom.xml +++ b/pom.xml @@ -67,11 +67,14 @@ 1.7.1 2.7.5 1.1.0 + 1.1.7 + 4.1.2 1.8 1.8 1.8 UTF-8 + @@ -270,7 +273,7 @@ com.weibo motan-core - 1.1.6 + ${motan.version} provided @@ -287,7 +290,7 @@ com.weibo motan-transport-netty4 - 1.1.6 + ${motan.version} provided @@ -304,7 +307,7 @@ com.weibo motan-registry-consul - 1.1.6 + ${motan.version} provided @@ -321,7 +324,7 @@ com.weibo motan-registry-zookeeper - 1.1.6 + ${motan.version} provided @@ -372,27 +375,27 @@ provided - io.dropwizard.metrics metrics-core - 4.1.2 + ${metrics.version} io.dropwizard.metrics - metrics-graphite - 4.1.1 - provided + metrics-servlets + ${metrics.version} + io.dropwizard.metrics - metrics-servlets - 4.1.2 + metrics-healthchecks + ${metrics.version} + com.github.davidb metrics-influxdb @@ -401,6 +404,14 @@ + + io.dropwizard.metrics + metrics-graphite + 4.1.1 + provided + + + org.apache.shiro shiro-core -- Gitee From 206c7c07acd1a3df6c8aa247452275e6b4d781c5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 26 Feb 2020 14:02:32 +0800 Subject: [PATCH 0011/1965] optimize Sentinel component --- ...r.java => AbstractSentinelProccesser.java} | 4 +- .../support/sentinel/SentinelInterceptor.java | 63 ++------------ .../support/sentinel/SentinelManager.java | 23 +++-- .../support/sentinel/SentinelProcesser.java | 87 +++++++++++++++++++ 4 files changed, 114 insertions(+), 63 deletions(-) rename src/main/java/io/jboot/support/sentinel/{AbstractSentinelInterceptor.java => AbstractSentinelProccesser.java} (98%) create mode 100644 src/main/java/io/jboot/support/sentinel/SentinelProcesser.java diff --git a/src/main/java/io/jboot/support/sentinel/AbstractSentinelInterceptor.java b/src/main/java/io/jboot/support/sentinel/AbstractSentinelProccesser.java similarity index 98% rename from src/main/java/io/jboot/support/sentinel/AbstractSentinelInterceptor.java rename to src/main/java/io/jboot/support/sentinel/AbstractSentinelProccesser.java index f54d4e17..c22857f6 100644 --- a/src/main/java/io/jboot/support/sentinel/AbstractSentinelInterceptor.java +++ b/src/main/java/io/jboot/support/sentinel/AbstractSentinelProccesser.java @@ -21,9 +21,7 @@ import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.util.MethodUtil; import com.alibaba.csp.sentinel.util.StringUtil; -import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; -import io.jboot.web.fixedinterceptor.FixedInterceptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -35,7 +33,7 @@ import java.util.Arrays; * * @author Eric Zhao */ -public abstract class AbstractSentinelInterceptor implements Interceptor, FixedInterceptor { +public abstract class AbstractSentinelProccesser { protected void traceException(Throwable ex) { Tracer.trace(ex); diff --git a/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java b/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java index a853be35..9b1caa8b 100644 --- a/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java +++ b/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java @@ -15,71 +15,24 @@ */ package io.jboot.support.sentinel; -import com.alibaba.csp.sentinel.Entry; -import com.alibaba.csp.sentinel.EntryType; -import com.alibaba.csp.sentinel.SphU; -import com.alibaba.csp.sentinel.annotation.SentinelResource; -import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; +import io.jboot.web.fixedinterceptor.FixedInterceptor; /** * @author michael yang (fuhai999@gmail.com) * @Date: 2020/1/7 */ -public class SentinelInterceptor extends AbstractSentinelInterceptor { +public class SentinelInterceptor implements Interceptor, FixedInterceptor { + @Override public void intercept(Invocation inv) { - - SentinelResource annotation = inv.getMethod().getAnnotation(SentinelResource.class); - if (annotation == null) { + SentinelProcesser processer = SentinelManager.me().getProcesser(); + if (processer != null){ + processer.doProcess(inv); + }else { inv.invoke(); - return; - } - - - String resourceName = getResourceName(annotation.value(), inv.getMethod()); - EntryType entryType = annotation.entryType(); - int resourceType = annotation.resourceType(); - Entry entry = null; - try { - entry = SphU.entry(resourceName, resourceType, entryType, inv.getArgs()); -// Object result = pjp.proceed(); -// return result; - inv.invoke(); - } catch (BlockException ex) { - try { - inv.setReturnValue(handleBlockException(inv, annotation, ex)); - } catch (Throwable throwable) { - if (inv.isActionInvocation()) { - inv.getController().renderText("Blocked by Sentinel " + ex.getRule()); - } else { - throwable.printStackTrace(); - } - } - return; - } catch (Throwable ex) { - Class[] exceptionsToIgnore = annotation.exceptionsToIgnore(); - // The ignore list will be checked first. - if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) { - throw ex; - } - if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) { - traceException(ex); - try { - inv.setReturnValue(handleFallback(inv, annotation, ex)); - } catch (Throwable throwable) { - throwable.printStackTrace(); - } - return; - } - - // No fallback function can handle the exception, so throw it out. - throw ex; - } finally { - if (entry != null) { - entry.exit(1, inv.getArgs()); - } } } diff --git a/src/main/java/io/jboot/support/sentinel/SentinelManager.java b/src/main/java/io/jboot/support/sentinel/SentinelManager.java index e58a044a..43587324 100644 --- a/src/main/java/io/jboot/support/sentinel/SentinelManager.java +++ b/src/main/java/io/jboot/support/sentinel/SentinelManager.java @@ -27,24 +27,37 @@ import java.lang.reflect.Field; */ public class SentinelManager { - private SentinelManager(){} + private SentinelManager() { + } private static SentinelManager me = new SentinelManager(); - public static SentinelManager me(){ + public static SentinelManager me() { return me; } - public void init(){ + private SentinelProcesser processer; + + public void init() { try { + +// throw ClassNotFoundException if not dependency sentinel + Class.forName("com.alibaba.csp.sentinel.Sph"); + JbootApplicationConfig appConfig = JbootConfigManager.me().get(JbootApplicationConfig.class); Field field = AppNameUtil.class.getDeclaredField("appName"); field.setAccessible(true); - field.set(null,appConfig.getName()); + field.set(null, appConfig.getName()); + + processer = new SentinelProcesser(); + } catch (Exception e) { - e.printStackTrace(); + // do nothing... } + } + public SentinelProcesser getProcesser() { + return processer; } } diff --git a/src/main/java/io/jboot/support/sentinel/SentinelProcesser.java b/src/main/java/io/jboot/support/sentinel/SentinelProcesser.java new file mode 100644 index 00000000..315af641 --- /dev/null +++ b/src/main/java/io/jboot/support/sentinel/SentinelProcesser.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.support.sentinel; + +import com.alibaba.csp.sentinel.Entry; +import com.alibaba.csp.sentinel.EntryType; +import com.alibaba.csp.sentinel.SphU; +import com.alibaba.csp.sentinel.annotation.SentinelResource; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.jfinal.aop.Invocation; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/1/7 + */ +public class SentinelProcesser extends AbstractSentinelProccesser { + + + public void doProcess(Invocation inv) { + + SentinelResource annotation = inv.getMethod().getAnnotation(SentinelResource.class); + if (annotation == null) { + inv.invoke(); + return; + } + + + String resourceName = getResourceName(annotation.value(), inv.getMethod()); + EntryType entryType = annotation.entryType(); + int resourceType = annotation.resourceType(); + Entry entry = null; + try { + entry = SphU.entry(resourceName, resourceType, entryType, inv.getArgs()); +// Object result = pjp.proceed(); +// return result; + inv.invoke(); + } catch (BlockException ex) { + try { + inv.setReturnValue(handleBlockException(inv, annotation, ex)); + } catch (Throwable throwable) { + if (inv.isActionInvocation()) { + inv.getController().renderText("Blocked by Sentinel " + ex.getRule()); + } else { + throwable.printStackTrace(); + } + } + return; + } catch (Throwable ex) { + Class[] exceptionsToIgnore = annotation.exceptionsToIgnore(); + // The ignore list will be checked first. + if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) { + throw ex; + } + if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) { + traceException(ex); + try { + inv.setReturnValue(handleFallback(inv, annotation, ex)); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + return; + } + + // No fallback function can handle the exception, so throw it out. + throw ex; + } finally { + if (entry != null) { + entry.exit(1, inv.getArgs()); + } + } + } + + +} -- Gitee From c130ebcc71e1b759e669169af1493080f4691ce9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 26 Feb 2020 14:12:10 +0800 Subject: [PATCH 0012/1965] v3.0.2 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 9 +++++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5561be79..9fad2e99 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.0.1 + 3.0.2 ``` diff --git a/changes.txt b/changes.txt index 02226800..f7a00e55 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,12 @@ +jboot v3.0.2: +新增:StrUtil 新增 splitToSetByComma() 方法 +新增:StrUtil 新增 escapeModel() 和 escapeMap() 方法 +优化:StrUti.isDecimal() 方法,防止在某些极端情况下出现判断不正确的问题 +优化:对 pom.xml 进行优化,排除非必要的依赖 +优化:重构 Sentinel 模块,修改为非必须依赖 + + + jboot v3.0.1: 修复:紧急修复 v3.0.0 必须依赖 nacos-client 的问题 diff --git a/doc/docs/install.md b/doc/docs/install.md index eab83655..aaa0b0a9 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.0.1 + 3.0.2 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 935e4d74..fc5f447f 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.0.1 + 3.0.2 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 852f4bb4..4a1e1526 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.0.1"; + public static String VERSION = "3.0.2"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From a5e776c5c83e25c84398c8792454515aafc51e00 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 26 Feb 2020 14:12:31 +0800 Subject: [PATCH 0013/1965] v3.0.2 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 83574b9f..fce9f8fb 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.0.2-SNAPSHOT + 3.0.2 jar jboot -- Gitee From 74b12be3a735a7d8f2981a7a8d3e67a6c954e3fa Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 28 Feb 2020 17:02:07 +0800 Subject: [PATCH 0014/1965] add jboot objects --- .../jboot/objects/counter/JbootCounter.java | 23 +++++++++++++++++++ .../objects/counter/JbootCounterManager.java | 23 +++++++++++++++++++ .../java/io/jboot/objects/list/JbootList.java | 23 +++++++++++++++++++ .../java/io/jboot/objects/lock/JbootLock.java | 8 +++++++ .../java/io/jboot/objects/map/JbootMap.java | 23 +++++++++++++++++++ .../io/jboot/objects/map/JbootMapManager.java | 23 +++++++++++++++++++ .../jboot/objects/multimap/JbootMultimap.java | 8 +++++++ .../java/io/jboot/objects/set/JbootSet.java | 8 +++++++ 8 files changed, 139 insertions(+) create mode 100644 src/main/java/io/jboot/objects/counter/JbootCounter.java create mode 100644 src/main/java/io/jboot/objects/counter/JbootCounterManager.java create mode 100644 src/main/java/io/jboot/objects/list/JbootList.java create mode 100644 src/main/java/io/jboot/objects/lock/JbootLock.java create mode 100644 src/main/java/io/jboot/objects/map/JbootMap.java create mode 100644 src/main/java/io/jboot/objects/map/JbootMapManager.java create mode 100644 src/main/java/io/jboot/objects/multimap/JbootMultimap.java create mode 100644 src/main/java/io/jboot/objects/set/JbootSet.java diff --git a/src/main/java/io/jboot/objects/counter/JbootCounter.java b/src/main/java/io/jboot/objects/counter/JbootCounter.java new file mode 100644 index 00000000..af2993f3 --- /dev/null +++ b/src/main/java/io/jboot/objects/counter/JbootCounter.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.objects.counter; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/2/28 + */ +public interface JbootCounter { +} diff --git a/src/main/java/io/jboot/objects/counter/JbootCounterManager.java b/src/main/java/io/jboot/objects/counter/JbootCounterManager.java new file mode 100644 index 00000000..510254c1 --- /dev/null +++ b/src/main/java/io/jboot/objects/counter/JbootCounterManager.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.objects.counter; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/2/28 + */ +public class JbootCounterManager { +} diff --git a/src/main/java/io/jboot/objects/list/JbootList.java b/src/main/java/io/jboot/objects/list/JbootList.java new file mode 100644 index 00000000..fb6ba05b --- /dev/null +++ b/src/main/java/io/jboot/objects/list/JbootList.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.objects.list; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/2/28 + */ +public interface JbootList { +} diff --git a/src/main/java/io/jboot/objects/lock/JbootLock.java b/src/main/java/io/jboot/objects/lock/JbootLock.java new file mode 100644 index 00000000..f7c389d5 --- /dev/null +++ b/src/main/java/io/jboot/objects/lock/JbootLock.java @@ -0,0 +1,8 @@ +package io.jboot.objects.lock; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/2/28 + */ +public interface JbootLock { +} diff --git a/src/main/java/io/jboot/objects/map/JbootMap.java b/src/main/java/io/jboot/objects/map/JbootMap.java new file mode 100644 index 00000000..119cfbc3 --- /dev/null +++ b/src/main/java/io/jboot/objects/map/JbootMap.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.objects.map; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/2/28 + */ +public interface JbootMap { +} diff --git a/src/main/java/io/jboot/objects/map/JbootMapManager.java b/src/main/java/io/jboot/objects/map/JbootMapManager.java new file mode 100644 index 00000000..2e8d6556 --- /dev/null +++ b/src/main/java/io/jboot/objects/map/JbootMapManager.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.objects.map; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/2/28 + */ +public class JbootMapManager { +} diff --git a/src/main/java/io/jboot/objects/multimap/JbootMultimap.java b/src/main/java/io/jboot/objects/multimap/JbootMultimap.java new file mode 100644 index 00000000..c34a4525 --- /dev/null +++ b/src/main/java/io/jboot/objects/multimap/JbootMultimap.java @@ -0,0 +1,8 @@ +package io.jboot.objects.multimap; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/2/28 + */ +public interface JbootMultimap { +} diff --git a/src/main/java/io/jboot/objects/set/JbootSet.java b/src/main/java/io/jboot/objects/set/JbootSet.java new file mode 100644 index 00000000..c27749c7 --- /dev/null +++ b/src/main/java/io/jboot/objects/set/JbootSet.java @@ -0,0 +1,8 @@ +package io.jboot.objects.set; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/2/28 + */ +public interface JbootSet { +} -- Gitee From 12779ba1a074d3a19fec8a83066f33cb9851b62e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 1 Mar 2020 11:18:35 +0800 Subject: [PATCH 0015/1965] optimize Mq component --- src/main/java/io/jboot/components/mq/JbootmqConfig.java | 7 ++++++- src/main/java/io/jboot/components/mq/JbootmqManager.java | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/mq/JbootmqConfig.java b/src/main/java/io/jboot/components/mq/JbootmqConfig.java index 72c9c548..60832357 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqConfig.java +++ b/src/main/java/io/jboot/components/mq/JbootmqConfig.java @@ -16,6 +16,7 @@ package io.jboot.components.mq; import io.jboot.app.config.annotation.ConfigModel; +import io.jboot.utils.StrUtil; @ConfigModel(prefix = "jboot.mq") @@ -26,7 +27,7 @@ public class JbootmqConfig { public static final String TYPE_RABBITMQ = "rabbitmq"; public static final String TYPE_QPID = "qpid"; - private String type = TYPE_REDIS; + private String type; private String channel; private String syncRecevieMessageChannel; //可同步接收消息的channel配置 private String serializer; @@ -63,4 +64,8 @@ public class JbootmqConfig { public void setSyncRecevieMessageChannel(String syncRecevieMessageChannel) { this.syncRecevieMessageChannel = syncRecevieMessageChannel; } + + public boolean isConfigOk(){ + return StrUtil.isNotEmpty(type); + } } diff --git a/src/main/java/io/jboot/components/mq/JbootmqManager.java b/src/main/java/io/jboot/components/mq/JbootmqManager.java index c5f13097..827b44df 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqManager.java +++ b/src/main/java/io/jboot/components/mq/JbootmqManager.java @@ -55,6 +55,10 @@ public class JbootmqManager { throw new IllegalArgumentException("config must not be null"); } + if (!config.isConfigOk()){ + return null; + } + switch (config.getType()) { case JbootmqConfig.TYPE_REDIS: return new JbootRedismqImpl(); -- Gitee From a96cc49295f7f79167bf7145637c7305438a77ac Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 1 Mar 2020 11:18:55 +0800 Subject: [PATCH 0016/1965] add jboot object --- .../io/jboot/objects/object/JbootObject.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/io/jboot/objects/object/JbootObject.java diff --git a/src/main/java/io/jboot/objects/object/JbootObject.java b/src/main/java/io/jboot/objects/object/JbootObject.java new file mode 100644 index 00000000..fc756d95 --- /dev/null +++ b/src/main/java/io/jboot/objects/object/JbootObject.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.objects.object; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/2/29 + */ +public class JbootObject { +} -- Gitee From 95dba815f9317316d306e1495121c10b204b586f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 1 Mar 2020 11:19:23 +0800 Subject: [PATCH 0017/1965] optimize pom.xml --- pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pom.xml b/pom.xml index fce9f8fb..106f7804 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,7 @@ 2.10.2 1.12.2 2.10.6 + 2.8.1 3.9 2.6 1.13 @@ -205,6 +206,12 @@ ${ehcache.version} + + com.github.ben-manes.caffeine + caffeine + ${caffeine.version} + + org.apache.commons commons-lang3 -- Gitee From e03d00c67dcd9e26a1d3462ec3da3b08754a1e77 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 2 Mar 2020 18:20:54 +0800 Subject: [PATCH 0018/1965] optimize pom.xml --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 106f7804..0baefe5c 100644 --- a/pom.xml +++ b/pom.xml @@ -52,11 +52,11 @@ 2.0.29.Final 1.7.30 2.57 - 1.2.62 + 1.2.66 28.2-jre 2.2.5 2.10.2 - 1.12.2 + 1.13.1 2.10.6 2.8.1 3.9 @@ -69,7 +69,7 @@ 2.7.5 1.1.0 1.1.7 - 4.1.2 + 4.1.4 1.8 1.8 @@ -414,7 +414,7 @@ io.dropwizard.metrics metrics-graphite - 4.1.1 + 4.1.4 provided -- Gitee From 0e5d1a8f153ac9b8b1cb04c0994ef091385bb190 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 2 Mar 2020 18:22:39 +0800 Subject: [PATCH 0019/1965] add refresh cache methods --- .../io/jboot/components/cache/JbootCache.java | 4 ++ .../components/cache/JbootCacheBase.java | 9 +++ .../cache/caredis/JbootCaredisCacheImpl.java | 11 +++ .../cache/ehcache/JbootEhcacheImpl.java | 2 + .../cache/ehredis/JbootEhredisCacheImpl.java | 14 ++++ .../components/cache/j2cache/J2cacheImpl.java | 68 ++++++++++++++++++- .../components/cache/none/NoneCacheImpl.java | 10 +++ 7 files changed, 116 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/JbootCache.java b/src/main/java/io/jboot/components/cache/JbootCache.java index 504ba477..9dd5a279 100644 --- a/src/main/java/io/jboot/components/cache/JbootCache.java +++ b/src/main/java/io/jboot/components/cache/JbootCache.java @@ -46,4 +46,8 @@ public interface JbootCache extends com.jfinal.plugin.activerecord.cache.ICache public void setTtl(String cacheName, Object key, int seconds); + public void refresh(String cacheName, Object key); + + public void refresh(String cacheName); + } diff --git a/src/main/java/io/jboot/components/cache/JbootCacheBase.java b/src/main/java/io/jboot/components/cache/JbootCacheBase.java index 9d10e83e..75c89913 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheBase.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheBase.java @@ -18,4 +18,13 @@ package io.jboot.components.cache; public abstract class JbootCacheBase implements JbootCache { + @Override + public void refresh(String cacheName, Object key) { + + } + + @Override + public void refresh(String cacheName) { + + } } diff --git a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java index 9690fa3d..a3c49c06 100644 --- a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java @@ -198,7 +198,18 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { } finally { publishMessage(JbootCaredisMessage.ACTION_REMOVE, cacheName, key); } + } + + @Override + public void refresh(String cacheName, Object key) { + publishMessage(JbootCaredisMessage.ACTION_REMOVE, cacheName, key); + } + + + @Override + public void refresh(String cacheName) { + publishMessage(JbootCaredisMessage.ACTION_REMOVE_ALL, cacheName, null); } diff --git a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java index c6467512..6a5799f9 100644 --- a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java @@ -159,6 +159,8 @@ public class JbootEhcacheImpl extends JbootCacheBase { getOrAddCache(cacheName).put(element); } + + public CacheManager getCacheManager() { return cacheManager; } diff --git a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java index d773e0f4..f930acb6 100644 --- a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java @@ -214,6 +214,20 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL keysCache.invalidate(cacheName); } + + @Override + public void refresh(String cacheName, Object key) { + publishMessage(JbootEhredisMessage.ACTION_REMOVE, cacheName, key); + } + + + @Override + public void refresh(String cacheName) { + publishMessage(JbootEhredisMessage.ACTION_REMOVE_ALL, cacheName,null); + } + + + public void onMessage(String channel, Object obj) { JbootEhredisMessage message = (JbootEhredisMessage) obj; diff --git a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java index 7f9da59a..8bfd666f 100644 --- a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java +++ b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java @@ -16,11 +16,13 @@ package io.jboot.components.cache.j2cache; import com.jfinal.plugin.ehcache.IDataLoader; -import io.jboot.components.cache.JbootCache; +import io.jboot.components.cache.JbootCacheBase; import io.jboot.exception.JbootException; +import net.oschina.j2cache.CacheChannel; import net.oschina.j2cache.CacheObject; import net.oschina.j2cache.J2Cache; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -30,7 +32,7 @@ import java.util.List; * @version V1.0 * @Package io.jboot.core.cache.j2cache */ -public class J2cacheImpl implements JbootCache { +public class J2cacheImpl extends JbootCacheBase { @Override public T get(String cacheName, Object key) { @@ -90,6 +92,68 @@ public class J2cacheImpl implements JbootCache { return (T) data; } + private Method sendEvictCmdMethod; + + @Override + public void refresh(String cacheName, Object key) { + if (sendEvictCmdMethod == null) { + synchronized (this) { + if (sendEvictCmdMethod == null) { + sendEvictCmdMethod = getSendEvictCmdMethod(); + } + } + } + try { + sendEvictCmdMethod.invoke(J2Cache.getChannel(), cacheName, key); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + private Method sendClearCmdMethod; + + @Override + public void refresh(String cacheName) { + if (sendClearCmdMethod == null) { + synchronized (this) { + if (sendClearCmdMethod == null) { + sendClearCmdMethod = getSendClearCmdMethod(); + } + } + } + try { + sendClearCmdMethod.invoke(J2Cache.getChannel(), cacheName); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + private Method getSendEvictCmdMethod() { + try { + Method method = CacheChannel.class.getDeclaredMethod("sendEvictCmd", String.class, String[].class); + method.setAccessible(true); + return method; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + + private Method getSendClearCmdMethod() { + try { + Method method = CacheChannel.class.getDeclaredMethod("sendClearCmd", String.class); + method.setAccessible(true); + return method; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @Override public Integer getTtl(String cacheName, Object key) { throw new JbootException("getTtl not support in j2cache"); diff --git a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java index 77f00dd1..f9e8338d 100644 --- a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java @@ -74,4 +74,14 @@ public class NoneCacheImpl implements JbootCache { public void setTtl(String cacheName, Object key, int seconds) { //do nothing } + + @Override + public void refresh(String cacheName, Object key) { + + } + + @Override + public void refresh(String cacheName) { + + } } -- Gitee From aa332b0f58325f8a11f6b00343be423e8ec1c797 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 2 Mar 2020 18:35:27 +0800 Subject: [PATCH 0020/1965] v3.0.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0baefe5c..6c13bbfc 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.0.2 + 3.0.3-SNAPSHOT jar jboot -- Gitee From b3e43668c3802ce8f4bc4b6c75659007e6c17f05 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 2 Mar 2020 21:57:30 +0800 Subject: [PATCH 0021/1965] =?UTF-8?q?fixed=EF=BC=9Asql=20paras=20is=20erro?= =?UTF-8?q?r=20if=20Columns=20use=20group()=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/Group.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/model/Group.java b/src/main/java/io/jboot/db/model/Group.java index 1060cc6e..260e7ae8 100644 --- a/src/main/java/io/jboot/db/model/Group.java +++ b/src/main/java/io/jboot/db/model/Group.java @@ -34,6 +34,15 @@ public class Group extends Column { @Override public boolean hasPara() { - return false; + return true; + } + + @Override + public Object getValue() { + if (columns == null || columns.isEmpty()){ + return null; + }else { + return Util.getValueArray(columns.getList()); + } } } -- Gitee From e7a4ef57b0afd2bc07b9774c20d6522dec4c0184 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 3 Mar 2020 11:58:49 +0800 Subject: [PATCH 0022/1965] optimize JbootDirectiveBase --- .../directive/base/JbootDirectiveBase.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java b/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java index 2461616a..b8943a3d 100644 --- a/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java +++ b/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java @@ -22,6 +22,7 @@ import com.jfinal.template.TemplateException; import com.jfinal.template.expr.ast.ExprList; import com.jfinal.template.io.Writer; import com.jfinal.template.stat.Scope; +import io.jboot.utils.StrUtil; import java.io.IOException; import java.math.BigInteger; @@ -93,7 +94,8 @@ public abstract class JbootDirectiveBase extends Directive { if (object == null || object instanceof Integer) { return (Integer) object; } - return Integer.valueOf(object.toString()); + String objStr = object.toString(); + return StrUtil.isBlank(objStr) ? null : Integer.valueOf(objStr); } public Integer getParaToInt(String key, Scope scope, Integer defaultValue) { @@ -106,7 +108,8 @@ public abstract class JbootDirectiveBase extends Directive { if (object == null || object instanceof Integer) { return (Integer) object; } - return Integer.valueOf(object.toString()); + String objStr = object.toString(); + return StrUtil.isBlank(objStr) ? null : Integer.valueOf(objStr); } public Integer getParaToInt(int index, Scope scope, Integer defaultValue) { @@ -120,7 +123,8 @@ public abstract class JbootDirectiveBase extends Directive { if (object == null || object instanceof Long) { return (Long) object; } - return Long.valueOf(object.toString()); + String objStr = object.toString(); + return StrUtil.isBlank(objStr) ? null : Long.valueOf(objStr); } public Long getParaToLong(String key, Scope scope, Long defaultValue) { @@ -133,7 +137,8 @@ public abstract class JbootDirectiveBase extends Directive { if (object == null || object instanceof Long) { return (Long) object; } - return Long.valueOf(object.toString()); + String objStr = object.toString(); + return StrUtil.isBlank(objStr) ? null : Long.valueOf(objStr); } public Long getParaToLong(int index, Scope scope, Long defaultValue) { @@ -146,7 +151,8 @@ public abstract class JbootDirectiveBase extends Directive { if (object == null || object instanceof Boolean) { return (Boolean) object; } - return Boolean.valueOf(object.toString()); + String objStr = object.toString(); + return StrUtil.isBlank(objStr) ? null : Boolean.valueOf(objStr); } public Boolean getParaToBool(String key, Scope scope, Boolean defaultValue) { @@ -159,7 +165,8 @@ public abstract class JbootDirectiveBase extends Directive { if (object == null || object instanceof Boolean) { return (Boolean) object; } - return Boolean.valueOf(object.toString()); + String objStr = object.toString(); + return StrUtil.isBlank(objStr) ? null : Boolean.valueOf(objStr); } public Boolean getParaToBool(int index, Scope scope, Boolean defaultValue) { @@ -172,7 +179,8 @@ public abstract class JbootDirectiveBase extends Directive { if (object == null || object instanceof BigInteger) { return (BigInteger) object; } - return new BigInteger(object.toString()); + String objStr = object.toString(); + return StrUtil.isBlank(objStr) ? null : new BigInteger(objStr); } public BigInteger getParaToBigInteger(String key, Scope scope, BigInteger defaultValue) { @@ -185,7 +193,8 @@ public abstract class JbootDirectiveBase extends Directive { if (object == null || object instanceof BigInteger) { return (BigInteger) object; } - return new BigInteger(object.toString()); + String objStr = object.toString(); + return StrUtil.isBlank(objStr) ? null : new BigInteger(objStr); } public BigInteger getParaToBigInteger(int index, Scope scope, BigInteger defaultValue) { -- Gitee From a86b8ff12a7c0d586b890ab9c545f97b9bca3463 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 3 Mar 2020 12:06:38 +0800 Subject: [PATCH 0023/1965] optimize Columns --- src/main/java/io/jboot/db/model/Columns.java | 106 +++++++++++++++++-- 1 file changed, 100 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 1013090e..f4d9567f 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -197,6 +197,20 @@ public class Columns implements Serializable { } + + + /** + * + * @param name + * @param condition + * @return + */ + public Columns isNullIf(String name,boolean condition) { + this.add(Column.create(name, null, Column.LOGIC_IS_NULL)); + return this; + } + + /** * IS NOT NULL * @@ -209,28 +223,73 @@ public class Columns implements Serializable { } + /** + * IS NOT NULL + * @param name + * @param condition + * @return + */ + public Columns isNotNullIf(String name,boolean condition) { + if (condition) { + this.add(Column.create(name, null, Column.LOGIC_IS_NOT_NULL)); + } + return this; + } + + + /** + * in arrays + * @param name + * @param arrays + * @return + */ public Columns in(String name, Object... arrays) { this.add(Column.create(name, arrays, Column.LOGIC_IN)); return this; } + /** + * not int arrays + * @param name + * @param arrays + * @return + */ public Columns notIn(String name, Object... arrays) { this.add(Column.create(name, arrays, Column.LOGIC_NOT_IN)); return this; } + /** + * between + * @param name + * @param start + * @param end + * @return + */ public Columns between(String name, Object start, Object end) { this.add(Column.create(name, new Object[]{start, end}, Column.LOGIC_BETWEEN)); return this; } + /** + * not between + * @param name + * @param start + * @param end + * @return + */ public Columns notBetween(String name, Object start, Object end) { this.add(Column.create(name, new Object[]{start, end}, Column.LOGIC_NOT_BETWEEN)); return this; } + /** + * group + * @param columns + * @return + */ public Columns group(Columns columns) { if (!columns.isEmpty()) { this.add(new Group(columns)); @@ -238,6 +297,26 @@ public class Columns implements Serializable { return this; } + + /** + * + * @param columns + * @param conditon + * @return + */ + public Columns groupIf(Columns columns,boolean conditon) { + if (conditon && !columns.isEmpty()) { + this.add(new Group(columns)); + } + return this; + } + + + /** + * customize string sql + * @param string + * @return + */ public Columns string(String string) { if (StrUtil.isNotBlank(string)) { this.add(new Str(string)); @@ -246,6 +325,20 @@ public class Columns implements Serializable { } + /** + * customize string sql + * @param string + * @param condition + * @return + */ + public Columns stringIf(String string,boolean condition) { + if (condition && StrUtil.isNotBlank(string)) { + this.add(new Str(string)); + } + return this; + } + + public Columns or() { this.add(new Or()); return this; @@ -298,21 +391,22 @@ public class Columns implements Serializable { return s.toString(); } + private static final char SQL_CACHE_SEPARATOR = '-'; private void buildCacheKey(StringBuilder s, List columns) { for (Column column : columns) { if (column instanceof Or) { - s.append("or").append("-"); + s.append("or").append(SQL_CACHE_SEPARATOR); } else if (column instanceof Group) { - s.append("("); + s.append('('); buildCacheKey(s, ((Group) column).getColumns().getList()); - s.append(")-"); + s.append(')').append(SQL_CACHE_SEPARATOR); } else if (column instanceof Str) { s.append(deleteWhitespace(((Str) column).getString())).append("-"); } else { s.append(column.getName()) - .append("-") + .append(SQL_CACHE_SEPARATOR) .append(getLogicStr(column.getLogic())) - .append("-"); + .append(SQL_CACHE_SEPARATOR); Object value = column.getValue(); if (value != null) { if (value.getClass().isArray()) { @@ -320,7 +414,7 @@ public class Columns implements Serializable { } else { s.append(column.getValue()); } - s.append("-"); + s.append(SQL_CACHE_SEPARATOR); } } } -- Gitee From 60a1cac4afaa6df700947e556e89b05aa64b39c3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 3 Mar 2020 15:23:46 +0800 Subject: [PATCH 0024/1965] optimize SqlDebugger --- src/main/java/io/jboot/db/SqlDebugger.java | 59 ++++++++++--------- .../java/io/jboot/db/dbpro/JbootDbPro.java | 6 +- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/main/java/io/jboot/db/SqlDebugger.java b/src/main/java/io/jboot/db/SqlDebugger.java index 60676e95..aeaec370 100644 --- a/src/main/java/io/jboot/db/SqlDebugger.java +++ b/src/main/java/io/jboot/db/SqlDebugger.java @@ -53,39 +53,42 @@ public class SqlDebugger { } public static void debug(Config config, String sql, Object... paras) { - if (printer.isPrint(config, sql, paras)) { - if (paras != null) { - for (Object value : paras) { - // null - if (value == null) { - sql = sql.replaceFirst("\\?", "null"); - } - // number - else if (value instanceof Number) { - sql = sql.replaceFirst("\\?", value.toString()); - } - // numeric - else if (value instanceof String && StrUtil.isNumeric((String) value)) { - sql = sql.replaceFirst("\\?", (String) value); - } - // other - else { - StringBuilder sb = new StringBuilder(); - sb.append("'"); - if (value instanceof Date) { - sb.append(DateKit.toStr((Date) value, DateKit.timeStampPattern)); - } else { - sb.append(value.toString()); - } - sb.append("'"); - sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(sb.toString())); + if (!printer.isPrint(config, sql, paras)) { + return; + } + + + if (paras != null) { + for (Object value : paras) { + // null + if (value == null) { + sql = sql.replaceFirst("\\?", "null"); + } + // number + else if (value instanceof Number) { + sql = sql.replaceFirst("\\?", value.toString()); + } + // numeric + else if (value instanceof String && StrUtil.isNumeric((String) value)) { + sql = sql.replaceFirst("\\?", (String) value); + } + // other + else { + StringBuilder sb = new StringBuilder(); + sb.append("'"); + if (value instanceof Date) { + sb.append(DateKit.toStr((Date) value, DateKit.timeStampPattern)); + } else { + sb.append(value.toString()); } + sb.append("'"); + sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(sb.toString())); } } - - printer.print(sql); } + + printer.print(sql); } diff --git a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java index 251877a7..b413727f 100644 --- a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java +++ b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java @@ -72,6 +72,9 @@ public class JbootDbPro extends DbPro { dialect.forDbSave(tableName, pKeys, record, sql, paras); + //add sql debug support + SqlDebugger.debug(config, sql.toString(), paras.toArray()); + PreparedStatement pst; if (dialect.isOracle()) { pst = conn.prepareStatement(sql.toString(), pKeys); @@ -89,8 +92,6 @@ public class JbootDbPro extends DbPro { throw new ActiveRecordException(e); } } - //add sql debug support - SqlDebugger.debug(config, sql.toString(), paras.toArray()); return result >= 1; } @@ -124,4 +125,5 @@ public class JbootDbPro extends DbPro { return columns.isEmpty() ? delete(sql) : delete(sql, columns.getValueArray()); } + } -- Gitee From ae419b05edb1a1a8e17903775677e69d64db956c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 3 Mar 2020 16:01:27 +0800 Subject: [PATCH 0025/1965] upgrade dependencies --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6c13bbfc..20392f42 100644 --- a/pom.xml +++ b/pom.xml @@ -47,7 +47,7 @@ 2.4 3.3.0 3.4.2 - 2.10.2 + 2.10.3 5.1.48 2.0.29.Final 1.7.30 -- Gitee From 7dfb0ff604cd7ca6cc3fcf385bc08a5d95cf524f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 3 Mar 2020 18:42:59 +0800 Subject: [PATCH 0026/1965] optimize jboot caches --- .../cache/caredis/JbootCaredisCacheImpl.java | 7 ++----- .../cache/ehredis/JbootEhredisCacheImpl.java | 11 +++-------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java index a3c49c06..6743e765 100644 --- a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java @@ -226,9 +226,8 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { public void onMessage(String channel, Object obj) { JbootCaredisMessage message = (JbootCaredisMessage) obj; - /** - * 不处理自己发送的消息 - */ + + //不处理自己发送的消息 if (clientId.equals(message.getClientId())) { return; } @@ -237,8 +236,6 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { switch (message.getAction()) { case JbootCaredisMessage.ACTION_PUT: - caffeineCacheImpl.remove(message.getCacheName(), message.getKey()); - break; case JbootCaredisMessage.ACTION_REMOVE: caffeineCacheImpl.remove(message.getCacheName(), message.getKey()); break; diff --git a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java index f930acb6..93dbc9ca 100644 --- a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java @@ -200,7 +200,6 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL } finally { publishMessage(JbootEhredisMessage.ACTION_REMOVE, cacheName, key); } - } @@ -223,17 +222,15 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL @Override public void refresh(String cacheName) { - publishMessage(JbootEhredisMessage.ACTION_REMOVE_ALL, cacheName,null); + publishMessage(JbootEhredisMessage.ACTION_REMOVE_ALL, cacheName, null); } - public void onMessage(String channel, Object obj) { JbootEhredisMessage message = (JbootEhredisMessage) obj; - /** - * 不处理自己发送的消息 - */ + + //不处理自己发送的消息 if (clientId.equals(message.getClientId())) { return; } @@ -242,8 +239,6 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL switch (message.getAction()) { case JbootEhredisMessage.ACTION_PUT: - ehcacheImpl.remove(message.getCacheName(), message.getKey()); - break; case JbootEhredisMessage.ACTION_REMOVE: ehcacheImpl.remove(message.getCacheName(), message.getKey()); break; -- Gitee From 7da9269418150f1bbbdfa78a6fd4097e5973ffc7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 4 Mar 2020 14:39:45 +0800 Subject: [PATCH 0027/1965] add : JbootModelFilter PrimaryKeyValueGenerator config support --- .../java/io/jboot/db/model/JbootModel.java | 16 +++-- .../io/jboot/db/model/JbootModelConfig.java | 58 +++++++++++++++++++ .../io/jboot/db/model/JbootModelFilter.java | 27 +++++++++ .../db/model/PrimarykeyValueGenerator.java | 29 ++++++++++ 4 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 src/main/java/io/jboot/db/model/JbootModelFilter.java create mode 100644 src/main/java/io/jboot/db/model/PrimarykeyValueGenerator.java diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 010c735a..55f73ac1 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -171,10 +171,15 @@ public class JbootModel> extends Model { set(column_created, new Date()); } - boolean needInitPrimaryKey = (String.class == _getPrimaryType() && null == get(_getPrimaryKey())); +// boolean needInitPrimaryKey = (String.class == _getPrimaryType() && null == get(_getPrimaryKey())); +// +// if (needInitPrimaryKey) { +// set(_getPrimaryKey(), generatePrimaryValue()); +// } - if (needInitPrimaryKey) { - set(_getPrimaryKey(), generatePrimaryValue()); + // 生成主键 + if (get(_getPrimaryKey()) == null ) { + set(_getPrimaryKey(), config.getPrimarykeyValueGeneratorObj().genValue(this, _getPrimaryType())); } @@ -217,8 +222,9 @@ public class JbootModel> extends Model { } - protected String generatePrimaryValue() { - return StrUtil.uuid(); + @Override + protected void filter(int filterBy) { + config.getFilterObj().filter(this, filterBy); } @Override diff --git a/src/main/java/io/jboot/db/model/JbootModelConfig.java b/src/main/java/io/jboot/db/model/JbootModelConfig.java index 8d82b1f0..99eab251 100644 --- a/src/main/java/io/jboot/db/model/JbootModelConfig.java +++ b/src/main/java/io/jboot/db/model/JbootModelConfig.java @@ -20,6 +20,8 @@ import io.jboot.app.config.annotation.ConfigModel; import io.jboot.components.cache.JbootCache; import io.jboot.components.cache.JbootCacheConfig; import io.jboot.components.cache.JbootCacheManager; +import io.jboot.utils.ClassUtil; +import io.jboot.utils.StrUtil; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) @@ -36,6 +38,11 @@ public class JbootModelConfig { private String columnModified = "modified"; private int idCacheTime = 60 * 60 * 1; // id 缓存默认缓存1个小时 private boolean idCacheEnable = true; // 是否启用ID自动缓存 + + private String filter; + private String primarykeyValueGenerator; + + private String idCacheType = Jboot.config(JbootCacheConfig.class).getType(); @@ -95,6 +102,57 @@ public class JbootModelConfig { this.idCacheType = idCacheType; } + + public String getFilter() { + return filter; + } + + public void setFilter(String filter) { + this.filter = filter; + } + + public String getPrimarykeyValueGenerator() { + return primarykeyValueGenerator; + } + + public void setPrimarykeyValueGenerator(String primarykeyValueGenerator) { + this.primarykeyValueGenerator = primarykeyValueGenerator; + } + + private JbootModelFilter filterObj; + public JbootModelFilter getFilterObj() { + if (filterObj == null){ + if (StrUtil.isNotBlank(filter)){ + synchronized (this){ + if (filterObj == null){ + filterObj = ClassUtil.newInstance(filter); + } + } + }else { + filterObj = JbootModelFilter.DEFAULT; + } + } + return filterObj; + } + + + private PrimarykeyValueGenerator primarykeyValueGeneratorObj; + public PrimarykeyValueGenerator getPrimarykeyValueGeneratorObj() { + if (primarykeyValueGeneratorObj == null){ + if (StrUtil.isNotBlank(primarykeyValueGenerator)){ + synchronized (this){ + if (primarykeyValueGeneratorObj == null){ + primarykeyValueGeneratorObj = ClassUtil.newInstance(primarykeyValueGenerator); + } + } + }else { + primarykeyValueGeneratorObj = PrimarykeyValueGenerator.DEFAULT; + } + } + return primarykeyValueGeneratorObj; + } + + private static JbootModelConfig config; public static JbootModelConfig getConfig() { diff --git a/src/main/java/io/jboot/db/model/JbootModelFilter.java b/src/main/java/io/jboot/db/model/JbootModelFilter.java new file mode 100644 index 00000000..1cef7b67 --- /dev/null +++ b/src/main/java/io/jboot/db/model/JbootModelFilter.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.db.model; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/4 + */ +public interface JbootModelFilter { + + static JbootModelFilter DEFAULT = (model, filterBy) -> {}; + + public void filter(JbootModel model,int filterBy); +} diff --git a/src/main/java/io/jboot/db/model/PrimarykeyValueGenerator.java b/src/main/java/io/jboot/db/model/PrimarykeyValueGenerator.java new file mode 100644 index 00000000..84e9cad8 --- /dev/null +++ b/src/main/java/io/jboot/db/model/PrimarykeyValueGenerator.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.db.model; + +import io.jboot.utils.StrUtil; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/4 + */ +public interface PrimarykeyValueGenerator { + + static PrimarykeyValueGenerator DEFAULT = (model, primarykeyValueClass) -> primarykeyValueClass == String.class ? StrUtil.uuid() : null; + + public Object genValue(JbootModel model,Class primarykeyValueClass); +} -- Gitee From 129781927025147451b04240d621f772d064bb61 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 4 Mar 2020 14:54:18 +0800 Subject: [PATCH 0028/1965] add : PrimaryKeyValueGenerator config support --- src/main/java/io/jboot/db/model/JbootModel.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 55f73ac1..8efd23bc 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -177,9 +177,13 @@ public class JbootModel> extends Model { // set(_getPrimaryKey(), generatePrimaryValue()); // } - // 生成主键 - if (get(_getPrimaryKey()) == null ) { - set(_getPrimaryKey(), config.getPrimarykeyValueGeneratorObj().genValue(this, _getPrimaryType())); + // 生成主键,只对单一主键的表生成,如果是多主键,不生成。 + String[] pkeys = _getPrimaryKeys(); + if (pkeys != null && pkeys.length == 1 && get(pkeys[0]) == null) { + Object value = config.getPrimarykeyValueGeneratorObj().genValue(this, _getPrimaryType()); + if (value != null) { + set(pkeys[0], value); + } } -- Gitee From 33a030a9a976b13155b3d0be7dd2e5445cd7393c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 5 Mar 2020 10:01:53 +0800 Subject: [PATCH 0029/1965] v3.0.3 --- .../io/jboot/db/model/JbootModelConfig.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModelConfig.java b/src/main/java/io/jboot/db/model/JbootModelConfig.java index 99eab251..abea8e0e 100644 --- a/src/main/java/io/jboot/db/model/JbootModelConfig.java +++ b/src/main/java/io/jboot/db/model/JbootModelConfig.java @@ -36,10 +36,27 @@ public class JbootModelConfig { private String columnCreated = "created"; private String columnModified = "modified"; - private int idCacheTime = 60 * 60 * 1; // id 缓存默认缓存1个小时 - private boolean idCacheEnable = true; // 是否启用ID自动缓存 + /** + * 是否启用 id 缓存,如果启用,当根据 id 查询的时候,会自动存入缓存 + * 下次再通过 id 查询的时候,直接从缓存中获取 Model + */ + private boolean idCacheEnable = true; + + /** + * id 缓存的时间,默认为 1 个小时,单位:秒 + */ + private int idCacheTime = 60 * 60 * 1; + + /** + * Model 过滤器,可以通过这个配置来防止 xss 等问题 + * filter 会在 save 和 update 的时候被执行 + */ private String filter; + + /** + * 主键的值的生成器,可以通过配置这个来自定义主键的生成策略 + */ private String primarykeyValueGenerator; -- Gitee From c163f65dfb3637310e56442582cc64ef314cda9c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 5 Mar 2020 10:43:05 +0800 Subject: [PATCH 0030/1965] v3.0.3 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9fad2e99..55024f38 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.0.2 + 3.0.3 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index aaa0b0a9..2516cc38 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.0.2 + 3.0.3 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index fc5f447f..abf1ad59 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.0.2 + 3.0.3 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 4a1e1526..ce8ef8d6 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.0.2"; + public static String VERSION = "3.0.3"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 0d34c2f641485bbaec528218c1b0886abc8b1e20 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 5 Mar 2020 10:44:26 +0800 Subject: [PATCH 0031/1965] v3.0.3 release (^.^)YYa!! --- changes.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/changes.txt b/changes.txt index f7a00e55..f2ba2337 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,15 @@ +jboot v3.0.3: +新增:Cache 模块新增 refresh() 的方法,在分布式缓存在某些极端情况下出现不同步的时候进行刷新。 +新增:对 Columns 优化,新增 gourpIf()/stringIf()/isNullIf() 等方法 +新增:新增 PrimaryKeyValueGenerator 对 Model 的主键值生成策略配置 +新增:新增 JbootModelFilter 对 Model 的过滤器策略配置,可以用于在 save 或者 update 的时候防止 Model 存在 xss 等问题 +优化:对 MQ 进行优化,当不配置的时候可能返回一个错误的 MQ 对象的问题 +优化:升级 JFinal-Wexin、Fastjson、Fastxml 等依赖到最新版本 +优化:JbootDirectiveBase 当传入空值的时候明确返回 null +修复:当 Columns 查询的时候,使用 group 会出现 value 内容缺失的问题 + + + jboot v3.0.2: 新增:StrUtil 新增 splitToSetByComma() 方法 新增:StrUtil 新增 escapeModel() 和 escapeMap() 方法 -- Gitee From 43b939cd222dd17ef828e5fcfd9f5b373822ff01 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 5 Mar 2020 10:44:46 +0800 Subject: [PATCH 0032/1965] v3.0.3 release (^.^)YYa!! --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 20392f42..7720d3a8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.0.3-SNAPSHOT + 3.0.3 jar jboot @@ -44,7 +44,7 @@ 4.8 2019.8 2.0 - 2.4 + 2.5 3.3.0 3.4.2 2.10.3 -- Gitee From ce829750f29285f6aabec2dbde2229e56cffba84 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 6 Mar 2020 10:29:43 +0800 Subject: [PATCH 0033/1965] =?UTF-8?q?fixed=EF=BC=9AColumns.str()=20method?= =?UTF-8?q?=20can=20not=20work?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/Columns.java | 2 ++ src/main/java/io/jboot/db/model/SqlBuilder.java | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index f4d9567f..c62b3525 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -539,6 +539,8 @@ public class Columns implements Serializable { columns.between("name", "123", "1233"); System.out.println(columns.getCacheKey()); + columns.string("group by xxx"); + System.out.println(Arrays.toString(columns.getValueArray())); System.out.println(columns.toMysqlSql()); System.out.println(columns.toSqlServerSql()); diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index 3cb24506..db227fb9 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -63,7 +63,7 @@ public class SqlBuilder { } // string else if (curent instanceof Str) { - sqlBuilder.append(((Str) curent).getString()); + sqlBuilder.append(' ').append(((Str) curent).getString()); } // group else if (curent instanceof Group) { @@ -87,7 +87,10 @@ public class SqlBuilder { } } - appendLinkString(sqlBuilder, next); + // if next is Str, no need append 'AND' or 'OR' + if (!(next instanceof Str)) { + appendLinkString(sqlBuilder, next); + } } } -- Gitee From 4148bcff1eb027d59114ba9b8cea31075b008edd Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 6 Mar 2020 12:01:51 +0800 Subject: [PATCH 0034/1965] rename Columns.string() to Columns.sqlPart() --- src/main/java/io/jboot/db/model/Columns.java | 47 +++++++++++-------- .../java/io/jboot/db/model/SqlBuilder.java | 8 ++-- .../jboot/db/model/{Str.java => SqlPart.java} | 16 +++---- 3 files changed, 39 insertions(+), 32 deletions(-) rename src/main/java/io/jboot/db/model/{Str.java => SqlPart.java} (76%) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index c62b3525..5dca48ae 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -197,16 +197,15 @@ public class Columns implements Serializable { } - - /** - * * @param name * @param condition * @return */ - public Columns isNullIf(String name,boolean condition) { - this.add(Column.create(name, null, Column.LOGIC_IS_NULL)); + public Columns isNullIf(String name, boolean condition) { + if (condition) { + this.add(Column.create(name, null, Column.LOGIC_IS_NULL)); + } return this; } @@ -225,11 +224,12 @@ public class Columns implements Serializable { /** * IS NOT NULL + * * @param name * @param condition * @return */ - public Columns isNotNullIf(String name,boolean condition) { + public Columns isNotNullIf(String name, boolean condition) { if (condition) { this.add(Column.create(name, null, Column.LOGIC_IS_NOT_NULL)); } @@ -239,6 +239,7 @@ public class Columns implements Serializable { /** * in arrays + * * @param name * @param arrays * @return @@ -250,6 +251,7 @@ public class Columns implements Serializable { /** * not int arrays + * * @param name * @param arrays * @return @@ -262,6 +264,7 @@ public class Columns implements Serializable { /** * between + * * @param name * @param start * @param end @@ -274,6 +277,7 @@ public class Columns implements Serializable { /** * not between + * * @param name * @param start * @param end @@ -287,6 +291,7 @@ public class Columns implements Serializable { /** * group + * * @param columns * @return */ @@ -299,12 +304,11 @@ public class Columns implements Serializable { /** - * * @param columns * @param conditon * @return */ - public Columns groupIf(Columns columns,boolean conditon) { + public Columns groupIf(Columns columns, boolean conditon) { if (conditon && !columns.isEmpty()) { this.add(new Group(columns)); } @@ -314,12 +318,13 @@ public class Columns implements Serializable { /** * customize string sql - * @param string + * + * @param sql * @return */ - public Columns string(String string) { - if (StrUtil.isNotBlank(string)) { - this.add(new Str(string)); + public Columns sqlPart(String sql) { + if (StrUtil.isNotBlank(sql)) { + this.add(new SqlPart(sql)); } return this; } @@ -327,13 +332,14 @@ public class Columns implements Serializable { /** * customize string sql - * @param string + * + * @param sql * @param condition * @return */ - public Columns stringIf(String string,boolean condition) { - if (condition && StrUtil.isNotBlank(string)) { - this.add(new Str(string)); + public Columns sqlPartIf(String sql, boolean condition) { + if (condition && StrUtil.isNotBlank(sql)) { + this.add(new SqlPart(sql)); } return this; } @@ -392,6 +398,7 @@ public class Columns implements Serializable { } private static final char SQL_CACHE_SEPARATOR = '-'; + private void buildCacheKey(StringBuilder s, List columns) { for (Column column : columns) { if (column instanceof Or) { @@ -400,8 +407,8 @@ public class Columns implements Serializable { s.append('('); buildCacheKey(s, ((Group) column).getColumns().getList()); s.append(')').append(SQL_CACHE_SEPARATOR); - } else if (column instanceof Str) { - s.append(deleteWhitespace(((Str) column).getString())).append("-"); + } else if (column instanceof SqlPart) { + s.append(deleteWhitespace(((SqlPart) column).getSql())).append("-"); } else { s.append(column.getName()) .append(SQL_CACHE_SEPARATOR) @@ -523,7 +530,7 @@ public class Columns implements Serializable { System.out.println(columns.getCacheKey()); columns.ge("age", 10); - columns.string("user.id != 1"); + columns.sqlPart("and user.id != 1"); System.out.println(columns.getCacheKey()); columns.group(Columns.create().likeAppendPercent("name", "lisi").eq("age", 20)); @@ -539,7 +546,7 @@ public class Columns implements Serializable { columns.between("name", "123", "1233"); System.out.println(columns.getCacheKey()); - columns.string("group by xxx"); + columns.sqlPart("group by xxx"); System.out.println(Arrays.toString(columns.getValueArray())); System.out.println(columns.toMysqlSql()); diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index db227fb9..b44ff263 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -62,8 +62,8 @@ public class SqlBuilder { continue; } // string - else if (curent instanceof Str) { - sqlBuilder.append(' ').append(((Str) curent).getString()); + else if (curent instanceof SqlPart) { + sqlBuilder.append(' ').append(((SqlPart) curent).getSql()); } // group else if (curent instanceof Group) { @@ -87,8 +87,8 @@ public class SqlBuilder { } } - // if next is Str, no need append 'AND' or 'OR' - if (!(next instanceof Str)) { + // if next is SqlPart, no need append 'AND' or 'OR' + if (!(next instanceof SqlPart)) { appendLinkString(sqlBuilder, next); } } diff --git a/src/main/java/io/jboot/db/model/Str.java b/src/main/java/io/jboot/db/model/SqlPart.java similarity index 76% rename from src/main/java/io/jboot/db/model/Str.java rename to src/main/java/io/jboot/db/model/SqlPart.java index e31b4ba0..eed054dc 100644 --- a/src/main/java/io/jboot/db/model/Str.java +++ b/src/main/java/io/jboot/db/model/SqlPart.java @@ -16,20 +16,20 @@ package io.jboot.db.model; -class Str extends Column { +class SqlPart extends Column { - private String string; + private String sql; - public Str(String string) { - this.string = string; + public SqlPart(String sql) { + this.sql = sql; } - public String getString() { - return string; + public String getSql() { + return sql; } - public void setString(String string) { - this.string = string; + public void setSql(String sql) { + this.sql = sql; } @Override -- Gitee From 62f68b4a7a8e9b63368665a290f2b061ae61de3f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 7 Mar 2020 09:16:47 +0800 Subject: [PATCH 0035/1965] support set filter and set primarykeyValueGenerator in JbootModelConfig --- .../java/io/jboot/db/model/JbootModel.java | 8 +- .../io/jboot/db/model/JbootModelConfig.java | 76 +++++++++++-------- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 8efd23bc..784c9165 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -180,7 +180,7 @@ public class JbootModel> extends Model { // 生成主键,只对单一主键的表生成,如果是多主键,不生成。 String[] pkeys = _getPrimaryKeys(); if (pkeys != null && pkeys.length == 1 && get(pkeys[0]) == null) { - Object value = config.getPrimarykeyValueGeneratorObj().genValue(this, _getPrimaryType()); + Object value = config.getPrimarykeyValueGenerator().genValue(this, _getPrimaryType()); if (value != null) { set(pkeys[0], value); } @@ -228,7 +228,7 @@ public class JbootModel> extends Model { @Override protected void filter(int filterBy) { - config.getFilterObj().filter(this, filterBy); + config.getFilter().filter(this, filterBy); } @Override @@ -252,7 +252,7 @@ public class JbootModel> extends Model { protected M loadByCache(Object... idValues) { try { - return config.getCache().get(_getTableName() + return config.getIdCache().get(_getTableName() , buildCacheKey(idValues) , () -> JbootModel.super.findByIds(idValues) , config.getIdCacheTime()); @@ -265,7 +265,7 @@ public class JbootModel> extends Model { protected void safeDeleteCache(Object... idValues) { try { - config.getCache().remove(_getTableName() + config.getIdCache().remove(_getTableName() , buildCacheKey(idValues)); } catch (Exception ex) { LOG.error(ex.toString(), ex); diff --git a/src/main/java/io/jboot/db/model/JbootModelConfig.java b/src/main/java/io/jboot/db/model/JbootModelConfig.java index abea8e0e..a7461c28 100644 --- a/src/main/java/io/jboot/db/model/JbootModelConfig.java +++ b/src/main/java/io/jboot/db/model/JbootModelConfig.java @@ -52,12 +52,12 @@ public class JbootModelConfig { * Model 过滤器,可以通过这个配置来防止 xss 等问题 * filter 会在 save 和 update 的时候被执行 */ - private String filter; + private String filterClass; /** * 主键的值的生成器,可以通过配置这个来自定义主键的生成策略 */ - private String primarykeyValueGenerator; + private String primarykeyValueGeneratorClass; private String idCacheType = Jboot.config(JbootCacheConfig.class).getType(); @@ -119,54 +119,62 @@ public class JbootModelConfig { this.idCacheType = idCacheType; } - - public String getFilter() { - return filter; + public String getFilterClass() { + return filterClass; } - public void setFilter(String filter) { - this.filter = filter; + public void setFilterClass(String filterClass) { + this.filterClass = filterClass; } - public String getPrimarykeyValueGenerator() { - return primarykeyValueGenerator; + public String getPrimarykeyValueGeneratorClass() { + return primarykeyValueGeneratorClass; } - public void setPrimarykeyValueGenerator(String primarykeyValueGenerator) { - this.primarykeyValueGenerator = primarykeyValueGenerator; + public void setPrimarykeyValueGeneratorClass(String primarykeyValueGeneratorClass) { + this.primarykeyValueGeneratorClass = primarykeyValueGeneratorClass; } - private JbootModelFilter filterObj; - public JbootModelFilter getFilterObj() { - if (filterObj == null){ - if (StrUtil.isNotBlank(filter)){ + private JbootModelFilter filter; + public JbootModelFilter getFilter() { + if (filter == null){ + if (StrUtil.isNotBlank(filterClass)){ synchronized (this){ - if (filterObj == null){ - filterObj = ClassUtil.newInstance(filter); + if (filter == null){ + filter = ClassUtil.newInstance(filterClass); } } }else { - filterObj = JbootModelFilter.DEFAULT; + filter = JbootModelFilter.DEFAULT; } } - return filterObj; + return filter; + } + + public void setFilter(JbootModelFilter filter) { + this.filter = filter; } - private PrimarykeyValueGenerator primarykeyValueGeneratorObj; - public PrimarykeyValueGenerator getPrimarykeyValueGeneratorObj() { - if (primarykeyValueGeneratorObj == null){ - if (StrUtil.isNotBlank(primarykeyValueGenerator)){ + private PrimarykeyValueGenerator primarykeyValueGenerator; + public PrimarykeyValueGenerator getPrimarykeyValueGenerator() { + if (primarykeyValueGenerator == null){ + if (StrUtil.isNotBlank(primarykeyValueGeneratorClass)){ synchronized (this){ - if (primarykeyValueGeneratorObj == null){ - primarykeyValueGeneratorObj = ClassUtil.newInstance(primarykeyValueGenerator); + if (primarykeyValueGenerator == null){ + primarykeyValueGenerator = ClassUtil.newInstance(primarykeyValueGeneratorClass); } } }else { - primarykeyValueGeneratorObj = PrimarykeyValueGenerator.DEFAULT; + primarykeyValueGenerator = PrimarykeyValueGenerator.DEFAULT; } } - return primarykeyValueGeneratorObj; + return primarykeyValueGenerator; + } + + + public void setPrimarykeyValueGenerator(PrimarykeyValueGenerator primarykeyValueGenerator) { + this.primarykeyValueGenerator = primarykeyValueGenerator; } @@ -179,12 +187,16 @@ public class JbootModelConfig { return config; } - private JbootCache jbootCache; + private JbootCache idCache; - public JbootCache getCache() { - if (jbootCache == null) { - jbootCache = JbootCacheManager.me().getCache(idCacheType); + public JbootCache getIdCache() { + if (idCache == null) { + idCache = JbootCacheManager.me().getCache(idCacheType); } - return jbootCache; + return idCache; + } + + public void setIdCache(JbootCache idCache) { + this.idCache = idCache; } } -- Gitee From 499fda1e728a42d8f3d843ebbc96064c958c8347 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 7 Mar 2020 09:27:54 +0800 Subject: [PATCH 0036/1965] =?UTF-8?q?fixed=EF=BC=9AStrUtil.isNumeric=20is?= =?UTF-8?q?=20not=20correct=20if=20para=20is=20empty=20String.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/utils/StrUtil.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/utils/StrUtil.java b/src/main/java/io/jboot/utils/StrUtil.java index c6aa0955..26bd83dc 100644 --- a/src/main/java/io/jboot/utils/StrUtil.java +++ b/src/main/java/io/jboot/utils/StrUtil.java @@ -151,7 +151,7 @@ public class StrUtil extends StrKit { * @return */ public static boolean isNumeric(String str) { - if (str == null) { + if (isBlank(str)) { return false; } for (int i = str.length(); --i >= 0; ) { @@ -170,7 +170,7 @@ public class StrUtil extends StrKit { * @return */ public static boolean isDecimal(String str) { - if (str == null) { + if (isBlank(str)) { return false; } boolean hasDot = false; @@ -197,18 +197,18 @@ public class StrUtil extends StrKit { * @return */ public static boolean isEmail(String email) { - return Pattern.matches("\\w+@(\\w+.)+[a-z]{2,3}", email); + return isBlank(email) ? false : Pattern.matches("\\w+@(\\w+.)+[a-z]{2,3}", email); } /** * 是否是中国地区手机号码 * - * @param phoneNumber + * @param mobileNumber * @return */ - public static boolean isMobileNumber(String phoneNumber) { - return Pattern.matches("^(1[3,4,5,6,7,8,9])\\d{9}$", phoneNumber); + public static boolean isMobileNumber(String mobileNumber) { + return isBlank(mobileNumber) ? false : Pattern.matches("^(1[3,4,5,6,7,8,9])\\d{9}$", mobileNumber); } -- Gitee From 0de51a414ff0a689924dedaba218bc54879220dd Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 7 Mar 2020 09:38:33 +0800 Subject: [PATCH 0037/1965] add Local mq Impl --- .../io/jboot/components/mq/JbootmqConfig.java | 1 + .../jboot/components/mq/JbootmqManager.java | 5 ++- .../components/mq/local/JbootLocalmqImpl.java | 40 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java diff --git a/src/main/java/io/jboot/components/mq/JbootmqConfig.java b/src/main/java/io/jboot/components/mq/JbootmqConfig.java index 60832357..0d823041 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqConfig.java +++ b/src/main/java/io/jboot/components/mq/JbootmqConfig.java @@ -26,6 +26,7 @@ public class JbootmqConfig { public static final String TYPE_ALIYUNMQ = "aliyunmq"; public static final String TYPE_RABBITMQ = "rabbitmq"; public static final String TYPE_QPID = "qpid"; + public static final String TYPE_LOCAL= "local"; private String type; private String channel; diff --git a/src/main/java/io/jboot/components/mq/JbootmqManager.java b/src/main/java/io/jboot/components/mq/JbootmqManager.java index 827b44df..3dcc7f75 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqManager.java +++ b/src/main/java/io/jboot/components/mq/JbootmqManager.java @@ -17,6 +17,7 @@ package io.jboot.components.mq; import io.jboot.Jboot; import io.jboot.components.mq.aliyunmq.JbootAliyunmqImpl; +import io.jboot.components.mq.local.JbootLocalmqImpl; import io.jboot.components.mq.qpidmq.JbootQpidmqImpl; import io.jboot.components.mq.rabbitmq.JbootRabbitmqImpl; import io.jboot.components.mq.redismq.JbootRedismqImpl; @@ -55,7 +56,7 @@ public class JbootmqManager { throw new IllegalArgumentException("config must not be null"); } - if (!config.isConfigOk()){ + if (!config.isConfigOk()) { return null; } @@ -70,6 +71,8 @@ public class JbootmqManager { return new JbootQpidmqImpl(); case JbootmqConfig.TYPE_ACTIVEMQ: throw new RuntimeException("not finished!!!!"); + case JbootmqConfig.TYPE_LOCAL: + return new JbootLocalmqImpl(); default: return JbootSpiLoader.load(Jbootmq.class, config.getType()); } diff --git a/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java b/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java new file mode 100644 index 00000000..ad1e957f --- /dev/null +++ b/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.mq.local; + +import io.jboot.components.mq.JbootmqBase; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/7 + */ +public class JbootLocalmqImpl extends JbootmqBase { + + @Override + protected void onStartListening() { + //do nothing + } + + @Override + public void enqueue(Object message, String toChannel) { + notifyListeners(toChannel, message); + } + + @Override + public void publish(Object message, String toChannel) { + notifyListeners(toChannel, message); + } +} -- Gitee From 8dc869f6520bd7d1374e8627fd2e9c3eb6aaea5f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 7 Mar 2020 09:38:51 +0800 Subject: [PATCH 0038/1965] v3.0.4 --- src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java index ed379a1f..a2cf164d 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java @@ -1403,6 +1403,7 @@ public class JbootJedisImpl extends JbootRedisBase { * @param listener * @param channels */ + @Override public void subscribe(JedisPubSub listener, final String... channels) { /** * https://github.com/xetorthio/jedis/wiki/AdvancedUsage @@ -1443,6 +1444,7 @@ public class JbootJedisImpl extends JbootRedisBase { * @param binaryListener * @param channels */ + @Override public void subscribe(BinaryJedisPubSub binaryListener, final byte[]... channels) { /** * https://github.com/xetorthio/jedis/wiki/AdvancedUsage -- Gitee From 0017726f2f1453bc6ac9602042f801920182b041 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 7 Mar 2020 11:35:50 +0800 Subject: [PATCH 0039/1965] =?UTF-8?q?fixed=EF=BC=9Asql=20is=20not=20correc?= =?UTF-8?q?t=20if=20group()=20is=20empty=20sql?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/Columns.java | 146 +++++++++++++----- .../java/io/jboot/db/model/SqlBuilder.java | 58 +++++-- src/main/java/io/jboot/db/model/Util.java | 69 +++++++++ 3 files changed, 219 insertions(+), 54 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 5dca48ae..48838ce8 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -329,6 +329,19 @@ public class Columns implements Serializable { return this; } + /** + * customize string sql + * + * @param sql + * @param paras + * @return + */ + public Columns sqlPart(String sql, Object... paras) { + if (StrUtil.isNotBlank(sql)) { + this.add(new SqlPart(sql, paras)); + } + return this; + } /** * customize string sql @@ -344,6 +357,77 @@ public class Columns implements Serializable { return this; } + /** + * customize string sql + * + * @param sql + * @param condition + * @param paras + * @return + */ + public Columns sqlPartIf(String sql, boolean condition, Object... paras) { + if (condition && StrUtil.isNotBlank(sql)) { + this.add(new SqlPart(sql, paras)); + } + return this; + } + + /** + * customize string sql + * + * @param sql + * @return + */ + public Columns sqlPartWithoutLink(String sql) { + if (StrUtil.isNotBlank(sql)) { + this.add(new SqlPart(sql, true)); + } + return this; + } + + /** + * customize string sql + * + * @param sql + * @param paras + * @return + */ + public Columns sqlPartWithoutLink(String sql, Object... paras) { + if (StrUtil.isNotBlank(sql)) { + this.add(new SqlPart(sql, paras, true)); + } + return this; + } + + /** + * customize string sql + * + * @param sql + * @param condition + * @return + */ + public Columns sqlPartIfWithoutLink(String sql, boolean condition) { + if (condition && StrUtil.isNotBlank(sql)) { + this.add(new SqlPart(sql, true)); + } + return this; + } + + /** + * customize string sql + * + * @param sql + * @param condition + * @param paras + * @return + */ + public Columns sqlPartIfWithoutLink(String sql, boolean condition, Object... paras) { + if (condition && StrUtil.isNotBlank(sql)) { + this.add(new SqlPart(sql, paras, true)); + } + return this; + } + public Columns or() { this.add(new Or()); @@ -408,7 +492,19 @@ public class Columns implements Serializable { buildCacheKey(s, ((Group) column).getColumns().getList()); s.append(')').append(SQL_CACHE_SEPARATOR); } else if (column instanceof SqlPart) { - s.append(deleteWhitespace(((SqlPart) column).getSql())).append("-"); + String sqlpart = ((SqlPart) column).getSql(); + Object value = column.getValue(); + if (value != null) { + if (value.getClass().isArray()) { + Object[] values = (Object[]) value; + for (Object v : values) { + sqlpart = Util.replaceSqlPara(sqlpart, v); + } + } else { + sqlpart = Util.replaceSqlPara(sqlpart, value); + } + } + s.append(Util.deleteWhitespace(sqlpart)).append(SQL_CACHE_SEPARATOR); } else { s.append(column.getName()) .append(SQL_CACHE_SEPARATOR) @@ -417,7 +513,7 @@ public class Columns implements Serializable { Object value = column.getValue(); if (value != null) { if (value.getClass().isArray()) { - s.append(array2String((Object[]) column.getValue())); + s.append(Util.array2String((Object[]) value)); } else { s.append(column.getValue()); } @@ -428,42 +524,6 @@ public class Columns implements Serializable { s.deleteCharAt(s.length() - 1); } - private static String deleteWhitespace(String str) { - final int strLen = str.length(); - final char[] chs = new char[strLen]; - int count = 0; - for (int i = 0; i < strLen; i++) { - if (!Character.isWhitespace(str.charAt(i))) { - chs[count++] = str.charAt(i); - } - } - if (count == strLen) { - return str; - } - return new String(chs, 0, count); - } - - private static String array2String(Object[] a) { - if (a == null) { - return "null"; - } - - int iMax = a.length - 1; - if (iMax == -1) { - return "[]"; - } - - StringBuilder b = new StringBuilder(); - b.append('['); - for (int i = 0; ; i++) { - b.append(a[i]); - if (i == iMax) { - return b.append(']').toString(); - } - b.append("-"); - } - } - /** * @param logic @@ -530,13 +590,19 @@ public class Columns implements Serializable { System.out.println(columns.getCacheKey()); columns.ge("age", 10); - columns.sqlPart("and user.id != 1"); + columns.or(); + columns.sqlPart("user.id != ? and xxx= ?", 1, "abc2"); System.out.println(columns.getCacheKey()); - columns.group(Columns.create().likeAppendPercent("name", "lisi").eq("age", 20)); + columns.or(); + columns.group(Columns.create().likeAppendPercent("name", null).or() + .eq("age", null)); System.out.println(columns.getCacheKey()); columns.or(); + columns.group(Columns.create().likeAppendPercent("name", null).or() + .eq("age", null)); +// columns.or(); columns.group(Columns.create().isNotNull("price").isNull("nickname").group(Columns.create().in("name", "123", "123", "111").notIn("nickname", "aaa", "bbb"))); diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index b44ff263..7559535f 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -27,6 +27,9 @@ import java.util.List; */ public class SqlBuilder { + private static final String OR = " OR "; + private static final String AND = " AND "; + public static void buildMysqlWhereSql(StringBuilder sqlBuilder, List columns) { buildWhereSql(sqlBuilder, columns, '`'); } @@ -54,8 +57,9 @@ public class SqlBuilder { private static void buildByColumns(StringBuilder sqlBuilder, List columns, char separator) { for (int i = 0; i < columns.size(); i++) { + Column before = i > 0 ? columns.get(i - 1) : null; Column curent = columns.get(i); - Column next = i >= columns.size() - 1 ? null : columns.get(i + 1); +// // or if (curent instanceof Or) { @@ -63,11 +67,13 @@ public class SqlBuilder { } // string else if (curent instanceof SqlPart) { - sqlBuilder.append(' ').append(((SqlPart) curent).getSql()); + appendSqlPartLogic(sqlBuilder, before, (SqlPart) curent); + continue; } // group else if (curent instanceof Group) { - appendGroupLogic(sqlBuilder, ((Group) curent).getColumns().getList(), separator); + appendGroupLogic(sqlBuilder, before, (Group) curent, separator); + continue; } // in logic else if (Column.LOGIC_IN.equals(curent.getLogic()) || Column.LOGIC_NOT_IN.equals(curent.getLogic())) { @@ -87,13 +93,21 @@ public class SqlBuilder { } } - // if next is SqlPart, no need append 'AND' or 'OR' - if (!(next instanceof SqlPart)) { - appendLinkString(sqlBuilder, next); - } + Column next1 = i >= columns.size() - 1 ? null : columns.get(i + 1); + Column next2 = i >= columns.size() - 2 ? null : columns.get(i + 2); + appendLinkString(sqlBuilder, next1, next2); } } + + private static void appendSqlPartLogic(StringBuilder sqlBuilder, Column before, SqlPart sqlPart) { + if (!sqlPart.isWithoutLink()) { + sqlBuilder.append(before instanceof Or ? OR : AND); + } + sqlBuilder.append(' ').append(sqlPart.getSql()); + } + + private static void appendColumnName(StringBuilder sqlBuilder, Column column, char separator) { if (column.getName().contains(".")) { sqlBuilder.append(column.getName()) @@ -111,23 +125,39 @@ public class SqlBuilder { } - private static void appendLinkString(StringBuilder sqlBuilder, Column next) { - if (next == null) { + private static void appendLinkString(StringBuilder sqlBuilder, Column next1, Column next2) { + if (next1 == null) { + return; + } + //if next is Group , 'AND' or 'OR' append by appendGroupLogic() + else if (next1 instanceof Group || (next1 instanceof Or && next2 instanceof Group)) { + return; + } + //if next is SqlPart, 'AND' or 'OR' append by appendSqlPartLogic() + else if (next1 instanceof SqlPart || (next1 instanceof Or && next2 instanceof SqlPart)) { return; } else { - sqlBuilder.append(next instanceof Or ? " OR " : " AND "); + sqlBuilder.append(next1 instanceof Or ? OR : AND); } } - public static void appendGroupLogic(StringBuilder sqlBuilder, List columns, char separator) { + public static void appendGroupLogic(StringBuilder sqlBuilder, Column before, Group group, char separator) { + List columns = group.getColumns().getList(); if (ArrayUtil.isNullOrEmpty(columns)) { return; } - sqlBuilder.append("("); - buildByColumns(sqlBuilder, columns, separator); - sqlBuilder.append(")"); + StringBuilder groupSqlBuilder = new StringBuilder(); + buildByColumns(groupSqlBuilder, columns, separator); + + String groupSql = groupSqlBuilder.toString(); + if (StrUtil.isNotBlank(groupSql)) { + sqlBuilder.append(before instanceof Or ? OR : AND); + sqlBuilder.append("("); + sqlBuilder.append(groupSql); + sqlBuilder.append(")"); + } } diff --git a/src/main/java/io/jboot/db/model/Util.java b/src/main/java/io/jboot/db/model/Util.java index d8e52c0b..6c696f19 100644 --- a/src/main/java/io/jboot/db/model/Util.java +++ b/src/main/java/io/jboot/db/model/Util.java @@ -16,9 +16,14 @@ package io.jboot.db.model; +import com.jfinal.ext.kit.DateKit; +import io.jboot.utils.StrUtil; + import java.util.Collections; +import java.util.Date; import java.util.LinkedList; import java.util.List; +import java.util.regex.Matcher; class Util { @@ -49,4 +54,68 @@ class Util { return values.isEmpty() ? NULL_PARA_ARRAY : values.toArray(); } + + + static String replaceSqlPara(String sql, Object value) { + // null + if (value == null) { + return sql.replaceFirst("\\?", "null"); + } + // number + else if (value instanceof Number) { + return sql.replaceFirst("\\?", value.toString()); + } + // numeric + else if (value instanceof String && StrUtil.isNumeric((String) value)) { + return sql.replaceFirst("\\?", (String) value); + } + // other + else { + StringBuilder sb = new StringBuilder(); + if (value instanceof Date) { + sb.append(DateKit.toStr((Date) value, DateKit.timeStampPattern)); + } else { + sb.append(value.toString()); + } + return sql.replaceFirst("\\?", Matcher.quoteReplacement(sb.toString())); + } + } + + + static String deleteWhitespace(String str) { + final int strLen = str.length(); + final char[] chs = new char[strLen]; + int count = 0; + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + chs[count++] = str.charAt(i); + } + } + if (count == strLen) { + return str; + } + return new String(chs, 0, count); + } + + + static String array2String(Object[] a) { + if (a == null) { + return "null"; + } + + int iMax = a.length - 1; + if (iMax == -1) { + return "[]"; + } + + StringBuilder b = new StringBuilder(); + b.append('['); + for (int i = 0; ; i++) { + b.append(a[i]); + if (i == iMax) { + return b.append(']').toString(); + } + b.append("-"); + } + } } -- Gitee From ed47b43c58beadc478c09c6da92f363548b07498 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 7 Mar 2020 11:37:11 +0800 Subject: [PATCH 0040/1965] enhance SqlPart... --- src/main/java/io/jboot/db/model/SqlPart.java | 41 +++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/model/SqlPart.java b/src/main/java/io/jboot/db/model/SqlPart.java index eed054dc..fd573021 100644 --- a/src/main/java/io/jboot/db/model/SqlPart.java +++ b/src/main/java/io/jboot/db/model/SqlPart.java @@ -19,11 +19,29 @@ package io.jboot.db.model; class SqlPart extends Column { private String sql; + private Object para; + private boolean withoutLink = false; public SqlPart(String sql) { this.sql = sql; } + public SqlPart(String sql, Object para) { + this.sql = sql; + this.para = para; + } + + public SqlPart(String sql, boolean withoutLink) { + this.sql = sql; + this.withoutLink = withoutLink; + } + + public SqlPart(String sql, Object para, boolean withoutLink) { + this.sql = sql; + this.para = para; + this.withoutLink = withoutLink; + } + public String getSql() { return sql; } @@ -32,8 +50,29 @@ class SqlPart extends Column { this.sql = sql; } + public Object getPara() { + return para; + } + + public void setPara(Object para) { + this.para = para; + } + + public boolean isWithoutLink() { + return withoutLink; + } + + public void setWithoutLink(boolean withoutLink) { + this.withoutLink = withoutLink; + } + @Override public boolean hasPara() { - return false; + return para != null; + } + + @Override + public Object getValue() { + return para; } } -- Gitee From f25b1dbd700ebeba734d0f509e7f8ce62c09dbb6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 7 Mar 2020 12:41:12 +0800 Subject: [PATCH 0041/1965] rename Columns.sqlPartIfWithoutLink() to Columns.sqlPartWithoutLinkIf() --- src/main/java/io/jboot/db/model/Columns.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 48838ce8..de72315a 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -406,7 +406,7 @@ public class Columns implements Serializable { * @param condition * @return */ - public Columns sqlPartIfWithoutLink(String sql, boolean condition) { + public Columns sqlPartWithoutLinkIf(String sql, boolean condition) { if (condition && StrUtil.isNotBlank(sql)) { this.add(new SqlPart(sql, true)); } @@ -421,7 +421,7 @@ public class Columns implements Serializable { * @param paras * @return */ - public Columns sqlPartIfWithoutLink(String sql, boolean condition, Object... paras) { + public Columns sqlPartWithoutLinkIf(String sql, boolean condition, Object... paras) { if (condition && StrUtil.isNotBlank(sql)) { this.add(new SqlPart(sql, paras, true)); } @@ -429,6 +429,7 @@ public class Columns implements Serializable { } + public Columns or() { this.add(new Or()); return this; @@ -612,7 +613,7 @@ public class Columns implements Serializable { columns.between("name", "123", "1233"); System.out.println(columns.getCacheKey()); - columns.sqlPart("group by xxx"); + columns.sqlPartWithoutLink("group by xxx"); System.out.println(Arrays.toString(columns.getValueArray())); System.out.println(columns.toMysqlSql()); -- Gitee From d24ad1a3fb4349e371b17bb317e453173ae942c4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 7 Mar 2020 13:33:06 +0800 Subject: [PATCH 0042/1965] add localCounter and redisCounter --- .../jboot/objects/counter/JbootCounter.java | 6 ++ .../counter/impl/JbootLocalCounter.java | 52 +++++++++++++++++ .../counter/impl/JbootRedicCounter.java | 56 +++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java create mode 100644 src/main/java/io/jboot/objects/counter/impl/JbootRedicCounter.java diff --git a/src/main/java/io/jboot/objects/counter/JbootCounter.java b/src/main/java/io/jboot/objects/counter/JbootCounter.java index af2993f3..edec77cb 100644 --- a/src/main/java/io/jboot/objects/counter/JbootCounter.java +++ b/src/main/java/io/jboot/objects/counter/JbootCounter.java @@ -20,4 +20,10 @@ package io.jboot.objects.counter; * @Date: 2020/2/28 */ public interface JbootCounter { + + public void increment(); + public void decrement(); + public Long get(); + public void set(long newValue); + } diff --git a/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java b/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java new file mode 100644 index 00000000..54bb360f --- /dev/null +++ b/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.objects.counter.impl; + +import io.jboot.objects.counter.JbootCounter; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/7 + */ +public class JbootLocalCounter implements JbootCounter { + + AtomicLong atomicLong = new AtomicLong(); + + public JbootLocalCounter() { + } + + @Override + public void increment() { + atomicLong.incrementAndGet(); + } + + @Override + public void decrement() { + atomicLong.decrementAndGet(); + } + + @Override + public Long get() { + return atomicLong.get(); + } + + @Override + public void set(long newValue) { + atomicLong.set(newValue); + } +} diff --git a/src/main/java/io/jboot/objects/counter/impl/JbootRedicCounter.java b/src/main/java/io/jboot/objects/counter/impl/JbootRedicCounter.java new file mode 100644 index 00000000..c9628fff --- /dev/null +++ b/src/main/java/io/jboot/objects/counter/impl/JbootRedicCounter.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.objects.counter.impl; + +import io.jboot.Jboot; +import io.jboot.objects.counter.JbootCounter; +import io.jboot.support.redis.JbootRedis; +import io.jboot.utils.StrUtil; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/7 + */ +public class JbootRedicCounter implements JbootCounter { + + private JbootRedis redis = Jboot.getRedis(); + private String name; + + public JbootRedicCounter(String name) { + this.name = name; + } + + @Override + public void increment() { + redis.incr(name); + } + + @Override + public void decrement() { + redis.decr(name); + } + + @Override + public Long get() { + String value = redis.getWithoutSerialize(name); + return StrUtil.isNotBlank(value) ? null : Long.valueOf(value); + } + + @Override + public void set(long newValue) { + redis.setWithoutSerialize(name, newValue); + } +} -- Gitee From 6a3ac0739fc21fb40db1eef6d723d7ae974ce2e8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 7 Mar 2020 13:52:35 +0800 Subject: [PATCH 0043/1965] fixed sqlBuilder bugs --- src/main/java/io/jboot/db/model/Columns.java | 50 +++++++++++-------- .../java/io/jboot/db/model/SqlBuilder.java | 7 +-- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index de72315a..651b3d73 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -585,43 +585,49 @@ public class Columns implements Serializable { public static void main(String[] args) { Columns columns = Columns.create(); - System.out.println(columns.getCacheKey()); - +// System.out.println(columns.getCacheKey()); +// columns.add("name", "zhangsan"); - System.out.println(columns.getCacheKey()); - - columns.ge("age", 10); - columns.or(); - columns.sqlPart("user.id != ? and xxx= ?", 1, "abc2"); - System.out.println(columns.getCacheKey()); +// System.out.println(columns.getCacheKey()); +// +// columns.ge("age", 10); +// columns.or(); +// columns.sqlPart("user.id != ? and xxx= ?", 1, "abc2"); +// System.out.println(columns.getCacheKey()); columns.or(); columns.group(Columns.create().likeAppendPercent("name", null).or() - .eq("age", null)); - System.out.println(columns.getCacheKey()); - + .eq("age", null).or().eq("ddd","null").eq("ccc","ccc")); +// columns.or(); columns.group(Columns.create().likeAppendPercent("name", null).or() + .eq("age", "18").eq("ddd","ddd")); +// System.out.println(columns.getCacheKey()); +// + columns.or(); + columns.group(Columns.create().likeAppendPercent("name", "aaa").or() .eq("age", null)); // columns.or(); - - columns.group(Columns.create().isNotNull("price").isNull("nickname").group(Columns.create().in("name", "123", "123", "111").notIn("nickname", "aaa", "bbb"))); - - System.out.println(columns.getCacheKey()); +// +// columns.group(Columns.create().isNotNull("price").isNull("nickname").group(Columns.create().in("name", "123", "123", "111").notIn("nickname", "aaa", "bbb"))); +// +// System.out.println(columns.getCacheKey()); columns.or(); - +// columns.between("name", "123", "1233"); - System.out.println(columns.getCacheKey()); - +// System.out.println(columns.getCacheKey()); +// columns.sqlPartWithoutLink("group by xxx"); + + System.out.println(Arrays.toString(columns.getValueArray())); System.out.println(columns.toMysqlSql()); - System.out.println(columns.toSqlServerSql()); +// System.out.println(columns.toSqlServerSql()); - JbootMysqlDialect dialect = new JbootMysqlDialect(); - System.out.println(dialect.forDeleteByColumns("table", columns.getList())); - System.out.println(dialect.forFindCountByColumns("table", columns.getList())); +// JbootMysqlDialect dialect = new JbootMysqlDialect(); +// System.out.println(dialect.forDeleteByColumns("table", columns.getList())); +// System.out.println(dialect.forFindCountByColumns("table", columns.getList())); } diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index 7559535f..15de5e2b 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -59,7 +59,6 @@ public class SqlBuilder { Column before = i > 0 ? columns.get(i - 1) : null; Column curent = columns.get(i); -// // or if (curent instanceof Or) { @@ -68,12 +67,10 @@ public class SqlBuilder { // string else if (curent instanceof SqlPart) { appendSqlPartLogic(sqlBuilder, before, (SqlPart) curent); - continue; } // group else if (curent instanceof Group) { appendGroupLogic(sqlBuilder, before, (Group) curent, separator); - continue; } // in logic else if (Column.LOGIC_IN.equals(curent.getLogic()) || Column.LOGIC_NOT_IN.equals(curent.getLogic())) { @@ -136,6 +133,10 @@ public class SqlBuilder { //if next is SqlPart, 'AND' or 'OR' append by appendSqlPartLogic() else if (next1 instanceof SqlPart || (next1 instanceof Or && next2 instanceof SqlPart)) { return; + } + //if the last is OR + else if (next1 instanceof Or && next2 == null) { + return; } else { sqlBuilder.append(next1 instanceof Or ? OR : AND); } -- Gitee From 1b17ec4ca9ce26eeee88dccc6f572ce1c1cd94f0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 8 Mar 2020 11:35:51 +0800 Subject: [PATCH 0044/1965] v3.0.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7720d3a8..2ef48ce8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.0.3 + 3.0.4-SNAPSHOT jar jboot -- Gitee From 2fa168dccd09355686e9a33a0ed5f6be12ec71df Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 8 Mar 2020 12:34:32 +0800 Subject: [PATCH 0045/1965] v3.0.4 --- src/main/java/io/jboot/utils/ClassUtil.java | 54 ++++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassUtil.java b/src/main/java/io/jboot/utils/ClassUtil.java index 85735ceb..55becd26 100644 --- a/src/main/java/io/jboot/utils/ClassUtil.java +++ b/src/main/java/io/jboot/utils/ClassUtil.java @@ -73,6 +73,27 @@ public class ClassUtil { } + /** + * 创建新的实例,并传入初始化参数 + * + * @param clazz + * @param paras + * @param + * @return + */ + public static T newInstance(Class clazz, Object... paras) { + return newInstance(clazz, true, paras); + } + + + /** + * 是否通过 AOP 来实例化 + * + * @param clazz + * @param createByAop + * @param + * @return + */ public static T newInstance(Class clazz, boolean createByAop) { if (createByAop) { return Aop.get(clazz); @@ -89,6 +110,32 @@ public class ClassUtil { } } + + public static T newInstance(Class clazz, boolean createByAop, Object... paras) { + try { + Class[] classes = new Class[paras.length]; + for (int i = 0; i < paras.length; i++) { + Object data = paras[i]; + if (data == null) { + throw new IllegalArgumentException("paras must not null"); + } + classes[i] = data.getClass(); + } + Constructor constructor = clazz.getDeclaredConstructor(classes); + constructor.setAccessible(true); + Object ret = constructor.newInstance(paras); + if (createByAop) { + Aop.inject(ret); + } + return (T) ret; + } catch (Exception e) { + log.error("can not newInstance class:" + clazz + "\n" + e.toString(), e); + } + + return null; + } + + public static T newInstanceByStaticConstruct(Class clazz) { StaticConstruct staticConstruct = clazz.getAnnotation(StaticConstruct.class); if (staticConstruct == null) { @@ -98,6 +145,7 @@ public class ClassUtil { return newInstanceByStaticConstruct(clazz, staticConstruct); } + public static T newInstanceByStaticConstruct(Class clazz, StaticConstruct staticConstruct) { Method method = getStaticConstruct(staticConstruct.value(), clazz); @@ -184,14 +232,16 @@ public class ClassUtil { } + private static final String ENHANCER_BY = "$$EnhancerBy"; + public static Class getUsefulClass(Class clazz) { //ControllerTest$ServiceTest$$EnhancerByGuice$$40471411#hello //com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158 - return clazz.getName().indexOf("$$EnhancerBy") == -1 ? clazz : clazz.getSuperclass(); + return clazz.getName().indexOf(ENHANCER_BY) == -1 ? clazz : clazz.getSuperclass(); } - public static Class getGenericClass(Class clazz){ + public static Class getGenericClass(Class clazz) { Type type = getUsefulClass(clazz).getGenericSuperclass(); return (Class) ((ParameterizedType) type).getActualTypeArguments()[0]; } -- Gitee From edd883f1b86b12c640091d3362efccbe4a8c8e0e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 8 Mar 2020 12:35:01 +0800 Subject: [PATCH 0046/1965] v3.0.4 --- .../objects/counter/JbootCounterConfig.java | 39 +++++++++++++++++++ .../objects/counter/JbootCounterManager.java | 27 +++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/main/java/io/jboot/objects/counter/JbootCounterConfig.java diff --git a/src/main/java/io/jboot/objects/counter/JbootCounterConfig.java b/src/main/java/io/jboot/objects/counter/JbootCounterConfig.java new file mode 100644 index 00000000..69ecb2bf --- /dev/null +++ b/src/main/java/io/jboot/objects/counter/JbootCounterConfig.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.objects.counter; + +import io.jboot.app.config.annotation.ConfigModel; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/8 + */ +@ConfigModel(prefix = "jboot.object.counter") +public class JbootCounterConfig { + + public static final String TYPE_LOCAL = "local"; + public static final String TYPE_REDIS = "redis"; + + private String type = TYPE_LOCAL; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/src/main/java/io/jboot/objects/counter/JbootCounterManager.java b/src/main/java/io/jboot/objects/counter/JbootCounterManager.java index 510254c1..13f3b8d9 100644 --- a/src/main/java/io/jboot/objects/counter/JbootCounterManager.java +++ b/src/main/java/io/jboot/objects/counter/JbootCounterManager.java @@ -15,9 +15,36 @@ */ package io.jboot.objects.counter; +import io.jboot.Jboot; +import io.jboot.core.spi.JbootSpiLoader; +import io.jboot.objects.counter.impl.JbootLocalCounter; +import io.jboot.objects.counter.impl.JbootRedicCounter; + /** * @author michael yang (fuhai999@gmail.com) * @Date: 2020/2/28 */ public class JbootCounterManager { + + private static JbootCounterManager instance = new JbootCounterManager(); + public static JbootCounterManager me() { + return instance; + } + + + private JbootCounterConfig config = Jboot.config(JbootCounterConfig.class); + + public JbootCounter create(String name){ + switch (config.getType()){ + case JbootCounterConfig.TYPE_LOCAL: + return new JbootLocalCounter(); + case JbootCounterConfig.TYPE_REDIS: + return new JbootRedicCounter(name); + default: + return JbootSpiLoader.load(JbootCounter.class,config.getType()); + } + + + } + } -- Gitee From 0982676b207bf98f8de5f475176f6344c01aa9d5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 8 Mar 2020 12:58:35 +0800 Subject: [PATCH 0047/1965] v3.0.4 --- src/main/java/io/jboot/db/model/Util.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Util.java b/src/main/java/io/jboot/db/model/Util.java index 6c696f19..52537322 100644 --- a/src/main/java/io/jboot/db/model/Util.java +++ b/src/main/java/io/jboot/db/model/Util.java @@ -35,7 +35,7 @@ class Util { return NULL_PARA_ARRAY; } - List values = new LinkedList<>(); + List paras = new LinkedList<>(); for (Column column : columns) { if (!column.hasPara()) { @@ -44,15 +44,15 @@ class Util { Object value = column.getValue(); if (value != null) { if (value.getClass().isArray()) { - Object[] vs = (Object[]) value; - Collections.addAll(values, vs); + Object[] values = (Object[]) value; + Collections.addAll(paras, values); } else { - values.add(value); + paras.add(value); } } } - return values.isEmpty() ? NULL_PARA_ARRAY : values.toArray(); + return paras.isEmpty() ? NULL_PARA_ARRAY : paras.toArray(); } -- Gitee From 553c413212f482026cb985d14e5b95b71f55e8eb Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 8 Mar 2020 13:41:23 +0800 Subject: [PATCH 0048/1965] v3.0.4 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 55024f38..a29aaa2e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.0.3 + 3.0.4 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 2516cc38..126a770f 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.0.3 + 3.0.4 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index abf1ad59..d4e318b1 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.0.3 + 3.0.4 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index ce8ef8d6..81b59ea3 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.0.3"; + public static String VERSION = "3.0.4"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 6849b449c71b52fdc2f0e1114bc65705b2a75939 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 8 Mar 2020 13:41:35 +0800 Subject: [PATCH 0049/1965] v3.0.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2ef48ce8..be5116f7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.0.4-SNAPSHOT + 3.0.4 jar jboot -- Gitee From ead3b81e5eb02b46af8908b15503221e9d0733eb Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 8 Mar 2020 13:41:49 +0800 Subject: [PATCH 0050/1965] v3.0.4 release (^.^)YYa!! --- changes.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/changes.txt b/changes.txt index f2ba2337..a270682a 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,15 @@ +jboot v3.0.4: +新增:Jboot MQ 新增 Local 类型 +新增:sqlPart 支持参数设置 +优化:修改 Columns.string() 为 Columns.sqlPart() +优化:JbootModelConfig 的 primarykeyValueGenerator 和 Filter 支持通过 api 动态配置 +优化:StrUtil.isNumeric、isDecimal、isEmail 等方法 +优化:完善 JbootCounter 功能 +修复:当 Columns.group() 内容为空的时候,构建了错误Sql的问题 +修复:Columns.isNullIf 条件判断错误的问题 + + + jboot v3.0.3: 新增:Cache 模块新增 refresh() 的方法,在分布式缓存在某些极端情况下出现不同步的时候进行刷新。 新增:对 Columns 优化,新增 gourpIf()/stringIf()/isNullIf() 等方法 -- Gitee From c0eef41b8a612b3415c9eedcfb09be2176f6895d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 9 Mar 2020 12:32:43 +0800 Subject: [PATCH 0051/1965] =?UTF-8?q?fixed=EF=BC=9ASqlBuilder=20bugs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/Columns.java | 76 ++++++++++--------- .../java/io/jboot/db/model/SqlBuilder.java | 66 +++++++--------- 2 files changed, 69 insertions(+), 73 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 651b3d73..1afe6dde 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -296,6 +296,9 @@ public class Columns implements Serializable { * @return */ public Columns group(Columns columns) { + if (columns == this){ + throw new IllegalArgumentException("Columns.group(...) need a new Columns"); + } if (!columns.isEmpty()) { this.add(new Group(columns)); } @@ -309,6 +312,9 @@ public class Columns implements Serializable { * @return */ public Columns groupIf(Columns columns, boolean conditon) { + if (columns == this){ + throw new IllegalArgumentException("Columns.group(...) need a new Columns"); + } if (conditon && !columns.isEmpty()) { this.add(new Group(columns)); } @@ -485,9 +491,15 @@ public class Columns implements Serializable { private static final char SQL_CACHE_SEPARATOR = '-'; private void buildCacheKey(StringBuilder s, List columns) { - for (Column column : columns) { + for (int i = 0; i < columns.size(); i++) { + + Column column = columns.get(i); + if (column instanceof Or) { - s.append("or").append(SQL_CACHE_SEPARATOR); + Column before = i > 0 ? columns.get(i - 1) : null; + if (before != null && !(before instanceof Or) ) { + s.append("or").append(SQL_CACHE_SEPARATOR); + } } else if (column instanceof Group) { s.append('('); buildCacheKey(s, ((Group) column).getColumns().getList()); @@ -583,51 +595,45 @@ public class Columns implements Serializable { return getCacheKey(); } + public static void main(String[] args) { - Columns columns = Columns.create(); -// System.out.println(columns.getCacheKey()); -// - columns.add("name", "zhangsan"); -// System.out.println(columns.getCacheKey()); -// -// columns.ge("age", 10); -// columns.or(); -// columns.sqlPart("user.id != ? and xxx= ?", 1, "abc2"); -// System.out.println(columns.getCacheKey()); + + Columns columns = Columns.create().or().or().or().eq("aa","bb").or().or().or().notIn("aaa",123,456,789).like("titile","a"); + columns.group(Columns.create().or().or().sqlPart("aa=bb")); + columns.group(Columns.create("aa","bb").eq("cc","dd") + .group(Columns.create("aa","bb").eq("cc","dd")) + .group(Columns.create("aa","bb").eq("cc","dd").group(Columns.create("aa","bb").eq("cc","dd")))); + + columns.ge("age", 10); + columns.or(); + columns.or(); + columns.or(); + columns.or(); + columns.sqlPart("user.id != ? and xxx= ?", 1, "abc2"); + columns.sqlPart("user.id != ? and xxx= ?", 1, "abc2"); columns.or(); - columns.group(Columns.create().likeAppendPercent("name", null).or() - .eq("age", null).or().eq("ddd","null").eq("ccc","ccc")); -// columns.or(); - columns.group(Columns.create().likeAppendPercent("name", null).or() + columns.or(); + columns.group(Columns.create().likeAppendPercent("name", "null").or().or().or() .eq("age", "18").eq("ddd","ddd")); -// System.out.println(columns.getCacheKey()); -// + + columns.or(); + columns.or(); + + columns.group(Columns.create().or().or().sqlPart("name = ? ", "zhangsan")); + columns.or(); columns.or(); - columns.group(Columns.create().likeAppendPercent("name", "aaa").or() - .eq("age", null)); -// columns.or(); -// -// columns.group(Columns.create().isNotNull("price").isNull("nickname").group(Columns.create().in("name", "123", "123", "111").notIn("nickname", "aaa", "bbb"))); -// -// System.out.println(columns.getCacheKey()); columns.or(); -// - columns.between("name", "123", "1233"); -// System.out.println(columns.getCacheKey()); -// - columns.sqlPartWithoutLink("group by xxx"); + columns.between("name", "123", "1233"); + columns.between("name", "123", "1233"); + columns.sqlPartWithoutLink("group by xxx"); + System.out.println(columns.getCacheKey()); System.out.println(Arrays.toString(columns.getValueArray())); System.out.println(columns.toMysqlSql()); -// System.out.println(columns.toSqlServerSql()); - -// JbootMysqlDialect dialect = new JbootMysqlDialect(); -// System.out.println(dialect.forDeleteByColumns("table", columns.getList())); -// System.out.println(dialect.forFindCountByColumns("table", columns.getList())); } diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index 15de5e2b..72275971 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -48,9 +48,14 @@ public class SqlBuilder { return; } - sqlBuilder.append(" WHERE "); - buildByColumns(sqlBuilder, columns, separator); + StringBuilder whereSqlBuilder = new StringBuilder(); + buildByColumns(whereSqlBuilder, columns, separator); + String whereSql = whereSqlBuilder.toString(); + + if (StrUtil.isNotBlank(whereSql)) { + sqlBuilder.append(" WHERE ").append(whereSql); + } } @@ -60,11 +65,10 @@ public class SqlBuilder { Column before = i > 0 ? columns.get(i - 1) : null; Column curent = columns.get(i); - // or if (curent instanceof Or) { continue; } - // string + // sqlPart else if (curent instanceof SqlPart) { appendSqlPartLogic(sqlBuilder, before, (SqlPart) curent); } @@ -74,32 +78,30 @@ public class SqlBuilder { } // in logic else if (Column.LOGIC_IN.equals(curent.getLogic()) || Column.LOGIC_NOT_IN.equals(curent.getLogic())) { + appendLinkString(sqlBuilder, before); appendInLogic(sqlBuilder, curent, separator); } // between logic else if (Column.LOGIC_BETWEEN.equals(curent.getLogic()) || Column.LOGIC_NOT_BETWEEN.equals(curent.getLogic())) { + appendLinkString(sqlBuilder, before); appendBetweenLogic(sqlBuilder, curent, separator); } // others else { - + appendLinkString(sqlBuilder, before); appendColumnName(sqlBuilder, curent, separator); if (curent.hasPara()) { - sqlBuilder.append("?"); + sqlBuilder.append('?'); } } - - Column next1 = i >= columns.size() - 1 ? null : columns.get(i + 1); - Column next2 = i >= columns.size() - 2 ? null : columns.get(i + 2); - appendLinkString(sqlBuilder, next1, next2); } } private static void appendSqlPartLogic(StringBuilder sqlBuilder, Column before, SqlPart sqlPart) { if (!sqlPart.isWithoutLink()) { - sqlBuilder.append(before instanceof Or ? OR : AND); + appendLinkString(sqlBuilder, before); } sqlBuilder.append(' ').append(sqlPart.getSql()); } @@ -108,37 +110,25 @@ public class SqlBuilder { private static void appendColumnName(StringBuilder sqlBuilder, Column column, char separator) { if (column.getName().contains(".")) { sqlBuilder.append(column.getName()) - .append(" ") + .append(' ') .append(column.getLogic()) - .append(" "); + .append(' '); } else { sqlBuilder.append(separator) .append(column.getName()) .append(separator) - .append(" ") + .append(' ') .append(column.getLogic()) - .append(" "); + .append(' '); } } - private static void appendLinkString(StringBuilder sqlBuilder, Column next1, Column next2) { - if (next1 == null) { - return; - } - //if next is Group , 'AND' or 'OR' append by appendGroupLogic() - else if (next1 instanceof Group || (next1 instanceof Or && next2 instanceof Group)) { - return; - } - //if next is SqlPart, 'AND' or 'OR' append by appendSqlPartLogic() - else if (next1 instanceof SqlPart || (next1 instanceof Or && next2 instanceof SqlPart)) { - return; - } - //if the last is OR - else if (next1 instanceof Or && next2 == null) { + private static void appendLinkString(StringBuilder sqlBuilder, Column before) { + if (sqlBuilder.length() == 0 || before == null) { return; } else { - sqlBuilder.append(next1 instanceof Or ? OR : AND); + sqlBuilder.append(before instanceof Or ? OR : AND); } } @@ -154,10 +144,10 @@ public class SqlBuilder { String groupSql = groupSqlBuilder.toString(); if (StrUtil.isNotBlank(groupSql)) { - sqlBuilder.append(before instanceof Or ? OR : AND); - sqlBuilder.append("("); + appendLinkString(sqlBuilder, before); + sqlBuilder.append('('); sqlBuilder.append(groupSql); - sqlBuilder.append(")"); + sqlBuilder.append(')'); } } @@ -166,15 +156,15 @@ public class SqlBuilder { appendColumnName(sqlBuilder, column, separator); - sqlBuilder.append("("); + sqlBuilder.append('('); Object[] values = (Object[]) column.getValue(); for (int i = 0; i < values.length; i++) { - sqlBuilder.append("?"); + sqlBuilder.append('?'); if (i != values.length - 1) { - sqlBuilder.append(","); + sqlBuilder.append(','); } } - sqlBuilder.append(")"); + sqlBuilder.append(')'); } @@ -182,7 +172,7 @@ public class SqlBuilder { sqlBuilder.append(separator) .append(column.getName()) .append(separator) - .append(" ") + .append(' ') .append(column.getLogic()); sqlBuilder.append(" ? AND ?"); -- Gitee From b446cf3fb6e359c0e35261e0e6b0cfb5bb33c5f8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 9 Mar 2020 19:21:07 +0800 Subject: [PATCH 0052/1965] v3.0.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index be5116f7..cd336cdf 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.0.4 + 3.0.5-SNAPSHOT jar jboot -- Gitee From b5880918ebf5c19b986f2aec279c1e67f3e6776d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 9 Mar 2020 19:22:19 +0800 Subject: [PATCH 0053/1965] remove deprecated api Jboot.bean() and Jboot.injectMembers --- src/main/java/io/jboot/Jboot.java | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/src/main/java/io/jboot/Jboot.java b/src/main/java/io/jboot/Jboot.java index db8257ad..16529e16 100644 --- a/src/main/java/io/jboot/Jboot.java +++ b/src/main/java/io/jboot/Jboot.java @@ -16,7 +16,6 @@ package io.jboot; import com.codahale.metrics.MetricRegistry; -import com.jfinal.aop.Aop; import io.jboot.app.config.JbootConfigManager; import io.jboot.components.cache.JbootCache; import io.jboot.components.cache.JbootCacheManager; @@ -186,29 +185,4 @@ public class Jboot { sendEvent(new JbootEvent(action, data)); } - - /** - * 使用 Aop.get 代替 - * - * @param clazz - * @param - * @return - */ - @Deprecated - public static T bean(Class clazz) { - return Aop.get(clazz); - } - - - /** - * 使用 Aop.inject 代替 - * - * @param object - */ - @Deprecated - public static void injectMembers(Object object) { - Aop.inject(object); - } - - } -- Gitee From 775ba20a7826ab78a0f9a0283b523b38e331ecd7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 10 Mar 2020 09:15:58 +0800 Subject: [PATCH 0054/1965] =?UTF-8?q?fixed=EF=BC=9ASqlBuilder=20bugs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/Columns.java | 6 +++++- src/main/java/io/jboot/db/model/SqlBuilder.java | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 1afe6dde..3b6560bd 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -621,15 +621,19 @@ public class Columns implements Serializable { columns.or(); columns.or(); - columns.group(Columns.create().or().or().sqlPart("name = ? ", "zhangsan")); + columns.group(Columns.create().or().or().sqlPart("name = ?", "zhangsan")); columns.or(); columns.or(); columns.or(); columns.between("name", "123", "1233"); columns.between("name", "123", "1233"); + columns.or(); columns.sqlPartWithoutLink("group by xxx"); + columns.or(); + columns.or(); + columns.or(); System.out.println(columns.getCacheKey()); System.out.println(Arrays.toString(columns.getValueArray())); diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index 72275971..3c255ee1 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -103,7 +103,7 @@ public class SqlBuilder { if (!sqlPart.isWithoutLink()) { appendLinkString(sqlBuilder, before); } - sqlBuilder.append(' ').append(sqlPart.getSql()); + sqlBuilder.append(' ').append(sqlPart.getSql()).append(' '); } -- Gitee From 3c2d039c2eb73c3b00619b07700b8b32c4e81a31 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 10 Mar 2020 19:10:34 +0800 Subject: [PATCH 0055/1965] add JbootMap --- .../java/io/jboot/objects/map/JbootMap.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/main/java/io/jboot/objects/map/JbootMap.java b/src/main/java/io/jboot/objects/map/JbootMap.java index 119cfbc3..731fad0d 100644 --- a/src/main/java/io/jboot/objects/map/JbootMap.java +++ b/src/main/java/io/jboot/objects/map/JbootMap.java @@ -15,9 +15,41 @@ */ package io.jboot.objects.map; +import java.util.List; + /** * @author michael yang (fuhai999@gmail.com) * @Date: 2020/2/28 */ public interface JbootMap { + + /** + * put value + * @param key + * @param value + */ + public void put(String key, Object value); + + /** + * get value + * @param key + * @param + * @return + */ + public T get(String key); + + /** + * get all values + * @param + * @return + */ + public List values(); + + /** + * get all keys + * @return + */ + public List keys(); + + } -- Gitee From c67be72fc0978a9f62bd47e431b22d9ea6bc2864 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 11 Mar 2020 09:48:46 +0800 Subject: [PATCH 0056/1965] v3.0.5 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- pom.xml | 20 ++++++++++---------- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index a29aaa2e..ee578994 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.0.4 + 3.0.5 ``` diff --git a/changes.txt b/changes.txt index a270682a..a270482d 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.0.5: +修复:SqlBuilder 在某些极端情况下生成 SQL 的一些问题 +优化:移除 Jboot 的一些过时的方法 + + + jboot v3.0.4: 新增:Jboot MQ 新增 Local 类型 新增:sqlPart 支持参数设置 diff --git a/doc/docs/install.md b/doc/docs/install.md index 126a770f..784d2e41 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.0.4 + 3.0.5 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index d4e318b1..6e92dcde 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.0.4 + 3.0.5 ``` diff --git a/pom.xml b/pom.xml index cd336cdf..adaf0ca1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.0.5-SNAPSHOT + 3.0.5 jar jboot @@ -61,15 +61,15 @@ 2.8.1 3.9 2.6 - 1.13 - 4.5.10 - 4.4.12 - 4.1.45.Final + 1.14 + 4.5.12 + 4.4.13 + 4.1.47.Final 1.7.1 2.7.5 1.1.0 1.1.7 - 4.1.4 + 4.1.5 1.8 1.8 @@ -272,7 +272,7 @@ com.alibaba.nacos nacos-client - 1.1.4 + 1.2.0 provided @@ -349,7 +349,7 @@ io.lettuce lettuce-core - 5.2.0.RELEASE + 5.2.2.RELEASE provided @@ -378,7 +378,7 @@ org.apache.shardingsphere sharding-jdbc-core - 4.0.0 + 4.0.1 provided @@ -414,7 +414,7 @@ io.dropwizard.metrics metrics-graphite - 4.1.4 + ${metrics.version} provided diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 81b59ea3..ca30c618 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.0.4"; + public static String VERSION = "3.0.5"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From b370cd2350b06c993d4d1014929869021f0cee70 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 11 Mar 2020 12:38:36 +0800 Subject: [PATCH 0057/1965] add @Configuration support --- .../java/io/jboot/aop/JbootAopFactory.java | 54 +++++++++++++++---- .../java/io/jboot/aop/annotation/Bean.java | 2 +- .../jboot/aop/annotation/Configuration.java | 25 +++++++++ 3 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 src/main/java/io/jboot/aop/annotation/Configuration.java diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 71624bd6..538fe2d6 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -24,10 +24,7 @@ import com.jfinal.log.Log; import com.jfinal.plugin.activerecord.Model; import com.jfinal.proxy.Proxy; import com.jfinal.proxy.ProxyManager; -import io.jboot.aop.annotation.Bean; -import io.jboot.aop.annotation.BeanExclude; -import io.jboot.aop.annotation.ConfigValue; -import io.jboot.aop.annotation.StaticConstruct; +import io.jboot.aop.annotation.*; import io.jboot.app.config.JbootConfigManager; import io.jboot.app.config.annotation.ConfigModel; import io.jboot.components.event.JbootEventListener; @@ -43,8 +40,11 @@ import io.jboot.web.controller.JbootController; import java.io.Serializable; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class JbootAopFactory extends AopFactory { @@ -65,6 +65,9 @@ public class JbootAopFactory extends AopFactory { } + private Map beansMap = new ConcurrentHashMap<>(); + + private JbootAopFactory() { ProxyManager.me().setProxyFactory(new CglibProxyFactory()); setInjectSuperClass(true); @@ -98,7 +101,12 @@ public class JbootAopFactory extends AopFactory { Inject inject = field.getAnnotation(Inject.class); if (inject != null) { - doInjectJFinalOrginal(targetObject, field, inject); + Bean bean = field.getAnnotation(Bean.class); + if (bean != null && StrUtil.isNotBlank(bean.name())) { + doInjectByName(targetObject, field, bean.name()); + } else { + doInjectJFinalOrginal(targetObject, field, inject); + } continue; } @@ -133,6 +141,12 @@ public class JbootAopFactory extends AopFactory { } } + + private void doInjectByName(Object targetObject, Field field, String name) throws ReflectiveOperationException { + Object fieldInjectedObject = beansMap.get(name); + setFieldValue(field, targetObject, fieldInjectedObject); + } + /** * JFinal 原生 service 注入 * @@ -150,7 +164,6 @@ public class JbootAopFactory extends AopFactory { Object fieldInjectedObject = doGet(fieldInjectedClass); setFieldValue(field, targetObject, fieldInjectedObject); - } /** @@ -192,7 +205,6 @@ public class JbootAopFactory extends AopFactory { if (StrUtil.isNotBlank(value)) { Object fieldInjectedObject = JbootConfigManager.me().convert(fieldInjectedClass, value); - setFieldValue(field, targetObject, fieldInjectedObject); return; } @@ -260,21 +272,45 @@ public class JbootAopFactory extends AopFactory { private void initBeanMapping() { List classes = ClassScanner.scanClassByAnnotation(Bean.class, true); for (Class implClass : classes) { + Bean bean = (Bean) implClass.getAnnotation(Bean.class); + if (StrUtil.isNotBlank(bean.name())) { + beansMap.put(bean.name(), get(implClass)); + } Class[] interfaceClasses = implClass.getInterfaces(); - if (interfaceClasses == null || interfaceClasses.length == 0) { continue; } Class[] excludes = buildExcludeClasses(implClass); - for (Class interfaceClass : interfaceClasses) { if (inExcludes(interfaceClass, excludes) == false) { this.addMapping(interfaceClass, implClass); } } } + + + List configurationClasses = ClassScanner.scanClassByAnnotation(Configuration.class, true); + for (Class configurationClass : configurationClasses) { + Object configurationObj = ClassUtil.newInstance(configurationClass); + if (configurationObj == null) { + throw new NullPointerException("can not newInstance for class : " + configurationClass); + } + Method[] methods = configurationClass.getDeclaredMethods(); + for (Method method : methods) { + Bean bean = method.getAnnotation(Bean.class); + if (bean != null) { + String beanName = StrUtil.isBlank(bean.name()) ? method.getName() : bean.name(); + try { + Object methodObj = method.invoke(configurationObj); + beansMap.put(beanName, methodObj); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + } } private Class[] buildExcludeClasses(Class implClass) { diff --git a/src/main/java/io/jboot/aop/annotation/Bean.java b/src/main/java/io/jboot/aop/annotation/Bean.java index a43ed141..ea435d6d 100644 --- a/src/main/java/io/jboot/aop/annotation/Bean.java +++ b/src/main/java/io/jboot/aop/annotation/Bean.java @@ -19,7 +19,7 @@ import java.lang.annotation.*; @Inherited @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE}) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) public @interface Bean { String name() default ""; } \ No newline at end of file diff --git a/src/main/java/io/jboot/aop/annotation/Configuration.java b/src/main/java/io/jboot/aop/annotation/Configuration.java new file mode 100644 index 00000000..1ea2f933 --- /dev/null +++ b/src/main/java/io/jboot/aop/annotation/Configuration.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop.annotation; + +import java.lang.annotation.*; + +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface Configuration { + String name() default ""; +} \ No newline at end of file -- Gitee From 67101b8b2f5d748a70bdf74a193ce36bb82839db Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 11 Mar 2020 12:57:12 +0800 Subject: [PATCH 0058/1965] add @Configuration support --- .../java/io/jboot/aop/JbootAopFactory.java | 11 ++++-- .../io/jboot/test/aop/AopConfiguration.java | 36 +++++++++++++++++++ .../test/aop/cache/AopCacheController.java | 8 +++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 src/test/java/io/jboot/test/aop/AopConfiguration.java diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 538fe2d6..28d7bbb4 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -103,7 +103,7 @@ public class JbootAopFactory extends AopFactory { if (inject != null) { Bean bean = field.getAnnotation(Bean.class); if (bean != null && StrUtil.isNotBlank(bean.name())) { - doInjectByName(targetObject, field, bean.name()); + doInjectByName(targetObject, field, inject, bean.name()); } else { doInjectJFinalOrginal(targetObject, field, inject); } @@ -142,9 +142,14 @@ public class JbootAopFactory extends AopFactory { } - private void doInjectByName(Object targetObject, Field field, String name) throws ReflectiveOperationException { + private void doInjectByName(Object targetObject, Field field, Inject inject, String name) throws ReflectiveOperationException { Object fieldInjectedObject = beansMap.get(name); - setFieldValue(field, targetObject, fieldInjectedObject); + if (fieldInjectedObject != null) { + setFieldValue(field, targetObject, fieldInjectedObject); + } else { + LOG.warn("can not inject by name [" + name + "] in " + targetObject.getClass()+"."+field.getName()+", use default."); + doInjectJFinalOrginal(targetObject, field, inject); + } } /** diff --git a/src/test/java/io/jboot/test/aop/AopConfiguration.java b/src/test/java/io/jboot/test/aop/AopConfiguration.java new file mode 100644 index 00000000..9d63df8f --- /dev/null +++ b/src/test/java/io/jboot/test/aop/AopConfiguration.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.test.aop; + +import io.jboot.aop.annotation.Bean; +import io.jboot.aop.annotation.Configuration; +import io.jboot.test.aop.cache.CommentService; +import io.jboot.test.aop.cache.CommentServiceImpl; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/11 + */ +@Configuration +public class AopConfiguration { + + @Bean(name = "myCommentServiceFromConfiguration") + public CommentService myCommentService(){ + CommentService commentService = new CommentServiceImpl(); + System.out.println("myCommentService:" + commentService); + return commentService; + } +} diff --git a/src/test/java/io/jboot/test/aop/cache/AopCacheController.java b/src/test/java/io/jboot/test/aop/cache/AopCacheController.java index cbb8aee8..63e27f9b 100644 --- a/src/test/java/io/jboot/test/aop/cache/AopCacheController.java +++ b/src/test/java/io/jboot/test/aop/cache/AopCacheController.java @@ -1,6 +1,7 @@ package io.jboot.test.aop.cache; import com.jfinal.aop.Inject; +import io.jboot.aop.annotation.Bean; import io.jboot.test.db.model.User; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; @@ -14,8 +15,15 @@ public class AopCacheController extends JbootController { @Inject private CommentService commentService; + @Inject + @Bean(name = "myCommentServiceFromConfiguration") + private CommentService myCommentService; + public void index() { + System.out.println("commentService:"+commentService); + System.out.println("myCommentService:"+myCommentService); + renderText("text from : " + commentService.getCommentById("index")); } -- Gitee From 23aa4d9a4c9f5c8200e6dea707ef36a979500fe8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 11 Mar 2020 14:38:15 +0800 Subject: [PATCH 0059/1965] add @Configuration support --- src/main/java/io/jboot/Jboot.java | 25 ++++++++++++++++++- .../java/io/jboot/aop/JbootAopFactory.java | 21 +++++++++++----- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/Jboot.java b/src/main/java/io/jboot/Jboot.java index 16529e16..802e2c43 100644 --- a/src/main/java/io/jboot/Jboot.java +++ b/src/main/java/io/jboot/Jboot.java @@ -16,6 +16,8 @@ package io.jboot; import com.codahale.metrics.MetricRegistry; +import com.jfinal.aop.Aop; +import io.jboot.aop.JbootAopFactory; import io.jboot.app.config.JbootConfigManager; import io.jboot.components.cache.JbootCache; import io.jboot.components.cache.JbootCacheManager; @@ -67,7 +69,7 @@ public class Jboot { /** * 获取 MetricRegistry * - * @return // + * @return */ public static MetricRegistry getMetric() { return JbootMetricManager.me().metric(); @@ -185,4 +187,25 @@ public class Jboot { sendEvent(new JbootEvent(action, data)); } + + /** + * 根据类名获取 Aop 下的 Bean + * @param clazz + * @param + * @return + */ + public static T getBean(Class clazz) { + return Aop.get(clazz); + } + + /** + * 根据名称获取 Aop 下的 Bean + * @param name + * @param + * @return + */ + public static T getBean(String name) { + return JbootAopFactory.me().getBean(name); + } + } diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 28d7bbb4..7ec3241f 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -90,6 +90,7 @@ public class JbootAopFactory extends AopFactory { return Proxy.get(targetClass); } + @Override protected void doInject(Class targetClass, Object targetObject) throws ReflectiveOperationException { targetClass = getUsefulClass(targetClass); @@ -102,8 +103,9 @@ public class JbootAopFactory extends AopFactory { Inject inject = field.getAnnotation(Inject.class); if (inject != null) { Bean bean = field.getAnnotation(Bean.class); - if (bean != null && StrUtil.isNotBlank(bean.name())) { - doInjectByName(targetObject, field, inject, bean.name()); + String beanName = bean != null ? AnnotationUtil.get(bean.name()) : null; + if (StrUtil.isNotBlank(beanName)) { + doInjectByName(targetObject, field, inject, beanName); } else { doInjectJFinalOrginal(targetObject, field, inject); } @@ -147,7 +149,7 @@ public class JbootAopFactory extends AopFactory { if (fieldInjectedObject != null) { setFieldValue(field, targetObject, fieldInjectedObject); } else { - LOG.warn("can not inject by name [" + name + "] in " + targetObject.getClass()+"."+field.getName()+", use default."); + LOG.warn("can not inject by name [" + name + "] in " + targetObject.getClass() + "." + field.getName() + ", use default."); doInjectJFinalOrginal(targetObject, field, inject); } } @@ -278,8 +280,9 @@ public class JbootAopFactory extends AopFactory { List classes = ClassScanner.scanClassByAnnotation(Bean.class, true); for (Class implClass : classes) { Bean bean = (Bean) implClass.getAnnotation(Bean.class); - if (StrUtil.isNotBlank(bean.name())) { - beansMap.put(bean.name(), get(implClass)); + String beanName = AnnotationUtil.get(bean.name()); + if (StrUtil.isNotBlank(beanName)) { + beansMap.put(beanName, get(implClass)); } Class[] interfaceClasses = implClass.getInterfaces(); @@ -306,7 +309,7 @@ public class JbootAopFactory extends AopFactory { for (Method method : methods) { Bean bean = method.getAnnotation(Bean.class); if (bean != null) { - String beanName = StrUtil.isBlank(bean.name()) ? method.getName() : bean.name(); + String beanName = StrUtil.obtainDefaultIfBlank(AnnotationUtil.get(bean.name()), method.getName()); try { Object methodObj = method.invoke(configurationObj); beansMap.put(beanName, methodObj); @@ -327,6 +330,7 @@ public class JbootAopFactory extends AopFactory { : ArrayUtil.concat(DEFAULT_EXCLUDES_MAPPING_CLASSES, beanExclude.value()); } + private boolean inExcludes(Class interfaceClass, Class[] excludes) { for (Class ex : excludes) { if (ex.isAssignableFrom(interfaceClass)) { @@ -335,4 +339,9 @@ public class JbootAopFactory extends AopFactory { } return false; } + + + public T getBean(String name) { + return (T) beansMap.get(name); + } } -- Gitee From ed5cf935ebc2c9d0e5411d26c3f83920d24fadf9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 11 Mar 2020 14:48:47 +0800 Subject: [PATCH 0060/1965] add @Configuration support --- src/main/java/io/jboot/db/model/JbootModel.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 784c9165..87852286 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -260,6 +260,7 @@ public class JbootModel> extends Model { LOG.error(ex.toString(), ex); safeDeleteCache(idValues); } + return JbootModel.super.findByIds(idValues); } @@ -268,7 +269,7 @@ public class JbootModel> extends Model { config.getIdCache().remove(_getTableName() , buildCacheKey(idValues)); } catch (Exception ex) { - LOG.error(ex.toString(), ex); + LOG.error("remove cache exception by name [" + _getTableName() + "] and key [" + buildCacheKey(idValues) + "]", ex); } } -- Gitee From bbe8194681678e6b0049461d22a6ace13ae6dfa7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 11 Mar 2020 21:23:23 +0800 Subject: [PATCH 0061/1965] add aop docs --- doc/docs/aop.md | 124 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 doc/docs/aop.md diff --git a/doc/docs/aop.md b/doc/docs/aop.md new file mode 100644 index 00000000..1df94274 --- /dev/null +++ b/doc/docs/aop.md @@ -0,0 +1,124 @@ +# 面向切面编程 + +## 目录 + +- JFinal AOP +- JBoot AOP +- @Inject +- @RPCInject +- @Bean +- @BeanExclude +- @Configuration +- @ConfigValue +- @StaticConstruct + +## JFinal AOP + +参考文档:https://jfinal.com/doc/4-6 + +## JBoot AOP + +JBoot AOP 在 JFinal AOP 的基础上,新增了我们在分布式下常用的功能,同时借鉴了 Spring AOP 的一些特征,对 JFinal AOP 做了功能的强制,但是又没有 Spring AOP 体系的复杂度。 + +## @Inject + +我们可以通过 @Inject 对任何 Bean 的属性进行注入,例如 Controller + +```java +@RequestMapping("/helloworld") +public class MyController extends Controller{ + + @Inject + private UserService userService; + + public void index(){ + renderJson(userService.findAll()); + } +} +``` + +在以上的例子中,在默认情况下,JFinal AOP 会去实例化一个 UserService 实例,并注入到 MyController 的 userService 中,因此需要注意的是:UserService 必须是一个可以被实例化的类,**不能是**抽象类或者接口(Interface)。 + +如果说,UserService 是一个接口,它有实现类比如 `UserServiceImpl.class`,JFinal 提供了另一种方案,代码如下: + +```java +@RequestMapping("/helloworld") +public class MyController extends Controller{ + + @Inject(UserServiceImpl.class) + private UserService userService; + + public void index(){ + renderJson(userService.findAll()); + } +} +``` + +## @Bean +在以上的例子中,我们认为 `@Inject(UserServiceImpl.class)` 这可能不是最好的方案,因此,JBoot 提供了可以通过注解 `@Bean` 给 `UserServiceImpl.class` 添加在类上,这样 Jboot 在启动的时候,会自动扫描到 `UserServiceImpl.class` ,并通过 JbootAopFactory 把 UserService 和 UserServiceImpl 添加上关联关系。 + +代码如下: + +Controller: + +```java +@RequestMapping("/helloworld") +public class MyController extends Controller{ + + @Inject + private UserService userService; + + public void index(){ + renderJson(userService.findAll()); + } +} +``` + +UserService: + +```java +public interface UserService{ + public List findAll(); +} +``` + + +UserServiceImpl: + +```java +@Bean +public class UserServiceImpl implements UserService{ + public List findAll(){ + //do sth + } +} +``` + +## @BeanExclude + +当我们使用 `@Bean` 给某个类添加上注解之后,这个类会做好其实现的所有接口,但是,很多时候我们往往不需要这样做,比如: + +```java +@Bean +public class UserServiceImpl implements UserService, +OtherInterface1,OtherInterface2...{ + + public List findAll(){ + //do sth + } +} + +在某些场景下,我们可能只希望 UserServiceImpl 和 UserService 做好映射关系,此时,`@BeanExclude` 就派上用场了。 + +如下代码排除了 UserServiceImpl 和 OtherInterface1,OtherInterface2 的映射关系。 + +```java +@Bean +@BeanExclude({OtherInterface1.cass,OtherInterface2.class}) +public class UserServiceImpl implements UserService, +OtherInterface1,OtherInterface2...{ + + public List findAll(){ + //do sth + } +} \ No newline at end of file -- Gitee From 8a6b73d38abfd884995a531401098990422c1a26 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 12 Mar 2020 08:35:32 +0800 Subject: [PATCH 0062/1965] add @Configuration support --- .../java/io/jboot/aop/JbootAopFactory.java | 40 +++++++++++++++++-- .../io/jboot/test/aop/AopConfiguration.java | 7 ++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 7ec3241f..04d3fcd2 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -34,6 +34,7 @@ import io.jboot.components.rpc.JbootrpcManager; import io.jboot.components.rpc.JbootrpcServiceConfig; import io.jboot.components.rpc.annotation.RPCInject; import io.jboot.db.model.JbootModel; +import io.jboot.exception.JbootException; import io.jboot.service.JbootServiceBase; import io.jboot.utils.*; import io.jboot.web.controller.JbootController; @@ -282,6 +283,10 @@ public class JbootAopFactory extends AopFactory { Bean bean = (Bean) implClass.getAnnotation(Bean.class); String beanName = AnnotationUtil.get(bean.name()); if (StrUtil.isNotBlank(beanName)) { + if (beansMap.containsKey(beanName)) { + throw new JbootException("application has contains beanName \"" + beanName + "\" for " + beansMap.get(beanName) + + ", can not add for class " + implClass); + } beansMap.put(beanName, get(implClass)); } @@ -292,7 +297,7 @@ public class JbootAopFactory extends AopFactory { Class[] excludes = buildExcludeClasses(implClass); for (Class interfaceClass : interfaceClasses) { - if (inExcludes(interfaceClass, excludes) == false) { + if (!inExcludes(interfaceClass, excludes)) { this.addMapping(interfaceClass, implClass); } } @@ -310,17 +315,42 @@ public class JbootAopFactory extends AopFactory { Bean bean = method.getAnnotation(Bean.class); if (bean != null) { String beanName = StrUtil.obtainDefaultIfBlank(AnnotationUtil.get(bean.name()), method.getName()); + if (beansMap.containsKey(beanName)) { + throw new JbootException("application has contains beanName \"" + beanName + "\" for " + beansMap.get(beanName) + + ", can not add again by method:" + ClassUtil.buildMethodString(method)); + } + + Object methodObj = null; try { - Object methodObj = method.invoke(configurationObj); - beansMap.put(beanName, methodObj); + methodObj = method.invoke(configurationObj); + if (methodObj != null) { + inject(methodObj); + beansMap.put(beanName, methodObj); + } } catch (Exception e) { throw new RuntimeException(e); } + + if (methodObj != null) { + Class implClass = ClassUtil.getUsefulClass(methodObj.getClass()); + Class[] interfaceClasses = implClass.getInterfaces(); + if (interfaceClasses == null || interfaceClasses.length == 0) { + continue; + } + + Class[] excludes = buildExcludeClasses(implClass); + for (Class interfaceClass : interfaceClasses) { + if (!inExcludes(interfaceClass, excludes) && !mapping.containsKey(interfaceClass)) { + this.addMapping(interfaceClass, implClass); + } + } + } } } } } + private Class[] buildExcludeClasses(Class implClass) { BeanExclude beanExclude = (BeanExclude) implClass.getAnnotation(BeanExclude.class); @@ -344,4 +374,8 @@ public class JbootAopFactory extends AopFactory { public T getBean(String name) { return (T) beansMap.get(name); } + + public void setBean(String name, Object obj) { + beansMap.put(name, obj); + } } diff --git a/src/test/java/io/jboot/test/aop/AopConfiguration.java b/src/test/java/io/jboot/test/aop/AopConfiguration.java index 9d63df8f..49719d26 100644 --- a/src/test/java/io/jboot/test/aop/AopConfiguration.java +++ b/src/test/java/io/jboot/test/aop/AopConfiguration.java @@ -33,4 +33,11 @@ public class AopConfiguration { System.out.println("myCommentService:" + commentService); return commentService; } + + @Bean() + public CommentService myCommentService1(){ + CommentService commentService = new CommentServiceImpl(); + System.out.println("myCommentService:" + commentService); + return commentService; + } } -- Gitee From 1497d1e306865ec0c582e0c5b84929b2dae4528d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 12 Mar 2020 08:35:50 +0800 Subject: [PATCH 0063/1965] add aop docs --- doc/docs/aop.md | 120 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/doc/docs/aop.md b/doc/docs/aop.md index 1df94274..8910347d 100644 --- a/doc/docs/aop.md +++ b/doc/docs/aop.md @@ -94,6 +94,68 @@ public class UserServiceImpl implements UserService{ } ``` +当一个接口有多个实现类,或者当在系统中存在多个实例对象,比如有两份 Cache 对象,一份可能是 Redis Server1,一份可能是 Redis Server2,或者有两份数据源 DataSource 等,在这种情况下,我们注入的时候就需要确定注入那个实例。 + +这时候,我们就需要用到 `@Bean(name= "myName")` 去给不同的子类去添加注释。 + +例如: + + +```java +@RequestMapping("/helloworld") +public class MyController extends Controller{ + + @Inject + @Bean(name="userServieImpl1") //注入名称为 userServieImpl1 的实例 + private UserService userService1; + + @Inject + @Bean(name="userServieImpl2") //注入名称为 userServieImpl2 的实例 + private UserService userService2; + + public void index(){ + renderJson(userService.findAll()); + } +} +``` + +UserService: + +```java +public interface UserService{ + public List findAll(); +} +``` + + +UserServiceImpl1: + +```java +@Bean(name="userServiceImpl1") +public class UserServiceImpl1 implements UserService{ + public List findAll(){ + //do sth + } +} +``` + + +UserServiceImpl2: + +```java +@Bean(name="userServiceImpl2") +public class UserServiceImpl2 implements UserService{ + public List findAll(){ + //do sth + } +} +``` + +这种情况,只是针对一个接口有多个实现类的情况,那么,如果是一个接口只有一个实现类,但是有多个实例,如何进行注入呢? + +参考 @Configuration 。 + + ## @BeanExclude 当我们使用 `@Bean` 给某个类添加上注解之后,这个类会做好其实现的所有接口,但是,很多时候我们往往不需要这样做,比如: @@ -121,4 +183,60 @@ OtherInterface1,OtherInterface2...{ public List findAll(){ //do sth } -} \ No newline at end of file +} + +``` + +## @Configuration + + +在 Jboot 中的 `@Configuration` 和 Spring 体系的 `@Configuration` 功能类似。 + + +我们可以在一个普通类中添加注解 `@Configuration` , 然后在其方法通过 `@Bean` 去对方法进行添加注解。 + +例如: + +```java +@Configuration +public class AppConfiguration { + + @Bean(name = "myCommentServiceFromConfiguration") + public CommentService myCommentService1(){ + CommentService commentService = new CommentServiceImpl(); + return commentService; + } + + @Bean + public CommentService myCommentService2(){ + CommentService commentService = new CommentServiceImpl(); + return commentService; + } +} + +``` + +这样,在一个 Jboot 应用中,就会存在两份 `CommentService` 他们的名称分别为:myCommentServiceFromConfiguration 和 myCommentService2(当只用了注解 @Bean 但是为添加 name 参数时,名称为方法名) + +这样,我们就可以在 Controller 里,通过 `@Inject` 配合 `@Bean(name = ... )` 进行注入,例如: + + +```java +@RequestMapping("/aopcache") +public class AopCacheController extends JbootController { + + @Inject + @Bean(name="myCommentService2") + private CommentService commentService; + + @Inject + @Bean(name = "myCommentServiceFromConfiguration") + private CommentService myCommentService; + + + public void index() { + System.out.println("commentService:"+commentService); + System.out.println("myCommentService:"+myCommentService); + } +} +``` \ No newline at end of file -- Gitee From ad75e4cfa75818648cce5b01e30d685b890aeaaa Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 12 Mar 2020 09:20:11 +0800 Subject: [PATCH 0064/1965] optimize @ConfigValue --- src/main/java/io/jboot/aop/JbootAopFactory.java | 11 ----------- .../java/io/jboot/aop/annotation/ConfigValue.java | 6 ------ .../java/io/jboot/test/aop/inject/AopController.java | 2 +- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 04d3fcd2..11c80b96 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -216,17 +216,6 @@ public class JbootAopFactory extends AopFactory { setFieldValue(field, targetObject, fieldInjectedObject); return; } - - if (configValue.requireNullOrBlank()) { - field.setAccessible(true); - if (fieldInjectedClass == int.class) { - field.set(targetObject, 0); - } else if (fieldInjectedClass == boolean.class) { - field.set(targetObject, false); - } else { - field.set(targetObject, null); - } - } } diff --git a/src/main/java/io/jboot/aop/annotation/ConfigValue.java b/src/main/java/io/jboot/aop/annotation/ConfigValue.java index 17c6f7f6..47c0d01f 100644 --- a/src/main/java/io/jboot/aop/annotation/ConfigValue.java +++ b/src/main/java/io/jboot/aop/annotation/ConfigValue.java @@ -25,10 +25,4 @@ public @interface ConfigValue { String value(); - /** - * 当读取配置内容为空的时候,是否进行赋值 - * @return 默认为 true - */ - boolean requireNullOrBlank() default false; - } diff --git a/src/test/java/io/jboot/test/aop/inject/AopController.java b/src/test/java/io/jboot/test/aop/inject/AopController.java index f21061db..5026f256 100644 --- a/src/test/java/io/jboot/test/aop/inject/AopController.java +++ b/src/test/java/io/jboot/test/aop/inject/AopController.java @@ -23,7 +23,7 @@ public class AopController extends JbootController { @ConfigValue("undertow.port") private int port; - @ConfigValue(value = "undertow.xxx",requireNullOrBlank = true) + @ConfigValue(value = "undertow.xxx") private int xxx; -- Gitee From 4357a47fbb3434a007cb241fed26c3341c820bc8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 12 Mar 2020 09:20:31 +0800 Subject: [PATCH 0065/1965] add aop docs --- README.md | 1 + doc/docs/aop.md | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ doc/readme.md | 1 + 3 files changed, 90 insertions(+) diff --git a/README.md b/README.md index ee578994..c6e35179 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ public class HelloworldController extends JbootController { - [JFinalConfig](./doc/docs/jfinalConfig.md) - [WebSocket](./doc/docs/websocket.md) - [MVC](./doc/docs/mvc.md) +- [AOP](./doc/docs/aop.md) - [数据库](./doc/docs/db.md) - [缓存](./doc/docs/cache.md) - [RPC远程调用](./doc/docs/rpc.md) diff --git a/doc/docs/aop.md b/doc/docs/aop.md index 8910347d..a8501ec6 100644 --- a/doc/docs/aop.md +++ b/doc/docs/aop.md @@ -239,4 +239,92 @@ public class AopCacheController extends JbootController { System.out.println("myCommentService:"+myCommentService); } } +``` + + +## @ConfigValue + +在 AOP 注入中,可能很多时候我们需要注入的只是一个配置内容,而非一个对象实例,此时,我们就可以使用注解 `@ConfigValue`,例如: + +```java +@RequestMapping("/aop") +public class AopController extends JbootController { + + @ConfigValue("undertow.host") + private String host; + + @ConfigValue("undertow.port") + private int port; + + @ConfigValue(value = "undertow.xxx") + private int xxx; +} +``` + +此时,配置文件 jboot.properties (包括分布式配置中心) 里配置的 undertow.host 的值自动赋值给 host 属性。其他属性同理。 + + +## @StaticConstruct + +静态的构造方法。 + +在某些类中,这个类的创建方式并不是通过 new 的方式进行创建的,或者可能构造函数是私有的,或者这个类可能是一个单例示例的类,比如: + +```java +public class JbootManager { + + private static JbootManager me = new JbootManager(); + + public static JbootManager me() { + return me; + } + + private JbootManager(){ + //do sth + } +} +``` + +我们在其他类注入 JbootManager 的时候,并不希望通过 new JbootManager() 的方式进行创建注入,还是希望通过其方法 `me()` 进行获取。 + +此时,我们就可以给 JbootManager 类添加 `@StaticConstruct` 注解,例如: + +```java +@StaticConstruct +public class JbootManager { + + private static JbootManager me = new JbootManager(); + + public static JbootManager me() { + return me; + } + + private JbootManager(){ + //do sth + } +} +``` + +但如果 JbootManager 有多个返回自己对象的静态方法,我们就可以使用 `@StaticConstruct` 的 value 参数来指定。 + +例如: + +```java +@StaticConstruct("me") +public class JbootManager { + + private static JbootManager me = new JbootManager(); + + public static JbootManager me() { + return me; + } + + public static JbootManager create(){ + return new JbootManager() + } + + private JbootManager(){ + //do sth + } +} ``` \ No newline at end of file diff --git a/doc/readme.md b/doc/readme.md index 72be4b64..e16c8c48 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -9,6 +9,7 @@ - [JFinalConfig](./docs/jfinalConfig.md) - [WebSocket](./docs/websocket.md) - [MVC](./docs/mvc.md) +- [AOP](./docs/aop.md) - [数据库操作](./docs/db.md) - [缓存](./docs/cache.md) - [RPC远程调用](./docs/rpc.md) -- Gitee From 04a7d1a31a1fa50d5d18507ce3775e5b78ae0cf6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 12 Mar 2020 09:23:10 +0800 Subject: [PATCH 0066/1965] add aop docs --- doc/docs/aop.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/docs/aop.md b/doc/docs/aop.md index a8501ec6..ec0c8868 100644 --- a/doc/docs/aop.md +++ b/doc/docs/aop.md @@ -169,6 +169,7 @@ OtherInterface1,OtherInterface2...{ //do sth } } +``` 在某些场景下,我们可能只希望 UserServiceImpl 和 UserService 做好映射关系,此时,`@BeanExclude` 就派上用场了。 @@ -184,7 +185,6 @@ OtherInterface1,OtherInterface2...{ //do sth } } - ``` ## @Configuration -- Gitee From 9e9470c1e929af5897d21bf5b2b89eab6f73d58e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 12 Mar 2020 10:53:31 +0800 Subject: [PATCH 0067/1965] add JbootAopInterceptor.addInterceptor() and JbootAopInterceptor.removeInterceptor() methods --- .../io/jboot/aop/JbootAopInterceptor.java | 124 +++++++++++++++++- .../java/io/jboot/aop/JbootAopInvocation.java | 25 +--- 2 files changed, 127 insertions(+), 22 deletions(-) diff --git a/src/main/java/io/jboot/aop/JbootAopInterceptor.java b/src/main/java/io/jboot/aop/JbootAopInterceptor.java index adfcc0da..c7ca328d 100644 --- a/src/main/java/io/jboot/aop/JbootAopInterceptor.java +++ b/src/main/java/io/jboot/aop/JbootAopInterceptor.java @@ -17,13 +17,135 @@ package io.jboot.aop; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; +import io.jboot.components.cache.interceptor.JbootCacheEvictInterceptor; +import io.jboot.components.cache.interceptor.JbootCacheInterceptor; +import io.jboot.components.cache.interceptor.JbootCachePutInterceptor; +import io.jboot.components.cache.interceptor.JbootCachesEvictInterceptor; +import io.jboot.components.limiter.LimiterInterceptor; +import io.jboot.support.metric.JbootMetricInterceptor; +import io.jboot.support.seata.interceptor.SeataGlobalTransactionalInterceptor; +import io.jboot.support.sentinel.SentinelInterceptor; + +import java.util.LinkedList; +import java.util.List; public class JbootAopInterceptor implements Interceptor { + static final Interceptor[] JBOOT_INTERS = { + new SentinelInterceptor(), + new LimiterInterceptor(), + new JbootMetricInterceptor(), + new JbootCacheEvictInterceptor(), + new JbootCachesEvictInterceptor(), + new JbootCachePutInterceptor(), + new JbootCacheInterceptor(), + new SeataGlobalTransactionalInterceptor() + }; + + + private static Interceptor[] aopInterceptors = JBOOT_INTERS; + + @Override public void intercept(Invocation inv) { - JbootAopInvocation invocation = new JbootAopInvocation(inv); + JbootAopInvocation invocation = new JbootAopInvocation(inv, aopInterceptors); invocation.invoke(); } + + /** + * 添加新的拦截器 + * + * @param interceptor + * @param toIndex + */ + public static void addInterceptor(Interceptor interceptor, int toIndex) { + + if (interceptor == null) { + throw new NullPointerException("interceptor is null"); + } + + synchronized (JbootAopInterceptor.class) { + + int length = aopInterceptors.length; + + if (toIndex < 0) { + toIndex = 0; + } + + if (toIndex > length) { + toIndex = length; + } + + Interceptor[] temp = new Interceptor[length + 1]; + + System.arraycopy(aopInterceptors, 0, temp, 0, toIndex); + temp[toIndex] = interceptor; + if (toIndex < length) { + System.arraycopy(aopInterceptors, toIndex, temp, toIndex + 1, length - toIndex); + } + + aopInterceptors = temp; + } + } + + + /** + * 移除拦截器 + * + * @param interceptor + */ + public static void removeInterceptor(Interceptor interceptor) { + + if (interceptor == null) { + throw new NullPointerException("interceptor is null"); + } + + synchronized (JbootAopInterceptor.class) { + + int length = aopInterceptors.length; + List tempList = new LinkedList<>(); + + for (int i = 0; i < length; i++) { + if (aopInterceptors[i] != interceptor) { + tempList.add(aopInterceptors[i]); + } + } + + if (tempList.size() != length) { + aopInterceptors = tempList.toArray(new Interceptor[]{}); + } + } + } + + + /** + * 移除拦截器 + * + * @param clazz + */ + public static void removeInterceptor(Class clazz) { + + if (clazz == null) { + throw new NullPointerException("interceptor class is null"); + } + + synchronized (JbootAopInterceptor.class) { + + int length = aopInterceptors.length; + List tempList = new LinkedList<>(); + + for (int i = 0; i < length; i++) { + if (aopInterceptors[i].getClass() != clazz) { + tempList.add(aopInterceptors[i]); + } + } + + if (tempList.size() != length) { + aopInterceptors = tempList.toArray(new Interceptor[]{}); + } + } + } + + } diff --git a/src/main/java/io/jboot/aop/JbootAopInvocation.java b/src/main/java/io/jboot/aop/JbootAopInvocation.java index ff035747..dbd8a005 100644 --- a/src/main/java/io/jboot/aop/JbootAopInvocation.java +++ b/src/main/java/io/jboot/aop/JbootAopInvocation.java @@ -17,41 +17,24 @@ package io.jboot.aop; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; -import io.jboot.components.cache.interceptor.JbootCacheEvictInterceptor; -import io.jboot.components.cache.interceptor.JbootCacheInterceptor; -import io.jboot.components.cache.interceptor.JbootCachePutInterceptor; -import io.jboot.components.cache.interceptor.JbootCachesEvictInterceptor; -import io.jboot.components.limiter.LimiterInterceptor; import io.jboot.exception.JbootException; -import io.jboot.support.metric.JbootMetricInterceptor; -import io.jboot.support.seata.interceptor.SeataGlobalTransactionalInterceptor; -import io.jboot.support.sentinel.SentinelInterceptor; import java.lang.reflect.Method; public class JbootAopInvocation extends Invocation { - private static final Interceptor[] ALL_INTERS = { - new SentinelInterceptor(), - new JbootMetricInterceptor(), - new JbootCacheEvictInterceptor(), - new JbootCachesEvictInterceptor(), - new JbootCachePutInterceptor(), - new JbootCacheInterceptor(), - new LimiterInterceptor(), - new SeataGlobalTransactionalInterceptor() - }; - - private Interceptor[] inters = ALL_INTERS; + private Interceptor[] inters; private Invocation originInvocation; + private int index = 0; - public JbootAopInvocation(Invocation originInvocation) { + public JbootAopInvocation(Invocation originInvocation, Interceptor[] inters) { this.originInvocation = originInvocation; + this.inters = inters; } -- Gitee From 0bb2f6cf56df230a4ffe0a611119e03e31348581 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 13 Mar 2020 10:03:24 +0800 Subject: [PATCH 0068/1965] optimize ... --- .../java/io/jboot/app/config/JbootConfigDecryptor.java | 6 ++++-- src/main/java/io/jboot/app/config/JbootConfigManager.java | 2 +- .../jboot/app/config/support/nacos/NacosConfigIniter.java | 3 +-- .../app/config/support/nacos/NacosConfigManager.java | 8 +++++++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigDecryptor.java b/src/main/java/io/jboot/app/config/JbootConfigDecryptor.java index 504c8ad1..d9591ebf 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigDecryptor.java +++ b/src/main/java/io/jboot/app/config/JbootConfigDecryptor.java @@ -16,9 +16,11 @@ package io.jboot.app.config; /** - * Jboot Config 的内容解密器,加密方式由可客户自己编写的加密算法来加密 + * Jboot Config 的内容解密器 + * + * value 值的加密方式,由用户自己编写的加密算法来加密 *

- * 此时,只需要给 JbootConfigManager 配置上 Decryptor 进行解密即可 + * 此时,要正确读取 value 的内容,需要给 JbootConfigManager 配置上 Decryptor 进行解密 */ public interface JbootConfigDecryptor { diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index eac9d959..59322f06 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -253,7 +253,7 @@ public class JbootConfigManager { /** - * 获取值的优先顺序:1、启动配置 2、环境变量 3、properties配置文件 + * 获取值的优先顺序:1、远程配置 2、启动配置 3、环境变量 4、系统属性 5、properties配置文件 * * @param key * @return diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java index a1ec0c3b..195d2d49 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java @@ -35,8 +35,7 @@ public class NacosConfigIniter { public void initListener(ConfigService configService, NacosServerConfig config) { try { - configService.addListener(config.getDataId() - , config.getGroup() + configService.addListener(config.getDataId(), config.getGroup() , new Listener() { @Override public Executor getExecutor() { diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java index 7dd6f2e2..50f307ef 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java @@ -43,6 +43,9 @@ public class NacosConfigManager { private Properties contentProperties; + /** + * 初始化 nacos 配置监听 + */ public void init() { NacosServerConfig nacosServerConfig = Jboot.config(NacosServerConfig.class); @@ -73,7 +76,10 @@ public class NacosConfigManager { } - + /** + * 接收的 nacos 服务器消息 + * @param configInfo + */ public void doReceiveConfigInfo(String configInfo) { Properties properties = str2Properties(configInfo); Set changedKeys = new HashSet<>(); -- Gitee From 620e28489a4a58b6fe0ecde50974d0249b9de6fe Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 15 Mar 2020 08:19:36 +0800 Subject: [PATCH 0069/1965] add sentinel docs --- README.md | 1 + doc/docs/sentinel.md | 84 +++++++++++++++++++ doc/readme.md | 1 + .../io/jboot/test/db/simple/DbController.java | 2 + 4 files changed, 88 insertions(+) create mode 100644 doc/docs/sentinel.md diff --git a/README.md b/README.md index c6e35179..6580fb26 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ public class HelloworldController extends JbootController { - [1.x 升级到 2.x 教程](./doc/docs/upgrade.md) - [交流社区、QQ群和微信群](./doc/docs/communication.md) - 第三方组件的支持 + - [sentinel 限流降级](./doc/docs/sentinel.md) - [redis](./doc/docs/redis.md) - [shiro](./doc/docs/shiro.md) - [jwt](./doc/docs/jwt.md) diff --git a/doc/docs/sentinel.md b/doc/docs/sentinel.md new file mode 100644 index 00000000..d81a0fae --- /dev/null +++ b/doc/docs/sentinel.md @@ -0,0 +1,84 @@ +# Sentinel 限流 + +## 目录 + +- 概述 +- sentinel 的使用 + +## 概述 +随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 + +Sentinel 具有以下特征: + +- **丰富的应用场景**:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。 +- **完备的实时监控**:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。 +- **广泛的开源生态**:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。 +- **完善的 SPI 扩展点**:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。 + + + +Sentinel 分为两个部分: + +核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。 +控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。 + + +更多的文档请参考: + +https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D + + +## sentinel 的使用 + +**第一步,启动 Sentinel dashboard:** + +下载 Sentinel 的 jar 到本地,并通过如下方式启动 启动 sentinel dashboard + +``` +java -jar sentinel-dashboard-1.7.1.jar +``` + +**第二步:配置项目的 sentinel.properties** + + 在项目的 resource 目录下创建 sentinel.properties 文本,并配置相关信息如下: + + ``` +csp.sentinel.dashboard.server=localhost:8080 + ``` + +**第三步:配置限流资源** + +在项目的任意目录下,使用注解 `@SentinelResource` 给方法进行配置,代码如下: + +```java +@RequestMapping("/sentinel") +public class SentinelController extends JbootController { + + @SentinelResource + public void index(){ + renderText("sentinel index..."); + } +} +``` + +或者在 Service 中 + +```java +public class UserService{ + + // 原本的业务方法. + @SentinelResource(blockHandler = "blockHandlerForGetUser") + public User getUserById(String id) { + throw new RuntimeException("getUserById command failed"); + } + + // blockHandler 函数,原方法调用被限流/降级/系统保护的时候调用 + public User blockHandlerForGetUser(String id, BlockException ex) { + return new User("admin"); + } +} +``` + +然后通过浏览器进入 sentinel dashboard 中心,对 `SentinelController.index()` 和 `UserService.getUserById()` 的资源进行具体的限流参数配置。 + +更多的配置内容请参考:https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0 \ No newline at end of file diff --git a/doc/readme.md b/doc/readme.md index e16c8c48..4ea4ca3e 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -27,6 +27,7 @@ - [1.x 升级到 2.x 教程](./docs/upgrade.md) - [交流社区、QQ群和微信群](./docs/communication.md) - 第三方组件的支持 + - [sentinel 限流降级](./docs/sentinel.md) - [redis](./docs/redis.md) - [shiro](./docs/shiro.md) - [jwt](./docs/jwt.md) diff --git a/src/test/java/io/jboot/test/db/simple/DbController.java b/src/test/java/io/jboot/test/db/simple/DbController.java index 278ae3d5..05e57c92 100644 --- a/src/test/java/io/jboot/test/db/simple/DbController.java +++ b/src/test/java/io/jboot/test/db/simple/DbController.java @@ -103,6 +103,8 @@ public class DbController extends JbootController { columns.in("user.`id`",1,2,3,4); columns.likeAppendPercent("login_name","c"); + +// List users = dao.leftJoin("article","a","user.id=a.user_id").findListByColumns(columns); List users = dao.leftJoin("article").as("a").on("user.id=a.user_id").findListByColumns(columns); renderJson(users); } -- Gitee From 74f3f0307585fc7db98ebdc5d65082b674512d4c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 15 Mar 2020 08:22:40 +0800 Subject: [PATCH 0070/1965] add sentinel docs --- doc/docs/sentinel.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/docs/sentinel.md b/doc/docs/sentinel.md index d81a0fae..171d2d5d 100644 --- a/doc/docs/sentinel.md +++ b/doc/docs/sentinel.md @@ -81,4 +81,6 @@ public class UserService{ 然后通过浏览器进入 sentinel dashboard 中心,对 `SentinelController.index()` 和 `UserService.getUserById()` 的资源进行具体的限流参数配置。 -更多的配置内容请参考:https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0 \ No newline at end of file +关注注解 `@SentinelResource` 更多的配置,请参考文档: https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81#sentinelresource-%E6%B3%A8%E8%A7%A3 + +更多的 sentinel dashboard 配置内容请参考:https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0 \ No newline at end of file -- Gitee From e5c1ca874da32917b4aac6cf3f94dad9808e0281 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 15 Mar 2020 08:45:31 +0800 Subject: [PATCH 0071/1965] add sentinel docs --- doc/docs/sentinel.md | 50 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/doc/docs/sentinel.md b/doc/docs/sentinel.md index 171d2d5d..eaef0648 100644 --- a/doc/docs/sentinel.md +++ b/doc/docs/sentinel.md @@ -38,13 +38,61 @@ https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D java -jar sentinel-dashboard-1.7.1.jar ``` -**第二步:配置项目的 sentinel.properties** + jar 的下载地址:https://github.com/alibaba/Sentinel/releases + +启动时,默认端口号为:8080,可以通过 -Dserver.port=8888 用于指定 Sentinel 控制台端口为 8888。 + +例如: + +``` +java -Dserver.port=8888 -jar sentinel-dashboard-1.7.1.jar +``` + +从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel。 + +可以通过如下配置来修改掉默认的账号和密码: +- -Dsentinel.dashboard.auth.username=sentinel 用于指定控制台的登录用户名为 sentinel; +- -Dsentinel.dashboard.auth.password=123456 用于指定控制台的登录密码为 123456; + +关于控制台的更多配置,请参考: +https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0#%E6%8E%A7%E5%88%B6%E5%8F%B0%E9%85%8D%E7%BD%AE%E9%A1%B9 + +**第二步:配置项目的 sentinel.properties 和 Maven 依赖** 在项目的 resource 目录下创建 sentinel.properties 文本,并配置相关信息如下: ``` csp.sentinel.dashboard.server=localhost:8080 ``` +关于更多 sentinel.properties 的配置请参考: + +https://github.com/alibaba/Sentinel/wiki/%E5%90%AF%E5%8A%A8%E9%85%8D%E7%BD%AE%E9%A1%B9#%E5%9F%BA%E7%A1%80%E9%85%8D%E7%BD%AE%E9%A1%B9 + + + 添加 maven 依赖: + + ```xml + + com.alibaba.csp + sentinel-core + ${sentinel.version} + provided + + + + com.alibaba.csp + sentinel-cluster-client-default + ${sentinel.version} + provided + + + + com.alibaba.csp + sentinel-transport-simple-http + ${sentinel.version} + provided + + ``` **第三步:配置限流资源** -- Gitee From 0a3c84f6b558151511f3dd1246c9e297ed41e847 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 16 Mar 2020 19:01:14 +0800 Subject: [PATCH 0072/1965] optimize ... --- src/main/java/io/jboot/aop/JbootAopFactory.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 11c80b96..05eba2b0 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -146,7 +146,7 @@ public class JbootAopFactory extends AopFactory { private void doInjectByName(Object targetObject, Field field, Inject inject, String name) throws ReflectiveOperationException { - Object fieldInjectedObject = beansMap.get(name); + Object fieldInjectedObject = getBean(name); if (fieldInjectedObject != null) { setFieldValue(field, targetObject, fieldInjectedObject); } else { @@ -273,7 +273,7 @@ public class JbootAopFactory extends AopFactory { String beanName = AnnotationUtil.get(bean.name()); if (StrUtil.isNotBlank(beanName)) { if (beansMap.containsKey(beanName)) { - throw new JbootException("application has contains beanName \"" + beanName + "\" for " + beansMap.get(beanName) + throw new JbootException("application has contains beanName \"" + beanName + "\" for " + getBean(beanName) + ", can not add for class " + implClass); } beansMap.put(beanName, get(implClass)); @@ -305,7 +305,7 @@ public class JbootAopFactory extends AopFactory { if (bean != null) { String beanName = StrUtil.obtainDefaultIfBlank(AnnotationUtil.get(bean.name()), method.getName()); if (beansMap.containsKey(beanName)) { - throw new JbootException("application has contains beanName \"" + beanName + "\" for " + beansMap.get(beanName) + throw new JbootException("application has contains beanName \"" + beanName + "\" for " + getBean(beanName) + ", can not add again by method:" + ClassUtil.buildMethodString(method)); } -- Gitee From 0b3b21397ca7169d930ba013d4b5152b9d4edf0e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 17 Mar 2020 10:01:42 +0800 Subject: [PATCH 0073/1965] add mutil directUrl config support --- .../jboot/components/rpc/JbootrpcConfig.java | 28 ++++++++++++++++--- .../components/rpc/dubbo/JbootDubborpc.java | 6 ++-- .../components/rpc/motan/JbootMotanrpc.java | 6 ++-- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java index b3876040..b407c9c6 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java @@ -20,6 +20,7 @@ import io.jboot.utils.StrUtil; import java.io.IOException; import java.net.ServerSocket; +import java.util.Map; @ConfigModel(prefix = "jboot.rpc") @@ -71,6 +72,12 @@ public class JbootrpcConfig { */ private String directUrl; + /** + * 直连模式下,一个 APP 可能需要链接到多个服务 + * 每个服务有自己的 group 以及对应的 url + */ + private Map directUrlsMap; + /** * 对外暴露服务的相关配置 @@ -159,7 +166,6 @@ public class JbootrpcConfig { } } } - return defaultPort; } @@ -219,11 +225,25 @@ public class JbootrpcConfig { return directUrl; } + public String getDirectUrl(String group) { + return directUrlsMap == null ? null : directUrlsMap.get(group); + } + + public void setDirectUrl(String directUrl) { - if (directUrl != null && directUrl.contains(":")) { - this.defaultPort = Integer.valueOf(directUrl.split(":")[1]); + if (StrUtil.isNotBlank(directUrl) && directUrl.contains(";")) { + String[] directUrls = directUrl.split(";"); + for (String url : directUrls) { + if (StrUtil.isNotBlank(url)) { + int indexOf = url.indexOf(":"); + String group = url.substring(0, indexOf); + String urlStr = url.substring(indexOf + 1); + directUrlsMap.put(group, urlStr); + } + } + } else { + this.directUrl = directUrl; } - this.directUrl = directUrl; } public boolean isDirectCallMode() { diff --git a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java index c301b35d..b1e9ffae 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java @@ -112,10 +112,12 @@ public class JbootDubborpc extends JbootrpcBase { * 直连调用模式 */ else if (getConfig().isDirectCallMode()) { - if (StrUtil.isBlank(getConfig().getDirectUrl())) { + String url = getConfig().getDirectUrl(serviceConfig.getGroup()); + url = StrUtil.isBlank(url) ? getConfig().getDirectUrl() : url; + if (StrUtil.isBlank(url)) { throw new JbootIllegalConfigException("directUrl must not be blank if you use direct call mode,please config jboot.rpc.directUrl value"); } - reference.setUrl(getConfig().getDirectUrl()); + reference.setUrl(url); } diff --git a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java index a2606fe0..bfea034d 100644 --- a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java +++ b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java @@ -102,10 +102,12 @@ public class JbootMotanrpc extends JbootrpcBase { * 直连模式 */ else if (getConfig().isDirectCallMode()) { - if (StrUtil.isBlank(getConfig().getDirectUrl())) { + String url = getConfig().getDirectUrl(serviceConfig.getGroup()); + url = StrUtil.isBlank(url) ? getConfig().getDirectUrl() : url; + if (StrUtil.isBlank(url)) { throw new JbootIllegalConfigException("directUrl must not be blank if you use direct call mode,please config jboot.rpc.directUrl value"); } - refererConfig.setDirectUrl(getConfig().getDirectUrl()); + refererConfig.setDirectUrl(url); } -- Gitee From 7a77b899cc2d7741d5ad87426ee7681ba0c0f1e6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 17 Mar 2020 22:08:46 +0800 Subject: [PATCH 0074/1965] fixed jbootHttpImpl upload file error --- .../components/http/jboot/JbootHttpImpl.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index c2c62cd0..1f146037 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -124,24 +124,26 @@ public class JbootHttpImpl implements JbootHttp { private void uploadData(JbootHttpRequest request, HttpURLConnection connection) throws IOException { String endFlag = "\r\n"; - String boundary = "---------" + StrUtil.uuid(); - connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); + String startFlag = "--"; + String boundary = "------" + StrUtil.uuid(); + connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); DataOutputStream dos = new DataOutputStream(connection.getOutputStream()); for (Map.Entry entry : request.getParams().entrySet()) { if (entry.getValue() instanceof File) { File file = (File) entry.getValue(); checkFileNormal(file); - dos.writeBytes(boundary + endFlag); - dos.writeBytes(String.format("Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"", entry.getKey(), file.getName()) + endFlag); + dos.writeBytes(startFlag + boundary + endFlag); + dos.writeBytes("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"; filename=\"" + file.getName() + "\""); + dos.writeBytes(endFlag); dos.writeBytes(endFlag); FileInputStream fStream = new FileInputStream(file); byte[] buffer = new byte[2028]; for (int len = 0; (len = fStream.read(buffer)) > 0; ) { dos.write(buffer, 0, len); } - dos.writeBytes(endFlag); } else { + dos.writeBytes(startFlag + boundary + endFlag); dos.writeBytes("Content-Disposition: form-data; name=\"" + entry.getKey() + "\""); dos.writeBytes(endFlag); dos.writeBytes(endFlag); @@ -150,7 +152,8 @@ public class JbootHttpImpl implements JbootHttp { } } - dos.writeBytes("--" + boundary + "--" + endFlag); + dos.writeBytes(startFlag + boundary + startFlag + endFlag); + dos.flush(); } private static void checkFileNormal(File file) { -- Gitee From 793650d3a0ced6b2d000c6db7606da0ca94977b4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 18 Mar 2020 00:04:56 +0800 Subject: [PATCH 0075/1965] fixed jbootHttpImpl upload file error --- .../components/http/jboot/JbootHttpImpl.java | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index 1f146037..6b432d7a 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -59,15 +59,7 @@ public class JbootHttpImpl implements JbootHttp { configConnection(connection, request); - if (request.isPostRequest() == false) { - - connection.setInstanceFollowRedirects(true); - connection.connect(); - } - /** - * 处理 post请求 - */ - else if (request.isPostRequest()) { + if (request.isPostRequest()) { connection.setRequestMethod("POST"); connection.setDoOutput(true); @@ -89,6 +81,9 @@ public class JbootHttpImpl implements JbootHttp { } } + }else { + connection.setInstanceFollowRedirects(true); + connection.connect(); } @@ -132,30 +127,32 @@ public class JbootHttpImpl implements JbootHttp { if (entry.getValue() instanceof File) { File file = (File) entry.getValue(); checkFileNormal(file); - dos.writeBytes(startFlag + boundary + endFlag); - dos.writeBytes("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"; filename=\"" + file.getName() + "\""); - dos.writeBytes(endFlag); - dos.writeBytes(endFlag); + writeString(dos, request, startFlag + boundary + endFlag); + writeString(dos, request, "Content-Disposition: form-data; name=\"" + entry.getKey() + "\"; filename=\"" + file.getName() + "\""); + writeString(dos, request, endFlag + endFlag); FileInputStream fStream = new FileInputStream(file); byte[] buffer = new byte[2028]; for (int len = 0; (len = fStream.read(buffer)) > 0; ) { dos.write(buffer, 0, len); } - dos.writeBytes(endFlag); + writeString(dos, request, endFlag); } else { - dos.writeBytes(startFlag + boundary + endFlag); - dos.writeBytes("Content-Disposition: form-data; name=\"" + entry.getKey() + "\""); - dos.writeBytes(endFlag); - dos.writeBytes(endFlag); - dos.writeBytes(String.valueOf(entry.getValue())); - dos.writeBytes(endFlag); + writeString(dos, request, startFlag + boundary + endFlag); + writeString(dos, request, "Content-Disposition: form-data; name=\"" + entry.getKey() + "\""); + writeString(dos, request, endFlag + endFlag); + writeString(dos, request, String.valueOf(entry.getValue())); + writeString(dos, request, endFlag); } } - dos.writeBytes(startFlag + boundary + startFlag + endFlag); + writeString(dos, request, startFlag + boundary + startFlag + endFlag); dos.flush(); } + private void writeString(DataOutputStream dos, JbootHttpRequest request, String s) throws IOException { + dos.write(s.getBytes(request.getCharset())); + } + private static void checkFileNormal(File file) { if (!file.exists()) { throw new JbootException("file not exists!!!!" + file); -- Gitee From 9c755d8e88e1dd3946cc13f8dec1eabb39260426 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 19 Mar 2020 12:28:11 +0800 Subject: [PATCH 0076/1965] add rpc configs --- .../rpc/config/ApplicationConfig.java | 308 ++++++++++++ .../components/rpc/config/ArgumentConfig.java | 66 +++ .../components/rpc/config/ConsumerConfig.java | 119 +++++ .../components/rpc/config/MethodConfig.java | 225 +++++++++ .../components/rpc/config/ModuleConfig.java | 93 ++++ .../components/rpc/config/MonitorConfig.java | 138 ++++++ .../components/rpc/config/ProtocolConfig.java | 456 ++++++++++++++++++ .../components/rpc/config/ProviderConfig.java | 326 +++++++++++++ .../rpc/config/ReferenceConfig.java | 282 +++++++++++ .../components/rpc/config/RegistryConfig.java | 403 ++++++++++++++++ .../components/rpc/config/ServiceConfig.java | 299 ++++++++++++ 11 files changed, 2715 insertions(+) create mode 100644 src/main/java/io/jboot/components/rpc/config/ApplicationConfig.java create mode 100644 src/main/java/io/jboot/components/rpc/config/ArgumentConfig.java create mode 100644 src/main/java/io/jboot/components/rpc/config/ConsumerConfig.java create mode 100644 src/main/java/io/jboot/components/rpc/config/MethodConfig.java create mode 100644 src/main/java/io/jboot/components/rpc/config/ModuleConfig.java create mode 100644 src/main/java/io/jboot/components/rpc/config/MonitorConfig.java create mode 100644 src/main/java/io/jboot/components/rpc/config/ProtocolConfig.java create mode 100644 src/main/java/io/jboot/components/rpc/config/ProviderConfig.java create mode 100644 src/main/java/io/jboot/components/rpc/config/ReferenceConfig.java create mode 100644 src/main/java/io/jboot/components/rpc/config/RegistryConfig.java create mode 100644 src/main/java/io/jboot/components/rpc/config/ServiceConfig.java diff --git a/src/main/java/io/jboot/components/rpc/config/ApplicationConfig.java b/src/main/java/io/jboot/components/rpc/config/ApplicationConfig.java new file mode 100644 index 00000000..30189f7f --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/config/ApplicationConfig.java @@ -0,0 +1,308 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.rpc.config; + +import org.apache.dubbo.config.MonitorConfig; + +import java.io.Serializable; +import java.util.Map; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/19 + */ +public class ApplicationConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * Application name + */ + private String name; + + /** + * The application version + */ + private String version; + + /** + * Application owner + */ + private String owner; + + /** + * Application's organization (BU) + */ + private String organization; + + /** + * Architecture layer + */ + private String architecture; + + /** + * Environment, e.g. dev, test or production + */ + private String environment; + + /** + * Java compiler + */ + private String compiler; + + /** + * The type of the log access + */ + private String logger; + + /** + * Registry centers + */ + private String registryIds; + + /** + * Monitor center + */ + private MonitorConfig monitor; + + /** + * Is default or not + */ + private Boolean isDefault; + + /** + * Directory for saving thread dump + */ + private String dumpDirectory; + + /** + * Whether to enable qos or not + */ + private Boolean qosEnable; + + /** + * The qos host to listen + */ + private String qosHost; + + /** + * The qos port to listen + */ + private Integer qosPort; + + /** + * Should we accept foreign ip or not? + */ + private Boolean qosAcceptForeignIp; + + /** + * Customized parameters + */ + private Map parameters; + + /** + * Config the shutdown.wait + */ + private String shutwait; + + private String hostname; + + /** + * Metadata type, local or remote, if choose remote, you need to further specify metadata center. + */ + private String metadataType; + + private Boolean registerConsumer; + + private String repository; + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getOrganization() { + return organization; + } + + public void setOrganization(String organization) { + this.organization = organization; + } + + public String getArchitecture() { + return architecture; + } + + public void setArchitecture(String architecture) { + this.architecture = architecture; + } + + public String getEnvironment() { + return environment; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public String getCompiler() { + return compiler; + } + + public void setCompiler(String compiler) { + this.compiler = compiler; + } + + public String getLogger() { + return logger; + } + + public void setLogger(String logger) { + this.logger = logger; + } + + public String getRegistryIds() { + return registryIds; + } + + public void setRegistryIds(String registryIds) { + this.registryIds = registryIds; + } + + public MonitorConfig getMonitor() { + return monitor; + } + + public void setMonitor(MonitorConfig monitor) { + this.monitor = monitor; + } + + public Boolean getDefault() { + return isDefault; + } + + public void setDefault(Boolean aDefault) { + isDefault = aDefault; + } + + public String getDumpDirectory() { + return dumpDirectory; + } + + public void setDumpDirectory(String dumpDirectory) { + this.dumpDirectory = dumpDirectory; + } + + public Boolean getQosEnable() { + return qosEnable; + } + + public void setQosEnable(Boolean qosEnable) { + this.qosEnable = qosEnable; + } + + public String getQosHost() { + return qosHost; + } + + public void setQosHost(String qosHost) { + this.qosHost = qosHost; + } + + public Integer getQosPort() { + return qosPort; + } + + public void setQosPort(Integer qosPort) { + this.qosPort = qosPort; + } + + public Boolean getQosAcceptForeignIp() { + return qosAcceptForeignIp; + } + + public void setQosAcceptForeignIp(Boolean qosAcceptForeignIp) { + this.qosAcceptForeignIp = qosAcceptForeignIp; + } + + public Map getParameters() { + return parameters; + } + + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + public String getShutwait() { + return shutwait; + } + + public void setShutwait(String shutwait) { + this.shutwait = shutwait; + } + + public String getHostname() { + return hostname; + } + + public void setHostname(String hostname) { + this.hostname = hostname; + } + + public String getMetadataType() { + return metadataType; + } + + public void setMetadataType(String metadataType) { + this.metadataType = metadataType; + } + + public Boolean getRegisterConsumer() { + return registerConsumer; + } + + public void setRegisterConsumer(Boolean registerConsumer) { + this.registerConsumer = registerConsumer; + } + + public String getRepository() { + return repository; + } + + public void setRepository(String repository) { + this.repository = repository; + } +} diff --git a/src/main/java/io/jboot/components/rpc/config/ArgumentConfig.java b/src/main/java/io/jboot/components/rpc/config/ArgumentConfig.java new file mode 100644 index 00000000..54216128 --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/config/ArgumentConfig.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.rpc.config; + +import java.io.Serializable; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/19 + */ +public class ArgumentConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * The argument index: index -1 represents not set + */ + private Integer index = -1; + + /** + * Argument type + */ + private String type; + + /** + * Whether the argument is the callback interface + */ + private Boolean callback; + + public Integer getIndex() { + return index; + } + + public void setIndex(Integer index) { + this.index = index; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Boolean getCallback() { + return callback; + } + + public void setCallback(Boolean callback) { + this.callback = callback; + } +} diff --git a/src/main/java/io/jboot/components/rpc/config/ConsumerConfig.java b/src/main/java/io/jboot/components/rpc/config/ConsumerConfig.java new file mode 100644 index 00000000..c4f5906f --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/config/ConsumerConfig.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.rpc.config; + +import java.io.Serializable; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/19 + */ +public class ConsumerConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * Whether to use the default protocol + */ + private Boolean isDefault; + + /** + * Networking framework client uses: netty, mina, etc. + */ + private String client; + + /** + * Consumer thread pool type: cached, fixed, limit, eager + */ + private String threadpool; + + /** + * Consumer threadpool core thread size + */ + private Integer corethreads; + + /** + * Consumer threadpool thread size + */ + private Integer threads; + + /** + * Consumer threadpool queue size + */ + private Integer queues; + + /** + * By default, a TCP long-connection communication is shared between the consumer process and the provider process. + * This property can be set to share multiple TCP long-connection communications. Note that only the dubbo protocol takes effect. + */ + private Integer shareconnections; + + public Boolean getDefault() { + return isDefault; + } + + public void setDefault(Boolean aDefault) { + isDefault = aDefault; + } + + public String getClient() { + return client; + } + + public void setClient(String client) { + this.client = client; + } + + public String getThreadpool() { + return threadpool; + } + + public void setThreadpool(String threadpool) { + this.threadpool = threadpool; + } + + public Integer getCorethreads() { + return corethreads; + } + + public void setCorethreads(Integer corethreads) { + this.corethreads = corethreads; + } + + public Integer getThreads() { + return threads; + } + + public void setThreads(Integer threads) { + this.threads = threads; + } + + public Integer getQueues() { + return queues; + } + + public void setQueues(Integer queues) { + this.queues = queues; + } + + public Integer getShareconnections() { + return shareconnections; + } + + public void setShareconnections(Integer shareconnections) { + this.shareconnections = shareconnections; + } +} diff --git a/src/main/java/io/jboot/components/rpc/config/MethodConfig.java b/src/main/java/io/jboot/components/rpc/config/MethodConfig.java new file mode 100644 index 00000000..4741203a --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/config/MethodConfig.java @@ -0,0 +1,225 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.rpc.config; + +import org.apache.dubbo.config.ArgumentConfig; + +import java.io.Serializable; +import java.util.List; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/19 + */ +public class MethodConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * The method name + */ + private String name; + + /** + * Stat + */ + private Integer stat; + + /** + * Whether to retry + */ + private Boolean retry; + + /** + * If it's reliable + */ + private Boolean reliable; + + /** + * Thread limits for method invocations + */ + private Integer executes; + + /** + * If it's deprecated + */ + private Boolean deprecated; + + /** + * Whether to enable sticky + */ + private Boolean sticky; + + /** + * Whether need to return + */ + private Boolean isReturn; + + /** + * Callback instance when async-call is invoked + */ + private Object oninvoke; + + /** + * Callback method when async-call is invoked + */ + private String oninvokeMethod; + + /** + * Callback instance when async-call is returned + */ + private Object onreturn; + + /** + * Callback method when async-call is returned + */ + private String onreturnMethod; + + /** + * Callback instance when async-call has exception thrown + */ + private Object onthrow; + + /** + * Callback method when async-call has exception thrown + */ + private String onthrowMethod; + + /** + * The method arguments + */ + private List arguments; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getStat() { + return stat; + } + + public void setStat(Integer stat) { + this.stat = stat; + } + + public Boolean getRetry() { + return retry; + } + + public void setRetry(Boolean retry) { + this.retry = retry; + } + + public Boolean getReliable() { + return reliable; + } + + public void setReliable(Boolean reliable) { + this.reliable = reliable; + } + + public Integer getExecutes() { + return executes; + } + + public void setExecutes(Integer executes) { + this.executes = executes; + } + + public Boolean getDeprecated() { + return deprecated; + } + + public void setDeprecated(Boolean deprecated) { + this.deprecated = deprecated; + } + + public Boolean getSticky() { + return sticky; + } + + public void setSticky(Boolean sticky) { + this.sticky = sticky; + } + + public Boolean getReturn() { + return isReturn; + } + + public void setReturn(Boolean aReturn) { + isReturn = aReturn; + } + + public Object getOninvoke() { + return oninvoke; + } + + public void setOninvoke(Object oninvoke) { + this.oninvoke = oninvoke; + } + + public String getOninvokeMethod() { + return oninvokeMethod; + } + + public void setOninvokeMethod(String oninvokeMethod) { + this.oninvokeMethod = oninvokeMethod; + } + + public Object getOnreturn() { + return onreturn; + } + + public void setOnreturn(Object onreturn) { + this.onreturn = onreturn; + } + + public String getOnreturnMethod() { + return onreturnMethod; + } + + public void setOnreturnMethod(String onreturnMethod) { + this.onreturnMethod = onreturnMethod; + } + + public Object getOnthrow() { + return onthrow; + } + + public void setOnthrow(Object onthrow) { + this.onthrow = onthrow; + } + + public String getOnthrowMethod() { + return onthrowMethod; + } + + public void setOnthrowMethod(String onthrowMethod) { + this.onthrowMethod = onthrowMethod; + } + + public List getArguments() { + return arguments; + } + + public void setArguments(List arguments) { + this.arguments = arguments; + } +} diff --git a/src/main/java/io/jboot/components/rpc/config/ModuleConfig.java b/src/main/java/io/jboot/components/rpc/config/ModuleConfig.java new file mode 100644 index 00000000..5b3471c9 --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/config/ModuleConfig.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.rpc.config; + +import java.io.Serializable; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/19 + */ +public class ModuleConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * Module name + */ + private String name; + + /** + * Module version + */ + private String version; + + /** + * Module owner + */ + private String owner; + + /** + * Module's organization + */ + private String organization; + + /** + * If it's default + */ + private Boolean isDefault; + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getOrganization() { + return organization; + } + + public void setOrganization(String organization) { + this.organization = organization; + } + + public Boolean getDefault() { + return isDefault; + } + + public void setDefault(Boolean aDefault) { + isDefault = aDefault; + } +} diff --git a/src/main/java/io/jboot/components/rpc/config/MonitorConfig.java b/src/main/java/io/jboot/components/rpc/config/MonitorConfig.java new file mode 100644 index 00000000..f9bbd18a --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/config/MonitorConfig.java @@ -0,0 +1,138 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.rpc.config; + +import java.io.Serializable; +import java.util.Map; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/19 + */ +public class MonitorConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * The protocol of the monitor, if the value is registry, it will search the monitor address from the registry center, + * otherwise, it will directly connect to the monitor center + */ + private String protocol; + + /** + * The monitor address + */ + private String address; + + /** + * The monitor user name + */ + private String username; + + /** + * The password + */ + private String password; + + private String group; + + private String version; + + private String interval; + + /** + * customized parameters + */ + private Map parameters; + + /** + * If it's default + */ + private Boolean isDefault; + + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getInterval() { + return interval; + } + + public void setInterval(String interval) { + this.interval = interval; + } + + public Map getParameters() { + return parameters; + } + + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + public Boolean getDefault() { + return isDefault; + } + + public void setDefault(Boolean aDefault) { + isDefault = aDefault; + } +} diff --git a/src/main/java/io/jboot/components/rpc/config/ProtocolConfig.java b/src/main/java/io/jboot/components/rpc/config/ProtocolConfig.java new file mode 100644 index 00000000..0f76ba00 --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/config/ProtocolConfig.java @@ -0,0 +1,456 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.rpc.config; + +import java.io.Serializable; +import java.util.Map; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/19 + * http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-protocol.html + */ +public class ProtocolConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + + /** + * Protocol name + */ + private String name; + + /** + * Service ip address (when there are multiple network cards available) + */ + private String host; + + /** + * Service port + */ + private Integer port; + + /** + * Context path + */ + private String contextpath; + + /** + * Thread pool + */ + private String threadpool; + + /** + * Thread pool core thread size + */ + private Integer corethreads; + + /** + * Thread pool size (fixed size) + */ + private Integer threads; + + /** + * IO thread pool size (fixed size) + */ + private Integer iothreads; + + /** + * Thread pool's queue length + */ + private Integer queues; + + /** + * Max acceptable connections + */ + private Integer accepts; + + /** + * Protocol codec + */ + private String codec; + + /** + * Serialization + */ + private String serialization; + + /** + * Charset + */ + private String charset; + + /** + * Payload max length + */ + private Integer payload; + + /** + * Buffer size + */ + private Integer buffer; + + /** + * Heartbeat interval + */ + private Integer heartbeat; + + /** + * Access log + */ + private String accesslog; + + /** + * Transfort + */ + private String transporter; + + /** + * How information is exchanged + */ + private String exchanger; + + /** + * Thread dispatch mode + */ + private String dispatcher; + + /** + * Networker + */ + private String networker; + + /** + * Sever impl + */ + private String server; + + /** + * Client impl + */ + private String client; + + /** + * Supported telnet commands, separated with comma. + */ + private String telnet; + + /** + * Command line prompt + */ + private String prompt; + + /** + * Status check + */ + private String status; + + /** + * Whether to register + */ + private Boolean register; + + /** + * whether it is a persistent connection + */ + //TODO add this to provider config + private Boolean keepAlive; + + // TODO add this to provider config + private String optimizer; + + /** + * The extension + */ + private String extension; + + /** + * The customized parameters + */ + private Map parameters; + + /** + * If it's default + */ + private Boolean isDefault; + + private Boolean sslEnabled; + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getContextpath() { + return contextpath; + } + + public void setContextpath(String contextpath) { + this.contextpath = contextpath; + } + + public String getThreadpool() { + return threadpool; + } + + public void setThreadpool(String threadpool) { + this.threadpool = threadpool; + } + + public Integer getCorethreads() { + return corethreads; + } + + public void setCorethreads(Integer corethreads) { + this.corethreads = corethreads; + } + + public Integer getThreads() { + return threads; + } + + public void setThreads(Integer threads) { + this.threads = threads; + } + + public Integer getIothreads() { + return iothreads; + } + + public void setIothreads(Integer iothreads) { + this.iothreads = iothreads; + } + + public Integer getQueues() { + return queues; + } + + public void setQueues(Integer queues) { + this.queues = queues; + } + + public Integer getAccepts() { + return accepts; + } + + public void setAccepts(Integer accepts) { + this.accepts = accepts; + } + + public String getCodec() { + return codec; + } + + public void setCodec(String codec) { + this.codec = codec; + } + + public String getSerialization() { + return serialization; + } + + public void setSerialization(String serialization) { + this.serialization = serialization; + } + + public String getCharset() { + return charset; + } + + public void setCharset(String charset) { + this.charset = charset; + } + + public Integer getPayload() { + return payload; + } + + public void setPayload(Integer payload) { + this.payload = payload; + } + + public Integer getBuffer() { + return buffer; + } + + public void setBuffer(Integer buffer) { + this.buffer = buffer; + } + + public Integer getHeartbeat() { + return heartbeat; + } + + public void setHeartbeat(Integer heartbeat) { + this.heartbeat = heartbeat; + } + + public String getAccesslog() { + return accesslog; + } + + public void setAccesslog(String accesslog) { + this.accesslog = accesslog; + } + + public String getTransporter() { + return transporter; + } + + public void setTransporter(String transporter) { + this.transporter = transporter; + } + + public String getExchanger() { + return exchanger; + } + + public void setExchanger(String exchanger) { + this.exchanger = exchanger; + } + + public String getDispatcher() { + return dispatcher; + } + + public void setDispatcher(String dispatcher) { + this.dispatcher = dispatcher; + } + + public String getNetworker() { + return networker; + } + + public void setNetworker(String networker) { + this.networker = networker; + } + + public String getServer() { + return server; + } + + public void setServer(String server) { + this.server = server; + } + + public String getClient() { + return client; + } + + public void setClient(String client) { + this.client = client; + } + + public String getTelnet() { + return telnet; + } + + public void setTelnet(String telnet) { + this.telnet = telnet; + } + + public String getPrompt() { + return prompt; + } + + public void setPrompt(String prompt) { + this.prompt = prompt; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Boolean getRegister() { + return register; + } + + public void setRegister(Boolean register) { + this.register = register; + } + + public Boolean getKeepAlive() { + return keepAlive; + } + + public void setKeepAlive(Boolean keepAlive) { + this.keepAlive = keepAlive; + } + + public String getOptimizer() { + return optimizer; + } + + public void setOptimizer(String optimizer) { + this.optimizer = optimizer; + } + + public String getExtension() { + return extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + + public Map getParameters() { + return parameters; + } + + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + public Boolean getDefault() { + return isDefault; + } + + public void setDefault(Boolean aDefault) { + isDefault = aDefault; + } + + public Boolean getSslEnabled() { + return sslEnabled; + } + + public void setSslEnabled(Boolean sslEnabled) { + this.sslEnabled = sslEnabled; + } +} diff --git a/src/main/java/io/jboot/components/rpc/config/ProviderConfig.java b/src/main/java/io/jboot/components/rpc/config/ProviderConfig.java new file mode 100644 index 00000000..335caa91 --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/config/ProviderConfig.java @@ -0,0 +1,326 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.rpc.config; + +import java.io.Serializable; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/19 + */ +public class ProviderConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * Service ip addresses (used when there are multiple network cards available) + */ + private String host; + + /** + * Service port + */ + private Integer port; + + /** + * Context path + */ + private String contextpath; + + /** + * Thread pool + */ + private String threadpool; + + /** + * Thread pool size (fixed size) + */ + private Integer threads; + + /** + * IO thread pool size (fixed size) + */ + private Integer iothreads; + + /** + * Thread pool queue length + */ + private Integer queues; + + /** + * Max acceptable connections + */ + private Integer accepts; + + /** + * Protocol codec + */ + private String codec; + + /** + * The serialization charset + */ + private String charset; + + /** + * Payload max length + */ + private Integer payload; + + /** + * The network io buffer size + */ + private Integer buffer; + + /** + * Transporter + */ + private String transporter; + + /** + * How information gets exchanged + */ + private String exchanger; + + /** + * Thread dispatching mode + */ + private String dispatcher; + + /** + * Networker + */ + private String networker; + + /** + * The server-side implementation model of the protocol + */ + private String server; + + /** + * The client-side implementation model of the protocol + */ + private String client; + + /** + * Supported telnet commands, separated with comma. + */ + private String telnet; + + /** + * Command line prompt + */ + private String prompt; + + /** + * Status check + */ + private String status; + + /** + * Wait time when stop + */ + private Integer wait; + + /** + * Whether to use the default protocol + */ + private Boolean isDefault; + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getContextpath() { + return contextpath; + } + + public void setContextpath(String contextpath) { + this.contextpath = contextpath; + } + + public String getThreadpool() { + return threadpool; + } + + public void setThreadpool(String threadpool) { + this.threadpool = threadpool; + } + + public Integer getThreads() { + return threads; + } + + public void setThreads(Integer threads) { + this.threads = threads; + } + + public Integer getIothreads() { + return iothreads; + } + + public void setIothreads(Integer iothreads) { + this.iothreads = iothreads; + } + + public Integer getQueues() { + return queues; + } + + public void setQueues(Integer queues) { + this.queues = queues; + } + + public Integer getAccepts() { + return accepts; + } + + public void setAccepts(Integer accepts) { + this.accepts = accepts; + } + + public String getCodec() { + return codec; + } + + public void setCodec(String codec) { + this.codec = codec; + } + + public String getCharset() { + return charset; + } + + public void setCharset(String charset) { + this.charset = charset; + } + + public Integer getPayload() { + return payload; + } + + public void setPayload(Integer payload) { + this.payload = payload; + } + + public Integer getBuffer() { + return buffer; + } + + public void setBuffer(Integer buffer) { + this.buffer = buffer; + } + + public String getTransporter() { + return transporter; + } + + public void setTransporter(String transporter) { + this.transporter = transporter; + } + + public String getExchanger() { + return exchanger; + } + + public void setExchanger(String exchanger) { + this.exchanger = exchanger; + } + + public String getDispatcher() { + return dispatcher; + } + + public void setDispatcher(String dispatcher) { + this.dispatcher = dispatcher; + } + + public String getNetworker() { + return networker; + } + + public void setNetworker(String networker) { + this.networker = networker; + } + + public String getServer() { + return server; + } + + public void setServer(String server) { + this.server = server; + } + + public String getClient() { + return client; + } + + public void setClient(String client) { + this.client = client; + } + + public String getTelnet() { + return telnet; + } + + public void setTelnet(String telnet) { + this.telnet = telnet; + } + + public String getPrompt() { + return prompt; + } + + public void setPrompt(String prompt) { + this.prompt = prompt; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Integer getWait() { + return wait; + } + + public void setWait(Integer wait) { + this.wait = wait; + } + + public Boolean getDefault() { + return isDefault; + } + + public void setDefault(Boolean aDefault) { + isDefault = aDefault; + } +} diff --git a/src/main/java/io/jboot/components/rpc/config/ReferenceConfig.java b/src/main/java/io/jboot/components/rpc/config/ReferenceConfig.java new file mode 100644 index 00000000..90512c13 --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/config/ReferenceConfig.java @@ -0,0 +1,282 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.rpc.config; + +import java.io.Serializable; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/17 + * + * http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-reference.html + */ +public class ReferenceConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + private String id; + private String name; + private String interfaceClass; + private String version; + private String group; + private Long timeout; + private Integer retries; + private Integer connections; + private String loadbalance; + private Boolean async; + private Boolean generic; + private Boolean check; + private String url; + private String stub; + private String mock; + private String cache; + private Boolean validation; + private Boolean proxy; + private String client; + private String registry; + private String owner; + private Integer actives; + private String cluster; + private String filter; + private String listener; + private String layer; + private Boolean init; + private String protocol; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getInterfaceClass() { + return interfaceClass; + } + + public void setInterfaceClass(String interfaceClass) { + this.interfaceClass = interfaceClass; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public Long getTimeout() { + return timeout; + } + + public void setTimeout(Long timeout) { + this.timeout = timeout; + } + + public Integer getRetries() { + return retries; + } + + public void setRetries(Integer retries) { + this.retries = retries; + } + + public Integer getConnections() { + return connections; + } + + public void setConnections(Integer connections) { + this.connections = connections; + } + + public String getLoadbalance() { + return loadbalance; + } + + public void setLoadbalance(String loadbalance) { + this.loadbalance = loadbalance; + } + + public Boolean getAsync() { + return async; + } + + public void setAsync(Boolean async) { + this.async = async; + } + + public Boolean getGeneric() { + return generic; + } + + public void setGeneric(Boolean generic) { + this.generic = generic; + } + + public Boolean getCheck() { + return check; + } + + public void setCheck(Boolean check) { + this.check = check; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getStub() { + return stub; + } + + public void setStub(String stub) { + this.stub = stub; + } + + public String getMock() { + return mock; + } + + public void setMock(String mock) { + this.mock = mock; + } + + public String getCache() { + return cache; + } + + public void setCache(String cache) { + this.cache = cache; + } + + public Boolean getValidation() { + return validation; + } + + public void setValidation(Boolean validation) { + this.validation = validation; + } + + public Boolean getProxy() { + return proxy; + } + + public void setProxy(Boolean proxy) { + this.proxy = proxy; + } + + public String getClient() { + return client; + } + + public void setClient(String client) { + this.client = client; + } + + public String getRegistry() { + return registry; + } + + public void setRegistry(String registry) { + this.registry = registry; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public Integer getActives() { + return actives; + } + + public void setActives(Integer actives) { + this.actives = actives; + } + + public String getCluster() { + return cluster; + } + + public void setCluster(String cluster) { + this.cluster = cluster; + } + + public String getFilter() { + return filter; + } + + public void setFilter(String filter) { + this.filter = filter; + } + + public String getListener() { + return listener; + } + + public void setListener(String listener) { + this.listener = listener; + } + + public String getLayer() { + return layer; + } + + public void setLayer(String layer) { + this.layer = layer; + } + + public Boolean getInit() { + return init; + } + + public void setInit(Boolean init) { + this.init = init; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } +} diff --git a/src/main/java/io/jboot/components/rpc/config/RegistryConfig.java b/src/main/java/io/jboot/components/rpc/config/RegistryConfig.java new file mode 100644 index 00000000..e8df30cb --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/config/RegistryConfig.java @@ -0,0 +1,403 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.rpc.config; + +import java.io.Serializable; +import java.util.Map; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/17 + */ +public class RegistryConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * Register center address + */ + private String address; + + /** + * Username to login register center + */ + private String username; + + /** + * Password to login register center + */ + private String password; + + /** + * Default port for register center + */ + private Integer port; + + /** + * Protocol for register center + */ + private String protocol; + + /** + * Network transmission type + */ + private String transporter; + + private String server; + + private String client; + + /** + * Affects how traffic distributes among registries, useful when subscribing multiple registries, available options: + * 1. zone-aware, a certain type of traffic always goes to one Registry according to where the traffic is originated. + */ + private String cluster; + + /** + * The region where the registry belongs, usually used to isolate traffics + */ + private String zone; + + /** + * The group the services registry in + */ + private String group; + + private String version; + + /** + * Request timeout in milliseconds for register center + */ + private Integer timeout; + + /** + * Session timeout in milliseconds for register center + */ + private Integer session; + + /** + * File for saving register center dynamic list + */ + private String file; + + /** + * Wait time before stop + */ + private Integer wait; + + /** + * Whether to check if register center is available when boot up + */ + private Boolean check; + + /** + * Whether to allow dynamic service to register on the register center + */ + private Boolean dynamic; + + /** + * Whether to export service on the register center + */ + private Boolean register; + + /** + * Whether allow to subscribe service on the register center + */ + private Boolean subscribe; + + /** + * The customized parameters + */ + private Map parameters; + + /** + * Whether it's default + */ + private Boolean isDefault; + + /** + * Simple the registry. both useful for provider and consumer + * + * @since 2.7.0 + */ + private Boolean simplified; + /** + * After simplify the registry, should add some paramter individually. just for provider. + *

+ * such as: extra-keys = A,b,c,d + * + * @since 2.7.0 + */ + private String extraKeys; + + /** + * the address work as config center or not + */ + private Boolean useAsConfigCenter; + + /** + * the address work as remote metadata center or not + */ + private Boolean useAsMetadataCenter; + + /** + * list of rpc protocols accepted by this registry, for example, "dubbo,rest" + */ + private String accepts; + + /** + * Always use this registry first if set to true, useful when subscribe to multiple registries + */ + private Boolean preferred; + + /** + * Affects traffic distribution among registries, useful when subscribe to multiple registries + * Take effect only when no preferred registry is specified. + */ + private Integer weight; + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getTransporter() { + return transporter; + } + + public void setTransporter(String transporter) { + this.transporter = transporter; + } + + public String getServer() { + return server; + } + + public void setServer(String server) { + this.server = server; + } + + public String getClient() { + return client; + } + + public void setClient(String client) { + this.client = client; + } + + public String getCluster() { + return cluster; + } + + public void setCluster(String cluster) { + this.cluster = cluster; + } + + public String getZone() { + return zone; + } + + public void setZone(String zone) { + this.zone = zone; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public Integer getTimeout() { + return timeout; + } + + public void setTimeout(Integer timeout) { + this.timeout = timeout; + } + + public Integer getSession() { + return session; + } + + public void setSession(Integer session) { + this.session = session; + } + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } + + public Integer getWait() { + return wait; + } + + public void setWait(Integer wait) { + this.wait = wait; + } + + public Boolean getCheck() { + return check; + } + + public void setCheck(Boolean check) { + this.check = check; + } + + public Boolean getDynamic() { + return dynamic; + } + + public void setDynamic(Boolean dynamic) { + this.dynamic = dynamic; + } + + public Boolean getRegister() { + return register; + } + + public void setRegister(Boolean register) { + this.register = register; + } + + public Boolean getSubscribe() { + return subscribe; + } + + public void setSubscribe(Boolean subscribe) { + this.subscribe = subscribe; + } + + public Map getParameters() { + return parameters; + } + + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + public Boolean getDefault() { + return isDefault; + } + + public void setDefault(Boolean aDefault) { + isDefault = aDefault; + } + + public Boolean getSimplified() { + return simplified; + } + + public void setSimplified(Boolean simplified) { + this.simplified = simplified; + } + + public String getExtraKeys() { + return extraKeys; + } + + public void setExtraKeys(String extraKeys) { + this.extraKeys = extraKeys; + } + + public Boolean getUseAsConfigCenter() { + return useAsConfigCenter; + } + + public void setUseAsConfigCenter(Boolean useAsConfigCenter) { + this.useAsConfigCenter = useAsConfigCenter; + } + + public Boolean getUseAsMetadataCenter() { + return useAsMetadataCenter; + } + + public void setUseAsMetadataCenter(Boolean useAsMetadataCenter) { + this.useAsMetadataCenter = useAsMetadataCenter; + } + + public String getAccepts() { + return accepts; + } + + public void setAccepts(String accepts) { + this.accepts = accepts; + } + + public Boolean getPreferred() { + return preferred; + } + + public void setPreferred(Boolean preferred) { + this.preferred = preferred; + } + + public Integer getWeight() { + return weight; + } + + public void setWeight(Integer weight) { + this.weight = weight; + } +} diff --git a/src/main/java/io/jboot/components/rpc/config/ServiceConfig.java b/src/main/java/io/jboot/components/rpc/config/ServiceConfig.java new file mode 100644 index 00000000..c4ed0527 --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/config/ServiceConfig.java @@ -0,0 +1,299 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.rpc.config; + +import java.io.Serializable; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/17 + * 文档:http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-service.html + */ +public class ServiceConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + private String interfaceName; + private String className; + private String version; + private String group; + private String path; + private Integer delay; + private Integer timeout; + private Integer retries; + private Integer connections; + private String loadbalance; + private Boolean async; + private String stub; + private String mock; + private String token; + private String registry; + private String provider; + private String deprecated; + private String dynamic; + private String accesslog; + private String owner; + private String document; + private Integer weight; + private Integer executes; + private String proxy; + private String cluster; + private String filter; + private String listener; + private String protocol; + private String layer; + private Boolean register; + + public String getInterfaceName() { + return interfaceName; + } + + public void setInterfaceName(String interfaceName) { + this.interfaceName = interfaceName; + } + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public Integer getDelay() { + return delay; + } + + public void setDelay(Integer delay) { + this.delay = delay; + } + + public Integer getTimeout() { + return timeout; + } + + public void setTimeout(Integer timeout) { + this.timeout = timeout; + } + + public Integer getRetries() { + return retries; + } + + public void setRetries(Integer retries) { + this.retries = retries; + } + + public Integer getConnections() { + return connections; + } + + public void setConnections(Integer connections) { + this.connections = connections; + } + + public String getLoadbalance() { + return loadbalance; + } + + public void setLoadbalance(String loadbalance) { + this.loadbalance = loadbalance; + } + + public Boolean getAsync() { + return async; + } + + public void setAsync(Boolean async) { + this.async = async; + } + + public String getStub() { + return stub; + } + + public void setStub(String stub) { + this.stub = stub; + } + + public String getMock() { + return mock; + } + + public void setMock(String mock) { + this.mock = mock; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getRegistry() { + return registry; + } + + public void setRegistry(String registry) { + this.registry = registry; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + public String getDeprecated() { + return deprecated; + } + + public void setDeprecated(String deprecated) { + this.deprecated = deprecated; + } + + public String getDynamic() { + return dynamic; + } + + public void setDynamic(String dynamic) { + this.dynamic = dynamic; + } + + public String getAccesslog() { + return accesslog; + } + + public void setAccesslog(String accesslog) { + this.accesslog = accesslog; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getDocument() { + return document; + } + + public void setDocument(String document) { + this.document = document; + } + + public Integer getWeight() { + return weight; + } + + public void setWeight(Integer weight) { + this.weight = weight; + } + + public Integer getExecutes() { + return executes; + } + + public void setExecutes(Integer executes) { + this.executes = executes; + } + + public String getProxy() { + return proxy; + } + + public void setProxy(String proxy) { + this.proxy = proxy; + } + + public String getCluster() { + return cluster; + } + + public void setCluster(String cluster) { + this.cluster = cluster; + } + + public String getFilter() { + return filter; + } + + public void setFilter(String filter) { + this.filter = filter; + } + + public String getListener() { + return listener; + } + + public void setListener(String listener) { + this.listener = listener; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getLayer() { + return layer; + } + + public void setLayer(String layer) { + this.layer = layer; + } + + public Boolean getRegister() { + return register; + } + + public void setRegister(Boolean register) { + this.register = register; + } +} -- Gitee From f9722ec21e1c4538355ba71ff4c5e485e1f05bce Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 19 Mar 2020 15:15:50 +0800 Subject: [PATCH 0077/1965] add JbootConfigUtil --- .../io/jboot/app/config/JbootConfigUtil.java | 65 +++++++++++++++++++ .../datasource/DataSourceConfigManager.java | 41 ++++-------- .../jboot/test/config/ConfigController.java | 32 +++++++++ .../io/jboot/test/config/TestConfigModel.java | 42 ++++++++++++ src/test/resources/jboot.properties | 12 +++- 5 files changed, 161 insertions(+), 31 deletions(-) create mode 100644 src/main/java/io/jboot/app/config/JbootConfigUtil.java create mode 100644 src/test/java/io/jboot/test/config/ConfigController.java create mode 100644 src/test/java/io/jboot/test/config/TestConfigModel.java diff --git a/src/main/java/io/jboot/app/config/JbootConfigUtil.java b/src/main/java/io/jboot/app/config/JbootConfigUtil.java new file mode 100644 index 00000000..1abd7566 --- /dev/null +++ b/src/main/java/io/jboot/app/config/JbootConfigUtil.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.app.config; + +import io.jboot.Jboot; +import io.jboot.utils.StrUtil; +import org.apache.commons.lang3.StringUtils; + +import java.util.*; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/19 + */ +public class JbootConfigUtil { + + public static Map getConfigModels(Class tClass, String prefix) { + Map objMap = new HashMap<>(); + + T defaultObj = Jboot.config(tClass, prefix); + objMap.put("default", defaultObj); + + + Set objNames = new HashSet<>(); + + String objPrefix = prefix + "."; + int pointCount = StringUtils.countMatches(prefix, '.'); + + + Properties prop = JbootConfigManager.me().getProperties(); + + for (Map.Entry entry : prop.entrySet()) { + if (entry.getKey() == null || StrUtil.isBlank(entry.getKey().toString())) { + continue; + } + String key = entry.getKey().toString().toLowerCase().replace('_', '.'); + if (key.startsWith(objPrefix) && entry.getValue() != null) { + String[] keySplits = key.split("\\."); + if (keySplits.length == pointCount + 3) { + objNames.add(keySplits[pointCount + 1]); + } + } + } + + for (String name : objNames) { + T obj = Jboot.config(tClass, objPrefix + name); + objMap.put(name, obj); + } + + return objMap; + } +} diff --git a/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java b/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java index bd147c6b..705b48a6 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java @@ -16,11 +16,10 @@ package io.jboot.db.datasource; import com.google.common.collect.Maps; -import io.jboot.Jboot; -import io.jboot.app.config.JbootConfigManager; +import io.jboot.app.config.JbootConfigUtil; import io.jboot.utils.StrUtil; -import java.util.*; +import java.util.Map; public class DataSourceConfigManager { @@ -37,37 +36,19 @@ public class DataSourceConfigManager { private DataSourceConfigManager() { - DataSourceConfig datasourceConfig = Jboot.config(DataSourceConfig.class, "jboot.datasource"); + Map configMap = JbootConfigUtil.getConfigModels(DataSourceConfig.class,"jboot.datasource"); - //若未配置数据源的名称,设置为默认 - if (StrUtil.isBlank(datasourceConfig.getName())) { - datasourceConfig.setName(DataSourceConfig.NAME_DEFAULT); - } - - addConfig(datasourceConfig); + for (Map.Entry entry : configMap.entrySet()){ + DataSourceConfig config = entry.getValue(); - Properties prop = JbootConfigManager.me().getProperties(); - Set datasourceNames = new HashSet<>(); - for (Map.Entry entry : prop.entrySet()) { - if (entry.getKey() == null) { - continue; - } - String key = entry.getKey().toString().toLowerCase().replace('_','.'); - if (key.startsWith(DATASOURCE_PREFIX) && entry.getValue() != null) { - String[] keySplits = key.split("\\."); - if (keySplits.length == 4) { - datasourceNames.add(keySplits[2]); - } + //默认数据源 + if (entry.getKey().equals("default") && StrUtil.isBlank(config.getName())){ + config.setName(DataSourceConfig.NAME_DEFAULT); + }else if (StrUtil.isBlank(config.getName())){ + config.setName(entry.getKey()); } - } - - for (String name : datasourceNames) { - DataSourceConfig dsc = Jboot.config(DataSourceConfig.class, DATASOURCE_PREFIX + name); - if (StrUtil.isBlank(dsc.getName())) { - dsc.setName(name); - } - addConfig(dsc); + addConfig(config); } } diff --git a/src/test/java/io/jboot/test/config/ConfigController.java b/src/test/java/io/jboot/test/config/ConfigController.java new file mode 100644 index 00000000..372fa7b9 --- /dev/null +++ b/src/test/java/io/jboot/test/config/ConfigController.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.test.config; + +import io.jboot.app.config.JbootConfigUtil; +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/19 + */ +@RequestMapping("config") +public class ConfigController extends JbootController { + + public void index(){ + renderJson(JbootConfigUtil.getConfigModels(TestConfigModel.class,"config.test.test")); + } +} diff --git a/src/test/java/io/jboot/test/config/TestConfigModel.java b/src/test/java/io/jboot/test/config/TestConfigModel.java new file mode 100644 index 00000000..64f0514c --- /dev/null +++ b/src/test/java/io/jboot/test/config/TestConfigModel.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.test.config; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/19 + */ +public class TestConfigModel { + + private String name; + private String type; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index 4e21b773..9f6744a3 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -23,4 +23,14 @@ jboot.config.nacos.group = jboot jboot.config.apollo.enable = false jboot.config.apollo.appId = SampleApp -jboot.config.apollo.meta = http://106.54.227.205:8080 \ No newline at end of file +jboot.config.apollo.meta = http://106.54.227.205:8080 + + +config.test.test.name = name1 +config.test.test.type = type1 + +config.test.test.bbb.name = name2 +config.test.test.bbb.type = type2 + +config.test.test.ccc.name = name3 +config.test.test.ccc.type = type3 \ No newline at end of file -- Gitee From d549a174818de2be050bdb6b95653c1cfe670c24 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 19 Mar 2020 15:34:51 +0800 Subject: [PATCH 0078/1965] rename Jbootrpc.onInitBefore() to Jbootrpc.onInit() --- src/main/java/io/jboot/components/rpc/Jbootrpc.java | 2 +- src/main/java/io/jboot/components/rpc/JbootrpcBase.java | 2 +- src/main/java/io/jboot/components/rpc/JbootrpcManager.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/Jbootrpc.java b/src/main/java/io/jboot/components/rpc/Jbootrpc.java index 50ca8815..c88f0692 100644 --- a/src/main/java/io/jboot/components/rpc/Jbootrpc.java +++ b/src/main/java/io/jboot/components/rpc/Jbootrpc.java @@ -22,7 +22,7 @@ public interface Jbootrpc { public boolean serviceExport(Class interfaceClass, Object object, JbootrpcServiceConfig serviceConfig); - public void onInitBefore(); + public void onInit(); public void onInited(); } diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java index 614ab3cc..60d1583b 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java @@ -27,7 +27,7 @@ public abstract class JbootrpcBase implements Jbootrpc { } @Override - public void onInitBefore() { + public void onInit() { } diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java index 75786959..dc450c55 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java @@ -69,7 +69,7 @@ public class JbootrpcManager { } Jbootrpc jbootrpc = getJbootrpc(); - jbootrpc.onInitBefore(); + jbootrpc.onInit(); if (defaultConfig.isAutoExportEnable()) { exportRPCBean(jbootrpc); -- Gitee From 5c8dc9f0a1050961f234f4b17467fdd1f5896349 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 20 Mar 2020 10:35:35 +0800 Subject: [PATCH 0079/1965] add Map/List/Set config in jboot.properties --- .../java/io/jboot/aop/JbootAopFactory.java | 7 +- .../jboot/app/config/JbootConfigManager.java | 11 ++- src/main/java/io/jboot/app/config/Utils.java | 95 ++++++++++++++++--- .../rpc/config/ReferenceConfig.java | 9 ++ .../jboot/components/rpc/dubbo/DubboUtil.java | 76 +++++++++++++++ .../components/rpc/dubbo/JbootDubborpc.java | 29 ++++++ src/main/java/io/jboot/utils/ClassUtil.java | 28 +++++- 7 files changed, 231 insertions(+), 24 deletions(-) create mode 100644 src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 05eba2b0..ad0256d4 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -212,9 +212,10 @@ public class JbootAopFactory extends AopFactory { String value = getConfigValue(key, targetObject, field); if (StrUtil.isNotBlank(value)) { - Object fieldInjectedObject = JbootConfigManager.me().convert(fieldInjectedClass, value); - setFieldValue(field, targetObject, fieldInjectedObject); - return; + Object fieldInjectedObject = JbootConfigManager.me().convert(fieldInjectedClass, value, field.getGenericType()); + if (fieldInjectedObject != null) { + setFieldValue(field, targetObject, fieldInjectedObject); + } } } diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 59322f06..2100fcbf 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -20,6 +20,7 @@ import io.jboot.app.config.annotation.ConfigModel; import java.io.File; import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -215,8 +216,10 @@ public class JbootConfigManager { try { if (Utils.isNotBlank(value)) { - Object val = convert(method.getParameterTypes()[0], value); - method.invoke(configObject, val); + Object val = convert(method.getParameterTypes()[0], value, method.getGenericParameterTypes()[0]); + if (val != null) { + method.invoke(configObject, val); + } } } catch (Throwable ex) { ex.printStackTrace(); @@ -228,8 +231,8 @@ public class JbootConfigManager { } - public Object convert(Class type, String s) { - return Utils.convert(type, s); + public Object convert(Class clazz, String s, Type genericType) { + return Utils.convert(clazz, s, genericType); } private String buildKey(String prefix, Method method) { diff --git a/src/main/java/io/jboot/app/config/Utils.java b/src/main/java/io/jboot/app/config/Utils.java index 2e74219c..026eadcf 100644 --- a/src/main/java/io/jboot/app/config/Utils.java +++ b/src/main/java/io/jboot/app/config/Utils.java @@ -20,8 +20,10 @@ import java.io.File; import java.io.UnsupportedEncodingException; import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; class Utils { @@ -52,8 +54,8 @@ class Utils { for (Method method : methods) { if (method.getName().startsWith("set") && method.getName().length() > 3 - && method.getParameterCount() == 1) { - + && method.getParameterCount() == 1 + && Character.isUpperCase(method.getName().charAt(3))) { setMethods.add(method); } } @@ -109,21 +111,21 @@ class Utils { public static void doNothing(Throwable ex) { } - public static final Object convert(Class type, String s) { + public static final Object convert(Class convertClass, String s, Type genericType) { - if (type == String.class) { + if (convertClass == String.class || s == null) { return s; } - if (type == Integer.class || type == int.class) { + if (convertClass == Integer.class || convertClass == int.class) { return Integer.parseInt(s); - } else if (type == Long.class || type == long.class) { + } else if (convertClass == Long.class || convertClass == long.class) { return Long.parseLong(s); - } else if (type == Double.class || type == double.class) { + } else if (convertClass == Double.class || convertClass == double.class) { return Double.parseDouble(s); - } else if (type == Float.class || type == float.class) { + } else if (convertClass == Float.class || convertClass == float.class) { return Float.parseFloat(s); - } else if (type == Boolean.class || type == boolean.class) { + } else if (convertClass == Boolean.class || convertClass == boolean.class) { String value = s.toLowerCase(); if ("1".equals(value) || "true".equals(value)) { return Boolean.TRUE; @@ -132,16 +134,79 @@ class Utils { } else { throw new RuntimeException("Can not parse to boolean type of value: " + s); } - } else if (type == java.math.BigDecimal.class) { + } else if (convertClass == java.math.BigDecimal.class) { return new java.math.BigDecimal(s); - } else if (type == java.math.BigInteger.class) { + } else if (convertClass == java.math.BigInteger.class) { return new java.math.BigInteger(s); - } else if (type == byte[].class) { + } else if (convertClass == byte[].class) { return s.getBytes(); + } else if (Map.class.isAssignableFrom(convertClass)) { + if (!s.contains(":") || !genericClassCheck(genericType)) { + return null; + } else { + Map map = convertClass == ConcurrentHashMap.class ? new ConcurrentHashMap() : new HashMap(); + String[] strings = s.split(";"); + for (String kv : strings) { + String[] keyValue = kv.split(":"); + if (keyValue.length == 2) { + map.put(keyValue[0], keyValue[1]); + } + } + return map; + } + } else if (List.class.isAssignableFrom(convertClass)) { + if (genericClassCheck(genericType)) { + List list = LinkedList.class == convertClass ? new LinkedList() : new ArrayList(); + String[] strings = s.split(";"); + for (String s1 : strings) { + if (s != null && s1.trim().length() > 0) { + list.add(s1.trim()); + } + } + return list; + } else { + return null; + } + } else if (Set.class.isAssignableFrom(convertClass)) { + if (genericClassCheck(genericType)) { + Set set = LinkedHashSet.class == convertClass ? new LinkedHashSet() : new HashSet(); + String[] strings = s.split(";"); + for (String s1 : strings) { + if (s != null && s1.trim().length() > 0) { + set.add(s1.trim()); + } + } + return set; + } else { + return null; + } + } else if (Class.class == convertClass) { + try { + return Class.forName(s, false, Thread.currentThread().getContextClassLoader()); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } } - throw new RuntimeException(type.getName() + " can not be converted, please use other type in your config class!"); + throw new RuntimeException(convertClass.getName() + " can not be converted, please use other type in your config class!"); } + /** + * 对泛型类型进行检测,只支持 String 类型的泛型,或者不是泛型才会支持 + * + * @param type + * @return + */ + private static boolean genericClassCheck(Type type) { + if (type instanceof ParameterizedType) { + for (Type at : ((ParameterizedType) type).getActualTypeArguments()) { + if (String.class != at) { + return false; + } + } + } + return true; + } + } diff --git a/src/main/java/io/jboot/components/rpc/config/ReferenceConfig.java b/src/main/java/io/jboot/components/rpc/config/ReferenceConfig.java index 90512c13..bcc2088c 100644 --- a/src/main/java/io/jboot/components/rpc/config/ReferenceConfig.java +++ b/src/main/java/io/jboot/components/rpc/config/ReferenceConfig.java @@ -55,6 +55,7 @@ public class ReferenceConfig implements Serializable { private String layer; private Boolean init; private String protocol; + private String consumer; public String getId() { return id; @@ -279,4 +280,12 @@ public class ReferenceConfig implements Serializable { public void setProtocol(String protocol) { this.protocol = protocol; } + + public String getConsumer() { + return consumer; + } + + public void setConsumer(String consumer) { + this.consumer = consumer; + } } diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java new file mode 100644 index 00000000..0641362b --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.rpc.dubbo; + + +import io.jboot.Jboot; +import io.jboot.components.rpc.config.ApplicationConfig; +import io.jboot.components.rpc.config.ConsumerConfig; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/19 + */ + class DubboUtil { + + public static org.apache.dubbo.config.ApplicationConfig getApplicationConfig(){ + ApplicationConfig app = Jboot.config(ApplicationConfig.class,"jboot.rpc.application"); + + org.apache.dubbo.config.ApplicationConfig dubboConfig = new org.apache.dubbo.config.ApplicationConfig(); + + dubboConfig.setQosEnable(app.getQosEnable()); + dubboConfig.setQosAcceptForeignIp(app.getQosAcceptForeignIp()); + dubboConfig.setQosHost(app.getQosHost()); + dubboConfig.setQosPort(app.getQosPort()); + dubboConfig.setName(app.getName()); + dubboConfig.setArchitecture(app.getArchitecture()); + dubboConfig.setCompiler(app.getCompiler()); + dubboConfig.setDefault(app.getDefault()); + dubboConfig.setDumpDirectory(app.getDumpDirectory()); + dubboConfig.setEnvironment(app.getEnvironment()); + dubboConfig.setLogger(app.getLogger()); + dubboConfig.setMetadataType(app.getMetadataType()); + dubboConfig.setMonitor(app.getMonitor()); + dubboConfig.setOrganization(app.getOrganization()); + dubboConfig.setRegistryIds(app.getRegistryIds()); + dubboConfig.setRegisterConsumer(app.getRegisterConsumer()); + dubboConfig.setRepository(app.getRepository()); + dubboConfig.setShutwait(app.getShutwait()); + dubboConfig.setOwner(app.getOwner()); + dubboConfig.setVersion(app.getVersion()); + + return dubboConfig; + } + + + + public static org.apache.dubbo.config.ConsumerConfig getConsumerConfig(){ + ConsumerConfig app = Jboot.config(ConsumerConfig.class,"jboot.rpc.consumer"); + + org.apache.dubbo.config.ConsumerConfig dubboConfig = new org.apache.dubbo.config.ConsumerConfig(); + dubboConfig.setClient(app.getClient()); + dubboConfig.setCorethreads(app.getCorethreads()); + dubboConfig.setDefault(app.getDefault()); + dubboConfig.setQueues(app.getQueues()); + dubboConfig.setShareconnections(app.getShareconnections()); + dubboConfig.setThreadpool(app.getThreadpool()); + dubboConfig.setThreads(app.getThreads()); +// dubboConfig.setTimeout(app.gett); + + + return dubboConfig; + } +} diff --git a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java index b1e9ffae..26297e48 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java @@ -21,6 +21,7 @@ import io.jboot.components.rpc.JbootrpcServiceConfig; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.StrUtil; import org.apache.dubbo.config.*; +import org.apache.dubbo.config.bootstrap.DubboBootstrap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -36,6 +37,9 @@ public class JbootDubborpc extends JbootrpcBase { private JbootDubborpcConfig dubboConfig; private RegistryConfig registryConfig; + + + public JbootDubborpc() { dubboConfig = Jboot.config(JbootDubborpcConfig.class); @@ -80,6 +84,30 @@ public class JbootDubborpc extends JbootrpcBase { } + + + @Override + public void onInit() { + + + DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance(); + dubboBootstrap.application(DubboUtil.getApplicationConfig()) + .consumer(DubboUtil.getConsumerConfig()) +// .protocols() +// .registries() +// .configCenters() +// .consumers() +// .metrics() +// .module() +// .monitor() +// .ssl() +// .cache() + .start(); + + + } + + @Override public T serviceObtain(Class serviceClass, JbootrpcServiceConfig serviceConfig) { @@ -152,6 +180,7 @@ public class JbootDubborpc extends JbootrpcBase { service.setApplication(createApplicationConfig(serviceConfig.getGroup())); service.setRegistry(registryConfig); // 多个注册中心可以用setRegistries() service.setProtocol(protocolConfig); // 多个协议可以用setProtocols() +// service.setProtocolIds(); service.setInterface(interfaceClass); service.setRef((T) object); diff --git a/src/main/java/io/jboot/utils/ClassUtil.java b/src/main/java/io/jboot/utils/ClassUtil.java index 55becd26..c9e53d0d 100644 --- a/src/main/java/io/jboot/utils/ClassUtil.java +++ b/src/main/java/io/jboot/utils/ClassUtil.java @@ -242,11 +242,35 @@ public class ClassUtil { public static Class getGenericClass(Class clazz) { - Type type = getUsefulClass(clazz).getGenericSuperclass(); - return (Class) ((ParameterizedType) type).getActualTypeArguments()[0]; + return getGenericClass(getUsefulClass(clazz).getGenericSuperclass()); } + public static Class[] getGenericClass(Method method) { + Type[] type = method.getGenericParameterTypes(); + Class[] classes = new Class[type.length]; + for (int i = 0; i < type.length; i++) { + classes[i] = getGenericClass(type[i]); + } + return classes; + } + + + public static Class getGenericClass(Field field) { + return getGenericClass(field.getType()); + } + + + public static Class getGenericClass(Type type) { + if (type instanceof ParameterizedType) { + return (Class) ((ParameterizedType) type).getActualTypeArguments()[0]; + } else { + return null; + } + } + + + public static String buildMethodString(Method method) { StringBuilder sb = new StringBuilder() -- Gitee From 9550217776519d523d34baae5b830fb4bd0543b2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 20 Mar 2020 11:56:26 +0800 Subject: [PATCH 0080/1965] Restructure Dubbo RPC --- .../rpc/config/ApplicationConfig.java | 308 ------------ .../components/rpc/config/ArgumentConfig.java | 66 --- .../components/rpc/config/ConsumerConfig.java | 119 ----- .../components/rpc/config/MethodConfig.java | 225 --------- .../components/rpc/config/ModuleConfig.java | 93 ---- .../components/rpc/config/MonitorConfig.java | 138 ------ .../components/rpc/config/ProtocolConfig.java | 456 ------------------ .../components/rpc/config/ProviderConfig.java | 326 ------------- .../rpc/config/ReferenceConfig.java | 291 ----------- .../components/rpc/config/RegistryConfig.java | 403 ---------------- .../components/rpc/config/ServiceConfig.java | 299 ------------ .../jboot/components/rpc/dubbo/DubboUtil.java | 218 +++++++-- .../components/rpc/dubbo/JbootDubborpc.java | 19 +- 13 files changed, 168 insertions(+), 2793 deletions(-) delete mode 100644 src/main/java/io/jboot/components/rpc/config/ApplicationConfig.java delete mode 100644 src/main/java/io/jboot/components/rpc/config/ArgumentConfig.java delete mode 100644 src/main/java/io/jboot/components/rpc/config/ConsumerConfig.java delete mode 100644 src/main/java/io/jboot/components/rpc/config/MethodConfig.java delete mode 100644 src/main/java/io/jboot/components/rpc/config/ModuleConfig.java delete mode 100644 src/main/java/io/jboot/components/rpc/config/MonitorConfig.java delete mode 100644 src/main/java/io/jboot/components/rpc/config/ProtocolConfig.java delete mode 100644 src/main/java/io/jboot/components/rpc/config/ProviderConfig.java delete mode 100644 src/main/java/io/jboot/components/rpc/config/ReferenceConfig.java delete mode 100644 src/main/java/io/jboot/components/rpc/config/RegistryConfig.java delete mode 100644 src/main/java/io/jboot/components/rpc/config/ServiceConfig.java diff --git a/src/main/java/io/jboot/components/rpc/config/ApplicationConfig.java b/src/main/java/io/jboot/components/rpc/config/ApplicationConfig.java deleted file mode 100644 index 30189f7f..00000000 --- a/src/main/java/io/jboot/components/rpc/config/ApplicationConfig.java +++ /dev/null @@ -1,308 +0,0 @@ -/** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt - *

- * 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 io.jboot.components.rpc.config; - -import org.apache.dubbo.config.MonitorConfig; - -import java.io.Serializable; -import java.util.Map; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/19 - */ -public class ApplicationConfig implements Serializable { - - private static final long serialVersionUID = 1L; - - /** - * Application name - */ - private String name; - - /** - * The application version - */ - private String version; - - /** - * Application owner - */ - private String owner; - - /** - * Application's organization (BU) - */ - private String organization; - - /** - * Architecture layer - */ - private String architecture; - - /** - * Environment, e.g. dev, test or production - */ - private String environment; - - /** - * Java compiler - */ - private String compiler; - - /** - * The type of the log access - */ - private String logger; - - /** - * Registry centers - */ - private String registryIds; - - /** - * Monitor center - */ - private MonitorConfig monitor; - - /** - * Is default or not - */ - private Boolean isDefault; - - /** - * Directory for saving thread dump - */ - private String dumpDirectory; - - /** - * Whether to enable qos or not - */ - private Boolean qosEnable; - - /** - * The qos host to listen - */ - private String qosHost; - - /** - * The qos port to listen - */ - private Integer qosPort; - - /** - * Should we accept foreign ip or not? - */ - private Boolean qosAcceptForeignIp; - - /** - * Customized parameters - */ - private Map parameters; - - /** - * Config the shutdown.wait - */ - private String shutwait; - - private String hostname; - - /** - * Metadata type, local or remote, if choose remote, you need to further specify metadata center. - */ - private String metadataType; - - private Boolean registerConsumer; - - private String repository; - - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.owner = owner; - } - - public String getOrganization() { - return organization; - } - - public void setOrganization(String organization) { - this.organization = organization; - } - - public String getArchitecture() { - return architecture; - } - - public void setArchitecture(String architecture) { - this.architecture = architecture; - } - - public String getEnvironment() { - return environment; - } - - public void setEnvironment(String environment) { - this.environment = environment; - } - - public String getCompiler() { - return compiler; - } - - public void setCompiler(String compiler) { - this.compiler = compiler; - } - - public String getLogger() { - return logger; - } - - public void setLogger(String logger) { - this.logger = logger; - } - - public String getRegistryIds() { - return registryIds; - } - - public void setRegistryIds(String registryIds) { - this.registryIds = registryIds; - } - - public MonitorConfig getMonitor() { - return monitor; - } - - public void setMonitor(MonitorConfig monitor) { - this.monitor = monitor; - } - - public Boolean getDefault() { - return isDefault; - } - - public void setDefault(Boolean aDefault) { - isDefault = aDefault; - } - - public String getDumpDirectory() { - return dumpDirectory; - } - - public void setDumpDirectory(String dumpDirectory) { - this.dumpDirectory = dumpDirectory; - } - - public Boolean getQosEnable() { - return qosEnable; - } - - public void setQosEnable(Boolean qosEnable) { - this.qosEnable = qosEnable; - } - - public String getQosHost() { - return qosHost; - } - - public void setQosHost(String qosHost) { - this.qosHost = qosHost; - } - - public Integer getQosPort() { - return qosPort; - } - - public void setQosPort(Integer qosPort) { - this.qosPort = qosPort; - } - - public Boolean getQosAcceptForeignIp() { - return qosAcceptForeignIp; - } - - public void setQosAcceptForeignIp(Boolean qosAcceptForeignIp) { - this.qosAcceptForeignIp = qosAcceptForeignIp; - } - - public Map getParameters() { - return parameters; - } - - public void setParameters(Map parameters) { - this.parameters = parameters; - } - - public String getShutwait() { - return shutwait; - } - - public void setShutwait(String shutwait) { - this.shutwait = shutwait; - } - - public String getHostname() { - return hostname; - } - - public void setHostname(String hostname) { - this.hostname = hostname; - } - - public String getMetadataType() { - return metadataType; - } - - public void setMetadataType(String metadataType) { - this.metadataType = metadataType; - } - - public Boolean getRegisterConsumer() { - return registerConsumer; - } - - public void setRegisterConsumer(Boolean registerConsumer) { - this.registerConsumer = registerConsumer; - } - - public String getRepository() { - return repository; - } - - public void setRepository(String repository) { - this.repository = repository; - } -} diff --git a/src/main/java/io/jboot/components/rpc/config/ArgumentConfig.java b/src/main/java/io/jboot/components/rpc/config/ArgumentConfig.java deleted file mode 100644 index 54216128..00000000 --- a/src/main/java/io/jboot/components/rpc/config/ArgumentConfig.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt - *

- * 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 io.jboot.components.rpc.config; - -import java.io.Serializable; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/19 - */ -public class ArgumentConfig implements Serializable { - - private static final long serialVersionUID = 1L; - - /** - * The argument index: index -1 represents not set - */ - private Integer index = -1; - - /** - * Argument type - */ - private String type; - - /** - * Whether the argument is the callback interface - */ - private Boolean callback; - - public Integer getIndex() { - return index; - } - - public void setIndex(Integer index) { - this.index = index; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public Boolean getCallback() { - return callback; - } - - public void setCallback(Boolean callback) { - this.callback = callback; - } -} diff --git a/src/main/java/io/jboot/components/rpc/config/ConsumerConfig.java b/src/main/java/io/jboot/components/rpc/config/ConsumerConfig.java deleted file mode 100644 index c4f5906f..00000000 --- a/src/main/java/io/jboot/components/rpc/config/ConsumerConfig.java +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt - *

- * 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 io.jboot.components.rpc.config; - -import java.io.Serializable; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/19 - */ -public class ConsumerConfig implements Serializable { - - private static final long serialVersionUID = 1L; - - /** - * Whether to use the default protocol - */ - private Boolean isDefault; - - /** - * Networking framework client uses: netty, mina, etc. - */ - private String client; - - /** - * Consumer thread pool type: cached, fixed, limit, eager - */ - private String threadpool; - - /** - * Consumer threadpool core thread size - */ - private Integer corethreads; - - /** - * Consumer threadpool thread size - */ - private Integer threads; - - /** - * Consumer threadpool queue size - */ - private Integer queues; - - /** - * By default, a TCP long-connection communication is shared between the consumer process and the provider process. - * This property can be set to share multiple TCP long-connection communications. Note that only the dubbo protocol takes effect. - */ - private Integer shareconnections; - - public Boolean getDefault() { - return isDefault; - } - - public void setDefault(Boolean aDefault) { - isDefault = aDefault; - } - - public String getClient() { - return client; - } - - public void setClient(String client) { - this.client = client; - } - - public String getThreadpool() { - return threadpool; - } - - public void setThreadpool(String threadpool) { - this.threadpool = threadpool; - } - - public Integer getCorethreads() { - return corethreads; - } - - public void setCorethreads(Integer corethreads) { - this.corethreads = corethreads; - } - - public Integer getThreads() { - return threads; - } - - public void setThreads(Integer threads) { - this.threads = threads; - } - - public Integer getQueues() { - return queues; - } - - public void setQueues(Integer queues) { - this.queues = queues; - } - - public Integer getShareconnections() { - return shareconnections; - } - - public void setShareconnections(Integer shareconnections) { - this.shareconnections = shareconnections; - } -} diff --git a/src/main/java/io/jboot/components/rpc/config/MethodConfig.java b/src/main/java/io/jboot/components/rpc/config/MethodConfig.java deleted file mode 100644 index 4741203a..00000000 --- a/src/main/java/io/jboot/components/rpc/config/MethodConfig.java +++ /dev/null @@ -1,225 +0,0 @@ -/** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt - *

- * 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 io.jboot.components.rpc.config; - -import org.apache.dubbo.config.ArgumentConfig; - -import java.io.Serializable; -import java.util.List; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/19 - */ -public class MethodConfig implements Serializable { - - private static final long serialVersionUID = 1L; - - /** - * The method name - */ - private String name; - - /** - * Stat - */ - private Integer stat; - - /** - * Whether to retry - */ - private Boolean retry; - - /** - * If it's reliable - */ - private Boolean reliable; - - /** - * Thread limits for method invocations - */ - private Integer executes; - - /** - * If it's deprecated - */ - private Boolean deprecated; - - /** - * Whether to enable sticky - */ - private Boolean sticky; - - /** - * Whether need to return - */ - private Boolean isReturn; - - /** - * Callback instance when async-call is invoked - */ - private Object oninvoke; - - /** - * Callback method when async-call is invoked - */ - private String oninvokeMethod; - - /** - * Callback instance when async-call is returned - */ - private Object onreturn; - - /** - * Callback method when async-call is returned - */ - private String onreturnMethod; - - /** - * Callback instance when async-call has exception thrown - */ - private Object onthrow; - - /** - * Callback method when async-call has exception thrown - */ - private String onthrowMethod; - - /** - * The method arguments - */ - private List arguments; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Integer getStat() { - return stat; - } - - public void setStat(Integer stat) { - this.stat = stat; - } - - public Boolean getRetry() { - return retry; - } - - public void setRetry(Boolean retry) { - this.retry = retry; - } - - public Boolean getReliable() { - return reliable; - } - - public void setReliable(Boolean reliable) { - this.reliable = reliable; - } - - public Integer getExecutes() { - return executes; - } - - public void setExecutes(Integer executes) { - this.executes = executes; - } - - public Boolean getDeprecated() { - return deprecated; - } - - public void setDeprecated(Boolean deprecated) { - this.deprecated = deprecated; - } - - public Boolean getSticky() { - return sticky; - } - - public void setSticky(Boolean sticky) { - this.sticky = sticky; - } - - public Boolean getReturn() { - return isReturn; - } - - public void setReturn(Boolean aReturn) { - isReturn = aReturn; - } - - public Object getOninvoke() { - return oninvoke; - } - - public void setOninvoke(Object oninvoke) { - this.oninvoke = oninvoke; - } - - public String getOninvokeMethod() { - return oninvokeMethod; - } - - public void setOninvokeMethod(String oninvokeMethod) { - this.oninvokeMethod = oninvokeMethod; - } - - public Object getOnreturn() { - return onreturn; - } - - public void setOnreturn(Object onreturn) { - this.onreturn = onreturn; - } - - public String getOnreturnMethod() { - return onreturnMethod; - } - - public void setOnreturnMethod(String onreturnMethod) { - this.onreturnMethod = onreturnMethod; - } - - public Object getOnthrow() { - return onthrow; - } - - public void setOnthrow(Object onthrow) { - this.onthrow = onthrow; - } - - public String getOnthrowMethod() { - return onthrowMethod; - } - - public void setOnthrowMethod(String onthrowMethod) { - this.onthrowMethod = onthrowMethod; - } - - public List getArguments() { - return arguments; - } - - public void setArguments(List arguments) { - this.arguments = arguments; - } -} diff --git a/src/main/java/io/jboot/components/rpc/config/ModuleConfig.java b/src/main/java/io/jboot/components/rpc/config/ModuleConfig.java deleted file mode 100644 index 5b3471c9..00000000 --- a/src/main/java/io/jboot/components/rpc/config/ModuleConfig.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt - *

- * 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 io.jboot.components.rpc.config; - -import java.io.Serializable; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/19 - */ -public class ModuleConfig implements Serializable { - - private static final long serialVersionUID = 1L; - - /** - * Module name - */ - private String name; - - /** - * Module version - */ - private String version; - - /** - * Module owner - */ - private String owner; - - /** - * Module's organization - */ - private String organization; - - /** - * If it's default - */ - private Boolean isDefault; - - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.owner = owner; - } - - public String getOrganization() { - return organization; - } - - public void setOrganization(String organization) { - this.organization = organization; - } - - public Boolean getDefault() { - return isDefault; - } - - public void setDefault(Boolean aDefault) { - isDefault = aDefault; - } -} diff --git a/src/main/java/io/jboot/components/rpc/config/MonitorConfig.java b/src/main/java/io/jboot/components/rpc/config/MonitorConfig.java deleted file mode 100644 index f9bbd18a..00000000 --- a/src/main/java/io/jboot/components/rpc/config/MonitorConfig.java +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt - *

- * 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 io.jboot.components.rpc.config; - -import java.io.Serializable; -import java.util.Map; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/19 - */ -public class MonitorConfig implements Serializable { - - private static final long serialVersionUID = 1L; - - /** - * The protocol of the monitor, if the value is registry, it will search the monitor address from the registry center, - * otherwise, it will directly connect to the monitor center - */ - private String protocol; - - /** - * The monitor address - */ - private String address; - - /** - * The monitor user name - */ - private String username; - - /** - * The password - */ - private String password; - - private String group; - - private String version; - - private String interval; - - /** - * customized parameters - */ - private Map parameters; - - /** - * If it's default - */ - private Boolean isDefault; - - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getGroup() { - return group; - } - - public void setGroup(String group) { - this.group = group; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getInterval() { - return interval; - } - - public void setInterval(String interval) { - this.interval = interval; - } - - public Map getParameters() { - return parameters; - } - - public void setParameters(Map parameters) { - this.parameters = parameters; - } - - public Boolean getDefault() { - return isDefault; - } - - public void setDefault(Boolean aDefault) { - isDefault = aDefault; - } -} diff --git a/src/main/java/io/jboot/components/rpc/config/ProtocolConfig.java b/src/main/java/io/jboot/components/rpc/config/ProtocolConfig.java deleted file mode 100644 index 0f76ba00..00000000 --- a/src/main/java/io/jboot/components/rpc/config/ProtocolConfig.java +++ /dev/null @@ -1,456 +0,0 @@ -/** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt - *

- * 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 io.jboot.components.rpc.config; - -import java.io.Serializable; -import java.util.Map; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/19 - * http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-protocol.html - */ -public class ProtocolConfig implements Serializable { - - private static final long serialVersionUID = 1L; - - - /** - * Protocol name - */ - private String name; - - /** - * Service ip address (when there are multiple network cards available) - */ - private String host; - - /** - * Service port - */ - private Integer port; - - /** - * Context path - */ - private String contextpath; - - /** - * Thread pool - */ - private String threadpool; - - /** - * Thread pool core thread size - */ - private Integer corethreads; - - /** - * Thread pool size (fixed size) - */ - private Integer threads; - - /** - * IO thread pool size (fixed size) - */ - private Integer iothreads; - - /** - * Thread pool's queue length - */ - private Integer queues; - - /** - * Max acceptable connections - */ - private Integer accepts; - - /** - * Protocol codec - */ - private String codec; - - /** - * Serialization - */ - private String serialization; - - /** - * Charset - */ - private String charset; - - /** - * Payload max length - */ - private Integer payload; - - /** - * Buffer size - */ - private Integer buffer; - - /** - * Heartbeat interval - */ - private Integer heartbeat; - - /** - * Access log - */ - private String accesslog; - - /** - * Transfort - */ - private String transporter; - - /** - * How information is exchanged - */ - private String exchanger; - - /** - * Thread dispatch mode - */ - private String dispatcher; - - /** - * Networker - */ - private String networker; - - /** - * Sever impl - */ - private String server; - - /** - * Client impl - */ - private String client; - - /** - * Supported telnet commands, separated with comma. - */ - private String telnet; - - /** - * Command line prompt - */ - private String prompt; - - /** - * Status check - */ - private String status; - - /** - * Whether to register - */ - private Boolean register; - - /** - * whether it is a persistent connection - */ - //TODO add this to provider config - private Boolean keepAlive; - - // TODO add this to provider config - private String optimizer; - - /** - * The extension - */ - private String extension; - - /** - * The customized parameters - */ - private Map parameters; - - /** - * If it's default - */ - private Boolean isDefault; - - private Boolean sslEnabled; - - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public Integer getPort() { - return port; - } - - public void setPort(Integer port) { - this.port = port; - } - - public String getContextpath() { - return contextpath; - } - - public void setContextpath(String contextpath) { - this.contextpath = contextpath; - } - - public String getThreadpool() { - return threadpool; - } - - public void setThreadpool(String threadpool) { - this.threadpool = threadpool; - } - - public Integer getCorethreads() { - return corethreads; - } - - public void setCorethreads(Integer corethreads) { - this.corethreads = corethreads; - } - - public Integer getThreads() { - return threads; - } - - public void setThreads(Integer threads) { - this.threads = threads; - } - - public Integer getIothreads() { - return iothreads; - } - - public void setIothreads(Integer iothreads) { - this.iothreads = iothreads; - } - - public Integer getQueues() { - return queues; - } - - public void setQueues(Integer queues) { - this.queues = queues; - } - - public Integer getAccepts() { - return accepts; - } - - public void setAccepts(Integer accepts) { - this.accepts = accepts; - } - - public String getCodec() { - return codec; - } - - public void setCodec(String codec) { - this.codec = codec; - } - - public String getSerialization() { - return serialization; - } - - public void setSerialization(String serialization) { - this.serialization = serialization; - } - - public String getCharset() { - return charset; - } - - public void setCharset(String charset) { - this.charset = charset; - } - - public Integer getPayload() { - return payload; - } - - public void setPayload(Integer payload) { - this.payload = payload; - } - - public Integer getBuffer() { - return buffer; - } - - public void setBuffer(Integer buffer) { - this.buffer = buffer; - } - - public Integer getHeartbeat() { - return heartbeat; - } - - public void setHeartbeat(Integer heartbeat) { - this.heartbeat = heartbeat; - } - - public String getAccesslog() { - return accesslog; - } - - public void setAccesslog(String accesslog) { - this.accesslog = accesslog; - } - - public String getTransporter() { - return transporter; - } - - public void setTransporter(String transporter) { - this.transporter = transporter; - } - - public String getExchanger() { - return exchanger; - } - - public void setExchanger(String exchanger) { - this.exchanger = exchanger; - } - - public String getDispatcher() { - return dispatcher; - } - - public void setDispatcher(String dispatcher) { - this.dispatcher = dispatcher; - } - - public String getNetworker() { - return networker; - } - - public void setNetworker(String networker) { - this.networker = networker; - } - - public String getServer() { - return server; - } - - public void setServer(String server) { - this.server = server; - } - - public String getClient() { - return client; - } - - public void setClient(String client) { - this.client = client; - } - - public String getTelnet() { - return telnet; - } - - public void setTelnet(String telnet) { - this.telnet = telnet; - } - - public String getPrompt() { - return prompt; - } - - public void setPrompt(String prompt) { - this.prompt = prompt; - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - public Boolean getRegister() { - return register; - } - - public void setRegister(Boolean register) { - this.register = register; - } - - public Boolean getKeepAlive() { - return keepAlive; - } - - public void setKeepAlive(Boolean keepAlive) { - this.keepAlive = keepAlive; - } - - public String getOptimizer() { - return optimizer; - } - - public void setOptimizer(String optimizer) { - this.optimizer = optimizer; - } - - public String getExtension() { - return extension; - } - - public void setExtension(String extension) { - this.extension = extension; - } - - public Map getParameters() { - return parameters; - } - - public void setParameters(Map parameters) { - this.parameters = parameters; - } - - public Boolean getDefault() { - return isDefault; - } - - public void setDefault(Boolean aDefault) { - isDefault = aDefault; - } - - public Boolean getSslEnabled() { - return sslEnabled; - } - - public void setSslEnabled(Boolean sslEnabled) { - this.sslEnabled = sslEnabled; - } -} diff --git a/src/main/java/io/jboot/components/rpc/config/ProviderConfig.java b/src/main/java/io/jboot/components/rpc/config/ProviderConfig.java deleted file mode 100644 index 335caa91..00000000 --- a/src/main/java/io/jboot/components/rpc/config/ProviderConfig.java +++ /dev/null @@ -1,326 +0,0 @@ -/** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt - *

- * 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 io.jboot.components.rpc.config; - -import java.io.Serializable; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/19 - */ -public class ProviderConfig implements Serializable { - - private static final long serialVersionUID = 1L; - - /** - * Service ip addresses (used when there are multiple network cards available) - */ - private String host; - - /** - * Service port - */ - private Integer port; - - /** - * Context path - */ - private String contextpath; - - /** - * Thread pool - */ - private String threadpool; - - /** - * Thread pool size (fixed size) - */ - private Integer threads; - - /** - * IO thread pool size (fixed size) - */ - private Integer iothreads; - - /** - * Thread pool queue length - */ - private Integer queues; - - /** - * Max acceptable connections - */ - private Integer accepts; - - /** - * Protocol codec - */ - private String codec; - - /** - * The serialization charset - */ - private String charset; - - /** - * Payload max length - */ - private Integer payload; - - /** - * The network io buffer size - */ - private Integer buffer; - - /** - * Transporter - */ - private String transporter; - - /** - * How information gets exchanged - */ - private String exchanger; - - /** - * Thread dispatching mode - */ - private String dispatcher; - - /** - * Networker - */ - private String networker; - - /** - * The server-side implementation model of the protocol - */ - private String server; - - /** - * The client-side implementation model of the protocol - */ - private String client; - - /** - * Supported telnet commands, separated with comma. - */ - private String telnet; - - /** - * Command line prompt - */ - private String prompt; - - /** - * Status check - */ - private String status; - - /** - * Wait time when stop - */ - private Integer wait; - - /** - * Whether to use the default protocol - */ - private Boolean isDefault; - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public Integer getPort() { - return port; - } - - public void setPort(Integer port) { - this.port = port; - } - - public String getContextpath() { - return contextpath; - } - - public void setContextpath(String contextpath) { - this.contextpath = contextpath; - } - - public String getThreadpool() { - return threadpool; - } - - public void setThreadpool(String threadpool) { - this.threadpool = threadpool; - } - - public Integer getThreads() { - return threads; - } - - public void setThreads(Integer threads) { - this.threads = threads; - } - - public Integer getIothreads() { - return iothreads; - } - - public void setIothreads(Integer iothreads) { - this.iothreads = iothreads; - } - - public Integer getQueues() { - return queues; - } - - public void setQueues(Integer queues) { - this.queues = queues; - } - - public Integer getAccepts() { - return accepts; - } - - public void setAccepts(Integer accepts) { - this.accepts = accepts; - } - - public String getCodec() { - return codec; - } - - public void setCodec(String codec) { - this.codec = codec; - } - - public String getCharset() { - return charset; - } - - public void setCharset(String charset) { - this.charset = charset; - } - - public Integer getPayload() { - return payload; - } - - public void setPayload(Integer payload) { - this.payload = payload; - } - - public Integer getBuffer() { - return buffer; - } - - public void setBuffer(Integer buffer) { - this.buffer = buffer; - } - - public String getTransporter() { - return transporter; - } - - public void setTransporter(String transporter) { - this.transporter = transporter; - } - - public String getExchanger() { - return exchanger; - } - - public void setExchanger(String exchanger) { - this.exchanger = exchanger; - } - - public String getDispatcher() { - return dispatcher; - } - - public void setDispatcher(String dispatcher) { - this.dispatcher = dispatcher; - } - - public String getNetworker() { - return networker; - } - - public void setNetworker(String networker) { - this.networker = networker; - } - - public String getServer() { - return server; - } - - public void setServer(String server) { - this.server = server; - } - - public String getClient() { - return client; - } - - public void setClient(String client) { - this.client = client; - } - - public String getTelnet() { - return telnet; - } - - public void setTelnet(String telnet) { - this.telnet = telnet; - } - - public String getPrompt() { - return prompt; - } - - public void setPrompt(String prompt) { - this.prompt = prompt; - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - public Integer getWait() { - return wait; - } - - public void setWait(Integer wait) { - this.wait = wait; - } - - public Boolean getDefault() { - return isDefault; - } - - public void setDefault(Boolean aDefault) { - isDefault = aDefault; - } -} diff --git a/src/main/java/io/jboot/components/rpc/config/ReferenceConfig.java b/src/main/java/io/jboot/components/rpc/config/ReferenceConfig.java deleted file mode 100644 index bcc2088c..00000000 --- a/src/main/java/io/jboot/components/rpc/config/ReferenceConfig.java +++ /dev/null @@ -1,291 +0,0 @@ -/** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt - *

- * 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 io.jboot.components.rpc.config; - -import java.io.Serializable; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/17 - * - * http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-reference.html - */ -public class ReferenceConfig implements Serializable { - - private static final long serialVersionUID = 1L; - - private String id; - private String name; - private String interfaceClass; - private String version; - private String group; - private Long timeout; - private Integer retries; - private Integer connections; - private String loadbalance; - private Boolean async; - private Boolean generic; - private Boolean check; - private String url; - private String stub; - private String mock; - private String cache; - private Boolean validation; - private Boolean proxy; - private String client; - private String registry; - private String owner; - private Integer actives; - private String cluster; - private String filter; - private String listener; - private String layer; - private Boolean init; - private String protocol; - private String consumer; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getInterfaceClass() { - return interfaceClass; - } - - public void setInterfaceClass(String interfaceClass) { - this.interfaceClass = interfaceClass; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getGroup() { - return group; - } - - public void setGroup(String group) { - this.group = group; - } - - public Long getTimeout() { - return timeout; - } - - public void setTimeout(Long timeout) { - this.timeout = timeout; - } - - public Integer getRetries() { - return retries; - } - - public void setRetries(Integer retries) { - this.retries = retries; - } - - public Integer getConnections() { - return connections; - } - - public void setConnections(Integer connections) { - this.connections = connections; - } - - public String getLoadbalance() { - return loadbalance; - } - - public void setLoadbalance(String loadbalance) { - this.loadbalance = loadbalance; - } - - public Boolean getAsync() { - return async; - } - - public void setAsync(Boolean async) { - this.async = async; - } - - public Boolean getGeneric() { - return generic; - } - - public void setGeneric(Boolean generic) { - this.generic = generic; - } - - public Boolean getCheck() { - return check; - } - - public void setCheck(Boolean check) { - this.check = check; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getStub() { - return stub; - } - - public void setStub(String stub) { - this.stub = stub; - } - - public String getMock() { - return mock; - } - - public void setMock(String mock) { - this.mock = mock; - } - - public String getCache() { - return cache; - } - - public void setCache(String cache) { - this.cache = cache; - } - - public Boolean getValidation() { - return validation; - } - - public void setValidation(Boolean validation) { - this.validation = validation; - } - - public Boolean getProxy() { - return proxy; - } - - public void setProxy(Boolean proxy) { - this.proxy = proxy; - } - - public String getClient() { - return client; - } - - public void setClient(String client) { - this.client = client; - } - - public String getRegistry() { - return registry; - } - - public void setRegistry(String registry) { - this.registry = registry; - } - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.owner = owner; - } - - public Integer getActives() { - return actives; - } - - public void setActives(Integer actives) { - this.actives = actives; - } - - public String getCluster() { - return cluster; - } - - public void setCluster(String cluster) { - this.cluster = cluster; - } - - public String getFilter() { - return filter; - } - - public void setFilter(String filter) { - this.filter = filter; - } - - public String getListener() { - return listener; - } - - public void setListener(String listener) { - this.listener = listener; - } - - public String getLayer() { - return layer; - } - - public void setLayer(String layer) { - this.layer = layer; - } - - public Boolean getInit() { - return init; - } - - public void setInit(Boolean init) { - this.init = init; - } - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - public String getConsumer() { - return consumer; - } - - public void setConsumer(String consumer) { - this.consumer = consumer; - } -} diff --git a/src/main/java/io/jboot/components/rpc/config/RegistryConfig.java b/src/main/java/io/jboot/components/rpc/config/RegistryConfig.java deleted file mode 100644 index e8df30cb..00000000 --- a/src/main/java/io/jboot/components/rpc/config/RegistryConfig.java +++ /dev/null @@ -1,403 +0,0 @@ -/** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt - *

- * 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 io.jboot.components.rpc.config; - -import java.io.Serializable; -import java.util.Map; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/17 - */ -public class RegistryConfig implements Serializable { - - private static final long serialVersionUID = 1L; - - /** - * Register center address - */ - private String address; - - /** - * Username to login register center - */ - private String username; - - /** - * Password to login register center - */ - private String password; - - /** - * Default port for register center - */ - private Integer port; - - /** - * Protocol for register center - */ - private String protocol; - - /** - * Network transmission type - */ - private String transporter; - - private String server; - - private String client; - - /** - * Affects how traffic distributes among registries, useful when subscribing multiple registries, available options: - * 1. zone-aware, a certain type of traffic always goes to one Registry according to where the traffic is originated. - */ - private String cluster; - - /** - * The region where the registry belongs, usually used to isolate traffics - */ - private String zone; - - /** - * The group the services registry in - */ - private String group; - - private String version; - - /** - * Request timeout in milliseconds for register center - */ - private Integer timeout; - - /** - * Session timeout in milliseconds for register center - */ - private Integer session; - - /** - * File for saving register center dynamic list - */ - private String file; - - /** - * Wait time before stop - */ - private Integer wait; - - /** - * Whether to check if register center is available when boot up - */ - private Boolean check; - - /** - * Whether to allow dynamic service to register on the register center - */ - private Boolean dynamic; - - /** - * Whether to export service on the register center - */ - private Boolean register; - - /** - * Whether allow to subscribe service on the register center - */ - private Boolean subscribe; - - /** - * The customized parameters - */ - private Map parameters; - - /** - * Whether it's default - */ - private Boolean isDefault; - - /** - * Simple the registry. both useful for provider and consumer - * - * @since 2.7.0 - */ - private Boolean simplified; - /** - * After simplify the registry, should add some paramter individually. just for provider. - *

- * such as: extra-keys = A,b,c,d - * - * @since 2.7.0 - */ - private String extraKeys; - - /** - * the address work as config center or not - */ - private Boolean useAsConfigCenter; - - /** - * the address work as remote metadata center or not - */ - private Boolean useAsMetadataCenter; - - /** - * list of rpc protocols accepted by this registry, for example, "dubbo,rest" - */ - private String accepts; - - /** - * Always use this registry first if set to true, useful when subscribe to multiple registries - */ - private Boolean preferred; - - /** - * Affects traffic distribution among registries, useful when subscribe to multiple registries - * Take effect only when no preferred registry is specified. - */ - private Integer weight; - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public Integer getPort() { - return port; - } - - public void setPort(Integer port) { - this.port = port; - } - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - public String getTransporter() { - return transporter; - } - - public void setTransporter(String transporter) { - this.transporter = transporter; - } - - public String getServer() { - return server; - } - - public void setServer(String server) { - this.server = server; - } - - public String getClient() { - return client; - } - - public void setClient(String client) { - this.client = client; - } - - public String getCluster() { - return cluster; - } - - public void setCluster(String cluster) { - this.cluster = cluster; - } - - public String getZone() { - return zone; - } - - public void setZone(String zone) { - this.zone = zone; - } - - public String getGroup() { - return group; - } - - public void setGroup(String group) { - this.group = group; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public Integer getTimeout() { - return timeout; - } - - public void setTimeout(Integer timeout) { - this.timeout = timeout; - } - - public Integer getSession() { - return session; - } - - public void setSession(Integer session) { - this.session = session; - } - - public String getFile() { - return file; - } - - public void setFile(String file) { - this.file = file; - } - - public Integer getWait() { - return wait; - } - - public void setWait(Integer wait) { - this.wait = wait; - } - - public Boolean getCheck() { - return check; - } - - public void setCheck(Boolean check) { - this.check = check; - } - - public Boolean getDynamic() { - return dynamic; - } - - public void setDynamic(Boolean dynamic) { - this.dynamic = dynamic; - } - - public Boolean getRegister() { - return register; - } - - public void setRegister(Boolean register) { - this.register = register; - } - - public Boolean getSubscribe() { - return subscribe; - } - - public void setSubscribe(Boolean subscribe) { - this.subscribe = subscribe; - } - - public Map getParameters() { - return parameters; - } - - public void setParameters(Map parameters) { - this.parameters = parameters; - } - - public Boolean getDefault() { - return isDefault; - } - - public void setDefault(Boolean aDefault) { - isDefault = aDefault; - } - - public Boolean getSimplified() { - return simplified; - } - - public void setSimplified(Boolean simplified) { - this.simplified = simplified; - } - - public String getExtraKeys() { - return extraKeys; - } - - public void setExtraKeys(String extraKeys) { - this.extraKeys = extraKeys; - } - - public Boolean getUseAsConfigCenter() { - return useAsConfigCenter; - } - - public void setUseAsConfigCenter(Boolean useAsConfigCenter) { - this.useAsConfigCenter = useAsConfigCenter; - } - - public Boolean getUseAsMetadataCenter() { - return useAsMetadataCenter; - } - - public void setUseAsMetadataCenter(Boolean useAsMetadataCenter) { - this.useAsMetadataCenter = useAsMetadataCenter; - } - - public String getAccepts() { - return accepts; - } - - public void setAccepts(String accepts) { - this.accepts = accepts; - } - - public Boolean getPreferred() { - return preferred; - } - - public void setPreferred(Boolean preferred) { - this.preferred = preferred; - } - - public Integer getWeight() { - return weight; - } - - public void setWeight(Integer weight) { - this.weight = weight; - } -} diff --git a/src/main/java/io/jboot/components/rpc/config/ServiceConfig.java b/src/main/java/io/jboot/components/rpc/config/ServiceConfig.java deleted file mode 100644 index c4ed0527..00000000 --- a/src/main/java/io/jboot/components/rpc/config/ServiceConfig.java +++ /dev/null @@ -1,299 +0,0 @@ -/** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt - *

- * 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 io.jboot.components.rpc.config; - -import java.io.Serializable; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/17 - * 文档:http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-service.html - */ -public class ServiceConfig implements Serializable { - - private static final long serialVersionUID = 1L; - - private String interfaceName; - private String className; - private String version; - private String group; - private String path; - private Integer delay; - private Integer timeout; - private Integer retries; - private Integer connections; - private String loadbalance; - private Boolean async; - private String stub; - private String mock; - private String token; - private String registry; - private String provider; - private String deprecated; - private String dynamic; - private String accesslog; - private String owner; - private String document; - private Integer weight; - private Integer executes; - private String proxy; - private String cluster; - private String filter; - private String listener; - private String protocol; - private String layer; - private Boolean register; - - public String getInterfaceName() { - return interfaceName; - } - - public void setInterfaceName(String interfaceName) { - this.interfaceName = interfaceName; - } - - public String getClassName() { - return className; - } - - public void setClassName(String className) { - this.className = className; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getGroup() { - return group; - } - - public void setGroup(String group) { - this.group = group; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - - public Integer getDelay() { - return delay; - } - - public void setDelay(Integer delay) { - this.delay = delay; - } - - public Integer getTimeout() { - return timeout; - } - - public void setTimeout(Integer timeout) { - this.timeout = timeout; - } - - public Integer getRetries() { - return retries; - } - - public void setRetries(Integer retries) { - this.retries = retries; - } - - public Integer getConnections() { - return connections; - } - - public void setConnections(Integer connections) { - this.connections = connections; - } - - public String getLoadbalance() { - return loadbalance; - } - - public void setLoadbalance(String loadbalance) { - this.loadbalance = loadbalance; - } - - public Boolean getAsync() { - return async; - } - - public void setAsync(Boolean async) { - this.async = async; - } - - public String getStub() { - return stub; - } - - public void setStub(String stub) { - this.stub = stub; - } - - public String getMock() { - return mock; - } - - public void setMock(String mock) { - this.mock = mock; - } - - public String getToken() { - return token; - } - - public void setToken(String token) { - this.token = token; - } - - public String getRegistry() { - return registry; - } - - public void setRegistry(String registry) { - this.registry = registry; - } - - public String getProvider() { - return provider; - } - - public void setProvider(String provider) { - this.provider = provider; - } - - public String getDeprecated() { - return deprecated; - } - - public void setDeprecated(String deprecated) { - this.deprecated = deprecated; - } - - public String getDynamic() { - return dynamic; - } - - public void setDynamic(String dynamic) { - this.dynamic = dynamic; - } - - public String getAccesslog() { - return accesslog; - } - - public void setAccesslog(String accesslog) { - this.accesslog = accesslog; - } - - public String getOwner() { - return owner; - } - - public void setOwner(String owner) { - this.owner = owner; - } - - public String getDocument() { - return document; - } - - public void setDocument(String document) { - this.document = document; - } - - public Integer getWeight() { - return weight; - } - - public void setWeight(Integer weight) { - this.weight = weight; - } - - public Integer getExecutes() { - return executes; - } - - public void setExecutes(Integer executes) { - this.executes = executes; - } - - public String getProxy() { - return proxy; - } - - public void setProxy(String proxy) { - this.proxy = proxy; - } - - public String getCluster() { - return cluster; - } - - public void setCluster(String cluster) { - this.cluster = cluster; - } - - public String getFilter() { - return filter; - } - - public void setFilter(String filter) { - this.filter = filter; - } - - public String getListener() { - return listener; - } - - public void setListener(String listener) { - this.listener = listener; - } - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - public String getLayer() { - return layer; - } - - public void setLayer(String layer) { - this.layer = layer; - } - - public Boolean getRegister() { - return register; - } - - public void setRegister(Boolean register) { - this.register = register; - } -} diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index 0641362b..684ac323 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -16,61 +16,177 @@ package io.jboot.components.rpc.dubbo; -import io.jboot.Jboot; -import io.jboot.components.rpc.config.ApplicationConfig; -import io.jboot.components.rpc.config.ConsumerConfig; +import io.jboot.app.config.JbootConfigManager; +import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.StrUtil; +import org.apache.dubbo.config.*; +import org.apache.dubbo.config.bootstrap.DubboBootstrap; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * @author michael yang (fuhai999@gmail.com) * @Date: 2020/3/19 */ - class DubboUtil { - - public static org.apache.dubbo.config.ApplicationConfig getApplicationConfig(){ - ApplicationConfig app = Jboot.config(ApplicationConfig.class,"jboot.rpc.application"); - - org.apache.dubbo.config.ApplicationConfig dubboConfig = new org.apache.dubbo.config.ApplicationConfig(); - - dubboConfig.setQosEnable(app.getQosEnable()); - dubboConfig.setQosAcceptForeignIp(app.getQosAcceptForeignIp()); - dubboConfig.setQosHost(app.getQosHost()); - dubboConfig.setQosPort(app.getQosPort()); - dubboConfig.setName(app.getName()); - dubboConfig.setArchitecture(app.getArchitecture()); - dubboConfig.setCompiler(app.getCompiler()); - dubboConfig.setDefault(app.getDefault()); - dubboConfig.setDumpDirectory(app.getDumpDirectory()); - dubboConfig.setEnvironment(app.getEnvironment()); - dubboConfig.setLogger(app.getLogger()); - dubboConfig.setMetadataType(app.getMetadataType()); - dubboConfig.setMonitor(app.getMonitor()); - dubboConfig.setOrganization(app.getOrganization()); - dubboConfig.setRegistryIds(app.getRegistryIds()); - dubboConfig.setRegisterConsumer(app.getRegisterConsumer()); - dubboConfig.setRepository(app.getRepository()); - dubboConfig.setShutwait(app.getShutwait()); - dubboConfig.setOwner(app.getOwner()); - dubboConfig.setVersion(app.getVersion()); - - return dubboConfig; - } - - - - public static org.apache.dubbo.config.ConsumerConfig getConsumerConfig(){ - ConsumerConfig app = Jboot.config(ConsumerConfig.class,"jboot.rpc.consumer"); - - org.apache.dubbo.config.ConsumerConfig dubboConfig = new org.apache.dubbo.config.ConsumerConfig(); - dubboConfig.setClient(app.getClient()); - dubboConfig.setCorethreads(app.getCorethreads()); - dubboConfig.setDefault(app.getDefault()); - dubboConfig.setQueues(app.getQueues()); - dubboConfig.setShareconnections(app.getShareconnections()); - dubboConfig.setThreadpool(app.getThreadpool()); - dubboConfig.setThreads(app.getThreads()); -// dubboConfig.setTimeout(app.gett); - - - return dubboConfig; +class DubboUtil { + + private static Map protocolConfigMap = new ConcurrentHashMap<>(); + private static Map registryConfigMap = new ConcurrentHashMap<>(); + private static Map providerConfigMap = new ConcurrentHashMap<>(); + private static Map consumerConfigMap = new ConcurrentHashMap<>(); + + + public static void initDubbo() { + DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance(); + + //application 配置 + ApplicationConfig applicationConfig = config(ApplicationConfig.class, "jboot.rpc.dubbo.application"); + dubboBootstrap.application(applicationConfig); + + + //ssl 配置 + SslConfig sslConfig = config(SslConfig.class, "jboot.rpc.dubbo.ssl"); + dubboBootstrap.ssl(sslConfig); + + + //monitor 配置 + MonitorConfig monitorConfig = config(MonitorConfig.class, "jboot.rpc.dubbo.monitor"); + dubboBootstrap.monitor(monitorConfig); + + + //monitor 配置 + MetricsConfig metricsConfig = config(MetricsConfig.class, "jboot.rpc.dubbo.metrics"); + dubboBootstrap.metrics(metricsConfig); + + + //module 配置 + ModuleConfig moduleConfig = config(ModuleConfig.class, "jboot.rpc.dubbo.module"); + dubboBootstrap.module(moduleConfig); + + + //protocol 配置 + Map protocolConfigs = configs(ProtocolConfig.class, "jboot.rpc.dubbo.protocol"); + if (protocolConfigs != null && !protocolConfigs.isEmpty()) { + protocolConfigMap.putAll(protocolConfigs); + if (protocolConfigs.size() == 1) { + dubboBootstrap.protocol(getDefault(protocolConfigs)); + } else { + dubboBootstrap.protocols((List) toList(protocolConfigs)); + } + } + + //registry 配置 + Map registryConfigs = configs(RegistryConfig.class, "jboot.rpc.dubbo.registry"); + if (registryConfigs != null && !registryConfigs.isEmpty()) { + registryConfigMap.putAll(registryConfigs); + if (registryConfigs.size() == 1) { + dubboBootstrap.registry(getDefault(registryConfigs)); + } else { + dubboBootstrap.registries((List) toList(registryConfigs)); + } + } + + + //provider 配置 + Map providerConfigs = configs(ProviderConfig.class, "jboot.rpc.dubbo.provider"); + if (providerConfigs != null && !providerConfigs.isEmpty()) { + providerConfigMap.putAll(providerConfigs); + if (providerConfigs.size() == 1) { + dubboBootstrap.provider(getDefault(providerConfigs)); + } else { + dubboBootstrap.providers((List) toList(providerConfigs)); + } + } + + + //consumer 配置 + Map consumerConfigs = configs(ConsumerConfig.class, "jboot.rpc.dubbo.consumer"); + if (consumerConfigs != null && !consumerConfigs.isEmpty()) { + consumerConfigMap.putAll(consumerConfigs); + if (consumerConfigs.size() == 1) { + dubboBootstrap.consumer(getDefault(consumerConfigs)); + } else { + dubboBootstrap.consumers((List) toList(consumerConfigs)); + } + } } + + + public static ProtocolConfig getProtocolConfig(String name) { + return protocolConfigMap.get(name); + } + + public static List getProtocolConfigs(String names) { + return filterMap(StrUtil.splitToSetByComma(names), protocolConfigMap); + } + + public static RegistryConfig getRegistryConfig(String name) { + return registryConfigMap.get(name); + } + + public static List getRegistryConfigs(String names) { + return filterMap(StrUtil.splitToSetByComma(names), registryConfigMap); + } + + public static ProviderConfig getProviderConfig(String name) { + return providerConfigMap.get(name); + } + + public static List getProviderConfigs(String names) { + return filterMap(StrUtil.splitToSetByComma(names), providerConfigMap); + } + + public static ConsumerConfig getConsumerConfig(String name) { + return consumerConfigMap.get(name); + } + + public static List getConsumerConfigs(String names) { + return filterMap(StrUtil.splitToSetByComma(names), consumerConfigMap); + } + + private static List filterMap(Set keys, Map map) { + if (keys == null || keys.isEmpty() || map == null || map.isEmpty()) { + return null; + } + List list = new ArrayList<>(); + for (String key : keys) { + T t = map.get(key); + if (t != null) { + list.add(t); + } + } + return list.isEmpty() ? null : list; + } + + + private static T config(Class clazz, String prefix) { + return JbootConfigManager.me().get(clazz, prefix, null); + } + + + private static Map configs(Class clazz, String prefix) { + return JbootConfigUtil.getConfigModels(clazz, prefix); + } + + private static T getDefault(Map map) { + return map.get("default"); + } + + private static List toList(Map map) { + List arrayList = new ArrayList<>(map.size()); + for (Map.Entry entry : map.entrySet()) { + AbstractConfig config = entry.getValue(); + if (StrUtil.isBlank(config.getId())) { + config.setId(entry.getKey()); + } + arrayList.add(config); + } + return arrayList; + } + + } diff --git a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java index 26297e48..2a5db0c2 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java @@ -21,7 +21,6 @@ import io.jboot.components.rpc.JbootrpcServiceConfig; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.StrUtil; import org.apache.dubbo.config.*; -import org.apache.dubbo.config.bootstrap.DubboBootstrap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -88,23 +87,7 @@ public class JbootDubborpc extends JbootrpcBase { @Override public void onInit() { - - - DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance(); - dubboBootstrap.application(DubboUtil.getApplicationConfig()) - .consumer(DubboUtil.getConsumerConfig()) -// .protocols() -// .registries() -// .configCenters() -// .consumers() -// .metrics() -// .module() -// .monitor() -// .ssl() -// .cache() - .start(); - - + DubboUtil.initDubbo(); } -- Gitee From 5433cb36a4ef3966ea3fd2d28e5337194891791d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 20 Mar 2020 15:04:27 +0800 Subject: [PATCH 0081/1965] v3.1.0 --- src/main/java/io/jboot/app/config/Utils.java | 16 +- .../io/jboot/components/rpc/Jbootrpc.java | 2 +- .../io/jboot/components/rpc/JbootrpcBase.java | 7 + .../jboot/components/rpc/JbootrpcConfig.java | 4 +- .../rpc/JbootrpcReferenceConfig.java | 284 ++++++++++ .../components/rpc/JbootrpcServiceConfig.java | 295 ++++++----- .../java/io/jboot/components/rpc/Utils.java | 89 ++++ .../components/rpc/annotation/RPCBean.java | 96 +++- .../components/rpc/annotation/RPCInject.java | 107 +++- .../jboot/components/rpc/dubbo/DubboUtil.java | 38 ++ .../components/rpc/dubbo/JbootDubborpc.java | 212 +------- .../rpc/dubbo/JbootDubborpcConfig.java | 485 ------------------ .../components/rpc/local/JbootLocalrpc.java | 3 +- .../components/rpc/motan/JbootMotanrpc.java | 105 +--- .../jboot/components/rpc/motan/MotanUtil.java | 65 +++ .../support/metric/JbootMetricManager.java | 2 +- .../java/io/jboot/utils/CollectionUtil.java | 40 ++ src/main/java/io/jboot/utils/FileScanner.java | 8 +- 18 files changed, 921 insertions(+), 937 deletions(-) create mode 100644 src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java create mode 100644 src/main/java/io/jboot/components/rpc/Utils.java delete mode 100644 src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpcConfig.java create mode 100644 src/main/java/io/jboot/components/rpc/motan/MotanUtil.java create mode 100644 src/main/java/io/jboot/utils/CollectionUtil.java diff --git a/src/main/java/io/jboot/app/config/Utils.java b/src/main/java/io/jboot/app/config/Utils.java index 026eadcf..c6e76a74 100644 --- a/src/main/java/io/jboot/app/config/Utils.java +++ b/src/main/java/io/jboot/app/config/Utils.java @@ -18,10 +18,7 @@ package io.jboot.app.config; import java.io.File; import java.io.UnsupportedEncodingException; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; +import java.lang.reflect.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -53,9 +50,12 @@ class Utils { Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.getName().startsWith("set") + && Character.isUpperCase(method.getName().charAt(3)) && method.getName().length() > 3 && method.getParameterCount() == 1 - && Character.isUpperCase(method.getName().charAt(3))) { + && Modifier.isPublic(method.getModifiers()) + && !Modifier.isStatic(method.getModifiers())) { + setMethods.add(method); } } @@ -145,7 +145,7 @@ class Utils { return null; } else { Map map = convertClass == ConcurrentHashMap.class ? new ConcurrentHashMap() : new HashMap(); - String[] strings = s.split(";"); + String[] strings = s.split(","); for (String kv : strings) { String[] keyValue = kv.split(":"); if (keyValue.length == 2) { @@ -157,7 +157,7 @@ class Utils { } else if (List.class.isAssignableFrom(convertClass)) { if (genericClassCheck(genericType)) { List list = LinkedList.class == convertClass ? new LinkedList() : new ArrayList(); - String[] strings = s.split(";"); + String[] strings = s.split(","); for (String s1 : strings) { if (s != null && s1.trim().length() > 0) { list.add(s1.trim()); @@ -170,7 +170,7 @@ class Utils { } else if (Set.class.isAssignableFrom(convertClass)) { if (genericClassCheck(genericType)) { Set set = LinkedHashSet.class == convertClass ? new LinkedHashSet() : new HashSet(); - String[] strings = s.split(";"); + String[] strings = s.split(","); for (String s1 : strings) { if (s != null && s1.trim().length() > 0) { set.add(s1.trim()); diff --git a/src/main/java/io/jboot/components/rpc/Jbootrpc.java b/src/main/java/io/jboot/components/rpc/Jbootrpc.java index c88f0692..1239e890 100644 --- a/src/main/java/io/jboot/components/rpc/Jbootrpc.java +++ b/src/main/java/io/jboot/components/rpc/Jbootrpc.java @@ -18,7 +18,7 @@ package io.jboot.components.rpc; public interface Jbootrpc { - public T serviceObtain(Class serviceClass, JbootrpcServiceConfig serviceConfig); + public T serviceObtain(Class serviceClass, JbootrpcReferenceConfig referenceConfig); public boolean serviceExport(Class interfaceClass, Object object, JbootrpcServiceConfig serviceConfig); diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java index 60d1583b..a018fab7 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java @@ -35,4 +35,11 @@ public abstract class JbootrpcBase implements Jbootrpc { public void onInited() { } + + protected String buildCacheKey(Class serviceClass, JbootrpcReferenceConfig config){ + StringBuilder sb = new StringBuilder(serviceClass.getName()); + return sb.append(":").append(config.getGroup()) + .append(":").append(config.getVersion()) + .toString(); + } } diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java index b407c9c6..d5965813 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java @@ -231,8 +231,8 @@ public class JbootrpcConfig { public void setDirectUrl(String directUrl) { - if (StrUtil.isNotBlank(directUrl) && directUrl.contains(";")) { - String[] directUrls = directUrl.split(";"); + if (StrUtil.isNotBlank(directUrl) && directUrl.contains(",")) { + String[] directUrls = directUrl.split(","); for (String url : directUrls) { if (StrUtil.isNotBlank(url)) { int indexOf = url.indexOf(":"); diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java new file mode 100644 index 00000000..a6cd44e4 --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java @@ -0,0 +1,284 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.rpc; + +import io.jboot.components.rpc.annotation.RPCInject; + +import java.io.Serializable; + +/** + * @author Michael Yang 杨福海 (fuhai999@gmail.com) + * @version V1.0 + * @Package io.jboot.components.rpc + */ +public class JbootrpcReferenceConfig implements Serializable { + + /** + * Service version, default value is empty string + */ + String version; + + /** + * Service group, default value is empty string + */ + String group; + + /** + * Service target URL for direct invocation, if this is specified, then registry center takes no effect. + */ + String url; + + + /** + * Whether to enable generic invocation, default value is false + */ + boolean generic; + + + /** + * Check if service provider is available during boot up, default value is true + */ + boolean check; + + + /** + * Service invocation retry times + *

+ * see Constants#DEFAULT_RETRIES + */ + int retries; + + + /** + * Load balance strategy, legal values include: random, roundrobin, leastactive + *

+ * see Constants#DEFAULT_LOADBALANCE + */ + String loadbalance; + + /** + * Whether to enable async invocation, default value is false + */ + boolean async; + + /** + * Maximum active requests allowed, default value is 0 + */ + int actives; + + + /** + * Timeout value for service invocation, default value is 0 + */ + int timeout; + + /** + * Application associated name + */ + String application; + + /** + * Module associated name + */ + String module; + + + /** + * Consumer associated name + */ + String consumer; + + /** + * Monitor associated name + */ + String monitor; + + /** + * Registry associated name + */ + String registry; + + /** + * The communication protocol of Dubbo Service + * + * @return the default value is "" + */ + String protocol; + + /** + * Service tag name + */ + String tag; + + /** + * The id + * + * @return default value is empty + */ + String id; + + + public JbootrpcReferenceConfig() { + } + + public JbootrpcReferenceConfig(RPCInject inject) { + Utils.appendAnnotation(RPCInject.class, inject, this); + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public boolean isGeneric() { + return generic; + } + + public void setGeneric(boolean generic) { + this.generic = generic; + } + + public boolean isCheck() { + return check; + } + + public void setCheck(boolean check) { + this.check = check; + } + + public int getRetries() { + return retries; + } + + public void setRetries(int retries) { + this.retries = retries; + } + + public String getLoadbalance() { + return loadbalance; + } + + public void setLoadbalance(String loadbalance) { + this.loadbalance = loadbalance; + } + + public boolean isAsync() { + return async; + } + + public void setAsync(boolean async) { + this.async = async; + } + + public int getActives() { + return actives; + } + + public void setActives(int actives) { + this.actives = actives; + } + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public String getApplication() { + return application; + } + + public void setApplication(String application) { + this.application = application; + } + + public String getModule() { + return module; + } + + public void setModule(String module) { + this.module = module; + } + + public String getConsumer() { + return consumer; + } + + public void setConsumer(String consumer) { + this.consumer = consumer; + } + + public String getMonitor() { + return monitor; + } + + public void setMonitor(String monitor) { + this.monitor = monitor; + } + + public String getRegistry() { + return registry; + } + + public void setRegistry(String registry) { + this.registry = registry; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java index 5cd7d48f..21b47fed 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java @@ -15,188 +15,197 @@ */ package io.jboot.components.rpc; -import io.jboot.Jboot; import io.jboot.components.rpc.annotation.RPCBean; -import io.jboot.components.rpc.annotation.RPCInject; -import io.jboot.utils.AnnotationUtil; import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.core.rpc + * @Package io.jboot.components.rpc */ public class JbootrpcServiceConfig implements Serializable { + /** + * Service version, default value is empty string + */ + String version; - private int port = 0; - private String group; - private String version; - private Integer timeout; - private Integer retries; - private Integer actives; - private String loadbalance; - private Boolean async; - private Boolean check; + /** + * Service group, default value is empty string + */ + String group; + /** + * Service path, default value is empty string + */ + String path; - private String proxy; - private String filter; + /** + * Whether to export service, default value is true + */ + boolean export; - // 用于扩展,用户通过SPI扩展获取自定义的 Service的时候, - // 若以字段不满足,此时通过 params 自行扩展 - private Map params; + /** + * Service token, default value is false + */ + String token; - private static JbootrpcConfig defaultConfig = Jboot.config(JbootrpcConfig.class); + /** + * Whether the service is deprecated, default value is false + */ + boolean deprecated; - public JbootrpcServiceConfig() { - this.port = defaultConfig.getDefaultPort(); - this.group = defaultConfig.getDefaultGroup(); - this.version = defaultConfig.getDefaultVersion(); - this.timeout = defaultConfig.getRequestTimeOut(); - this.retries = defaultConfig.getRetries(); - this.proxy = defaultConfig.getProxy(); - this.filter = defaultConfig.getFilter(); - } + /** + * Whether to register the service to register center, default value is true + */ + boolean register; - public JbootrpcServiceConfig(RPCInject inject) { - this(); + /** + * Service weight value, default value is 0 + */ + int weight; - int port = AnnotationUtil.getInt(inject.port(), -1); - int timeout = AnnotationUtil.getInt(inject.timeout(), -1); - int retries = AnnotationUtil.getInt(inject.retries(), -1); - int actives = AnnotationUtil.getInt(inject.actives(), -1); + /** + * Service doc, default value is "" + */ + String document; - String group = AnnotationUtil.get(inject.group()); - String version = AnnotationUtil.get(inject.version()); - String loadbalance = AnnotationUtil.get(inject.loadbalance()); - Boolean async = AnnotationUtil.getBool(inject.async()); - Boolean check = AnnotationUtil.getBool(inject.check()); + /** + * Service invocation retry times + */ + int retries; + /** + * Load balance strategy, legal values include: random, roundrobin, leastactive + */ + String loadbalance; - if (port >= 0) { - this.port = port; - } - if (retries >= 0) { - this.retries = retries; - } + /** + * Application spring bean name + */ + String application; - if (actives >= 0) { - this.actives = actives; - } + /** + * Module spring bean name + */ + String module; - if (timeout >= 0) { - this.timeout = timeout; - } + /** + * Provider spring bean name + */ + String provider; + /** + * Protocol spring bean names + */ + String protocol; - if (group != null) { - this.group = group; - } + /** + * Monitor spring bean name + */ + String monitor; - if (version != null) { - this.version = version; - } + /** + * Registry spring bean name + */ + String registry; - if (loadbalance != null) { - this.loadbalance = loadbalance; - } + /** + * Service tag name + */ + String tag; - if (async != null) { - this.async = async; - } - if (check != null) { - this.check = check; - } + public JbootrpcServiceConfig() { } public JbootrpcServiceConfig(RPCBean bean) { - this(); - - int port = AnnotationUtil.getInt(bean.port(), -1); - int timeout = AnnotationUtil.getInt(bean.timeout(), -1); - int actives = AnnotationUtil.getInt(bean.actives(), -1); + Utils.appendAnnotation(RPCBean.class,bean,this); + } - String group = AnnotationUtil.get(bean.group()); - String version = AnnotationUtil.get(bean.version()); + public String getVersion() { + return version; + } - if (port >= 0) { - this.port = port; - } + public void setVersion(String version) { + this.version = version; + } - if (actives >= 0) { - this.actives = actives; - } + public String getGroup() { + return group; + } - if (timeout >= 0) { - this.timeout = timeout; - } + public void setGroup(String group) { + this.group = group; + } - if (group != null) { - this.group = group; - } + public String getPath() { + return path; + } - if (version != null) { - this.version = version; - } + public void setPath(String path) { + this.path = path; + } + public boolean isExport() { + return export; } + public void setExport(boolean export) { + this.export = export; + } - public String getGroup() { - return group; + public String getToken() { + return token; } - public void setGroup(String group) { - this.group = group; + public void setToken(String token) { + this.token = token; } - public String getVersion() { - return version; + public boolean isDeprecated() { + return deprecated; } - public void setVersion(String version) { - this.version = version; + public void setDeprecated(boolean deprecated) { + this.deprecated = deprecated; } - public int getPort() { - return port; + public boolean isRegister() { + return register; } - public void setPort(int port) { - this.port = port; + public void setRegister(boolean register) { + this.register = register; } - public Integer getTimeout() { - return timeout; + public int getWeight() { + return weight; } - public void setTimeout(Integer timeout) { - this.timeout = timeout; + public void setWeight(int weight) { + this.weight = weight; } - public Integer getRetries() { - return retries; + public String getDocument() { + return document; } - public void setRetries(Integer retries) { - this.retries = retries; + public void setDocument(String document) { + this.document = document; } - public Integer getActives() { - return actives; + public int getRetries() { + return retries; } - public void setActives(Integer actives) { - this.actives = actives; + public void setRetries(int retries) { + this.retries = retries; } public String getLoadbalance() { @@ -207,51 +216,59 @@ public class JbootrpcServiceConfig implements Serializable { this.loadbalance = loadbalance; } - public Boolean getAsync() { - return async; + public String getApplication() { + return application; } - public void setAsync(Boolean async) { - this.async = async; + public void setApplication(String application) { + this.application = application; } - public Boolean getCheck() { - return check; + public String getModule() { + return module; } - public void setCheck(Boolean check) { - this.check = check; + public void setModule(String module) { + this.module = module; } - public Map getParams() { - return params; + public String getProvider() { + return provider; } - public void setParams(Map params) { - this.params = params; + public void setProvider(String provider) { + this.provider = provider; } - public void addParam(Object key, Object value) { - if (params == null) { - params = new HashMap(); - } + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getMonitor() { + return monitor; + } - params.put(key, value); + public void setMonitor(String monitor) { + this.monitor = monitor; } - public String getProxy() { - return proxy; + public String getRegistry() { + return registry; } - public void setProxy(String proxy) { - this.proxy = proxy; + public void setRegistry(String registry) { + this.registry = registry; } - public String getFilter() { - return filter; + public String getTag() { + return tag; } - public void setFilter(String filter) { - this.filter = filter; + public void setTag(String tag) { + this.tag = tag; } } diff --git a/src/main/java/io/jboot/components/rpc/Utils.java b/src/main/java/io/jboot/components/rpc/Utils.java new file mode 100644 index 00000000..852e670b --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/Utils.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.rpc; + +import io.jboot.utils.CollectionUtil; +import org.apache.dubbo.common.utils.StringUtils; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Map; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/20 + */ +public class Utils { + + public static void appendAnnotation(Class annotationClass, Object annotation,Object appendTo) { + Method[] methods = annotationClass.getMethods(); + for (Method method : methods) { + if (method.getDeclaringClass() != Object.class + && method.getReturnType() != void.class + && method.getParameterTypes().length == 0 + && Modifier.isPublic(method.getModifiers()) + && !Modifier.isStatic(method.getModifiers())) { + try { + String property = method.getName(); + if ("interfaceClass".equals(property) || "interfaceName".equals(property)) { + property = "interface"; + } + String setter = "set" + property.substring(0, 1).toUpperCase() + property.substring(1); + Object value = method.invoke(annotation); + if (value != null && !value.equals(method.getDefaultValue())) { + Class parameterType = getBoxedClass(method.getReturnType()); + if ("filter".equals(property) || "listener".equals(property)) { + parameterType = String.class; + value = StringUtils.join((String[]) value, ","); + } else if ("parameters".equals(property)) { + parameterType = Map.class; + value = CollectionUtil.string2Map((String) value); + } + try { + Method setterMethod = appendTo.getClass().getMethod(setter, parameterType); + setterMethod.invoke(appendTo, value); + } catch (NoSuchMethodException e) { + // ignore + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + } + + private static Class getBoxedClass(Class c) { + if (c == int.class) { + c = Integer.class; + } else if (c == boolean.class) { + c = Boolean.class; + } else if (c == long.class) { + c = Long.class; + } else if (c == float.class) { + c = Float.class; + } else if (c == double.class) { + c = Double.class; + } else if (c == char.class) { + c = Character.class; + } else if (c == byte.class) { + c = Byte.class; + } else if (c == short.class) { + c = Short.class; + } + return c; + } +} diff --git a/src/main/java/io/jboot/components/rpc/annotation/RPCBean.java b/src/main/java/io/jboot/components/rpc/annotation/RPCBean.java index 71b173bb..263ce9df 100644 --- a/src/main/java/io/jboot/components/rpc/annotation/RPCBean.java +++ b/src/main/java/io/jboot/components/rpc/annotation/RPCBean.java @@ -22,15 +22,101 @@ import java.lang.annotation.*; @Target({ElementType.TYPE}) public @interface RPCBean { - String port() default ""; + /** + * Service version, default value is empty string + */ + String version() default ""; - String timeout() default ""; + /** + * Service group, default value is empty string + */ + String group() default ""; - String actives() default ""; + /** + * Service path, default value is empty string + */ + String path() default ""; - String group() default ""; + /** + * Whether to export service, default value is true + */ + boolean export() default true; + + /** + * Service token, default value is false + */ + String token() default ""; + + /** + * Whether the service is deprecated, default value is false + */ + boolean deprecated() default false; + + + /** + * Whether to register the service to register center, default value is true + */ + boolean register() default true; + + /** + * Service weight value, default value is 0 + */ + int weight() default 0; + + /** + * Service doc, default value is "" + */ + String document() default ""; + + + /** + * Service invocation retry times + * + */ + int retries() default 2; + + /** + * Load balance strategy, legal values include: random, roundrobin, leastactive + * + */ + String loadbalance() default "random"; + + + /** + * Application spring bean name + */ + String application() default ""; + + /** + * Module spring bean name + */ + String module() default ""; + + /** + * Provider spring bean name + */ + String provider() default ""; + + /** + * Protocol spring bean names + */ + String[] protocol() default {}; + + /** + * Monitor spring bean name + */ + String monitor() default ""; + + /** + * Registry spring bean name + */ + String[] registry() default {}; + + /** + * Service tag name + */ + String tag() default ""; - String version() default ""; //当一个Service类实现对个接口的时候,可以通过这个排除不暴露某个实现接口 Class[] exclude() default Void.class; diff --git a/src/main/java/io/jboot/components/rpc/annotation/RPCInject.java b/src/main/java/io/jboot/components/rpc/annotation/RPCInject.java index 88e65439..84e8fde2 100644 --- a/src/main/java/io/jboot/components/rpc/annotation/RPCInject.java +++ b/src/main/java/io/jboot/components/rpc/annotation/RPCInject.java @@ -22,22 +22,111 @@ import java.lang.annotation.*; @Target({ElementType.TYPE, ElementType.FIELD}) public @interface RPCInject { - String port() default ""; - String timeout() default ""; + /** + * Service version, default value is empty string + */ + String version() default ""; - String retries() default ""; + /** + * Service group, default value is empty string + */ + String group() default ""; - String actives() default ""; + /** + * Service target URL for direct invocation, if this is specified, then registry center takes no effect. + */ + String url() default ""; - String group() default ""; - String version() default ""; + /** + * Whether to enable generic invocation, default value is false + */ + boolean generic() default false; + + + /** + * Check if service provider is available during boot up, default value is true + */ + boolean check() default true; + + + /** + * Service invocation retry times + * + * see Constants#DEFAULT_RETRIES + */ + int retries() default 2; + + + /** + * Load balance strategy, legal values include: random, roundrobin, leastactive + * + * see Constants#DEFAULT_LOADBALANCE + */ + String loadbalance() default "random"; + + /** + * Whether to enable async invocation, default value is false + */ + boolean async() default false; + + /** + * Maximum active requests allowed, default value is 0 + */ + int actives() default 0; + + + /** + * Timeout value for service invocation, default value is 0 + */ + int timeout() default 0; + + /** + * Application associated name + */ + String application() default ""; + + /** + * Module associated name + */ + String module() default ""; + + + /** + * Consumer associated name + */ + String consumer() default ""; + + /** + * Monitor associated name + */ + String monitor() default ""; + + /** + * Registry associated name + */ + String[] registry() default {}; - String loadbalance() default ""; + /** + * The communication protocol of Dubbo Service + * + * @return the default value is "" + * @since 2.6.6 + */ + String protocol() default ""; - String async() default "false"; + /** + * Service tag name + */ + String tag() default ""; - String check() default "false"; + /** + * The id + * + * @return default value is empty + * @since 2.7.3 + */ + String id() default ""; } \ No newline at end of file diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index 684ac323..a1432d33 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -18,10 +18,14 @@ package io.jboot.components.rpc.dubbo; import io.jboot.app.config.JbootConfigManager; import io.jboot.app.config.JbootConfigUtil; +import io.jboot.components.rpc.JbootrpcReferenceConfig; +import io.jboot.components.rpc.JbootrpcServiceConfig; import io.jboot.utils.StrUtil; import org.apache.dubbo.config.*; import org.apache.dubbo.config.bootstrap.DubboBootstrap; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -148,6 +152,40 @@ class DubboUtil { return filterMap(StrUtil.splitToSetByComma(names), consumerConfigMap); } + + public static ReferenceConfig toReferenceConfig(JbootrpcReferenceConfig rc){ + ReferenceConfig referenceConfig = new ReferenceConfig(); + Field[] fields = rc.getClass().getDeclaredFields(); + for (Field field : fields){ + try { + Method method = ReferenceConfig.class.getDeclaredMethod("set"+StrUtil.firstCharToUpperCase(field.getName()),field.getType()); + field.setAccessible(true); + method.invoke(referenceConfig,field.get(rc)); + } catch (Exception e) { + // ignore + } + } + return referenceConfig; + } + + + public static ServiceConfig toServiceConfig(JbootrpcServiceConfig sc){ + ServiceConfig serviceConfig = new ServiceConfig(); + Field[] fields = sc.getClass().getDeclaredFields(); + for (Field field : fields){ + try { + Method method = ServiceConfig.class.getDeclaredMethod("set"+StrUtil.firstCharToUpperCase(field.getName()),field.getType()); + field.setAccessible(true); + method.invoke(serviceConfig,field.get(sc)); + } catch (Exception e) { + // ignore + } + } + return serviceConfig; + } + + + private static List filterMap(Set keys, Map map) { if (keys == null || keys.isEmpty() || map == null || map.isEmpty()) { return null; diff --git a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java index 2a5db0c2..4ae23f33 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java @@ -15,76 +15,19 @@ */ package io.jboot.components.rpc.dubbo; -import io.jboot.Jboot; import io.jboot.components.rpc.JbootrpcBase; +import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; -import io.jboot.exception.JbootIllegalConfigException; -import io.jboot.utils.StrUtil; -import org.apache.dubbo.config.*; +import org.apache.dubbo.config.ReferenceConfig; +import org.apache.dubbo.config.ServiceConfig; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -/** - * dubbo 官方宣布开始维护了 - * 开始添加dubbo的支持 - */ public class JbootDubborpc extends JbootrpcBase { private static final Map singletons = new ConcurrentHashMap<>(); - private JbootDubborpcConfig dubboConfig; - private RegistryConfig registryConfig; - - - - - public JbootDubborpc() { - dubboConfig = Jboot.config(JbootDubborpcConfig.class); - - registryConfig = new RegistryConfig(); - registryConfig.setCheck(getConfig().isRegistryCheck()); - - if (getConfig().getRegistryFile() != null) { - registryConfig.setFile(getConfig().getRegistryFile()); - } - - /** - * 注册中心的调用模式 - */ - if (getConfig().isRegistryCallMode()) { - - registryConfig.setProtocol(getConfig().getRegistryType()); - registryConfig.setAddress(getConfig().getRegistryAddress()); - registryConfig.setUsername(getConfig().getRegistryUserName()); - registryConfig.setPassword(getConfig().getRegistryPassword()); - } - /** - * 直连模式 - */ - else if (getConfig().isDirectCallMode()) { - registryConfig.setAddress(RegistryConfig.NO_AVAILABLE); - } - } - - - private ApplicationConfig createApplicationConfig(String group) { - ApplicationConfig applicationConfig = new ApplicationConfig(); - applicationConfig.setName(group); - if (dubboConfig.getQosEnable() != null && dubboConfig.getQosEnable()) { - applicationConfig.setQosEnable(true); - applicationConfig.setQosPort(dubboConfig.getQosPort()); - applicationConfig.setQosAcceptForeignIp(dubboConfig.getQosAcceptForeignIp()); - } else { - applicationConfig.setQosEnable(false); - } - - return applicationConfig; - } - - - - @Override public void onInit() { DubboUtil.initDubbo(); @@ -92,157 +35,36 @@ public class JbootDubborpc extends JbootrpcBase { @Override - public T serviceObtain(Class serviceClass, JbootrpcServiceConfig serviceConfig) { + public T serviceObtain(Class serviceClass, JbootrpcReferenceConfig config) { - String key = String.format("%s:%s:%s", serviceClass.getName(), serviceConfig.getGroup(), serviceConfig.getVersion()); + String key = buildCacheKey(serviceClass,config); T object = (T) singletons.get(key); - if (object != null) { - return object; - } - - - // 注意:ReferenceConfig为重对象,内部封装了与注册中心的连接,以及与服务提供方的连接 - // 引用远程服务 - // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏 - ReferenceConfig reference = new ReferenceConfig(); - reference.setApplication(createApplicationConfig(serviceConfig.getGroup())); - reference.setInterface(serviceClass); - reference.setCheck(getConfig().isConsumerCheck()); - - initReference(reference, serviceConfig); - - /** - * 注册中心的调用模式 - */ - if (getConfig().isRegistryCallMode()) { - reference.setRegistry(registryConfig); // 多个注册中心可以用setRegistries() - } - - /** - * 直连调用模式 - */ - else if (getConfig().isDirectCallMode()) { - String url = getConfig().getDirectUrl(serviceConfig.getGroup()); - url = StrUtil.isBlank(url) ? getConfig().getDirectUrl() : url; - if (StrUtil.isBlank(url)) { - throw new JbootIllegalConfigException("directUrl must not be blank if you use direct call mode,please config jboot.rpc.directUrl value"); + if (object == null) { + synchronized (this){ + if (singletons.get(key) == null) { + ReferenceConfig reference = DubboUtil.toReferenceConfig(config); + object = reference.get(); + if (object != null){ + singletons.put(key,object); + } + } } - reference.setUrl(url); } - - - // 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用 - object = reference.get(); - - if (object != null) { - singletons.put(key, object); - } - return object; } - @Override - public boolean serviceExport(Class interfaceClass, Object object, JbootrpcServiceConfig serviceConfig) { - - ProtocolConfig protocolConfig = dubboConfig.newProtocolConfig(); - - if (protocolConfig.getHost() == null && getConfig().getHost() != null) { - protocolConfig.setHost(getConfig().getHost()); - } - - if (protocolConfig.getSerialization() == null && getConfig().getSerialization() != null) { - protocolConfig.setSerialization(getConfig().getSerialization()); - } - protocolConfig.setPort(serviceConfig.getPort()); + @Override + public boolean serviceExport(Class interfaceClass, Object object, JbootrpcServiceConfig config) { - //此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏 - ServiceConfig service = new ServiceConfig(); - service.setApplication(createApplicationConfig(serviceConfig.getGroup())); - service.setRegistry(registryConfig); // 多个注册中心可以用setRegistries() - service.setProtocol(protocolConfig); // 多个协议可以用setProtocols() -// service.setProtocolIds(); + ServiceConfig service = DubboUtil.toServiceConfig(config); service.setInterface(interfaceClass); service.setRef((T) object); - - initService(service, serviceConfig); - - - // 暴露及注册服务 service.export(); return true; } - - private static void initReference(ReferenceConfig reference, JbootrpcServiceConfig config) { - reference.setGroup(config.getGroup()); - reference.setVersion(config.getVersion()); - reference.setTimeout(config.getTimeout()); - - if (config.getRetries() != null) { - reference.setRetries(config.getRetries()); - } - - if (config.getActives() != null) { - reference.setActives(config.getActives()); - } - - if (config.getLoadbalance() != null) { - reference.setLoadbalance(config.getLoadbalance()); - } - - if (config.getAsync() != null) { - reference.setAsync(config.getAsync()); - } - - if (config.getCheck() != null) { - reference.setCheck(config.getCheck()); - } - - if (StrUtil.isNotBlank(config.getProxy())) { - reference.setProxy(config.getProxy()); - } - - - if (StrUtil.isNotBlank(config.getFilter())) { - reference.setFilter(config.getFilter()); - } - } - - - private static void initService(ServiceConfig service, JbootrpcServiceConfig config) { - - service.setGroup(config.getGroup()); - service.setVersion(config.getVersion()); - service.setTimeout(config.getTimeout()); - - if (config.getRetries() != null) { - service.setRetries(config.getRetries()); - } - - if (config.getActives() != null) { - service.setActives(config.getActives()); - } - - if (config.getLoadbalance() != null) { - service.setLoadbalance(config.getLoadbalance()); - } - - if (config.getAsync() != null) { - service.setAsync(config.getAsync()); - } - - if (StrUtil.isNotBlank(config.getProxy())) { - service.setProxy(config.getProxy()); - } - - if (StrUtil.isNotBlank(config.getFilter())) { - service.setFilter(config.getFilter()); - } - } - - } diff --git a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpcConfig.java b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpcConfig.java deleted file mode 100644 index bf0c5bd1..00000000 --- a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpcConfig.java +++ /dev/null @@ -1,485 +0,0 @@ -/** - * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.components.rpc.dubbo; - -import io.jboot.app.config.annotation.ConfigModel; -import org.apache.dubbo.config.ProtocolConfig; - - -@ConfigModel(prefix = "jboot.rpc.dubbo") -public class JbootDubborpcConfig { - - - private String protocolName = "dubbo"; //default is dubbo - private String protocolServer = "netty"; //default is netty - private String protocolContextPath; - private String protocolTransporter; - private Integer protocolThreads; - - private Boolean qosEnable = false; - private Integer qosPort; - private Boolean qosAcceptForeignIp; - - - private String protocolHost; - - // service port - private Integer protocolPort; - - // context path - private String protocolContextpath; - - // thread pool - private String protocolThreadpool; - - // thread pool size (fixed size) - - // IO thread pool size (fixed size) - private Integer protocolIothreads; - - // thread pool's queue length - private Integer protocolQueues; - - // max acceptable connections - private Integer protocolAccepts; - - // protocol codec - private String protocolCodec; - - // serialization - private String protocolSerialization; - - // charset - private String protocolCharset; - - // payload max length - private Integer protocolPayload; - - // buffer size - private Integer protocolBuffer; - - // heartbeat interval - private Integer protocolHeartbeat; - - // access log - private String protocolAccesslog; - - - // how information is exchanged - private String protocolExchanger; - - // thread dispatch mode - private String protocolDispatcher; - - // networker - private String protocolNetworker; - - // client impl - private String protocolClient; - - // supported telnet commands, separated with comma. - private String protocolTelnet; - - // command line prompt - private String protocolPrompt; - - // status check - private String protocolStatus; - - // whether to register - private Boolean protocolRegister; - - // parameters - // 是否长连接 - // TODO add this to provider config - private Boolean protocolKeepAlive; - - // TODO add this to provider config - private String protocolOptimizer; - - private String protocolExtension; - - // if it's default - private Boolean protocolIsDefault; - - - public String getProtocolName() { - return protocolName; - } - - public void setProtocolName(String protocolName) { - this.protocolName = protocolName; - } - - public String getProtocolServer() { - return protocolServer; - } - - public void setProtocolServer(String protocolServer) { - this.protocolServer = protocolServer; - } - - public String getProtocolContextPath() { - return protocolContextPath; - } - - public void setProtocolContextPath(String protocolContextPath) { - this.protocolContextPath = protocolContextPath; - } - - public String getProtocolTransporter() { - return protocolTransporter; - } - - public void setProtocolTransporter(String protocolTransporter) { - this.protocolTransporter = protocolTransporter; - } - - public int getProtocolThreads() { - return protocolThreads; - } - - public void setProtocolThreads(int protocolThreads) { - this.protocolThreads = protocolThreads; - } - - public Boolean getQosEnable() { - return qosEnable; - } - - public void setQosEnable(Boolean qosEnable) { - this.qosEnable = qosEnable; - } - - public Integer getQosPort() { - return qosPort; - } - - public void setQosPort(Integer qosPort) { - this.qosPort = qosPort; - } - - public Boolean getQosAcceptForeignIp() { - return qosAcceptForeignIp; - } - - public void setQosAcceptForeignIp(Boolean qosAcceptForeignIp) { - this.qosAcceptForeignIp = qosAcceptForeignIp; - } - - public String getProtocolHost() { - return protocolHost; - } - - public void setProtocolHost(String protocolHost) { - this.protocolHost = protocolHost; - } - - public Integer getProtocolPort() { - return protocolPort; - } - - public void setProtocolPort(Integer protocolPort) { - this.protocolPort = protocolPort; - } - - public String getProtocolContextpath() { - return protocolContextpath; - } - - public void setProtocolContextpath(String protocolContextpath) { - this.protocolContextpath = protocolContextpath; - } - - public String getProtocolThreadpool() { - return protocolThreadpool; - } - - public void setProtocolThreadpool(String protocolThreadpool) { - this.protocolThreadpool = protocolThreadpool; - } - - public Integer getProtocolIothreads() { - return protocolIothreads; - } - - public void setProtocolIothreads(Integer protocolIothreads) { - this.protocolIothreads = protocolIothreads; - } - - public Integer getProtocolQueues() { - return protocolQueues; - } - - public void setProtocolQueues(Integer protocolQueues) { - this.protocolQueues = protocolQueues; - } - - public Integer getProtocolAccepts() { - return protocolAccepts; - } - - public void setProtocolAccepts(Integer protocolAccepts) { - this.protocolAccepts = protocolAccepts; - } - - public String getProtocolCodec() { - return protocolCodec; - } - - public void setProtocolCodec(String protocolCodec) { - this.protocolCodec = protocolCodec; - } - - public String getProtocolSerialization() { - return protocolSerialization; - } - - public void setProtocolSerialization(String protocolSerialization) { - this.protocolSerialization = protocolSerialization; - } - - public String getProtocolCharset() { - return protocolCharset; - } - - public void setProtocolCharset(String protocolCharset) { - this.protocolCharset = protocolCharset; - } - - public Integer getProtocolPayload() { - return protocolPayload; - } - - public void setProtocolPayload(Integer protocolPayload) { - this.protocolPayload = protocolPayload; - } - - public Integer getProtocolBuffer() { - return protocolBuffer; - } - - public void setProtocolBuffer(Integer protocolBuffer) { - this.protocolBuffer = protocolBuffer; - } - - public Integer getProtocolHeartbeat() { - return protocolHeartbeat; - } - - public void setProtocolHeartbeat(Integer protocolHeartbeat) { - this.protocolHeartbeat = protocolHeartbeat; - } - - public String getProtocolAccesslog() { - return protocolAccesslog; - } - - public void setProtocolAccesslog(String protocolAccesslog) { - this.protocolAccesslog = protocolAccesslog; - } - - public String getProtocolExchanger() { - return protocolExchanger; - } - - public void setProtocolExchanger(String protocolExchanger) { - this.protocolExchanger = protocolExchanger; - } - - public String getProtocolDispatcher() { - return protocolDispatcher; - } - - public void setProtocolDispatcher(String protocolDispatcher) { - this.protocolDispatcher = protocolDispatcher; - } - - public String getProtocolNetworker() { - return protocolNetworker; - } - - public void setProtocolNetworker(String protocolNetworker) { - this.protocolNetworker = protocolNetworker; - } - - public String getProtocolClient() { - return protocolClient; - } - - public void setProtocolClient(String protocolClient) { - this.protocolClient = protocolClient; - } - - public String getProtocolTelnet() { - return protocolTelnet; - } - - public void setProtocolTelnet(String protocolTelnet) { - this.protocolTelnet = protocolTelnet; - } - - public String getProtocolPrompt() { - return protocolPrompt; - } - - public void setProtocolPrompt(String protocolPrompt) { - this.protocolPrompt = protocolPrompt; - } - - public String getProtocolStatus() { - return protocolStatus; - } - - public void setProtocolStatus(String protocolStatus) { - this.protocolStatus = protocolStatus; - } - - public Boolean getProtocolRegister() { - return protocolRegister; - } - - public void setProtocolRegister(Boolean protocolRegister) { - this.protocolRegister = protocolRegister; - } - - public Boolean getProtocolKeepAlive() { - return protocolKeepAlive; - } - - public void setProtocolKeepAlive(Boolean protocolKeepAlive) { - this.protocolKeepAlive = protocolKeepAlive; - } - - public String getProtocolOptimizer() { - return protocolOptimizer; - } - - public void setProtocolOptimizer(String protocolOptimizer) { - this.protocolOptimizer = protocolOptimizer; - } - - public String getProtocolExtension() { - return protocolExtension; - } - - public void setProtocolExtension(String protocolExtension) { - this.protocolExtension = protocolExtension; - } - - public Boolean getProtocolIsDefault() { - return protocolIsDefault; - } - - public void setProtocolIsDefault(Boolean protocolIsDefault) { - this.protocolIsDefault = protocolIsDefault; - } - - public ProtocolConfig newProtocolConfig() { - - ProtocolConfig config = new ProtocolConfig(); - - if (this.protocolDispatcher != null) { - config.setDispatcher(this.protocolDispatcher); - } - if (this.protocolIsDefault != null) { - config.setDefault(this.protocolIsDefault); - } - if (this.protocolClient != null) { - config.setClient(this.protocolClient); - } - if (this.protocolCharset != null) { - config.setCharset(this.protocolCharset); - } - if (this.protocolAccepts != null) { - config.setAccepts(this.protocolAccepts); - } - if (this.protocolAccesslog != null) { - config.setAccesslog(this.protocolAccesslog); - } - if (this.protocolBuffer != null) { - config.setBuffer(this.protocolBuffer); - } - if (this.protocolCodec != null) { - config.setCodec(this.protocolCodec); - } - if (this.protocolContextpath != null) { - config.setContextpath(this.protocolContextpath); - } - if (this.protocolExchanger != null) { - config.setExchanger(this.protocolExchanger); - } - if (this.protocolExtension != null) { - config.setExtension(this.protocolExtension); - } - if (this.protocolHeartbeat != null) { - config.setHeartbeat(this.protocolHeartbeat); - } - if (this.protocolHost != null) { - config.setHost(this.protocolHost); - } - if (this.protocolIothreads != null) { - config.setIothreads(this.protocolIothreads); - } - if (this.protocolKeepAlive != null) { - config.setKeepAlive(this.protocolKeepAlive); - } - if (this.protocolName != null) { - config.setName(this.protocolName); - } - if (this.protocolNetworker != null) { - config.setNetworker(this.protocolNetworker); - } - if (this.protocolOptimizer != null) { - config.setOptimizer(this.protocolOptimizer); - } - if (this.protocolPayload != null) { - config.setPayload(this.protocolPayload); - } - if (this.protocolPort != null) { - config.setPort(this.protocolPort); - } - if (this.protocolPrompt != null) { - config.setPrompt(this.protocolPrompt); - } - if (this.protocolQueues != null) { - config.setQueues(this.protocolQueues); - } - if (this.protocolRegister != null) { - config.setRegister(this.protocolRegister); - } - if (this.protocolSerialization != null) { - config.setSerialization(this.protocolSerialization); - } - if (this.protocolServer != null) { - config.setServer(this.protocolServer); - } - if (this.protocolStatus != null) { - config.setStatus(this.protocolStatus); - } - if (this.protocolTelnet != null) { - config.setTelnet(this.protocolTelnet); - } - if (this.protocolThreadpool != null) { - config.setThreadpool(this.protocolThreadpool); - } - if (this.protocolThreads != null) { - config.setThreads(this.protocolThreads); - } - - return config; - } -} diff --git a/src/main/java/io/jboot/components/rpc/local/JbootLocalrpc.java b/src/main/java/io/jboot/components/rpc/local/JbootLocalrpc.java index c68b0d35..8cc1b067 100644 --- a/src/main/java/io/jboot/components/rpc/local/JbootLocalrpc.java +++ b/src/main/java/io/jboot/components/rpc/local/JbootLocalrpc.java @@ -18,6 +18,7 @@ package io.jboot.components.rpc.local; import com.jfinal.aop.Aop; import com.jfinal.aop.AopManager; import io.jboot.components.rpc.JbootrpcBase; +import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; import io.jboot.utils.ClassUtil; @@ -25,7 +26,7 @@ import io.jboot.utils.ClassUtil; public class JbootLocalrpc extends JbootrpcBase { @Override - public T serviceObtain(Class serviceClass, JbootrpcServiceConfig serviceConfig) { + public T serviceObtain(Class serviceClass, JbootrpcReferenceConfig referenceConfig) { return Aop.get(serviceClass); } diff --git a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java index bfea034d..19dc3491 100644 --- a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java +++ b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java @@ -16,11 +16,14 @@ package io.jboot.components.rpc.motan; import com.weibo.api.motan.common.MotanConstants; -import com.weibo.api.motan.config.*; +import com.weibo.api.motan.config.ProtocolConfig; +import com.weibo.api.motan.config.RefererConfig; +import com.weibo.api.motan.config.RegistryConfig; +import com.weibo.api.motan.config.ServiceConfig; import com.weibo.api.motan.util.MotanSwitcherUtil; import io.jboot.components.rpc.JbootrpcBase; +import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; -import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.StrUtil; import java.util.Map; @@ -73,80 +76,39 @@ public class JbootMotanrpc extends JbootrpcBase { @Override - public T serviceObtain(Class serviceClass, JbootrpcServiceConfig serviceConfig) { + public T serviceObtain(Class serviceClass, JbootrpcReferenceConfig config) { - String key = String.format("%s:%s:%s", serviceClass.getName(), serviceConfig.getGroup(), serviceConfig.getVersion()); + String key = buildCacheKey(serviceClass,config); T object = (T) singletons.get(key); - if (object != null) { - return object; - } - - RefererConfig refererConfig = new RefererConfig(); - - // 设置接口及实现类 - refererConfig.setProtocol(protocolConfig); - refererConfig.setInterface(serviceClass); - refererConfig.setCheck(String.valueOf(getConfig().isConsumerCheck())); - - initInterface(refererConfig, serviceConfig); - - /** - * 注册中心模式 - */ - if (getConfig().isRegistryCallMode()) { - refererConfig.setRegistry(registryConfig); - } - - /** - * 直连模式 - */ - else if (getConfig().isDirectCallMode()) { - String url = getConfig().getDirectUrl(serviceConfig.getGroup()); - url = StrUtil.isBlank(url) ? getConfig().getDirectUrl() : url; - if (StrUtil.isBlank(url)) { - throw new JbootIllegalConfigException("directUrl must not be blank if you use direct call mode,please config jboot.rpc.directUrl value"); + if (object == null) { + synchronized (this){ + if (singletons.get(key) == null) { + RefererConfig reference = MotanUtil.toRefererConfig(config); + object = reference.getRef(); + if (object != null){ + singletons.put(key,object); + } + } } - refererConfig.setDirectUrl(url); - } - - - object = refererConfig.getRef(); - - if (object != null) { - singletons.put(key, object); } return object; } @Override - public boolean serviceExport(Class interfaceClass, Object object, JbootrpcServiceConfig serviceConfig) { + public boolean serviceExport(Class interfaceClass, Object object, JbootrpcServiceConfig config) { synchronized (this) { MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, false); - ServiceConfig motanServiceConfig = new ServiceConfig(); - motanServiceConfig.setRegistry(registryConfig); - motanServiceConfig.setProtocol(protocolConfig); + ServiceConfig motanServiceConfig = MotanUtil.toServiceConfig(config); // 设置接口及实现类 motanServiceConfig.setInterface(interfaceClass); motanServiceConfig.setRef((T) object); - if (StrUtil.isNotBlank(getConfig().getHost())) { - motanServiceConfig.setHost(getConfig().getHost()); - } - - - motanServiceConfig.setShareChannel(true); - motanServiceConfig.setExport(String.format("motan:%s", serviceConfig.getPort())); - motanServiceConfig.setCheck(String.valueOf(getConfig().isProviderCheck())); - - initInterface(motanServiceConfig, serviceConfig); - - motanServiceConfig.export(); MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true); @@ -156,37 +118,6 @@ public class JbootMotanrpc extends JbootrpcBase { } - private static void initInterface(AbstractInterfaceConfig interfaceConfig, JbootrpcServiceConfig config) { - - interfaceConfig.setGroup(config.getGroup()); - interfaceConfig.setVersion(config.getVersion()); - interfaceConfig.setRequestTimeout(config.getTimeout()); - - - if (config.getActives() != null) { - interfaceConfig.setActives(config.getActives()); - } - - if (config.getAsync() != null) { - interfaceConfig.setAsync(config.getAsync()); - } - - if (config.getRetries() != null) { - interfaceConfig.setRetries(config.getRetries()); - } - - if (config.getCheck() != null) { - interfaceConfig.setCheck(config.getCheck().toString()); - } - - if (StrUtil.isNotBlank(config.getProxy())) { - interfaceConfig.setProxy(config.getProxy()); - } - - if (StrUtil.isNotBlank(config.getFilter())) { - interfaceConfig.setFilter(config.getFilter()); - } - } } diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java new file mode 100644 index 00000000..adaef3ac --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.rpc.motan; + +import com.weibo.api.motan.config.RefererConfig; +import com.weibo.api.motan.config.ServiceConfig; +import io.jboot.components.rpc.JbootrpcReferenceConfig; +import io.jboot.components.rpc.JbootrpcServiceConfig; +import io.jboot.utils.StrUtil; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/20 + */ +public class MotanUtil { + + + public static RefererConfig toRefererConfig(JbootrpcReferenceConfig rc){ + RefererConfig referenceConfig = new RefererConfig(); + Field[] fields = rc.getClass().getDeclaredFields(); + for (Field field : fields){ + try { + Method method = RefererConfig.class.getDeclaredMethod("set"+ StrUtil.firstCharToUpperCase(field.getName()),field.getType()); + field.setAccessible(true); + method.invoke(referenceConfig,field.get(rc)); + } catch (Exception e) { + // ignore + } + } + return referenceConfig; + } + + + + public static ServiceConfig toServiceConfig(JbootrpcServiceConfig sc){ + ServiceConfig serviceConfig = new ServiceConfig(); + Field[] fields = sc.getClass().getDeclaredFields(); + for (Field field : fields){ + try { + Method method = ServiceConfig.class.getDeclaredMethod("set"+StrUtil.firstCharToUpperCase(field.getName()),field.getType()); + field.setAccessible(true); + method.invoke(serviceConfig,field.get(sc)); + } catch (Exception e) { + // ignore + } + } + return serviceConfig; + } +} diff --git a/src/main/java/io/jboot/support/metric/JbootMetricManager.java b/src/main/java/io/jboot/support/metric/JbootMetricManager.java index ede0aa39..39e6e78b 100644 --- a/src/main/java/io/jboot/support/metric/JbootMetricManager.java +++ b/src/main/java/io/jboot/support/metric/JbootMetricManager.java @@ -90,7 +90,7 @@ public class JbootMetricManager { } List reporters = new ArrayList<>(); - String[] repoterStrings = repoterString.split(";"); + String[] repoterStrings = repoterString.split(","); for (String repoterName : repoterStrings) { JbootMetricReporter reporter = getReporterByName(repoterName); if (reporter != null) { diff --git a/src/main/java/io/jboot/utils/CollectionUtil.java b/src/main/java/io/jboot/utils/CollectionUtil.java new file mode 100644 index 00000000..e6c5bde5 --- /dev/null +++ b/src/main/java/io/jboot/utils/CollectionUtil.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.utils; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/20 + */ +public class CollectionUtil { + + public static Map string2Map(String s){ + Map map = new HashMap(); + String[] strings = s.split(","); + for (String kv : strings) { + if (kv != null && kv.contains(":")) { + String[] keyValue = kv.split(":"); + if (keyValue.length == 2) { + map.put(keyValue[0], keyValue[1]); + } + } + } + return map; + } +} diff --git a/src/main/java/io/jboot/utils/FileScanner.java b/src/main/java/io/jboot/utils/FileScanner.java index 015036e8..a5602b00 100644 --- a/src/main/java/io/jboot/utils/FileScanner.java +++ b/src/main/java/io/jboot/utils/FileScanner.java @@ -54,12 +54,12 @@ public abstract class FileScanner { public abstract void onChange(String action, String file); protected void working() { - if (!rootDir.contains(";")) { - scan(new File(rootDir)); + if (!rootDir.contains(",")) { + scan(new File(rootDir.trim())); } else { - String[] paths = rootDir.split(";"); + String[] paths = rootDir.split(","); for (String path : paths) { - scan(new File(path)); + scan(new File(path.trim())); } } -- Gitee From 70a5509176501dad35ef082d1934c3083599b273 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 20 Mar 2020 15:45:44 +0800 Subject: [PATCH 0082/1965] v3.1.0 --- .../io/jboot/components/rpc/JbootrpcBase.java | 25 +- .../jboot/components/rpc/JbootrpcConfig.java | 272 +----------------- .../components/rpc/dubbo/JbootDubborpc.java | 28 +- .../components/rpc/local/JbootLocalrpc.java | 7 +- .../components/rpc/motan/JbootMotanrpc.java | 71 +---- 5 files changed, 33 insertions(+), 370 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java index a018fab7..e7dc1f03 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java @@ -16,16 +16,33 @@ package io.jboot.components.rpc; -import io.jboot.app.config.JbootConfigManager; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public abstract class JbootrpcBase implements Jbootrpc { - private final JbootrpcConfig config = JbootConfigManager.me().get(JbootrpcConfig.class); + protected static final Map objectCache = new ConcurrentHashMap<>(); - public JbootrpcConfig getConfig() { - return config; + + @Override + public T serviceObtain(Class serviceClass, JbootrpcReferenceConfig config) { + String key = buildCacheKey(serviceClass,config); + T object = (T) objectCache.get(key); + if (object == null) { + synchronized (this){ + if (objectCache.get(key) == null) { + object = onServiceCreate(serviceClass,config); + if (object != null){ + objectCache.put(key,object); + } + } + } + } + return object; } + public abstract T onServiceCreate(Class serviceClass, JbootrpcReferenceConfig config); + @Override public void onInit() { diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java index d5965813..79704b99 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java @@ -18,97 +18,20 @@ package io.jboot.components.rpc; import io.jboot.app.config.annotation.ConfigModel; import io.jboot.utils.StrUtil; -import java.io.IOException; -import java.net.ServerSocket; -import java.util.Map; - @ConfigModel(prefix = "jboot.rpc") public class JbootrpcConfig { public static final String TYPE_DUBBO = "dubbo"; - public static final String TYPE_GRPC = "grpc"; public static final String TYPE_MOTAN = "motan"; - public static final String TYPE_THRIFT = "thrift"; public static final String TYPE_LOCAL = "local"; - public static final String REGISTRY_TYPE_CONSUL = "consul"; - public static final String REGISTRY_TYPE_ZOOKEEPER = "zookeeper"; - - - /** - * RPC的调用模式:registry 注册中心,direct直连模式 - */ - public static final String CALL_MODE_REGISTRY = "registry"; - public static final String CALL_MODE_DIRECT = "direct"; - - private String type; - private String callMode = CALL_MODE_REGISTRY; - - private int requestTimeOut = 5000; - - - /** - * 注册中心的相关调用 - */ - private String registryType = REGISTRY_TYPE_CONSUL; - private String registryAddress = "127.0.0.1:8500"; - private String registryName = "jboot"; - private String registryUserName; - private String registryPassword; - private String registryFile; - - /** - * 启动检查 - */ - private boolean registryCheck = false; - private boolean consumerCheck = false; - private boolean providerCheck = false; - - - /** - * 直连模式的时候,配置的url - */ - private String directUrl; - - /** - * 直连模式下,一个 APP 可能需要链接到多个服务 - * 每个服务有自己的 group 以及对应的 url - */ - private Map directUrlsMap; - - - /** - * 对外暴露服务的相关配置 - */ - private String host; //当有多个IP的时候可以指定某个IP - private Integer defaultPort = 0; //0 为随机可用端口 - private String defaultGroup = "jboot"; - private String defaultVersion = "1.0"; - private String proxy; - - //多个过滤器请用英文逗号(,)隔开,默认添加opentracing过滤器,用于对rpc分布式调用的追踪 - private String filter; - - private String serialization; - - //重试次数,不配置默认使用框架默认配置 motan和dubbo可能不一样 - private Integer retries; - - //本地自动暴露 @RPCBean 的service + //本地自动暴露 @RPCBean 的 service private boolean autoExportEnable = true; - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - public String getType() { return type; } @@ -117,199 +40,6 @@ public class JbootrpcConfig { this.type = type; } - public int getRequestTimeOut() { - return requestTimeOut; - } - - public void setRequestTimeOut(int requestTimeOut) { - this.requestTimeOut = requestTimeOut; - } - - public String getRegistryType() { - return registryType; - } - - public void setRegistryType(String registryType) { - this.registryType = registryType; - } - - public String getRegistryAddress() { - return registryAddress; - } - - public void setRegistryAddress(String registryAddress) { - this.registryAddress = registryAddress; - } - - public String getRegistryName() { - return registryName; - } - - public void setRegistryName(String registryName) { - this.registryName = registryName; - } - - public Integer getDefaultPort() { - if (defaultPort == 0) { - ServerSocket serverSocket = null; - try { - serverSocket = new ServerSocket(0); //.getLocalPort(); - return serverSocket.getLocalPort(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - if (serverSocket != null) { - try { - serverSocket.close(); - } catch (IOException e) { - } - } - } - } - return defaultPort; - } - - public void setDefaultPort(int defaultPort) { - this.defaultPort = defaultPort; - } - - public String getDefaultGroup() { - return defaultGroup; - } - - public void setDefaultGroup(String defaultGroup) { - this.defaultGroup = defaultGroup; - } - - public String getDefaultVersion() { - return defaultVersion; - } - - public void setDefaultVersion(String defaultVersion) { - this.defaultVersion = defaultVersion; - } - - public String getRegistryUserName() { - return registryUserName; - } - - public void setRegistryUserName(String registryUserName) { - this.registryUserName = registryUserName; - } - - public String getRegistryPassword() { - return registryPassword; - } - - public void setRegistryPassword(String registryPassword) { - this.registryPassword = registryPassword; - } - - public String getRegistryFile() { - return registryFile; - } - - public void setRegistryFile(String registryFile) { - this.registryFile = registryFile; - } - - public String getCallMode() { - return callMode; - } - - public void setCallMode(String callMode) { - this.callMode = callMode; - } - - public String getDirectUrl() { - return directUrl; - } - - public String getDirectUrl(String group) { - return directUrlsMap == null ? null : directUrlsMap.get(group); - } - - - public void setDirectUrl(String directUrl) { - if (StrUtil.isNotBlank(directUrl) && directUrl.contains(",")) { - String[] directUrls = directUrl.split(","); - for (String url : directUrls) { - if (StrUtil.isNotBlank(url)) { - int indexOf = url.indexOf(":"); - String group = url.substring(0, indexOf); - String urlStr = url.substring(indexOf + 1); - directUrlsMap.put(group, urlStr); - } - } - } else { - this.directUrl = directUrl; - } - } - - public boolean isDirectCallMode() { - return CALL_MODE_DIRECT.equals(getCallMode()); - } - - public boolean isRegistryCallMode() { - return CALL_MODE_REGISTRY.equals(getCallMode()); - } - - public String getProxy() { - return proxy; - } - - public void setProxy(String proxy) { - this.proxy = proxy; - } - - public String getFilter() { - return filter; - } - - public void setFilter(String filter) { - this.filter = filter; - } - - public boolean isRegistryCheck() { - return registryCheck; - } - - public void setRegistryCheck(boolean registryCheck) { - this.registryCheck = registryCheck; - } - - public boolean isConsumerCheck() { - return consumerCheck; - } - - public void setConsumerCheck(boolean consumerCheck) { - this.consumerCheck = consumerCheck; - } - - public boolean isProviderCheck() { - return providerCheck; - } - - public void setProviderCheck(boolean providerCheck) { - this.providerCheck = providerCheck; - } - - public String getSerialization() { - return serialization; - } - - public void setSerialization(String serialization) { - this.serialization = serialization; - } - - public Integer getRetries() { - return retries; - } - - public void setRetries(Integer retries) { - this.retries = retries; - } - public boolean isAutoExportEnable() { return autoExportEnable; } diff --git a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java index 4ae23f33..621d41b5 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java @@ -21,13 +21,8 @@ import io.jboot.components.rpc.JbootrpcServiceConfig; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.ServiceConfig; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - public class JbootDubborpc extends JbootrpcBase { - private static final Map singletons = new ConcurrentHashMap<>(); - @Override public void onInit() { DubboUtil.initDubbo(); @@ -35,35 +30,18 @@ public class JbootDubborpc extends JbootrpcBase { @Override - public T serviceObtain(Class serviceClass, JbootrpcReferenceConfig config) { - - String key = buildCacheKey(serviceClass,config); - - T object = (T) singletons.get(key); - if (object == null) { - synchronized (this){ - if (singletons.get(key) == null) { - ReferenceConfig reference = DubboUtil.toReferenceConfig(config); - object = reference.get(); - if (object != null){ - singletons.put(key,object); - } - } - } - } - return object; + public T onServiceCreate(Class serviceClass, JbootrpcReferenceConfig config) { + ReferenceConfig reference = DubboUtil.toReferenceConfig(config); + return reference.get(); } - @Override public boolean serviceExport(Class interfaceClass, Object object, JbootrpcServiceConfig config) { - ServiceConfig service = DubboUtil.toServiceConfig(config); service.setInterface(interfaceClass); service.setRef((T) object); service.export(); - return true; } diff --git a/src/main/java/io/jboot/components/rpc/local/JbootLocalrpc.java b/src/main/java/io/jboot/components/rpc/local/JbootLocalrpc.java index 8cc1b067..028bb8a4 100644 --- a/src/main/java/io/jboot/components/rpc/local/JbootLocalrpc.java +++ b/src/main/java/io/jboot/components/rpc/local/JbootLocalrpc.java @@ -31,10 +31,13 @@ public class JbootLocalrpc extends JbootrpcBase { } @Override - public boolean serviceExport(Class interfaceClass, Object object, JbootrpcServiceConfig serviceConfig) { + public T onServiceCreate(Class serviceClass, JbootrpcReferenceConfig config) { + return null; + } + @Override + public boolean serviceExport(Class interfaceClass, Object object, JbootrpcServiceConfig serviceConfig) { AopManager.me().addMapping(interfaceClass, ClassUtil.getUsefulClass(object.getClass())); - return true; } } diff --git a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java index 19dc3491..a9757964 100644 --- a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java +++ b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java @@ -16,83 +16,21 @@ package io.jboot.components.rpc.motan; import com.weibo.api.motan.common.MotanConstants; -import com.weibo.api.motan.config.ProtocolConfig; import com.weibo.api.motan.config.RefererConfig; -import com.weibo.api.motan.config.RegistryConfig; import com.weibo.api.motan.config.ServiceConfig; import com.weibo.api.motan.util.MotanSwitcherUtil; import io.jboot.components.rpc.JbootrpcBase; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; -import io.jboot.utils.StrUtil; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; public class JbootMotanrpc extends JbootrpcBase { - private RegistryConfig registryConfig; - private ProtocolConfig protocolConfig; - - private static final Map singletons = new ConcurrentHashMap<>(); - - public JbootMotanrpc() { - registryConfig = new RegistryConfig(); - registryConfig.setCheck(String.valueOf(getConfig().isRegistryCheck())); - - /** - * 注册中心的调用模式 - */ - if (getConfig().isRegistryCallMode()) { - - registryConfig.setRegProtocol(getConfig().getRegistryType()); - registryConfig.setAddress(getConfig().getRegistryAddress()); - registryConfig.setName(getConfig().getRegistryName()); - } - - /** - * 直连模式 - */ - else if (getConfig().isDirectCallMode()) { - registryConfig.setRegProtocol("local"); - } - - - protocolConfig = new ProtocolConfig(); - protocolConfig.setId("motan"); - protocolConfig.setName("motan"); - - if (StrUtil.isNotBlank(getConfig().getProxy())) { - protocolConfig.setFilter(getConfig().getProxy()); - } - - if (StrUtil.isNotBlank(getConfig().getSerialization())) { - protocolConfig.setSerialization(getConfig().getSerialization()); - } - - } - - @Override - public T serviceObtain(Class serviceClass, JbootrpcReferenceConfig config) { - - String key = buildCacheKey(serviceClass,config); - - T object = (T) singletons.get(key); - if (object == null) { - synchronized (this){ - if (singletons.get(key) == null) { - RefererConfig reference = MotanUtil.toRefererConfig(config); - object = reference.getRef(); - if (object != null){ - singletons.put(key,object); - } - } - } - } - return object; + public T onServiceCreate(Class serviceClass, JbootrpcReferenceConfig config) { + RefererConfig reference = MotanUtil.toRefererConfig(config); + return reference.getRef(); } @@ -104,11 +42,8 @@ public class JbootMotanrpc extends JbootrpcBase { MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, false); ServiceConfig motanServiceConfig = MotanUtil.toServiceConfig(config); - - // 设置接口及实现类 motanServiceConfig.setInterface(interfaceClass); motanServiceConfig.setRef((T) object); - motanServiceConfig.export(); MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true); -- Gitee From c82ba5abd4d684eccf69b39c3b53e506b1a48f50 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 20 Mar 2020 16:11:57 +0800 Subject: [PATCH 0083/1965] v3.1.0 --- .../jboot/components/rpc/dubbo/DubboUtil.java | 59 ++++--------------- .../components/rpc/motan/JbootMotanrpc.java | 4 ++ .../jboot/components/rpc/motan/MotanUtil.java | 59 ++++++++++++++++++- 3 files changed, 74 insertions(+), 48 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index a1432d33..cd133279 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -29,7 +29,6 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** @@ -43,6 +42,9 @@ class DubboUtil { private static Map providerConfigMap = new ConcurrentHashMap<>(); private static Map consumerConfigMap = new ConcurrentHashMap<>(); + private static Map methodConfigMap = new ConcurrentHashMap<>(); + private static Map argumentConfigMap = new ConcurrentHashMap<>(); + public static void initDubbo() { DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance(); @@ -117,39 +119,19 @@ class DubboUtil { dubboBootstrap.consumers((List) toList(consumerConfigs)); } } - } - - - public static ProtocolConfig getProtocolConfig(String name) { - return protocolConfigMap.get(name); - } - - public static List getProtocolConfigs(String names) { - return filterMap(StrUtil.splitToSetByComma(names), protocolConfigMap); - } - - public static RegistryConfig getRegistryConfig(String name) { - return registryConfigMap.get(name); - } - public static List getRegistryConfigs(String names) { - return filterMap(StrUtil.splitToSetByComma(names), registryConfigMap); - } - - public static ProviderConfig getProviderConfig(String name) { - return providerConfigMap.get(name); - } - - public static List getProviderConfigs(String names) { - return filterMap(StrUtil.splitToSetByComma(names), providerConfigMap); - } + //methodConfig 配置 + Map methodConfigs = configs(MethodConfig.class, "jboot.rpc.dubbo.method"); + if (methodConfigs != null && !methodConfigs.isEmpty()) { + methodConfigMap.putAll(methodConfigs); + } - public static ConsumerConfig getConsumerConfig(String name) { - return consumerConfigMap.get(name); - } - public static List getConsumerConfigs(String names) { - return filterMap(StrUtil.splitToSetByComma(names), consumerConfigMap); + //argumentConfig 配置 + Map argumentConfigs = configs(ArgumentConfig.class, "jboot.rpc.dubbo.argument"); + if (argumentConfigs != null && !argumentConfigs.isEmpty()) { + argumentConfigMap.putAll(argumentConfigs); + } } @@ -186,21 +168,6 @@ class DubboUtil { - private static List filterMap(Set keys, Map map) { - if (keys == null || keys.isEmpty() || map == null || map.isEmpty()) { - return null; - } - List list = new ArrayList<>(); - for (String key : keys) { - T t = map.get(key); - if (t != null) { - list.add(t); - } - } - return list.isEmpty() ? null : list; - } - - private static T config(Class clazz, String prefix) { return JbootConfigManager.me().get(clazz, prefix, null); } diff --git a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java index a9757964..fb74002c 100644 --- a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java +++ b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java @@ -26,6 +26,10 @@ import io.jboot.components.rpc.JbootrpcServiceConfig; public class JbootMotanrpc extends JbootrpcBase { + @Override + public void onInit() { + MotanUtil.initMotan(); + } @Override public T onServiceCreate(Class serviceClass, JbootrpcReferenceConfig config) { diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java index adaef3ac..32b74a42 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java @@ -15,14 +15,17 @@ */ package io.jboot.components.rpc.motan; -import com.weibo.api.motan.config.RefererConfig; -import com.weibo.api.motan.config.ServiceConfig; +import com.weibo.api.motan.config.*; +import io.jboot.app.config.JbootConfigManager; +import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; import io.jboot.utils.StrUtil; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * @author michael yang (fuhai999@gmail.com) @@ -30,6 +33,48 @@ import java.lang.reflect.Method; */ public class MotanUtil { + private static Map protocolConfigMap = new ConcurrentHashMap<>(); + private static Map registryConfigMap = new ConcurrentHashMap<>(); + private static Map baseRefererConfigMap = new ConcurrentHashMap<>(); + private static Map baseServiceConfigMap = new ConcurrentHashMap<>(); + private static Map methodConfigMap = new ConcurrentHashMap<>(); + + + public static void initMotan() { + + //protocol 配置 + Map protocolConfigs = configs(ProtocolConfig.class, "jboot.rpc.motan.protocol"); + if (protocolConfigs != null && !protocolConfigs.isEmpty()) { + protocolConfigMap.putAll(protocolConfigs); + } + + //registry 配置 + Map registryConfigs = configs(RegistryConfig.class, "jboot.rpc.motan.registry"); + if (registryConfigs != null && !registryConfigs.isEmpty()) { + registryConfigMap.putAll(registryConfigs); + } + + //baseReferer 配置 + Map refererConfigs = configs(RefererConfig.class, "jboot.rpc.motan.referer"); + if (refererConfigs != null && !refererConfigs.isEmpty()) { + baseRefererConfigMap.putAll(refererConfigs); + } + + //baseService 配置 + Map serviceConfigs = configs(ServiceConfig.class, "jboot.rpc.motan.service"); + if (serviceConfigs != null && !serviceConfigs.isEmpty()) { + baseServiceConfigMap.putAll(serviceConfigs); + } + + //methodConfig 配置 + Map methodConfigs = configs(MethodConfig.class, "jboot.rpc.motan.method"); + if (methodConfigs != null && !methodConfigs.isEmpty()) { + methodConfigMap.putAll(methodConfigs); + } + + + } + public static RefererConfig toRefererConfig(JbootrpcReferenceConfig rc){ RefererConfig referenceConfig = new RefererConfig(); @@ -62,4 +107,14 @@ public class MotanUtil { } return serviceConfig; } + + + private static T config(Class clazz, String prefix) { + return JbootConfigManager.me().get(clazz, prefix, null); + } + + + private static Map configs(Class clazz, String prefix) { + return JbootConfigUtil.getConfigModels(clazz, prefix); + } } -- Gitee From b533e25446ac6a9419d506687ddc912ac37b1a2b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 20 Mar 2020 16:41:52 +0800 Subject: [PATCH 0084/1965] v3.1.0 --- .../io/jboot/components/cache/JbootCache.java | 7 ++- .../cache/caffeine/CaffeineCacheImpl.java | 42 +++++++++++++----- .../cache/caredis/JbootCaredisCacheImpl.java | 41 ++++++++++-------- .../cache/ehcache/JbootEhcacheImpl.java | 16 ++++--- .../cache/ehredis/JbootEhredisCacheImpl.java | 31 +++++++------ .../cache/redis/JbootRedisCacheImpl.java | 43 ++++++++++++------- 6 files changed, 118 insertions(+), 62 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/JbootCache.java b/src/main/java/io/jboot/components/cache/JbootCache.java index 9dd5a279..7cc4d4a1 100644 --- a/src/main/java/io/jboot/components/cache/JbootCache.java +++ b/src/main/java/io/jboot/components/cache/JbootCache.java @@ -22,6 +22,7 @@ import java.util.List; public interface JbootCache extends com.jfinal.plugin.activerecord.cache.ICache { + @Override public T get(String cacheName, Object key); @@ -30,7 +31,6 @@ public interface JbootCache extends com.jfinal.plugin.activerecord.cache.ICache public void put(String cacheName, Object key, Object value, int liveSeconds); - public List getKeys(String cacheName); @Override public void remove(String cacheName, Object key); @@ -50,4 +50,9 @@ public interface JbootCache extends com.jfinal.plugin.activerecord.cache.ICache public void refresh(String cacheName); + + public List getNames(); + + public List getKeys(String cacheName); + } diff --git a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java index 153b6e2a..4ddabbb9 100644 --- a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java @@ -76,22 +76,22 @@ public class CaffeineCacheImpl extends JbootCacheBase { putData(getCache(cacheName), key, new CaffeineCacheObject(value, liveSeconds)); } - @Override - public List getKeys(String cacheName) { - Cache cache = getCacheOnly(cacheName); - return cache == null ? null : new ArrayList(cache.asMap().keySet()); - } + @Override public void remove(String cacheName, Object key) { Cache cache = getCacheOnly(cacheName); - if (cache != null) cache.invalidate(key); + if (cache != null) { + cache.invalidate(key); + } } @Override public void removeAll(String cacheName) { Cache cache = getCacheOnly(cacheName); - if (cache != null) cache.invalidateAll(); + if (cache != null) { + cache.invalidateAll(); + } } @Override @@ -129,10 +129,14 @@ public class CaffeineCacheImpl extends JbootCacheBase { @Override public Integer getTtl(String cacheName, Object key) { Cache cache = getCacheOnly(cacheName); - if (cache == null) return null; + if (cache == null) { + return null; + } CaffeineCacheObject data = (CaffeineCacheObject) cache.getIfPresent(key); - if (data == null) return null; + if (data == null) { + return null; + } return data.getTtl(); } @@ -140,16 +144,32 @@ public class CaffeineCacheImpl extends JbootCacheBase { @Override public void setTtl(String cacheName, Object key, int seconds) { Cache cache = getCacheOnly(cacheName); - if (cache == null) return; + if (cache == null) { + return; + } CaffeineCacheObject data = (CaffeineCacheObject) cache.getIfPresent(key); - if (data == null) return; + if (data == null) { + return; + } data.setLiveSeconds(seconds); putData(cache, key, data); } + @Override + public List getNames() { + return new ArrayList(cacheMap.keySet()); + } + + @Override + public List getKeys(String cacheName) { + Cache cache = getCacheOnly(cacheName); + return cache == null ? null : new ArrayList(cache.asMap().keySet()); + } + + protected void putData(Cache cache, Object key, CaffeineCacheObject value) { value.setCachetime(System.currentTimeMillis()); cache.put(key, value); diff --git a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java index 6743e765..50a20b24 100644 --- a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java @@ -69,23 +69,6 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { } - @Override - public List getKeys(String cacheName) { - List list = keysCache.getIfPresent(cacheName); - if (list == null) { - list = redisCacheImpl.getKeys(cacheName); - if (list == null) { - synchronized (cacheName.intern()) { - if (list == null) { - list = new ArrayList(); - } - } - } - keysCache.put(cacheName, list); - } - return list; - } - @Override public T get(String cacheName, Object key) { T value = caffeineCacheImpl.get(cacheName, key); @@ -213,6 +196,30 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { } + @Override + public List getNames() { + return redisCacheImpl.getNames(); + } + + + @Override + public List getKeys(String cacheName) { + List list = keysCache.getIfPresent(cacheName); + if (list == null) { + list = redisCacheImpl.getKeys(cacheName); + if (list == null) { + synchronized (cacheName.intern()) { + if (list == null) { + list = new ArrayList(); + } + } + } + keysCache.put(cacheName, list); + } + return list; + } + + private void publishMessage(int action, String cacheName, Object key) { clearKeysCache(cacheName); JbootCaredisMessage message = new JbootCaredisMessage(clientId, action, cacheName, key); diff --git a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java index 6a5799f9..1baff90e 100644 --- a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java @@ -26,6 +26,7 @@ import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; import net.sf.ehcache.event.CacheEventListener; +import java.util.Arrays; import java.util.List; @@ -69,7 +70,6 @@ public class JbootEhcacheImpl extends JbootCacheBase { synchronized (locker) { cache = cacheManager.getCache(cacheName); if (cache == null) { - log.warn("Could not find cache config [" + cacheName + "], using default."); cacheManager.addCacheIfAbsent(cacheName); cache = cacheManager.getCache(cacheName); if (cacheEventListener != null) { @@ -81,10 +81,6 @@ public class JbootEhcacheImpl extends JbootCacheBase { return cache; } - @Override - public List getKeys(String cacheName) { - return getOrAddCache(cacheName).getKeys(); - } @Override public T get(String cacheName, Object key) { @@ -159,6 +155,16 @@ public class JbootEhcacheImpl extends JbootCacheBase { getOrAddCache(cacheName).put(element); } + @Override + public List getNames() { + return Arrays.asList(cacheManager.getCacheNames()); + } + + @Override + public List getKeys(String cacheName) { + return getOrAddCache(cacheName).getKeys(); + } + public CacheManager getCacheManager() { diff --git a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java index 93dbc9ca..d0a30d8e 100644 --- a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java @@ -75,19 +75,6 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL } - @Override - public List getKeys(String cacheName) { - List list = keysCache.getIfPresent(cacheName); - if (list == null) { - list = redisCacheImpl.getKeys(cacheName); - if (list == null) { - list = new ArrayList(); - } - keysCache.put(cacheName, list); - } - return list; - } - @Override public T get(String cacheName, Object key) { T value = ehcacheImpl.get(cacheName, key); @@ -225,6 +212,24 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL publishMessage(JbootEhredisMessage.ACTION_REMOVE_ALL, cacheName, null); } + @Override + public List getNames() { + return redisCacheImpl.getNames(); + } + + @Override + public List getKeys(String cacheName) { + List list = keysCache.getIfPresent(cacheName); + if (list == null) { + list = redisCacheImpl.getKeys(cacheName); + if (list == null) { + list = new ArrayList(); + } + keysCache.put(cacheName, list); + } + return list; + } + public void onMessage(String channel, Object obj) { diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java index af5528e6..19a8054a 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java @@ -31,6 +31,8 @@ public class JbootRedisCacheImpl extends JbootCacheBase { private JbootRedis redis; + private static final String redisCacheNamesKey = "jboot_cache_names"; + public JbootRedisCacheImpl() { JbootRedisCacheConfig redisConfig = Jboot.config(JbootRedisCacheConfig.class); @@ -58,6 +60,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { return; } redis.set(buildKey(cacheName, key), value); + redis.sadd(redisCacheNamesKey, cacheName); } @Override @@ -72,19 +75,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { } redis.setex(buildKey(cacheName, key), liveSeconds, value); - } - - @Override - public List getKeys(String cacheName) { - Set keyset = redis.keys(cacheName + ":*"); - if (keyset == null || keyset.size() == 0) { - return null; - } - List keys = new ArrayList<>(keyset); - for (int i = 0; i < keys.size(); i++) { - keys.set(i, keys.get(i).substring(cacheName.length() + 3)); - } - return keys; + redis.sadd(redisCacheNamesKey, cacheName); } @@ -116,9 +107,9 @@ public class JbootRedisCacheImpl extends JbootCacheBase { private String buildKey(String cacheName, Object key) { - if (key instanceof Number) + if (key instanceof Number) { return String.format("%s:I:%s", cacheName, key); - else { + } else { Class keyClass = key.getClass(); if (String.class.equals(keyClass) || StringBuffer.class.equals(keyClass) || @@ -150,11 +141,33 @@ public class JbootRedisCacheImpl extends JbootCacheBase { return ttl != null ? ttl.intValue() : null; } + @Override public void setTtl(String cacheName, Object key, int seconds) { redis.expire(buildKey(cacheName, key), seconds); } + + @Override + public List getNames() { + Set set = redis.smembers(redisCacheNamesKey); + return set == null ? null : new ArrayList(set); + } + + + @Override + public List getKeys(String cacheName) { + Set keyset = redis.keys(cacheName + ":*"); + if (keyset == null || keyset.size() == 0) { + return null; + } + List keys = new ArrayList<>(keyset); + for (int i = 0; i < keys.size(); i++) { + keys.set(i, keys.get(i).substring(cacheName.length() + 3)); + } + return keys; + } + public JbootRedis getRedis() { return redis; } -- Gitee From e4f44bce03a5ea828d5b6a2001ac5ecdf8ffa2be Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 20 Mar 2020 19:42:38 +0800 Subject: [PATCH 0085/1965] v3.1.0 --- pom.xml | 2 +- src/main/java/io/jboot/Jboot.java | 6 +++--- .../java/io/jboot/aop/JbootAopFactory.java | 4 ++-- .../components/cache/j2cache/J2cacheImpl.java | 21 ++++++++++++++----- .../components/cache/none/NoneCacheImpl.java | 15 ++++++++----- .../jboot/components/rpc/dubbo/DubboUtil.java | 2 ++ 6 files changed, 34 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index adaf0ca1..243eef09 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.0.5 + 3.1.0-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/Jboot.java b/src/main/java/io/jboot/Jboot.java index 802e2c43..9057ceee 100644 --- a/src/main/java/io/jboot/Jboot.java +++ b/src/main/java/io/jboot/Jboot.java @@ -26,7 +26,7 @@ import io.jboot.components.event.JbootEventManager; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqManager; import io.jboot.components.rpc.JbootrpcManager; -import io.jboot.components.rpc.JbootrpcServiceConfig; +import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.serializer.JbootSerializer; import io.jboot.components.serializer.JbootSerializerManager; import io.jboot.support.metric.JbootMetricManager; @@ -153,7 +153,7 @@ public class Jboot { * @return */ public static T service(Class clazz) { - return service(clazz, new JbootrpcServiceConfig()); + return service(clazz, new JbootrpcReferenceConfig()); } /** @@ -164,7 +164,7 @@ public class Jboot { * @param * @return */ - public static T service(Class clazz, JbootrpcServiceConfig config) { + public static T service(Class clazz, JbootrpcReferenceConfig config) { return JbootrpcManager.me().getJbootrpc().serviceObtain(clazz, config); } diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index ad0256d4..9f35239a 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -31,7 +31,7 @@ import io.jboot.components.event.JbootEventListener; import io.jboot.components.mq.JbootmqMessageListener; import io.jboot.components.rpc.Jbootrpc; import io.jboot.components.rpc.JbootrpcManager; -import io.jboot.components.rpc.JbootrpcServiceConfig; +import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.annotation.RPCInject; import io.jboot.db.model.JbootModel; import io.jboot.exception.JbootException; @@ -184,7 +184,7 @@ public class JbootAopFactory extends AopFactory { private void doInjectRPC(Object targetObject, Field field, RPCInject rpcInject) { try { - JbootrpcServiceConfig serviceConfig = new JbootrpcServiceConfig(rpcInject); + JbootrpcReferenceConfig serviceConfig = new JbootrpcReferenceConfig(rpcInject); Class fieldInjectedClass = field.getType(); Jbootrpc jbootrpc = JbootrpcManager.me().getJbootrpc(); diff --git a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java index 8bfd666f..4e09a0c9 100644 --- a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java +++ b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java @@ -26,6 +26,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) @@ -50,11 +51,6 @@ public class J2cacheImpl extends JbootCacheBase { J2Cache.getChannel().set(cacheName, key.toString(), value, liveSeconds); } - @Override - public List getKeys(String cacheName) { - Collection keys = J2Cache.getChannel().keys(cacheName); - return keys != null ? new ArrayList(keys) : null; - } @Override public void remove(String cacheName, Object key) { @@ -129,6 +125,21 @@ public class J2cacheImpl extends JbootCacheBase { } } + @Override + public List getNames() { + Collection regions = J2Cache.getChannel().getL1Provider().regions(); + return regions != null && !regions.isEmpty() + ? regions.stream().map(CacheChannel.Region::getName).collect(Collectors.toList()) + : null; + } + + + @Override + public List getKeys(String cacheName) { + Collection keys = J2Cache.getChannel().keys(cacheName); + return keys != null ? new ArrayList(keys) : null; + } + private Method getSendEvictCmdMethod() { try { diff --git a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java index f9e8338d..06b78a55 100644 --- a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java @@ -40,11 +40,6 @@ public class NoneCacheImpl implements JbootCache { //do nothing } - @Override - public List getKeys(String cacheName) { - return null; - } - @Override public void remove(String cacheName, Object key) { //do nothing @@ -84,4 +79,14 @@ public class NoneCacheImpl implements JbootCache { public void refresh(String cacheName) { } + + @Override + public List getNames() { + return null; + } + + @Override + public List getKeys(String cacheName) { + return null; + } } diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index cd133279..fcce929a 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -163,6 +163,8 @@ class DubboUtil { // ignore } } +// String p = sc.getProtocol(); +// if () return serviceConfig; } -- Gitee From a6ee386bc6b4eb10f8d3d904fce65a7ec87ae8a3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 20 Mar 2020 20:10:23 +0800 Subject: [PATCH 0086/1965] v3.1.0 --- src/main/java/io/jboot/app/config/Utils.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/app/config/Utils.java b/src/main/java/io/jboot/app/config/Utils.java index c6e76a74..26b14bc9 100644 --- a/src/main/java/io/jboot/app/config/Utils.java +++ b/src/main/java/io/jboot/app/config/Utils.java @@ -180,7 +180,18 @@ class Utils { } else { return null; } - } else if (Class.class == convertClass) { + } else if (convertClass.isArray() && convertClass.getComponentType() == String.class){ + List list = new LinkedList(); + String[] strings = s.split(","); + if (strings != null && strings.length > 0){ + for (String s1 : strings){ + if (s1 != null && s1.trim().length() != 0) { + list.add(s1.trim()); + } + } + } + return list.toArray(new String[0]); + }else if (Class.class == convertClass) { try { return Class.forName(s, false, Thread.currentThread().getContextClassLoader()); } catch (ClassNotFoundException e) { -- Gitee From 86aff6f7120cb20c81d7c4182aea638921700010 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 20 Mar 2020 20:35:49 +0800 Subject: [PATCH 0087/1965] v3.1.0 --- .../rpc/JbootrpcReferenceConfig.java | 1 - .../java/io/jboot/components/rpc/Utils.java | 22 +++++++- .../jboot/components/rpc/dubbo/DubboUtil.java | 51 +++++++++++-------- .../jboot/components/rpc/motan/MotanUtil.java | 37 ++------------ 4 files changed, 55 insertions(+), 56 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java index a6cd44e4..aee88484 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java @@ -112,7 +112,6 @@ public class JbootrpcReferenceConfig implements Serializable { String registry; /** - * The communication protocol of Dubbo Service * * @return the default value is "" */ diff --git a/src/main/java/io/jboot/components/rpc/Utils.java b/src/main/java/io/jboot/components/rpc/Utils.java index 852e670b..fdf4ed5b 100644 --- a/src/main/java/io/jboot/components/rpc/Utils.java +++ b/src/main/java/io/jboot/components/rpc/Utils.java @@ -16,8 +16,10 @@ package io.jboot.components.rpc; import io.jboot.utils.CollectionUtil; +import io.jboot.utils.StrUtil; import org.apache.dubbo.common.utils.StringUtils; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Map; @@ -28,7 +30,7 @@ import java.util.Map; */ public class Utils { - public static void appendAnnotation(Class annotationClass, Object annotation,Object appendTo) { + public static void appendAnnotation(Class annotationClass, Object annotation, Object appendTo) { Method[] methods = annotationClass.getMethods(); for (Method method : methods) { if (method.getDeclaringClass() != Object.class @@ -66,6 +68,24 @@ public class Utils { } } + + /** + * copy object field value to other + * @param copyFrom + * @param copyTo + */ + public static void copyFields(Object copyFrom, Object copyTo) { + Field[] fields = copyFrom.getClass().getDeclaredFields(); + for (Field field : fields) { + try { + Method method = copyTo.getClass().getDeclaredMethod("set" + StrUtil.firstCharToUpperCase(field.getName()), field.getType()); + method.invoke(copyTo, field.get(copyFrom)); + } catch (Exception e) { + // ignore + } + } + } + private static Class getBoxedClass(Class c) { if (c == int.class) { c = Integer.class; diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index fcce929a..fefc42f2 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -20,12 +20,11 @@ import io.jboot.app.config.JbootConfigManager; import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; +import io.jboot.components.rpc.Utils; import io.jboot.utils.StrUtil; import org.apache.dubbo.config.*; import org.apache.dubbo.config.bootstrap.DubboBootstrap; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -137,34 +136,42 @@ class DubboUtil { public static ReferenceConfig toReferenceConfig(JbootrpcReferenceConfig rc){ ReferenceConfig referenceConfig = new ReferenceConfig(); - Field[] fields = rc.getClass().getDeclaredFields(); - for (Field field : fields){ - try { - Method method = ReferenceConfig.class.getDeclaredMethod("set"+StrUtil.firstCharToUpperCase(field.getName()),field.getType()); - field.setAccessible(true); - method.invoke(referenceConfig,field.get(rc)); - } catch (Exception e) { - // ignore - } + Utils.copyFields(rc,referenceConfig); + + //reference coonsumer + if (rc.getConsumer() != null) { + referenceConfig.setConsumer(consumerConfigMap.get(rc.getConsumer())); } + + + //service registry + if (StrUtil.isNotBlank(rc.getRegistry())) { + referenceConfig.setRegistryIds(rc.getRegistry()); + } + return referenceConfig; } public static ServiceConfig toServiceConfig(JbootrpcServiceConfig sc){ ServiceConfig serviceConfig = new ServiceConfig(); - Field[] fields = sc.getClass().getDeclaredFields(); - for (Field field : fields){ - try { - Method method = ServiceConfig.class.getDeclaredMethod("set"+StrUtil.firstCharToUpperCase(field.getName()),field.getType()); - field.setAccessible(true); - method.invoke(serviceConfig,field.get(sc)); - } catch (Exception e) { - // ignore - } + Utils.copyFields(sc,serviceConfig); + + //service provider + if (StrUtil.isNotBlank(sc.getProtocol())) { + serviceConfig.setProviderIds(sc.getProvider()); + } + + //service protocol + if (StrUtil.isNotBlank(sc.getProtocol())) { + serviceConfig.setProtocolIds(sc.getProtocol()); } -// String p = sc.getProtocol(); -// if () + + //service registry + if (StrUtil.isNotBlank(sc.getRegistry())) { + serviceConfig.setRegistryIds(sc.getRegistry()); + } + return serviceConfig; } diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java index 32b74a42..d4154bda 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java @@ -16,14 +16,11 @@ package io.jboot.components.rpc.motan; import com.weibo.api.motan.config.*; -import io.jboot.app.config.JbootConfigManager; import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; -import io.jboot.utils.StrUtil; +import io.jboot.components.rpc.Utils; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -76,44 +73,20 @@ public class MotanUtil { } - public static RefererConfig toRefererConfig(JbootrpcReferenceConfig rc){ + public static RefererConfig toRefererConfig(JbootrpcReferenceConfig rc) { RefererConfig referenceConfig = new RefererConfig(); - Field[] fields = rc.getClass().getDeclaredFields(); - for (Field field : fields){ - try { - Method method = RefererConfig.class.getDeclaredMethod("set"+ StrUtil.firstCharToUpperCase(field.getName()),field.getType()); - field.setAccessible(true); - method.invoke(referenceConfig,field.get(rc)); - } catch (Exception e) { - // ignore - } - } + Utils.copyFields(rc, referenceConfig); return referenceConfig; } - - public static ServiceConfig toServiceConfig(JbootrpcServiceConfig sc){ + public static ServiceConfig toServiceConfig(JbootrpcServiceConfig sc) { ServiceConfig serviceConfig = new ServiceConfig(); - Field[] fields = sc.getClass().getDeclaredFields(); - for (Field field : fields){ - try { - Method method = ServiceConfig.class.getDeclaredMethod("set"+StrUtil.firstCharToUpperCase(field.getName()),field.getType()); - field.setAccessible(true); - method.invoke(serviceConfig,field.get(sc)); - } catch (Exception e) { - // ignore - } - } + Utils.copyFields(sc, serviceConfig); return serviceConfig; } - private static T config(Class clazz, String prefix) { - return JbootConfigManager.me().get(clazz, prefix, null); - } - - private static Map configs(Class clazz, String prefix) { return JbootConfigUtil.getConfigModels(clazz, prefix); } -- Gitee From e056cdbde5ca8f5443eb3f6f88f4d56fe38bd7dc Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 20 Mar 2020 21:46:27 +0800 Subject: [PATCH 0088/1965] v3.1.0 --- .../java/io/jboot/components/rpc/Utils.java | 41 +++++++ .../jboot/components/rpc/dubbo/DubboUtil.java | 72 +++++++----- .../jboot/components/rpc/motan/MotanUtil.java | 108 +++++++++++++++--- 3 files changed, 177 insertions(+), 44 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/Utils.java b/src/main/java/io/jboot/components/rpc/Utils.java index fdf4ed5b..4b548c95 100644 --- a/src/main/java/io/jboot/components/rpc/Utils.java +++ b/src/main/java/io/jboot/components/rpc/Utils.java @@ -15,6 +15,7 @@ */ package io.jboot.components.rpc; +import io.jboot.Jboot; import io.jboot.utils.CollectionUtil; import io.jboot.utils.StrUtil; import org.apache.dubbo.common.utils.StringUtils; @@ -22,7 +23,10 @@ import org.apache.dubbo.common.utils.StringUtils; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; import java.util.Map; +import java.util.Set; /** * @author michael yang (fuhai999@gmail.com) @@ -71,6 +75,7 @@ public class Utils { /** * copy object field value to other + * * @param copyFrom * @param copyTo */ @@ -86,6 +91,42 @@ public class Utils { } } + + public static void appendChildConfig(Map appendTo, Map dataFrom, String prefix, String arrName) { + if (appendTo != null && !appendTo.isEmpty()) { + for (Map.Entry entry : appendTo.entrySet()) { + String configKey = "default".equals(entry.getKey()) + ? prefix + "." + arrName //"jboot.rpc.dubbo.method.argument" + : prefix + "." + entry.getKey() + "." + arrName;//"jboot.rpc.dubbo.method."+entry.getKey()+".argument"; + + String configValue = Jboot.configValue(configKey); + if (StrUtil.isNotBlank(configValue)) { + List argCfgList = new ArrayList<>(); + Set arguments = StrUtil.splitToSetByComma(configValue); + for (String arg : arguments) { + F fillObj = dataFrom.get(arg); + if (fillObj != null) { + argCfgList.add(fillObj); + } + } + if (!argCfgList.isEmpty()) { + try { + //setArguments/setMethods/setRegistries + String setterMethodName = arrName.endsWith("registry") + ? "setRegistries" + : "set" + StrUtil.firstCharToUpperCase(arrName) + "s"; + + Method method = entry.getValue().getClass().getDeclaredMethod(setterMethodName, List.class); + method.invoke(entry.getValue(), argCfgList); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + } + private static Class getBoxedClass(Class c) { if (c == int.class) { c = Integer.class; diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index fefc42f2..c808c91f 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -41,9 +41,6 @@ class DubboUtil { private static Map providerConfigMap = new ConcurrentHashMap<>(); private static Map consumerConfigMap = new ConcurrentHashMap<>(); - private static Map methodConfigMap = new ConcurrentHashMap<>(); - private static Map argumentConfigMap = new ConcurrentHashMap<>(); - public static void initDubbo() { DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance(); @@ -72,8 +69,18 @@ class DubboUtil { ModuleConfig moduleConfig = config(ModuleConfig.class, "jboot.rpc.dubbo.module"); dubboBootstrap.module(moduleConfig); + //元数据 配置 + Map metadataReportConfigs = configs(MetadataReportConfig.class, "jboot.rpc.dubbo.metadataReport"); + if (metadataReportConfigs != null && !metadataReportConfigs.isEmpty()) { + if (metadataReportConfigs.size() == 1) { + dubboBootstrap.metadataReport(getDefault(metadataReportConfigs)); + } else { + dubboBootstrap.metadataReports((List) toList(metadataReportConfigs)); + } + } + - //protocol 配置 + //协议 配置 Map protocolConfigs = configs(ProtocolConfig.class, "jboot.rpc.dubbo.protocol"); if (protocolConfigs != null && !protocolConfigs.isEmpty()) { protocolConfigMap.putAll(protocolConfigs); @@ -84,7 +91,7 @@ class DubboUtil { } } - //registry 配置 + //服务注册中心 配置 Map registryConfigs = configs(RegistryConfig.class, "jboot.rpc.dubbo.registry"); if (registryConfigs != null && !registryConfigs.isEmpty()) { registryConfigMap.putAll(registryConfigs); @@ -96,20 +103,22 @@ class DubboUtil { } - //provider 配置 - Map providerConfigs = configs(ProviderConfig.class, "jboot.rpc.dubbo.provider"); - if (providerConfigs != null && !providerConfigs.isEmpty()) { - providerConfigMap.putAll(providerConfigs); - if (providerConfigs.size() == 1) { - dubboBootstrap.provider(getDefault(providerConfigs)); - } else { - dubboBootstrap.providers((List) toList(providerConfigs)); - } - } + //方法参数配置 配置 + Map argumentConfigs = configs(ArgumentConfig.class, "jboot.rpc.dubbo.argument"); - //consumer 配置 + //方法配置 配置 + Map methodConfigs = configs(MethodConfig.class, "jboot.rpc.dubbo.method"); + Utils.appendChildConfig(methodConfigs,argumentConfigs,"jboot.rpc.dubbo.method","argument"); + + + //消费者 配置 Map consumerConfigs = configs(ConsumerConfig.class, "jboot.rpc.dubbo.consumer"); + Utils.appendChildConfig(consumerConfigs,methodConfigs,"jboot.rpc.dubbo.consumer","method"); + Utils.appendChildConfig(consumerConfigs,protocolConfigs,"jboot.rpc.dubbo.consumer","protocol"); + Utils.appendChildConfig(consumerConfigs,registryConfigs,"jboot.rpc.dubbo.consumer","registry"); + + if (consumerConfigs != null && !consumerConfigs.isEmpty()) { consumerConfigMap.putAll(consumerConfigs); if (consumerConfigs.size() == 1) { @@ -119,24 +128,26 @@ class DubboUtil { } } - //methodConfig 配置 - Map methodConfigs = configs(MethodConfig.class, "jboot.rpc.dubbo.method"); - if (methodConfigs != null && !methodConfigs.isEmpty()) { - methodConfigMap.putAll(methodConfigs); - } - + //服务提供者 配置 + Map providerConfigs = configs(ProviderConfig.class, "jboot.rpc.dubbo.provider"); + Utils.appendChildConfig(providerConfigs,methodConfigs,"jboot.rpc.dubbo.provider","method"); + Utils.appendChildConfig(providerConfigs,protocolConfigs,"jboot.rpc.dubbo.provider","protocol"); + Utils.appendChildConfig(providerConfigs,registryConfigs,"jboot.rpc.dubbo.provider","registry"); - //argumentConfig 配置 - Map argumentConfigs = configs(ArgumentConfig.class, "jboot.rpc.dubbo.argument"); - if (argumentConfigs != null && !argumentConfigs.isEmpty()) { - argumentConfigMap.putAll(argumentConfigs); + if (providerConfigs != null && !providerConfigs.isEmpty()) { + providerConfigMap.putAll(providerConfigs); + if (providerConfigs.size() == 1) { + dubboBootstrap.provider(getDefault(providerConfigs)); + } else { + dubboBootstrap.providers((List) toList(providerConfigs)); + } } } - public static ReferenceConfig toReferenceConfig(JbootrpcReferenceConfig rc){ + public static ReferenceConfig toReferenceConfig(JbootrpcReferenceConfig rc) { ReferenceConfig referenceConfig = new ReferenceConfig(); - Utils.copyFields(rc,referenceConfig); + Utils.copyFields(rc, referenceConfig); //reference coonsumer if (rc.getConsumer() != null) { @@ -153,9 +164,9 @@ class DubboUtil { } - public static ServiceConfig toServiceConfig(JbootrpcServiceConfig sc){ + public static ServiceConfig toServiceConfig(JbootrpcServiceConfig sc) { ServiceConfig serviceConfig = new ServiceConfig(); - Utils.copyFields(sc,serviceConfig); + Utils.copyFields(sc, serviceConfig); //service provider if (StrUtil.isNotBlank(sc.getProtocol())) { @@ -176,7 +187,6 @@ class DubboUtil { } - private static T config(Class clazz, String prefix) { return JbootConfigManager.me().get(clazz, prefix, null); } diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java index d4154bda..0cf091eb 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java @@ -20,8 +20,12 @@ import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; import io.jboot.components.rpc.Utils; +import io.jboot.utils.StrUtil; +import java.util.ArrayList; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** @@ -34,7 +38,7 @@ public class MotanUtil { private static Map registryConfigMap = new ConcurrentHashMap<>(); private static Map baseRefererConfigMap = new ConcurrentHashMap<>(); private static Map baseServiceConfigMap = new ConcurrentHashMap<>(); - private static Map methodConfigMap = new ConcurrentHashMap<>(); +// private static Map methodConfigMap = new ConcurrentHashMap<>(); public static void initMotan() { @@ -51,22 +55,29 @@ public class MotanUtil { registryConfigMap.putAll(registryConfigs); } - //baseReferer 配置 - Map refererConfigs = configs(RefererConfig.class, "jboot.rpc.motan.referer"); - if (refererConfigs != null && !refererConfigs.isEmpty()) { - baseRefererConfigMap.putAll(refererConfigs); - } + //methodConfig 配置 + Map methodConfigs = configs(MethodConfig.class, "jboot.rpc.motan.method"); + //baseService 配置 Map serviceConfigs = configs(ServiceConfig.class, "jboot.rpc.motan.service"); + Utils.appendChildConfig(serviceConfigs,methodConfigs,"jboot.rpc.motan.service","method"); + Utils.appendChildConfig(serviceConfigs,protocolConfigs,"jboot.rpc.motan.service","protocol"); + Utils.appendChildConfig(serviceConfigs,registryConfigs,"jboot.rpc.motan.service","registry"); + + if (serviceConfigs != null && !serviceConfigs.isEmpty()) { baseServiceConfigMap.putAll(serviceConfigs); } - //methodConfig 配置 - Map methodConfigs = configs(MethodConfig.class, "jboot.rpc.motan.method"); - if (methodConfigs != null && !methodConfigs.isEmpty()) { - methodConfigMap.putAll(methodConfigs); + //baseReferer 配置 + Map refererConfigs = configs(RefererConfig.class, "jboot.rpc.motan.referer"); + Utils.appendChildConfig(refererConfigs,methodConfigs,"jboot.rpc.motan.referer","method"); + Utils.appendChildConfig(refererConfigs,protocolConfigs,"jboot.rpc.motan.referer","protocol"); + Utils.appendChildConfig(refererConfigs,registryConfigs,"jboot.rpc.motan.referer","registry"); + + if (refererConfigs != null && !refererConfigs.isEmpty()) { + baseRefererConfigMap.putAll(refererConfigs); } @@ -74,15 +85,86 @@ public class MotanUtil { public static RefererConfig toRefererConfig(JbootrpcReferenceConfig rc) { - RefererConfig referenceConfig = new RefererConfig(); - Utils.copyFields(rc, referenceConfig); - return referenceConfig; + RefererConfig refererConfig = new RefererConfig(); + Utils.copyFields(rc, refererConfig); + + + //referer protocol + if (StrUtil.isNotBlank(rc.getProtocol())) { + List protocolConfigs = new ArrayList<>(); + Set protocolNames = StrUtil.splitToSetByComma(rc.getRegistry()); + for (String protocalName : protocolNames){ + ProtocolConfig registryConfig = protocolConfigMap.get(protocalName); + if (registryConfig != null){ + protocolConfigs.add(registryConfig); + } + } + if (!protocolConfigs.isEmpty()){ + refererConfig.setProtocols(protocolConfigs); + } + } + + + + //referer registry + if (StrUtil.isNotBlank(rc.getRegistry())) { + List registryConfigs = new ArrayList<>(); + Set registryNames = StrUtil.splitToSetByComma(rc.getRegistry()); + for (String registryName : registryNames){ + RegistryConfig registryConfig = registryConfigMap.get(registryName); + if (registryConfig != null){ + registryConfigs.add(registryConfig); + } + } + if (!registryConfigs.isEmpty()){ + refererConfig.setRegistries(registryConfigs); + } + } + + + return refererConfig; } public static ServiceConfig toServiceConfig(JbootrpcServiceConfig sc) { ServiceConfig serviceConfig = new ServiceConfig(); Utils.copyFields(sc, serviceConfig); + + + + //service protocol + if (StrUtil.isNotBlank(sc.getProtocol())) { + List protocolConfigs = new ArrayList<>(); + Set protocolNames = StrUtil.splitToSetByComma(sc.getRegistry()); + for (String protocalName : protocolNames){ + ProtocolConfig registryConfig = protocolConfigMap.get(protocalName); + if (registryConfig != null){ + protocolConfigs.add(registryConfig); + } + } + if (!protocolConfigs.isEmpty()){ + serviceConfig.setProtocols(protocolConfigs); + } + } + + + + //service registry + if (StrUtil.isNotBlank(sc.getRegistry())) { + List registryConfigs = new ArrayList<>(); + Set registryNames = StrUtil.splitToSetByComma(sc.getRegistry()); + for (String registryName : registryNames){ + RegistryConfig registryConfig = registryConfigMap.get(registryName); + if (registryConfig != null){ + registryConfigs.add(registryConfig); + } + } + if (!registryConfigs.isEmpty()){ + serviceConfig.setRegistries(registryConfigs); + } + } + + return serviceConfig; } -- Gitee From 1cb11a958f54939a80bedd9763cced9ef391167d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 09:31:06 +0800 Subject: [PATCH 0089/1965] v3.1.0 --- .../java/io/jboot/core/JbootCoreConfig.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 93b1e52a..07a3ceb9 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -21,6 +21,8 @@ import com.jfinal.config.*; import com.jfinal.core.Controller; import com.jfinal.json.JsonManager; import com.jfinal.kit.LogKit; +import com.jfinal.kit.PathKit; +import com.jfinal.kit.PropKit; import com.jfinal.plugin.activerecord.ActiveRecordPlugin; import com.jfinal.template.Engine; import com.jfinal.weixin.sdk.api.ApiConfig; @@ -61,11 +63,13 @@ import io.jboot.web.render.JbootRenderFactory; import io.jboot.wechat.JbootAccessTokenCache; import io.jboot.wechat.JbootWechatConfig; +import java.io.File; import java.sql.Driver; import java.sql.DriverManager; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; +import java.util.Properties; public class JbootCoreConfig extends JFinalConfig { @@ -93,11 +97,31 @@ public class JbootCoreConfig extends JFinalConfig { */ private void initSystemProperties() { + File spf = new File(PathKit.getRootClassPath(), "jboot-system.properties"); + if (spf.exists() && spf.isFile()) { + Properties properties = PropKit.use(spf).getProperties(); + if (properties != null && !properties.isEmpty()) { + for (Object key : properties.keySet()) { + if (StrUtil.isNotBlank(key)) { + String newKey = key.toString().trim(); + String systemValue = System.getProperty(newKey); + if (StrUtil.isNotBlank(systemValue)) { + continue; + } + String newValue = properties.getProperty(newKey); + if (StrUtil.isNotBlank(newValue)) { + System.setProperty(newKey, newValue.trim()); + } + } + } + } + } + //apollo 配置 ApolloServerConfig apolloConfig = Jboot.config(ApolloServerConfig.class); - if (apolloConfig.isEnable() && apolloConfig.isConfigOk()){ - System.setProperty("app.id",apolloConfig.getAppId()); - System.setProperty("apollo.meta",apolloConfig.getMeta()); + if (apolloConfig.isEnable() && apolloConfig.isConfigOk()) { + System.setProperty("app.id", apolloConfig.getAppId()); + System.setProperty("apollo.meta", apolloConfig.getMeta()); } } -- Gitee From cd97ee65a34af34bccfa92f3d34de670f2da951c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 09:50:24 +0800 Subject: [PATCH 0090/1965] v3.1.0 --- src/main/java/io/jboot/app/config/Utils.java | 14 +++++++------- .../io/jboot/components/rpc/JbootrpcConfig.java | 14 ++++++++++++++ src/main/java/io/jboot/core/JbootCoreConfig.java | 1 + src/test/java/io/jboot/test/JavaTester.java | 2 -- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/app/config/Utils.java b/src/main/java/io/jboot/app/config/Utils.java index 26b14bc9..06e4f6f4 100644 --- a/src/main/java/io/jboot/app/config/Utils.java +++ b/src/main/java/io/jboot/app/config/Utils.java @@ -147,9 +147,9 @@ class Utils { Map map = convertClass == ConcurrentHashMap.class ? new ConcurrentHashMap() : new HashMap(); String[] strings = s.split(","); for (String kv : strings) { - String[] keyValue = kv.split(":"); - if (keyValue.length == 2) { - map.put(keyValue[0], keyValue[1]); + int indexOf = kv.indexOf(":"); + if (indexOf > 0 && indexOf < kv.trim().length() - 1) { + map.put(kv.substring(0, indexOf).trim(), kv.substring(indexOf + 1).trim()); } } return map; @@ -180,18 +180,18 @@ class Utils { } else { return null; } - } else if (convertClass.isArray() && convertClass.getComponentType() == String.class){ + } else if (convertClass.isArray() && convertClass.getComponentType() == String.class) { List list = new LinkedList(); String[] strings = s.split(","); - if (strings != null && strings.length > 0){ - for (String s1 : strings){ + if (strings != null && strings.length > 0) { + for (String s1 : strings) { if (s1 != null && s1.trim().length() != 0) { list.add(s1.trim()); } } } return list.toArray(new String[0]); - }else if (Class.class == convertClass) { + } else if (Class.class == convertClass) { try { return Class.forName(s, false, Thread.currentThread().getContextClassLoader()); } catch (ClassNotFoundException e) { diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java index 79704b99..404d1f5a 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java @@ -18,6 +18,8 @@ package io.jboot.components.rpc; import io.jboot.app.config.annotation.ConfigModel; import io.jboot.utils.StrUtil; +import java.util.Map; + @ConfigModel(prefix = "jboot.rpc") public class JbootrpcConfig { @@ -28,6 +30,10 @@ public class JbootrpcConfig { private String type; + //用于直连时的配置,直连一般只用于测试环境 + //com.service.AAAService:127.0.0.1:8080,com.service.XXXService:127.0.0.1:8080 + private Map urls; + //本地自动暴露 @RPCBean 的 service private boolean autoExportEnable = true; @@ -40,6 +46,14 @@ public class JbootrpcConfig { this.type = type; } + public Map getUrls() { + return urls; + } + + public void setUrls(Map urls) { + this.urls = urls; + } + public boolean isAutoExportEnable() { return autoExportEnable; } diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 07a3ceb9..e3f5d094 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -97,6 +97,7 @@ public class JbootCoreConfig extends JFinalConfig { */ private void initSystemProperties() { + //加载 jboot-system.properties 代替启动参数的 -D 配置 File spf = new File(PathKit.getRootClassPath(), "jboot-system.properties"); if (spf.exists() && spf.isFile()) { Properties properties = PropKit.use(spf).getProperties(); diff --git a/src/test/java/io/jboot/test/JavaTester.java b/src/test/java/io/jboot/test/JavaTester.java index cd409280..2e5f5ee7 100644 --- a/src/test/java/io/jboot/test/JavaTester.java +++ b/src/test/java/io/jboot/test/JavaTester.java @@ -8,6 +8,4 @@ public class JavaTester { } - - } -- Gitee From 668c99f7eb244d3803270a82ad1fee5332c6f5ef Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 09:55:09 +0800 Subject: [PATCH 0091/1965] v3.1.0 --- src/main/java/io/jboot/components/rpc/JbootrpcBase.java | 3 +++ .../java/io/jboot/components/rpc/JbootrpcConfig.java | 4 ++++ .../io/jboot/components/rpc/dubbo/JbootDubborpc.java | 5 +++++ .../io/jboot/components/rpc/motan/JbootMotanrpc.java | 9 +++++++-- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java index e7dc1f03..4d6b6fcc 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java @@ -16,12 +16,15 @@ package io.jboot.components.rpc; +import io.jboot.Jboot; + import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public abstract class JbootrpcBase implements Jbootrpc { protected static final Map objectCache = new ConcurrentHashMap<>(); + protected static JbootrpcConfig rpcConfig = Jboot.config(JbootrpcConfig.class); @Override diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java index 404d1f5a..0235ba8c 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java @@ -54,6 +54,10 @@ public class JbootrpcConfig { this.urls = urls; } + public String getUrl(String serviceClass){ + return urls == null ? null : urls.get(serviceClass); + } + public boolean isAutoExportEnable() { return autoExportEnable; } diff --git a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java index 621d41b5..e69a8be9 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java @@ -18,6 +18,7 @@ package io.jboot.components.rpc.dubbo; import io.jboot.components.rpc.JbootrpcBase; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; +import io.jboot.utils.StrUtil; import org.apache.dubbo.config.ReferenceConfig; import org.apache.dubbo.config.ServiceConfig; @@ -32,6 +33,10 @@ public class JbootDubborpc extends JbootrpcBase { @Override public T onServiceCreate(Class serviceClass, JbootrpcReferenceConfig config) { ReferenceConfig reference = DubboUtil.toReferenceConfig(config); + String directUrl = rpcConfig.getUrl(serviceClass.getName()); + if (StrUtil.isNotBlank(directUrl)){ + reference.setUrl(directUrl); + } return reference.get(); } diff --git a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java index fb74002c..f681f891 100644 --- a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java +++ b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java @@ -22,6 +22,7 @@ import com.weibo.api.motan.util.MotanSwitcherUtil; import io.jboot.components.rpc.JbootrpcBase; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; +import io.jboot.utils.StrUtil; public class JbootMotanrpc extends JbootrpcBase { @@ -33,8 +34,12 @@ public class JbootMotanrpc extends JbootrpcBase { @Override public T onServiceCreate(Class serviceClass, JbootrpcReferenceConfig config) { - RefererConfig reference = MotanUtil.toRefererConfig(config); - return reference.getRef(); + RefererConfig referer = MotanUtil.toRefererConfig(config); + String directUrl = rpcConfig.getUrl(serviceClass.getName()); + if (StrUtil.isNotBlank(directUrl)){ + referer.setDirectUrl(directUrl); + } + return referer.getRef(); } -- Gitee From 2b67313099d22c3f1483308afb59eebacb1f1464 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 10:34:05 +0800 Subject: [PATCH 0092/1965] v3.1.0 --- .../io/jboot/app/config/JbootConfigUtil.java | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigUtil.java b/src/main/java/io/jboot/app/config/JbootConfigUtil.java index 1abd7566..c6edb761 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigUtil.java +++ b/src/main/java/io/jboot/app/config/JbootConfigUtil.java @@ -30,8 +30,8 @@ public class JbootConfigUtil { public static Map getConfigModels(Class tClass, String prefix) { Map objMap = new HashMap<>(); - T defaultObj = Jboot.config(tClass, prefix); - objMap.put("default", defaultObj); + + boolean initDefault = false; Set objNames = new HashSet<>(); @@ -46,7 +46,22 @@ public class JbootConfigUtil { if (entry.getKey() == null || StrUtil.isBlank(entry.getKey().toString())) { continue; } - String key = entry.getKey().toString().toLowerCase().replace('_', '.'); + + String key = entry.getKey().toString().trim(); + + //配置来源于 Docker 的环境变量配置 + if (key.contains("_") && Character.isUpperCase(key.charAt(0))){ + key = key.toLowerCase().replace('_','.'); + } + + //初始化默认的配置 + if (!initDefault && key.startsWith(prefix) && StringUtils.countMatches(key, '.') == pointCount + 1) { + initDefault = true; + T defaultObj = Jboot.config(tClass, prefix); + objMap.put("default", defaultObj); + + } + if (key.startsWith(objPrefix) && entry.getValue() != null) { String[] keySplits = key.split("\\."); if (keySplits.length == pointCount + 3) { -- Gitee From 00a4ecc0654cfb7e6c57bb5f586e423c8035e553 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 10:34:49 +0800 Subject: [PATCH 0093/1965] v3.1.0 --- .../java/io/jboot/components/rpc/JbootrpcBase.java | 10 +++++----- .../java/io/jboot/components/rpc/dubbo/DubboUtil.java | 9 +++++++++ .../io/jboot/components/rpc/dubbo/JbootDubborpc.java | 5 +++-- .../io/jboot/components/rpc/motan/JbootMotanrpc.java | 5 +++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java index 4d6b6fcc..0a98bc44 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java @@ -28,13 +28,13 @@ public abstract class JbootrpcBase implements Jbootrpc { @Override - public T serviceObtain(Class serviceClass, JbootrpcReferenceConfig config) { - String key = buildCacheKey(serviceClass,config); + public T serviceObtain(Class interfaceClass, JbootrpcReferenceConfig config) { + String key = buildCacheKey(interfaceClass,config); T object = (T) objectCache.get(key); if (object == null) { synchronized (this){ if (objectCache.get(key) == null) { - object = onServiceCreate(serviceClass,config); + object = onServiceCreate(interfaceClass,config); if (object != null){ objectCache.put(key,object); } @@ -56,8 +56,8 @@ public abstract class JbootrpcBase implements Jbootrpc { } - protected String buildCacheKey(Class serviceClass, JbootrpcReferenceConfig config){ - StringBuilder sb = new StringBuilder(serviceClass.getName()); + protected String buildCacheKey(Class interfaceClass, JbootrpcReferenceConfig config){ + StringBuilder sb = new StringBuilder(interfaceClass.getName()); return sb.append(":").append(config.getGroup()) .append(":").append(config.getVersion()) .toString(); diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index c808c91f..2088ca07 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -47,6 +47,9 @@ class DubboUtil { //application 配置 ApplicationConfig applicationConfig = config(ApplicationConfig.class, "jboot.rpc.dubbo.application"); + if (StrUtil.isBlank(applicationConfig.getName())){ + applicationConfig.setName("jboot"); + } dubboBootstrap.application(applicationConfig); @@ -101,6 +104,12 @@ class DubboUtil { dubboBootstrap.registries((List) toList(registryConfigs)); } } + //没有配置注册中心,一般只用于希望此服务网提供直连的方式给客户端使用 + else { + RegistryConfig config = new RegistryConfig(); + config.setAddress(RegistryConfig.NO_AVAILABLE); + dubboBootstrap.registry(config); + } //方法参数配置 配置 diff --git a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java index e69a8be9..6af21def 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java @@ -31,9 +31,10 @@ public class JbootDubborpc extends JbootrpcBase { @Override - public T onServiceCreate(Class serviceClass, JbootrpcReferenceConfig config) { + public T onServiceCreate(Class interfaceClass, JbootrpcReferenceConfig config) { ReferenceConfig reference = DubboUtil.toReferenceConfig(config); - String directUrl = rpcConfig.getUrl(serviceClass.getName()); + reference.setInterface(interfaceClass); + String directUrl = rpcConfig.getUrl(interfaceClass.getName()); if (StrUtil.isNotBlank(directUrl)){ reference.setUrl(directUrl); } diff --git a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java index f681f891..04c48454 100644 --- a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java +++ b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java @@ -33,9 +33,10 @@ public class JbootMotanrpc extends JbootrpcBase { } @Override - public T onServiceCreate(Class serviceClass, JbootrpcReferenceConfig config) { + public T onServiceCreate(Class interfaceClass, JbootrpcReferenceConfig config) { RefererConfig referer = MotanUtil.toRefererConfig(config); - String directUrl = rpcConfig.getUrl(serviceClass.getName()); + referer.setInterface(interfaceClass); + String directUrl = rpcConfig.getUrl(interfaceClass.getName()); if (StrUtil.isNotBlank(directUrl)){ referer.setDirectUrl(directUrl); } -- Gitee From 1b0a9e784e20c9effaeaf90675921d8b5f757f72 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 10:36:38 +0800 Subject: [PATCH 0094/1965] v3.1.0 --- .../java/io/jboot/test/rpc/dubbo/DubboClient.java | 8 +++++--- .../java/io/jboot/test/rpc/dubbo/DubboServer.java | 13 +++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java index 85005bb8..cd21324f 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java @@ -14,12 +14,14 @@ public class DubboClient extends JbootController { //Undertow端口号配置 - JbootApplication.setBootArg("undertow.port", "8888"); + JbootApplication.setBootArg("undertow.port", "9999"); //RPC配置 JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); - JbootApplication.setBootArg("jboot.rpc.callMode", "direct");//直连模式,默认为注册中心 - JbootApplication.setBootArg("jboot.rpc.directUrl", "127.0.0.1:8000");//直连模式的server url地址 + + //设置直连模式,方便调试,默认为注册中心 + JbootApplication.setBootArg("jboot.rpc.urls", "io.jboot.test.rpc.commons.BlogService:127.0.0.1:28080"); + JbootApplication.run(args); } diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java index cce8bb5d..88c73611 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java @@ -7,18 +7,19 @@ public class DubboServer { public static void main(String[] args) throws InterruptedException { - JbootApplication.setBootArg("undertow.port", "8887"); + JbootApplication.setBootArg("undertow.port", "9998"); JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); - //开启 @RPCBean 自动暴露功能,默认情况下是自动暴露的,但是 jboot.properties 文件关闭了,这里需要开启下 + // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, + // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); - //设置直连模式,方便调试,默认为注册中心 - JbootApplication.setBootArg("jboot.rpc.callMode", "direct"); + //dubbo 的协议配置 + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.name", "dubbo"); + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "28080"); + - //直连模式的url地址 - JbootApplication.setBootArg("jboot.rpc.directUrl", "127.0.0.1:8000"); JbootApplication.run(args); -- Gitee From 2ebb235d393eaf19417670ca8b2165b4c9b55e80 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 10:43:14 +0800 Subject: [PATCH 0095/1965] v3.1.0 --- src/main/java/io/jboot/components/rpc/Jbootrpc.java | 2 ++ src/main/java/io/jboot/components/rpc/JbootrpcBase.java | 7 +++++++ src/main/java/io/jboot/components/rpc/JbootrpcManager.java | 4 ++++ src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java | 3 +++ .../java/io/jboot/components/rpc/dubbo/JbootDubborpc.java | 4 ++++ src/main/java/io/jboot/core/JbootCoreConfig.java | 1 + 6 files changed, 21 insertions(+) diff --git a/src/main/java/io/jboot/components/rpc/Jbootrpc.java b/src/main/java/io/jboot/components/rpc/Jbootrpc.java index 1239e890..173135ac 100644 --- a/src/main/java/io/jboot/components/rpc/Jbootrpc.java +++ b/src/main/java/io/jboot/components/rpc/Jbootrpc.java @@ -25,4 +25,6 @@ public interface Jbootrpc { public void onInit(); public void onInited(); + + public void onStop(); } diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java index 0a98bc44..84a5e330 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java @@ -56,10 +56,17 @@ public abstract class JbootrpcBase implements Jbootrpc { } + @Override + public void onStop() { + + } + protected String buildCacheKey(Class interfaceClass, JbootrpcReferenceConfig config){ StringBuilder sb = new StringBuilder(interfaceClass.getName()); return sb.append(":").append(config.getGroup()) .append(":").append(config.getVersion()) .toString(); } + + } diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java index dc450c55..5fe3508c 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java @@ -78,6 +78,10 @@ public class JbootrpcManager { jbootrpc.onInited(); } + public void stop(){ + getJbootrpc().onStop(); + } + public void exportRPCBean(Jbootrpc jbootrpc) { List classes = ClassScanner.scanClassByAnnotation(RPCBean.class, true); diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index 2088ca07..2babdb6a 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -42,6 +42,9 @@ class DubboUtil { private static Map consumerConfigMap = new ConcurrentHashMap<>(); + public static void stopDubbo() { + DubboBootstrap.getInstance().stop(); + } public static void initDubbo() { DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance(); diff --git a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java index 6af21def..bd657c69 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java @@ -29,6 +29,10 @@ public class JbootDubborpc extends JbootrpcBase { DubboUtil.initDubbo(); } + @Override + public void onStop() { + DubboUtil.stopDubbo(); + } @Override public T onServiceCreate(Class interfaceClass, JbootrpcReferenceConfig config) { diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index e3f5d094..b9890c64 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -306,6 +306,7 @@ public class JbootCoreConfig extends JFinalConfig { JbootAppListenerManager.me().onStop(); JbootScheduleManager.me().stop(); JbootSeataManager.me().stop(); + JbootrpcManager.me().stop(); } -- Gitee From 8b5560162c4aea76b05b7c4b970c53e2bbd0bafb Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 10:58:44 +0800 Subject: [PATCH 0096/1965] v3.1.0 --- .../test/rpc/commons/BlogServiceProvider.java | 2 +- .../io/jboot/test/rpc/dubbo/DubboServer.java | 2 +- .../DubboClientZookeeperDemo.java | 16 +++++++----- .../DubboServer1ZookeeperDemo.java | 26 ++++++++++++++----- .../DubboServer2ZookeeperDemo.java | 24 ++++++++++++----- 5 files changed, 49 insertions(+), 21 deletions(-) diff --git a/src/test/java/io/jboot/test/rpc/commons/BlogServiceProvider.java b/src/test/java/io/jboot/test/rpc/commons/BlogServiceProvider.java index 4c3e62d0..e428065d 100644 --- a/src/test/java/io/jboot/test/rpc/commons/BlogServiceProvider.java +++ b/src/test/java/io/jboot/test/rpc/commons/BlogServiceProvider.java @@ -10,7 +10,7 @@ public class BlogServiceProvider implements BlogService { @Override public String findById() { - System.out.println("BlogServiceProvider.findById() invoked."); + System.err.println("BlogServiceProvider.findById() invoked."); return "id from provider"; } diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java index 88c73611..6e2f1476 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java @@ -15,7 +15,7 @@ public class DubboServer { // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); - //dubbo 的协议配置 + //dubbo 的通信协议配置 JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.name", "dubbo"); JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "28080"); diff --git a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboClientZookeeperDemo.java b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboClientZookeeperDemo.java index 53999315..878a3b2b 100644 --- a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboClientZookeeperDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboClientZookeeperDemo.java @@ -12,13 +12,16 @@ public class DubboClientZookeeperDemo extends JbootController { public static void main(String[] args) { //jboot端口号配置 - JbootApplication.setBootArg("undertow.port", "8888"); + JbootApplication.setBootArg("undertow.port", "9999"); - //RPC配置 JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); - JbootApplication.setBootArg("jboot.rpc.callMode", "registry");//注册中心模式 - JbootApplication.setBootArg("jboot.rpc.registryType", "zookeeper");//注册中心的类型:zookeeper - JbootApplication.setBootArg("jboot.rpc.registryAddress", "127.0.0.1:2181");//注册中心,即zookeeper的地址 + + + // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "zookeeper"); + //注册中心地址,即zookeeper的地址 + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:2181"); + JbootApplication.run(args); } @@ -32,7 +35,6 @@ public class DubboClientZookeeperDemo extends JbootController { System.out.println("DubboClientZookeeperDemo.index()"); System.out.println(blogService); - System.out.println(blogService.findById()); - renderText("ok"); + renderText(blogService.findById()); } } diff --git a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer1ZookeeperDemo.java b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer1ZookeeperDemo.java index 80edab49..e397d33b 100644 --- a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer1ZookeeperDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer1ZookeeperDemo.java @@ -8,15 +8,29 @@ public class DubboServer1ZookeeperDemo { public static void main(String[] args) { //jboot端口号配置 - JbootApplication.setBootArg("undertow.port", "8081"); + JbootApplication.setBootArg("undertow.port", "9997"); + // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, + // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 + JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); - JbootApplication.setBootArg("jboot.rpc.callMode", "registry");//注册中心模式 - JbootApplication.setBootArg("jboot.rpc.registryType", "zookeeper");//注册中心的类型:zookeeper - JbootApplication.setBootArg("jboot.rpc.registryAddress", "127.0.0.1:2181");//注册中心,即zookeeper的地址 - //开启 @RPCBean 自动暴露功能,默认情况下是自动暴露的,但是 jboot.properties 文件关闭了,这里需要开启下 - JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); + + + + + // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "zookeeper"); + //注册中心地址,即zookeeper的地址 + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:2181"); + + + //dubbo 的通信协议配置,name 可以不用配置,默认值为 dubbo + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.name", "dubbo"); + //dubbo 的通信协议配置,如果port配置为-1,则会分配一个没有被占用的端口。 + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "28080"); + + JbootApplication.run(args); diff --git a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer2ZookeeperDemo.java b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer2ZookeeperDemo.java index 50b81796..3dfd0ee1 100644 --- a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer2ZookeeperDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer2ZookeeperDemo.java @@ -8,15 +8,27 @@ public class DubboServer2ZookeeperDemo { public static void main(String[] args) { //jboot端口号配置 - JbootApplication.setBootArg("undertow.port", "8082"); + JbootApplication.setBootArg("undertow.port", "9998"); + // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, + // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 + JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); - JbootApplication.setBootArg("jboot.rpc.callMode", "registry");//注册中心模式 - JbootApplication.setBootArg("jboot.rpc.registryType", "zookeeper");//注册中心的类型:zookeeper - JbootApplication.setBootArg("jboot.rpc.registryAddress", "127.0.0.1:2181");//注册中心,即zookeeper的地址 - //开启 @RPCBean 自动暴露功能,默认情况下是自动暴露的,但是 jboot.properties 文件关闭了,这里需要开启下 - JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); + + + + + // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "zookeeper"); + //注册中心地址,即zookeeper的地址 + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:2181"); + + + //dubbo 的通信协议配置,name 可以不用配置,默认值为 dubbo + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.name", "dubbo"); + //dubbo 的通信协议配置,如果port配置为-1,则会分配一个没有被占用的端口。 + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "28081"); JbootApplication.run(args); -- Gitee From f2d44da643891d3ec345f2345372e9b677472694 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 11:21:50 +0800 Subject: [PATCH 0097/1965] v3.1.0 --- .../java/io/jboot/components/rpc/Utils.java | 24 ++++++++++++++++--- .../jboot/components/rpc/dubbo/DubboUtil.java | 14 +++++------ .../jboot/components/rpc/motan/MotanUtil.java | 12 +++++----- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/Utils.java b/src/main/java/io/jboot/components/rpc/Utils.java index 4b548c95..cc89eb72 100644 --- a/src/main/java/io/jboot/components/rpc/Utils.java +++ b/src/main/java/io/jboot/components/rpc/Utils.java @@ -34,6 +34,13 @@ import java.util.Set; */ public class Utils { + /** + * 根据注解来设置对象内容,参考 dubbo 下的 AbstractConfig + * @see org.apache.dubbo.config.AbstractConfig#appendAnnotation + * @param annotationClass + * @param annotation + * @param appendTo + */ public static void appendAnnotation(Class annotationClass, Object annotation, Object appendTo) { Method[] methods = annotationClass.getMethods(); for (Method method : methods) { @@ -92,9 +99,19 @@ public class Utils { } - public static void appendChildConfig(Map appendTo, Map dataFrom, String prefix, String arrName) { + /** + * 设置子节点配置,比如 ProviderConfig 下的 MethodsConfig ,或者 MethodConfig 下的 ArgumentConfig 等 + * @param appendTo 要设置的对象 + * @param dataSource 设置子节点的数据源 + * @param prefix 要设置对象的配置前缀(jboot.properties 下的配置) + * @param arrName 要设置对象的属性名 + * @param + * @param + */ + public static void setChildConfig(Map appendTo, Map dataSource, String prefix, String arrName) { if (appendTo != null && !appendTo.isEmpty()) { for (Map.Entry entry : appendTo.entrySet()) { + String configKey = "default".equals(entry.getKey()) ? prefix + "." + arrName //"jboot.rpc.dubbo.method.argument" : prefix + "." + entry.getKey() + "." + arrName;//"jboot.rpc.dubbo.method."+entry.getKey()+".argument"; @@ -104,7 +121,7 @@ public class Utils { List argCfgList = new ArrayList<>(); Set arguments = StrUtil.splitToSetByComma(configValue); for (String arg : arguments) { - F fillObj = dataFrom.get(arg); + F fillObj = dataSource.get(arg); if (fillObj != null) { argCfgList.add(fillObj); } @@ -112,12 +129,13 @@ public class Utils { if (!argCfgList.isEmpty()) { try { //setArguments/setMethods/setRegistries - String setterMethodName = arrName.endsWith("registry") + String setterMethodName = arrName.equals("registry") ? "setRegistries" : "set" + StrUtil.firstCharToUpperCase(arrName) + "s"; Method method = entry.getValue().getClass().getDeclaredMethod(setterMethodName, List.class); method.invoke(entry.getValue(), argCfgList); + } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index 2babdb6a..12dc6d52 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -121,14 +121,14 @@ class DubboUtil { //方法配置 配置 Map methodConfigs = configs(MethodConfig.class, "jboot.rpc.dubbo.method"); - Utils.appendChildConfig(methodConfigs,argumentConfigs,"jboot.rpc.dubbo.method","argument"); + Utils.setChildConfig(methodConfigs,argumentConfigs,"jboot.rpc.dubbo.method","argument"); //消费者 配置 Map consumerConfigs = configs(ConsumerConfig.class, "jboot.rpc.dubbo.consumer"); - Utils.appendChildConfig(consumerConfigs,methodConfigs,"jboot.rpc.dubbo.consumer","method"); - Utils.appendChildConfig(consumerConfigs,protocolConfigs,"jboot.rpc.dubbo.consumer","protocol"); - Utils.appendChildConfig(consumerConfigs,registryConfigs,"jboot.rpc.dubbo.consumer","registry"); + Utils.setChildConfig(consumerConfigs,methodConfigs,"jboot.rpc.dubbo.consumer","method"); + Utils.setChildConfig(consumerConfigs,protocolConfigs,"jboot.rpc.dubbo.consumer","protocol"); + Utils.setChildConfig(consumerConfigs,registryConfigs,"jboot.rpc.dubbo.consumer","registry"); if (consumerConfigs != null && !consumerConfigs.isEmpty()) { @@ -142,9 +142,9 @@ class DubboUtil { //服务提供者 配置 Map providerConfigs = configs(ProviderConfig.class, "jboot.rpc.dubbo.provider"); - Utils.appendChildConfig(providerConfigs,methodConfigs,"jboot.rpc.dubbo.provider","method"); - Utils.appendChildConfig(providerConfigs,protocolConfigs,"jboot.rpc.dubbo.provider","protocol"); - Utils.appendChildConfig(providerConfigs,registryConfigs,"jboot.rpc.dubbo.provider","registry"); + Utils.setChildConfig(providerConfigs,methodConfigs,"jboot.rpc.dubbo.provider","method"); + Utils.setChildConfig(providerConfigs,protocolConfigs,"jboot.rpc.dubbo.provider","protocol"); + Utils.setChildConfig(providerConfigs,registryConfigs,"jboot.rpc.dubbo.provider","registry"); if (providerConfigs != null && !providerConfigs.isEmpty()) { providerConfigMap.putAll(providerConfigs); diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java index 0cf091eb..86d81890 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java @@ -61,9 +61,9 @@ public class MotanUtil { //baseService 配置 Map serviceConfigs = configs(ServiceConfig.class, "jboot.rpc.motan.service"); - Utils.appendChildConfig(serviceConfigs,methodConfigs,"jboot.rpc.motan.service","method"); - Utils.appendChildConfig(serviceConfigs,protocolConfigs,"jboot.rpc.motan.service","protocol"); - Utils.appendChildConfig(serviceConfigs,registryConfigs,"jboot.rpc.motan.service","registry"); + Utils.setChildConfig(serviceConfigs,methodConfigs,"jboot.rpc.motan.service","method"); + Utils.setChildConfig(serviceConfigs,protocolConfigs,"jboot.rpc.motan.service","protocol"); + Utils.setChildConfig(serviceConfigs,registryConfigs,"jboot.rpc.motan.service","registry"); if (serviceConfigs != null && !serviceConfigs.isEmpty()) { @@ -72,9 +72,9 @@ public class MotanUtil { //baseReferer 配置 Map refererConfigs = configs(RefererConfig.class, "jboot.rpc.motan.referer"); - Utils.appendChildConfig(refererConfigs,methodConfigs,"jboot.rpc.motan.referer","method"); - Utils.appendChildConfig(refererConfigs,protocolConfigs,"jboot.rpc.motan.referer","protocol"); - Utils.appendChildConfig(refererConfigs,registryConfigs,"jboot.rpc.motan.referer","registry"); + Utils.setChildConfig(refererConfigs,methodConfigs,"jboot.rpc.motan.referer","method"); + Utils.setChildConfig(refererConfigs,protocolConfigs,"jboot.rpc.motan.referer","protocol"); + Utils.setChildConfig(refererConfigs,registryConfigs,"jboot.rpc.motan.referer","registry"); if (refererConfigs != null && !refererConfigs.isEmpty()) { baseRefererConfigMap.putAll(refererConfigs); -- Gitee From be7d0045d80465135352657a32fd58549a366957 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 17:42:00 +0800 Subject: [PATCH 0098/1965] v3.1.0 --- src/main/java/io/jboot/utils/ClassScanner.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index cd426245..b6d0d5cd 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -203,17 +203,23 @@ public class ClassScanner { } static { - String scanJarPrefx = JbootConfigManager.me().getConfigValue("jboot.app.scanner.scanJarPrefix"); - if (scanJarPrefx != null) { - for (String prefix : scanJarPrefx.split(",")) { - addScanJarPrefix(prefix.trim()); + String scanJarPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.scanJarPrefix"); + if (scanJarPrefix != null) { + String[] prefixes = scanJarPrefix.split(","); + for (String prefix : prefixes) { + if (prefix != null && prefix.trim().length() > 0) { + addScanJarPrefix(prefix.trim()); + } } } String unScanJarPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.unScanJarPrefix"); if (unScanJarPrefix != null) { - for (String prefix : unScanJarPrefix.split(",")) { - addUnscanJarPrefix(prefix.trim()); + String[] prefixes = unScanJarPrefix.split(","); + for (String prefix : prefixes) { + if (prefix != null && prefix.trim().length() > 0) { + addUnscanJarPrefix(prefix.trim()); + } } } } -- Gitee From e6c992be81cba4d2e43d49a0a61cba57090e8e80 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 18:33:36 +0800 Subject: [PATCH 0099/1965] v3.1.0 --- doc/docs/rpc.md | 197 +++++++++++++++++++++++++++++++++++---------- doc/docs/rpc2.x.md | 156 +++++++++++++++++++++++++++++++++++ 2 files changed, 310 insertions(+), 43 deletions(-) create mode 100644 doc/docs/rpc2.x.md diff --git a/doc/docs/rpc.md b/doc/docs/rpc.md index 30116bc3..19a1cd35 100644 --- a/doc/docs/rpc.md +++ b/doc/docs/rpc.md @@ -1,5 +1,9 @@ # RPC 远程调用 +## 说明 + +此文档只适用于 jboot v3.1.0 以上,之前的版本请参考 [这里](./rpc2.x.md) 。 + ## 目录 - 配置 @@ -13,15 +17,13 @@ 例如 : ``` jboot.rpc.type = dubbo -jboot.rpc.callMode = direct -jboot.rpc.directUrl = 127.0.0.1:8000 +jboot.rpc.urls = com.yourdomain.AAAService:127.0.0.1:8080,com.yourdomain.XXXService:127.0.0.1:8080, +jboot.rpc.autoExportEnable = true ``` -- jboot.rpc.type : RPC 的类型,不配置默认为 Dubbo -- jboot.rpc.callMode : RPC 的调用方式 - - direct : 直联模式 - - registry : 注册中心模式(服务注册和服务发现) -- jboot.rpc.directUrl : 当 callMode 为 direct 直联模式时,需要配置直联模式的服务器 IP 地址和端口号。 +- jboot.rpc.type : RPC 的类型,目前只支持 dubbo 和 motan +- jboot.rpc.urls : 一般不用配置,只有直连模式下才会去配置,此处是配置 Service接口和URL地址的映射关系。 +- jboot.rpc.autoExportEnable : 当 Jboot 启动的时候,是否自动暴露 @RPCBean 注解的接口。 > 更多的配置请查看 [config.md](./config.md) @@ -31,7 +33,7 @@ jboot.rpc.directUrl = 127.0.0.1:8000 - 1、定义接口 - 2、编写实现类 -- 3、启动 Server 暴露服务 +- 3、启动 Server(Provider) 暴露服务 - 4、启动客户端、通过 RPC 调用 Server 提供的服务 @@ -73,6 +75,25 @@ public class BlogServiceProvider implements BlogService { public class DubboServer { public static void main(String[] args) { + + JbootApplication.setBootArg("undertow.port", "9998"); + + JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); + + // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, + // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 + JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); + + //dubbo 的通信协议配置 + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.name", "dubbo"); + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "28080"); + + + // dubbo 注册中心的配置, + // 当不配置注册中心的时候,默认此服务只提供了直联模式的请求 + // JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "zookeeper"); + // JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:2181"); + JbootApplication.run(args); System.out.println("DubboServer started..."); @@ -81,6 +102,8 @@ public class DubboServer { } ``` +备注:以上的 JbootApplication.setBootArg() 里设置的内容,都可以配置到 jboot.properties 里。 + **启动客户端、通过 RPC 调用 Server 提供的服务** ```java @@ -90,8 +113,14 @@ public class DubboClient extends JbootController{ public static void main(String[] args) { - //Undertow端口号配置 - JbootApplication.setBootArg("undertow.port", "8888"); + //Undertow端口号配置 + JbootApplication.setBootArg("undertow.port", "9999"); + + //RPC配置 + JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); + + //设置直连模式,方便调试,默认为注册中心 + JbootApplication.setBootArg("jboot.rpc.urls", "io.jboot.test.rpc.commons.BlogService:127.0.0.1:28080"); JbootApplication.run(args); } @@ -104,53 +133,135 @@ public class DubboClient extends JbootController{ System.out.println(blogService); renderText("blogId : " + blogService.findById()); } - } ``` ## 高级功能 -在以上的示例中,使用到了两个注解: -- @RPCBean,标示当前服务于RPC服务 -- @RPCInject,RPC注入赋值 +### 更多的 dubbo 配置 -虽然以上示例中,`@RPCBean` 和 `@RPCInject` 没有添加任何的参数,但实际是他们提供了非常丰富的配置。 +目前,jboot 不支持通过 jboot.properties 对 service 和 reference 配置,但是可以对 provider 和 consumer 的配置, +在通过 @RPCInject 来再次复制给 service 或者 reference,结果也等同于对 service 和 reference 配置进行配置了。 -以下分别是 `@RPCBean` 和 `@RPCInject` 的定义: -RPCBean : -```java +#### application -public @interface RPCBean { +http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-application.html - int port() default 0; - int timeout() default -1; - int actives() default -1; - String group() default ""; - String version() default ""; +对应的 jboot 的配置前缀为: `jboot.rpc.application.service` + +例如文档里提到的 version,在 jboot.properties 对应的配置为 - //当一个Service类实现对个接口的时候, - //可以通过这个排除不暴露某个实现接口 - Class[] exclude() default Void.class; -} +``` +jboot.rpc.dubbo.application.version = xx.xx.xx ``` +其他同理。 -RPCInject : +#### registry -```java +http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-registry.html -public @interface RPCInject { - - int port() default 0; - int timeout() default -1; - int retries() default -1; - int actives() default -1; - String group() default ""; - String version() default ""; - String loadbalance() default ""; - String async() default ""; - String check() default ""; -} -``` \ No newline at end of file + +对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.registry` + +例如文档里提到的 address,在 jboot.properties 对应的配置为 + +``` +jboot.rpc.dubbo.registry.address = xx.xx.xx +``` + +其他同理。 + + +多 registry (多注册中心) + +默认的注册中心配置内容如下: + +``` +jboot.rpc.dubbo.registry.address = xx.xx.xx +jboot.rpc.dubbo.registry.port = xx.xx.xx +jboot.rpc.dubbo.registry.username = xx.xx.xx +jboot.rpc.dubbo.registry.password = xx.xx.xx +``` + +多注册中心可以配置如下(多 protocol 、多 consumer、多provider 都是同理): + + +``` +jboot.rpc.dubbo.registry.address = xx.xx.xx +jboot.rpc.dubbo.registry.port = xx.xx.xx +jboot.rpc.dubbo.registry.username = xx.xx.xx +jboot.rpc.dubbo.registry.password = xx.xx.xx + + +jboot.rpc.dubbo.registry.other1.address = xx.xx.xx +jboot.rpc.dubbo.registry.other1.port = xx.xx.xx +jboot.rpc.dubbo.registry.other1.username = xx.xx.xx +jboot.rpc.dubbo.registry.other1.password = xx.xx.xx + + +jboot.rpc.dubbo.registry.other2.address = xx.xx.xx +jboot.rpc.dubbo.registry.other2.port = xx.xx.xx +jboot.rpc.dubbo.registry.other2.username = xx.xx.xx +jboot.rpc.dubbo.registry.other2.password = xx.xx.xx +``` + +这样,在系统中就存在了多个注册中心,名称(name 或者 id)分别为 default(没有name的时候,默认为default)、other1、other2,这样,当一个服务(或者接口)需要在 +多个注册中心暴露的时候,只需要在其 registry 配置相应的 id 即可。 + +例如: + +``` +jboot.rpc.dubbo.provider.address = default,other1 +``` + +若当服务没有指定注册中心,那么该服务会在这三个注册中心同时暴露。 + + +#### protocol + +http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-protocol.html + + +对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.protocol` + +例如文档里提到的 host,在 jboot.properties 对应的配置为 + +``` +jboot.rpc.dubbo.protocol.host = 127.0.0.1 +``` + +其他同理。 + + +#### provider + +http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-provider.html + + +对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.provider` + +例如文档里提到的 host,在 jboot.properties 对应的配置为 + +``` +jboot.rpc.dubbo.provider.host = 127.0.0.1 +``` + +其他同理。 + +#### consumer + +http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-consumer.html + + +对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.consumer` + +例如文档里提到的 timeout,在 jboot.properties 对应的配置为 + +``` +jboot.rpc.dubbo.consumer.timeout = 127.0.0.1 +``` + +其他同理。 \ No newline at end of file diff --git a/doc/docs/rpc2.x.md b/doc/docs/rpc2.x.md new file mode 100644 index 00000000..30116bc3 --- /dev/null +++ b/doc/docs/rpc2.x.md @@ -0,0 +1,156 @@ +# RPC 远程调用 + +## 目录 + +- 配置 +- 开始使用 +- 高级功能 + +## 配置 +在 Jboot 中,默认实现了对 Dubbo、motan 的 RPC 调用支持。在使用 RPC 远程调用之前,需要做一些基本的配置。 + + +例如 : +``` +jboot.rpc.type = dubbo +jboot.rpc.callMode = direct +jboot.rpc.directUrl = 127.0.0.1:8000 +``` + +- jboot.rpc.type : RPC 的类型,不配置默认为 Dubbo +- jboot.rpc.callMode : RPC 的调用方式 + - direct : 直联模式 + - registry : 注册中心模式(服务注册和服务发现) +- jboot.rpc.directUrl : 当 callMode 为 direct 直联模式时,需要配置直联模式的服务器 IP 地址和端口号。 + +> 更多的配置请查看 [config.md](./config.md) + +## 开始使用 + +一般情况下,RPC 调用需要以下几个步骤: + +- 1、定义接口 +- 2、编写实现类 +- 3、启动 Server 暴露服务 +- 4、启动客户端、通过 RPC 调用 Server 提供的服务 + + +**定义接口** + +```java + +public interface BlogService { + + public String findById(); + public List findAll(); +} +``` + +**编写实现类** + +```java + + +@RPCBean +public class BlogServiceProvider implements BlogService { + + @Override + public String findById() { + return "id from provider"; + } + + @Override + public List findAll() { + return Lists.newArrayList("item1","item2"); + } +} +``` + +**启动 Server 暴露服务** + +```java + +public class DubboServer { + + public static void main(String[] args) { + + JbootApplication.run(args); + System.out.println("DubboServer started..."); + + } +} +``` + + +**启动客户端、通过 RPC 调用 Server 提供的服务** +```java + +@RequestMapping("/dubbo") +public class DubboClient extends JbootController{ + + public static void main(String[] args) { + + //Undertow端口号配置 + JbootApplication.setBootArg("undertow.port", "8888"); + + JbootApplication.run(args); + } + + + @RPCInject + private BlogService blogService; + + public void index() { + System.out.println(blogService); + renderText("blogId : " + blogService.findById()); + } + +} +``` + + +## 高级功能 + +在以上的示例中,使用到了两个注解: +- @RPCBean,标示当前服务于RPC服务 +- @RPCInject,RPC注入赋值 + +虽然以上示例中,`@RPCBean` 和 `@RPCInject` 没有添加任何的参数,但实际是他们提供了非常丰富的配置。 + +以下分别是 `@RPCBean` 和 `@RPCInject` 的定义: + +RPCBean : +```java + +public @interface RPCBean { + + int port() default 0; + int timeout() default -1; + int actives() default -1; + String group() default ""; + String version() default ""; + + //当一个Service类实现对个接口的时候, + //可以通过这个排除不暴露某个实现接口 + Class[] exclude() default Void.class; +} +``` + + +RPCInject : + +```java + +public @interface RPCInject { + + int port() default 0; + int timeout() default -1; + int retries() default -1; + int actives() default -1; + String group() default ""; + String version() default ""; + String loadbalance() default ""; + String async() default ""; + String check() default ""; +} +``` \ No newline at end of file -- Gitee From c5ce6dc7e4266a2b08da0482ab509334f31d90af Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 18:36:26 +0800 Subject: [PATCH 0100/1965] v3.1.0 --- .../jboot/components/rpc/JbootrpcConfig.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java index 0235ba8c..78b01fd1 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java @@ -34,6 +34,14 @@ public class JbootrpcConfig { //com.service.AAAService:127.0.0.1:8080,com.service.XXXService:127.0.0.1:8080 private Map urls; + //服务的provider指定,可以通过注解 @RPCBean 指定,也可以通过此处指定,此处的配置优先于注解 + //com.service.AAAService:providerName,com.service.XXXService:providerName + private Map providers; + + //服务的consumer指定,可以通过注解 @RPCInject 指定,也可以通过此处指定,此处的配置优先于注解 + //com.service.AAAService:providerName,com.service.XXXService:providerName + private Map consumers; + //本地自动暴露 @RPCBean 的 service private boolean autoExportEnable = true; @@ -58,6 +66,30 @@ public class JbootrpcConfig { return urls == null ? null : urls.get(serviceClass); } + public Map getProviders() { + return providers; + } + + public void setProviders(Map providers) { + this.providers = providers; + } + + public String getProvider(String serviceClass){ + return providers == null ? null : providers.get(serviceClass); + } + + public Map getConsumers() { + return consumers; + } + + public void setConsumers(Map consumers) { + this.consumers = consumers; + } + + public String getConsumer(String serviceClass){ + return consumers == null ? null : consumers.get(serviceClass); + } + public boolean isAutoExportEnable() { return autoExportEnable; } -- Gitee From 0b7b086e89c7530d2720525f48ec03b821b2ac49 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 18:49:49 +0800 Subject: [PATCH 0101/1965] v3.1.0 --- .../jboot/components/rpc/dubbo/DubboUtil.java | 9 +++++++++ .../components/rpc/dubbo/JbootDubborpc.java | 13 ++++++++++++ .../components/rpc/motan/JbootMotanrpc.java | 20 +++++++++++++++---- .../jboot/components/rpc/motan/MotanUtil.java | 18 ++++++++++++----- 4 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index 12dc6d52..70e80047 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -198,6 +198,15 @@ class DubboUtil { return serviceConfig; } + public static ConsumerConfig getConsumer(String name){ + return consumerConfigMap.get(name); + } + + + public static ProviderConfig getProvider(String name){ + return providerConfigMap.get(name); + } + private static T config(Class clazz, String prefix) { return JbootConfigManager.me().get(clazz, prefix, null); diff --git a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java index bd657c69..d49f7d7f 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java @@ -38,10 +38,17 @@ public class JbootDubborpc extends JbootrpcBase { public T onServiceCreate(Class interfaceClass, JbootrpcReferenceConfig config) { ReferenceConfig reference = DubboUtil.toReferenceConfig(config); reference.setInterface(interfaceClass); + String directUrl = rpcConfig.getUrl(interfaceClass.getName()); if (StrUtil.isNotBlank(directUrl)){ reference.setUrl(directUrl); } + + String consumer = rpcConfig.getConsumer(interfaceClass.getName()); + if (consumer != null){ + reference.setConsumer(DubboUtil.getConsumer(consumer)); + } + return reference.get(); } @@ -51,6 +58,12 @@ public class JbootDubborpc extends JbootrpcBase { ServiceConfig service = DubboUtil.toServiceConfig(config); service.setInterface(interfaceClass); service.setRef((T) object); + + String provider = rpcConfig.getProvider(interfaceClass.getName()); + if (provider != null){ + service.setProvider(DubboUtil.getProvider(provider)); + } + service.export(); return true; } diff --git a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java index 04c48454..2605c63d 100644 --- a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java +++ b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java @@ -36,10 +36,17 @@ public class JbootMotanrpc extends JbootrpcBase { public T onServiceCreate(Class interfaceClass, JbootrpcReferenceConfig config) { RefererConfig referer = MotanUtil.toRefererConfig(config); referer.setInterface(interfaceClass); + String directUrl = rpcConfig.getUrl(interfaceClass.getName()); if (StrUtil.isNotBlank(directUrl)){ referer.setDirectUrl(directUrl); } + + String consumer = rpcConfig.getConsumer(interfaceClass.getName()); + if (consumer != null){ + referer.setBasicReferer(MotanUtil.getBaseReferer(consumer)); + } + return referer.getRef(); } @@ -51,11 +58,16 @@ public class JbootMotanrpc extends JbootrpcBase { MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, false); - ServiceConfig motanServiceConfig = MotanUtil.toServiceConfig(config); - motanServiceConfig.setInterface(interfaceClass); - motanServiceConfig.setRef((T) object); - motanServiceConfig.export(); + ServiceConfig serviceConfig = MotanUtil.toServiceConfig(config); + serviceConfig.setInterface(interfaceClass); + serviceConfig.setRef((T) object); + + String provider = rpcConfig.getProvider(interfaceClass.getName()); + if (provider != null){ + serviceConfig.setBasicService(MotanUtil.getBaseService(provider)); + } + serviceConfig.export(); MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true); } diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java index 86d81890..ac8692ef 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java @@ -36,9 +36,8 @@ public class MotanUtil { private static Map protocolConfigMap = new ConcurrentHashMap<>(); private static Map registryConfigMap = new ConcurrentHashMap<>(); - private static Map baseRefererConfigMap = new ConcurrentHashMap<>(); - private static Map baseServiceConfigMap = new ConcurrentHashMap<>(); -// private static Map methodConfigMap = new ConcurrentHashMap<>(); + private static Map baseRefererConfigMap = new ConcurrentHashMap<>(); + private static Map baseServiceConfigMap = new ConcurrentHashMap<>(); public static void initMotan() { @@ -60,7 +59,7 @@ public class MotanUtil { //baseService 配置 - Map serviceConfigs = configs(ServiceConfig.class, "jboot.rpc.motan.service"); + Map serviceConfigs = configs(BasicServiceInterfaceConfig.class, "jboot.rpc.motan.service"); Utils.setChildConfig(serviceConfigs,methodConfigs,"jboot.rpc.motan.service","method"); Utils.setChildConfig(serviceConfigs,protocolConfigs,"jboot.rpc.motan.service","protocol"); Utils.setChildConfig(serviceConfigs,registryConfigs,"jboot.rpc.motan.service","registry"); @@ -71,7 +70,7 @@ public class MotanUtil { } //baseReferer 配置 - Map refererConfigs = configs(RefererConfig.class, "jboot.rpc.motan.referer"); + Map refererConfigs = configs(BasicRefererInterfaceConfig.class, "jboot.rpc.motan.referer"); Utils.setChildConfig(refererConfigs,methodConfigs,"jboot.rpc.motan.referer","method"); Utils.setChildConfig(refererConfigs,protocolConfigs,"jboot.rpc.motan.referer","protocol"); Utils.setChildConfig(refererConfigs,registryConfigs,"jboot.rpc.motan.referer","registry"); @@ -169,6 +168,15 @@ public class MotanUtil { } + public static BasicRefererInterfaceConfig getBaseReferer(String name){ + return baseRefererConfigMap.get(name); + } + + + public static BasicServiceInterfaceConfig getBaseService(String name){ + return baseServiceConfigMap.get(name); + } + private static Map configs(Class clazz, String prefix) { return JbootConfigUtil.getConfigModels(clazz, prefix); } -- Gitee From df4af92791d916fe94778d2f4e7609948ab7c0a9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 18:54:57 +0800 Subject: [PATCH 0102/1965] v3.1.0 --- doc/docs/rpc.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/docs/rpc.md b/doc/docs/rpc.md index 19a1cd35..ee9c7f5c 100644 --- a/doc/docs/rpc.md +++ b/doc/docs/rpc.md @@ -17,12 +17,16 @@ 例如 : ``` jboot.rpc.type = dubbo -jboot.rpc.urls = com.yourdomain.AAAService:127.0.0.1:8080,com.yourdomain.XXXService:127.0.0.1:8080, +jboot.rpc.urls = com.yourdomain.AAAService:127.0.0.1:8080,com.yourdomain.XXXService:127.0.0.1:8080 +jboot.rpc.providers = com.yourdomain.AAAService:providerName,com.yourdomain.XXXService:providerName +jboot.rpc.consumers = com.yourdomain.AAAService:consumerName,com.yourdomain.XXXService:consumerName jboot.rpc.autoExportEnable = true ``` - jboot.rpc.type : RPC 的类型,目前只支持 dubbo 和 motan - jboot.rpc.urls : 一般不用配置,只有直连模式下才会去配置,此处是配置 Service接口和URL地址的映射关系。 +- jboot.rpc.providers : 配置 Service 和 Provider 的映射关系( Motan下配置的是 Service 和 BasicService 的映射关系)。 +- jboot.rpc.consumers : 配置 Reference 和 consumer 的映射关系( Motan下配置的是 Referer 和 BaseReferer 的映射关系)。 - jboot.rpc.autoExportEnable : 当 Jboot 启动的时候,是否自动暴露 @RPCBean 注解的接口。 > 更多的配置请查看 [config.md](./config.md) -- Gitee From 28e013b90894ed4c88e2b8b1d290f1ecc23ee8df Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 20:46:29 +0800 Subject: [PATCH 0103/1965] v3.1.0 --- doc/docs/rpc.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/doc/docs/rpc.md b/doc/docs/rpc.md index ee9c7f5c..def2e6c5 100644 --- a/doc/docs/rpc.md +++ b/doc/docs/rpc.md @@ -6,10 +6,59 @@ ## 目录 +- 添加依赖 - 配置 - 开始使用 - 高级功能 +## 添加依赖 + +Jboot 支持 dubbo 和 motan,假设我们需要使用 dubbo 作为底层的 RPC 框架,需要添加如下依赖: + +``` + + org.apache.dubbo + dubbo + ${dubbo.version} + + + + org.apache.dubbo + dubbo-dependencies-zookeeper + ${dubbo.version} + pom + +``` + +如果使用 motan,需要添加如下依赖: + +``` + + com.weibo + motan-core + ${motan.version} + + + + com.weibo + motan-transport-netty4 + ${motan.version} + + + + + com.weibo + motan-registry-consul + ${motan.version} + + + + com.weibo + motan-registry-zookeeper + ${motan.version} + +``` + ## 配置 在 Jboot 中,默认实现了对 Dubbo、motan 的 RPC 调用支持。在使用 RPC 远程调用之前,需要做一些基本的配置。 -- Gitee From f9ffdf1ebf9f9dc7dc2df0226a89b7c5410d8a4f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 20:53:01 +0800 Subject: [PATCH 0104/1965] v3.1.0 --- doc/docs/rpc.md | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/doc/docs/rpc.md b/doc/docs/rpc.md index def2e6c5..bb26f492 100644 --- a/doc/docs/rpc.md +++ b/doc/docs/rpc.md @@ -78,7 +78,6 @@ jboot.rpc.autoExportEnable = true - jboot.rpc.consumers : 配置 Reference 和 consumer 的映射关系( Motan下配置的是 Referer 和 BaseReferer 的映射关系)。 - jboot.rpc.autoExportEnable : 当 Jboot 启动的时候,是否自动暴露 @RPCBean 注解的接口。 -> 更多的配置请查看 [config.md](./config.md) ## 开始使用 @@ -155,7 +154,7 @@ public class DubboServer { } ``` -备注:以上的 JbootApplication.setBootArg() 里设置的内容,都可以配置到 jboot.properties 里。 +>备注:以上的 JbootApplication.setBootArg() 里设置的内容,都可以配置到 jboot.properties 里。 **启动客户端、通过 RPC 调用 Server 提供的服务** @@ -194,7 +193,7 @@ public class DubboClient extends JbootController{ ### 更多的 dubbo 配置 -目前,jboot 不支持通过 jboot.properties 对 service 和 reference 配置,但是可以对 provider 和 consumer 的配置, +目前,jboot 不支持通过 jboot.properties 直接对 service 和 reference 配置,但是可以对 provider 和 consumer 的配置, 在通过 @RPCInject 来再次复制给 service 或者 reference,结果也等同于对 service 和 reference 配置进行配置了。 @@ -202,15 +201,15 @@ public class DubboClient extends JbootController{ http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-application.html -对应的 jboot 的配置前缀为: `jboot.rpc.application.service` +对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.application` -例如文档里提到的 version,在 jboot.properties 对应的配置为 +例如: ``` +jboot.rpc.dubbo.application.name = xx.xx.xx jboot.rpc.dubbo.application.version = xx.xx.xx ``` -其他同理。 #### registry @@ -219,16 +218,15 @@ http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-registry.html 对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.registry` -例如文档里提到的 address,在 jboot.properties 对应的配置为 +例如: ``` jboot.rpc.dubbo.registry.address = xx.xx.xx ``` -其他同理。 -多 registry (多注册中心) +**多 registry (多注册中心)** 默认的注册中心配置内容如下: @@ -280,13 +278,13 @@ http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-protocol.html 对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.protocol` -例如文档里提到的 host,在 jboot.properties 对应的配置为 +例如: ``` jboot.rpc.dubbo.protocol.host = 127.0.0.1 +jboot.rpc.dubbo.protocol.port = 28080 ``` -其他同理。 #### provider @@ -296,13 +294,13 @@ http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-provider.html 对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.provider` -例如文档里提到的 host,在 jboot.properties 对应的配置为 +例如: ``` jboot.rpc.dubbo.provider.host = 127.0.0.1 ``` -其他同理。 + #### consumer @@ -311,10 +309,9 @@ http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-consumer.html 对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.consumer` -例如文档里提到的 timeout,在 jboot.properties 对应的配置为 +例如: ``` jboot.rpc.dubbo.consumer.timeout = 127.0.0.1 ``` -其他同理。 \ No newline at end of file -- Gitee From c09f487909d882b045d69400c0843cccfbe7dc53 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 21 Mar 2020 22:09:03 +0800 Subject: [PATCH 0105/1965] v3.1.0 --- .../gateway/JbootGatewayManager.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/main/java/io/jboot/components/gateway/JbootGatewayManager.java diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java new file mode 100644 index 00000000..7c3efadc --- /dev/null +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.gateway; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/21 + */ +public class JbootGatewayManager { + + private static JbootGatewayManager me = new JbootGatewayManager(); + + public static JbootGatewayManager me(){ + return me; + } +} -- Gitee From 5c0af82c87361715ca1c769dcfb06daf099aec16 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 10:20:33 +0800 Subject: [PATCH 0106/1965] add gateway component --- .../jboot/components/gateway/GatewayUtil.java | 176 +++++++++++ .../gateway/JbootGatawayHandler.java | 53 ++++ .../gateway/JbootGatewayConfig.java | 285 ++++++++++++++++++ .../gateway/JbootGatewayManager.java | 38 +++ 4 files changed, 552 insertions(+) create mode 100644 src/main/java/io/jboot/components/gateway/GatewayUtil.java create mode 100644 src/main/java/io/jboot/components/gateway/JbootGatawayHandler.java create mode 100644 src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java diff --git a/src/main/java/io/jboot/components/gateway/GatewayUtil.java b/src/main/java/io/jboot/components/gateway/GatewayUtil.java new file mode 100644 index 00000000..43b6974c --- /dev/null +++ b/src/main/java/io/jboot/components/gateway/GatewayUtil.java @@ -0,0 +1,176 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.gateway; + +import com.jfinal.log.Log; +import io.jboot.exception.JbootException; +import org.apache.commons.io.IOUtils; + +import javax.net.ssl.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.ProtocolException; +import java.net.URL; +import java.security.cert.X509Certificate; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +public class GatewayUtil { + + private static final Log LOG = Log.getLog(GatewayUtil.class); + + private static int READ_TIMEOUT = 10000; + private static int CONNECT_TIMEOUT = 5000; + + + public static void sendRequest(String url, HttpServletRequest request, HttpServletResponse response) { + HttpURLConnection connection = null; + InputStream inputStream = null; + OutputStream outputStream = null; + try { + connection = getConnection(url); + + configConnection(connection, request); + + connection.connect(); + + inputStream = getInutStream(connection); + outputStream = connection.getOutputStream(); + + IOUtils.copy(inputStream, outputStream); + + configResponse(response,connection); + } catch (Exception ex) { + LOG.warn(ex.toString(), ex); + } finally { + if (connection != null) { + connection.disconnect(); + } + quetlyClose(inputStream,outputStream); + } + } + + + private static void quetlyClose(Closeable ... closeables){ + for (Closeable closeable : closeables){ + if (closeable != null){ + try { + closeable.close(); + } catch (IOException e) { + } + } + } + } + + private static void configResponse(HttpServletResponse response, HttpURLConnection connection) throws IOException { + response.setContentType(connection.getContentType()); + response.setStatus(connection.getResponseCode()); + + Map> headerFields = connection.getHeaderFields(); + if (headerFields != null && !headerFields.isEmpty()){ + Set headerNames = headerFields.keySet(); + for (String headerName : headerNames){ + response.setHeader(headerName,connection.getHeaderField(headerName)); + } + } + } + + private static InputStream getInutStream(HttpURLConnection connection) throws IOException { + return connection.getResponseCode() >= 400 + ? connection.getErrorStream() + : connection.getInputStream(); + } + + + + private static void configConnection(HttpURLConnection connection, HttpServletRequest request) throws ProtocolException { + + connection.setReadTimeout(READ_TIMEOUT); + connection.setConnectTimeout(CONNECT_TIMEOUT); + connection.setInstanceFollowRedirects(true); + + connection.setRequestMethod(request.getMethod()); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()){ + String headerName = headerNames.nextElement(); + connection.setRequestProperty(headerName,request.getHeader(headerName)); + } + } + + private static HttpURLConnection getConnection(String urlString) { + try { + if (urlString.toLowerCase().startsWith("https")) { + return getHttpsConnection(urlString); + } else { + return getHttpConnection(urlString); + } + } catch (Throwable ex) { + throw new JbootException(ex); + } + } + + private static HttpURLConnection getHttpConnection(String urlStr) throws Exception { + URL url = new URL(urlStr); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + return conn; + } + + private static HttpsURLConnection getHttpsConnection(String urlString) throws Exception { + URL url = new URL(urlString); + HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); + conn.setHostnameVerifier(hnv); + SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); + if (sslContext != null) { + TrustManager[] tm = {trustAnyTrustManager}; + sslContext.init(null, tm, null); + SSLSocketFactory ssf = sslContext.getSocketFactory(); + conn.setSSLSocketFactory(ssf); + } + return conn; + } + + private static X509TrustManager trustAnyTrustManager = new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + }; + + private static HostnameVerifier hnv = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + + +} diff --git a/src/main/java/io/jboot/components/gateway/JbootGatawayHandler.java b/src/main/java/io/jboot/components/gateway/JbootGatawayHandler.java new file mode 100644 index 00000000..fd5c381c --- /dev/null +++ b/src/main/java/io/jboot/components/gateway/JbootGatawayHandler.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.gateway; + +import com.jfinal.handler.Handler; +import io.jboot.utils.StrUtil; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/22 + */ +public class JbootGatawayHandler extends Handler { + + + @Override + public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { + String uri = JbootGatewayManager.me().matchingURI(request); + if (StrUtil.isNotBlank(uri)) { + doProcessGateway(uri, request, response); + isHandled[0] = true; + } else { + next.handle(target, request, response, isHandled); + } + } + + private void doProcessGateway(String uri, HttpServletRequest request, HttpServletResponse response) { + StringBuilder url = new StringBuilder(uri); + if (StrUtil.isNotBlank(request.getRequestURI())){ + url.append(request.getRequestURI()); + } + if (StrUtil.isNotBlank(request.getQueryString())){ + url.append("?").append(request.getQueryString()); + } + + GatewayUtil.sendRequest(url.toString(),request,response); + } +} diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java new file mode 100644 index 00000000..82cb577b --- /dev/null +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -0,0 +1,285 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.components.gateway; + +import io.jboot.utils.StrUtil; + +import javax.servlet.http.HttpServletRequest; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/22 + */ +public class JbootGatewayConfig implements Serializable { + + private String name; + private String uri; + private boolean enable = false; + + private String[] pathEquals; + private String[] pathContains; + private String[] pathStartsWith; + private String[] pathEndswith; + + + private String[] hostEquals; + private String[] hostContains; + private String[] hostStartsWith; + private String[] hostEndswith; + + + private Map queryEquals; + private String[] queryContains; + +// 暂时不支持 cookie +// private Map cookieEquals; +// private String[] cookieContains; + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public boolean isEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + public String[] getPathEquals() { + return pathEquals; + } + + public void setPathEquals(String[] pathEquals) { + this.pathEquals = pathEquals; + } + + public String[] getPathContains() { + return pathContains; + } + + public void setPathContains(String[] pathContains) { + this.pathContains = pathContains; + } + + public String[] getPathStartsWith() { + return pathStartsWith; + } + + public void setPathStartsWith(String[] pathStartsWith) { + this.pathStartsWith = pathStartsWith; + } + + public String[] getPathEndswith() { + return pathEndswith; + } + + public void setPathEndswith(String[] pathEndswith) { + this.pathEndswith = pathEndswith; + } + + public String[] getHostEquals() { + return hostEquals; + } + + public void setHostEquals(String[] hostEquals) { + this.hostEquals = hostEquals; + } + + public String[] getHostContains() { + return hostContains; + } + + public void setHostContains(String[] hostContains) { + this.hostContains = hostContains; + } + + public String[] getHostStartsWith() { + return hostStartsWith; + } + + public void setHostStartsWith(String[] hostStartsWith) { + this.hostStartsWith = hostStartsWith; + } + + public String[] getHostEndswith() { + return hostEndswith; + } + + public void setHostEndswith(String[] hostEndswith) { + this.hostEndswith = hostEndswith; + } + + public Map getQueryEquals() { + return queryEquals; + } + + public void setQueryEquals(Map queryEquals) { + this.queryEquals = queryEquals; + } + + public String[] getQueryContains() { + return queryContains; + } + + public void setQueryContains(String[] queryContains) { + this.queryContains = queryContains; + } + + public boolean isConfigOk(){ + return StrUtil.isNotBlank(uri); + } + + + public boolean matches(HttpServletRequest request){ + if (request == null){ + return false; + } + + String path = request.getServletPath(); + if (pathEquals != null){ + for (String p : pathEquals){ + if (path.equals(p)){ + return true; + } + } + } + + if (pathContains != null){ + for (String p : pathContains){ + if (path.contains(p)){ + return true; + } + } + } + + if (pathStartsWith != null){ + for (String p : pathStartsWith){ + if (path.startsWith(p)){ + return true; + } + } + } + + if (pathEndswith != null){ + for (String p : pathEndswith){ + if (path.endsWith(p)){ + return true; + } + } + } + + String host = request.getServerName(); + if (hostEquals != null){ + for (String h : hostEquals){ + if (host.equals(h)){ + return true; + } + } + } + + if (hostContains != null){ + for (String h : hostContains){ + if (host.contains(h)){ + return true; + } + } + } + + if (hostStartsWith != null){ + for (String h : hostStartsWith){ + if (host.startsWith(h)){ + return true; + } + } + } + + if (hostEndswith != null){ + for (String h : hostStartsWith){ + if (host.endsWith(h)){ + return true; + } + } + } + + if (queryContains != null || queryEquals != null){ + Map queryMap = queryStringToMap(request.getQueryString()); + if (queryMap != null && !queryMap.isEmpty()){ + + if (queryContains != null) { + for (String q : queryContains) { + if (queryMap.containsKey(q)) { + return true; + } + } + } + + if (queryEquals != null){ + for (Map.Entry e : queryEquals.entrySet()){ + String queryValue = queryMap.get(e.getKey()); + if (Objects.equals(queryValue,e.getValue())){ + return true; + } + } + } + } + } + + + + return false; + } + + + + private static Map queryStringToMap(String queryString) { + if (StrUtil.isBlank(queryString)){ + return null; + } + String[] params = queryString.split("&"); + Map resMap = new HashMap<>(); + for (int i = 0; i < params.length; i++) { + String[] param = params[i].split("="); + if (param.length >= 2) { + String key = param[0]; + String value = param[1]; + for (int j = 2; j < param.length; j++) { + value += "=" + param[j]; + } + resMap.put(key, value); + } + } + return resMap; + } +} diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java index 7c3efadc..de7e8230 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -15,6 +15,12 @@ */ package io.jboot.components.gateway; +import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.StrUtil; + +import javax.servlet.http.HttpServletRequest; +import java.util.*; + /** * @author michael yang (fuhai999@gmail.com) * @Date: 2020/3/21 @@ -26,4 +32,36 @@ public class JbootGatewayManager { public static JbootGatewayManager me(){ return me; } + + private Set configs; + + + public void init(){ + Map configMap = JbootConfigUtil.getConfigModels(JbootGatewayConfig.class,"jboot.app.gateway"); + if (configMap != null && !configMap.isEmpty()){ + configs = new HashSet<>(); + for (Map.Entry e:configMap.entrySet()){ + JbootGatewayConfig config = e.getValue(); + if(config.isConfigOk() && config.isEnable()){ + if (StrUtil.isNotBlank(config.getName())){ + config.setName(e.getKey()); + } + configs.add(config); + } + } + } + } + + public String matchingURI(HttpServletRequest req){ + if (configs != null && !configs.isEmpty()){ + Iterator iterator = configs.iterator(); + while (iterator.hasNext()){ + JbootGatewayConfig config = iterator.next(); + if (config.matches(req)){ + return config.getUri(); + } + } + } + return null; + } } -- Gitee From fc540c506a4fe814e157fa84b732168686260f9d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 12:28:37 +0800 Subject: [PATCH 0107/1965] v3.1.0 --- .../components/http/jboot/JbootHttpImpl.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index 6b432d7a..0dd19b12 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -32,6 +32,7 @@ import java.security.KeyStore; import java.security.SecureRandom; import java.security.cert.X509Certificate; import java.util.Map; +import java.util.zip.GZIPInputStream; public class JbootHttpImpl implements JbootHttp { @@ -65,13 +66,13 @@ public class JbootHttpImpl implements JbootHttp { connection.setDoOutput(true); //处理文件上传的post提交 - if (request.isMultipartFormData()){ + if (request.isMultipartFormData()) { if (ArrayUtil.isNotEmpty(request.getParams())) { uploadData(request, connection); } } //处理正常的post提交 - else { + else { String postContent = request.getPostContent(); if (StrUtil.isNotEmpty(postContent)) { DataOutputStream dos = new DataOutputStream(connection.getOutputStream()); @@ -81,7 +82,7 @@ public class JbootHttpImpl implements JbootHttp { } } - }else { + } else { connection.setInstanceFollowRedirects(true); connection.connect(); } @@ -114,7 +115,17 @@ public class JbootHttpImpl implements JbootHttp { } private InputStream getInutStream(HttpURLConnection connection) throws IOException { - return connection.getResponseCode() >= 400 ? connection.getErrorStream() : connection.getInputStream(); + + InputStream stream = connection.getResponseCode() >= 400 + ? connection.getErrorStream() + : connection.getInputStream(); + + if ("gzip".equalsIgnoreCase(connection.getContentEncoding())) { + return new GZIPInputStream(stream); + } else { + return stream; + } + } private void uploadData(JbootHttpRequest request, HttpURLConnection connection) throws IOException { -- Gitee From c6f6610259e4c3b0d2ee855b9aa6b311c615566c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 12:48:55 +0800 Subject: [PATCH 0108/1965] v3.1.0 --- .../jboot/components/gateway/GatewayUtil.java | 112 ++++++++++++++---- ...yHandler.java => JbootGatewayHandler.java} | 2 +- .../gateway/JbootGatewayManager.java | 2 +- .../java/io/jboot/core/JbootCoreConfig.java | 4 + src/test/resources/jboot.properties | 8 +- 5 files changed, 103 insertions(+), 25 deletions(-) rename src/main/java/io/jboot/components/gateway/{JbootGatawayHandler.java => JbootGatewayHandler.java} (97%) diff --git a/src/main/java/io/jboot/components/gateway/GatewayUtil.java b/src/main/java/io/jboot/components/gateway/GatewayUtil.java index 43b6974c..14dfbf29 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayUtil.java +++ b/src/main/java/io/jboot/components/gateway/GatewayUtil.java @@ -17,15 +17,12 @@ package io.jboot.components.gateway; import com.jfinal.log.Log; import io.jboot.exception.JbootException; -import org.apache.commons.io.IOUtils; +import io.jboot.utils.StrUtil; import javax.net.ssl.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import java.net.HttpURLConnection; import java.net.ProtocolException; import java.net.URL; @@ -34,6 +31,7 @@ import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.zip.GZIPInputStream; public class GatewayUtil { @@ -45,36 +43,93 @@ public class GatewayUtil { public static void sendRequest(String url, HttpServletRequest request, HttpServletResponse response) { + HttpURLConnection connection = null; - InputStream inputStream = null; - OutputStream outputStream = null; + try { connection = getConnection(url); + /** + * 设置 http 请求头 + */ configConnection(connection, request); connection.connect(); - inputStream = getInutStream(connection); - outputStream = connection.getOutputStream(); + /** + * 复制 post 请求内容到目标服务器 + */ + copyRequestStreamToConnection(request, connection); + + + /** + * 配置响应的 HTTP 头 + */ + configResponse(response, connection); + + /** + * 复制目标流到 Response + */ + copyStreamToResponse(connection, response); - IOUtils.copy(inputStream, outputStream); - configResponse(response,connection); } catch (Exception ex) { LOG.warn(ex.toString(), ex); } finally { if (connection != null) { connection.disconnect(); } - quetlyClose(inputStream,outputStream); } } + private static void copyRequestStreamToConnection(HttpServletRequest request, HttpURLConnection connection) throws IOException { + OutputStream outStream = null; + InputStream inStream = null; + try { + + // 如果不是 post 请求,不需要复制 + if ("get".equalsIgnoreCase(request.getMethod())) { + return; + } + + connection.setDoOutput(true); + outStream = connection.getOutputStream(); + inStream = request.getInputStream(); + int n; + byte[] buffer = new byte[1024]; + while (-1 != (n = inStream.read(buffer))) { + outStream.write(buffer, 0, n); + } + + } finally { + quetlyClose(outStream, inStream); + } + } + + + private static void copyStreamToResponse(HttpURLConnection connection, HttpServletResponse response) throws IOException { + InputStream inStream = null; + try { + if (!response.isCommitted()) { + PrintWriter writer = response.getWriter(); + inStream = getInutStream(connection); + int len; + char[] buffer = new char[1024]; + InputStreamReader r = new InputStreamReader(inStream); + while ((len = r.read(buffer)) != -1) { + writer.write(buffer, 0, len); + } + } + } finally { + quetlyClose(inStream); + } + + } + - private static void quetlyClose(Closeable ... closeables){ - for (Closeable closeable : closeables){ - if (closeable != null){ + private static void quetlyClose(Closeable... closeables) { + for (Closeable closeable : closeables) { + if (closeable != null) { try { closeable.close(); } catch (IOException e) { @@ -83,25 +138,36 @@ public class GatewayUtil { } } + private static void configResponse(HttpServletResponse response, HttpURLConnection connection) throws IOException { response.setContentType(connection.getContentType()); response.setStatus(connection.getResponseCode()); Map> headerFields = connection.getHeaderFields(); - if (headerFields != null && !headerFields.isEmpty()){ + if (headerFields != null && !headerFields.isEmpty()) { Set headerNames = headerFields.keySet(); - for (String headerName : headerNames){ - response.setHeader(headerName,connection.getHeaderField(headerName)); + for (String headerName : headerNames) { + //需要排除 Content-Encoding,因为 Server 可能已经使用 gzip 压缩,但是此代理已经对 gzip 内容进行解压了 + if (StrUtil.isNotBlank(headerName) && !"Content-Encoding".equalsIgnoreCase(headerName)) { + response.setHeader(headerName, connection.getHeaderField(headerName)); + } } } } private static InputStream getInutStream(HttpURLConnection connection) throws IOException { - return connection.getResponseCode() >= 400 + + InputStream stream = connection.getResponseCode() >= 400 ? connection.getErrorStream() : connection.getInputStream(); - } + if ("gzip".equalsIgnoreCase(connection.getContentEncoding())) { + return new GZIPInputStream(stream); + } else { + return stream; + } + + } private static void configConnection(HttpURLConnection connection, HttpServletRequest request) throws ProtocolException { @@ -112,9 +178,11 @@ public class GatewayUtil { connection.setRequestMethod(request.getMethod()); Enumeration headerNames = request.getHeaderNames(); - while (headerNames.hasMoreElements()){ + while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); - connection.setRequestProperty(headerName,request.getHeader(headerName)); + if (StrUtil.isNotBlank(headerName)) { + connection.setRequestProperty(headerName, request.getHeader(headerName)); + } } } diff --git a/src/main/java/io/jboot/components/gateway/JbootGatawayHandler.java b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java similarity index 97% rename from src/main/java/io/jboot/components/gateway/JbootGatawayHandler.java rename to src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java index fd5c381c..e6204f6d 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatawayHandler.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java @@ -25,7 +25,7 @@ import javax.servlet.http.HttpServletResponse; * @author michael yang (fuhai999@gmail.com) * @Date: 2020/3/22 */ -public class JbootGatawayHandler extends Handler { +public class JbootGatewayHandler extends Handler { @Override diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java index de7e8230..38238cbf 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -37,7 +37,7 @@ public class JbootGatewayManager { public void init(){ - Map configMap = JbootConfigUtil.getConfigModels(JbootGatewayConfig.class,"jboot.app.gateway"); + Map configMap = JbootConfigUtil.getConfigModels(JbootGatewayConfig.class,"jboot.gateway"); if (configMap != null && !configMap.isEmpty()){ configs = new HashSet<>(); for (Map.Entry e:configMap.entrySet()){ diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index b9890c64..d8770d09 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -35,6 +35,8 @@ import io.jboot.aop.jfinal.JfinalPlugins; import io.jboot.app.config.support.apollo.ApolloConfigManager; import io.jboot.app.config.support.apollo.ApolloServerConfig; import io.jboot.app.config.support.nacos.NacosConfigManager; +import io.jboot.components.gateway.JbootGatewayHandler; +import io.jboot.components.gateway.JbootGatewayManager; import io.jboot.components.limiter.LimiterManager; import io.jboot.components.rpc.JbootrpcManager; import io.jboot.components.schedule.JbootScheduleManager; @@ -255,6 +257,7 @@ public class JbootCoreConfig extends JFinalConfig { //用户的handler优先于jboot的handler执行 JbootAppListenerManager.me().onHandlerConfig(new JfinalHandlers(handlers)); + handlers.add(new JbootGatewayHandler()); handlers.add(new JbootFilterHandler()); handlers.add(new JbootHandler()); @@ -286,6 +289,7 @@ public class JbootCoreConfig extends JFinalConfig { LimiterManager.me().init(); JbootSeataManager.me().init(); SentinelManager.me().init(); + JbootGatewayManager.me().init(); JbootAppListenerManager.me().onStart(); } diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index 9f6744a3..96d6f8e1 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -33,4 +33,10 @@ config.test.test.bbb.name = name2 config.test.test.bbb.type = type2 config.test.test.ccc.name = name3 -config.test.test.ccc.type = type3 \ No newline at end of file +config.test.test.ccc.type = type3 + + +# 配置 gateway ,访问首页的时候目标地址设置为 baidu 的地址 +jboot.gateway.enable = true +jboot.gateway.uri = https://www.baidu.com +jboot.gateway.pathEquals = / \ No newline at end of file -- Gitee From 4df79900cec8988c4767b331a2fdefabcbe4590e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 13:16:13 +0800 Subject: [PATCH 0109/1965] v3.1.0 --- src/main/java/io/jboot/components/gateway/GatewayUtil.java | 4 ++-- .../java/io/jboot/components/http/jboot/JbootHttpImpl.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayUtil.java b/src/main/java/io/jboot/components/gateway/GatewayUtil.java index 14dfbf29..86b60d8b 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayUtil.java +++ b/src/main/java/io/jboot/components/gateway/GatewayUtil.java @@ -112,7 +112,7 @@ public class GatewayUtil { try { if (!response.isCommitted()) { PrintWriter writer = response.getWriter(); - inStream = getInutStream(connection); + inStream = getInputStream(connection); int len; char[] buffer = new char[1024]; InputStreamReader r = new InputStreamReader(inStream); @@ -155,7 +155,7 @@ public class GatewayUtil { } } - private static InputStream getInutStream(HttpURLConnection connection) throws IOException { + private static InputStream getInputStream(HttpURLConnection connection) throws IOException { InputStream stream = connection.getResponseCode() >= 400 ? connection.getErrorStream() diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index 0dd19b12..e967874c 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -88,7 +88,7 @@ public class JbootHttpImpl implements JbootHttp { } - stream = getInutStream(connection); + stream = getInputStream(connection); response.setContentType(connection.getContentType()); response.setResponseCode(connection.getResponseCode()); @@ -114,7 +114,7 @@ public class JbootHttpImpl implements JbootHttp { } } - private InputStream getInutStream(HttpURLConnection connection) throws IOException { + private InputStream getInputStream(HttpURLConnection connection) throws IOException { InputStream stream = connection.getResponseCode() >= 400 ? connection.getErrorStream() -- Gitee From 83a384d4d242fccddc6811c7a9e635a173577d12 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 14:22:27 +0800 Subject: [PATCH 0110/1965] v3.1.0 --- README.md | 1 + doc/docs/gateway.md | 186 ++++++++++++++++++++++++++++++++++++++++++++ doc/readme.md | 1 + 3 files changed, 188 insertions(+) create mode 100644 doc/docs/gateway.md diff --git a/README.md b/README.md index 6580fb26..85da0d73 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ public class HelloworldController extends JbootController { - [缓存](./doc/docs/cache.md) - [RPC远程调用](./doc/docs/rpc.md) - [MQ消息队列](./doc/docs/mq.md) +- [Gateway 网关](./doc/docs/gateway.md) - [任务调度](./doc/docs/schedule.md) - [限流](./doc/docs/limit.md) - [监控](./doc/docs/metrics.md) diff --git a/doc/docs/gateway.md b/doc/docs/gateway.md new file mode 100644 index 00000000..128a3883 --- /dev/null +++ b/doc/docs/gateway.md @@ -0,0 +1,186 @@ +# 网关 + +## 目录 + +- 概述 +- path路由 +- host路由 +- query路由 +- 其他 + + +## 概述 + +Jboot 已经内置基础了网关,网关功能目前暂时只能通过在 jboot.properties 文件进行配置。 + +如下是一个正常的 gateway 配置。 + +``` +jboot.gateway.name = name +jboot.gateway.uri = http://youdomain:8080 +jboot.gateway.enable = true + +jboot.gateway.pathEquals = /path +jboot.gateway.pathContains = /path +jboot.gateway.pathStartsWith = /path +jboot.gateway.pathEndswith = /path + +jboot.gateway.hostEquals = xxx.com +jboot.gateway.hostContains = xxx.com +jboot.gateway.hostStartsWith = xxx.com +jboot.gateway.hostEndswith = xxx.com + +jboot.gateway.queryEquals = aa:bb,cc:dd +jboot.gateway.queryContains = aa,bb +``` + +## Path 路由 + +Path 路由一般是最常用的路由之一,是根据域名之后的路径进行路由的,Jboot 对 Path 路由提供了 4 中方式: + +**1、pathEquals** + +``` +jboot.gateway.name = name +jboot.gateway.uri = http://youdomain:8080 +jboot.gateway.enable = true + +jboot.gateway.pathEquals = /user +``` + +当用户访问 www.xxx.com/user 的时候,自动路由到 `http://youdomain:8080/user`,但是当用户请求 `www.xxx.com/user/other` 的时候不会进行路由。 + +**2、pathContains** + +``` +jboot.gateway.name = name +jboot.gateway.uri = http://youdomain:8080 +jboot.gateway.enable = true + +jboot.gateway.pathContains = /user +``` + +当 path 中,只要存在 `/user` 就会匹配到该路由,比如 `www.xxx.com/user/other` 或者 `www.xxx.com/other/user/xxx` 都会匹配到。 + + +**3、pathStartsWith** + +``` +jboot.gateway.name = name +jboot.gateway.uri = http://youdomain:8080 +jboot.gateway.enable = true + +jboot.gateway.pathStartsWith = /user +``` + +当 path 中,只要以 `/user` 开头就会匹配到该路由,比如 `www.xxx.com/user/other` ,但是 `www.xxx.com/other/user/xxx` 不会匹配到,因为它是以 `/other` 开头的。 + +**4、pathEndswith** + +``` +jboot.gateway.name = name +jboot.gateway.uri = http://youdomain:8080 +jboot.gateway.enable = true + +jboot.gateway.pathStartsWith = /user +``` + +当 path 中,只要以 `/user` 结束就会匹配到该路由,比如 `www.xxx.com/other/user` ,但是 `www.xxx.com/user/other` 不会匹配到,因为它是以 `/other` 结束的。 + + +## Host 路由 + +Host 路由是根据域名进行路由的,Jboot 对 Host 路由提供了 4 中方式: + +**1、hostEquals** + +``` +jboot.gateway.name = name +jboot.gateway.uri = http://youdomain:8080 +jboot.gateway.enable = true + +jboot.gateway.hostEquals = xxx.xxx.com +``` + +当用户访问 xxx.xxx.com/user/xx 的时候,自动路由到 `http://youdomain:8080/user/xx`,但是当用户请求 `www.xxx.com/user/xxx` 的时候不会进行路由。 + +**2、hostContains** + +``` +jboot.gateway.name = name +jboot.gateway.uri = http://youdomain:8080 +jboot.gateway.enable = true + +jboot.gateway.hostContains = xxx.xxx.com +``` + +在 Host 中,只要存在 `xxx.xxx.com` 就会匹配到该路由,比如 `aaa.bbb.xxx.xxx.com/user/other` 会自动路由到 `http://youdomain:8080/user/other`。 + + +**3、hostStartsWith** + +``` +jboot.gateway.name = name +jboot.gateway.uri = http://youdomain:8080 +jboot.gateway.enable = true + +jboot.gateway.hostStartsWith = xxx +``` + +在 Host 中,只要以 `xxx` 开头就会匹配到该路由,比如 `xxx.xxx.com/user/other` ,但是 `www.xxx.com/other/user/xxx` 不会匹配到,因为它的域名(host)是以 `xxx` 开头的。 + +**4、hostEndswith** + +``` +jboot.gateway.name = name +jboot.gateway.uri = http://youdomain:8080 +jboot.gateway.enable = true + +jboot.gateway.hostEndswith = com +``` + +在 Host 中,只要以 `com` 结束就会匹配到该路由,比如 `www.xxx.com/other/user` ,但是 `www.xxx.org/user/other` 不会匹配到,因为它的域名是以 `org` 结束的。 + +## Query 路由 + +根据 get 请求的参数进行路由,注意 post 请求参数不会路由。 + + +**1、queryEquals** +``` +jboot.gateway.name = name +jboot.gateway.uri = http://youdomain:8080 +jboot.gateway.enable = true + +jboot.gateway.queryEquals = aaa:bbb +``` + +以上配置中,如果用户访问 `www.xxx.com/controller?aaa=bbb` 会自动路由到 `http://youdomain:8080/controller?aaa=bbb` ,但是如果用户访问 `www.xxx.com/controller?aaa=ccc`不会路由。 + +**2、queryContains** +``` +jboot.gateway.name = name +jboot.gateway.uri = http://youdomain:8080 +jboot.gateway.enable = true + +jboot.gateway.queryContains = aaa +``` + +以上配置中,如果用户访问 `www.xxx.com/controller?aaa=bbb` 会自动路由到 `http://youdomain:8080/controller?aaa=bbb` ,或者用户访问 `www.xxx.com/controller?aaa=ccc` 也会路由到 `http://youdomain:8080/controller?aaa=ccc`,因为 query 都包含了 `aaa=**` 的请求,但是如果用户访问 `www.xxx.com/controller?other=aaa`不会路由。 + +## 其他 +当配置中,如果一个内容存在多个值的时候,需要用英文逗号(,)隔开。 + +比如: + +``` +jboot.gateway.name = name +jboot.gateway.uri = http://youdomain:8080 +jboot.gateway.enable = true + +jboot.gateway.pathContains = /user,/article +``` + +当 path 中,只要存在 `/user` 或者 存在 `/article` 都会匹配到该路由,比如 `www.xxx.com/user/xxx` 或者 `www.xxx.com/article/xxx` 都会匹配到。 + +其他同理。 diff --git a/doc/readme.md b/doc/readme.md index 4ea4ca3e..2afbe729 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -14,6 +14,7 @@ - [缓存](./docs/cache.md) - [RPC远程调用](./docs/rpc.md) - [MQ消息队列](./docs/mq.md) +- [Gateway 网关](./docs/gateway.md) - [任务调度](./docs/schedule.md) - [限流](./docs/limit.md) - [监控](./docs/metrics.md) -- Gitee From e9fb7350aca5c11bff41d6ee05511f9457864ace Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 14:34:25 +0800 Subject: [PATCH 0111/1965] v3.1.0 --- src/main/java/io/jboot/components/gateway/GatewayUtil.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayUtil.java b/src/main/java/io/jboot/components/gateway/GatewayUtil.java index 86b60d8b..1c6e7c50 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayUtil.java +++ b/src/main/java/io/jboot/components/gateway/GatewayUtil.java @@ -109,19 +109,20 @@ public class GatewayUtil { private static void copyStreamToResponse(HttpURLConnection connection, HttpServletResponse response) throws IOException { InputStream inStream = null; + InputStreamReader reader = null; try { if (!response.isCommitted()) { PrintWriter writer = response.getWriter(); inStream = getInputStream(connection); + reader = new InputStreamReader(inStream); int len; char[] buffer = new char[1024]; - InputStreamReader r = new InputStreamReader(inStream); - while ((len = r.read(buffer)) != -1) { + while ((len = reader.read(buffer)) != -1) { writer.write(buffer, 0, len); } } } finally { - quetlyClose(inStream); + quetlyClose(inStream, reader); } } -- Gitee From 20aeb20e64a42e96c11af1fedd62797a3f369f5d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 15:21:48 +0800 Subject: [PATCH 0112/1965] v3.1.0 --- changes.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/changes.txt b/changes.txt index a270482d..385e5b7c 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,22 @@ +jboot v3.1.0: +新增:新增网关的支持 +新增:新增对 Dubbo 多协议、多注册中心等的支持 +新增:新增对注解 @Configuration 的支持,可以通过其构建 name 实例 +新增:JbootAopInterceptor 可以动态的添加或者移除拦截器 +新增:配置文件可以配置 Map、Set、List 和 数组的支持 +新增:JbootCache 新增可以获取素有 Names 的功能,方便对缓存进行运维 +新增:JbootCache 新增可以对分布式缓存进行刷新,方便对缓存进行运维 +新增:jboot-system.properties 的支持,用于可以在里面对 System.properties 进行配置,很多第三方组件依赖于 System.porperties 的配置 +修复:修复 JbootHttpImpl 无法上传文件的bug +修复:修复 JbootHttpImpl 无法正确获取 gzip 压缩内容的问题 +优化:重构 RPC 的模块,使之更加简单清晰 +文档:完善 AOP 的相关文档 +文档:完善 Sentinel 限流的文档 +文档:完善 RPC 的相关文档 +文档:新增 网关配置使用的相关文档 + + + jboot v3.0.5: 修复:SqlBuilder 在某些极端情况下生成 SQL 的一些问题 优化:移除 Jboot 的一些过时的方法 -- Gitee From c4af8229772ce9d199dda3139cd85e8d22dd5244 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 15:47:45 +0800 Subject: [PATCH 0113/1965] v3.1.0 --- src/test/resources/jboot.properties | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index 96d6f8e1..ec69754b 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -39,4 +39,8 @@ config.test.test.ccc.type = type3 # 配置 gateway ,访问首页的时候目标地址设置为 baidu 的地址 jboot.gateway.enable = true jboot.gateway.uri = https://www.baidu.com -jboot.gateway.pathEquals = / \ No newline at end of file +jboot.gateway.pathEquals = / + +jboot.gateway.gitee.enable = true +jboot.gateway.gitee.uri= https://gitee.com +jboot.gateway.gitee.pathStartsWith = /fuhai \ No newline at end of file -- Gitee From dd812ffa6da61eab84edb537b4da8cffc2730bde Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 15:54:43 +0800 Subject: [PATCH 0114/1965] v3.1.0 --- src/main/java/io/jboot/components/rpc/JbootrpcManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java index 5fe3508c..e4dd95f4 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java @@ -79,7 +79,9 @@ public class JbootrpcManager { } public void stop(){ - getJbootrpc().onStop(); + if (defaultConfig.isConfigOk()) { + getJbootrpc().onStop(); + } } -- Gitee From 01fa6574737ed7d28f990ad49a37d98239ace6e4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 16:06:20 +0800 Subject: [PATCH 0115/1965] v3.1.0 --- .../gateway/JbootGatewayManager.java | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java index 38238cbf..8fd65a7b 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -19,7 +19,10 @@ import io.jboot.app.config.JbootConfigUtil; import io.jboot.utils.StrUtil; import javax.servlet.http.HttpServletRequest; -import java.util.*; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; /** * @author michael yang (fuhai999@gmail.com) @@ -29,39 +32,41 @@ public class JbootGatewayManager { private static JbootGatewayManager me = new JbootGatewayManager(); - public static JbootGatewayManager me(){ + public static JbootGatewayManager me() { return me; } private Set configs; - - public void init(){ - Map configMap = JbootConfigUtil.getConfigModels(JbootGatewayConfig.class,"jboot.gateway"); - if (configMap != null && !configMap.isEmpty()){ - configs = new HashSet<>(); - for (Map.Entry e:configMap.entrySet()){ - JbootGatewayConfig config = e.getValue(); - if(config.isConfigOk() && config.isEnable()){ - if (StrUtil.isNotBlank(config.getName())){ - config.setName(e.getKey()); - } - configs.add(config); - } - } - } + public void init() { + Map configMap = JbootConfigUtil.getConfigModels(JbootGatewayConfig.class, "jboot.gateway"); + if (configMap != null && !configMap.isEmpty()) { + configs = new HashSet<>(); + for (Map.Entry e : configMap.entrySet()) { + JbootGatewayConfig config = e.getValue(); + if (config.isConfigOk() && config.isEnable()) { + if (StrUtil.isNotBlank(config.getName())) { + config.setName(e.getKey()); + } + configs.add(config); + } + } + } } - public String matchingURI(HttpServletRequest req){ - if (configs != null && !configs.isEmpty()){ + + public String matchingURI(HttpServletRequest req) { + if (configs != null && !configs.isEmpty()) { Iterator iterator = configs.iterator(); - while (iterator.hasNext()){ + while (iterator.hasNext()) { JbootGatewayConfig config = iterator.next(); - if (config.matches(req)){ + if (config.matches(req)) { return config.getUri(); } } } return null; } + + } -- Gitee From cfaaca31f76aeb25c903427a328d6b2877b1bc43 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 16:23:05 +0800 Subject: [PATCH 0116/1965] v3.1.0 --- pom.xml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 243eef09..1a21c283 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ 2.0.29.Final 1.7.30 2.57 - 1.2.66 + 1.2.67 28.2-jre 2.2.5 2.10.2 @@ -64,9 +64,10 @@ 1.14 4.5.12 4.4.13 - 4.1.47.Final + 4.1.48.Final 1.7.1 2.7.5 + 3.11.0.Final 1.1.0 1.1.7 4.1.5 @@ -265,7 +266,7 @@ com.ctrip.framework.apollo apollo-client - 1.5.1 + 1.6.0 provided @@ -452,35 +453,35 @@ org.jboss.resteasy resteasy-jdk-http - 3.9.0.Final + ${resteasy.version} provided org.jboss.resteasy resteasy-jaxrs - 3.9.0.Final + ${resteasy.version} provided org.jboss.resteasy resteasy-client - 3.9.0.Final + ${resteasy.version} provided org.jboss.resteasy resteasy-netty4 - 3.9.0.Final + ${resteasy.version} provided org.jboss.resteasy resteasy-jackson-provider - 3.9.0.Final + ${resteasy.version} provided -- Gitee From adc9f641267feb07d7fd833c37cc2e047d6d55ca Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 18:53:53 +0800 Subject: [PATCH 0117/1965] v3.1.0 --- .../jboot/components/rpc/dubbo/DubboUtil.java | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index 70e80047..77dfcccc 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -45,12 +45,13 @@ class DubboUtil { public static void stopDubbo() { DubboBootstrap.getInstance().stop(); } + public static void initDubbo() { DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance(); //application 配置 ApplicationConfig applicationConfig = config(ApplicationConfig.class, "jboot.rpc.dubbo.application"); - if (StrUtil.isBlank(applicationConfig.getName())){ + if (StrUtil.isBlank(applicationConfig.getName())) { applicationConfig.setName("jboot"); } dubboBootstrap.application(applicationConfig); @@ -75,8 +76,9 @@ class DubboUtil { ModuleConfig moduleConfig = config(ModuleConfig.class, "jboot.rpc.dubbo.module"); dubboBootstrap.module(moduleConfig); + //元数据 配置 - Map metadataReportConfigs = configs(MetadataReportConfig.class, "jboot.rpc.dubbo.metadataReport"); + Map metadataReportConfigs = configs(MetadataReportConfig.class, "jboot.rpc.dubbo.metadata-report"); if (metadataReportConfigs != null && !metadataReportConfigs.isEmpty()) { if (metadataReportConfigs.size() == 1) { dubboBootstrap.metadataReport(getDefault(metadataReportConfigs)); @@ -85,6 +87,16 @@ class DubboUtil { } } + //配置中心配置 + Map configCenterConfigs = configs(ConfigCenterConfig.class, "jboot.rpc.dubbo.config-center"); + if (configCenterConfigs != null && !configCenterConfigs.isEmpty()) { + if (configCenterConfigs.size() == 1) { + dubboBootstrap.configCenter(getDefault(configCenterConfigs)); + } else { + dubboBootstrap.configCenters((List) toList(configCenterConfigs)); + } + } + //协议 配置 Map protocolConfigs = configs(ProtocolConfig.class, "jboot.rpc.dubbo.protocol"); @@ -121,14 +133,14 @@ class DubboUtil { //方法配置 配置 Map methodConfigs = configs(MethodConfig.class, "jboot.rpc.dubbo.method"); - Utils.setChildConfig(methodConfigs,argumentConfigs,"jboot.rpc.dubbo.method","argument"); + Utils.setChildConfig(methodConfigs, argumentConfigs, "jboot.rpc.dubbo.method", "argument"); //消费者 配置 Map consumerConfigs = configs(ConsumerConfig.class, "jboot.rpc.dubbo.consumer"); - Utils.setChildConfig(consumerConfigs,methodConfigs,"jboot.rpc.dubbo.consumer","method"); - Utils.setChildConfig(consumerConfigs,protocolConfigs,"jboot.rpc.dubbo.consumer","protocol"); - Utils.setChildConfig(consumerConfigs,registryConfigs,"jboot.rpc.dubbo.consumer","registry"); + Utils.setChildConfig(consumerConfigs, methodConfigs, "jboot.rpc.dubbo.consumer", "method"); + Utils.setChildConfig(consumerConfigs, protocolConfigs, "jboot.rpc.dubbo.consumer", "protocol"); + Utils.setChildConfig(consumerConfigs, registryConfigs, "jboot.rpc.dubbo.consumer", "registry"); if (consumerConfigs != null && !consumerConfigs.isEmpty()) { @@ -142,9 +154,9 @@ class DubboUtil { //服务提供者 配置 Map providerConfigs = configs(ProviderConfig.class, "jboot.rpc.dubbo.provider"); - Utils.setChildConfig(providerConfigs,methodConfigs,"jboot.rpc.dubbo.provider","method"); - Utils.setChildConfig(providerConfigs,protocolConfigs,"jboot.rpc.dubbo.provider","protocol"); - Utils.setChildConfig(providerConfigs,registryConfigs,"jboot.rpc.dubbo.provider","registry"); + Utils.setChildConfig(providerConfigs, methodConfigs, "jboot.rpc.dubbo.provider", "method"); + Utils.setChildConfig(providerConfigs, protocolConfigs, "jboot.rpc.dubbo.provider", "protocol"); + Utils.setChildConfig(providerConfigs, registryConfigs, "jboot.rpc.dubbo.provider", "registry"); if (providerConfigs != null && !providerConfigs.isEmpty()) { providerConfigMap.putAll(providerConfigs); @@ -198,12 +210,12 @@ class DubboUtil { return serviceConfig; } - public static ConsumerConfig getConsumer(String name){ + public static ConsumerConfig getConsumer(String name) { return consumerConfigMap.get(name); } - public static ProviderConfig getProvider(String name){ + public static ProviderConfig getProvider(String name) { return providerConfigMap.get(name); } @@ -221,16 +233,16 @@ class DubboUtil { return map.get("default"); } - private static List toList(Map map) { - List arrayList = new ArrayList<>(map.size()); + private static List toList(Map map) { + List list = new ArrayList<>(map.size()); for (Map.Entry entry : map.entrySet()) { AbstractConfig config = entry.getValue(); if (StrUtil.isBlank(config.getId())) { config.setId(entry.getKey()); } - arrayList.add(config); + list.add(config); } - return arrayList; + return list; } -- Gitee From c571ecb89f4417062fa9c5ac974f77aa129c1cd9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 20:07:12 +0800 Subject: [PATCH 0118/1965] v3.1.0 --- .../components/gateway/GatewayHttpProxy.java | 245 ++++++++++++++++ .../gateway/GatewaySentinelProcesser.java | 81 ++++++ .../jboot/components/gateway/GatewayUtil.java | 275 ++++++------------ .../gateway/JbootGatewayConfig.java | 24 +- .../gateway/JbootGatewayHandler.java | 32 +- .../gateway/JbootGatewayManager.java | 4 +- 6 files changed, 463 insertions(+), 198 deletions(-) create mode 100644 src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java create mode 100644 src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java new file mode 100644 index 00000000..e58684ca --- /dev/null +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -0,0 +1,245 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.gateway; + +import com.jfinal.log.Log; +import io.jboot.exception.JbootException; +import io.jboot.utils.StrUtil; + +import javax.net.ssl.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.ProtocolException; +import java.net.URL; +import java.security.cert.X509Certificate; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.GZIPInputStream; + + +public class GatewayHttpProxy { + + private static final Log LOG = Log.getLog(GatewayHttpProxy.class); + + private static int READ_TIMEOUT = 10000; + private static int CONNECT_TIMEOUT = 5000; + + + public static void sendRequest(String url, HttpServletRequest request, HttpServletResponse response) { + + HttpURLConnection connection = null; + + try { + connection = getConnection(url); + + /** + * 设置 http 请求头 + */ + configConnection(connection, request); + + connection.connect(); + + /** + * 复制 post 请求内容到目标服务器 + */ + copyRequestStreamToConnection(request, connection); + + + /** + * 配置响应的 HTTP 头 + */ + configResponse(response, connection); + + /** + * 复制目标流到 Response + */ + copyStreamToResponse(connection, response); + + + } catch (Exception ex) { + LOG.warn(ex.toString(), ex); + } finally { + if (connection != null) { + connection.disconnect(); + } + } + } + + private static void copyRequestStreamToConnection(HttpServletRequest request, HttpURLConnection connection) throws IOException { + OutputStream outStream = null; + InputStream inStream = null; + try { + + // 如果不是 post 请求,不需要复制 + if ("get".equalsIgnoreCase(request.getMethod())) { + return; + } + + connection.setDoOutput(true); + outStream = connection.getOutputStream(); + inStream = request.getInputStream(); + int n; + byte[] buffer = new byte[1024]; + while (-1 != (n = inStream.read(buffer))) { + outStream.write(buffer, 0, n); + } + + } finally { + quetlyClose(outStream, inStream); + } + } + + + private static void copyStreamToResponse(HttpURLConnection connection, HttpServletResponse response) throws IOException { + InputStream inStream = null; + InputStreamReader reader = null; + try { + if (!response.isCommitted()) { + PrintWriter writer = response.getWriter(); + inStream = getInputStream(connection); + reader = new InputStreamReader(inStream); + int len; + char[] buffer = new char[1024]; + while ((len = reader.read(buffer)) != -1) { + writer.write(buffer, 0, len); + } + } + } finally { + quetlyClose(inStream, reader); + } + + } + + + private static void quetlyClose(Closeable... closeables) { + for (Closeable closeable : closeables) { + if (closeable != null) { + try { + closeable.close(); + } catch (IOException e) { + } + } + } + } + + + private static void configResponse(HttpServletResponse response, HttpURLConnection connection) throws IOException { + response.setContentType(connection.getContentType()); + response.setStatus(connection.getResponseCode()); + + Map> headerFields = connection.getHeaderFields(); + if (headerFields != null && !headerFields.isEmpty()) { + Set headerNames = headerFields.keySet(); + for (String headerName : headerNames) { + //需要排除 Content-Encoding,因为 Server 可能已经使用 gzip 压缩,但是此代理已经对 gzip 内容进行解压了 + if (StrUtil.isNotBlank(headerName) && !"Content-Encoding".equalsIgnoreCase(headerName)) { + response.setHeader(headerName, connection.getHeaderField(headerName)); + } + } + } + } + + private static InputStream getInputStream(HttpURLConnection connection) throws IOException { + + InputStream stream = connection.getResponseCode() >= 400 + ? connection.getErrorStream() + : connection.getInputStream(); + + if ("gzip".equalsIgnoreCase(connection.getContentEncoding())) { + return new GZIPInputStream(stream); + } else { + return stream; + } + + } + + + private static void configConnection(HttpURLConnection connection, HttpServletRequest request) throws ProtocolException { + + connection.setReadTimeout(READ_TIMEOUT); + connection.setConnectTimeout(CONNECT_TIMEOUT); + connection.setInstanceFollowRedirects(true); + + connection.setRequestMethod(request.getMethod()); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + if (StrUtil.isNotBlank(headerName)) { + connection.setRequestProperty(headerName, request.getHeader(headerName)); + } + } + } + + private static HttpURLConnection getConnection(String urlString) { + try { + if (urlString.toLowerCase().startsWith("https")) { + return getHttpsConnection(urlString); + } else { + return getHttpConnection(urlString); + } + } catch (Throwable ex) { + throw new JbootException(ex); + } + } + + private static HttpURLConnection getHttpConnection(String urlStr) throws Exception { + URL url = new URL(urlStr); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + return conn; + } + + private static HttpsURLConnection getHttpsConnection(String urlString) throws Exception { + URL url = new URL(urlString); + HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); + conn.setHostnameVerifier(hnv); + SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); + if (sslContext != null) { + TrustManager[] tm = {trustAnyTrustManager}; + sslContext.init(null, tm, null); + SSLSocketFactory ssf = sslContext.getSocketFactory(); + conn.setSSLSocketFactory(ssf); + } + return conn; + } + + private static X509TrustManager trustAnyTrustManager = new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + }; + + private static HostnameVerifier hnv = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + + +} diff --git a/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java b/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java new file mode 100644 index 00000000..69db3fc1 --- /dev/null +++ b/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.gateway; + +import com.alibaba.csp.sentinel.*; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.util.StringUtil; +import io.jboot.utils.StrUtil; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/1/7 + */ +public class GatewaySentinelProcesser { + + + public void process(Runnable runnable, JbootGatewayConfig config, HttpServletRequest req, HttpServletResponse resp) { + Entry entry = null; + String resourceName = GatewayUtil.buildResource(req); + try { + entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN); + runnable.run(); + } catch (BlockException ex) { + processBlocked(config, req, resp, entry); + } catch (Exception ex) { + Tracer.traceEntry(ex, entry); + throw ex; + } finally { + if (entry != null) { + entry.exit(); + } + } + } + + private static void processBlocked(JbootGatewayConfig config, HttpServletRequest req, HttpServletResponse resp, Entry entry) { + StringBuffer url = req.getRequestURL(); + + if ("GET".equals(req.getMethod()) && StrUtil.isNotBlank(req.getQueryString())) { + url.append("?").append(req.getQueryString()); + } + + try { + if (StringUtil.isBlank(config.getSentinelBlockPage())) { + writeDefaultBlockedPage(resp); + } else { + String redirectUrl = config.getSentinelBlockPage() + "?http_referer=" + url.toString(); + resp.sendRedirect(redirectUrl); + } + } catch (IOException ex) { + Tracer.traceEntry(ex, entry); + } + + } + + + private static void writeDefaultBlockedPage(HttpServletResponse response) throws IOException { + response.setStatus(200); + PrintWriter out = response.getWriter(); + out.print("Blocked by Sentinel (flow limiting) in Jboot"); + } + + +} diff --git a/src/main/java/io/jboot/components/gateway/GatewayUtil.java b/src/main/java/io/jboot/components/gateway/GatewayUtil.java index 1c6e7c50..d78abdc4 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayUtil.java +++ b/src/main/java/io/jboot/components/gateway/GatewayUtil.java @@ -15,231 +15,144 @@ */ package io.jboot.components.gateway; -import com.jfinal.log.Log; -import io.jboot.exception.JbootException; -import io.jboot.utils.StrUtil; +import com.alibaba.csp.sentinel.util.StringUtil; -import javax.net.ssl.*; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.*; -import java.net.HttpURLConnection; -import java.net.ProtocolException; -import java.net.URL; -import java.security.cert.X509Certificate; -import java.util.Enumeration; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.zip.GZIPInputStream; - +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/22 + */ public class GatewayUtil { - private static final Log LOG = Log.getLog(GatewayUtil.class); - - private static int READ_TIMEOUT = 10000; - private static int CONNECT_TIMEOUT = 5000; + private static final String PATH_SPLIT = "/"; - public static void sendRequest(String url, HttpServletRequest request, HttpServletResponse response) { - - HttpURLConnection connection = null; + public static String buildResource(HttpServletRequest request) { + String pathInfo = getResourcePath(request); + if (!pathInfo.startsWith(PATH_SPLIT)) { + pathInfo = PATH_SPLIT + pathInfo; + } - try { - connection = getConnection(url); + if (PATH_SPLIT.equals(pathInfo)) { + return pathInfo; + } - /** - * 设置 http 请求头 - */ - configConnection(connection, request); + // Note: pathInfo should be converted to camelCase style. + int lastSlashIndex = pathInfo.lastIndexOf("/"); - connection.connect(); + if (lastSlashIndex >= 0) { + pathInfo = pathInfo.substring(0, lastSlashIndex) + "/" + + StringUtil.trim(pathInfo.substring(lastSlashIndex + 1)); + } else { + pathInfo = PATH_SPLIT + StringUtil.trim(pathInfo); + } - /** - * 复制 post 请求内容到目标服务器 - */ - copyRequestStreamToConnection(request, connection); + return pathInfo; + } - /** - * 配置响应的 HTTP 头 - */ - configResponse(response, connection); - /** - * 复制目标流到 Response - */ - copyStreamToResponse(connection, response); + private static String getResourcePath(HttpServletRequest request) { + String pathInfo = normalizeAbsolutePath(request.getPathInfo(), false); + String servletPath = normalizeAbsolutePath(request.getServletPath(), pathInfo.length() != 0); + return servletPath + pathInfo; + } - } catch (Exception ex) { - LOG.warn(ex.toString(), ex); - } finally { - if (connection != null) { - connection.disconnect(); - } - } + private static String normalizeAbsolutePath(String path, boolean removeTrailingSlash) throws IllegalStateException { + return normalizePath(path, true, false, removeTrailingSlash); } - private static void copyRequestStreamToConnection(HttpServletRequest request, HttpURLConnection connection) throws IOException { - OutputStream outStream = null; - InputStream inStream = null; - try { + private static String normalizePath(String path, boolean forceAbsolute, boolean forceRelative, + boolean removeTrailingSlash) throws IllegalStateException { + char[] pathChars = StringUtil.trimToEmpty(path).toCharArray(); + int length = pathChars.length; - // 如果不是 post 请求,不需要复制 - if ("get".equalsIgnoreCase(request.getMethod())) { - return; - } + // Check path and slash. + boolean startsWithSlash = false; + boolean endsWithSlash = false; - connection.setDoOutput(true); - outStream = connection.getOutputStream(); - inStream = request.getInputStream(); - int n; - byte[] buffer = new byte[1024]; - while (-1 != (n = inStream.read(buffer))) { - outStream.write(buffer, 0, n); - } + if (length > 0) { + char firstChar = pathChars[0]; + char lastChar = pathChars[length - 1]; - } finally { - quetlyClose(outStream, inStream); + startsWithSlash = firstChar == PATH_SPLIT.charAt(0) || firstChar == '\\'; + endsWithSlash = lastChar == PATH_SPLIT.charAt(0) || lastChar == '\\'; } - } + StringBuilder buf = new StringBuilder(length); + boolean isAbsolutePath = forceAbsolute || !forceRelative && startsWithSlash; + int index = startsWithSlash ? 0 : -1; + int level = 0; - private static void copyStreamToResponse(HttpURLConnection connection, HttpServletResponse response) throws IOException { - InputStream inStream = null; - InputStreamReader reader = null; - try { - if (!response.isCommitted()) { - PrintWriter writer = response.getWriter(); - inStream = getInputStream(connection); - reader = new InputStreamReader(inStream); - int len; - char[] buffer = new char[1024]; - while ((len = reader.read(buffer)) != -1) { - writer.write(buffer, 0, len); - } - } - } finally { - quetlyClose(inStream, reader); + if (isAbsolutePath) { + buf.append(PATH_SPLIT); } - } - + while (index < length) { + index = indexOfSlash(pathChars, index + 1, false); - private static void quetlyClose(Closeable... closeables) { - for (Closeable closeable : closeables) { - if (closeable != null) { - try { - closeable.close(); - } catch (IOException e) { - } + if (index == length) { + break; } - } - } + int nextSlashIndex = indexOfSlash(pathChars, index, true); - private static void configResponse(HttpServletResponse response, HttpURLConnection connection) throws IOException { - response.setContentType(connection.getContentType()); - response.setStatus(connection.getResponseCode()); + String element = new String(pathChars, index, nextSlashIndex - index); + index = nextSlashIndex; - Map> headerFields = connection.getHeaderFields(); - if (headerFields != null && !headerFields.isEmpty()) { - Set headerNames = headerFields.keySet(); - for (String headerName : headerNames) { - //需要排除 Content-Encoding,因为 Server 可能已经使用 gzip 压缩,但是此代理已经对 gzip 内容进行解压了 - if (StrUtil.isNotBlank(headerName) && !"Content-Encoding".equalsIgnoreCase(headerName)) { - response.setHeader(headerName, connection.getHeaderField(headerName)); - } + // Ignore "." + if (".".equals(element)) { + continue; } - } - } - - private static InputStream getInputStream(HttpURLConnection connection) throws IOException { - InputStream stream = connection.getResponseCode() >= 400 - ? connection.getErrorStream() - : connection.getInputStream(); - - if ("gzip".equalsIgnoreCase(connection.getContentEncoding())) { - return new GZIPInputStream(stream); - } else { - return stream; - } - - } - - - private static void configConnection(HttpURLConnection connection, HttpServletRequest request) throws ProtocolException { - - connection.setReadTimeout(READ_TIMEOUT); - connection.setConnectTimeout(CONNECT_TIMEOUT); - connection.setInstanceFollowRedirects(true); + // Backtrack ".." + if ("..".equals(element)) { + if (level == 0) { + if (isAbsolutePath) { + throw new IllegalStateException(path); + } else { + buf.append("..").append(PATH_SPLIT); + } + } else { + buf.setLength(pathChars[--level]); + } - connection.setRequestMethod(request.getMethod()); - Enumeration headerNames = request.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String headerName = headerNames.nextElement(); - if (StrUtil.isNotBlank(headerName)) { - connection.setRequestProperty(headerName, request.getHeader(headerName)); + continue; } + + pathChars[level++] = (char)buf.length(); + buf.append(element).append(PATH_SPLIT); } - } - private static HttpURLConnection getConnection(String urlString) { - try { - if (urlString.toLowerCase().startsWith("https")) { - return getHttpsConnection(urlString); - } else { - return getHttpConnection(urlString); + // remove the last "/" + if (buf.length() > 0) { + if (!endsWithSlash || removeTrailingSlash) { + buf.setLength(buf.length() - 1); } - } catch (Throwable ex) { - throw new JbootException(ex); } - } - private static HttpURLConnection getHttpConnection(String urlStr) throws Exception { - URL url = new URL(urlStr); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - return conn; + return buf.toString(); } - private static HttpsURLConnection getHttpsConnection(String urlString) throws Exception { - URL url = new URL(urlString); - HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); - conn.setHostnameVerifier(hnv); - SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); - if (sslContext != null) { - TrustManager[] tm = {trustAnyTrustManager}; - sslContext.init(null, tm, null); - SSLSocketFactory ssf = sslContext.getSocketFactory(); - conn.setSSLSocketFactory(ssf); - } - return conn; - } - - private static X509TrustManager trustAnyTrustManager = new X509TrustManager() { - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) { - } + private static int indexOfSlash(char[] chars, int beginIndex, boolean slash) { + int i = beginIndex; - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) { - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return null; - } - }; + for (; i < chars.length; i++) { + char ch = chars[i]; - private static HostnameVerifier hnv = new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; + if (slash) { + if (ch == PATH_SPLIT.charAt(0) || ch == '\\') { + break; // if a slash + } + } else { + if (ch != PATH_SPLIT.charAt(0) && ch != '\\') { + break; // if not a slash + } + } } - }; - + return i; + } } diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index 82cb577b..ade2f2d8 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, @@ -32,6 +32,8 @@ public class JbootGatewayConfig implements Serializable { private String name; private String uri; private boolean enable = false; + private boolean sentinelEnable = false; + private String sentinelBlockPage; private String[] pathEquals; private String[] pathContains; @@ -77,6 +79,22 @@ public class JbootGatewayConfig implements Serializable { this.enable = enable; } + public boolean isSentinelEnable() { + return sentinelEnable; + } + + public void setSentinelEnable(boolean sentinelEnable) { + this.sentinelEnable = sentinelEnable; + } + + public String getSentinelBlockPage() { + return sentinelBlockPage; + } + + public void setSentinelBlockPage(String sentinelBlockPage) { + this.sentinelBlockPage = sentinelBlockPage; + } + public String[] getPathEquals() { return pathEquals; } diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java index e6204f6d..571d213f 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java @@ -30,24 +30,32 @@ public class JbootGatewayHandler extends Handler { @Override public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { - String uri = JbootGatewayManager.me().matchingURI(request); - if (StrUtil.isNotBlank(uri)) { - doProcessGateway(uri, request, response); + JbootGatewayConfig config = JbootGatewayManager.me().matchingConfig(request); + if (config != null) { + doProcessGateway(config, request, response); isHandled[0] = true; } else { next.handle(target, request, response, isHandled); } } - private void doProcessGateway(String uri, HttpServletRequest request, HttpServletResponse response) { - StringBuilder url = new StringBuilder(uri); - if (StrUtil.isNotBlank(request.getRequestURI())){ - url.append(request.getRequestURI()); - } - if (StrUtil.isNotBlank(request.getQueryString())){ - url.append("?").append(request.getQueryString()); - } + private void doProcessGateway(JbootGatewayConfig config, HttpServletRequest request, HttpServletResponse response) { + Runnable runnable = () -> { + StringBuilder url = new StringBuilder(config.getUri()); + if (StrUtil.isNotBlank(request.getRequestURI())) { + url.append(request.getRequestURI()); + } + if (StrUtil.isNotBlank(request.getQueryString())) { + url.append("?").append(request.getQueryString()); + } + + GatewayHttpProxy.sendRequest(url.toString(), request, response); + }; - GatewayUtil.sendRequest(url.toString(),request,response); + if (config.isSentinelEnable()) { + new GatewaySentinelProcesser().process(runnable, config, request, response); + } else { + runnable.run(); + } } } diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java index 8fd65a7b..7a29bb4d 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -55,13 +55,13 @@ public class JbootGatewayManager { } - public String matchingURI(HttpServletRequest req) { + public JbootGatewayConfig matchingConfig(HttpServletRequest req) { if (configs != null && !configs.isEmpty()) { Iterator iterator = configs.iterator(); while (iterator.hasNext()) { JbootGatewayConfig config = iterator.next(); if (config.matches(req)) { - return config.getUri(); + return config; } } } -- Gitee From eaab948453008b8b44bdf1ad2d1f0aad96d6e4ba Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 20:13:17 +0800 Subject: [PATCH 0119/1965] v3.1.0 --- src/main/java/io/jboot/app/HttpContentTypes.java | 6 +++--- src/main/java/io/jboot/app/config/JbootConfigUtil.java | 6 +++--- .../app/config/support/apollo/ApolloConfigManager.java | 6 +++--- .../jboot/app/config/support/nacos/NacosConfigIniter.java | 6 +++--- .../jboot/app/config/support/nacos/NacosConfigManager.java | 6 +++--- src/main/java/io/jboot/components/cache/CacheTime.java | 6 +++--- .../io/jboot/components/gateway/JbootGatewayHandler.java | 6 +++--- .../io/jboot/components/gateway/JbootGatewayManager.java | 6 +++--- .../java/io/jboot/components/mq/local/JbootLocalmqImpl.java | 6 +++--- src/main/java/io/jboot/components/rpc/Utils.java | 6 +++--- src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java | 6 +++--- src/main/java/io/jboot/components/rpc/motan/MotanUtil.java | 6 +++--- src/main/java/io/jboot/core/log/Slf4jSimpleLogger.java | 6 +++--- src/main/java/io/jboot/db/SqlDebugger.java | 6 +++--- src/main/java/io/jboot/db/model/JbootModelFilter.java | 6 +++--- .../java/io/jboot/db/model/PrimarykeyValueGenerator.java | 6 +++--- src/main/java/io/jboot/objects/counter/JbootCounter.java | 6 +++--- .../java/io/jboot/objects/counter/JbootCounterConfig.java | 6 +++--- .../java/io/jboot/objects/counter/JbootCounterManager.java | 6 +++--- .../io/jboot/objects/counter/impl/JbootLocalCounter.java | 6 +++--- .../io/jboot/objects/counter/impl/JbootRedicCounter.java | 6 +++--- src/main/java/io/jboot/objects/list/JbootList.java | 6 +++--- src/main/java/io/jboot/objects/map/JbootMap.java | 6 +++--- src/main/java/io/jboot/objects/map/JbootMapManager.java | 6 +++--- src/main/java/io/jboot/objects/object/JbootObject.java | 6 +++--- .../java/io/jboot/support/sentinel/SentinelInterceptor.java | 6 +++--- .../java/io/jboot/support/sentinel/SentinelManager.java | 6 +++--- .../java/io/jboot/support/sentinel/SentinelProcesser.java | 6 +++--- src/main/java/io/jboot/utils/CollectionUtil.java | 6 +++--- src/main/java/io/jboot/utils/ModelCopier.java | 6 +++--- src/main/java/io/jboot/utils/NamedThreadFactory.java | 6 +++--- src/main/java/io/jboot/utils/NamedThreadPools.java | 6 +++--- src/main/java/io/jboot/web/render/JbootRenderFactory.java | 2 +- src/test/java/io/jboot/test/HelloWorld.java | 6 +++--- src/test/java/io/jboot/test/aop/AopConfiguration.java | 6 +++--- src/test/java/io/jboot/test/config/ConfigController.java | 6 +++--- src/test/java/io/jboot/test/config/TestConfigModel.java | 6 +++--- src/test/java/io/jboot/test/file/FileController.java | 6 +++--- src/test/java/io/jboot/test/other/ModelCopierTest.java | 6 +++--- .../java/io/jboot/test/sentinel/SentinelController.java | 6 +++--- 40 files changed, 118 insertions(+), 118 deletions(-) diff --git a/src/main/java/io/jboot/app/HttpContentTypes.java b/src/main/java/io/jboot/app/HttpContentTypes.java index 677d31df..fe2adf62 100644 --- a/src/main/java/io/jboot/app/HttpContentTypes.java +++ b/src/main/java/io/jboot/app/HttpContentTypes.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2019, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/app/config/JbootConfigUtil.java b/src/main/java/io/jboot/app/config/JbootConfigUtil.java index c6edb761..4def73fa 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigUtil.java +++ b/src/main/java/io/jboot/app/config/JbootConfigUtil.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java b/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java index 07e3ef07..42c3cf18 100644 --- a/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java index 195d2d49..acff7e4b 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java index 50f307ef..011f9d23 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/components/cache/CacheTime.java b/src/main/java/io/jboot/components/cache/CacheTime.java index 6b108617..fc1b25d2 100644 --- a/src/main/java/io/jboot/components/cache/CacheTime.java +++ b/src/main/java/io/jboot/components/cache/CacheTime.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2019, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java index 571d213f..f52329b5 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java index 7a29bb4d..04dc4777 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java b/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java index ad1e957f..871b519d 100644 --- a/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java +++ b/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/components/rpc/Utils.java b/src/main/java/io/jboot/components/rpc/Utils.java index cc89eb72..4532098c 100644 --- a/src/main/java/io/jboot/components/rpc/Utils.java +++ b/src/main/java/io/jboot/components/rpc/Utils.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index 77dfcccc..bea896de 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java index ac8692ef..6dfa3aee 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/core/log/Slf4jSimpleLogger.java b/src/main/java/io/jboot/core/log/Slf4jSimpleLogger.java index d9ebc19f..1cb510ea 100644 --- a/src/main/java/io/jboot/core/log/Slf4jSimpleLogger.java +++ b/src/main/java/io/jboot/core/log/Slf4jSimpleLogger.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2019, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/db/SqlDebugger.java b/src/main/java/io/jboot/db/SqlDebugger.java index aeaec370..66b98c37 100644 --- a/src/main/java/io/jboot/db/SqlDebugger.java +++ b/src/main/java/io/jboot/db/SqlDebugger.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2019, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/db/model/JbootModelFilter.java b/src/main/java/io/jboot/db/model/JbootModelFilter.java index 1cef7b67..dda8336a 100644 --- a/src/main/java/io/jboot/db/model/JbootModelFilter.java +++ b/src/main/java/io/jboot/db/model/JbootModelFilter.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/db/model/PrimarykeyValueGenerator.java b/src/main/java/io/jboot/db/model/PrimarykeyValueGenerator.java index 84e9cad8..dc16a06f 100644 --- a/src/main/java/io/jboot/db/model/PrimarykeyValueGenerator.java +++ b/src/main/java/io/jboot/db/model/PrimarykeyValueGenerator.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/objects/counter/JbootCounter.java b/src/main/java/io/jboot/objects/counter/JbootCounter.java index edec77cb..0f6e44d5 100644 --- a/src/main/java/io/jboot/objects/counter/JbootCounter.java +++ b/src/main/java/io/jboot/objects/counter/JbootCounter.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/objects/counter/JbootCounterConfig.java b/src/main/java/io/jboot/objects/counter/JbootCounterConfig.java index 69ecb2bf..50fdc6f9 100644 --- a/src/main/java/io/jboot/objects/counter/JbootCounterConfig.java +++ b/src/main/java/io/jboot/objects/counter/JbootCounterConfig.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/objects/counter/JbootCounterManager.java b/src/main/java/io/jboot/objects/counter/JbootCounterManager.java index 13f3b8d9..44bebb00 100644 --- a/src/main/java/io/jboot/objects/counter/JbootCounterManager.java +++ b/src/main/java/io/jboot/objects/counter/JbootCounterManager.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java b/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java index 54bb360f..f1ba1222 100644 --- a/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java +++ b/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/objects/counter/impl/JbootRedicCounter.java b/src/main/java/io/jboot/objects/counter/impl/JbootRedicCounter.java index c9628fff..7c1a23cc 100644 --- a/src/main/java/io/jboot/objects/counter/impl/JbootRedicCounter.java +++ b/src/main/java/io/jboot/objects/counter/impl/JbootRedicCounter.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/objects/list/JbootList.java b/src/main/java/io/jboot/objects/list/JbootList.java index fb6ba05b..42382505 100644 --- a/src/main/java/io/jboot/objects/list/JbootList.java +++ b/src/main/java/io/jboot/objects/list/JbootList.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/objects/map/JbootMap.java b/src/main/java/io/jboot/objects/map/JbootMap.java index 731fad0d..1b7798d8 100644 --- a/src/main/java/io/jboot/objects/map/JbootMap.java +++ b/src/main/java/io/jboot/objects/map/JbootMap.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/objects/map/JbootMapManager.java b/src/main/java/io/jboot/objects/map/JbootMapManager.java index 2e8d6556..8e7bfe16 100644 --- a/src/main/java/io/jboot/objects/map/JbootMapManager.java +++ b/src/main/java/io/jboot/objects/map/JbootMapManager.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/objects/object/JbootObject.java b/src/main/java/io/jboot/objects/object/JbootObject.java index fc756d95..affb692e 100644 --- a/src/main/java/io/jboot/objects/object/JbootObject.java +++ b/src/main/java/io/jboot/objects/object/JbootObject.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java b/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java index 9b1caa8b..8f04a65b 100644 --- a/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java +++ b/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/support/sentinel/SentinelManager.java b/src/main/java/io/jboot/support/sentinel/SentinelManager.java index 43587324..36749798 100644 --- a/src/main/java/io/jboot/support/sentinel/SentinelManager.java +++ b/src/main/java/io/jboot/support/sentinel/SentinelManager.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/support/sentinel/SentinelProcesser.java b/src/main/java/io/jboot/support/sentinel/SentinelProcesser.java index 315af641..e1908e39 100644 --- a/src/main/java/io/jboot/support/sentinel/SentinelProcesser.java +++ b/src/main/java/io/jboot/support/sentinel/SentinelProcesser.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/utils/CollectionUtil.java b/src/main/java/io/jboot/utils/CollectionUtil.java index e6c5bde5..459eb2bf 100644 --- a/src/main/java/io/jboot/utils/CollectionUtil.java +++ b/src/main/java/io/jboot/utils/CollectionUtil.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/utils/ModelCopier.java b/src/main/java/io/jboot/utils/ModelCopier.java index f6778d0c..3d5d5297 100644 --- a/src/main/java/io/jboot/utils/ModelCopier.java +++ b/src/main/java/io/jboot/utils/ModelCopier.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2019, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/utils/NamedThreadFactory.java b/src/main/java/io/jboot/utils/NamedThreadFactory.java index b2455ac5..fedefc54 100644 --- a/src/main/java/io/jboot/utils/NamedThreadFactory.java +++ b/src/main/java/io/jboot/utils/NamedThreadFactory.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2019, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/utils/NamedThreadPools.java b/src/main/java/io/jboot/utils/NamedThreadPools.java index c0dc3d74..a12eb2af 100644 --- a/src/main/java/io/jboot/utils/NamedThreadPools.java +++ b/src/main/java/io/jboot/utils/NamedThreadPools.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2019, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/main/java/io/jboot/web/render/JbootRenderFactory.java b/src/main/java/io/jboot/web/render/JbootRenderFactory.java index 12eb63b8..c8bdf569 100644 --- a/src/main/java/io/jboot/web/render/JbootRenderFactory.java +++ b/src/main/java/io/jboot/web/render/JbootRenderFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/HelloWorld.java b/src/test/java/io/jboot/test/HelloWorld.java index bc0979f0..e2063243 100644 --- a/src/test/java/io/jboot/test/HelloWorld.java +++ b/src/test/java/io/jboot/test/HelloWorld.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2019, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/test/java/io/jboot/test/aop/AopConfiguration.java b/src/test/java/io/jboot/test/aop/AopConfiguration.java index 49719d26..4a244169 100644 --- a/src/test/java/io/jboot/test/aop/AopConfiguration.java +++ b/src/test/java/io/jboot/test/aop/AopConfiguration.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/test/java/io/jboot/test/config/ConfigController.java b/src/test/java/io/jboot/test/config/ConfigController.java index 372fa7b9..a50a529e 100644 --- a/src/test/java/io/jboot/test/config/ConfigController.java +++ b/src/test/java/io/jboot/test/config/ConfigController.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/test/java/io/jboot/test/config/TestConfigModel.java b/src/test/java/io/jboot/test/config/TestConfigModel.java index 64f0514c..098b321a 100644 --- a/src/test/java/io/jboot/test/config/TestConfigModel.java +++ b/src/test/java/io/jboot/test/config/TestConfigModel.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/test/java/io/jboot/test/file/FileController.java b/src/test/java/io/jboot/test/file/FileController.java index f3492b2f..dfaca878 100644 --- a/src/test/java/io/jboot/test/file/FileController.java +++ b/src/test/java/io/jboot/test/file/FileController.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2019, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/test/java/io/jboot/test/other/ModelCopierTest.java b/src/test/java/io/jboot/test/other/ModelCopierTest.java index 4adeaa6d..0d1f74a2 100644 --- a/src/test/java/io/jboot/test/other/ModelCopierTest.java +++ b/src/test/java/io/jboot/test/other/ModelCopierTest.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/test/java/io/jboot/test/sentinel/SentinelController.java b/src/test/java/io/jboot/test/sentinel/SentinelController.java index c6a3c9cf..b4d027f9 100644 --- a/src/test/java/io/jboot/test/sentinel/SentinelController.java +++ b/src/test/java/io/jboot/test/sentinel/SentinelController.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, -- Gitee From 4479376d24d8150859a94ffd26f47ef958f0c10e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 20:18:45 +0800 Subject: [PATCH 0120/1965] v3.1.0 --- doc/docs/gateway.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/docs/gateway.md b/doc/docs/gateway.md index 128a3883..9e60e76d 100644 --- a/doc/docs/gateway.md +++ b/doc/docs/gateway.md @@ -20,6 +20,9 @@ jboot.gateway.name = name jboot.gateway.uri = http://youdomain:8080 jboot.gateway.enable = true +jboot.gateway.sentinelEnable = false +jboot.gateway.sentinelBlockPage = /block + jboot.gateway.pathEquals = /path jboot.gateway.pathContains = /path jboot.gateway.pathStartsWith = /path @@ -34,6 +37,12 @@ jboot.gateway.queryEquals = aa:bb,cc:dd jboot.gateway.queryContains = aa,bb ``` +- name 设置路由的名称 +- uri 网路路由目标网址 +- enable 是否启用该路由 +- sentinelEnable 是否启用 sentinel 限流功能 +- sentinelBlockPage 若该路由被限流后,网页自动跳转到哪个网址 + ## Path 路由 Path 路由一般是最常用的路由之一,是根据域名之后的路径进行路由的,Jboot 对 Path 路由提供了 4 中方式: -- Gitee From 570ada9829bf739f0f9d52a95e5aa7057384f01a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Mar 2020 20:37:21 +0800 Subject: [PATCH 0121/1965] v3.1.0 --- changes.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changes.txt b/changes.txt index 385e5b7c..7e4aac30 100644 --- a/changes.txt +++ b/changes.txt @@ -1,12 +1,12 @@ jboot v3.1.0: -新增:新增网关的支持 +新增:新增网关的支持,路由规则支持 host、path、query等 三种模式,同时支持基于 Sentinel 的限流配置 新增:新增对 Dubbo 多协议、多注册中心等的支持 新增:新增对注解 @Configuration 的支持,可以通过其构建 name 实例 新增:JbootAopInterceptor 可以动态的添加或者移除拦截器 新增:配置文件可以配置 Map、Set、List 和 数组的支持 新增:JbootCache 新增可以获取素有 Names 的功能,方便对缓存进行运维 新增:JbootCache 新增可以对分布式缓存进行刷新,方便对缓存进行运维 -新增:jboot-system.properties 的支持,用于可以在里面对 System.properties 进行配置,很多第三方组件依赖于 System.porperties 的配置 +新增:jboot-system.properties 的支持,用于替代启动参数的 -D,使用第三方组件的时候更加方便 修复:修复 JbootHttpImpl 无法上传文件的bug 修复:修复 JbootHttpImpl 无法正确获取 gzip 压缩内容的问题 优化:重构 RPC 的模块,使之更加简单清晰 -- Gitee From e0ad55fde472cac13f94851ff474a8e9cc4849fa Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 23 Mar 2020 07:42:06 +0800 Subject: [PATCH 0122/1965] v3.1.0 --- .../components/gateway/GatewayHttpProxy.java | 76 +++++++++---------- .../gateway/GatewaySentinelProcesser.java | 13 ++-- .../gateway/JbootGatewayConfig.java | 3 + 3 files changed, 48 insertions(+), 44 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index e58684ca..6923ba80 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -42,59 +42,59 @@ public class GatewayHttpProxy { private static int CONNECT_TIMEOUT = 5000; - public static void sendRequest(String url, HttpServletRequest request, HttpServletResponse response) { + public static void sendRequest(String url, HttpServletRequest req, HttpServletResponse resp) { - HttpURLConnection connection = null; + HttpURLConnection conn = null; try { - connection = getConnection(url); + conn = getConnection(url); /** * 设置 http 请求头 */ - configConnection(connection, request); + configConnection(conn, req); - connection.connect(); + conn.connect(); /** * 复制 post 请求内容到目标服务器 */ - copyRequestStreamToConnection(request, connection); + copyRequestStreamToConnection(req, conn); /** * 配置响应的 HTTP 头 */ - configResponse(response, connection); + configResponse(resp, conn); /** * 复制目标流到 Response */ - copyStreamToResponse(connection, response); + copyStreamToResponse(conn, resp); } catch (Exception ex) { LOG.warn(ex.toString(), ex); } finally { - if (connection != null) { - connection.disconnect(); + if (conn != null) { + conn.disconnect(); } } } - private static void copyRequestStreamToConnection(HttpServletRequest request, HttpURLConnection connection) throws IOException { + private static void copyRequestStreamToConnection(HttpServletRequest req, HttpURLConnection conn) throws IOException { OutputStream outStream = null; InputStream inStream = null; try { // 如果不是 post 请求,不需要复制 - if ("get".equalsIgnoreCase(request.getMethod())) { + if ("get".equalsIgnoreCase(req.getMethod())) { return; } - connection.setDoOutput(true); - outStream = connection.getOutputStream(); - inStream = request.getInputStream(); + conn.setDoOutput(true); + outStream = conn.getOutputStream(); + inStream = req.getInputStream(); int n; byte[] buffer = new byte[1024]; while (-1 != (n = inStream.read(buffer))) { @@ -107,13 +107,13 @@ public class GatewayHttpProxy { } - private static void copyStreamToResponse(HttpURLConnection connection, HttpServletResponse response) throws IOException { + private static void copyStreamToResponse(HttpURLConnection conn, HttpServletResponse resp) throws IOException { InputStream inStream = null; InputStreamReader reader = null; try { - if (!response.isCommitted()) { - PrintWriter writer = response.getWriter(); - inStream = getInputStream(connection); + if (!resp.isCommitted()) { + PrintWriter writer = resp.getWriter(); + inStream = getInputStream(conn); reader = new InputStreamReader(inStream); int len; char[] buffer = new char[1024]; @@ -140,29 +140,29 @@ public class GatewayHttpProxy { } - private static void configResponse(HttpServletResponse response, HttpURLConnection connection) throws IOException { - response.setContentType(connection.getContentType()); - response.setStatus(connection.getResponseCode()); + private static void configResponse(HttpServletResponse resp, HttpURLConnection conn) throws IOException { + resp.setContentType(conn.getContentType()); + resp.setStatus(conn.getResponseCode()); - Map> headerFields = connection.getHeaderFields(); + Map> headerFields = conn.getHeaderFields(); if (headerFields != null && !headerFields.isEmpty()) { Set headerNames = headerFields.keySet(); for (String headerName : headerNames) { //需要排除 Content-Encoding,因为 Server 可能已经使用 gzip 压缩,但是此代理已经对 gzip 内容进行解压了 if (StrUtil.isNotBlank(headerName) && !"Content-Encoding".equalsIgnoreCase(headerName)) { - response.setHeader(headerName, connection.getHeaderField(headerName)); + resp.setHeader(headerName, conn.getHeaderField(headerName)); } } } } - private static InputStream getInputStream(HttpURLConnection connection) throws IOException { + private static InputStream getInputStream(HttpURLConnection conn) throws IOException { - InputStream stream = connection.getResponseCode() >= 400 - ? connection.getErrorStream() - : connection.getInputStream(); + InputStream stream = conn.getResponseCode() >= 400 + ? conn.getErrorStream() + : conn.getInputStream(); - if ("gzip".equalsIgnoreCase(connection.getContentEncoding())) { + if ("gzip".equalsIgnoreCase(conn.getContentEncoding())) { return new GZIPInputStream(stream); } else { return stream; @@ -171,18 +171,18 @@ public class GatewayHttpProxy { } - private static void configConnection(HttpURLConnection connection, HttpServletRequest request) throws ProtocolException { + private static void configConnection(HttpURLConnection conn, HttpServletRequest req) throws ProtocolException { - connection.setReadTimeout(READ_TIMEOUT); - connection.setConnectTimeout(CONNECT_TIMEOUT); - connection.setInstanceFollowRedirects(true); + conn.setReadTimeout(READ_TIMEOUT); + conn.setConnectTimeout(CONNECT_TIMEOUT); + conn.setInstanceFollowRedirects(true); - connection.setRequestMethod(request.getMethod()); - Enumeration headerNames = request.getHeaderNames(); + conn.setRequestMethod(req.getMethod()); + Enumeration headerNames = req.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); if (StrUtil.isNotBlank(headerName)) { - connection.setRequestProperty(headerName, request.getHeader(headerName)); + conn.setRequestProperty(headerName, req.getHeader(headerName)); } } } @@ -199,8 +199,8 @@ public class GatewayHttpProxy { } } - private static HttpURLConnection getHttpConnection(String urlStr) throws Exception { - URL url = new URL(urlStr); + private static HttpURLConnection getHttpConnection(String urlString) throws Exception { + URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); return conn; } diff --git a/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java b/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java index 69db3fc1..7e7de2b8 100644 --- a/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java +++ b/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java @@ -18,6 +18,7 @@ package io.jboot.components.gateway; import com.alibaba.csp.sentinel.*; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.util.StringUtil; +import com.jfinal.kit.LogKit; import io.jboot.utils.StrUtil; import javax.servlet.http.HttpServletRequest; @@ -39,7 +40,7 @@ public class GatewaySentinelProcesser { entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN); runnable.run(); } catch (BlockException ex) { - processBlocked(config, req, resp, entry); + processBlocked(config, req, resp); } catch (Exception ex) { Tracer.traceEntry(ex, entry); throw ex; @@ -50,7 +51,7 @@ public class GatewaySentinelProcesser { } } - private static void processBlocked(JbootGatewayConfig config, HttpServletRequest req, HttpServletResponse resp, Entry entry) { + private static void processBlocked(JbootGatewayConfig config, HttpServletRequest req, HttpServletResponse resp) { StringBuffer url = req.getRequestURL(); if ("GET".equals(req.getMethod()) && StrUtil.isNotBlank(req.getQueryString())) { @@ -65,15 +66,15 @@ public class GatewaySentinelProcesser { resp.sendRedirect(redirectUrl); } } catch (IOException ex) { - Tracer.traceEntry(ex, entry); + LogKit.error(ex.toString(), ex); } } - private static void writeDefaultBlockedPage(HttpServletResponse response) throws IOException { - response.setStatus(200); - PrintWriter out = response.getWriter(); + private static void writeDefaultBlockedPage(HttpServletResponse resp) throws IOException { + resp.setStatus(200); + PrintWriter out = resp.getWriter(); out.print("Blocked by Sentinel (flow limiting) in Jboot"); } diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index ade2f2d8..af50e5c5 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -32,7 +32,10 @@ public class JbootGatewayConfig implements Serializable { private String name; private String uri; private boolean enable = false; + + // 是否启用 sentinel 限流 private boolean sentinelEnable = false; + // sentinel 被限流后跳转地址 private String sentinelBlockPage; private String[] pathEquals; -- Gitee From 1ea34266aba43e6c21608a22b957c0d7b57e444a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 23 Mar 2020 08:20:52 +0800 Subject: [PATCH 0123/1965] v3.1.0 --- .../components/gateway/GatewayHttpProxy.java | 30 ++++-- .../gateway/JbootGatewayConfig.java | 101 +++++++++++------- .../gateway/JbootGatewayHandler.java | 2 +- src/test/resources/jboot.properties | 4 +- 4 files changed, 88 insertions(+), 49 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index 6923ba80..4a533428 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -38,11 +38,17 @@ public class GatewayHttpProxy { private static final Log LOG = Log.getLog(GatewayHttpProxy.class); - private static int READ_TIMEOUT = 10000; - private static int CONNECT_TIMEOUT = 5000; - + private int readTimeOut; + private int connectTimeOut; + private String contentType; + + public GatewayHttpProxy(JbootGatewayConfig config) { + this.readTimeOut = config.getProxyReadTimeout(); + this.connectTimeOut = config.getProxyConnectTimeout(); + this.contentType = config.getProxyContentType(); + } - public static void sendRequest(String url, HttpServletRequest req, HttpServletResponse resp) { + public void sendRequest(String url, HttpServletRequest req, HttpServletResponse resp) { HttpURLConnection conn = null; @@ -82,7 +88,7 @@ public class GatewayHttpProxy { } } - private static void copyRequestStreamToConnection(HttpServletRequest req, HttpURLConnection conn) throws IOException { + private void copyRequestStreamToConnection(HttpServletRequest req, HttpURLConnection conn) throws IOException { OutputStream outStream = null; InputStream inStream = null; try { @@ -107,7 +113,7 @@ public class GatewayHttpProxy { } - private static void copyStreamToResponse(HttpURLConnection conn, HttpServletResponse resp) throws IOException { + private void copyStreamToResponse(HttpURLConnection conn, HttpServletResponse resp) throws IOException { InputStream inStream = null; InputStreamReader reader = null; try { @@ -140,8 +146,8 @@ public class GatewayHttpProxy { } - private static void configResponse(HttpServletResponse resp, HttpURLConnection conn) throws IOException { - resp.setContentType(conn.getContentType()); + private void configResponse(HttpServletResponse resp, HttpURLConnection conn) throws IOException { + resp.setContentType(contentType); resp.setStatus(conn.getResponseCode()); Map> headerFields = conn.getHeaderFields(); @@ -171,11 +177,13 @@ public class GatewayHttpProxy { } - private static void configConnection(HttpURLConnection conn, HttpServletRequest req) throws ProtocolException { + private void configConnection(HttpURLConnection conn, HttpServletRequest req) throws ProtocolException { - conn.setReadTimeout(READ_TIMEOUT); - conn.setConnectTimeout(CONNECT_TIMEOUT); + conn.setReadTimeout(readTimeOut); + conn.setConnectTimeout(connectTimeOut); conn.setInstanceFollowRedirects(true); + conn.setUseCaches(false); +// conn.setDefaultUseCaches(); conn.setRequestMethod(req.getMethod()); Enumeration headerNames = req.getHeaderNames(); diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index af50e5c5..0d4692b6 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -29,6 +29,8 @@ import java.util.Objects; */ public class JbootGatewayConfig implements Serializable { + public static final String DEFAULT_PROXY_CONTENT_TYPE = "text/html;charset=utf-8"; + private String name; private String uri; private boolean enable = false; @@ -38,6 +40,11 @@ public class JbootGatewayConfig implements Serializable { // sentinel 被限流后跳转地址 private String sentinelBlockPage; + // 设置 http 代理的参数 + private int proxyReadTimeout = 10000; //10s + private int proxyConnectTimeout = 5000; //5s + private String proxyContentType = DEFAULT_PROXY_CONTENT_TYPE; + private String[] pathEquals; private String[] pathContains; private String[] pathStartsWith; @@ -98,6 +105,30 @@ public class JbootGatewayConfig implements Serializable { this.sentinelBlockPage = sentinelBlockPage; } + public int getProxyReadTimeout() { + return proxyReadTimeout; + } + + public void setProxyReadTimeout(int proxyReadTimeout) { + this.proxyReadTimeout = proxyReadTimeout; + } + + public int getProxyConnectTimeout() { + return proxyConnectTimeout; + } + + public void setProxyConnectTimeout(int proxyConnectTimeout) { + this.proxyConnectTimeout = proxyConnectTimeout; + } + + public String getProxyContentType() { + return proxyContentType; + } + + public void setProxyContentType(String proxyContentType) { + this.proxyContentType = proxyContentType; + } + public String[] getPathEquals() { return pathEquals; } @@ -178,85 +209,85 @@ public class JbootGatewayConfig implements Serializable { this.queryContains = queryContains; } - public boolean isConfigOk(){ + public boolean isConfigOk() { return StrUtil.isNotBlank(uri); } - public boolean matches(HttpServletRequest request){ - if (request == null){ + public boolean matches(HttpServletRequest request) { + if (request == null) { return false; } String path = request.getServletPath(); - if (pathEquals != null){ - for (String p : pathEquals){ - if (path.equals(p)){ + if (pathEquals != null) { + for (String p : pathEquals) { + if (path.equals(p)) { return true; } } } - if (pathContains != null){ - for (String p : pathContains){ - if (path.contains(p)){ + if (pathContains != null) { + for (String p : pathContains) { + if (path.contains(p)) { return true; } } } - if (pathStartsWith != null){ - for (String p : pathStartsWith){ - if (path.startsWith(p)){ + if (pathStartsWith != null) { + for (String p : pathStartsWith) { + if (path.startsWith(p)) { return true; } } } - if (pathEndswith != null){ - for (String p : pathEndswith){ - if (path.endsWith(p)){ + if (pathEndswith != null) { + for (String p : pathEndswith) { + if (path.endsWith(p)) { return true; } } } String host = request.getServerName(); - if (hostEquals != null){ - for (String h : hostEquals){ - if (host.equals(h)){ + if (hostEquals != null) { + for (String h : hostEquals) { + if (host.equals(h)) { return true; } } } - if (hostContains != null){ - for (String h : hostContains){ - if (host.contains(h)){ + if (hostContains != null) { + for (String h : hostContains) { + if (host.contains(h)) { return true; } } } - if (hostStartsWith != null){ - for (String h : hostStartsWith){ - if (host.startsWith(h)){ + if (hostStartsWith != null) { + for (String h : hostStartsWith) { + if (host.startsWith(h)) { return true; } } } - if (hostEndswith != null){ - for (String h : hostStartsWith){ - if (host.endsWith(h)){ + if (hostEndswith != null) { + for (String h : hostStartsWith) { + if (host.endsWith(h)) { return true; } } } - if (queryContains != null || queryEquals != null){ - Map queryMap = queryStringToMap(request.getQueryString()); - if (queryMap != null && !queryMap.isEmpty()){ + if (queryContains != null || queryEquals != null) { + Map queryMap = queryStringToMap(request.getQueryString()); + if (queryMap != null && !queryMap.isEmpty()) { if (queryContains != null) { for (String q : queryContains) { @@ -266,10 +297,10 @@ public class JbootGatewayConfig implements Serializable { } } - if (queryEquals != null){ - for (Map.Entry e : queryEquals.entrySet()){ + if (queryEquals != null) { + for (Map.Entry e : queryEquals.entrySet()) { String queryValue = queryMap.get(e.getKey()); - if (Objects.equals(queryValue,e.getValue())){ + if (Objects.equals(queryValue, e.getValue())) { return true; } } @@ -278,14 +309,12 @@ public class JbootGatewayConfig implements Serializable { } - return false; } - private static Map queryStringToMap(String queryString) { - if (StrUtil.isBlank(queryString)){ + if (StrUtil.isBlank(queryString)) { return null; } String[] params = queryString.split("&"); diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java index f52329b5..c23c6b11 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java @@ -49,7 +49,7 @@ public class JbootGatewayHandler extends Handler { url.append("?").append(request.getQueryString()); } - GatewayHttpProxy.sendRequest(url.toString(), request, response); + new GatewayHttpProxy(config).sendRequest(url.toString(), request, response); }; if (config.isSentinelEnable()) { diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index ec69754b..741ebcc5 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -43,4 +43,6 @@ jboot.gateway.pathEquals = / jboot.gateway.gitee.enable = true jboot.gateway.gitee.uri= https://gitee.com -jboot.gateway.gitee.pathStartsWith = /fuhai \ No newline at end of file +jboot.gateway.gitee.sentinelEnable= true +jboot.gateway.gitee.sentinelBlockPage= / +jboot.gateway.gitee.pathStartsWith = /fuhai -- Gitee From df92a5f996c2f9996a93fa75cd80c27d776b5f48 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 23 Mar 2020 08:23:56 +0800 Subject: [PATCH 0124/1965] v3.1.0 --- doc/docs/gateway.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/docs/gateway.md b/doc/docs/gateway.md index 9e60e76d..a46c795b 100644 --- a/doc/docs/gateway.md +++ b/doc/docs/gateway.md @@ -23,6 +23,10 @@ jboot.gateway.enable = true jboot.gateway.sentinelEnable = false jboot.gateway.sentinelBlockPage = /block +jboot.gateway.proxyReadTimeout = 10000 +jboot.gateway.proxyConnectTimeout = 5000 +jboot.gateway.proxyContentType = text/html;charset=utf-8 + jboot.gateway.pathEquals = /path jboot.gateway.pathContains = /path jboot.gateway.pathStartsWith = /path @@ -42,6 +46,9 @@ jboot.gateway.queryContains = aa,bb - enable 是否启用该路由 - sentinelEnable 是否启用 sentinel 限流功能 - sentinelBlockPage 若该路由被限流后,网页自动跳转到哪个网址 +- proxyReadTimeout 发生路由后,默认的请求超时时间,默认为 10 秒 +- proxyConnectTimeout 发生路由后,默认的链接超时时间,默认为 5 秒 +- proxyContentType 发生路由后,返回给浏览器的 http-content-type,默认为:text/html;charset=utf-8 ## Path 路由 -- Gitee From 4e4610a0bc541ebd44660ce19f68e9710e57d4e4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 23 Mar 2020 08:55:58 +0800 Subject: [PATCH 0125/1965] add dubbo-nacos demos --- .../rpc/dubbonacos/DubboClientNacosDemo.java | 40 ++++++++++++++++++ .../rpc/dubbonacos/DubboServer1NacosDemo.java | 42 +++++++++++++++++++ .../rpc/dubbonacos/DubboServer2NacosDemo.java | 41 ++++++++++++++++++ .../io/jboot/test/rpc/dubbonacos/readme.md | 3 ++ 4 files changed, 126 insertions(+) create mode 100644 src/test/java/io/jboot/test/rpc/dubbonacos/DubboClientNacosDemo.java create mode 100644 src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java create mode 100644 src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java create mode 100644 src/test/java/io/jboot/test/rpc/dubbonacos/readme.md diff --git a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboClientNacosDemo.java b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboClientNacosDemo.java new file mode 100644 index 00000000..16a4105c --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboClientNacosDemo.java @@ -0,0 +1,40 @@ +package io.jboot.test.rpc.dubbonacos; + +import io.jboot.app.JbootApplication; +import io.jboot.components.rpc.annotation.RPCInject; +import io.jboot.test.rpc.commons.BlogService; +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; + +@RequestMapping("/dubbo/nacos") +public class DubboClientNacosDemo extends JbootController { + + public static void main(String[] args) { + + //jboot端口号配置 + JbootApplication.setBootArg("undertow.port", "9999"); + + JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); + + + // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "nacos"); + //注册中心地址,即zookeeper的地址 + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:8080"); + + + JbootApplication.run(args); + } + + + @RPCInject + private BlogService blogService; + + public void index() { + + System.out.println("DubboClientNacosDemo.index()"); + + System.out.println(blogService); + renderText(blogService.findById()); + } +} diff --git a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java new file mode 100644 index 00000000..95ed6c2e --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java @@ -0,0 +1,42 @@ +package io.jboot.test.rpc.dubbonacos; + + +import io.jboot.app.JbootApplication; + +public class DubboServer1NacosDemo { + + public static void main(String[] args) { + + //jboot端口号配置 + JbootApplication.setBootArg("undertow.port", "9997"); + + // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, + // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 + JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); + JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); + + + + + + // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "nacos"); + //注册中心地址,即zookeeper的地址 + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:8848"); + + + //dubbo 的通信协议配置,name 可以不用配置,默认值为 dubbo + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.name", "dubbo"); + //dubbo 的通信协议配置,如果port配置为-1,则会分配一个没有被占用的端口。 + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "28080"); + + + + JbootApplication.run(args); + + + System.out.println("DubboServer1NacosDemo started..."); + + + } +} diff --git a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java new file mode 100644 index 00000000..09900979 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java @@ -0,0 +1,41 @@ +package io.jboot.test.rpc.dubbonacos; + + +import io.jboot.app.JbootApplication; + +public class DubboServer2NacosDemo { + + public static void main(String[] args) { + + //jboot端口号配置 + JbootApplication.setBootArg("undertow.port", "9998"); + + // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, + // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 + JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); + JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); + + + + + + // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "nacos"); + //注册中心地址,即zookeeper的地址 + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:8848"); + + + //dubbo 的通信协议配置,name 可以不用配置,默认值为 dubbo + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.name", "dubbo"); + //dubbo 的通信协议配置,如果port配置为-1,则会分配一个没有被占用的端口。 + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "28081"); + + JbootApplication.run(args); + + + + System.out.println("DubboServer2NacosDemo started..."); + + + } +} diff --git a/src/test/java/io/jboot/test/rpc/dubbonacos/readme.md b/src/test/java/io/jboot/test/rpc/dubbonacos/readme.md new file mode 100644 index 00000000..6f524738 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/dubbonacos/readme.md @@ -0,0 +1,3 @@ +在开始之前,先启动 nacos,启动建议使用 docker 的方式,更加方便。 + +参考 https://nacos.io/zh-cn/docs/quick-start-docker.html \ No newline at end of file -- Gitee From b05c4703524c913ce2cc32f52837ca5f0c4c8ac2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 23 Mar 2020 09:44:45 +0800 Subject: [PATCH 0126/1965] v3.1.0 --- .../jboot/components/rpc/dubbo/DubboUtil.java | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index bea896de..28d055b4 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -83,7 +83,7 @@ class DubboUtil { if (metadataReportConfigs.size() == 1) { dubboBootstrap.metadataReport(getDefault(metadataReportConfigs)); } else { - dubboBootstrap.metadataReports((List) toList(metadataReportConfigs)); + dubboBootstrap.metadataReports(toList(metadataReportConfigs)); } } @@ -93,7 +93,7 @@ class DubboUtil { if (configCenterConfigs.size() == 1) { dubboBootstrap.configCenter(getDefault(configCenterConfigs)); } else { - dubboBootstrap.configCenters((List) toList(configCenterConfigs)); + dubboBootstrap.configCenters(toList(configCenterConfigs)); } } @@ -105,7 +105,7 @@ class DubboUtil { if (protocolConfigs.size() == 1) { dubboBootstrap.protocol(getDefault(protocolConfigs)); } else { - dubboBootstrap.protocols((List) toList(protocolConfigs)); + dubboBootstrap.protocols(toList(protocolConfigs)); } } @@ -116,7 +116,7 @@ class DubboUtil { if (registryConfigs.size() == 1) { dubboBootstrap.registry(getDefault(registryConfigs)); } else { - dubboBootstrap.registries((List) toList(registryConfigs)); + dubboBootstrap.registries(toList(registryConfigs)); } } //没有配置注册中心,一般只用于希望此服务网提供直连的方式给客户端使用 @@ -148,7 +148,7 @@ class DubboUtil { if (consumerConfigs.size() == 1) { dubboBootstrap.consumer(getDefault(consumerConfigs)); } else { - dubboBootstrap.consumers((List) toList(consumerConfigs)); + dubboBootstrap.consumers(toList(consumerConfigs)); } } @@ -163,7 +163,7 @@ class DubboUtil { if (providerConfigs.size() == 1) { dubboBootstrap.provider(getDefault(providerConfigs)); } else { - dubboBootstrap.providers((List) toList(providerConfigs)); + dubboBootstrap.providers(toList(providerConfigs)); } } } @@ -229,18 +229,20 @@ class DubboUtil { return JbootConfigUtil.getConfigModels(clazz, prefix); } - private static T getDefault(Map map) { - return map.get("default"); + private static T getDefault(Map map) { + AbstractConfig config = (AbstractConfig) map.values().iterator().next(); + if (config != null){ + config.setId(map.keySet().iterator().next()); + } + return (T) config; } - private static List toList(Map map) { - List list = new ArrayList<>(map.size()); - for (Map.Entry entry : map.entrySet()) { - AbstractConfig config = entry.getValue(); - if (StrUtil.isBlank(config.getId())) { - config.setId(entry.getKey()); - } - list.add(config); + private static List toList(Map map) { + List list = new ArrayList<>(map.size()); + for (Map.Entry entry : map.entrySet()) { + AbstractConfig config = (AbstractConfig) entry.getValue(); + config.setId(entry.getKey()); + list.add((T) config); } return list; } -- Gitee From 415fe91b517f6eb6b3bfc32c450123ca71ba10fc Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 23 Mar 2020 09:45:29 +0800 Subject: [PATCH 0127/1965] add dubbo-mutil-registry demos --- .../rpc/dubbonacos/DubboClientNacosDemo.java | 6 +-- .../multiregistry/DubboClientMultiDemo.java | 43 +++++++++++++++++ .../multiregistry/DubboClientNacosDemo.java | 41 ++++++++++++++++ .../rpc/multiregistry/DubboClientZkDemo.java | 38 +++++++++++++++ .../multiregistry/DubboServerMultiDemo.java | 47 +++++++++++++++++++ .../multiregistry/DubboServerNacosDemo.java | 42 +++++++++++++++++ .../rpc/multiregistry/DubboServerZkDemo.java | 39 +++++++++++++++ 7 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 src/test/java/io/jboot/test/rpc/multiregistry/DubboClientMultiDemo.java create mode 100644 src/test/java/io/jboot/test/rpc/multiregistry/DubboClientNacosDemo.java create mode 100644 src/test/java/io/jboot/test/rpc/multiregistry/DubboClientZkDemo.java create mode 100644 src/test/java/io/jboot/test/rpc/multiregistry/DubboServerMultiDemo.java create mode 100644 src/test/java/io/jboot/test/rpc/multiregistry/DubboServerNacosDemo.java create mode 100644 src/test/java/io/jboot/test/rpc/multiregistry/DubboServerZkDemo.java diff --git a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboClientNacosDemo.java b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboClientNacosDemo.java index 16a4105c..b1d55ef1 100644 --- a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboClientNacosDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboClientNacosDemo.java @@ -6,7 +6,7 @@ import io.jboot.test.rpc.commons.BlogService; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; -@RequestMapping("/dubbo/nacos") +@RequestMapping("/dubbonacos") public class DubboClientNacosDemo extends JbootController { public static void main(String[] args) { @@ -19,8 +19,8 @@ public class DubboClientNacosDemo extends JbootController { // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "nacos"); - //注册中心地址,即zookeeper的地址 - JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:8080"); + //注册中心地址,即 nacos 的地址 + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:8848"); JbootApplication.run(args); diff --git a/src/test/java/io/jboot/test/rpc/multiregistry/DubboClientMultiDemo.java b/src/test/java/io/jboot/test/rpc/multiregistry/DubboClientMultiDemo.java new file mode 100644 index 00000000..12d4e75e --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/multiregistry/DubboClientMultiDemo.java @@ -0,0 +1,43 @@ +package io.jboot.test.rpc.multiregistry; + +import io.jboot.app.JbootApplication; +import io.jboot.components.rpc.annotation.RPCInject; +import io.jboot.test.rpc.commons.BlogService; +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; + +@RequestMapping("/dubbo/mutil") +public class DubboClientMultiDemo extends JbootController { + + public static void main(String[] args) { + + //jboot端口号配置 + JbootApplication.setBootArg("undertow.port", "9005"); + + JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); + + + //两个注册中心,id 分别为 default、zk + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "nacos"); + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:8848"); + + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.zk.protocol", "zookeeper"); + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.zk.address", "127.0.0.1:2181"); + + + + JbootApplication.run(args); + } + + + @RPCInject + private BlogService blogService; + + public void index() { + + System.out.println("DubboClientNacosDemo.index()"); + + System.out.println(blogService); + renderText(blogService.findById()); + } +} diff --git a/src/test/java/io/jboot/test/rpc/multiregistry/DubboClientNacosDemo.java b/src/test/java/io/jboot/test/rpc/multiregistry/DubboClientNacosDemo.java new file mode 100644 index 00000000..7788ec28 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/multiregistry/DubboClientNacosDemo.java @@ -0,0 +1,41 @@ +package io.jboot.test.rpc.multiregistry; + +import io.jboot.app.JbootApplication; +import io.jboot.components.rpc.annotation.RPCInject; +import io.jboot.test.rpc.commons.BlogService; +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; + +@RequestMapping("/dubbo/nacos") +public class DubboClientNacosDemo extends JbootController { + + public static void main(String[] args) { + + //jboot端口号配置 + JbootApplication.setBootArg("undertow.port", "9004"); + + JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); + + + // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "nacos"); + //注册中心地址,即 nacos 的地址 + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:8848"); + + + + JbootApplication.run(args); + } + + + @RPCInject + private BlogService blogService; + + public void index() { + + System.out.println("DubboClientNacosDemo.index()"); + + System.out.println(blogService); + renderText(blogService.findById()); + } +} diff --git a/src/test/java/io/jboot/test/rpc/multiregistry/DubboClientZkDemo.java b/src/test/java/io/jboot/test/rpc/multiregistry/DubboClientZkDemo.java new file mode 100644 index 00000000..99bc1bd7 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/multiregistry/DubboClientZkDemo.java @@ -0,0 +1,38 @@ +package io.jboot.test.rpc.multiregistry; + +import io.jboot.app.JbootApplication; +import io.jboot.components.rpc.annotation.RPCInject; +import io.jboot.test.rpc.commons.BlogService; +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; + +@RequestMapping("/dubbo/zk") +public class DubboClientZkDemo extends JbootController { + + public static void main(String[] args) { + + //jboot端口号配置 + JbootApplication.setBootArg("undertow.port", "9003"); + + JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); + + + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.zk.protocol", "zookeeper"); + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.zk.address", "127.0.0.1:2181"); + + + JbootApplication.run(args); + } + + + @RPCInject + private BlogService blogService; + + public void index() { + + System.out.println("DubboClientZkDemo.index()"); + + System.out.println(blogService); + renderText(blogService.findById()); + } +} diff --git a/src/test/java/io/jboot/test/rpc/multiregistry/DubboServerMultiDemo.java b/src/test/java/io/jboot/test/rpc/multiregistry/DubboServerMultiDemo.java new file mode 100644 index 00000000..4ccd39d3 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/multiregistry/DubboServerMultiDemo.java @@ -0,0 +1,47 @@ +package io.jboot.test.rpc.multiregistry; + + +import io.jboot.app.JbootApplication; + +public class DubboServerMultiDemo { + + public static void main(String[] args) { + + //jboot端口号配置 + JbootApplication.setBootArg("undertow.port", "9002"); + + // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, + // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 + JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); + JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); + + + + + //两个注册中心,id 分别为 default、zk,默认情况下,如果 service 没指定注册中心,service 会向所有的注册中心发布自己的服务 + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "nacos"); + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:8848"); + + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.zk.protocol", "zookeeper"); + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.zk.address", "127.0.0.1:2181"); + + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.zk2.protocol", "zookeeper"); + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.zk2.address", "127.0.0.1:2181"); + + + + + //dubbo 的通信协议配置,name 可以不用配置,默认值为 dubbo + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.name", "dubbo"); + //dubbo 的通信协议配置,如果port配置为-1,则会分配一个没有被占用的端口。 + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "-1"); + + JbootApplication.run(args); + + + + System.out.println("DubboServer2NacosDemo started..."); + + + } +} diff --git a/src/test/java/io/jboot/test/rpc/multiregistry/DubboServerNacosDemo.java b/src/test/java/io/jboot/test/rpc/multiregistry/DubboServerNacosDemo.java new file mode 100644 index 00000000..c698a467 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/multiregistry/DubboServerNacosDemo.java @@ -0,0 +1,42 @@ +package io.jboot.test.rpc.multiregistry; + + +import io.jboot.app.JbootApplication; + +public class DubboServerNacosDemo { + + public static void main(String[] args) { + + //jboot端口号配置 + JbootApplication.setBootArg("undertow.port", "9001"); + + // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, + // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 + JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); + JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); + + + + + + // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "nacos"); + //注册中心地址,即zookeeper的地址 + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:8848"); + + + //dubbo 的通信协议配置,name 可以不用配置,默认值为 dubbo + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.name", "dubbo"); + //dubbo 的通信协议配置,如果port配置为-1,则会分配一个没有被占用的端口。 + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "-1"); + + + + JbootApplication.run(args); + + + System.out.println("DubboServer1NacosDemo started..."); + + + } +} diff --git a/src/test/java/io/jboot/test/rpc/multiregistry/DubboServerZkDemo.java b/src/test/java/io/jboot/test/rpc/multiregistry/DubboServerZkDemo.java new file mode 100644 index 00000000..539c881b --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/multiregistry/DubboServerZkDemo.java @@ -0,0 +1,39 @@ +package io.jboot.test.rpc.multiregistry; + + +import io.jboot.app.JbootApplication; + +public class DubboServerZkDemo { + + public static void main(String[] args) { + + //jboot端口号配置 + JbootApplication.setBootArg("undertow.port", "9000"); + + // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, + // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 + JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); + JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); + + + + // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "zookeeper"); + //注册中心地址,即zookeeper的地址 + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:2181"); + + + //dubbo 的通信协议配置,name 可以不用配置,默认值为 dubbo + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.name", "dubbo"); + //dubbo 的通信协议配置,如果port配置为-1,则会分配一个没有被占用的端口。 + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "-1"); + + JbootApplication.run(args); + + + + System.out.println("DubboServer2NacosDemo started..."); + + + } +} -- Gitee From 462ed238df0c59d53fbacb7aaacab45f61a47249 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 23 Mar 2020 10:04:32 +0800 Subject: [PATCH 0128/1965] v3.1.0 --- src/main/java/io/jboot/components/rpc/Utils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/rpc/Utils.java b/src/main/java/io/jboot/components/rpc/Utils.java index 4532098c..10ceff3f 100644 --- a/src/main/java/io/jboot/components/rpc/Utils.java +++ b/src/main/java/io/jboot/components/rpc/Utils.java @@ -133,7 +133,7 @@ public class Utils { ? "setRegistries" : "set" + StrUtil.firstCharToUpperCase(arrName) + "s"; - Method method = entry.getValue().getClass().getDeclaredMethod(setterMethodName, List.class); + Method method = entry.getValue().getClass().getMethod(setterMethodName, List.class); method.invoke(entry.getValue(), argCfgList); } catch (Exception e) { -- Gitee From 55bb08181ed3429d6dfeffc1a519f2f8774024e3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 23 Mar 2020 10:14:54 +0800 Subject: [PATCH 0129/1965] v3.1.0 --- src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index 28d055b4..b512700f 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -193,7 +193,7 @@ class DubboUtil { Utils.copyFields(sc, serviceConfig); //service provider - if (StrUtil.isNotBlank(sc.getProtocol())) { + if (StrUtil.isNotBlank(sc.getProvider())) { serviceConfig.setProviderIds(sc.getProvider()); } -- Gitee From d1be1b041a4f3aadfd15d4fe65615dace7b26678 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 23 Mar 2020 10:16:11 +0800 Subject: [PATCH 0130/1965] add dubbo-mutil-registry demos --- .../multiregistry/DubboServerMultiDemo.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/test/java/io/jboot/test/rpc/multiregistry/DubboServerMultiDemo.java b/src/test/java/io/jboot/test/rpc/multiregistry/DubboServerMultiDemo.java index 4ccd39d3..c6f36867 100644 --- a/src/test/java/io/jboot/test/rpc/multiregistry/DubboServerMultiDemo.java +++ b/src/test/java/io/jboot/test/rpc/multiregistry/DubboServerMultiDemo.java @@ -18,7 +18,8 @@ public class DubboServerMultiDemo { - //两个注册中心,id 分别为 default、zk,默认情况下,如果 service 没指定注册中心,service 会向所有的注册中心发布自己的服务 + // 3个注册中心,id 分别为 default、zk、zk2 + // 默认情况下,如果 service 没指定注册中心,service 会向所有的注册中心发布自己的服务 JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "nacos"); JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:8848"); @@ -29,6 +30,23 @@ public class DubboServerMultiDemo { JbootApplication.setBootArg("jboot.rpc.dubbo.registry.zk2.address", "127.0.0.1:2181"); + //定义一个名称为 pro3 的 provider,只注册到名称为 default 的注册中心 +// JbootApplication.setBootArg("jboot.rpc.dubbo.provider.pro3.registry", "default"); + + //定义一个名称为 pro3 的 provider,只注册到名称为 zk 的注册中心 + JbootApplication.setBootArg("jboot.rpc.dubbo.provider.pro3.registry", "zk"); + + + + // 让服务 BlogService 使用名称为 pro3 的 provider + // 当然,此项配置也可以在 @RPCInject 里进行配置 + JbootApplication.setBootArg("jboot.rpc.providers", "io.jboot.test.rpc.commons.BlogService:pro3"); + + + + + + //dubbo 的通信协议配置,name 可以不用配置,默认值为 dubbo -- Gitee From 49e448b7565a5d189685ff3da4d147e4da05abd0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 23 Mar 2020 10:58:40 +0800 Subject: [PATCH 0131/1965] v3.1.0 release (^.^)YYa!! --- changes.txt | 1 + src/main/java/io/jboot/JbootConsts.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 7e4aac30..581b7003 100644 --- a/changes.txt +++ b/changes.txt @@ -10,6 +10,7 @@ jboot v3.1.0: 修复:修复 JbootHttpImpl 无法上传文件的bug 修复:修复 JbootHttpImpl 无法正确获取 gzip 压缩内容的问题 优化:重构 RPC 的模块,使之更加简单清晰 +优化:升级 fastsjon 等相关依赖到最新版本,修改某些错误单词拼写的方法名等 文档:完善 AOP 的相关文档 文档:完善 Sentinel 限流的文档 文档:完善 RPC 的相关文档 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index ca30c618..86844384 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.0.5"; + public static String VERSION = "3.1.0"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From a8979aefea823b93dcb002f21f43899d5dcd75a5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 23 Mar 2020 10:59:27 +0800 Subject: [PATCH 0132/1965] v3.1.0 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 85da0d73..4d6ac27c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.0.5 + 3.1.0 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 784d2e41..e115a0cd 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.0.5 + 3.1.0 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 6e92dcde..b16eb1eb 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.0.5 + 3.1.0 ``` diff --git a/pom.xml b/pom.xml index 1a21c283..afc0396f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.0-SNAPSHOT + 3.1.0 jar jboot -- Gitee From 513a13f4b8909f20a50f489e8f61561c132c69a9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 23 Mar 2020 13:46:03 +0800 Subject: [PATCH 0133/1965] update docs --- doc/docs/gateway.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/docs/gateway.md b/doc/docs/gateway.md index a46c795b..56e1a44a 100644 --- a/doc/docs/gateway.md +++ b/doc/docs/gateway.md @@ -11,7 +11,7 @@ ## 概述 -Jboot 已经内置基础了网关,网关功能目前暂时只能通过在 jboot.properties 文件进行配置。 +Jboot 已经内置基础的网关,网关功能目前暂时只能通过在 jboot.properties 文件进行配置。 如下是一个正常的 gateway 配置。 @@ -42,12 +42,12 @@ jboot.gateway.queryContains = aa,bb ``` - name 设置路由的名称 -- uri 网路路由目标网址 +- uri 设置路由目标网址 - enable 是否启用该路由 - sentinelEnable 是否启用 sentinel 限流功能 - sentinelBlockPage 若该路由被限流后,网页自动跳转到哪个网址 - proxyReadTimeout 发生路由后,默认的请求超时时间,默认为 10 秒 -- proxyConnectTimeout 发生路由后,默认的链接超时时间,默认为 5 秒 +- proxyConnectTimeout 发生路由后,默认的连接超时时间,默认为 5 秒 - proxyContentType 发生路由后,返回给浏览器的 http-content-type,默认为:text/html;charset=utf-8 ## Path 路由 @@ -98,7 +98,7 @@ jboot.gateway.name = name jboot.gateway.uri = http://youdomain:8080 jboot.gateway.enable = true -jboot.gateway.pathStartsWith = /user +jboot.gateway.pathEndswith = /user ``` 当 path 中,只要以 `/user` 结束就会匹配到该路由,比如 `www.xxx.com/other/user` ,但是 `www.xxx.com/user/other` 不会匹配到,因为它是以 `/other` 结束的。 -- Gitee From 31df080cebedf585dada6b63c3365979d3613c51 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 23 Mar 2020 14:08:42 +0800 Subject: [PATCH 0134/1965] update docs --- doc/docs/gateway.md | 8 +++--- .../gateway/JbootGatewayConfig.java | 28 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/doc/docs/gateway.md b/doc/docs/gateway.md index 56e1a44a..e983854d 100644 --- a/doc/docs/gateway.md +++ b/doc/docs/gateway.md @@ -91,14 +91,14 @@ jboot.gateway.pathStartsWith = /user 当 path 中,只要以 `/user` 开头就会匹配到该路由,比如 `www.xxx.com/user/other` ,但是 `www.xxx.com/other/user/xxx` 不会匹配到,因为它是以 `/other` 开头的。 -**4、pathEndswith** +**4、pathEndsWith** ``` jboot.gateway.name = name jboot.gateway.uri = http://youdomain:8080 jboot.gateway.enable = true -jboot.gateway.pathEndswith = /user +jboot.gateway.pathEndsWith = /user ``` 当 path 中,只要以 `/user` 结束就会匹配到该路由,比如 `www.xxx.com/other/user` ,但是 `www.xxx.com/user/other` 不会匹配到,因为它是以 `/other` 结束的。 @@ -145,14 +145,14 @@ jboot.gateway.hostStartsWith = xxx 在 Host 中,只要以 `xxx` 开头就会匹配到该路由,比如 `xxx.xxx.com/user/other` ,但是 `www.xxx.com/other/user/xxx` 不会匹配到,因为它的域名(host)是以 `xxx` 开头的。 -**4、hostEndswith** +**4、hostEndsWith** ``` jboot.gateway.name = name jboot.gateway.uri = http://youdomain:8080 jboot.gateway.enable = true -jboot.gateway.hostEndswith = com +jboot.gateway.hostEndsWith = com ``` 在 Host 中,只要以 `com` 结束就会匹配到该路由,比如 `www.xxx.com/other/user` ,但是 `www.xxx.org/user/other` 不会匹配到,因为它的域名是以 `org` 结束的。 diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index 0d4692b6..cfc67116 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -48,13 +48,13 @@ public class JbootGatewayConfig implements Serializable { private String[] pathEquals; private String[] pathContains; private String[] pathStartsWith; - private String[] pathEndswith; + private String[] pathEndsWith; private String[] hostEquals; private String[] hostContains; private String[] hostStartsWith; - private String[] hostEndswith; + private String[] hostEndsWith; private Map queryEquals; @@ -153,12 +153,12 @@ public class JbootGatewayConfig implements Serializable { this.pathStartsWith = pathStartsWith; } - public String[] getPathEndswith() { - return pathEndswith; + public String[] getPathEndsWith() { + return pathEndsWith; } - public void setPathEndswith(String[] pathEndswith) { - this.pathEndswith = pathEndswith; + public void setPathEndsWith(String[] pathEndsWith) { + this.pathEndsWith = pathEndsWith; } public String[] getHostEquals() { @@ -185,12 +185,12 @@ public class JbootGatewayConfig implements Serializable { this.hostStartsWith = hostStartsWith; } - public String[] getHostEndswith() { - return hostEndswith; + public String[] getHostEndsWith() { + return hostEndsWith; } - public void setHostEndswith(String[] hostEndswith) { - this.hostEndswith = hostEndswith; + public void setHostEndsWith(String[] hostEndsWith) { + this.hostEndsWith = hostEndsWith; } public Map getQueryEquals() { @@ -244,8 +244,8 @@ public class JbootGatewayConfig implements Serializable { } } - if (pathEndswith != null) { - for (String p : pathEndswith) { + if (pathEndsWith != null) { + for (String p : pathEndsWith) { if (path.endsWith(p)) { return true; } @@ -277,8 +277,8 @@ public class JbootGatewayConfig implements Serializable { } } - if (hostEndswith != null) { - for (String h : hostStartsWith) { + if (hostEndsWith != null) { + for (String h : hostEndsWith) { if (host.endsWith(h)) { return true; } -- Gitee From 54bcf53ffb9b12ad7f464538de17048dcfb7397e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 11:18:01 +0800 Subject: [PATCH 0135/1965] support: add custom Interceptor for Gateway --- .../gateway/GatewayInterceptor.java | 24 ++++ .../components/gateway/GatewayInvocation.java | 110 ++++++++++++++++++ .../jboot/components/gateway/GatewayUtil.java | 12 ++ .../gateway/JbootGatewayConfig.java | 36 ++++++ .../gateway/JbootGatewayHandler.java | 23 +--- 5 files changed, 183 insertions(+), 22 deletions(-) create mode 100644 src/main/java/io/jboot/components/gateway/GatewayInterceptor.java create mode 100644 src/main/java/io/jboot/components/gateway/GatewayInvocation.java diff --git a/src/main/java/io/jboot/components/gateway/GatewayInterceptor.java b/src/main/java/io/jboot/components/gateway/GatewayInterceptor.java new file mode 100644 index 00000000..0cf758ab --- /dev/null +++ b/src/main/java/io/jboot/components/gateway/GatewayInterceptor.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.gateway; + +/** + * GatewayInterceptor. + */ +public interface GatewayInterceptor { + void intercept(GatewayInvocation inv); +} + diff --git a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java new file mode 100644 index 00000000..8589527f --- /dev/null +++ b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.gateway; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/24 + */ +public class GatewayInvocation { + + private JbootGatewayConfig config; + private GatewayInterceptor[] inters; + private HttpServletRequest request; + private HttpServletResponse response; + + private int index = 0; + + + public GatewayInvocation(JbootGatewayConfig config, HttpServletRequest request, HttpServletResponse response) { + this.config = config; + this.request = request; + this.response = response; + this.inters = config.getInters(); + } + + public void invoke() { + if (inters == null || inters.length == 0) { + doInvoke(); + return; + } + if (index < inters.length) { + inters[index++].intercept(this); + } else if (index++ >= inters.length) { + try { + doInvoke(); + } catch (Exception e) { + throw e; + } + } + } + + protected void doInvoke() { + Runnable runnable = () -> { + new GatewayHttpProxy(config) + .sendRequest(GatewayUtil.buildProxyUrl(config, request), request, response); + }; + if (config.isSentinelEnable()) { + new GatewaySentinelProcesser().process(runnable, config, request, response); + } else { + runnable.run(); + } + } + + + public JbootGatewayConfig getConfig() { + return config; + } + + public void setConfig(JbootGatewayConfig config) { + this.config = config; + } + + public GatewayInterceptor[] getInters() { + return inters; + } + + public void setInters(GatewayInterceptor[] inters) { + this.inters = inters; + } + + public HttpServletRequest getRequest() { + return request; + } + + public void setRequest(HttpServletRequest request) { + this.request = request; + } + + public HttpServletResponse getResponse() { + return response; + } + + public void setResponse(HttpServletResponse response) { + this.response = response; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } +} diff --git a/src/main/java/io/jboot/components/gateway/GatewayUtil.java b/src/main/java/io/jboot/components/gateway/GatewayUtil.java index d78abdc4..55665e94 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayUtil.java +++ b/src/main/java/io/jboot/components/gateway/GatewayUtil.java @@ -16,6 +16,7 @@ package io.jboot.components.gateway; import com.alibaba.csp.sentinel.util.StringUtil; +import io.jboot.utils.StrUtil; import javax.servlet.http.HttpServletRequest; @@ -27,6 +28,17 @@ public class GatewayUtil { private static final String PATH_SPLIT = "/"; + public static String buildProxyUrl(JbootGatewayConfig config,HttpServletRequest request){ + StringBuilder url = new StringBuilder(config.getUri()); + if (StrUtil.isNotBlank(request.getRequestURI())) { + url.append(request.getRequestURI()); + } + if (StrUtil.isNotBlank(request.getQueryString())) { + url.append("?").append(request.getQueryString()); + } + return url.toString(); + } + public static String buildResource(HttpServletRequest request) { String pathInfo = getResourcePath(request); diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index cfc67116..cdd7f4f8 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -15,6 +15,7 @@ */ package io.jboot.components.gateway; +import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; import javax.servlet.http.HttpServletRequest; @@ -60,6 +61,9 @@ public class JbootGatewayConfig implements Serializable { private Map queryEquals; private String[] queryContains; + //拦截器配置,一般可以用于对请求进行 鉴权 等处理 + private String[] interceptors; + // 暂时不支持 cookie // private Map cookieEquals; // private String[] cookieContains; @@ -209,6 +213,38 @@ public class JbootGatewayConfig implements Serializable { this.queryContains = queryContains; } + public String[] getInterceptors() { + return interceptors; + } + + public void setInterceptors(String[] interceptors) { + this.interceptors = interceptors; + } + + + private GatewayInterceptor[] inters; + public GatewayInterceptor[] getInters() { + if (interceptors == null || interceptors.length == 0) { + return null; + } + if (inters == null) { + synchronized (this) { + if (inters != null) { + inters = new GatewayInterceptor[interceptors.length]; + for (int i = 0; i < interceptors.length; i++) { + GatewayInterceptor interceptor = ClassUtil.newInstance(interceptors[i]); + if (interceptor == null) { + throw new NullPointerException("can not new instance by class:" + interceptors[i]); + } + inters[i] = interceptor; + } + } + } + } + return inters; + } + + public boolean isConfigOk() { return StrUtil.isNotBlank(uri); } diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java index c23c6b11..59541486 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java @@ -16,7 +16,6 @@ package io.jboot.components.gateway; import com.jfinal.handler.Handler; -import io.jboot.utils.StrUtil; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -32,30 +31,10 @@ public class JbootGatewayHandler extends Handler { public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { JbootGatewayConfig config = JbootGatewayManager.me().matchingConfig(request); if (config != null) { - doProcessGateway(config, request, response); + new GatewayInvocation(config,request,response).invoke(); isHandled[0] = true; } else { next.handle(target, request, response, isHandled); } } - - private void doProcessGateway(JbootGatewayConfig config, HttpServletRequest request, HttpServletResponse response) { - Runnable runnable = () -> { - StringBuilder url = new StringBuilder(config.getUri()); - if (StrUtil.isNotBlank(request.getRequestURI())) { - url.append(request.getRequestURI()); - } - if (StrUtil.isNotBlank(request.getQueryString())) { - url.append("?").append(request.getQueryString()); - } - - new GatewayHttpProxy(config).sendRequest(url.toString(), request, response); - }; - - if (config.isSentinelEnable()) { - new GatewaySentinelProcesser().process(runnable, config, request, response); - } else { - runnable.run(); - } - } } -- Gitee From 08ea5f7cf380c0213b1b361c18bf989edade7a42 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 11:25:42 +0800 Subject: [PATCH 0136/1965] support: add custom Interceptor for Gateway --- .../java/io/jboot/components/gateway/JbootGatewayConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index cdd7f4f8..0fe912be 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -229,7 +229,7 @@ public class JbootGatewayConfig implements Serializable { } if (inters == null) { synchronized (this) { - if (inters != null) { + if (inters == null) { inters = new GatewayInterceptor[interceptors.length]; for (int i = 0; i < interceptors.length; i++) { GatewayInterceptor interceptor = ClassUtil.newInstance(interceptors[i]); -- Gitee From 6cd238abc40a729959007ae0dcec01106c16e7f4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 13:29:10 +0800 Subject: [PATCH 0137/1965] add ${para} config in jboot.properties --- .../java/io/jboot/app/config/ConfigPart.java | 81 +++++++++++++++++++ .../app/config/JbootConfigChangeListener.java | 15 ++++ .../jboot/app/config/JbootConfigManager.java | 18 ++++- src/main/java/io/jboot/app/config/Utils.java | 29 +++++++ src/test/java/io/jboot/test/JavaTester.java | 1 + src/test/resources/jboot.properties | 2 +- 6 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/jboot/app/config/ConfigPart.java diff --git a/src/main/java/io/jboot/app/config/ConfigPart.java b/src/main/java/io/jboot/app/config/ConfigPart.java new file mode 100644 index 00000000..3bfbfb65 --- /dev/null +++ b/src/main/java/io/jboot/app/config/ConfigPart.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.app.config; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/24 + */ +public class ConfigPart { + + private int start = 0; + private int end = 0; + private int keyValueIndexOf = 0; + private StringBuilder sb = null; + + public String getKey(){ + if (sb == null){ + return null; + } + return keyValueIndexOf > 0 ? sb.substring(0,keyValueIndexOf - 1) : sb.toString(); + } + + public String getDefaultValue(){ + if (sb == null){ + return ""; + } + return keyValueIndexOf > 0 ? sb.substring(keyValueIndexOf).trim(): ""; + } + + public boolean isOk(){ + return start > end && sb != null; + } + + public void append(char c){ + if(sb == null){ + sb = new StringBuilder(); + } + sb.append(c); + } + + public int getStart() { + return start; + } + + public void setStart(int start) { + this.start = start; + } + + public int getEnd() { + return end; + } + + public void setEnd(int end) { + this.end = end; + } + + public int getKeyValueIndexOf() { + return keyValueIndexOf; + } + + public void setKeyValueIndexOf(int keyValueIndexOf) { + this.keyValueIndexOf = keyValueIndexOf; + } + + public String getPartString(){ + return sb == null ? "${}" : "${" + sb.toString() +"}"; + } +} diff --git a/src/main/java/io/jboot/app/config/JbootConfigChangeListener.java b/src/main/java/io/jboot/app/config/JbootConfigChangeListener.java index 68a77d97..6ee403a4 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigChangeListener.java +++ b/src/main/java/io/jboot/app/config/JbootConfigChangeListener.java @@ -1,3 +1,18 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.jboot.app.config; /** diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 2100fcbf..7213d784 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -17,6 +17,7 @@ package io.jboot.app.config; import com.jfinal.kit.LogKit; import io.jboot.app.config.annotation.ConfigModel; +import io.jboot.utils.StrUtil; import java.io.File; import java.lang.reflect.Method; @@ -250,8 +251,23 @@ public class JbootConfigManager { public String getConfigValue(Properties properties, String key) { + if (StrUtil.isBlank(key)){ + return ""; + } String originalValue = getOriginalConfigValue(properties, key); - return decryptor != null ? decryptor.decrypt(key, originalValue) : originalValue; + String stringValue = decryptor != null ? decryptor.decrypt(key, originalValue) : originalValue; + + List configParts = Utils.parseParts(stringValue); + if (configParts == null || configParts.isEmpty()){ + return stringValue; + } + + for (ConfigPart cp : configParts){ + String value = getConfigValue(properties,cp.getKey()); + value = StrUtil.isNotBlank(value) ? value : cp.getDefaultValue(); + stringValue = stringValue.replace(cp.getPartString(),value); + } + return stringValue; } diff --git a/src/main/java/io/jboot/app/config/Utils.java b/src/main/java/io/jboot/app/config/Utils.java index 06e4f6f4..e4ed6f1e 100644 --- a/src/main/java/io/jboot/app/config/Utils.java +++ b/src/main/java/io/jboot/app/config/Utils.java @@ -16,6 +16,8 @@ package io.jboot.app.config; +import io.jboot.utils.StrUtil; + import java.io.File; import java.io.UnsupportedEncodingException; import java.lang.reflect.*; @@ -44,6 +46,33 @@ class Utils { return null; } + public static List parseParts(String string){ + if (StrUtil.isBlank(string)){ + return null; + } + List configParts = new LinkedList<>(); + char[] chars = string.toCharArray(); + ConfigPart part = null; + int index = 0; + for (char c : chars){ + if (c == '{' && chars[index-1] == '$' && part == null){ + part = new ConfigPart(); + part.setStart(index); + }else if ( c == '}' && part != null){ + part.setEnd(index); + configParts.add(part); + part = null; + }else if (part != null){ + part.append(c); + if (c ==':' && part.getKeyValueIndexOf() == 0){ + part.setKeyValueIndexOf(index - part.getStart()); + } + } + index ++; + } + return configParts; + } + public static List getClassSetMethods(Class clazz) { List setMethods = new ArrayList<>(); diff --git a/src/test/java/io/jboot/test/JavaTester.java b/src/test/java/io/jboot/test/JavaTester.java index 2e5f5ee7..7a8fe241 100644 --- a/src/test/java/io/jboot/test/JavaTester.java +++ b/src/test/java/io/jboot/test/JavaTester.java @@ -8,4 +8,5 @@ public class JavaTester { } + } diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index 741ebcc5..e6e439f5 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -29,7 +29,7 @@ jboot.config.apollo.meta = http://106.54.227.205:8080 config.test.test.name = name1 config.test.test.type = type1 -config.test.test.bbb.name = name2 +config.test.test.bbb.name = name2${config.test.test.name}--cc config.test.test.bbb.type = type2 config.test.test.ccc.name = name3 -- Gitee From 3ea6bb77d3a2a06c8e9e820b91b4ce0558d3ee77 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 13:51:35 +0800 Subject: [PATCH 0138/1965] optimize Undertow Config --- src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java b/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java index 99e5f709..a2eb6f37 100644 --- a/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java +++ b/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java @@ -57,7 +57,7 @@ public class JbootUndertowConfig extends UndertowConfig { if (port == null || port.trim().length() == 0) { propExt.getProperties().put(UNDERTOW_PORT, "8080"); JbootConfigManager.me().setBootArg(UNDERTOW_PORT, "8080"); - } else if (port.trim().equals("*") && availablePort != null) { + } else if ((port.trim().equals("*") || port.trim().equals("-1")) && availablePort != null) { propExt.getProperties().put(UNDERTOW_PORT, availablePort.toString()); JbootConfigManager.me().setBootArg(UNDERTOW_PORT, availablePort.toString()); } -- Gitee From fd28ad3efd605f39e621928be2d37585b9175e10 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 15:55:27 +0800 Subject: [PATCH 0139/1965] add JbootRpcApplication for start RPC services only --- .../io/jboot/app/JbootRpcApplication.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/main/java/io/jboot/app/JbootRpcApplication.java diff --git a/src/main/java/io/jboot/app/JbootRpcApplication.java b/src/main/java/io/jboot/app/JbootRpcApplication.java new file mode 100644 index 00000000..60dacc38 --- /dev/null +++ b/src/main/java/io/jboot/app/JbootRpcApplication.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.app; + +import io.jboot.app.config.JbootConfigManager; +import io.jboot.core.JbootCoreConfig; + +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/24 + */ +public class JbootRpcApplication { + + private static final ReentrantLock LOCK = new ReentrantLock(); + private static final Condition STOP = LOCK.newCondition(); + + public static void main(String[] args) { + run(args); + } + + public static void setBootArg(String key, Object value) { + JbootConfigManager.me().setBootArg(key, value); + } + + public static void run(String[] args) { + JbootConfigManager.me().parseArgs(args); + JbootCoreConfig coreConfig = new JbootCoreConfig(); + new RPCServer(coreConfig).start(); + } + + + + static class RPCServer extends Thread { + private JbootCoreConfig coreConfig; + + public RPCServer(JbootCoreConfig coreConfig) { + this.coreConfig = coreConfig; + doInit(); + } + + private void doInit(){ + this.coreConfig.onStart(); + } + + @Override + public void run() { + addHook(coreConfig); + await(); + } + + private void await() { + try { + LOCK.lock(); + STOP.await(); + } catch (InterruptedException e) { + System.err.println("jboot rpc application has stopped, interrupted by other thread!"); + } finally { + LOCK.unlock(); + } + } + + private void addHook(JbootCoreConfig config) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + config.onStop(); + } catch (Exception e) { + System.err.println("jboot rpc stop exception : " + e.toString()); + } + + System.err.println("jboot rpc application exit, all service stopped."); + try { + LOCK.lock(); + STOP.signal(); + } finally { + LOCK.unlock(); + } + }, "jboot-rpc-application-hook")); + } + } + +} -- Gitee From 3b8f9ef04bb19c302919d45476d648cc7a363e81 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 16:07:50 +0800 Subject: [PATCH 0140/1965] fixed : can not inject rpc service in Interceptor --- .../io/jboot/components/rpc/Jbootrpc.java | 4 +- .../io/jboot/components/rpc/JbootrpcBase.java | 41 +++++++++++++------ .../jboot/components/rpc/JbootrpcManager.java | 4 +- .../components/rpc/dubbo/JbootDubborpc.java | 3 +- .../components/rpc/motan/JbootMotanrpc.java | 3 +- 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/Jbootrpc.java b/src/main/java/io/jboot/components/rpc/Jbootrpc.java index 173135ac..c97f6b45 100644 --- a/src/main/java/io/jboot/components/rpc/Jbootrpc.java +++ b/src/main/java/io/jboot/components/rpc/Jbootrpc.java @@ -22,9 +22,7 @@ public interface Jbootrpc { public boolean serviceExport(Class interfaceClass, Object object, JbootrpcServiceConfig serviceConfig); - public void onInit(); - - public void onInited(); + public void onStart(); public void onStop(); } diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java index 84a5e330..b84d7c3b 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java @@ -25,18 +25,21 @@ public abstract class JbootrpcBase implements Jbootrpc { protected static final Map objectCache = new ConcurrentHashMap<>(); protected static JbootrpcConfig rpcConfig = Jboot.config(JbootrpcConfig.class); + private boolean started = false; @Override public T serviceObtain(Class interfaceClass, JbootrpcReferenceConfig config) { - String key = buildCacheKey(interfaceClass,config); + + String key = buildCacheKey(interfaceClass, config); T object = (T) objectCache.get(key); if (object == null) { - synchronized (this){ + synchronized (this) { if (objectCache.get(key) == null) { - object = onServiceCreate(interfaceClass,config); - if (object != null){ - objectCache.put(key,object); + startIfNecessary(); + object = onServiceCreate(interfaceClass, config); + if (object != null) { + objectCache.put(key, object); } } } @@ -44,29 +47,41 @@ public abstract class JbootrpcBase implements Jbootrpc { return object; } - public abstract T onServiceCreate(Class serviceClass, JbootrpcReferenceConfig config); - - @Override - public void onInit() { - + protected void startIfNecessary() { + if (!started) { + synchronized (this) { + if (!started) { + onStart(); + } + } + } } - @Override - public void onInited() { + public abstract T onServiceCreate(Class serviceClass, JbootrpcReferenceConfig config); + @Override + public void onStart() { + setStarted(true); } + @Override public void onStop() { } - protected String buildCacheKey(Class interfaceClass, JbootrpcReferenceConfig config){ + protected String buildCacheKey(Class interfaceClass, JbootrpcReferenceConfig config) { StringBuilder sb = new StringBuilder(interfaceClass.getName()); return sb.append(":").append(config.getGroup()) .append(":").append(config.getVersion()) .toString(); } + public boolean isStarted() { + return started; + } + public void setStarted(boolean started) { + this.started = started; + } } diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java index e4dd95f4..f73b4a3e 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java @@ -69,13 +69,11 @@ public class JbootrpcManager { } Jbootrpc jbootrpc = getJbootrpc(); - jbootrpc.onInit(); + jbootrpc.onStart(); if (defaultConfig.isAutoExportEnable()) { exportRPCBean(jbootrpc); } - - jbootrpc.onInited(); } public void stop(){ diff --git a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java index d49f7d7f..169e2bec 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java @@ -25,8 +25,9 @@ import org.apache.dubbo.config.ServiceConfig; public class JbootDubborpc extends JbootrpcBase { @Override - public void onInit() { + public void onStart() { DubboUtil.initDubbo(); + setStarted(true); } @Override diff --git a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java index 2605c63d..0a7639fc 100644 --- a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java +++ b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java @@ -28,8 +28,9 @@ import io.jboot.utils.StrUtil; public class JbootMotanrpc extends JbootrpcBase { @Override - public void onInit() { + public void onStart() { MotanUtil.initMotan(); + setStarted(true); } @Override -- Gitee From 12f501b84619b5692810a4b8e0373e8c425b4158 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 16:08:19 +0800 Subject: [PATCH 0141/1965] add interceptor rpc inject demo --- .../io/jboot/test/rpc/dubbo/DubboClient.java | 2 + .../test/rpc/dubbo/DubboInterceptor.java | 39 +++++++++++++++++++ .../io/jboot/test/rpc/dubbo/DubboServer.java | 5 +-- 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java index cd21324f..55118b20 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java @@ -1,6 +1,7 @@ package io.jboot.test.rpc.dubbo; +import com.jfinal.aop.Before; import io.jboot.app.JbootApplication; import io.jboot.components.rpc.annotation.RPCInject; import io.jboot.test.rpc.commons.BlogService; @@ -30,6 +31,7 @@ public class DubboClient extends JbootController { @RPCInject private BlogService blogService; + @Before(DubboInterceptor.class) public void index() { System.out.println("blogService:" + blogService); diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java new file mode 100644 index 00000000..9394668d --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

+ * 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 io.jboot.test.rpc.dubbo; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import io.jboot.components.rpc.annotation.RPCInject; +import io.jboot.test.rpc.commons.BlogService; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/24 + */ +public class DubboInterceptor implements Interceptor { + + @RPCInject + private BlogService blogService; + + @Override + public void intercept(Invocation inv) { + + System.out.println("intercept : " + blogService); + + inv.invoke(); + } +} diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java index 6e2f1476..89db7a4a 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java @@ -2,12 +2,12 @@ package io.jboot.test.rpc.dubbo; import io.jboot.app.JbootApplication; +import io.jboot.app.JbootRpcApplication; public class DubboServer { public static void main(String[] args) throws InterruptedException { - JbootApplication.setBootArg("undertow.port", "9998"); JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); @@ -20,8 +20,7 @@ public class DubboServer { JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "28080"); - - JbootApplication.run(args); + JbootRpcApplication.run(args); System.out.println("DubboServer started..."); -- Gitee From 75cf4fcd340a8419d126c7acec82f570137998df Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 16:13:00 +0800 Subject: [PATCH 0142/1965] v3.1.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index afc0396f..1f78aef5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.0 + 3.1.1-SNAPSHOT jar jboot -- Gitee From 852f71fa4022631ae48417ca5ca49f458fe91244 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 16:25:18 +0800 Subject: [PATCH 0143/1965] v3.1.1 --- .../java/io/jboot/test/rpc/dubbo/DubboClient.java | 15 +++++++++++++++ .../io/jboot/test/rpc/dubbo/DubboInterceptor.java | 6 +++--- .../java/io/jboot/test/rpc/dubbo/DubboServer.java | 15 +++++++++++++++ .../rpc/dubbonacos/DubboServer1NacosDemo.java | 2 -- .../rpc/dubbonacos/DubboServer2NacosDemo.java | 2 -- 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java index 55118b20..e546fdf4 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java @@ -1,3 +1,18 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.jboot.test.rpc.dubbo; diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java index 9394668d..3c2dae50 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java index 89db7a4a..2487fc6d 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java @@ -1,3 +1,18 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.jboot.test.rpc.dubbo; diff --git a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java index 95ed6c2e..276924c0 100644 --- a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java @@ -17,8 +17,6 @@ public class DubboServer1NacosDemo { - - // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "nacos"); //注册中心地址,即zookeeper的地址 diff --git a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java index 09900979..0f82bab7 100644 --- a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java @@ -17,8 +17,6 @@ public class DubboServer2NacosDemo { - - // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "nacos"); //注册中心地址,即zookeeper的地址 -- Gitee From 29019b132ca7caeed214728d98679ef1d0c7b3c2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 16:34:53 +0800 Subject: [PATCH 0144/1965] update dosc --- doc/docs/gateway.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/docs/gateway.md b/doc/docs/gateway.md index e983854d..d5192ec0 100644 --- a/doc/docs/gateway.md +++ b/doc/docs/gateway.md @@ -27,6 +27,8 @@ jboot.gateway.proxyReadTimeout = 10000 jboot.gateway.proxyConnectTimeout = 5000 jboot.gateway.proxyContentType = text/html;charset=utf-8 +jboot.gateway.interceptors = com.xxx.Interceptor1,com.xxx.Interceptor2 + jboot.gateway.pathEquals = /path jboot.gateway.pathContains = /path jboot.gateway.pathStartsWith = /path @@ -49,6 +51,7 @@ jboot.gateway.queryContains = aa,bb - proxyReadTimeout 发生路由后,默认的请求超时时间,默认为 10 秒 - proxyConnectTimeout 发生路由后,默认的连接超时时间,默认为 5 秒 - proxyContentType 发生路由后,返回给浏览器的 http-content-type,默认为:text/html;charset=utf-8 +- interceptors 网关拦截器,一般用于进行鉴权等功能,配置类名,多个拦截器用英文逗号隔开,拦截器必须实现 GatewayInterceptor 接口 ## Path 路由 -- Gitee From e7102c5898b40d745cac2c58aaf19c3f777a8d9f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 18:09:05 +0800 Subject: [PATCH 0145/1965] fixed : can not copy fields for ReferenceConfig --- src/main/java/io/jboot/components/rpc/Utils.java | 13 ++++++++----- .../io/jboot/test/rpc/dubbo/DubboInterceptor.java | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/Utils.java b/src/main/java/io/jboot/components/rpc/Utils.java index 10ceff3f..f2d061f4 100644 --- a/src/main/java/io/jboot/components/rpc/Utils.java +++ b/src/main/java/io/jboot/components/rpc/Utils.java @@ -36,10 +36,11 @@ public class Utils { /** * 根据注解来设置对象内容,参考 dubbo 下的 AbstractConfig - * @see org.apache.dubbo.config.AbstractConfig#appendAnnotation + * * @param annotationClass * @param annotation * @param appendTo + * @see org.apache.dubbo.config.AbstractConfig#appendAnnotation */ public static void appendAnnotation(Class annotationClass, Object annotation, Object appendTo) { Method[] methods = annotationClass.getMethods(); @@ -90,7 +91,8 @@ public class Utils { Field[] fields = copyFrom.getClass().getDeclaredFields(); for (Field field : fields) { try { - Method method = copyTo.getClass().getDeclaredMethod("set" + StrUtil.firstCharToUpperCase(field.getName()), field.getType()); + String setterName = "set" + StrUtil.firstCharToUpperCase(field.getName()); + Method method = copyTo.getClass().getMethod(setterName, getBoxedClass(field.getType())); method.invoke(copyTo, field.get(copyFrom)); } catch (Exception e) { // ignore @@ -101,10 +103,11 @@ public class Utils { /** * 设置子节点配置,比如 ProviderConfig 下的 MethodsConfig ,或者 MethodConfig 下的 ArgumentConfig 等 - * @param appendTo 要设置的对象 + * + * @param appendTo 要设置的对象 * @param dataSource 设置子节点的数据源 - * @param prefix 要设置对象的配置前缀(jboot.properties 下的配置) - * @param arrName 要设置对象的属性名 + * @param prefix 要设置对象的配置前缀(jboot.properties 下的配置) + * @param arrName 要设置对象的属性名 * @param * @param */ diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java index 3c2dae50..6c84c73f 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java @@ -26,7 +26,7 @@ import io.jboot.test.rpc.commons.BlogService; */ public class DubboInterceptor implements Interceptor { - @RPCInject + @RPCInject(check = false) private BlogService blogService; @Override -- Gitee From 661dca62649210f0150aef49179d19968417c6fe Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 19:06:17 +0800 Subject: [PATCH 0146/1965] fixed dubbo export default value is error --- .../jboot/components/rpc/JbootrpcManager.java | 6 ++- .../rpc/JbootrpcReferenceConfig.java | 37 +++++++++--------- .../components/rpc/JbootrpcServiceConfig.java | 39 +++++++++---------- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java index f73b4a3e..ed0d95fa 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java @@ -111,7 +111,11 @@ public class JbootrpcManager { continue; } - jbootrpc.serviceExport(inter, Aop.get(clazz), new JbootrpcServiceConfig(rpcBean)); + if (jbootrpc.serviceExport(inter, Aop.get(clazz), new JbootrpcServiceConfig(rpcBean))){ + if (Jboot.isDevMode()){ + System.err.println("rpc service[" + inter+"] has exported ok!"); + } + } } } } diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java index aee88484..8a927522 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java @@ -29,29 +29,29 @@ public class JbootrpcReferenceConfig implements Serializable { /** * Service version, default value is empty string */ - String version; + private String version; /** * Service group, default value is empty string */ - String group; + private String group; /** * Service target URL for direct invocation, if this is specified, then registry center takes no effect. */ - String url; + private String url; /** * Whether to enable generic invocation, default value is false */ - boolean generic; + private boolean generic = false; /** * Check if service provider is available during boot up, default value is true */ - boolean check; + private boolean check = true; /** @@ -59,7 +59,7 @@ public class JbootrpcReferenceConfig implements Serializable { *

* see Constants#DEFAULT_RETRIES */ - int retries; + private int retries = 2; /** @@ -67,67 +67,66 @@ public class JbootrpcReferenceConfig implements Serializable { *

* see Constants#DEFAULT_LOADBALANCE */ - String loadbalance; + private String loadbalance = "random"; /** * Whether to enable async invocation, default value is false */ - boolean async; + private boolean async = false; /** * Maximum active requests allowed, default value is 0 */ - int actives; + private int actives = 0; /** * Timeout value for service invocation, default value is 0 */ - int timeout; + private int timeout = 0; /** * Application associated name */ - String application; + private String application; /** * Module associated name */ - String module; + private String module; /** * Consumer associated name */ - String consumer; + private String consumer; /** * Monitor associated name */ - String monitor; + private String monitor; /** * Registry associated name */ - String registry; + private String registry; /** - * * @return the default value is "" */ - String protocol; + private String protocol = ""; /** * Service tag name */ - String tag; + private String tag; /** * The id * * @return default value is empty */ - String id; + private String id; public JbootrpcReferenceConfig() { diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java index 21b47fed..930bcc11 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java @@ -29,103 +29,102 @@ public class JbootrpcServiceConfig implements Serializable { /** * Service version, default value is empty string */ - String version; + private String version; /** * Service group, default value is empty string */ - String group; + private String group; /** * Service path, default value is empty string */ - String path; + private String path; /** * Whether to export service, default value is true */ - boolean export; + private boolean export = true; /** * Service token, default value is false */ - String token; + private String token; /** * Whether the service is deprecated, default value is false */ - boolean deprecated; + private boolean deprecated = false; /** * Whether to register the service to register center, default value is true */ - boolean register; + private boolean register = true; /** * Service weight value, default value is 0 */ - int weight; + private int weight = 0; /** * Service doc, default value is "" */ - String document; + private String document = ""; /** * Service invocation retry times */ - int retries; + private int retries = 2; /** * Load balance strategy, legal values include: random, roundrobin, leastactive */ - String loadbalance; + private String loadbalance = "random"; /** * Application spring bean name */ - String application; + private String application; /** * Module spring bean name */ - String module; + private String module; /** * Provider spring bean name */ - String provider; + private String provider; /** * Protocol spring bean names */ - String protocol; + private String protocol; /** * Monitor spring bean name */ - String monitor; + private String monitor; /** * Registry spring bean name */ - String registry; + private String registry; /** * Service tag name */ - String tag; - + private String tag; public JbootrpcServiceConfig() { } public JbootrpcServiceConfig(RPCBean bean) { - Utils.appendAnnotation(RPCBean.class,bean,this); + Utils.appendAnnotation(RPCBean.class, bean, this); } public String getVersion() { -- Gitee From 818ffed323d849ff5446e907b2753858a7d4a575 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 19:06:37 +0800 Subject: [PATCH 0147/1965] v3.1.1 --- .../java/io/jboot/components/schedule/JbootScheduleManager.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java b/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java index b8978e17..b019ff03 100644 --- a/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java +++ b/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java @@ -59,8 +59,6 @@ public class JbootScheduleManager { cron4jPlugin = cron4jProperties.exists() ? new JbootCron4jPlugin(new Prop(config.getCron4jFile())) : new JbootCron4jPlugin(); - LOG.info("Init JbootScheduleManager"); - } -- Gitee From 93ce74d504690d674c05eca97c64864fca14aeb7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 19:09:40 +0800 Subject: [PATCH 0148/1965] optimize rpc demos --- src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java | 1 + .../java/io/jboot/test/rpc/dubbo/DubboInterceptor.java | 3 +-- .../jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java | 6 ++---- .../jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java | 6 ++---- .../rpc/dubbozookeeper/DubboClientZookeeperDemo.java | 3 +-- .../rpc/dubbozookeeper/DubboServer1ZookeeperDemo.java | 9 ++------- .../rpc/dubbozookeeper/DubboServer2ZookeeperDemo.java | 7 ++----- 7 files changed, 11 insertions(+), 24 deletions(-) diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java index e546fdf4..bc8e806e 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java @@ -34,6 +34,7 @@ public class DubboClient extends JbootController { //RPC配置 JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); + JbootApplication.setBootArg("jboot.rpc.autoExportEnable", false); //设置直连模式,方便调试,默认为注册中心 JbootApplication.setBootArg("jboot.rpc.urls", "io.jboot.test.rpc.commons.BlogService:127.0.0.1:28080"); diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java index 6c84c73f..999ce946 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java @@ -17,7 +17,6 @@ package io.jboot.test.rpc.dubbo; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; -import io.jboot.components.rpc.annotation.RPCInject; import io.jboot.test.rpc.commons.BlogService; /** @@ -26,7 +25,7 @@ import io.jboot.test.rpc.commons.BlogService; */ public class DubboInterceptor implements Interceptor { - @RPCInject(check = false) +// @RPCInject(check = false) private BlogService blogService; @Override diff --git a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java index 276924c0..a37fd089 100644 --- a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java @@ -2,14 +2,12 @@ package io.jboot.test.rpc.dubbonacos; import io.jboot.app.JbootApplication; +import io.jboot.app.JbootRpcApplication; public class DubboServer1NacosDemo { public static void main(String[] args) { - //jboot端口号配置 - JbootApplication.setBootArg("undertow.port", "9997"); - // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); @@ -30,7 +28,7 @@ public class DubboServer1NacosDemo { - JbootApplication.run(args); + JbootRpcApplication.run(args); System.out.println("DubboServer1NacosDemo started..."); diff --git a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java index 0f82bab7..e4bf67f6 100644 --- a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java @@ -2,14 +2,12 @@ package io.jboot.test.rpc.dubbonacos; import io.jboot.app.JbootApplication; +import io.jboot.app.JbootRpcApplication; public class DubboServer2NacosDemo { public static void main(String[] args) { - //jboot端口号配置 - JbootApplication.setBootArg("undertow.port", "9998"); - // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); @@ -28,7 +26,7 @@ public class DubboServer2NacosDemo { //dubbo 的通信协议配置,如果port配置为-1,则会分配一个没有被占用的端口。 JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "28081"); - JbootApplication.run(args); + JbootRpcApplication.run(args); diff --git a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboClientZookeeperDemo.java b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboClientZookeeperDemo.java index 878a3b2b..94379c94 100644 --- a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboClientZookeeperDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboClientZookeeperDemo.java @@ -16,13 +16,11 @@ public class DubboClientZookeeperDemo extends JbootController { JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); - // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "zookeeper"); //注册中心地址,即zookeeper的地址 JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:2181"); - JbootApplication.run(args); } @@ -35,6 +33,7 @@ public class DubboClientZookeeperDemo extends JbootController { System.out.println("DubboClientZookeeperDemo.index()"); System.out.println(blogService); + renderText(blogService.findById()); } } diff --git a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer1ZookeeperDemo.java b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer1ZookeeperDemo.java index e397d33b..ec8145e6 100644 --- a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer1ZookeeperDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer1ZookeeperDemo.java @@ -2,23 +2,18 @@ package io.jboot.test.rpc.dubbozookeeper; import io.jboot.app.JbootApplication; +import io.jboot.app.JbootRpcApplication; public class DubboServer1ZookeeperDemo { public static void main(String[] args) { - //jboot端口号配置 - JbootApplication.setBootArg("undertow.port", "9997"); - // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); JbootApplication.setBootArg("jboot.rpc.type", "dubbo"); - - - // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "zookeeper"); //注册中心地址,即zookeeper的地址 @@ -32,7 +27,7 @@ public class DubboServer1ZookeeperDemo { - JbootApplication.run(args); + JbootRpcApplication.run(args); System.out.println("DubboServer1ZookeeperDemo started..."); diff --git a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer2ZookeeperDemo.java b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer2ZookeeperDemo.java index 3dfd0ee1..e9b59b60 100644 --- a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer2ZookeeperDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer2ZookeeperDemo.java @@ -2,13 +2,12 @@ package io.jboot.test.rpc.dubbozookeeper; import io.jboot.app.JbootApplication; +import io.jboot.app.JbootRpcApplication; public class DubboServer2ZookeeperDemo { public static void main(String[] args) { - //jboot端口号配置 - JbootApplication.setBootArg("undertow.port", "9998"); // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 @@ -17,8 +16,6 @@ public class DubboServer2ZookeeperDemo { - - // dubbo 的注册中心的协议,支持的类型有 dubbo, multicast, zookeeper, redis, consul(2.7.1), sofa(2.7.2), etcd(2.7.2), nacos(2.7.2) JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol", "zookeeper"); //注册中心地址,即zookeeper的地址 @@ -30,7 +27,7 @@ public class DubboServer2ZookeeperDemo { //dubbo 的通信协议配置,如果port配置为-1,则会分配一个没有被占用的端口。 JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "28081"); - JbootApplication.run(args); + JbootRpcApplication.run(args); -- Gitee From 54bc663b0290467df44c968f61176edf04bf7434 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 19:17:31 +0800 Subject: [PATCH 0149/1965] close qos server by default --- src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java | 5 +++++ src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index b512700f..91d03b68 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -54,6 +54,11 @@ class DubboUtil { if (StrUtil.isBlank(applicationConfig.getName())) { applicationConfig.setName("jboot"); } + //默认关闭 qos + if (applicationConfig.getQosEnable() == null){ + applicationConfig.setQosEnable(false); + } + dubboBootstrap.application(applicationConfig); diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java index 999ce946..6c84c73f 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java @@ -17,6 +17,7 @@ package io.jboot.test.rpc.dubbo; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; +import io.jboot.components.rpc.annotation.RPCInject; import io.jboot.test.rpc.commons.BlogService; /** @@ -25,7 +26,7 @@ import io.jboot.test.rpc.commons.BlogService; */ public class DubboInterceptor implements Interceptor { -// @RPCInject(check = false) + @RPCInject(check = false) private BlogService blogService; @Override -- Gitee From 93f4a99a03d1169c7d322610ed91f029552e163e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 19:32:06 +0800 Subject: [PATCH 0150/1965] optimize JbootRpcApplication --- src/main/java/io/jboot/app/JbootRpcApplication.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/app/JbootRpcApplication.java b/src/main/java/io/jboot/app/JbootRpcApplication.java index 60dacc38..4b49bde8 100644 --- a/src/main/java/io/jboot/app/JbootRpcApplication.java +++ b/src/main/java/io/jboot/app/JbootRpcApplication.java @@ -1,11 +1,11 @@ /** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

- * http://www.gnu.org/licenses/lgpl-3.0.txt + * 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, @@ -47,6 +47,7 @@ public class JbootRpcApplication { static class RPCServer extends Thread { + private JbootCoreConfig coreConfig; public RPCServer(JbootCoreConfig coreConfig) { -- Gitee From c6e6d3fea5105169d7eef8273a64547b51042201 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 22:21:12 +0800 Subject: [PATCH 0151/1965] add proxyRetries config for GatewayHttpProxy --- .../components/gateway/GatewayHttpProxy.java | 26 ++++++++++++++++--- .../gateway/JbootGatewayConfig.java | 10 +++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index 4a533428..8c7980e7 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -40,18 +40,38 @@ public class GatewayHttpProxy { private int readTimeOut; private int connectTimeOut; + private int retries; private String contentType; public GatewayHttpProxy(JbootGatewayConfig config) { this.readTimeOut = config.getProxyReadTimeout(); this.connectTimeOut = config.getProxyConnectTimeout(); + this.retries = config.getProxyRetries(); this.contentType = config.getProxyContentType(); + } public void sendRequest(String url, HttpServletRequest req, HttpServletResponse resp) { + int triesCount = retries < 0 ? 0 : retries; + Exception exception = null; + do { + try { + exception = null; + doSendRequest(url, req, resp); + } catch (Exception ex) { + exception = ex; + } + } while (exception != null && triesCount-- > 0); - HttpURLConnection conn = null; + if (exception != null) { + LOG.error(exception.toString(), exception); + } + } + + public void doSendRequest(String url, HttpServletRequest req, HttpServletResponse resp) throws Exception { + + HttpURLConnection conn = null; try { conn = getConnection(url); @@ -78,9 +98,6 @@ public class GatewayHttpProxy { */ copyStreamToResponse(conn, resp); - - } catch (Exception ex) { - LOG.warn(ex.toString(), ex); } finally { if (conn != null) { conn.disconnect(); @@ -88,6 +105,7 @@ public class GatewayHttpProxy { } } + private void copyRequestStreamToConnection(HttpServletRequest req, HttpURLConnection conn) throws IOException { OutputStream outStream = null; InputStream inStream = null; diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index 0fe912be..048dfc7c 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -44,8 +44,10 @@ public class JbootGatewayConfig implements Serializable { // 设置 http 代理的参数 private int proxyReadTimeout = 10000; //10s private int proxyConnectTimeout = 5000; //5s + private int proxyRetries = 2; //2 times private String proxyContentType = DEFAULT_PROXY_CONTENT_TYPE; + private String[] pathEquals; private String[] pathContains; private String[] pathStartsWith; @@ -125,6 +127,14 @@ public class JbootGatewayConfig implements Serializable { this.proxyConnectTimeout = proxyConnectTimeout; } + public int getProxyRetries() { + return proxyRetries; + } + + public void setProxyRetries(int proxyRetries) { + this.proxyRetries = proxyRetries; + } + public String getProxyContentType() { return proxyContentType; } -- Gitee From d3cee9f691bc1fdbb062cfdaa225ab0ea5bed2ab Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 22:35:21 +0800 Subject: [PATCH 0152/1965] add proxyRetries config for GatewayHttpProxy --- .../java/io/jboot/components/gateway/GatewayHttpProxy.java | 1 + src/test/resources/jboot.properties | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index 8c7980e7..5c4c53f2 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -54,6 +54,7 @@ public class GatewayHttpProxy { public void sendRequest(String url, HttpServletRequest req, HttpServletResponse resp) { int triesCount = retries < 0 ? 0 : retries; Exception exception = null; + do { try { exception = null; diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index e6e439f5..5cc87e7a 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -39,6 +39,7 @@ config.test.test.ccc.type = type3 # 配置 gateway ,访问首页的时候目标地址设置为 baidu 的地址 jboot.gateway.enable = true jboot.gateway.uri = https://www.baidu.com +#jboot.gateway.proxyRetries = 2 jboot.gateway.pathEquals = / jboot.gateway.gitee.enable = true @@ -46,3 +47,5 @@ jboot.gateway.gitee.uri= https://gitee.com jboot.gateway.gitee.sentinelEnable= true jboot.gateway.gitee.sentinelBlockPage= / jboot.gateway.gitee.pathStartsWith = /fuhai + +jboot.rpc.type = local -- Gitee From 12c59e48bb40496450e9b760b58ed3bc96885b8d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Mar 2020 22:46:58 +0800 Subject: [PATCH 0153/1965] v3.1.1 release (^.^)YYa!! --- changes.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/changes.txt b/changes.txt index 581b7003..afea3ca7 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,16 @@ +jboot v3.1.1: +新增:Gateway 网关自定义拦截器的支持,方便通过网关进行鉴权等操作 +新增:Gateway 网关新增错误重试次数的配置,方便网关重试配置 +新增:jboot.properties 配置里的 value 值添加 ${para} 参数的支持 +新增:undertow 端口配置,添加 -1 的支持,通过 -1 配置可用的随机端口 +新增:JbootRpcApplication,方便用于只启动 RPC Service 服务,用于给消费者提供服务 +修复:修复 @RPCInject 在拦截器使用的时候出错的问题 +修复:RPC 工具类 Utils.appendAnnotation 无法正确给 int 、boolean 参数赋值的问题 +优化:当不对 Dubbo 的 qos 配置的时候,设置为默认关闭,方便单机情况下进行开发调试 +文档:更新 Gateway 网关的相关文档 + + + jboot v3.1.0: 新增:新增网关的支持,路由规则支持 host、path、query等 三种模式,同时支持基于 Sentinel 的限流配置 新增:新增对 Dubbo 多协议、多注册中心等的支持 -- Gitee From a638ab417d9a4ff2fcb77575ec1ad1eef4fbbdf0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 25 Mar 2020 08:24:35 +0800 Subject: [PATCH 0154/1965] v3.1.1 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4d6ac27c..48617538 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.1.0 + 3.1.1 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index e115a0cd..668bf938 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.1.0 + 3.1.1 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index b16eb1eb..788189c9 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.1.0 + 3.1.1 ``` diff --git a/pom.xml b/pom.xml index 1f78aef5..9e312162 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.1-SNAPSHOT + 3.1.1 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 86844384..8415f186 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.1.0"; + public static String VERSION = "3.1.1"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 549bfef8d4df665d4180ae0a58138eb4c5d9e292 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 26 Mar 2020 10:04:38 +0800 Subject: [PATCH 0155/1965] v3.1.2 release (^.^)YYa!! --- .../java/io/jboot/utils/ClassScanner.java | 76 +++++++++---------- 1 file changed, 34 insertions(+), 42 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index b6d0d5cd..e24ad220 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -21,9 +21,6 @@ import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Modifier; -import java.net.URL; -import java.net.URLClassLoader; -import java.net.URLDecoder; import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -31,7 +28,8 @@ import java.util.stream.Collectors; public class ClassScanner { - private static final Set applicationClassCache = new HashSet<>(); + private static final Set appClassesCache = new HashSet<>(); + public static final Set includeJars = new HashSet<>(); public static final Set excludeJars = new HashSet<>(); @@ -199,7 +197,17 @@ public class ClassScanner { excludeJars.add("servo-"); excludeJars.add("compactmap-"); excludeJars.add("dexx-"); + excludeJars.add("spotbugs-"); excludeJars.add("xmlpull-"); + excludeJars.add("shardingsphere-"); + excludeJars.add("sentinel-"); + excludeJars.add("spring-"); + excludeJars.add("simpleclient-"); + excludeJars.add("breeze-"); + excludeJars.add("config-"); + excludeJars.add("encrypt-core-"); + excludeJars.add("jakarta."); + } static { @@ -245,17 +253,17 @@ public class ClassScanner { initIfNecessary(); if (!isInstantiable) { - return new ArrayList<>(applicationClassCache); + return new ArrayList<>(appClassesCache); } - return applicationClassCache.stream() + return appClassesCache.stream() .filter(ClassScanner::isInstantiable) .collect(Collectors.toList()); } - public static void clearClassCache() { - applicationClassCache.clear(); + public static void clearAppClassesCache() { + appClassesCache.clear(); } @@ -268,7 +276,7 @@ public class ClassScanner { initIfNecessary(); List list = new ArrayList<>(); - for (Class clazz : applicationClassCache) { + for (Class clazz : appClassesCache) { Annotation annotation = clazz.getAnnotation(annotationClass); if (annotation == null) { continue; @@ -285,14 +293,14 @@ public class ClassScanner { } private static void initIfNecessary() { - if (applicationClassCache.isEmpty()) { + if (appClassesCache.isEmpty()) { initAppClasses(); } } private static void findChildClasses(List> classes, Class parent, boolean isInstantiable) { - for (Class clazz : applicationClassCache) { + for (Class clazz : appClassesCache) { if (!parent.isAssignableFrom(clazz)) { continue; @@ -312,7 +320,7 @@ public class ClassScanner { Set jarPaths = new HashSet<>(); Set classPaths = new HashSet<>(); - findClassPathsAndJars(jarPaths, classPaths, ClassScanner.class.getClassLoader()); + findClassPathsAndJars(jarPaths, classPaths); String tomcatClassPath = null; @@ -400,36 +408,26 @@ public class ClassScanner { private static void addClass(Class clazz) { if (clazz != null) { - applicationClassCache.add(clazz); + appClassesCache.add(clazz); } } - private static void findClassPathsAndJars(Set jarPaths, Set classPaths, ClassLoader classLoader) { + private static void findClassPathsAndJars(Set jarPaths, Set classPaths) { try { - if (classLoader instanceof URLClassLoader) { - URLClassLoader urlClassLoader = (URLClassLoader) classLoader; - URL[] urLs = urlClassLoader.getURLs(); - for (URL url : urLs) { - String path = url.getPath(); - path = URLDecoder.decode(path, "UTF-8"); - - // path : /d:/xxx - if (path.startsWith("/") && path.indexOf(":") == 2) { - path = path.substring(1); - } - - if (!path.toLowerCase().endsWith(".jar")) { - classPaths.add(new File(path).getCanonicalPath()); - continue; - } - - jarPaths.add(path); + + String[] classPathArray = System.getProperty("java.class.path").split(File.pathSeparator); + for (String path : classPathArray) { + if (path.startsWith("/") && path.indexOf(":") == 2) { + path = path.substring(1); } - } - ClassLoader parent = classLoader.getParent(); - if (parent != null) { - findClassPathsAndJars(jarPaths, classPaths, parent); + + if (!path.toLowerCase().endsWith(".jar")) { + classPaths.add(new File(path).getCanonicalPath()); + continue; + } + + jarPaths.add(path); } } catch (Exception ex) { ex.printStackTrace(); @@ -452,12 +450,6 @@ public class ClassScanner { } } - //from maven repository - if (path.contains("/.m2/repository") - || path.contains("\\.m2\\repository")) { - return false; - } - //from jre lib if (path.contains("/jre/lib") || path.contains("\\jre\\lib")) { -- Gitee From aa87687416114236755bb976985655bff211c9e5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 26 Mar 2020 10:09:28 +0800 Subject: [PATCH 0156/1965] v3.1.2 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 5 +++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- src/test/resources/jboot.properties | 1 + 7 files changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 48617538..96d7b655 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.1.1 + 3.1.2 ``` diff --git a/changes.txt b/changes.txt index afea3ca7..db6c5dd0 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,8 @@ +jboot v3.1.2: +新增:新增对 JDK11+ 的支持 + + + jboot v3.1.1: 新增:Gateway 网关自定义拦截器的支持,方便通过网关进行鉴权等操作 新增:Gateway 网关新增错误重试次数的配置,方便网关重试配置 diff --git a/doc/docs/install.md b/doc/docs/install.md index 668bf938..3f97fc02 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.1.1 + 3.1.2 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 788189c9..0faf5188 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.1.1 + 3.1.2 ``` diff --git a/pom.xml b/pom.xml index 9e312162..964cc228 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.1 + 3.1.2 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 8415f186..a9b8681f 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.1.1"; + public static String VERSION = "3.1.2"; public static final String ATTR_REQUEST = "REQUEST"; diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index 5cc87e7a..151711f4 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -1,6 +1,7 @@ undertow.host=127.0.0.1 undertow.port=9999 undertow.resourcePath = src/test/resources +#undertow.devMode = false #undertow.hotSwapClassPrefix= #undertow.unHotSwapClassPrefix= -- Gitee From 52bff56db0675d8cd5b31ac405891997beb46270 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 26 Mar 2020 10:26:02 +0800 Subject: [PATCH 0157/1965] update docs --- doc/docs/rpc.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/docs/rpc.md b/doc/docs/rpc.md index bb26f492..1d0f3275 100644 --- a/doc/docs/rpc.md +++ b/doc/docs/rpc.md @@ -20,6 +20,16 @@ Jboot 支持 dubbo 和 motan,假设我们需要使用 dubbo 作为底层的 RP org.apache.dubbo dubbo ${dubbo.version} + + + org.springframework + spring-context + + + com.alibaba.spring + spring-context-support + + -- Gitee From fa2eae112779fefb5174715bd5f64246dbb2a88f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 26 Mar 2020 10:26:19 +0800 Subject: [PATCH 0158/1965] v3.1.2 release (^.^)YYa!! --- pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pom.xml b/pom.xml index 964cc228..56ebb578 100644 --- a/pom.xml +++ b/pom.xml @@ -439,6 +439,16 @@ dubbo ${dubbo.version} provided + + + org.springframework + spring-context + + + com.alibaba.spring + spring-context-support + + -- Gitee From b8910b519bbc8b41278b11b871904881896941ce Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 26 Mar 2020 10:49:31 +0800 Subject: [PATCH 0159/1965] v3.1.2 release (^.^)YYa!! --- .../java/io/jboot/components/rpc/JbootrpcReferenceConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java index 8a927522..ba719583 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java @@ -112,7 +112,7 @@ public class JbootrpcReferenceConfig implements Serializable { private String registry; /** - * @return the default value is "" + * the default value is "" */ private String protocol = ""; @@ -124,7 +124,7 @@ public class JbootrpcReferenceConfig implements Serializable { /** * The id * - * @return default value is empty + * default value is empty */ private String id; -- Gitee From b6b45390d7ec03480fe5d7837ab80db235419fef Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 26 Mar 2020 10:51:33 +0800 Subject: [PATCH 0160/1965] v3.1.2 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 56ebb578..1831330c 100644 --- a/pom.xml +++ b/pom.xml @@ -806,7 +806,7 @@ -Xdoclint:none true - https://jboot.io + http://jboot.io -- Gitee From 8efb06d1d175a01ebdd91762143ca7e3b6c271a1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 26 Mar 2020 10:57:01 +0800 Subject: [PATCH 0161/1965] v3.1.2 release (^.^)YYa!! --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 1831330c..23007bfa 100644 --- a/pom.xml +++ b/pom.xml @@ -805,6 +805,7 @@ true -Xdoclint:none true + 8 http://jboot.io -- Gitee From f43a8fb4cfb6196ae43f04a9fa9253f31fab9c44 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 26 Mar 2020 11:12:12 +0800 Subject: [PATCH 0162/1965] v3.1.2 release (^.^)YYa!! --- pom.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pom.xml b/pom.xml index 23007bfa..1f5bffdf 100644 --- a/pom.xml +++ b/pom.xml @@ -806,9 +806,6 @@ -Xdoclint:none true 8 - - http://jboot.io - -- Gitee From 6c9ea9a4efded1aea6cf535a5b53d5e252448e19 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 26 Mar 2020 12:54:52 +0800 Subject: [PATCH 0163/1965] v3.1.3 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- .../java/io/jboot/utils/ClassScanner.java | 58 +++++++++++++++---- 6 files changed, 51 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 96d7b655..8c402fa7 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.1.2 + 3.1.3 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 3f97fc02..386b8515 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.1.2 + 3.1.3 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 0faf5188..a418d4df 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.1.2 + 3.1.3 ``` diff --git a/pom.xml b/pom.xml index 1f5bffdf..48617c11 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.2 + 3.1.3 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index a9b8681f..9929e4c1 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.1.2"; + public static String VERSION = "3.1.3"; public static final String ATTR_REQUEST = "REQUEST"; diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index e24ad220..57813359 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -20,7 +20,12 @@ import io.jboot.app.config.JbootConfigManager; import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLDecoder; import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -320,14 +325,15 @@ public class ClassScanner { Set jarPaths = new HashSet<>(); Set classPaths = new HashSet<>(); - findClassPathsAndJars(jarPaths, classPaths); + findClassPathsAndJars(jarPaths, classPaths, ClassScanner.class.getClassLoader()); String tomcatClassPath = null; for (String classPath : classPaths) { //过滤tomcat自身的lib 以及 bin 下的jar File tomcatApiJarFile = new File(classPath, "tomcat-api.jar"); - if (tomcatApiJarFile.exists()) { + File tomcatJuliJarFile = new File(classPath, "tomcat-juli.jar"); + if (tomcatApiJarFile.exists() || tomcatJuliJarFile.exists()) { tomcatClassPath = tomcatApiJarFile .getParentFile() .getParentFile().getAbsolutePath(); @@ -413,25 +419,53 @@ public class ClassScanner { } - private static void findClassPathsAndJars(Set jarPaths, Set classPaths) { + private static void findClassPathsAndJars(Set jarPaths, Set classPaths, ClassLoader classLoader) { try { - String[] classPathArray = System.getProperty("java.class.path").split(File.pathSeparator); - for (String path : classPathArray) { - if (path.startsWith("/") && path.indexOf(":") == 2) { - path = path.substring(1); + URL[] urls = null; + if (classLoader instanceof URLClassLoader) { + URLClassLoader ucl = (URLClassLoader) classLoader; + urls = ucl.getURLs(); + } else { + Field ucpf = classLoader.getClass().getDeclaredField("ucp"); + if (ucpf != null) { + ucpf.setAccessible(true); + Object obj = ucpf.get(classLoader); + Method method = obj.getClass().getMethod("getURLs"); + if (method != null) { + urls = (URL[]) method.invoke(obj); + } } + } - if (!path.toLowerCase().endsWith(".jar")) { - classPaths.add(new File(path).getCanonicalPath()); - continue; - } + if (urls != null) { + for (URL url : urls) { + String path = url.getPath(); + path = URLDecoder.decode(path, "UTF-8"); + + // path : /d:/xxx + if (path.startsWith("/") && path.indexOf(":") == 2) { + path = path.substring(1); + } + + if (!path.toLowerCase().endsWith(".jar")) { + classPaths.add(new File(path).getCanonicalPath()); + continue; + } - jarPaths.add(path); + jarPaths.add(path); + } } + } catch (NoSuchFieldException | NoSuchMethodException e) { + // ignore } catch (Exception ex) { ex.printStackTrace(); } + + ClassLoader parent = classLoader.getParent(); + if (parent != null) { + findClassPathsAndJars(jarPaths, classPaths, parent); + } } private static boolean isIncludeJar(String path) { -- Gitee From 024f92f9ec1476f2ac0c362bccd35b72190788f1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 26 Mar 2020 12:55:19 +0800 Subject: [PATCH 0164/1965] v3.1.3 release (^.^)YYa!! --- changes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index db6c5dd0..653ea0d3 100644 --- a/changes.txt +++ b/changes.txt @@ -1,4 +1,4 @@ -jboot v3.1.2: +jboot v3.1.3: 新增:新增对 JDK11+ 的支持 -- Gitee From db1641343ff63777933a54f846901df22062a787 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 28 Mar 2020 17:04:19 +0800 Subject: [PATCH 0165/1965] v3.1.4 --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 48617c11..5facae8c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.3 + 3.1.4-SNAPSHOT jar jboot @@ -113,6 +113,7 @@ ${cglib.version} + io.undertow undertow-websockets-jsr -- Gitee From 57b93c8c0ed06f82a4a6d24eff0f814e9094a77a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 28 Mar 2020 17:14:39 +0800 Subject: [PATCH 0166/1965] v3.1.4 --- .../java/io/jboot/utils/ClassScanner.java | 56 ++++++++++++------- .../java/io/jboot/test/TestAppStater.java | 3 +- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 57813359..6c9f86cd 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -20,8 +20,6 @@ import io.jboot.app.config.JbootConfigManager; import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.net.URLClassLoader; @@ -325,7 +323,15 @@ public class ClassScanner { Set jarPaths = new HashSet<>(); Set classPaths = new HashSet<>(); - findClassPathsAndJars(jarPaths, classPaths, ClassScanner.class.getClassLoader()); + // jdk8 及以下、 + // tomcat 容器、 + // jfinal-undertow、 + // 以上三种加载模式通过 classloader 获取 + findClassPathsAndJarsByClassloader(jarPaths, classPaths, ClassScanner.class.getClassLoader()); + + //jdk9+ 等其他方式通过 classpath 获取 + findClassPathsAndJarsByClassPath(jarPaths, classPaths); + String tomcatClassPath = null; @@ -419,25 +425,13 @@ public class ClassScanner { } - private static void findClassPathsAndJars(Set jarPaths, Set classPaths, ClassLoader classLoader) { + private static void findClassPathsAndJarsByClassloader(Set jarPaths, Set classPaths, ClassLoader classLoader) { try { - URL[] urls = null; if (classLoader instanceof URLClassLoader) { URLClassLoader ucl = (URLClassLoader) classLoader; urls = ucl.getURLs(); - } else { - Field ucpf = classLoader.getClass().getDeclaredField("ucp"); - if (ucpf != null) { - ucpf.setAccessible(true); - Object obj = ucpf.get(classLoader); - Method method = obj.getClass().getMethod("getURLs"); - if (method != null) { - urls = (URL[]) method.invoke(obj); - } - } } - if (urls != null) { for (URL url : urls) { String path = url.getPath(); @@ -452,22 +446,44 @@ public class ClassScanner { classPaths.add(new File(path).getCanonicalPath()); continue; } - jarPaths.add(path); } } - } catch (NoSuchFieldException | NoSuchMethodException e) { - // ignore } catch (Exception ex) { ex.printStackTrace(); } ClassLoader parent = classLoader.getParent(); if (parent != null) { - findClassPathsAndJars(jarPaths, classPaths, parent); + findClassPathsAndJarsByClassloader(jarPaths, classPaths, parent); + } + } + + private static void findClassPathsAndJarsByClassPath(Set jarPaths, Set classPaths) { + String[] classPathArray = System.getProperty("java.class.path").split(File.pathSeparator); + for (String path : classPathArray) { + path = path.trim(); + + if (path.startsWith("./")) { + path = path.substring(2); + } + + if (path.startsWith("/") && path.indexOf(":") == 2) { + path = path.substring(1); + } + + if (!path.toLowerCase().endsWith(".jar") && !jarPaths.contains(path)) { + try { + classPaths.add(new File(path).getCanonicalPath()); + } catch (IOException e) { + } + } else { + jarPaths.add(path); + } } } + private static boolean isIncludeJar(String path) { String jarName = new File(path).getName().toLowerCase(); diff --git a/src/test/java/io/jboot/test/TestAppStater.java b/src/test/java/io/jboot/test/TestAppStater.java index 270b8c24..56c9d691 100644 --- a/src/test/java/io/jboot/test/TestAppStater.java +++ b/src/test/java/io/jboot/test/TestAppStater.java @@ -2,11 +2,10 @@ package io.jboot.test; import io.jboot.app.JbootApplication; - public class TestAppStater { public static void main(String[] args){ -// JbootApplication.setBootArg("jboot.app.mode","product"); JbootApplication.run(args); +// JbootRpcApplication.run(args); } } -- Gitee From fb8057433b4d8b242619cc3f66f7c4ece795212f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 29 Mar 2020 10:40:53 +0800 Subject: [PATCH 0167/1965] v3.1.4 --- .../jboot/app/config/JbootConfigManager.java | 66 +++++++------------ src/main/java/io/jboot/app/config/Prop.java | 14 ++-- src/main/java/io/jboot/app/config/Utils.java | 2 +- 3 files changed, 32 insertions(+), 50 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 7213d784..134eea8b 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -19,7 +19,6 @@ import com.jfinal.kit.LogKit; import io.jboot.app.config.annotation.ConfigModel; import io.jboot.utils.StrUtil; -import java.io.File; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.*; @@ -65,20 +64,13 @@ public class JbootConfigManager { private void init() { - File jbootPropertiesFile = new File(Utils.getRootClassPath(), "jboot.properties"); - if (!jbootPropertiesFile.exists()) { - mainProperties = new Properties(); - } else { - mainProperties = new Prop("jboot.properties").getProperties(); - } + mainProperties = new Prop("jboot.properties").getProperties(); String mode = getConfigValue("jboot.app.mode"); if (Utils.isNotBlank(mode)) { String p = String.format("jboot-%s.properties", mode); - if (new File(Utils.getRootClassPath(), p).exists()) { - mainProperties.putAll(new Prop(p).getProperties()); - } + mainProperties.putAll(new Prop(p).getProperties()); } } @@ -167,22 +159,15 @@ public class JbootConfigManager { private void refreshMainProperties() { - Properties jbootProperties = new Prop("jboot.properties").getProperties(); - if (jbootProperties == null) { - return; - } - - mainProperties.putAll(jbootProperties); + Properties properties = new Prop("jboot.properties").getProperties(); + mainProperties.putAll(properties); - String mode = getConfigValue(jbootProperties, "jboot.app.mode"); + String mode = getConfigValue(properties, "jboot.app.mode"); if (Utils.isNotBlank(mode)) { String p = String.format("jboot-%s.properties", mode); - if (new File(Utils.getRootClassPath(), p).exists()) { - mainProperties.putAll(new Prop(p).getProperties()); - } + mainProperties.putAll(new Prop(p).getProperties()); } - } @@ -195,7 +180,7 @@ public class JbootConfigManager { * @param * @return */ - public T createConfigObject(Class clazz, String prefix, String file) { + public T createConfigObject(Class clazz, String prefix, String file){ Object configObject = Utils.newInstance(clazz); List setMethods = Utils.getClassSetMethods(clazz); if (setMethods != null) { @@ -204,26 +189,23 @@ public class JbootConfigManager { String key = buildKey(prefix, method); String value = getConfigValue(key); - if (Utils.isNotBlank(file) && getClass().getClassLoader().getResource(file) != null) { - try { - Prop prop = new Prop(file); - String filePropValue = getConfigValue(prop.getProperties(), key); - if (Utils.isNotBlank(filePropValue)) { - value = filePropValue; - } - } catch (Throwable ex) { + if (Utils.isNotBlank(file)) { + Prop prop = new Prop(file); + String filePropValue = getConfigValue(prop.getProperties(), key); + if (Utils.isNotBlank(filePropValue)) { + value = filePropValue; } } - try { - if (Utils.isNotBlank(value)) { - Object val = convert(method.getParameterTypes()[0], value, method.getGenericParameterTypes()[0]); - if (val != null) { + if (Utils.isNotBlank(value)) { + Object val = convert(method.getParameterTypes()[0], value, method.getGenericParameterTypes()[0]); + if (val != null) { + try { method.invoke(configObject, val); + } catch (Exception e) { + e.printStackTrace(); } } - } catch (Throwable ex) { - ex.printStackTrace(); } } } @@ -251,21 +233,21 @@ public class JbootConfigManager { public String getConfigValue(Properties properties, String key) { - if (StrUtil.isBlank(key)){ + if (StrUtil.isBlank(key)) { return ""; } String originalValue = getOriginalConfigValue(properties, key); - String stringValue = decryptor != null ? decryptor.decrypt(key, originalValue) : originalValue; + String stringValue = decryptor != null ? decryptor.decrypt(key, originalValue) : originalValue; List configParts = Utils.parseParts(stringValue); - if (configParts == null || configParts.isEmpty()){ + if (configParts == null || configParts.isEmpty()) { return stringValue; } - for (ConfigPart cp : configParts){ - String value = getConfigValue(properties,cp.getKey()); + for (ConfigPart cp : configParts) { + String value = getConfigValue(properties, cp.getKey()); value = StrUtil.isNotBlank(value) ? value : cp.getDefaultValue(); - stringValue = stringValue.replace(cp.getPartString(),value); + stringValue = stringValue.replace(cp.getPartString(), value); } return stringValue; } diff --git a/src/main/java/io/jboot/app/config/Prop.java b/src/main/java/io/jboot/app/config/Prop.java index bf850b53..56aa0ffc 100644 --- a/src/main/java/io/jboot/app/config/Prop.java +++ b/src/main/java/io/jboot/app/config/Prop.java @@ -20,7 +20,6 @@ import java.util.Properties; class Prop { - protected Properties properties = null; private static final String DEFAULT_ENCODING = "UTF-8"; @@ -29,16 +28,17 @@ class Prop { } public Prop(String fileName, String encoding) { + properties = new Properties(); InputStream inputStream = null; try { inputStream = Utils.getClassLoader().getResourceAsStream(fileName); - if (inputStream == null) { - throw new IllegalArgumentException("properties file not found in classpath, fileName : " + fileName); + if (inputStream != null) { + properties.load(new InputStreamReader(inputStream, encoding)); + } else { + System.err.println("warning: can not load properties file in classpath, file name :" + fileName); } - properties = new Properties(); - properties.load(new InputStreamReader(inputStream, encoding)); - } catch (IOException e) { - throw new RuntimeException("error loading properties file.", e); + } catch (Exception e) { + System.err.println("warning: can not load properties file in classpath, file name :" + fileName); } finally { if (inputStream != null) { try { diff --git a/src/main/java/io/jboot/app/config/Utils.java b/src/main/java/io/jboot/app/config/Utils.java index e4ed6f1e..5d2c37a0 100644 --- a/src/main/java/io/jboot/app/config/Utils.java +++ b/src/main/java/io/jboot/app/config/Utils.java @@ -117,7 +117,7 @@ class Utils { path = path.substring(0, path.length() - 1); } /** - * Fix path带有文件名 + * Fix path 带有文件名 */ if (path.endsWith(".jar")) { path = path.substring(0, path.lastIndexOf("/") + 1); -- Gitee From 21c5390722e04e689ddcc0c5fd4f99e6f721a901 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 29 Mar 2020 11:20:29 +0800 Subject: [PATCH 0168/1965] v3.1.4 --- .../java/io/jboot/core/JbootCoreConfig.java | 10 +++ .../java/io/jboot/utils/ClassScanner.java | 73 ++++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index d8770d09..db13d3d8 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -151,6 +151,7 @@ public class JbootCoreConfig extends JFinalConfig { constants.setJsonFactory(() -> new JbootJson()); constants.setInjectDependency(true); + JbootAppListenerManager.me().onConstantConfig(constants); } @@ -201,6 +202,10 @@ public class JbootCoreConfig extends JFinalConfig { @Override public void configEngine(Engine engine) { + if (inFatJar()) { + engine.setToClassPathSourceFactory(); + } + List directiveClasses = ClassScanner.scanClass(); for (Class clazz : directiveClasses) { JFinalDirective directive = (JFinalDirective) clazz.getAnnotation(JFinalDirective.class); @@ -228,6 +233,11 @@ public class JbootCoreConfig extends JFinalConfig { } + private boolean inFatJar(){ + return JbootCoreConfig.class.getResource("") == null; + } + + @Override public void configPlugin(Plugins plugins) { diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 6c9f86cd..301036d8 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -35,6 +35,7 @@ public class ClassScanner { public static final Set includeJars = new HashSet<>(); public static final Set excludeJars = new HashSet<>(); + public static final Set excludeClasses = new HashSet<>(); public static void addScanJarPrefix(String prefix) { includeJars.add(prefix.trim()); @@ -210,7 +211,67 @@ public class ClassScanner { excludeJars.add("config-"); excludeJars.add("encrypt-core-"); excludeJars.add("jakarta."); + } + + + public static void addUnscanClass(String prefix) { + excludeClasses.add(prefix.trim()); + } + static { + addUnscanClass("com.jfinal."); + addUnscanClass("org.aopalliance."); + addUnscanClass("org.apache."); + addUnscanClass("org.nustaq."); + addUnscanClass("net.sf."); + addUnscanClass("org.slf4j."); + addUnscanClass("org.antlr."); + addUnscanClass("org.jboss."); + addUnscanClass("org.javassist."); + addUnscanClass("org.hamcrest."); + addUnscanClass("org.jsoup."); + addUnscanClass("org.objenesis."); + addUnscanClass("org.ow2."); + addUnscanClass("org.reactivest."); + addUnscanClass("org.yaml."); + addUnscanClass("org.checker"); + addUnscanClass("org.codehaus"); + addUnscanClass("ch.qos."); + addUnscanClass("com.alibaba.csp."); + addUnscanClass("com.alibaba.nacos."); + addUnscanClass("com.alibaba.druid."); + addUnscanClass("com.alibaba.fastjson."); + addUnscanClass("com.aliyun.open"); + addUnscanClass("com.caucho"); + addUnscanClass("com.codahale"); + addUnscanClass("com.ctrip.framework.apollo"); + addUnscanClass("com.ecwid."); + addUnscanClass("com.esotericsoftware."); + addUnscanClass("com.fasterxml."); + addUnscanClass("com.github."); + addUnscanClass("com.google."); + addUnscanClass("com.rabbitmq."); + addUnscanClass("com.squareup."); + addUnscanClass("com.typesafe."); + addUnscanClass("com.weibo."); + addUnscanClass("com.zaxxer."); + addUnscanClass("com.mysql."); + addUnscanClass("org.gjt."); + addUnscanClass("io.dropwizard"); + addUnscanClass("io.jsonwebtoken"); + addUnscanClass("io.lettuce"); + addUnscanClass("reactor.adapter"); + addUnscanClass("io.prometheus"); + addUnscanClass("io.seata."); + addUnscanClass("io.swagger."); + addUnscanClass("io.undertow."); + addUnscanClass("it.sauronsoftware"); + addUnscanClass("javax."); + addUnscanClass("java."); + addUnscanClass("junit."); + addUnscanClass("jline."); + addUnscanClass("redis."); + addUnscanClass("net.oschina.j2cache"); } static { @@ -419,11 +480,21 @@ public class ClassScanner { } private static void addClass(Class clazz) { - if (clazz != null) { + if (clazz != null && isNotExcludeClass(clazz.getName())) { appClassesCache.add(clazz); } } + //用于在进行 fatjar 打包时,提高性能 + private static boolean isNotExcludeClass(String clazzName) { + for (String Prefix : excludeClasses) { + if (clazzName.startsWith(Prefix)) { + return false; + } + } + return true; + } + private static void findClassPathsAndJarsByClassloader(Set jarPaths, Set classPaths, ClassLoader classLoader) { try { -- Gitee From a320b55e71211c6cb41e300d11a20ef09502ee8e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 29 Mar 2020 12:56:09 +0800 Subject: [PATCH 0169/1965] v3.1.4 --- src/main/java/io/jboot/core/JbootCoreConfig.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index db13d3d8..a7b17840 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -202,8 +202,11 @@ public class JbootCoreConfig extends JFinalConfig { @Override public void configEngine(Engine engine) { - if (inFatJar()) { + if (runInFatjar()){ engine.setToClassPathSourceFactory(); + engine.setBaseTemplatePath(null); + }else { + engine.setBaseTemplatePath(PathKit.getRootClassPath()); } List directiveClasses = ClassScanner.scanClass(); @@ -233,8 +236,8 @@ public class JbootCoreConfig extends JFinalConfig { } - private boolean inFatJar(){ - return JbootCoreConfig.class.getResource("") == null; + private boolean runInFatjar() { + return Thread.currentThread().getContextClassLoader().getResource("") == null; } -- Gitee From 4da45cfb83fd317be6b11f8857227edc3dd0b5a2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 29 Mar 2020 13:05:33 +0800 Subject: [PATCH 0170/1965] v3.1.4 --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index 5facae8c..32110958 100644 --- a/pom.xml +++ b/pom.xml @@ -778,6 +778,7 @@ release + org.apache.maven.plugins @@ -831,6 +832,7 @@ + -- Gitee From ff3b5b39b6bf5237f676bcaa807dcd774217aca8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 29 Mar 2020 15:52:17 +0800 Subject: [PATCH 0171/1965] v3.1.4 --- src/main/java/io/jboot/core/JbootCoreConfig.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index a7b17840..df044ec0 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -202,11 +202,10 @@ public class JbootCoreConfig extends JFinalConfig { @Override public void configEngine(Engine engine) { - if (runInFatjar()){ + //通过 java -jar xxx.jar 在单独的jar里运行 + if (runInFatjar()) { engine.setToClassPathSourceFactory(); engine.setBaseTemplatePath(null); - }else { - engine.setBaseTemplatePath(PathKit.getRootClassPath()); } List directiveClasses = ClassScanner.scanClass(); @@ -235,12 +234,10 @@ public class JbootCoreConfig extends JFinalConfig { JbootAppListenerManager.me().onEngineConfig(engine); } - private boolean runInFatjar() { return Thread.currentThread().getContextClassLoader().getResource("") == null; } - @Override public void configPlugin(Plugins plugins) { -- Gitee From 89bb64c8f23eaf35984509af30e71d5e111597af Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 30 Mar 2020 11:27:39 +0800 Subject: [PATCH 0172/1965] v3.1.4 --- pom.xml | 1 + .../java/io/jboot/core/JbootCoreConfig.java | 22 ++++++------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index 32110958..04996cbf 100644 --- a/pom.xml +++ b/pom.xml @@ -99,6 +99,7 @@ com.jfinal jfinal-weixin ${jfinal-weixin.version} + provided diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index df044ec0..35e59b29 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -25,8 +25,6 @@ import com.jfinal.kit.PathKit; import com.jfinal.kit.PropKit; import com.jfinal.plugin.activerecord.ActiveRecordPlugin; import com.jfinal.template.Engine; -import com.jfinal.weixin.sdk.api.ApiConfig; -import com.jfinal.weixin.sdk.api.ApiConfigKit; import io.jboot.Jboot; import io.jboot.aop.JbootAopFactory; import io.jboot.aop.JbootAopInterceptor; @@ -62,8 +60,6 @@ import io.jboot.web.handler.JbootActionHandler; import io.jboot.web.handler.JbootFilterHandler; import io.jboot.web.handler.JbootHandler; import io.jboot.web.render.JbootRenderFactory; -import io.jboot.wechat.JbootAccessTokenCache; -import io.jboot.wechat.JbootWechatConfig; import java.io.File; import java.sql.Driver; @@ -135,13 +131,13 @@ public class JbootCoreConfig extends JFinalConfig { constants.setRenderFactory(JbootRenderFactory.me()); constants.setDevMode(Jboot.isDevMode()); - ApiConfigKit.setDevMode(Jboot.isDevMode()); - - JbootWechatConfig config = Jboot.config(JbootWechatConfig.class); - ApiConfig apiConfig = config.getApiConfig(); - if (apiConfig != null) { - ApiConfigKit.putApiConfig(apiConfig); - } +// ApiConfigKit.setDevMode(Jboot.isDevMode()); +// +// JbootWechatConfig config = Jboot.config(JbootWechatConfig.class); +// ApiConfig apiConfig = config.getApiConfig(); +// if (apiConfig != null) { +// ApiConfigKit.putApiConfig(apiConfig); +// } constants.setLogFactory(new Slf4jLogFactory()); constants.setMaxPostSize(1024 * 1024 * 2000); @@ -283,10 +279,6 @@ public class JbootCoreConfig extends JFinalConfig { JbootAppListenerManager.me().onStartBefore(); - /** - * 配置微信accessToken的缓存 - */ - ApiConfigKit.setAccessTokenCache(new JbootAccessTokenCache()); JsonManager.me().setDefaultDatePattern("yyyy-MM-dd HH:mm:ss"); /** -- Gitee From 066b642edeb3e9496d3a9919f318854abb60204c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 30 Mar 2020 11:39:25 +0800 Subject: [PATCH 0173/1965] v3.1.4 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 8 ++++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8c402fa7..8a644d48 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.1.3 + 3.1.4 ``` diff --git a/changes.txt b/changes.txt index 653ea0d3..37e45638 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,11 @@ +jboot v3.1.4: +优化:重构 ClassScanner ,提高在 fatjar 模式的扫描性能 +优化:重构 ConfigManager ,以便更好的支持 fatjar 模式下的配置文件读取 +优化:重构 JbootCoreConfig,方便在 fatjar 下能够准确读取 html 等资源文件 +优化:重构 JbootCoreConfig,把 JFinal-Weixin 设置非必须依赖 + + + jboot v3.1.3: 新增:新增对 JDK11+ 的支持 diff --git a/doc/docs/install.md b/doc/docs/install.md index 386b8515..aa964ced 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.1.3 + 3.1.4 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index a418d4df..82fb65b7 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.1.3 + 3.1.4 ``` diff --git a/pom.xml b/pom.xml index 04996cbf..273cfac7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.4-SNAPSHOT + 3.1.4 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 9929e4c1..28a9284b 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.1.3"; + public static String VERSION = "3.1.4"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 0381782cadb74b4cfe5713799316da3cf58edfc5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 30 Mar 2020 12:13:33 +0800 Subject: [PATCH 0174/1965] v3.1.4 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 273cfac7..60dd3a5f 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ 2.0.29.Final 1.7.30 2.57 - 1.2.67 + 1.2.68 28.2-jre 2.2.5 2.10.2 -- Gitee From d72c22d2bb2ea0e0a73c441b17feed5578db2a71 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 30 Mar 2020 17:23:29 +0800 Subject: [PATCH 0175/1965] v3.1.5 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 60dd3a5f..176a2ff2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.4 + 3.1.5-SNAPSHOT jar jboot -- Gitee From 4cbc0b0b7a1968cf8aac46b43b5c593d8d849019 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 30 Mar 2020 17:36:06 +0800 Subject: [PATCH 0176/1965] v3.1.5 --- .../java/io/jboot/utils/ClassScanner.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 301036d8..00b49387 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -210,6 +210,8 @@ public class ClassScanner { excludeJars.add("breeze-"); excludeJars.add("config-"); excludeJars.add("encrypt-core-"); + excludeJars.add("lombok-"); + excludeJars.add("hutool-"); excludeJars.add("jakarta."); } @@ -271,7 +273,9 @@ public class ClassScanner { addUnscanClass("junit."); addUnscanClass("jline."); addUnscanClass("redis."); + addUnscanClass("lombok."); addUnscanClass("net.oschina.j2cache"); + addUnscanClass("cn.hutool."); } static { @@ -514,10 +518,11 @@ public class ClassScanner { } if (!path.toLowerCase().endsWith(".jar")) { - classPaths.add(new File(path).getCanonicalPath()); + classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); continue; } - jarPaths.add(path); + + jarPaths.add(path.replace('\\', '/')); } } } catch (Exception ex) { @@ -531,7 +536,14 @@ public class ClassScanner { } private static void findClassPathsAndJarsByClassPath(Set jarPaths, Set classPaths) { - String[] classPathArray = System.getProperty("java.class.path").split(File.pathSeparator); + String classPath = System.getProperty("java.class.path"); + if (classPath == null || classPath.trim().length() == 0) { + return; + } + String[] classPathArray = classPath.split(File.pathSeparator); + if (classPathArray == null || classPathArray.length == 0) { + return; + } for (String path : classPathArray) { path = path.trim(); @@ -545,11 +557,11 @@ public class ClassScanner { if (!path.toLowerCase().endsWith(".jar") && !jarPaths.contains(path)) { try { - classPaths.add(new File(path).getCanonicalPath()); + classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); } catch (IOException e) { } } else { - jarPaths.add(path); + jarPaths.add(path.replace('\\', '/')); } } } -- Gitee From 39415fd648502595a2e26cc15a05f8b561f8c8af Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 31 Mar 2020 15:10:14 +0800 Subject: [PATCH 0177/1965] v3.1.5 --- .../jboot/components/rpc/JbootrpcServiceConfig.java | 12 ++++++------ .../io/jboot/components/rpc/annotation/RPCBean.java | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java index 930bcc11..fe8db2da 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java @@ -85,32 +85,32 @@ public class JbootrpcServiceConfig implements Serializable { /** - * Application spring bean name + * Application bean name */ private String application; /** - * Module spring bean name + * Module bean name */ private String module; /** - * Provider spring bean name + * Provider bean name */ private String provider; /** - * Protocol spring bean names + * Protocol bean names */ private String protocol; /** - * Monitor spring bean name + * Monitor bean name */ private String monitor; /** - * Registry spring bean name + * Registry bean name */ private String registry; diff --git a/src/main/java/io/jboot/components/rpc/annotation/RPCBean.java b/src/main/java/io/jboot/components/rpc/annotation/RPCBean.java index 263ce9df..9a45ba72 100644 --- a/src/main/java/io/jboot/components/rpc/annotation/RPCBean.java +++ b/src/main/java/io/jboot/components/rpc/annotation/RPCBean.java @@ -83,32 +83,32 @@ public @interface RPCBean { /** - * Application spring bean name + * Application bean name */ String application() default ""; /** - * Module spring bean name + * Module bean name */ String module() default ""; /** - * Provider spring bean name + * Provider bean name */ String provider() default ""; /** - * Protocol spring bean names + * Protocol bean names */ String[] protocol() default {}; /** - * Monitor spring bean name + * Monitor bean name */ String monitor() default ""; /** - * Registry spring bean name + * Registry bean name */ String[] registry() default {}; -- Gitee From c4e6e7b04817053b7ae3f11613f4f10fce85c321 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 31 Mar 2020 15:10:29 +0800 Subject: [PATCH 0178/1965] v3.1.5 --- .../components/rpc/motan/JbootMotanrpc.java | 14 ++-- .../jboot/components/rpc/motan/MotanUtil.java | 66 ++++++++++++------- .../components/rpc/motan/MotanrpcConfig.java | 56 ++++++++++++++++ 3 files changed, 108 insertions(+), 28 deletions(-) create mode 100644 src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java diff --git a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java index 0a7639fc..9cb59126 100644 --- a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java +++ b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java @@ -19,6 +19,7 @@ import com.weibo.api.motan.common.MotanConstants; import com.weibo.api.motan.config.RefererConfig; import com.weibo.api.motan.config.ServiceConfig; import com.weibo.api.motan.util.MotanSwitcherUtil; +import io.jboot.Jboot; import io.jboot.components.rpc.JbootrpcBase; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; @@ -27,6 +28,8 @@ import io.jboot.utils.StrUtil; public class JbootMotanrpc extends JbootrpcBase { + private MotanrpcConfig defaultConfig = Jboot.config(MotanrpcConfig.class); + @Override public void onStart() { MotanUtil.initMotan(); @@ -39,12 +42,12 @@ public class JbootMotanrpc extends JbootrpcBase { referer.setInterface(interfaceClass); String directUrl = rpcConfig.getUrl(interfaceClass.getName()); - if (StrUtil.isNotBlank(directUrl)){ + if (StrUtil.isNotBlank(directUrl)) { referer.setDirectUrl(directUrl); } String consumer = rpcConfig.getConsumer(interfaceClass.getName()); - if (consumer != null){ + if (consumer != null) { referer.setBasicReferer(MotanUtil.getBaseReferer(consumer)); } @@ -62,9 +65,12 @@ public class JbootMotanrpc extends JbootrpcBase { ServiceConfig serviceConfig = MotanUtil.toServiceConfig(config); serviceConfig.setInterface(interfaceClass); serviceConfig.setRef((T) object); + serviceConfig.setShareChannel(true); + serviceConfig.setExport(defaultConfig.getDefaultExport()); + serviceConfig.setHost(defaultConfig.getDefaultHost()); String provider = rpcConfig.getProvider(interfaceClass.getName()); - if (provider != null){ + if (provider != null) { serviceConfig.setBasicService(MotanUtil.getBaseService(provider)); } @@ -76,6 +82,4 @@ public class JbootMotanrpc extends JbootrpcBase { } - - } diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java index 6dfa3aee..85a79d81 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java @@ -16,6 +16,7 @@ package io.jboot.components.rpc.motan; import com.weibo.api.motan.config.*; +import com.weibo.api.motan.util.MotanFrameworkUtil; import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; @@ -46,12 +47,16 @@ public class MotanUtil { Map protocolConfigs = configs(ProtocolConfig.class, "jboot.rpc.motan.protocol"); if (protocolConfigs != null && !protocolConfigs.isEmpty()) { protocolConfigMap.putAll(protocolConfigs); + } else { + protocolConfigMap.put("default", MotanFrameworkUtil.getDefaultProtocolConfig()); } //registry 配置 Map registryConfigs = configs(RegistryConfig.class, "jboot.rpc.motan.registry"); if (registryConfigs != null && !registryConfigs.isEmpty()) { registryConfigMap.putAll(registryConfigs); + } else { + registryConfigMap.put("default", MotanFrameworkUtil.getDefaultRegistryConfig()); } //methodConfig 配置 @@ -60,9 +65,9 @@ public class MotanUtil { //baseService 配置 Map serviceConfigs = configs(BasicServiceInterfaceConfig.class, "jboot.rpc.motan.service"); - Utils.setChildConfig(serviceConfigs,methodConfigs,"jboot.rpc.motan.service","method"); - Utils.setChildConfig(serviceConfigs,protocolConfigs,"jboot.rpc.motan.service","protocol"); - Utils.setChildConfig(serviceConfigs,registryConfigs,"jboot.rpc.motan.service","registry"); + Utils.setChildConfig(serviceConfigs, methodConfigs, "jboot.rpc.motan.service", "method"); + Utils.setChildConfig(serviceConfigs, protocolConfigs, "jboot.rpc.motan.service", "protocol"); + Utils.setChildConfig(serviceConfigs, registryConfigs, "jboot.rpc.motan.service", "registry"); if (serviceConfigs != null && !serviceConfigs.isEmpty()) { @@ -71,9 +76,9 @@ public class MotanUtil { //baseReferer 配置 Map refererConfigs = configs(BasicRefererInterfaceConfig.class, "jboot.rpc.motan.referer"); - Utils.setChildConfig(refererConfigs,methodConfigs,"jboot.rpc.motan.referer","method"); - Utils.setChildConfig(refererConfigs,protocolConfigs,"jboot.rpc.motan.referer","protocol"); - Utils.setChildConfig(refererConfigs,registryConfigs,"jboot.rpc.motan.referer","registry"); + Utils.setChildConfig(refererConfigs, methodConfigs, "jboot.rpc.motan.referer", "method"); + Utils.setChildConfig(refererConfigs, protocolConfigs, "jboot.rpc.motan.referer", "protocol"); + Utils.setChildConfig(refererConfigs, registryConfigs, "jboot.rpc.motan.referer", "registry"); if (refererConfigs != null && !refererConfigs.isEmpty()) { baseRefererConfigMap.putAll(refererConfigs); @@ -92,32 +97,35 @@ public class MotanUtil { if (StrUtil.isNotBlank(rc.getProtocol())) { List protocolConfigs = new ArrayList<>(); Set protocolNames = StrUtil.splitToSetByComma(rc.getRegistry()); - for (String protocalName : protocolNames){ + for (String protocalName : protocolNames) { ProtocolConfig registryConfig = protocolConfigMap.get(protocalName); - if (registryConfig != null){ + if (registryConfig != null) { protocolConfigs.add(registryConfig); } } - if (!protocolConfigs.isEmpty()){ + if (!protocolConfigs.isEmpty()) { refererConfig.setProtocols(protocolConfigs); } + } else { + refererConfig.setProtocols(toList(protocolConfigMap)); } - //referer registry if (StrUtil.isNotBlank(rc.getRegistry())) { List registryConfigs = new ArrayList<>(); Set registryNames = StrUtil.splitToSetByComma(rc.getRegistry()); - for (String registryName : registryNames){ + for (String registryName : registryNames) { RegistryConfig registryConfig = registryConfigMap.get(registryName); - if (registryConfig != null){ + if (registryConfig != null) { registryConfigs.add(registryConfig); } } - if (!registryConfigs.isEmpty()){ + if (!registryConfigs.isEmpty()) { refererConfig.setRegistries(registryConfigs); } + } else { + refererConfig.setRegistries(toList(registryConfigMap)); } @@ -130,37 +138,39 @@ public class MotanUtil { Utils.copyFields(sc, serviceConfig); - //service protocol if (StrUtil.isNotBlank(sc.getProtocol())) { List protocolConfigs = new ArrayList<>(); Set protocolNames = StrUtil.splitToSetByComma(sc.getRegistry()); - for (String protocalName : protocolNames){ + for (String protocalName : protocolNames) { ProtocolConfig registryConfig = protocolConfigMap.get(protocalName); - if (registryConfig != null){ + if (registryConfig != null) { protocolConfigs.add(registryConfig); } } - if (!protocolConfigs.isEmpty()){ + if (!protocolConfigs.isEmpty()) { serviceConfig.setProtocols(protocolConfigs); } + } else { + serviceConfig.setProtocols(toList(protocolConfigMap)); } - //service registry if (StrUtil.isNotBlank(sc.getRegistry())) { List registryConfigs = new ArrayList<>(); Set registryNames = StrUtil.splitToSetByComma(sc.getRegistry()); - for (String registryName : registryNames){ + for (String registryName : registryNames) { RegistryConfig registryConfig = registryConfigMap.get(registryName); - if (registryConfig != null){ + if (registryConfig != null) { registryConfigs.add(registryConfig); } } - if (!registryConfigs.isEmpty()){ + if (!registryConfigs.isEmpty()) { serviceConfig.setRegistries(registryConfigs); } + } else { + serviceConfig.setRegistries(toList(registryConfigMap)); } @@ -168,16 +178,26 @@ public class MotanUtil { } - public static BasicRefererInterfaceConfig getBaseReferer(String name){ + public static BasicRefererInterfaceConfig getBaseReferer(String name) { return baseRefererConfigMap.get(name); } - public static BasicServiceInterfaceConfig getBaseService(String name){ + public static BasicServiceInterfaceConfig getBaseService(String name) { return baseServiceConfigMap.get(name); } private static Map configs(Class clazz, String prefix) { return JbootConfigUtil.getConfigModels(clazz, prefix); } + + private static List toList(Map map) { + List list = new ArrayList<>(map.size()); + for (Map.Entry entry : map.entrySet()) { + AbstractConfig config = (AbstractConfig) entry.getValue(); + config.setId(entry.getKey()); + list.add((T) config); + } + return list; + } } diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java b/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java new file mode 100644 index 00000000..b739251e --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.rpc.motan; + +import io.jboot.app.config.annotation.ConfigModel; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/31 + * @see com.weibo.api.motan.config.AbstractServiceConfig + */ +@ConfigModel(prefix = "jboot.rpc.motan") +public class MotanrpcConfig { + + /** + * 一个service可以按多个protocol提供服务,不同protocol使用不同port 利用export来设置protocol和port,格式如下: + * protocol1:port1,protocol2:port2 + **/ + protected String defaultExport; + + + /** + * 一般不用设置,由服务自己获取,但如果有多个ip,而只想用指定ip,则可以在此处指定 + */ + protected String defaultHost; + + + public String getDefaultExport() { + return defaultExport; + } + + public void setDefaultExport(String defaultExport) { + this.defaultExport = defaultExport; + } + + public String getDefaultHost() { + return defaultHost; + } + + public void setDefaultHost(String defaultHost) { + this.defaultHost = defaultHost; + } +} -- Gitee From 080bf46ba0b265b55c14fda575a565e3b09f2bd1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 31 Mar 2020 15:16:17 +0800 Subject: [PATCH 0179/1965] v3.1.5 --- .../components/rpc/motan/JbootMotanrpc.java | 4 +- .../components/rpc/motan/MotanrpcConfig.java | 44 ++++++++++++++++++- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java index 9cb59126..f9c1c4b4 100644 --- a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java +++ b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java @@ -66,8 +66,8 @@ public class JbootMotanrpc extends JbootrpcBase { serviceConfig.setInterface(interfaceClass); serviceConfig.setRef((T) object); serviceConfig.setShareChannel(true); - serviceConfig.setExport(defaultConfig.getDefaultExport()); - serviceConfig.setHost(defaultConfig.getDefaultHost()); + serviceConfig.setExport(defaultConfig.getExport(interfaceClass.getName())); + serviceConfig.setHost(defaultConfig.getHost(interfaceClass.getName())); String provider = rpcConfig.getProvider(interfaceClass.getName()); if (provider != null) { diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java b/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java index b739251e..1f96175f 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java @@ -16,6 +16,9 @@ package io.jboot.components.rpc.motan; import io.jboot.app.config.annotation.ConfigModel; +import io.jboot.utils.StrUtil; + +import java.util.Map; /** * @author michael yang (fuhai999@gmail.com) @@ -29,13 +32,24 @@ public class MotanrpcConfig { * 一个service可以按多个protocol提供服务,不同protocol使用不同port 利用export来设置protocol和port,格式如下: * protocol1:port1,protocol2:port2 **/ - protected String defaultExport; + private String defaultExport; /** * 一般不用设置,由服务自己获取,但如果有多个ip,而只想用指定ip,则可以在此处指定 */ - protected String defaultHost; + private String defaultHost; + + + /** + * motan 对每个 service 设置的 export + */ + private Map exports; + + /** + * 某个服务暴露自己的主机 + */ + private Map hosts; public String getDefaultExport() { @@ -53,4 +67,30 @@ public class MotanrpcConfig { public void setDefaultHost(String defaultHost) { this.defaultHost = defaultHost; } + + public Map getExports() { + return exports; + } + + public void setExports(Map exports) { + this.exports = exports; + } + + public String getExport(String className) { + String export = exports == null ? null : exports.get(className); + return StrUtil.isNotBlank(export) ? export : defaultExport; + } + + public Map getHosts() { + return hosts; + } + + public void setHosts(Map hosts) { + this.hosts = hosts; + } + + public String getHost(String className) { + String host = hosts == null ? null : hosts.get(className); + return StrUtil.isNotBlank(host) ? host : defaultHost; + } } -- Gitee From 7b1789b7dfb8e9d8a110315737ea1c675f4c0378 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 31 Mar 2020 15:16:44 +0800 Subject: [PATCH 0180/1965] add demo for motan rpc --- .../test/rpc/dubbo/DubboInterceptor.java | 3 +- .../io/jboot/test/rpc/motan/MotanClient.java | 59 +++++++++++++++++++ .../test/rpc/motan/MotanInterceptor.java | 39 ++++++++++++ .../io/jboot/test/rpc/motan/MotanServer.java | 53 +++++++++++++++++ 4 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 src/test/java/io/jboot/test/rpc/motan/MotanClient.java create mode 100644 src/test/java/io/jboot/test/rpc/motan/MotanInterceptor.java create mode 100644 src/test/java/io/jboot/test/rpc/motan/MotanServer.java diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java index 6c84c73f..999ce946 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java @@ -17,7 +17,6 @@ package io.jboot.test.rpc.dubbo; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; -import io.jboot.components.rpc.annotation.RPCInject; import io.jboot.test.rpc.commons.BlogService; /** @@ -26,7 +25,7 @@ import io.jboot.test.rpc.commons.BlogService; */ public class DubboInterceptor implements Interceptor { - @RPCInject(check = false) +// @RPCInject(check = false) private BlogService blogService; @Override diff --git a/src/test/java/io/jboot/test/rpc/motan/MotanClient.java b/src/test/java/io/jboot/test/rpc/motan/MotanClient.java new file mode 100644 index 00000000..a85bed2a --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/motan/MotanClient.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.test.rpc.motan; + + +import com.jfinal.aop.Before; +import io.jboot.app.JbootApplication; +import io.jboot.components.rpc.annotation.RPCInject; +import io.jboot.test.rpc.commons.BlogService; +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; + +@RequestMapping("/motan") +public class MotanClient extends JbootController { + + public static void main(String[] args) { + + + //Undertow端口号配置 + JbootApplication.setBootArg("undertow.port", "9999"); + + //RPC配置 + JbootApplication.setBootArg("jboot.rpc.type", "motan"); + JbootApplication.setBootArg("jboot.rpc.autoExportEnable", false); + + //设置直连模式,方便调试,默认为注册中心 + JbootApplication.setBootArg("jboot.rpc.urls", "io.jboot.test.rpc.commons.BlogService:127.0.0.1:28080"); + + + JbootApplication.run(args); + } + + + @RPCInject + private BlogService blogService; + + @Before(MotanInterceptor.class) + public void index() { + + System.out.println("blogService:" + blogService); + + renderText("blogId : " + blogService.findById()); + } + + +} diff --git a/src/test/java/io/jboot/test/rpc/motan/MotanInterceptor.java b/src/test/java/io/jboot/test/rpc/motan/MotanInterceptor.java new file mode 100644 index 00000000..2b14b072 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/motan/MotanInterceptor.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.test.rpc.motan; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import io.jboot.components.rpc.annotation.RPCInject; +import io.jboot.test.rpc.commons.BlogService; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/24 + */ +public class MotanInterceptor implements Interceptor { + + @RPCInject(check = false) + private BlogService blogService; + + @Override + public void intercept(Invocation inv) { + + System.out.println("intercept : " + blogService); + + inv.invoke(); + } +} diff --git a/src/test/java/io/jboot/test/rpc/motan/MotanServer.java b/src/test/java/io/jboot/test/rpc/motan/MotanServer.java new file mode 100644 index 00000000..f3e8bbc7 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/motan/MotanServer.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.test.rpc.motan; + + +import io.jboot.app.JbootApplication; +import io.jboot.app.JbootRpcApplication; + +public class MotanServer { + + public static void main(String[] args) throws InterruptedException { + + + JbootApplication.setBootArg("jboot.rpc.type", "motan"); + + // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, + // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 + JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); + +// JbootApplication.setBootArg("jboot.rpc.motan.registry.regProtocol", "local"); +// +// //motan 的通信协议配置 +// JbootApplication.setBootArg("jboot.rpc.motan.protocol.name", "motan"); +// JbootApplication.setBootArg("jboot.rpc.motan.protocol.port", "28080"); +// JbootApplication.setBootArg("jboot.rpc.motan.service.export", "motan:28080"); + JbootApplication.setBootArg("jboot.rpc.motan.defaultExport", "default:28080"); +// JbootApplication.setBootArg("jboot.rpc.motan.default.host", "127.0.0.1"); + + + + + + + + JbootRpcApplication.run(args); + + System.out.println("DubboServer started..."); + + } +} -- Gitee From f2b06c78371e3a5ea093cf2fdfeca724ac265c0e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 31 Mar 2020 15:33:13 +0800 Subject: [PATCH 0181/1965] add demo for motan rpc --- .../io/jboot/test/rpc/motan/MotanServer.java | 15 ++----- .../MotanClientZookeeperDemo.java | 39 +++++++++++++++++++ .../MotanServer1ZookeeperDemo.java | 35 +++++++++++++++++ .../MotanServer2ZookeeperDemo.java | 36 +++++++++++++++++ 4 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 src/test/java/io/jboot/test/rpc/motanzookeeper/MotanClientZookeeperDemo.java create mode 100644 src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer1ZookeeperDemo.java create mode 100644 src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer2ZookeeperDemo.java diff --git a/src/test/java/io/jboot/test/rpc/motan/MotanServer.java b/src/test/java/io/jboot/test/rpc/motan/MotanServer.java index f3e8bbc7..7e324864 100644 --- a/src/test/java/io/jboot/test/rpc/motan/MotanServer.java +++ b/src/test/java/io/jboot/test/rpc/motan/MotanServer.java @@ -30,24 +30,15 @@ public class MotanServer { // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); -// JbootApplication.setBootArg("jboot.rpc.motan.registry.regProtocol", "local"); -// -// //motan 的通信协议配置 -// JbootApplication.setBootArg("jboot.rpc.motan.protocol.name", "motan"); -// JbootApplication.setBootArg("jboot.rpc.motan.protocol.port", "28080"); -// JbootApplication.setBootArg("jboot.rpc.motan.service.export", "motan:28080"); + // motan 与 dubbo 不一样,motan 需要配置 export, + // export 配置内容为 协议ID:端口号,默认的协议 id 为 default JbootApplication.setBootArg("jboot.rpc.motan.defaultExport", "default:28080"); -// JbootApplication.setBootArg("jboot.rpc.motan.default.host", "127.0.0.1"); - - - - JbootRpcApplication.run(args); - System.out.println("DubboServer started..."); + System.out.println("MotanServer started..."); } } diff --git a/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanClientZookeeperDemo.java b/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanClientZookeeperDemo.java new file mode 100644 index 00000000..a30c1396 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanClientZookeeperDemo.java @@ -0,0 +1,39 @@ +package io.jboot.test.rpc.motanzookeeper; + +import io.jboot.app.JbootApplication; +import io.jboot.components.rpc.annotation.RPCInject; +import io.jboot.test.rpc.commons.BlogService; +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; + +@RequestMapping("/motanzk") +public class MotanClientZookeeperDemo extends JbootController { + + public static void main(String[] args) { + + //jboot端口号配置 + JbootApplication.setBootArg("undertow.port", "9999"); + + JbootApplication.setBootArg("jboot.rpc.type", "motan"); + + // motan 的注册中心的协议 + JbootApplication.setBootArg("jboot.rpc.motan.registry.regProtocol", "zookeeper"); + // 注册中心地址,即zookeeper的地址 + JbootApplication.setBootArg("jboot.rpc.motan.registry.address", "127.0.0.1:2181"); + + JbootApplication.run(args); + } + + + @RPCInject + private BlogService blogService; + + public void index() { + + System.out.println("MotanClientZookeeperDemo.index()"); + + System.out.println(blogService); + + renderText(blogService.findById()); + } +} diff --git a/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer1ZookeeperDemo.java b/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer1ZookeeperDemo.java new file mode 100644 index 00000000..595348f8 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer1ZookeeperDemo.java @@ -0,0 +1,35 @@ +package io.jboot.test.rpc.motanzookeeper; + + +import io.jboot.app.JbootApplication; +import io.jboot.app.JbootRpcApplication; + +public class MotanServer1ZookeeperDemo { + + public static void main(String[] args) { + + // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, + // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 + JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); + JbootApplication.setBootArg("jboot.rpc.type", "motan"); + + + // motan 的注册中心的协议 + JbootApplication.setBootArg("jboot.rpc.motan.registry.regProtocol", "zookeeper"); + //注册中心地址,即zookeeper的地址 + JbootApplication.setBootArg("jboot.rpc.motan.registry.address", "127.0.0.1:2181"); + + + //export + JbootApplication.setBootArg("jboot.rpc.motan.defaultExport", "default:28080"); + + + + JbootRpcApplication.run(args); + + + System.out.println("MotanServer1ZookeeperDemo started..."); + + + } +} diff --git a/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer2ZookeeperDemo.java b/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer2ZookeeperDemo.java new file mode 100644 index 00000000..b18ea36b --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer2ZookeeperDemo.java @@ -0,0 +1,36 @@ +package io.jboot.test.rpc.motanzookeeper; + + +import io.jboot.app.JbootApplication; +import io.jboot.app.JbootRpcApplication; + +public class MotanServer2ZookeeperDemo { + + public static void main(String[] args) { + + + // 开启 @RPCBean 自动暴露功能,默认情况下是开启的,无需配置, + // 但是此测试代码的 jboot.properties 文件关闭了,这里需要开启下 + JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true); + JbootApplication.setBootArg("jboot.rpc.type", "motan"); + + + + // motan 的注册中心的协议 + JbootApplication.setBootArg("jboot.rpc.motan.registry.regProtocol", "zookeeper"); + //注册中心地址,即zookeeper的地址 + JbootApplication.setBootArg("jboot.rpc.motan.registry.address", "127.0.0.1:2181"); + + + //export + JbootApplication.setBootArg("jboot.rpc.motan.defaultExport", "default:28081"); + + JbootRpcApplication.run(args); + + + + System.out.println("MotanServer2ZookeeperDemo started..."); + + + } +} -- Gitee From 6fe63e2e3f9df444713315e1a5d7a6a5875ef9a4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 1 Apr 2020 08:32:19 +0800 Subject: [PATCH 0182/1965] v3.1.5 --- src/main/java/io/jboot/utils/ClassScanner.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 00b49387..8139e3a8 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -407,7 +407,7 @@ public class ClassScanner { if (tomcatApiJarFile.exists() || tomcatJuliJarFile.exists()) { tomcatClassPath = tomcatApiJarFile .getParentFile() - .getParentFile().getAbsolutePath(); + .getParentFile().getAbsolutePath().replace('\\', '/'); continue; } @@ -519,10 +519,9 @@ public class ClassScanner { if (!path.toLowerCase().endsWith(".jar")) { classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); - continue; + } else { + jarPaths.add(path.replace('\\', '/')); } - - jarPaths.add(path.replace('\\', '/')); } } } catch (Exception ex) { @@ -584,8 +583,7 @@ public class ClassScanner { } //from jre lib - if (path.contains("/jre/lib") - || path.contains("\\jre\\lib")) { + if (path.contains("/jre/lib")) { return false; } @@ -631,8 +629,11 @@ public class ClassScanner { private static String getJavaHome() { if (javaHome == null) { try { - javaHome = new File(System.getProperty("java.home"), "..").getCanonicalPath(); - } catch (IOException e) { + String javaHomeString = System.getProperty("java.home"); + if (javaHomeString != null && javaHomeString.trim().length() > 0) { + javaHome = new File(javaHomeString, "..").getCanonicalPath().replace('\\', '/'); + } + } catch (Exception e) { e.printStackTrace(); } } -- Gitee From 5b8a090002d1eb221aba2b5abc8097456fc60d31 Mon Sep 17 00:00:00 2001 From: wangjun Date: Wed, 1 Apr 2020 11:02:07 +0800 Subject: [PATCH 0183/1965] =?UTF-8?q?JbootJson=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=A9=BC=E5=B3=B0=E5=BC=8FJsonKey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/web/JbootJson.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index 870625a1..d500754a 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -17,6 +17,7 @@ package io.jboot.web; import com.alibaba.fastjson.JSON; import com.jfinal.json.JFinalJson; +import com.jfinal.kit.StrKit; import java.util.Iterator; import java.util.Map; @@ -28,7 +29,23 @@ public class JbootJson extends JFinalJson { @Override protected String mapToJson(Map map, int depth) { optimizeMapAttrs(map); - return map == null || map.isEmpty() ? "null" : super.mapToJson(map, depth); + + StringBuilder sb = new StringBuilder(); + boolean first = true; + Iterator iter = map.entrySet().iterator(); + + sb.append('{'); + while(iter.hasNext()){ + if(first) + first = false; + else + sb.append(','); + + Map.Entry entry = (Map.Entry)iter.next(); + toKeyValue(StrKit.toCamelCase(String.valueOf(entry.getKey())), entry.getValue(), sb, depth); + } + sb.append('}'); + return sb.toString(); } -- Gitee From 9398abb2be454696adf8b22bc6a9e102f8f56434 Mon Sep 17 00:00:00 2001 From: wangjun Date: Wed, 1 Apr 2020 11:32:22 +0800 Subject: [PATCH 0184/1965] =?UTF-8?q?JbootJson=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=A9=BC=E5=B3=B0=E5=BC=8FJsonKey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/web/JbootJson.java | 10 +++++++++- src/main/java/io/jboot/web/JbootWebConfig.java | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index d500754a..af365935 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -18,6 +18,7 @@ package io.jboot.web; import com.alibaba.fastjson.JSON; import com.jfinal.json.JFinalJson; import com.jfinal.kit.StrKit; +import io.jboot.Jboot; import java.util.Iterator; import java.util.Map; @@ -30,6 +31,13 @@ public class JbootJson extends JFinalJson { protected String mapToJson(Map map, int depth) { optimizeMapAttrs(map); + if(Jboot.config(JbootWebConfig.class).getCamelCaseJsonStyleEnable()){ + return toCamelCase(map, depth); + } + return map == null || map.isEmpty() ? "null" : super.mapToJson(map, depth); + } + + private String toCamelCase(Map map, int depth){ StringBuilder sb = new StringBuilder(); boolean first = true; Iterator iter = map.entrySet().iterator(); @@ -42,7 +50,7 @@ public class JbootJson extends JFinalJson { sb.append(','); Map.Entry entry = (Map.Entry)iter.next(); - toKeyValue(StrKit.toCamelCase(String.valueOf(entry.getKey())), entry.getValue(), sb, depth); + toKeyValue(StrKit.toCamelCase(String.valueOf(entry.getKey())),entry.getValue(), sb, depth); } sb.append('}'); return sb.toString(); diff --git a/src/main/java/io/jboot/web/JbootWebConfig.java b/src/main/java/io/jboot/web/JbootWebConfig.java index da22e047..74048a4b 100644 --- a/src/main/java/io/jboot/web/JbootWebConfig.java +++ b/src/main/java/io/jboot/web/JbootWebConfig.java @@ -30,6 +30,7 @@ public class JbootWebConfig { private String cookieEncryptKey = DEFAULT_COOKIE_ENCRYPT_KEY; private String webSocketEndpoint; + private Boolean camelCaseJsonStyleEnable = false; public String getCookieEncryptKey() { return cookieEncryptKey; @@ -46,4 +47,12 @@ public class JbootWebConfig { public void setWebSocketEndpoint(String webSocketEndpoint) { this.webSocketEndpoint = webSocketEndpoint; } + + public Boolean getCamelCaseJsonStyleEnable() { + return camelCaseJsonStyleEnable; + } + + public void setCamelCaseJsonStyleEnable(Boolean camelCaseJsonStyleEnable) { + this.camelCaseJsonStyleEnable = camelCaseJsonStyleEnable; + } } -- Gitee From b207fb6c463609f34d99335c3ced081a3e8e294d Mon Sep 17 00:00:00 2001 From: wangjun Date: Wed, 1 Apr 2020 11:41:56 +0800 Subject: [PATCH 0185/1965] =?UTF-8?q?JbootJson=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=A9=BC=E5=B3=B0=E5=BC=8FJsonKey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/web/JbootJson.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index af365935..45e2a11c 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -26,12 +26,20 @@ import java.util.Map; public class JbootJson extends JFinalJson { + private static JbootWebConfig config; + + public static JbootWebConfig getConfig() { + if (config == null) { + config = Jboot.config(JbootWebConfig.class); + } + return config; + } @Override protected String mapToJson(Map map, int depth) { optimizeMapAttrs(map); - if(Jboot.config(JbootWebConfig.class).getCamelCaseJsonStyleEnable()){ + if(getConfig().getCamelCaseJsonStyleEnable()){ return toCamelCase(map, depth); } return map == null || map.isEmpty() ? "null" : super.mapToJson(map, depth); -- Gitee From 1a3d05b7e4a175920b9ada4e812e0a8a7deaf085 Mon Sep 17 00:00:00 2001 From: wangjun Date: Wed, 1 Apr 2020 11:45:36 +0800 Subject: [PATCH 0186/1965] =?UTF-8?q?JbootJson=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=A9=BC=E5=B3=B0=E5=BC=8FJsonKey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/web/JbootWebConfig.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/web/JbootWebConfig.java b/src/main/java/io/jboot/web/JbootWebConfig.java index 74048a4b..36bf5488 100644 --- a/src/main/java/io/jboot/web/JbootWebConfig.java +++ b/src/main/java/io/jboot/web/JbootWebConfig.java @@ -30,7 +30,7 @@ public class JbootWebConfig { private String cookieEncryptKey = DEFAULT_COOKIE_ENCRYPT_KEY; private String webSocketEndpoint; - private Boolean camelCaseJsonStyleEnable = false; + private boolean camelCaseJsonStyleEnable = false; public String getCookieEncryptKey() { return cookieEncryptKey; @@ -48,11 +48,11 @@ public class JbootWebConfig { this.webSocketEndpoint = webSocketEndpoint; } - public Boolean getCamelCaseJsonStyleEnable() { + public boolean getCamelCaseJsonStyleEnable() { return camelCaseJsonStyleEnable; } - public void setCamelCaseJsonStyleEnable(Boolean camelCaseJsonStyleEnable) { + public void setCamelCaseJsonStyleEnable(boolean camelCaseJsonStyleEnable) { this.camelCaseJsonStyleEnable = camelCaseJsonStyleEnable; } } -- Gitee From 4b48754ef730a56813e5cf707932265be8efc0c8 Mon Sep 17 00:00:00 2001 From: wangjun Date: Wed, 1 Apr 2020 11:46:45 +0800 Subject: [PATCH 0187/1965] =?UTF-8?q?JbootJson=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=A9=BC=E5=B3=B0=E5=BC=8FJsonKey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/web/JbootJson.java | 2 +- src/main/java/io/jboot/web/JbootWebConfig.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index 45e2a11c..f01b5b05 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -39,7 +39,7 @@ public class JbootJson extends JFinalJson { protected String mapToJson(Map map, int depth) { optimizeMapAttrs(map); - if(getConfig().getCamelCaseJsonStyleEnable()){ + if(getConfig().isCamelCaseJsonStyleEnable()){ return toCamelCase(map, depth); } return map == null || map.isEmpty() ? "null" : super.mapToJson(map, depth); diff --git a/src/main/java/io/jboot/web/JbootWebConfig.java b/src/main/java/io/jboot/web/JbootWebConfig.java index 36bf5488..287f5855 100644 --- a/src/main/java/io/jboot/web/JbootWebConfig.java +++ b/src/main/java/io/jboot/web/JbootWebConfig.java @@ -48,7 +48,7 @@ public class JbootWebConfig { this.webSocketEndpoint = webSocketEndpoint; } - public boolean getCamelCaseJsonStyleEnable() { + public boolean isCamelCaseJsonStyleEnable() { return camelCaseJsonStyleEnable; } -- Gitee From b321c4928739e554ab7e92a95b38612d6da5c097 Mon Sep 17 00:00:00 2001 From: wangjun Date: Wed, 1 Apr 2020 11:58:10 +0800 Subject: [PATCH 0188/1965] =?UTF-8?q?JbootJson=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=A9=BC=E5=B3=B0=E5=BC=8FJsonKey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/web/JbootJson.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index f01b5b05..77223329 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -26,20 +26,13 @@ import java.util.Map; public class JbootJson extends JFinalJson { - private static JbootWebConfig config; - - public static JbootWebConfig getConfig() { - if (config == null) { - config = Jboot.config(JbootWebConfig.class); - } - return config; - } + private static boolean isCamelCaseJsonStyleEnable = Jboot.config(JbootWebConfig.class).isCamelCaseJsonStyleEnable(); @Override protected String mapToJson(Map map, int depth) { optimizeMapAttrs(map); - if(getConfig().isCamelCaseJsonStyleEnable()){ + if(isCamelCaseJsonStyleEnable){ return toCamelCase(map, depth); } return map == null || map.isEmpty() ? "null" : super.mapToJson(map, depth); -- Gitee From a2538c800bcb5256d0b683ee07f6bf1240eaa5ab Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 2 Apr 2020 09:29:24 +0800 Subject: [PATCH 0189/1965] v3.1.5 --- src/main/java/io/jboot/utils/ClassScanner.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 8139e3a8..7875f5d9 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -213,6 +213,7 @@ public class ClassScanner { excludeJars.add("lombok-"); excludeJars.add("hutool-"); excludeJars.add("jakarta."); + excludeJars.add("protostuff-"); } @@ -276,6 +277,8 @@ public class ClassScanner { addUnscanClass("lombok."); addUnscanClass("net.oschina.j2cache"); addUnscanClass("cn.hutool."); + addUnscanClass("com.dyuproject."); + addUnscanClass("io.protostuff."); } static { -- Gitee From 88396ade7ec4b13c1f3b99ccf1441301e50e01f4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 2 Apr 2020 13:39:23 +0800 Subject: [PATCH 0190/1965] v3.1.5 --- src/main/java/io/jboot/utils/ClassScanner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 7875f5d9..1fce8cb1 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -51,7 +51,7 @@ public class ClassScanner { static { excludeJars.add("jfinal-"); - excludeJars.add("cos-20"); + excludeJars.add("cos-"); excludeJars.add("cglib-"); excludeJars.add("undertow-"); excludeJars.add("xnio-"); -- Gitee From 05b19f6c7ea96f16436c73acd75306876dac4159 Mon Sep 17 00:00:00 2001 From: yangyao Date: Thu, 2 Apr 2020 14:43:27 +0800 Subject: [PATCH 0191/1965] =?UTF-8?q?restful=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../restful/DefaultRestfulErrorRender.java | 109 +++++ .../jboot/components/restful/HttpStatus.java | 405 ++++++++++++++++++ .../restful/JbootRestfulManager.java | 237 ++++++++++ .../components/restful/ResponseEntity.java | 67 +++ .../components/restful/RestfulAction.java | 47 ++ .../components/restful/RestfulCallback.java | 29 ++ .../restful/RestfulErrorRender.java | 35 ++ .../components/restful/RestfulHandler.java | 234 ++++++++++ .../components/restful/RestfulInvocation.java | 19 + .../components/restful/RestfulUtils.java | 201 +++++++++ .../restful/annotation/DeleteMapping.java | 19 + .../restful/annotation/DownloadResponse.java | 12 + .../restful/annotation/GetMapping.java | 16 + .../restful/annotation/PathVariable.java | 27 ++ .../restful/annotation/PostMapping.java | 16 + .../restful/annotation/PutMapping.java | 19 + .../restful/annotation/RequestBody.java | 20 + .../restful/annotation/RequestHeader.java | 29 ++ .../restful/annotation/RequestParam.java | 29 ++ .../restful/annotation/ResponseHeader.java | 17 + .../restful/annotation/ResponseHeaders.java | 12 + .../restful/annotation/RestController.java | 12 + .../ParameterNullErrorException.java | 24 ++ .../ParameterParseErrorException.java | 39 ++ .../RequestMethodErrorException.java | 39 ++ .../jboot/test/restful/RestfulController.java | 117 +++++ .../io/jboot/test/restful/RestfulService.java | 5 + .../test/restful/RestfulServiceImpl.java | 12 + 28 files changed, 1847 insertions(+) create mode 100644 src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java create mode 100644 src/main/java/io/jboot/components/restful/HttpStatus.java create mode 100644 src/main/java/io/jboot/components/restful/JbootRestfulManager.java create mode 100644 src/main/java/io/jboot/components/restful/ResponseEntity.java create mode 100644 src/main/java/io/jboot/components/restful/RestfulAction.java create mode 100644 src/main/java/io/jboot/components/restful/RestfulCallback.java create mode 100644 src/main/java/io/jboot/components/restful/RestfulErrorRender.java create mode 100644 src/main/java/io/jboot/components/restful/RestfulHandler.java create mode 100644 src/main/java/io/jboot/components/restful/RestfulInvocation.java create mode 100644 src/main/java/io/jboot/components/restful/RestfulUtils.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/DeleteMapping.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/DownloadResponse.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/GetMapping.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/PathVariable.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/PostMapping.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/PutMapping.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/RequestBody.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/RequestHeader.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/RequestParam.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/ResponseHeader.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/ResponseHeaders.java create mode 100644 src/main/java/io/jboot/components/restful/annotation/RestController.java create mode 100644 src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java create mode 100644 src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java create mode 100644 src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java create mode 100644 src/test/java/io/jboot/test/restful/RestfulController.java create mode 100644 src/test/java/io/jboot/test/restful/RestfulService.java create mode 100644 src/test/java/io/jboot/test/restful/RestfulServiceImpl.java diff --git a/src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java b/src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java new file mode 100644 index 00000000..1ba7e278 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java @@ -0,0 +1,109 @@ +package io.jboot.components.restful; + +import com.jfinal.core.ActionException; +import com.jfinal.log.Log; +import com.jfinal.render.Render; +import com.jfinal.render.RenderException; +import com.jfinal.render.RenderManager; +import io.jboot.components.restful.exception.ParameterNullErrorException; +import io.jboot.components.restful.exception.ParameterParseErrorException; +import io.jboot.components.restful.exception.RequestMethodErrorException; +import io.jboot.web.handler.JbootActionHandler; + +import java.io.Serializable; + +/** + * 默认的restful错误响应处理器 + */ +public class DefaultRestfulErrorRender extends RestfulErrorRender { + + private static final Log log = Log.getLog(JbootActionHandler.class); + + public static class Error implements Serializable { + private String errorClass; + private int code; + private String message; + + public Error(String errorClass, int code, String message) { + this.errorClass = errorClass; + this.code = code; + this.message = message; + } + + public String getErrorClass() { + return errorClass; + } + + public Error setErrorClass(String errorClass) { + this.errorClass = errorClass; + return this; + } + + public int getCode() { + return code; + } + + public Error setCode(int code) { + this.code = code; + return this; + } + + public String getMessage() { + return message; + } + + public Error setMessage(String message) { + this.message = message; + return this; + } + } + + public void render() { + log.error("The restful handler intercepted the error", super.getError()); + Error error = null; + if(super.getError() instanceof ParameterNullErrorException + || super.getError() instanceof ParameterParseErrorException){ + //400 + error = new Error(super.getError().getClass().getName(), + HttpStatus.BAD_REQUEST.value(), super.getError().getMessage()); + } else if(super.getError() instanceof RequestMethodErrorException){ + error = new Error(super.getError().getClass().getName(), + HttpStatus.METHOD_NOT_ALLOWED.value(), super.getError().getMessage()); + } else if(super.getError() instanceof ActionException){ + //解析错误代码 + ActionException actionException = (ActionException)super.getError(); + int errorCode = actionException.getErrorCode(); + String msg = ""; + if (errorCode == 404) { + msg = HttpStatus.NOT_FOUND.getReasonPhrase(); + } else if (errorCode == 400) { + msg = HttpStatus.BAD_REQUEST.getReasonPhrase(); + } else if (errorCode == 401) { + msg = HttpStatus.UNAUTHORIZED.getReasonPhrase(); + } else if (errorCode == 403) { + msg = HttpStatus.FORBIDDEN.getReasonPhrase(); + } else if(errorCode == 405){ + msg = HttpStatus.METHOD_NOT_ALLOWED.getReasonPhrase(); + } else { + msg = super.getError().getMessage(); + } + error = new Error(super.getError().getClass().getName(), + errorCode, msg); + } else if(super.getError() instanceof RenderException){ + //500 + error = new Error(super.getError().getClass().getName(), + HttpStatus.INTERNAL_SERVER_ERROR.value(), super.getError().getMessage()); + } else { + //500 + error = new Error(super.getError().getClass().getName(), + HttpStatus.INTERNAL_SERVER_ERROR.value(), super.getError().getMessage()); + } + Render jsonRender = RenderManager.me().getRenderFactory().getJsonRender(error); + jsonRender.setContext(super.request, super.response, super.getAction() == null? "" : super.getAction().getViewPath()); + super.response.setStatus(error.getCode()); + jsonRender.render(); + } + + + +} diff --git a/src/main/java/io/jboot/components/restful/HttpStatus.java b/src/main/java/io/jboot/components/restful/HttpStatus.java new file mode 100644 index 00000000..aa54f020 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/HttpStatus.java @@ -0,0 +1,405 @@ +package io.jboot.components.restful; + +public enum HttpStatus { + + /** + * {@code 100 Continue}. + * @see HTTP/1.1: Semantics and Content, section 6.2.1 + */ + CONTINUE(100, "Continue"), + /** + * {@code 101 Switching Protocols}. + * @see HTTP/1.1: Semantics and Content, section 6.2.2 + */ + SWITCHING_PROTOCOLS(101, "Switching Protocols"), + /** + * {@code 102 Processing}. + * @see WebDAV + */ + PROCESSING(102, "Processing"), + /** + * {@code 103 Checkpoint}. + * @see A proposal for supporting + * resumable POST/PUT HTTP requests in HTTP/1.0 + */ + CHECKPOINT(103, "Checkpoint"), + + // 2xx Success + + /** + * {@code 200 OK}. + * @see HTTP/1.1: Semantics and Content, section 6.3.1 + */ + OK(200, "OK"), + /** + * {@code 201 Created}. + * @see HTTP/1.1: Semantics and Content, section 6.3.2 + */ + CREATED(201, "Created"), + /** + * {@code 202 Accepted}. + * @see HTTP/1.1: Semantics and Content, section 6.3.3 + */ + ACCEPTED(202, "Accepted"), + /** + * {@code 203 Non-Authoritative Information}. + * @see HTTP/1.1: Semantics and Content, section 6.3.4 + */ + NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"), + /** + * {@code 204 No Content}. + * @see HTTP/1.1: Semantics and Content, section 6.3.5 + */ + NO_CONTENT(204, "No Content"), + /** + * {@code 205 Reset Content}. + * @see HTTP/1.1: Semantics and Content, section 6.3.6 + */ + RESET_CONTENT(205, "Reset Content"), + /** + * {@code 206 Partial Content}. + * @see HTTP/1.1: Range Requests, section 4.1 + */ + PARTIAL_CONTENT(206, "Partial Content"), + /** + * {@code 207 Multi-Status}. + * @see WebDAV + */ + MULTI_STATUS(207, "Multi-Status"), + /** + * {@code 208 Already Reported}. + * @see WebDAV Binding Extensions + */ + ALREADY_REPORTED(208, "Already Reported"), + /** + * {@code 226 IM Used}. + * @see Delta encoding in HTTP + */ + IM_USED(226, "IM Used"), + + // 3xx Redirection + + /** + * {@code 300 Multiple Choices}. + * @see HTTP/1.1: Semantics and Content, section 6.4.1 + */ + MULTIPLE_CHOICES(300, "Multiple Choices"), + /** + * {@code 301 Moved Permanently}. + * @see HTTP/1.1: Semantics and Content, section 6.4.2 + */ + MOVED_PERMANENTLY(301, "Moved Permanently"), + /** + * {@code 302 Found}. + * @see HTTP/1.1: Semantics and Content, section 6.4.3 + */ + FOUND(302, "Found"), + /** + * {@code 302 Moved Temporarily}. + * @see HTTP/1.0, section 9.3 + * @deprecated in favor of {@link #FOUND} which will be returned from {@code HttpStatus.valueOf(302)} + */ + @Deprecated + MOVED_TEMPORARILY(302, "Moved Temporarily"), + /** + * {@code 303 See Other}. + * @see HTTP/1.1: Semantics and Content, section 6.4.4 + */ + SEE_OTHER(303, "See Other"), + /** + * {@code 304 Not Modified}. + * @see HTTP/1.1: Conditional Requests, section 4.1 + */ + NOT_MODIFIED(304, "Not Modified"), + /** + * {@code 305 Use Proxy}. + * @see HTTP/1.1: Semantics and Content, section 6.4.5 + * @deprecated due to security concerns regarding in-band configuration of a proxy + */ + @Deprecated + USE_PROXY(305, "Use Proxy"), + /** + * {@code 307 Temporary Redirect}. + * @see HTTP/1.1: Semantics and Content, section 6.4.7 + */ + TEMPORARY_REDIRECT(307, "Temporary Redirect"), + /** + * {@code 308 Permanent Redirect}. + * @see RFC 7238 + */ + PERMANENT_REDIRECT(308, "Permanent Redirect"), + + // --- 4xx Client Error --- + + /** + * {@code 400 Bad Request}. + * @see HTTP/1.1: Semantics and Content, section 6.5.1 + */ + BAD_REQUEST(400, "Bad Request"), + /** + * {@code 401 Unauthorized}. + * @see HTTP/1.1: Authentication, section 3.1 + */ + UNAUTHORIZED(401, "Unauthorized"), + /** + * {@code 402 Payment Required}. + * @see HTTP/1.1: Semantics and Content, section 6.5.2 + */ + PAYMENT_REQUIRED(402, "Payment Required"), + /** + * {@code 403 Forbidden}. + * @see HTTP/1.1: Semantics and Content, section 6.5.3 + */ + FORBIDDEN(403, "Forbidden"), + /** + * {@code 404 Not Found}. + * @see HTTP/1.1: Semantics and Content, section 6.5.4 + */ + NOT_FOUND(404, "Not Found"), + /** + * {@code 405 Method Not Allowed}. + * @see HTTP/1.1: Semantics and Content, section 6.5.5 + */ + METHOD_NOT_ALLOWED(405, "Method Not Allowed"), + /** + * {@code 406 Not Acceptable}. + * @see HTTP/1.1: Semantics and Content, section 6.5.6 + */ + NOT_ACCEPTABLE(406, "Not Acceptable"), + /** + * {@code 407 Proxy Authentication Required}. + * @see HTTP/1.1: Authentication, section 3.2 + */ + PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"), + /** + * {@code 408 Request Timeout}. + * @see HTTP/1.1: Semantics and Content, section 6.5.7 + */ + REQUEST_TIMEOUT(408, "Request Timeout"), + /** + * {@code 409 Conflict}. + * @see HTTP/1.1: Semantics and Content, section 6.5.8 + */ + CONFLICT(409, "Conflict"), + /** + * {@code 410 Gone}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.9 + */ + GONE(410, "Gone"), + /** + * {@code 411 Length Required}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.10 + */ + LENGTH_REQUIRED(411, "Length Required"), + /** + * {@code 412 Precondition failed}. + * @see + * HTTP/1.1: Conditional Requests, section 4.2 + */ + PRECONDITION_FAILED(412, "Precondition Failed"), + /** + * {@code 413 Payload Too Large}. + * @since 4.1 + * @see + * HTTP/1.1: Semantics and Content, section 6.5.11 + */ + PAYLOAD_TOO_LARGE(413, "Payload Too Large"), + /** + * {@code 413 Request Entity Too Large}. + * @see HTTP/1.1, section 10.4.14 + * @deprecated in favor of {@link #PAYLOAD_TOO_LARGE} which will be + * returned from {@code HttpStatus.valueOf(413)} + */ + @Deprecated + REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large"), + /** + * {@code 414 URI Too Long}. + * @since 4.1 + * @see + * HTTP/1.1: Semantics and Content, section 6.5.12 + */ + URI_TOO_LONG(414, "URI Too Long"), + /** + * {@code 414 Request-URI Too Long}. + * @see HTTP/1.1, section 10.4.15 + * @deprecated in favor of {@link #URI_TOO_LONG} which will be returned from {@code HttpStatus.valueOf(414)} + */ + @Deprecated + REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"), + /** + * {@code 415 Unsupported Media Type}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.13 + */ + UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), + /** + * {@code 416 Requested Range Not Satisfiable}. + * @see HTTP/1.1: Range Requests, section 4.4 + */ + REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested range not satisfiable"), + /** + * {@code 417 Expectation Failed}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.14 + */ + EXPECTATION_FAILED(417, "Expectation Failed"), + /** + * {@code 418 I'm a teapot}. + * @see HTCPCP/1.0 + */ + I_AM_A_TEAPOT(418, "I'm a teapot"), + /** + * @deprecated See + * + * WebDAV Draft Changes + */ + @Deprecated + INSUFFICIENT_SPACE_ON_RESOURCE(419, "Insufficient Space On Resource"), + /** + * @deprecated See + * + * WebDAV Draft Changes + */ + @Deprecated + METHOD_FAILURE(420, "Method Failure"), + /** + * @deprecated + * See + * WebDAV Draft Changes + */ + @Deprecated + DESTINATION_LOCKED(421, "Destination Locked"), + /** + * {@code 422 Unprocessable Entity}. + * @see WebDAV + */ + UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"), + /** + * {@code 423 Locked}. + * @see WebDAV + */ + LOCKED(423, "Locked"), + /** + * {@code 424 Failed Dependency}. + * @see WebDAV + */ + FAILED_DEPENDENCY(424, "Failed Dependency"), + /** + * {@code 426 Upgrade Required}. + * @see Upgrading to TLS Within HTTP/1.1 + */ + UPGRADE_REQUIRED(426, "Upgrade Required"), + /** + * {@code 428 Precondition Required}. + * @see Additional HTTP Status Codes + */ + PRECONDITION_REQUIRED(428, "Precondition Required"), + /** + * {@code 429 Too Many Requests}. + * @see Additional HTTP Status Codes + */ + TOO_MANY_REQUESTS(429, "Too Many Requests"), + /** + * {@code 431 Request Header Fields Too Large}. + * @see Additional HTTP Status Codes + */ + REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"), + /** + * {@code 451 Unavailable For Legal Reasons}. + * @see + * An HTTP Status Code to Report Legal Obstacles + * @since 4.3 + */ + UNAVAILABLE_FOR_LEGAL_REASONS(451, "Unavailable For Legal Reasons"), + + // --- 5xx Server Error --- + + /** + * {@code 500 Internal Server Error}. + * @see HTTP/1.1: Semantics and Content, section 6.6.1 + */ + INTERNAL_SERVER_ERROR(500, "Internal Server Error"), + /** + * {@code 501 Not Implemented}. + * @see HTTP/1.1: Semantics and Content, section 6.6.2 + */ + NOT_IMPLEMENTED(501, "Not Implemented"), + /** + * {@code 502 Bad Gateway}. + * @see HTTP/1.1: Semantics and Content, section 6.6.3 + */ + BAD_GATEWAY(502, "Bad Gateway"), + /** + * {@code 503 Service Unavailable}. + * @see HTTP/1.1: Semantics and Content, section 6.6.4 + */ + SERVICE_UNAVAILABLE(503, "Service Unavailable"), + /** + * {@code 504 Gateway Timeout}. + * @see HTTP/1.1: Semantics and Content, section 6.6.5 + */ + GATEWAY_TIMEOUT(504, "Gateway Timeout"), + /** + * {@code 505 HTTP Version Not Supported}. + * @see HTTP/1.1: Semantics and Content, section 6.6.6 + */ + HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version not supported"), + /** + * {@code 506 Variant Also Negotiates} + * @see Transparent Content Negotiation + */ + VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates"), + /** + * {@code 507 Insufficient Storage} + * @see WebDAV + */ + INSUFFICIENT_STORAGE(507, "Insufficient Storage"), + /** + * {@code 508 Loop Detected} + * @see WebDAV Binding Extensions + */ + LOOP_DETECTED(508, "Loop Detected"), + /** + * {@code 509 Bandwidth Limit Exceeded} + */ + BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded"), + /** + * {@code 510 Not Extended} + * @see HTTP Extension Framework + */ + NOT_EXTENDED(510, "Not Extended"), + /** + * {@code 511 Network Authentication Required}. + * @see Additional HTTP Status Codes + */ + NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required"); + + + private final int value; + + private final String reasonPhrase; + + + HttpStatus(int value, String reasonPhrase) { + this.value = value; + this.reasonPhrase = reasonPhrase; + } + + + /** + * Return the integer value of this status code. + */ + public int value() { + return this.value; + } + + /** + * Return the reason phrase of this status code. + */ + public String getReasonPhrase() { + return this.reasonPhrase; + } + +} diff --git a/src/main/java/io/jboot/components/restful/JbootRestfulManager.java b/src/main/java/io/jboot/components/restful/JbootRestfulManager.java new file mode 100644 index 00000000..ff1fb15b --- /dev/null +++ b/src/main/java/io/jboot/components/restful/JbootRestfulManager.java @@ -0,0 +1,237 @@ +package io.jboot.components.restful; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.InterceptorManager; +import com.jfinal.config.Routes; +import com.jfinal.core.Action; +import com.jfinal.core.Controller; +import com.jfinal.core.NotAction; +import io.jboot.components.restful.annotation.DeleteMapping; +import io.jboot.components.restful.annotation.GetMapping; +import io.jboot.components.restful.annotation.PostMapping; +import io.jboot.components.restful.annotation.PutMapping; +import io.jboot.components.restful.exception.RequestMethodErrorException; +import io.jboot.utils.StrUtil; +import io.jboot.web.controller.annotation.RequestMapping; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class JbootRestfulManager { + + public static class Config { + private boolean mappingSupperClass; + private String baseViewPath; + private Interceptor[] routeInterceptors; + private List routes; + + public Config() { + } + + public Config(boolean mappingSupperClass, String baseViewPath, + Interceptor[] routeInterceptors, List routes) { + this.mappingSupperClass = mappingSupperClass; + this.baseViewPath = baseViewPath; + this.routeInterceptors = routeInterceptors; + this.routes = routes; + } + + public boolean isMappingSupperClass() { + return mappingSupperClass; + } + + public Config setMappingSupperClass(boolean mappingSupperClass) { + this.mappingSupperClass = mappingSupperClass; + return this; + } + + public String getBaseViewPath() { + return baseViewPath; + } + + public Config setBaseViewPath(String baseViewPath) { + this.baseViewPath = baseViewPath; + return this; + } + + public Interceptor[] getRouteInterceptors() { + return routeInterceptors; + } + + public Config setRouteInterceptors(Interceptor[] routeInterceptors) { + this.routeInterceptors = routeInterceptors; + return this; + } + + public List getRoutes() { + return routes; + } + + public Config setRoutes(List routes) { + this.routes = routes; + return this; + } + } + + private static JbootRestfulManager me = new JbootRestfulManager(); + + private Map restfulActions = new HashMap<>(2048, 0.5F); + + protected static final String SLASH = "/"; + + private RestfulErrorRender restfulErrorRender = new DefaultRestfulErrorRender(); + + public static JbootRestfulManager me() { + return me; + } + + public void init(Config config) { + if (config.getRoutes() == null || config.getRoutes().isEmpty()) { + return; + } + InterceptorManager interMan = InterceptorManager.me(); + Class dc; + // 初始化自定义的restful controller + for (Routes.Route route : config.getRoutes()) { + Class controllerClass = route.getControllerClass(); + Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass); + + boolean declaredMethods = config.isMappingSupperClass() + ? controllerClass.getSuperclass() == Controller.class + : true; + + String baseRequestMapping = SLASH; + if (controllerClass.getAnnotation(RequestMapping.class) != null) { + RequestMapping requestMapping = controllerClass.getAnnotation(RequestMapping.class); + if (requestMapping.value().startsWith(SLASH)) { + baseRequestMapping = requestMapping.value(); + } else { + baseRequestMapping = baseRequestMapping + requestMapping.value(); + } + } + + Method[] methods = (declaredMethods ? controllerClass.getDeclaredMethods() : controllerClass.getMethods()); + for (Method method : methods) { + if (declaredMethods) { + if (!Modifier.isPublic(method.getModifiers())) + continue; + } else { + dc = method.getDeclaringClass(); + if (dc == Controller.class || dc == Object.class) + continue; + } + //去除mapping + if (method.getAnnotation(NotAction.class) != null) { + continue; + } + Interceptor[] actionInters = interMan.buildControllerActionInterceptor(config.getRouteInterceptors(), controllerInters, controllerClass, method); + + String actionKey = baseRequestMapping; + + //GET 判断 + GetMapping getMapping = method.getAnnotation(GetMapping.class); + PostMapping postMapping = method.getAnnotation(PostMapping.class); + PutMapping putMapping = method.getAnnotation(PutMapping.class); + DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class); + String requestMethod = "", mappingValue = ""; + if (getMapping != null) { + requestMethod = "GET"; + if (StrUtil.isNotBlank(getMapping.value())) { + mappingValue = getMapping.value(); + } + } else if (postMapping != null) { + requestMethod = "POST"; + if (StrUtil.isNotBlank(postMapping.value())) { + mappingValue = postMapping.value(); + } + } else if (putMapping != null) { + requestMethod = "PUT"; + if (StrUtil.isNotBlank(putMapping.value())) { + mappingValue = putMapping.value(); + } + } else if (deleteMapping != null) { + requestMethod = "DELETE"; + if (StrUtil.isNotBlank(deleteMapping.value())) { + mappingValue = deleteMapping.value(); + } + } else { + //默认为get请求 + requestMethod = "GET"; + mappingValue = SLASH; + } + if (StrUtil.isNotBlank(mappingValue)) { + if (!actionKey.endsWith(SLASH)) { + actionKey = actionKey + SLASH; + } + if (mappingValue.startsWith(SLASH)) { + mappingValue = mappingValue.substring(1); + } + actionKey = actionKey + mappingValue; + } else { + if (actionKey.endsWith(SLASH)) { + actionKey = actionKey.substring(0, actionKey.length() - 1); + } + } + Action action = new Action(baseRequestMapping, actionKey, controllerClass, + method, method.getName(), actionInters, route.getFinalViewPath(config.getBaseViewPath())); + String key = requestMethod + ":" + actionKey; + + RestfulAction restfulAction = new RestfulAction(action, actionKey, requestMethod); + if (restfulActions.put(key, restfulAction) != null) { + //已经存在指定的key + throw new RuntimeException(buildMsg(actionKey, controllerClass, method)); + } + } + } + } + + protected String buildMsg(String actionKey, Class controllerClass, Method method) { + StringBuilder sb = new StringBuilder("The action \"") + .append(controllerClass.getName()).append(".") + .append(method.getName()).append("()\" can not be mapped, ") + .append("actionKey \"").append(actionKey).append("\" is already in use."); + + String msg = sb.toString(); + System.err.println("\nException: " + msg); + return msg; + } + + public RestfulAction getRestfulAction(String target, String requestMethod) { + String actionKey = requestMethod + ":" + target; + //先直接获取 + RestfulAction restfulAction = restfulActions.get(actionKey); + if (restfulAction == null) { + //路径判断 + String[] paths = actionKey.split(":")[1].replace(requestMethod, "").split(SLASH); + for (String _actionKey : restfulActions.keySet()) { + String _requestMethod = _actionKey.split(":")[0]; + String _target = _actionKey.split(":")[1]; + System.out.println("---------> target:"+target+",_target:"+_target+",_requestMethod:"+_requestMethod+",requestMethod:" + requestMethod); + if( target.equals(_target) && !_requestMethod.equals(requestMethod) ){ + //请求方法不正确 + throw new RequestMethodErrorException(_actionKey, _requestMethod, target, requestMethod); + } + String[] _paths = _actionKey.split(":")[1].replace(requestMethod, "").split(SLASH); + if (_actionKey.startsWith(requestMethod) && + _actionKey.contains("{") && _actionKey.contains("}") + && paths.length == _paths.length && RestfulUtils.comparePaths(_paths, paths)) { + restfulAction = restfulActions.get(_actionKey); + break; + } + } + } + return restfulAction; + } + + public RestfulErrorRender getRestfulErrorRender() { + return restfulErrorRender; + } + + public JbootRestfulManager setRestfulErrorRender(RestfulErrorRender restfulErrorRender) { + this.restfulErrorRender = restfulErrorRender; + return this; + } +} diff --git a/src/main/java/io/jboot/components/restful/ResponseEntity.java b/src/main/java/io/jboot/components/restful/ResponseEntity.java new file mode 100644 index 00000000..90aad644 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/ResponseEntity.java @@ -0,0 +1,67 @@ +package io.jboot.components.restful; + +import java.util.HashMap; +import java.util.Map; + +public class ResponseEntity { + + //响应的数据 + private T data; + + //自定义响应头部信息 + private Map headers = new HashMap<>(); + + //默认http状态 + private HttpStatus httpStatus = HttpStatus.OK; + + public T getData() { + return data; + } + + public ResponseEntity setData(T data) { + this.data = data; + return this; + } + + public ResponseEntity addHeaders(Map headers) { + this.headers.putAll(headers); + return this; + } + + public ResponseEntity addHeader(String key, String value) { + this.headers.put(key, value); + return this; + } + + public Map getHeaders() { + return headers; + } + + public ResponseEntity setHttpStatus(HttpStatus httpStatus) { + this.httpStatus = httpStatus; + return this; + } + + public HttpStatus getHttpStatus() { + return httpStatus; + } + + public ResponseEntity() { + } + + public ResponseEntity(T data, Map headers, HttpStatus httpStatus) { + this.data = data; + this.headers = headers; + this.httpStatus = httpStatus; + } + + public ResponseEntity(Map headers, HttpStatus httpStatus) { + this.headers = headers; + this.httpStatus = httpStatus; + } + + public ResponseEntity(T data) { + this.data = data; + } + +} diff --git a/src/main/java/io/jboot/components/restful/RestfulAction.java b/src/main/java/io/jboot/components/restful/RestfulAction.java new file mode 100644 index 00000000..98843e3b --- /dev/null +++ b/src/main/java/io/jboot/components/restful/RestfulAction.java @@ -0,0 +1,47 @@ +package io.jboot.components.restful; + +import com.jfinal.core.Action; + +import java.io.Serializable; + +public class RestfulAction implements Serializable { + + private Action action; + + private String actionKey; + + private String method; + + public RestfulAction(Action action, String actionKey, String method) { + this.action = action; + this.actionKey = actionKey; + this.method = method; + } + + public Action getAction() { + return action; + } + + public RestfulAction setAction(Action action) { + this.action = action; + return this; + } + + public String getActionKey() { + return actionKey; + } + + public RestfulAction setActionKey(String actionKey) { + this.actionKey = actionKey; + return this; + } + + public String getMethod() { + return method; + } + + public RestfulAction setMethod(String method) { + this.method = method; + return this; + } +} diff --git a/src/main/java/io/jboot/components/restful/RestfulCallback.java b/src/main/java/io/jboot/components/restful/RestfulCallback.java new file mode 100644 index 00000000..0e088863 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/RestfulCallback.java @@ -0,0 +1,29 @@ +package io.jboot.components.restful; + +import com.jfinal.proxy.Callback; + +public class RestfulCallback implements Callback { + + private RestfulAction restfulAction; + + private Object target; + + public RestfulCallback(RestfulAction restfulAction, Object target) { + this.restfulAction = restfulAction; + this.target = target; + } + + public RestfulAction getRestfulAction() { + return restfulAction; + } + + public Object getTarget() { + return target; + } + + @Override + public Object call(Object[] args) throws Throwable { + return restfulAction.getAction().getMethod().invoke(target, args); + } + +} diff --git a/src/main/java/io/jboot/components/restful/RestfulErrorRender.java b/src/main/java/io/jboot/components/restful/RestfulErrorRender.java new file mode 100644 index 00000000..d2154da2 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/RestfulErrorRender.java @@ -0,0 +1,35 @@ +package io.jboot.components.restful; + +import com.jfinal.core.Action; +import com.jfinal.render.Render; + + +public abstract class RestfulErrorRender extends Render { + + private String target; + + private Action action; + + private Exception error; + + public void init (String target, Action action, Exception error) { + this.target = target; + this.action = action; + this.error = error; + } + + protected RestfulErrorRender() { + } + + protected String getTarget() { + return target; + } + + protected Action getAction() { + return action; + } + + protected Exception getError() { + return error; + } +} diff --git a/src/main/java/io/jboot/components/restful/RestfulHandler.java b/src/main/java/io/jboot/components/restful/RestfulHandler.java new file mode 100644 index 00000000..ca18c1b5 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/RestfulHandler.java @@ -0,0 +1,234 @@ +package io.jboot.components.restful; + +import com.jfinal.aop.Invocation; +import com.jfinal.core.*; +import com.jfinal.log.Log; +import com.jfinal.render.Render; +import com.jfinal.render.RenderException; +import io.jboot.components.restful.annotation.DownloadResponse; +import io.jboot.components.restful.annotation.ResponseHeader; +import io.jboot.components.restful.annotation.ResponseHeaders; +import io.jboot.components.restful.exception.ParameterNullErrorException; +import io.jboot.components.restful.exception.ParameterParseErrorException; +import io.jboot.components.restful.exception.RequestMethodErrorException; +import io.jboot.utils.ArrayUtil; +import io.jboot.utils.ClassUtil; +import io.jboot.utils.StrUtil; +import io.jboot.web.controller.JbootControllerContext; +import io.jboot.web.handler.JbootActionHandler; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.lang.reflect.Method; +import java.util.Map; + +public class RestfulHandler extends JbootActionHandler { + + private static final Log log = Log.getLog(JbootActionHandler.class); + + public Action getAction(String target, String[] urlPara) { + return super.getAction(target, urlPara); + } + + @Override + public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { + if (target.indexOf('.') != -1) { + return; + } + + isHandled[0] = true; + String[] urlPara = {null}; + Action action = getAction(target, urlPara); + RestfulAction restfulAction = null; + if (action != null && action.getActionKey().equals("/") && action.getMethodName().equals("index") + && StrUtil.isNotBlank(target) && !target.equals(action.getActionKey())) { + //如果被默认的"/"拦截,并且为index方法,但是请求的url又和actionKey不匹配,则有可能是restful请求 + try { + restfulAction = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); + } catch (RequestMethodErrorException e) { + handleActionException(target, request, response, action, e); + return; + } + if (restfulAction != null) { + action = null; + } + } + //如果无法从内置的action中获取action则尝试从restful管理的action中获取 + if (action == null) { + // 尝试从restful 获取action + try { + if (restfulAction == null) { + restfulAction = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); + } + } catch (RequestMethodErrorException e) { + handleActionException(target, request, response, action, e); + return; + } + if (restfulAction != null) { + //restful 风格的请求处理 + restfulRequest(restfulAction, target, request, response, isHandled); + return; + } + if (log.isWarnEnabled()) { + String qs = request.getQueryString(); + log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs)); + } + renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); + return; + } + //在获取到了 + super.handle(target, request, response, isHandled); + } + + private void restfulRequest(RestfulAction restfulAction, String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { + Action action = restfulAction.getAction(); + Controller controller = null; + try { + controller = controllerFactory.getController(action.getControllerClass()); + //注入依赖 + if (injectDependency) { + com.jfinal.aop.Aop.inject(controller); + } + //绑定controller本身到当前线程 + JbootControllerContext.hold(controller); + //初始化controller + CPI._init_(controller, action, request, response, null); + + //解析参数 + Object[] args = RestfulUtils.parseActionMethodParameters(target, restfulAction.getActionKey(), + action.getMethod(), request, controller.getRawData()); + RestfulCallback restfulCallback = new RestfulCallback(restfulAction, controller); + + Invocation invocation = new Invocation(controller, action.getMethod(), action.getInterceptors(), + restfulCallback, args); + + RestfulInvocation fixedInvocation; + if (devMode) { + if (ActionReporter.isReportAfterInvocation(request)) { + fixedInvocation = invokeInvocation(invocation); + ActionReporter.report(target, controller, action); + } else { + ActionReporter.report(target, controller, action); + fixedInvocation = invokeInvocation(invocation); + } + } else { + fixedInvocation = invokeInvocation(invocation); + } + + Object returnValue = fixedInvocation.getReturnValue(); + + // 判断是否带有@DownloadResponse + DownloadResponse downloadResponse = action.getMethod().getAnnotation(DownloadResponse.class); + if (returnValue == null && downloadResponse == null) { + //无返回结果的 restful 请求 + controller.renderNull(); + } + // 如果标记了@DownloadResponse,并且返回值不为空,会被认为自行处理了下载行为 + if (downloadResponse != null) { + return; + } + if (returnValue != null) { + //初始化返回值 + initRenderValue(returnValue, controller); + } + + Render render = controller.getRender(); + if (render instanceof ForwardActionRender) { + String actionUrl = ((ForwardActionRender) render).getActionUrl(); + if (target.equals(actionUrl)) { + throw new RuntimeException("The forward action url is the same as before."); + } else { + handle(actionUrl, request, response, isHandled); + } + return; + } + + if (render == null) { + render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); + } + + //初始化自定义头部 + initResponseHeaders(response, fixedInvocation.getMethod()); + //响应开始 + render.setContext(request, response, action.getViewPath()).render(); + + } catch (RenderException | ParameterNullErrorException | ParameterParseErrorException | ActionException e) { + handleActionException(target, request, response, action, e); + } catch (Exception e) { + String info = ClassUtil.buildMethodString(action.getMethod()); + if (log.isErrorEnabled()) { + String qs = request.getQueryString(); + String targetInfo = qs == null ? target : target + "?" + qs; + log.error(info + " : " + targetInfo, e); + } + //自定义错误处理 + handleActionException(target, request, response, action, e); + } finally { + JbootControllerContext.release(); + controllerFactory.recycle(controller); + } + } + + private void handleActionException(String target, HttpServletRequest request, HttpServletResponse response, + Action action, Exception e) { + RestfulErrorRender restfulErrorRender = JbootRestfulManager.me().getRestfulErrorRender(); + restfulErrorRender.setContext(request, response, action == null ? "" : action.getViewPath()); + restfulErrorRender.init(target, action, e); + restfulErrorRender.render(); + } + + + protected RestfulInvocation invokeInvocation(Invocation inv) { + RestfulInvocation fixedInvocation = new RestfulInvocation(inv); + fixedInvocation.invoke(); + return fixedInvocation; + } + + protected void initResponseHeaders(HttpServletResponse response, Method actionMethod) { + ResponseHeader[] responseHeaders = actionMethod.getAnnotationsByType(ResponseHeader.class); + ResponseHeaders responseHeadersList = actionMethod.getAnnotation(ResponseHeaders.class); + if (responseHeadersList != null && responseHeadersList.value().length > 0) { + if (responseHeaders != null && responseHeaders.length > 0) { + responseHeaders = ArrayUtil.concat(responseHeaders, responseHeadersList.value()); + } else { + responseHeaders = responseHeadersList.value(); + } + } + if (responseHeaders.length > 0) { + for (ResponseHeader header : responseHeaders) { + response.setHeader(header.key(), header.value()); + } + } + } + + protected void initRenderValue(Object o, Controller controller) { + if (o.getClass().equals(String.class) + || o.getClass().equals(int.class) + || o.getClass().equals(double.class) + || o.getClass().equals(byte.class) + || o.getClass().equals(long.class) + || o.getClass().equals(float.class) + || o.getClass().equals(short.class) + || o.getClass().equals(char.class) + || o.getClass().equals(boolean.class)) { + controller.renderText(String.valueOf(o)); + } else if (o.getClass().equals(File.class)) { + controller.renderFile((File) o); + } else if (o.getClass().equals(ResponseEntity.class)) { + ResponseEntity responseEntity = (ResponseEntity) o; + //设置自定义头部信息 + Map headers = responseEntity.getHeaders(); + + headers.forEach((k, v) -> controller.getResponse().setHeader(k, v)); + //设置http状态代码 + controller.getResponse().setStatus(responseEntity.getHttpStatus().value()); + initRenderValue(responseEntity.getData(), controller); + } else if (o instanceof Render) { //如果是render类型直接设置render + controller.render((Render) o); + } else { + controller.renderJson(o); + } + } + +} diff --git a/src/main/java/io/jboot/components/restful/RestfulInvocation.java b/src/main/java/io/jboot/components/restful/RestfulInvocation.java new file mode 100644 index 00000000..6be030eb --- /dev/null +++ b/src/main/java/io/jboot/components/restful/RestfulInvocation.java @@ -0,0 +1,19 @@ +package io.jboot.components.restful; + +import com.jfinal.aop.Invocation; +import com.jfinal.core.Controller; +import io.jboot.web.fixedinterceptor.FixedInvocation; + +public class RestfulInvocation extends FixedInvocation { + + public RestfulInvocation(Invocation invocation) { + super(invocation); + } + + @Override + public Controller getController() { + if (getTarget() == null) + throw new RuntimeException("This method can only be used for action interception"); + return (Controller)getTarget(); + } +} diff --git a/src/main/java/io/jboot/components/restful/RestfulUtils.java b/src/main/java/io/jboot/components/restful/RestfulUtils.java new file mode 100644 index 00000000..cbb64f62 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/RestfulUtils.java @@ -0,0 +1,201 @@ +package io.jboot.components.restful; + + +import com.jfinal.kit.JsonKit; +import io.jboot.components.restful.annotation.PathVariable; +import io.jboot.components.restful.annotation.RequestBody; +import io.jboot.components.restful.annotation.RequestHeader; +import io.jboot.components.restful.annotation.RequestParam; +import io.jboot.components.restful.exception.ParameterNullErrorException; +import io.jboot.components.restful.exception.ParameterParseErrorException; +import io.jboot.utils.StrUtil; + +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.HashMap; +import java.util.Map; + +public class RestfulUtils { + + /** + * 从url中解析路径参数 + * + * @param url + * @param actionKey + * @return + */ + public static Map parsePathVariables(String url, String actionKey) { + if (actionKey.contains("{") && actionKey.contains("}")) { + Map pathVariables = new HashMap<>(); + String[] paths = url.split("/"); + String[] _paths = actionKey.split("/"); + for (int i = 0; i < paths.length; i++) { + if (_paths[i].startsWith("{") && _paths[i].endsWith("}")) { + String pathKey = _paths[i].substring(1, _paths[i].length() - 1); + String value = paths[i]; + pathVariables.put(pathKey, value); + } + } + return pathVariables; + } else { + return null; + } + } + + /** + * 转换请求action请求的参数信息 + * + * @param target + * @param actionKey + * @param actionMethod + * @param request + * @param rawData + * @return + * @throws ParameterNullErrorException + * @throws ParameterParseErrorException + */ + public static Object[] parseActionMethodParameters(String target, String actionKey, Method actionMethod, HttpServletRequest request, String rawData) + throws ParameterNullErrorException, ParameterParseErrorException { + Object[] args = new Object[actionMethod.getParameters().length]; + for (int i = 0; i < actionMethod.getParameters().length; i++) { + Parameter parameter = actionMethod.getParameters()[i]; + RequestParam requestParam = parameter.getAnnotation(RequestParam.class); + RequestBody requestBody = parameter.getAnnotation(RequestBody.class); + RequestHeader requestHeader = parameter.getAnnotation(RequestHeader.class); + PathVariable pathVariable = parameter.getAnnotation(PathVariable.class); + String parameterName = parameter.getName(); + String values[]; + if (requestParam != null) { + if (StrUtil.isNotBlank(requestParam.value())) { + parameterName = requestParam.value(); + } + values = request.getParameterValues(parameterName); + parameter.getType(); + args[i] = parseRequestParamToParameter(values, parameterName, parameter.getType()); + if (args[i] == null && requestParam.required()) { + //要求参数为空,但是却并没有提供参数 + throw new ParameterNullErrorException(parameterName); + } + } else if (requestBody != null) { + args[i] = parseRequestBodyToParameter(rawData, parameterName, parameter.getType()); + } else if (requestHeader != null) { + if (StrUtil.isNotBlank(requestHeader.value())) { + parameterName = requestHeader.value(); + } + String value = request.getHeader(parameterName); + args[i] = parseRequestHeaderToParameter(value, parameterName, parameter.getType()); + if (args[i] == null && requestHeader.required()) { + //要求参数为空,但是却并没有提供参数 + throw new ParameterNullErrorException(parameterName); + } + } else if (pathVariable != null) { + if (StrUtil.isNotBlank(pathVariable.value())) { + parameterName = pathVariable.value(); + } + args[i] = parsePathVariableToParameter(target, actionKey, parameterName, parameter.getType()); + } else { + args[i] = null; + } + } + return args; + } + + /** + * 比对url请求路径 + * + * @param sourcePaths action配置的原路径 + * @param targetPaths 请求的目标路径 + * @return + */ + public static boolean comparePaths(String[] sourcePaths, String[] targetPaths) { + int matchingCount = 0; + for (int i = 0; i < sourcePaths.length; i++) { + if (sourcePaths[i].equals(targetPaths[i]) + || (sourcePaths[i].startsWith("{") && sourcePaths[i].endsWith("}"))) { + matchingCount += 1; + } + } + return matchingCount == sourcePaths.length; + } + + private static Object parseRequestParamToParameter(String[] value, String name, Class parameterTypeClass) { + if(parameterTypeClass.isArray()){ + Object [] objects = new Object[value.length]; + for (int i = 0; i < value.length; i++) { + objects[i] = parseCommonValue(value[i], name, parameterTypeClass); + } + return objects; + } else { + if(value != null && value.length > 0){ + return parseCommonValue(value[0], name, parameterTypeClass); + } + } + + return null; + } + + private static Object parseRequestHeaderToParameter(String header, String name, Class parameterTypeClass) { + return parseCommonValue(header, name, parameterTypeClass); + } + + private static Object parseRequestBodyToParameter(String body, String name, Class parameterTypeClass) { + //先当作基本数据来转换 + Object value = parseCommonValue(body, name, parameterTypeClass); + if(value == null){ + value = JsonKit.parse(body, parameterTypeClass); + } + return value; + } + + private static Object parsePathVariableToParameter(String target, String actionKey, String parameterName, Class parameterTypeClass) { + Map pathVariables = parsePathVariables(target, actionKey); + String value = pathVariables.get(parameterName); + return parseCommonValue(value, parameterName, parameterTypeClass); + } + + /** + * 转换基本类型参数,目前支持string,int,double,float,boolean,long基本类型数据 + * @param value + * @param name + * @param parameterTypeClass + * @return + */ + private static Object parseCommonValue(String value, String name, Class parameterTypeClass) { + if (StrUtil.isBlank(value)) { + return null; + } + if (parameterTypeClass.equals(String.class)) { + return value; + } else if (parameterTypeClass.equals(int.class)) { + try { + return Integer.valueOf(value); + } catch (NumberFormatException e) { + throw new ParameterParseErrorException(value, name, parameterTypeClass); + } + } else if (parameterTypeClass.equals(double.class)) { + try { + return Double.valueOf(value); + } catch (NumberFormatException e) { + throw new ParameterParseErrorException(value, name, parameterTypeClass); + } + } else if (parameterTypeClass.equals(float.class)) { + try { + return Float.valueOf(value); + } catch (NumberFormatException e) { + throw new ParameterParseErrorException(value, name, parameterTypeClass); + } + } else if (parameterTypeClass.equals(boolean.class)) { + return Boolean.valueOf(value); + } else if (parameterTypeClass.equals(long.class)) { + try { + return Long.valueOf(value); + } catch (NumberFormatException e) { + throw new ParameterParseErrorException(value, name, parameterTypeClass); + } + } else { + return null; + } + } + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/DeleteMapping.java b/src/main/java/io/jboot/components/restful/annotation/DeleteMapping.java new file mode 100644 index 00000000..2483f1c3 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/DeleteMapping.java @@ -0,0 +1,19 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * 删除delete 方法注解 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface DeleteMapping { + + /** + * url mapping + * @return + */ + String value() default ""; + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/DownloadResponse.java b/src/main/java/io/jboot/components/restful/annotation/DownloadResponse.java new file mode 100644 index 00000000..c4aa83e8 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/DownloadResponse.java @@ -0,0 +1,12 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * 标注controller action是一个下载响应,并且需要action自行处理response + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface DownloadResponse { +} diff --git a/src/main/java/io/jboot/components/restful/annotation/GetMapping.java b/src/main/java/io/jboot/components/restful/annotation/GetMapping.java new file mode 100644 index 00000000..c571a745 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/GetMapping.java @@ -0,0 +1,16 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface GetMapping { + + /** + * url mapping + * @return + */ + String value() default ""; + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/PathVariable.java b/src/main/java/io/jboot/components/restful/annotation/PathVariable.java new file mode 100644 index 00000000..68a96249 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/PathVariable.java @@ -0,0 +1,27 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * 路径参数注解 + * /user/{id}/cards + * 支持如下类型参数注入: + * string + * int + * double + * float + * boolean + * long + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +@Documented +public @interface PathVariable { + + /** + * 如果为空则默认为参数名本身 + * @return + */ + String value() default ""; + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/PostMapping.java b/src/main/java/io/jboot/components/restful/annotation/PostMapping.java new file mode 100644 index 00000000..e0f9a45a --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/PostMapping.java @@ -0,0 +1,16 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface PostMapping { + + /** + * url mapping + * @return + */ + String value() default ""; + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/PutMapping.java b/src/main/java/io/jboot/components/restful/annotation/PutMapping.java new file mode 100644 index 00000000..8d0c66d2 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/PutMapping.java @@ -0,0 +1,19 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * Put 请求方法定义 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface PutMapping { + + /** + * url mapping + * @return + */ + String value() default ""; + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/RequestBody.java b/src/main/java/io/jboot/components/restful/annotation/RequestBody.java new file mode 100644 index 00000000..73327a66 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/RequestBody.java @@ -0,0 +1,20 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * 请求体参数注解 + * 支持如下类型参数: + * string / string[] + * int / int[] + * double / double[] + * float / float[] + * boolean / boolean[] + * long / long[] + * object / object[] + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +@Documented +public @interface RequestBody { +} diff --git a/src/main/java/io/jboot/components/restful/annotation/RequestHeader.java b/src/main/java/io/jboot/components/restful/annotation/RequestHeader.java new file mode 100644 index 00000000..5a967970 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/RequestHeader.java @@ -0,0 +1,29 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * 请求头部信息注解 + * 支持如下类型参数注入: + * string + * int + * double + * float + * boolean + * long + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +@Documented +public @interface RequestHeader { + + /** + * 如果为空则默认为参数名本身 + * @return + */ + String value() default ""; + + boolean required() default false; + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/RequestParam.java b/src/main/java/io/jboot/components/restful/annotation/RequestParam.java new file mode 100644 index 00000000..e4a16089 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/RequestParam.java @@ -0,0 +1,29 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * 请求参数注解 + * 支持如下类型参数: + * string / string[] + * int / int[] + * double / double[] + * float / float[] + * boolean / boolean[] + * long / long[] + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +@Documented +public @interface RequestParam { + + /** + * 如果为空则默认为参数名本身 + * @return + */ + String value() default ""; + + boolean required() default false; + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/ResponseHeader.java b/src/main/java/io/jboot/components/restful/annotation/ResponseHeader.java new file mode 100644 index 00000000..626f22bf --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/ResponseHeader.java @@ -0,0 +1,17 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * 自定义响应头 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface ResponseHeader { + + String key(); + + String value(); + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/ResponseHeaders.java b/src/main/java/io/jboot/components/restful/annotation/ResponseHeaders.java new file mode 100644 index 00000000..d51f43ae --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/ResponseHeaders.java @@ -0,0 +1,12 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface ResponseHeaders { + + ResponseHeader[] value(); + +} diff --git a/src/main/java/io/jboot/components/restful/annotation/RestController.java b/src/main/java/io/jboot/components/restful/annotation/RestController.java new file mode 100644 index 00000000..6650c476 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/annotation/RestController.java @@ -0,0 +1,12 @@ +package io.jboot.components.restful.annotation; + +import java.lang.annotation.*; + +/** + * rest controller 标识 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +public @interface RestController { +} diff --git a/src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java b/src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java new file mode 100644 index 00000000..081867cc --- /dev/null +++ b/src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java @@ -0,0 +1,24 @@ +package io.jboot.components.restful.exception; + +/** + * 参数为空错误 + */ +public class ParameterNullErrorException extends RuntimeException { + + private String parameterName; + + + public String getParameterName() { + return parameterName; + } + + public ParameterNullErrorException(String parameterName) { + super("Parameter '"+parameterName+"' specifies a forced check, but the value is null"); + this.parameterName = parameterName; + } + + public ParameterNullErrorException(Exception e) { + super(e); + } + +} diff --git a/src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java b/src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java new file mode 100644 index 00000000..2a025ab6 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java @@ -0,0 +1,39 @@ +package io.jboot.components.restful.exception; + +/** + * 参数类型错误 + */ +public class ParameterParseErrorException extends RuntimeException { + + + private String parameterValue; + + private String parameterName; + + private Class parameterType; + + public String getParameterValue() { + return parameterValue; + } + + public String getParameterName() { + return parameterName; + } + + public Class getParameterType() { + return parameterType; + } + + public ParameterParseErrorException(String parameterValue, String parameterName, Class parameterType) { + super("Error resolving parameter '" + parameterName + "', unable to match value '" + + parameterValue + "' to specified type '" + parameterType.getName() + "'"); + this.parameterValue = parameterValue; + this.parameterName = parameterName; + this.parameterType = parameterType; + } + + public ParameterParseErrorException(Exception e) { + super(e); + } + +} diff --git a/src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java b/src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java new file mode 100644 index 00000000..e72f83c9 --- /dev/null +++ b/src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java @@ -0,0 +1,39 @@ +package io.jboot.components.restful.exception; + +/** + * 请求方法错误 + */ +public class RequestMethodErrorException extends RuntimeException { + + private String actionKey; + + private String actionMethod; + + private String target; + + private String targetMethod; + + public String getActionKey() { + return actionKey; + } + + public String getActionMethod() { + return actionMethod; + } + + public String getTarget() { + return target; + } + + public String getTargetMethod() { + return targetMethod; + } + + public RequestMethodErrorException(String actionKey, String actionMethod, String target, String targetMethod) { + super("'" + target + "' is specified as a '" + actionMethod + "' request. '" + targetMethod + "' requests are not supported"); + this.actionKey = actionKey; + this.actionMethod = actionMethod; + this.target = target; + this.targetMethod = targetMethod; + } +} diff --git a/src/test/java/io/jboot/test/restful/RestfulController.java b/src/test/java/io/jboot/test/restful/RestfulController.java new file mode 100644 index 00000000..08a2367d --- /dev/null +++ b/src/test/java/io/jboot/test/restful/RestfulController.java @@ -0,0 +1,117 @@ +package io.jboot.test.restful; + +import com.jfinal.aop.Before; +import com.jfinal.aop.Inject; +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import com.jfinal.core.NotAction; +import com.jfinal.kit.JsonKit; +import com.jfinal.kit.StrKit; +import io.jboot.components.restful.HttpStatus; +import io.jboot.components.restful.ResponseEntity; +import io.jboot.components.restful.annotation.*; +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; +import io.jboot.web.cors.EnableCORS; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +@RestController +@RequestMapping("/restful") +public class RestfulController extends JbootController { + + public static class Data implements Serializable { + private String id; + private String name; + private int age; + + public Data(String id, String name, int age) { + this.id = id; + this.name = name; + this.age = age; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + } + + public static class RestfulInterceptor implements Interceptor { + + public void intercept(Invocation inv) { + System.out.println("--------> restful request begin"); + inv.invoke(); + System.out.println("--------> restful request end"); + } + } + + @Inject + private RestfulService restfulService; + + @NotAction + public List initData(){ + List users = new ArrayList<>(); + users.add(new RestfulController.Data("1", "tom", 18)); + users.add(new RestfulController.Data("2", "andy", 29)); + users.add(new RestfulController.Data("3", "max", 13)); + return users; + } + + //GET /restful + @GetMapping + @EnableCORS + @Before({RestfulInterceptor.class}) + @ResponseHeaders({@ResponseHeader(key = "d-head-1", value = "a"), @ResponseHeader(key = "d-head-2", value = "b")}) + public List users(){ + return initData(); + } + + // GET /restful/randomKey + @GetMapping("/randomKey") + public String randomKey(){ + return restfulService.getRandomKey(); + } + + // GET /restful/users + @GetMapping("/users") + public ResponseEntity> entityUsers(){ + return new ResponseEntity<>(initData()).addHeader("x-token", StrKit.getRandomUUID()).setHttpStatus(HttpStatus.ACCEPTED); + } + + // PUT /restful + @PutMapping + public void create(@RequestBody RestfulController.Data data){ + System.out.println("get request body data:\n" + JsonKit.toJson(data)); + } + + // PUT /restful/createList + @PutMapping("/createList") + public void createUsers(@RequestBody List users){ + System.out.println("get request body data:\n" + JsonKit.toJson(users)); + } + + // DELETE /restful/:id + @DeleteMapping("/{id}") + public void delete(@PathVariable("id") String id){ + System.out.println("delete by id : " + id ); + } + + // DELETE /restful/delete + @DeleteMapping("/delete") + public void deleteByName(@RequestParam(value = "name", required = true) String name, + @RequestHeader String token){ + System.out.println("delete by name : " + name); + System.out.println("get token header : " + token); + } + +} diff --git a/src/test/java/io/jboot/test/restful/RestfulService.java b/src/test/java/io/jboot/test/restful/RestfulService.java new file mode 100644 index 00000000..aea7b1cd --- /dev/null +++ b/src/test/java/io/jboot/test/restful/RestfulService.java @@ -0,0 +1,5 @@ +package io.jboot.test.restful; + +public interface RestfulService { + String getRandomKey(); +} diff --git a/src/test/java/io/jboot/test/restful/RestfulServiceImpl.java b/src/test/java/io/jboot/test/restful/RestfulServiceImpl.java new file mode 100644 index 00000000..1c042af3 --- /dev/null +++ b/src/test/java/io/jboot/test/restful/RestfulServiceImpl.java @@ -0,0 +1,12 @@ +package io.jboot.test.restful; + +import com.jfinal.kit.StrKit; +import io.jboot.aop.annotation.Bean; + +@Bean +public class RestfulServiceImpl implements RestfulService { + @Override + public String getRandomKey() { + return StrKit.getRandomUUID(); + } +} -- Gitee From 014f6703426abecb5fe58a833c83d9846d7a1b81 Mon Sep 17 00:00:00 2001 From: yangyao Date: Thu, 2 Apr 2020 14:49:39 +0800 Subject: [PATCH 0192/1965] =?UTF-8?q?restful=E6=94=AF=E6=8C=81=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=9A=84JbootCoreConfig=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/core/JbootCoreConfig.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 35e59b29..2a50dd26 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -36,6 +36,9 @@ import io.jboot.app.config.support.nacos.NacosConfigManager; import io.jboot.components.gateway.JbootGatewayHandler; import io.jboot.components.gateway.JbootGatewayManager; import io.jboot.components.limiter.LimiterManager; +import io.jboot.components.restful.JbootRestfulManager; +import io.jboot.components.restful.RestfulHandler; +import io.jboot.components.restful.annotation.RestController; import io.jboot.components.rpc.JbootrpcManager; import io.jboot.components.schedule.JbootScheduleManager; import io.jboot.core.listener.JbootAppListenerManager; @@ -74,6 +77,9 @@ public class JbootCoreConfig extends JFinalConfig { private List routeList = new ArrayList<>(); + private JbootRestfulManager.Config restfulConfig = new JbootRestfulManager.Config(); + + public JbootCoreConfig() { initSystemProperties(); @@ -158,6 +164,8 @@ public class JbootCoreConfig extends JFinalConfig { routes.setMappingSuperClass(true); + List restfulRoutes = new ArrayList<>(); + List> controllerClassList = ClassScanner.scanSubClass(Controller.class); if (ArrayUtil.isNotEmpty(controllerClassList)) { for (Class clazz : controllerClassList) { @@ -171,6 +179,13 @@ public class JbootCoreConfig extends JFinalConfig { continue; } + //检查是否是restful类型的controller,如果是则加入restful专门指定的routes + RestController restController = clazz.getAnnotation(RestController.class); + if(restController != null){ + restfulRoutes.add(new Routes.Route(value, clazz, value)); + continue; + } + String viewPath = AnnotationUtil.get(mapping.viewPath()); if (StrUtil.isNotBlank(viewPath)) { @@ -192,6 +207,18 @@ public class JbootCoreConfig extends JFinalConfig { JbootControllerManager.me().setMapping(route.getControllerKey(), route.getControllerClass()); } + if( !restfulRoutes.isEmpty() ){ + //处理restful专属的routes + restfulConfig.setRoutes(restfulRoutes) + .setBaseViewPath(routes.getBaseViewPath()) + .setMappingSupperClass(routes.getMappingSuperClass()) + .setRouteInterceptors(routes.getInterceptors()); + for (Routes.Route route : restfulRoutes) { + JbootControllerManager.me().setMapping(route.getControllerKey(), route.getControllerClass()); + } + routeList.addAll(restfulRoutes); + } + routeList.addAll(routes.getRouteItemList()); } @@ -266,6 +293,7 @@ public class JbootCoreConfig extends JFinalConfig { handlers.add(new JbootGatewayHandler()); handlers.add(new JbootFilterHandler()); handlers.add(new JbootHandler()); + handlers.setActionHandler(new RestfulHandler()); //若用户自己没配置 ActionHandler,默认使用 JbootActionHandler if (handlers.getActionHandler() == null) { @@ -292,6 +320,8 @@ public class JbootCoreConfig extends JFinalConfig { JbootSeataManager.me().init(); SentinelManager.me().init(); JbootGatewayManager.me().init(); + JbootRestfulManager.me().init(restfulConfig); + JbootAppListenerManager.me().onStart(); } -- Gitee From d499721a035f44a43a1aaadf68b1c59cdc986f38 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 2 Apr 2020 16:47:14 +0800 Subject: [PATCH 0193/1965] add restful controller support --- .../restful/JbootRestfulManager.java | 13 +- .../components/restful/RestfulAction.java | 45 +- .../components/restful/RestfulCallback.java | 2 +- .../components/restful/RestfulHandler.java | 391 +++++++++--------- .../components/restful/RestfulInvocation.java | 3 +- .../java/io/jboot/core/JbootCoreConfig.java | 14 +- .../jboot/web/handler/JbootActionHandler.java | 23 +- 7 files changed, 246 insertions(+), 245 deletions(-) diff --git a/src/main/java/io/jboot/components/restful/JbootRestfulManager.java b/src/main/java/io/jboot/components/restful/JbootRestfulManager.java index ff1fb15b..4a2f1ed5 100644 --- a/src/main/java/io/jboot/components/restful/JbootRestfulManager.java +++ b/src/main/java/io/jboot/components/restful/JbootRestfulManager.java @@ -3,7 +3,6 @@ package io.jboot.components.restful; import com.jfinal.aop.Interceptor; import com.jfinal.aop.InterceptorManager; import com.jfinal.config.Routes; -import com.jfinal.core.Action; import com.jfinal.core.Controller; import com.jfinal.core.NotAction; import io.jboot.components.restful.annotation.DeleteMapping; @@ -116,12 +115,14 @@ public class JbootRestfulManager { Method[] methods = (declaredMethods ? controllerClass.getDeclaredMethods() : controllerClass.getMethods()); for (Method method : methods) { if (declaredMethods) { - if (!Modifier.isPublic(method.getModifiers())) + if (!Modifier.isPublic(method.getModifiers())) { continue; + } } else { dc = method.getDeclaringClass(); - if (dc == Controller.class || dc == Object.class) + if (dc == Controller.class || dc == Object.class) { continue; + } } //去除mapping if (method.getAnnotation(NotAction.class) != null) { @@ -175,12 +176,12 @@ public class JbootRestfulManager { actionKey = actionKey.substring(0, actionKey.length() - 1); } } - Action action = new Action(baseRequestMapping, actionKey, controllerClass, + RestfulAction action = new RestfulAction(baseRequestMapping, actionKey, controllerClass, method, method.getName(), actionInters, route.getFinalViewPath(config.getBaseViewPath())); String key = requestMethod + ":" + actionKey; - RestfulAction restfulAction = new RestfulAction(action, actionKey, requestMethod); - if (restfulActions.put(key, restfulAction) != null) { +// RestfulAction restfulAction = new RestfulAction(action, actionKey, requestMethod); + if (restfulActions.put(key, action) != null) { //已经存在指定的key throw new RuntimeException(buildMsg(actionKey, controllerClass, method)); } diff --git a/src/main/java/io/jboot/components/restful/RestfulAction.java b/src/main/java/io/jboot/components/restful/RestfulAction.java index 98843e3b..7c2ccd4e 100644 --- a/src/main/java/io/jboot/components/restful/RestfulAction.java +++ b/src/main/java/io/jboot/components/restful/RestfulAction.java @@ -1,47 +1,14 @@ package io.jboot.components.restful; +import com.jfinal.aop.Interceptor; import com.jfinal.core.Action; +import com.jfinal.core.Controller; -import java.io.Serializable; +import java.lang.reflect.Method; -public class RestfulAction implements Serializable { +public class RestfulAction extends Action { - private Action action; - - private String actionKey; - - private String method; - - public RestfulAction(Action action, String actionKey, String method) { - this.action = action; - this.actionKey = actionKey; - this.method = method; - } - - public Action getAction() { - return action; - } - - public RestfulAction setAction(Action action) { - this.action = action; - return this; - } - - public String getActionKey() { - return actionKey; - } - - public RestfulAction setActionKey(String actionKey) { - this.actionKey = actionKey; - return this; - } - - public String getMethod() { - return method; - } - - public RestfulAction setMethod(String method) { - this.method = method; - return this; + public RestfulAction(String controllerKey, String actionKey, Class controllerClass, Method method, String methodName, Interceptor[] interceptors, String viewPath) { + super(controllerKey, actionKey, controllerClass, method, methodName, interceptors, viewPath); } } diff --git a/src/main/java/io/jboot/components/restful/RestfulCallback.java b/src/main/java/io/jboot/components/restful/RestfulCallback.java index 0e088863..99703c11 100644 --- a/src/main/java/io/jboot/components/restful/RestfulCallback.java +++ b/src/main/java/io/jboot/components/restful/RestfulCallback.java @@ -23,7 +23,7 @@ public class RestfulCallback implements Callback { @Override public Object call(Object[] args) throws Throwable { - return restfulAction.getAction().getMethod().invoke(target, args); + return restfulAction.getMethod().invoke(target, args); } } diff --git a/src/main/java/io/jboot/components/restful/RestfulHandler.java b/src/main/java/io/jboot/components/restful/RestfulHandler.java index ca18c1b5..b90e1d5e 100644 --- a/src/main/java/io/jboot/components/restful/RestfulHandler.java +++ b/src/main/java/io/jboot/components/restful/RestfulHandler.java @@ -1,193 +1,208 @@ package io.jboot.components.restful; import com.jfinal.aop.Invocation; -import com.jfinal.core.*; +import com.jfinal.core.Action; +import com.jfinal.core.Controller; import com.jfinal.log.Log; -import com.jfinal.render.Render; -import com.jfinal.render.RenderException; -import io.jboot.components.restful.annotation.DownloadResponse; import io.jboot.components.restful.annotation.ResponseHeader; import io.jboot.components.restful.annotation.ResponseHeaders; -import io.jboot.components.restful.exception.ParameterNullErrorException; -import io.jboot.components.restful.exception.ParameterParseErrorException; -import io.jboot.components.restful.exception.RequestMethodErrorException; import io.jboot.utils.ArrayUtil; -import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; -import io.jboot.web.controller.JbootControllerContext; import io.jboot.web.handler.JbootActionHandler; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.File; -import java.lang.reflect.Method; -import java.util.Map; public class RestfulHandler extends JbootActionHandler { private static final Log log = Log.getLog(JbootActionHandler.class); - public Action getAction(String target, String[] urlPara) { - return super.getAction(target, urlPara); - } - @Override - public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { - if (target.indexOf('.') != -1) { - return; - } - - isHandled[0] = true; - String[] urlPara = {null}; - Action action = getAction(target, urlPara); - RestfulAction restfulAction = null; - if (action != null && action.getActionKey().equals("/") && action.getMethodName().equals("index") - && StrUtil.isNotBlank(target) && !target.equals(action.getActionKey())) { - //如果被默认的"/"拦截,并且为index方法,但是请求的url又和actionKey不匹配,则有可能是restful请求 - try { - restfulAction = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); - } catch (RequestMethodErrorException e) { - handleActionException(target, request, response, action, e); - return; - } - if (restfulAction != null) { - action = null; - } - } - //如果无法从内置的action中获取action则尝试从restful管理的action中获取 + public Action getAction(String target, String[] urlPara, HttpServletRequest request) { + Action action = super.getAction(target, urlPara); if (action == null) { - // 尝试从restful 获取action - try { - if (restfulAction == null) { - restfulAction = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); - } - } catch (RequestMethodErrorException e) { - handleActionException(target, request, response, action, e); - return; - } - if (restfulAction != null) { - //restful 风格的请求处理 - restfulRequest(restfulAction, target, request, response, isHandled); - return; - } - if (log.isWarnEnabled()) { - String qs = request.getQueryString(); - log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs)); + if (action.getActionKey().equals("/") && action.getMethodName().equals("index") + && StrUtil.isNotBlank(target) && !target.equals(action.getActionKey())) { + action = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); } - renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); - return; } - //在获取到了 - super.handle(target, request, response, isHandled); - } - private void restfulRequest(RestfulAction restfulAction, String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { - Action action = restfulAction.getAction(); - Controller controller = null; - try { - controller = controllerFactory.getController(action.getControllerClass()); - //注入依赖 - if (injectDependency) { - com.jfinal.aop.Aop.inject(controller); - } - //绑定controller本身到当前线程 - JbootControllerContext.hold(controller); - //初始化controller - CPI._init_(controller, action, request, response, null); - //解析参数 - Object[] args = RestfulUtils.parseActionMethodParameters(target, restfulAction.getActionKey(), - action.getMethod(), request, controller.getRawData()); - RestfulCallback restfulCallback = new RestfulCallback(restfulAction, controller); + return action; + } - Invocation invocation = new Invocation(controller, action.getMethod(), action.getInterceptors(), + @Override + public Invocation getInvocation(Action action, Controller controller) { + if (action instanceof RestfulAction) { + Object[] args = RestfulUtils.parseActionMethodParameters(action.getActionKey(), action.getActionKey(), + action.getMethod(), controller.getRequest(), controller.getRawData()); + RestfulCallback restfulCallback = new RestfulCallback((RestfulAction) action, controller); + return new Invocation(controller, action.getMethod(), action.getInterceptors(), restfulCallback, args); - - RestfulInvocation fixedInvocation; - if (devMode) { - if (ActionReporter.isReportAfterInvocation(request)) { - fixedInvocation = invokeInvocation(invocation); - ActionReporter.report(target, controller, action); - } else { - ActionReporter.report(target, controller, action); - fixedInvocation = invokeInvocation(invocation); - } - } else { - fixedInvocation = invokeInvocation(invocation); - } - - Object returnValue = fixedInvocation.getReturnValue(); - - // 判断是否带有@DownloadResponse - DownloadResponse downloadResponse = action.getMethod().getAnnotation(DownloadResponse.class); - if (returnValue == null && downloadResponse == null) { - //无返回结果的 restful 请求 - controller.renderNull(); - } - // 如果标记了@DownloadResponse,并且返回值不为空,会被认为自行处理了下载行为 - if (downloadResponse != null) { - return; - } - if (returnValue != null) { - //初始化返回值 - initRenderValue(returnValue, controller); - } - - Render render = controller.getRender(); - if (render instanceof ForwardActionRender) { - String actionUrl = ((ForwardActionRender) render).getActionUrl(); - if (target.equals(actionUrl)) { - throw new RuntimeException("The forward action url is the same as before."); - } else { - handle(actionUrl, request, response, isHandled); - } - return; - } - - if (render == null) { - render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); - } - - //初始化自定义头部 - initResponseHeaders(response, fixedInvocation.getMethod()); - //响应开始 - render.setContext(request, response, action.getViewPath()).render(); - - } catch (RenderException | ParameterNullErrorException | ParameterParseErrorException | ActionException e) { - handleActionException(target, request, response, action, e); - } catch (Exception e) { - String info = ClassUtil.buildMethodString(action.getMethod()); - if (log.isErrorEnabled()) { - String qs = request.getQueryString(); - String targetInfo = qs == null ? target : target + "?" + qs; - log.error(info + " : " + targetInfo, e); - } - //自定义错误处理 - handleActionException(target, request, response, action, e); - } finally { - JbootControllerContext.release(); - controllerFactory.recycle(controller); + } else { + return super.getInvocation(action, controller); } } - private void handleActionException(String target, HttpServletRequest request, HttpServletResponse response, - Action action, Exception e) { - RestfulErrorRender restfulErrorRender = JbootRestfulManager.me().getRestfulErrorRender(); - restfulErrorRender.setContext(request, response, action == null ? "" : action.getViewPath()); - restfulErrorRender.init(target, action, e); - restfulErrorRender.render(); - } - - protected RestfulInvocation invokeInvocation(Invocation inv) { - RestfulInvocation fixedInvocation = new RestfulInvocation(inv); - fixedInvocation.invoke(); - return fixedInvocation; - } + // @Override +// public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { +// if (target.indexOf('.') != -1) { +// return; +// } +// +// isHandled[0] = true; +// String[] urlPara = {null}; +// Action action = getAction(target, urlPara); +// RestfulAction restfulAction = null; +// if (action != null && action.getActionKey().equals("/") && action.getMethodName().equals("index") +// && StrUtil.isNotBlank(target) && !target.equals(action.getActionKey())) { +// //如果被默认的"/"拦截,并且为index方法,但是请求的url又和actionKey不匹配,则有可能是restful请求 +// try { +// restfulAction = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); +// } catch (RequestMethodErrorException e) { +// handleActionException(target, request, response, action, e); +// return; +// } +// if (restfulAction != null) { +// action = null; +// } +// } +// //如果无法从内置的action中获取action则尝试从restful管理的action中获取 +// if (action == null) { +// // 尝试从restful 获取action +// try { +// if (restfulAction == null) { +// restfulAction = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); +// } +// } catch (RequestMethodErrorException e) { +// handleActionException(target, request, response, action, e); +// return; +// } +// if (restfulAction != null) { +// //restful 风格的请求处理 +// restfulRequest(restfulAction, target, request, response, isHandled); +// return; +// } +// if (log.isWarnEnabled()) { +// String qs = request.getQueryString(); +// log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs)); +// } +// renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); +// return; +// } +// //在获取到了 +// super.handle(target, request, response, isHandled); +// } + +// private void restfulRequest(RestfulAction restfulAction, String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { +// Action action = restfulAction.getAction(); +// Controller controller = null; +// try { +// controller = controllerFactory.getController(action.getControllerClass()); +// //注入依赖 +// if (injectDependency) { +// com.jfinal.aop.Aop.inject(controller); +// } +// //绑定controller本身到当前线程 +// JbootControllerContext.hold(controller); +// //初始化controller +// CPI._init_(controller, action, request, response, null); +// +// //解析参数 +// Object[] args = RestfulUtils.parseActionMethodParameters(target, restfulAction.getActionKey(), +// action.getMethod(), request, controller.getRawData()); +// RestfulCallback restfulCallback = new RestfulCallback(restfulAction, controller); +// +// Invocation invocation = new Invocation(controller, action.getMethod(), action.getInterceptors(), +// restfulCallback, args); +// +// RestfulInvocation fixedInvocation; +// if (devMode) { +// if (ActionReporter.isReportAfterInvocation(request)) { +// fixedInvocation = invokeInvocation(invocation); +// ActionReporter.report(target, controller, action); +// } else { +// ActionReporter.report(target, controller, action); +// fixedInvocation = invokeInvocation(invocation); +// } +// } else { +// fixedInvocation = invokeInvocation(invocation); +// } +// +// Object returnValue = fixedInvocation.getReturnValue(); +// +// // 判断是否带有@DownloadResponse +// DownloadResponse downloadResponse = action.getMethod().getAnnotation(DownloadResponse.class); +// if (returnValue == null && downloadResponse == null) { +// //无返回结果的 restful 请求 +// controller.renderNull(); +// } +// // 如果标记了@DownloadResponse,并且返回值不为空,会被认为自行处理了下载行为 +// if (downloadResponse != null) { +// return; +// } +// if (returnValue != null) { +// //初始化返回值 +// initRenderValue(returnValue, controller); +// } +// +// Render render = controller.getRender(); +// if (render instanceof ForwardActionRender) { +// String actionUrl = ((ForwardActionRender) render).getActionUrl(); +// if (target.equals(actionUrl)) { +// throw new RuntimeException("The forward action url is the same as before."); +// } else { +// handle(actionUrl, request, response, isHandled); +// } +// return; +// } +// +// if (render == null) { +// render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); +// } +// +// //初始化自定义头部 +// initResponseHeaders(response, fixedInvocation.getMethod()); +// //响应开始 +// render.setContext(request, response, action.getViewPath()).render(); +// +// } catch (RenderException | ParameterNullErrorException | ParameterParseErrorException | ActionException e) { +// handleActionException(target, request, response, action, e); +// } catch (Exception e) { +// String info = ClassUtil.buildMethodString(action.getMethod()); +// if (log.isErrorEnabled()) { +// String qs = request.getQueryString(); +// String targetInfo = qs == null ? target : target + "?" + qs; +// log.error(info + " : " + targetInfo, e); +// } +// //自定义错误处理 +// handleActionException(target, request, response, action, e); +// } finally { +// JbootControllerContext.release(); +// controllerFactory.recycle(controller); +// } +// } + +// private void handleActionException(String target, HttpServletRequest request, HttpServletResponse response, +// Action action, Exception e) { +// RestfulErrorRender restfulErrorRender = JbootRestfulManager.me().getRestfulErrorRender(); +// restfulErrorRender.setContext(request, response, action == null ? "" : action.getViewPath()); +// restfulErrorRender.init(target, action, e); +// restfulErrorRender.render(); +// } + + +// protected RestfulInvocation invokeInvocation(Invocation inv) { +// RestfulInvocation fixedInvocation = new RestfulInvocation(inv); +// fixedInvocation.invoke(); +// return fixedInvocation; +// } - protected void initResponseHeaders(HttpServletResponse response, Method actionMethod) { - ResponseHeader[] responseHeaders = actionMethod.getAnnotationsByType(ResponseHeader.class); - ResponseHeaders responseHeadersList = actionMethod.getAnnotation(ResponseHeaders.class); + @Override + public void setResponse(HttpServletResponse response, Action action) { + ResponseHeader[] responseHeaders = action.getMethod().getAnnotationsByType(ResponseHeader.class); + ResponseHeaders responseHeadersList = action.getMethod().getAnnotation(ResponseHeaders.class); if (responseHeadersList != null && responseHeadersList.value().length > 0) { if (responseHeaders != null && responseHeaders.length > 0) { responseHeaders = ArrayUtil.concat(responseHeaders, responseHeadersList.value()); @@ -202,33 +217,33 @@ public class RestfulHandler extends JbootActionHandler { } } - protected void initRenderValue(Object o, Controller controller) { - if (o.getClass().equals(String.class) - || o.getClass().equals(int.class) - || o.getClass().equals(double.class) - || o.getClass().equals(byte.class) - || o.getClass().equals(long.class) - || o.getClass().equals(float.class) - || o.getClass().equals(short.class) - || o.getClass().equals(char.class) - || o.getClass().equals(boolean.class)) { - controller.renderText(String.valueOf(o)); - } else if (o.getClass().equals(File.class)) { - controller.renderFile((File) o); - } else if (o.getClass().equals(ResponseEntity.class)) { - ResponseEntity responseEntity = (ResponseEntity) o; - //设置自定义头部信息 - Map headers = responseEntity.getHeaders(); - - headers.forEach((k, v) -> controller.getResponse().setHeader(k, v)); - //设置http状态代码 - controller.getResponse().setStatus(responseEntity.getHttpStatus().value()); - initRenderValue(responseEntity.getData(), controller); - } else if (o instanceof Render) { //如果是render类型直接设置render - controller.render((Render) o); - } else { - controller.renderJson(o); - } - } +// protected void initRenderValue(Object o, Controller controller) { +// if (o.getClass().equals(String.class) +// || o.getClass().equals(int.class) +// || o.getClass().equals(double.class) +// || o.getClass().equals(byte.class) +// || o.getClass().equals(long.class) +// || o.getClass().equals(float.class) +// || o.getClass().equals(short.class) +// || o.getClass().equals(char.class) +// || o.getClass().equals(boolean.class)) { +// controller.renderText(String.valueOf(o)); +// } else if (o.getClass().equals(File.class)) { +// controller.renderFile((File) o); +// } else if (o.getClass().equals(ResponseEntity.class)) { +// ResponseEntity responseEntity = (ResponseEntity) o; +// //设置自定义头部信息 +// Map headers = responseEntity.getHeaders(); +// +// headers.forEach((k, v) -> controller.getResponse().setHeader(k, v)); +// //设置http状态代码 +// controller.getResponse().setStatus(responseEntity.getHttpStatus().value()); +// initRenderValue(responseEntity.getData(), controller); +// } else if (o instanceof Render) { //如果是render类型直接设置render +// controller.render((Render) o); +// } else { +// controller.renderJson(o); +// } +// } } diff --git a/src/main/java/io/jboot/components/restful/RestfulInvocation.java b/src/main/java/io/jboot/components/restful/RestfulInvocation.java index 6be030eb..4a5c3dd0 100644 --- a/src/main/java/io/jboot/components/restful/RestfulInvocation.java +++ b/src/main/java/io/jboot/components/restful/RestfulInvocation.java @@ -12,8 +12,9 @@ public class RestfulInvocation extends FixedInvocation { @Override public Controller getController() { - if (getTarget() == null) + if (getTarget() == null) { throw new RuntimeException("This method can only be used for action interception"); + } return (Controller)getTarget(); } } diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 2a50dd26..d5209810 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -76,6 +76,7 @@ import java.util.Properties; public class JbootCoreConfig extends JFinalConfig { private List routeList = new ArrayList<>(); + private List restfulRoutes = new ArrayList<>(); private JbootRestfulManager.Config restfulConfig = new JbootRestfulManager.Config(); @@ -164,7 +165,6 @@ public class JbootCoreConfig extends JFinalConfig { routes.setMappingSuperClass(true); - List restfulRoutes = new ArrayList<>(); List> controllerClassList = ClassScanner.scanSubClass(Controller.class); if (ArrayUtil.isNotEmpty(controllerClassList)) { @@ -181,7 +181,7 @@ public class JbootCoreConfig extends JFinalConfig { //检查是否是restful类型的controller,如果是则加入restful专门指定的routes RestController restController = clazz.getAnnotation(RestController.class); - if(restController != null){ + if (restController != null) { restfulRoutes.add(new Routes.Route(value, clazz, value)); continue; } @@ -207,7 +207,7 @@ public class JbootCoreConfig extends JFinalConfig { JbootControllerManager.me().setMapping(route.getControllerKey(), route.getControllerClass()); } - if( !restfulRoutes.isEmpty() ){ + if (!restfulRoutes.isEmpty()) { //处理restful专属的routes restfulConfig.setRoutes(restfulRoutes) .setBaseViewPath(routes.getBaseViewPath()) @@ -293,11 +293,15 @@ public class JbootCoreConfig extends JFinalConfig { handlers.add(new JbootGatewayHandler()); handlers.add(new JbootFilterHandler()); handlers.add(new JbootHandler()); - handlers.setActionHandler(new RestfulHandler()); +// handlers.setActionHandler(new RestfulHandler()); //若用户自己没配置 ActionHandler,默认使用 JbootActionHandler if (handlers.getActionHandler() == null) { - handlers.setActionHandler(new JbootActionHandler()); + if (restfulRoutes.isEmpty()) { + handlers.setActionHandler(new RestfulHandler()); + } else { + handlers.setActionHandler(new JbootActionHandler()); + } } } diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index e2cf42ba..d579a92c 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -44,12 +44,19 @@ public class JbootActionHandler extends ActionHandler { * @param urlPara * @return */ - @Override - public Action getAction(String target, String[] urlPara) { + public Action getAction(String target, String[] urlPara, HttpServletRequest request) { return actionMapping.getAction(target, urlPara); } + public Invocation getInvocation(Action action, Controller controller) { + return new Invocation(action, controller); + } + + public void setResponse(HttpServletResponse response, Action action) { + } + + /** * handle * 1: Action action = actionMapping.getAction(target) @@ -64,7 +71,7 @@ public class JbootActionHandler extends ActionHandler { isHandled[0] = true; String[] urlPara = {null}; - Action action = getAction(target, urlPara); + Action action = getAction(target, urlPara, request); if (action == null) { if (log.isWarnEnabled()) { @@ -86,7 +93,8 @@ public class JbootActionHandler extends ActionHandler { // controller.init(request, response, urlPara[0]); CPI._init_(controller, action, request, response, urlPara[0]); - Invocation invocation = new Invocation(action, controller); +// Invocation invocation = new Invocation(action, controller); + Invocation invocation = getInvocation(action, controller); if (devMode) { if (ActionReporter.isReportAfterInvocation(request)) { invokeInvocation(invocation); @@ -114,7 +122,11 @@ public class JbootActionHandler extends ActionHandler { render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); } + //设置 response 的一些信息,比如 headers 等 + setResponse(response, action); + render.setContext(request, response, action.getViewPath()).render(); + } catch (RenderException e) { if (log.isErrorEnabled()) { String qs = request.getQueryString(); @@ -137,6 +149,8 @@ public class JbootActionHandler extends ActionHandler { } + + private void handleActionException(String target, HttpServletRequest request, HttpServletResponse response, Action action, ActionException e) { int errorCode = e.getErrorCode(); String msg = null; @@ -170,7 +184,6 @@ public class JbootActionHandler extends ActionHandler { } - private void invokeInvocation(Invocation inv) { new FixedInvocation(inv).invoke(); } -- Gitee From 17c4559ac91143efa1201d2b8518b4577e2a303d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 2 Apr 2020 17:30:30 +0800 Subject: [PATCH 0194/1965] add restful controller support --- .../components/restful/RestfulCallback.java | 18 ++----- .../components/restful/RestfulHandler.java | 4 +- .../components/restful/RestfulInvocation.java | 53 ++++++++++++++++--- 3 files changed, 51 insertions(+), 24 deletions(-) diff --git a/src/main/java/io/jboot/components/restful/RestfulCallback.java b/src/main/java/io/jboot/components/restful/RestfulCallback.java index 99703c11..00b6bd66 100644 --- a/src/main/java/io/jboot/components/restful/RestfulCallback.java +++ b/src/main/java/io/jboot/components/restful/RestfulCallback.java @@ -1,29 +1,21 @@ package io.jboot.components.restful; +import com.jfinal.core.Action; import com.jfinal.proxy.Callback; public class RestfulCallback implements Callback { - private RestfulAction restfulAction; - + private Action action; private Object target; - public RestfulCallback(RestfulAction restfulAction, Object target) { - this.restfulAction = restfulAction; + public RestfulCallback(Action restfulAction, Object target) { + this.action = restfulAction; this.target = target; } - public RestfulAction getRestfulAction() { - return restfulAction; - } - - public Object getTarget() { - return target; - } - @Override public Object call(Object[] args) throws Throwable { - return restfulAction.getMethod().invoke(target, args); + return action.getMethod().invoke(target, args); } } diff --git a/src/main/java/io/jboot/components/restful/RestfulHandler.java b/src/main/java/io/jboot/components/restful/RestfulHandler.java index b90e1d5e..a7637393 100644 --- a/src/main/java/io/jboot/components/restful/RestfulHandler.java +++ b/src/main/java/io/jboot/components/restful/RestfulHandler.java @@ -36,9 +36,7 @@ public class RestfulHandler extends JbootActionHandler { if (action instanceof RestfulAction) { Object[] args = RestfulUtils.parseActionMethodParameters(action.getActionKey(), action.getActionKey(), action.getMethod(), controller.getRequest(), controller.getRawData()); - RestfulCallback restfulCallback = new RestfulCallback((RestfulAction) action, controller); - return new Invocation(controller, action.getMethod(), action.getInterceptors(), - restfulCallback, args); + return new RestfulInvocation(action, controller, args); } else { return super.getInvocation(action, controller); } diff --git a/src/main/java/io/jboot/components/restful/RestfulInvocation.java b/src/main/java/io/jboot/components/restful/RestfulInvocation.java index 4a5c3dd0..fe0df0d6 100644 --- a/src/main/java/io/jboot/components/restful/RestfulInvocation.java +++ b/src/main/java/io/jboot/components/restful/RestfulInvocation.java @@ -1,20 +1,57 @@ package io.jboot.components.restful; import com.jfinal.aop.Invocation; +import com.jfinal.core.Action; import com.jfinal.core.Controller; -import io.jboot.web.fixedinterceptor.FixedInvocation; -public class RestfulInvocation extends FixedInvocation { +public class RestfulInvocation extends Invocation { - public RestfulInvocation(Invocation invocation) { - super(invocation); + private Action action; + + + public RestfulInvocation(Action action, Controller controller, Object[] args) { + super(controller, action.getMethod(), action.getInterceptors(), new RestfulCallback(action, controller), args); + this.action = action; } + @Override public Controller getController() { - if (getTarget() == null) { - throw new RuntimeException("This method can only be used for action interception"); - } - return (Controller)getTarget(); + return super.getTarget(); + } + + /** + * Return the action key. + * actionKey = controllerKey + methodName + */ + @Override + public String getActionKey() { + return action.getActionKey(); + } + + /** + * Return the controller key. + */ + @Override + public String getControllerKey() { + return action.getControllerKey(); } + + /** + * Return view path of this controller. + */ + @Override + public String getViewPath() { + return action.getViewPath(); + } + + /** + * return true if it is action invocation. + */ + @Override + public boolean isActionInvocation() { + return action != null; + } + + } -- Gitee From 90ff04130c2105a2111ab3a550da32a30f09c0f2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 3 Apr 2020 12:28:48 +0800 Subject: [PATCH 0195/1965] add restful controller support --- .../jboot/web/handler/JbootActionHandler.java | 10 +- .../jboot/web/render/JbootRenderFactory.java | 5 + .../web/render/JbootReturnValueRender.java | 91 +++++++++++++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/jboot/web/render/JbootReturnValueRender.java diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index d579a92c..c5a0a4df 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -23,6 +23,7 @@ import com.jfinal.render.RenderException; import io.jboot.utils.ClassUtil; import io.jboot.web.controller.JbootControllerContext; import io.jboot.web.fixedinterceptor.FixedInvocation; +import io.jboot.web.render.JbootRenderFactory; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -118,6 +119,13 @@ public class JbootActionHandler extends ActionHandler { return; } + if (render == null + && invocation.getReturnValue() != null + && renderManager.getRenderFactory() instanceof JbootRenderFactory) { + JbootRenderFactory jrf = (JbootRenderFactory) renderManager.getRenderFactory(); + render = jrf.getReturnValueRender(action, invocation.getReturnValue()); + } + if (render == null) { render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); } @@ -149,8 +157,6 @@ public class JbootActionHandler extends ActionHandler { } - - private void handleActionException(String target, HttpServletRequest request, HttpServletResponse response, Action action, ActionException e) { int errorCode = e.getErrorCode(); String msg = null; diff --git a/src/main/java/io/jboot/web/render/JbootRenderFactory.java b/src/main/java/io/jboot/web/render/JbootRenderFactory.java index c8bdf569..5d85878b 100644 --- a/src/main/java/io/jboot/web/render/JbootRenderFactory.java +++ b/src/main/java/io/jboot/web/render/JbootRenderFactory.java @@ -15,6 +15,7 @@ */ package io.jboot.web.render; +import com.jfinal.core.Action; import com.jfinal.render.ContentType; import com.jfinal.render.Render; import com.jfinal.render.RenderFactory; @@ -103,4 +104,8 @@ public class JbootRenderFactory extends RenderFactory { return new JbootXmlRender(view); } + + public Render getReturnValueRender(Action action, Object returnValue) { + return new JbootReturnValueRender(action, returnValue); + } } diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java new file mode 100644 index 00000000..91f8788d --- /dev/null +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.render; + +import com.jfinal.core.Action; +import com.jfinal.kit.JsonKit; +import com.jfinal.render.Render; +import com.jfinal.render.RenderException; + +import java.io.IOException; +import java.io.PrintWriter; +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * @author Michael Yang 杨福海 (fuhai999@gmail.com) + * @version V1.0 + * @Package io.jboot.web.render + */ +public class JbootReturnValueRender extends Render { + + private Action action; + private Object value; + + public JbootReturnValueRender(Action action, Object returnValue) { + this.action = action; + this.value = returnValue; + } + + @Override + public void render() { + if (isBaseType()) { + renderText(); + } else { + renderJson(); + } + } + + + private void renderText() { + PrintWriter writer = null; + try { + response.setContentType("text/plain"); + writer = response.getWriter(); + writer.write(String.valueOf(value)); + // writer.flush(); + } catch (IOException e) { + throw new RenderException(e); + } + } + + + private void renderJson() { + PrintWriter writer = null; + try { + response.setContentType("application/json; charset=utf-8"); + writer = response.getWriter(); + writer.write(JsonKit.toJson(value)); + // writer.flush(); + } catch (IOException e) { + throw new RenderException(e); + } + } + + private boolean isBaseType() { + if (value == null) { + return true; + } + Class c = value.getClass(); + return c == String.class + || c == Integer.class || c == int.class + || c == Long.class || c == long.class + || c == Double.class || c == double.class + || c == Float.class || c == float.class + || c == Boolean.class || c == boolean.class + || c == BigDecimal.class || c == BigInteger.class; + } +} -- Gitee From 48154d0341f6eb916a31eb29e6a12d4912de0a3f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 3 Apr 2020 12:40:49 +0800 Subject: [PATCH 0196/1965] add restful controller support --- .../web/render/JbootReturnValueRender.java | 69 ++++++++++--------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index 91f8788d..caab4d13 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -17,13 +17,18 @@ package io.jboot.web.render; import com.jfinal.core.Action; import com.jfinal.kit.JsonKit; +import com.jfinal.render.FileRender; +import com.jfinal.render.JsonRender; import com.jfinal.render.Render; -import com.jfinal.render.RenderException; +import com.jfinal.render.TextRender; -import java.io.IOException; -import java.io.PrintWriter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; import java.math.BigDecimal; import java.math.BigInteger; +import java.text.SimpleDateFormat; +import java.util.Date; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) @@ -34,58 +39,58 @@ public class JbootReturnValueRender extends Render { private Action action; private Object value; + private Render render; public JbootReturnValueRender(Action action, Object returnValue) { this.action = action; - this.value = returnValue; - } + if (isBaseType(returnValue)) { + this.value = String.valueOf(returnValue); + } else { + this.value = returnValue; + } - @Override - public void render() { - if (isBaseType()) { - renderText(); + if (this.value instanceof File) { + this.render = new FileRender((File) value); + } else if (this.value instanceof String) { + this.render = new TextRender((String) value); + } else if (this.value instanceof Date) { + this.render = new TextRender(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) value)); } else { - renderJson(); + this.render = new JsonRender(JsonKit.toJson(value)); } } - private void renderText() { - PrintWriter writer = null; - try { - response.setContentType("text/plain"); - writer = response.getWriter(); - writer.write(String.valueOf(value)); - // writer.flush(); - } catch (IOException e) { - throw new RenderException(e); - } + @Override + public Render setContext(HttpServletRequest request, HttpServletResponse response) { + render.setContext(request, response); + return this; } + @Override + public Render setContext(HttpServletRequest request, HttpServletResponse response, String viewPath) { + render.setContext(request, response, viewPath); + return this; + } - private void renderJson() { - PrintWriter writer = null; - try { - response.setContentType("application/json; charset=utf-8"); - writer = response.getWriter(); - writer.write(JsonKit.toJson(value)); - // writer.flush(); - } catch (IOException e) { - throw new RenderException(e); - } + @Override + public void render() { + this.render.render(); } - private boolean isBaseType() { + + private boolean isBaseType(Object value) { if (value == null) { return true; } Class c = value.getClass(); - return c == String.class + return c == String.class || c == char.class || c == Integer.class || c == int.class || c == Long.class || c == long.class || c == Double.class || c == double.class || c == Float.class || c == float.class || c == Boolean.class || c == boolean.class + || c == Short.class || c == short.class || c == BigDecimal.class || c == BigInteger.class; } } -- Gitee From 4997a4f5118767578bbd97756ea0a39d1859000b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 3 Apr 2020 13:18:08 +0800 Subject: [PATCH 0197/1965] v3.1.5 --- .../restful/DefaultRestfulErrorRender.java | 109 ----- .../jboot/components/restful/HttpStatus.java | 405 ------------------ .../restful/JbootRestfulManager.java | 238 ---------- .../components/restful/ResponseEntity.java | 67 --- .../components/restful/RestfulAction.java | 14 - .../components/restful/RestfulCallback.java | 21 - .../restful/RestfulErrorRender.java | 35 -- .../components/restful/RestfulHandler.java | 247 ----------- .../components/restful/RestfulInvocation.java | 57 --- .../components/restful/RestfulUtils.java | 201 --------- .../restful/annotation/DeleteMapping.java | 19 - .../restful/annotation/DownloadResponse.java | 12 - .../restful/annotation/GetMapping.java | 16 - .../restful/annotation/PathVariable.java | 27 -- .../restful/annotation/PostMapping.java | 16 - .../restful/annotation/PutMapping.java | 19 - .../restful/annotation/RequestBody.java | 20 - .../restful/annotation/RequestHeader.java | 29 -- .../restful/annotation/RequestParam.java | 29 -- .../restful/annotation/ResponseHeader.java | 17 - .../restful/annotation/ResponseHeaders.java | 12 - .../restful/annotation/RestController.java | 12 - .../ParameterNullErrorException.java | 24 -- .../ParameterParseErrorException.java | 39 -- .../RequestMethodErrorException.java | 39 -- .../java/io/jboot/core/JbootCoreConfig.java | 34 +- .../jboot/test/restful/RestfulController.java | 117 ----- .../io/jboot/test/restful/RestfulService.java | 5 - .../test/restful/RestfulServiceImpl.java | 12 - 29 files changed, 1 insertion(+), 1891 deletions(-) delete mode 100644 src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java delete mode 100644 src/main/java/io/jboot/components/restful/HttpStatus.java delete mode 100644 src/main/java/io/jboot/components/restful/JbootRestfulManager.java delete mode 100644 src/main/java/io/jboot/components/restful/ResponseEntity.java delete mode 100644 src/main/java/io/jboot/components/restful/RestfulAction.java delete mode 100644 src/main/java/io/jboot/components/restful/RestfulCallback.java delete mode 100644 src/main/java/io/jboot/components/restful/RestfulErrorRender.java delete mode 100644 src/main/java/io/jboot/components/restful/RestfulHandler.java delete mode 100644 src/main/java/io/jboot/components/restful/RestfulInvocation.java delete mode 100644 src/main/java/io/jboot/components/restful/RestfulUtils.java delete mode 100644 src/main/java/io/jboot/components/restful/annotation/DeleteMapping.java delete mode 100644 src/main/java/io/jboot/components/restful/annotation/DownloadResponse.java delete mode 100644 src/main/java/io/jboot/components/restful/annotation/GetMapping.java delete mode 100644 src/main/java/io/jboot/components/restful/annotation/PathVariable.java delete mode 100644 src/main/java/io/jboot/components/restful/annotation/PostMapping.java delete mode 100644 src/main/java/io/jboot/components/restful/annotation/PutMapping.java delete mode 100644 src/main/java/io/jboot/components/restful/annotation/RequestBody.java delete mode 100644 src/main/java/io/jboot/components/restful/annotation/RequestHeader.java delete mode 100644 src/main/java/io/jboot/components/restful/annotation/RequestParam.java delete mode 100644 src/main/java/io/jboot/components/restful/annotation/ResponseHeader.java delete mode 100644 src/main/java/io/jboot/components/restful/annotation/ResponseHeaders.java delete mode 100644 src/main/java/io/jboot/components/restful/annotation/RestController.java delete mode 100644 src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java delete mode 100644 src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java delete mode 100644 src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java delete mode 100644 src/test/java/io/jboot/test/restful/RestfulController.java delete mode 100644 src/test/java/io/jboot/test/restful/RestfulService.java delete mode 100644 src/test/java/io/jboot/test/restful/RestfulServiceImpl.java diff --git a/src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java b/src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java deleted file mode 100644 index 1ba7e278..00000000 --- a/src/main/java/io/jboot/components/restful/DefaultRestfulErrorRender.java +++ /dev/null @@ -1,109 +0,0 @@ -package io.jboot.components.restful; - -import com.jfinal.core.ActionException; -import com.jfinal.log.Log; -import com.jfinal.render.Render; -import com.jfinal.render.RenderException; -import com.jfinal.render.RenderManager; -import io.jboot.components.restful.exception.ParameterNullErrorException; -import io.jboot.components.restful.exception.ParameterParseErrorException; -import io.jboot.components.restful.exception.RequestMethodErrorException; -import io.jboot.web.handler.JbootActionHandler; - -import java.io.Serializable; - -/** - * 默认的restful错误响应处理器 - */ -public class DefaultRestfulErrorRender extends RestfulErrorRender { - - private static final Log log = Log.getLog(JbootActionHandler.class); - - public static class Error implements Serializable { - private String errorClass; - private int code; - private String message; - - public Error(String errorClass, int code, String message) { - this.errorClass = errorClass; - this.code = code; - this.message = message; - } - - public String getErrorClass() { - return errorClass; - } - - public Error setErrorClass(String errorClass) { - this.errorClass = errorClass; - return this; - } - - public int getCode() { - return code; - } - - public Error setCode(int code) { - this.code = code; - return this; - } - - public String getMessage() { - return message; - } - - public Error setMessage(String message) { - this.message = message; - return this; - } - } - - public void render() { - log.error("The restful handler intercepted the error", super.getError()); - Error error = null; - if(super.getError() instanceof ParameterNullErrorException - || super.getError() instanceof ParameterParseErrorException){ - //400 - error = new Error(super.getError().getClass().getName(), - HttpStatus.BAD_REQUEST.value(), super.getError().getMessage()); - } else if(super.getError() instanceof RequestMethodErrorException){ - error = new Error(super.getError().getClass().getName(), - HttpStatus.METHOD_NOT_ALLOWED.value(), super.getError().getMessage()); - } else if(super.getError() instanceof ActionException){ - //解析错误代码 - ActionException actionException = (ActionException)super.getError(); - int errorCode = actionException.getErrorCode(); - String msg = ""; - if (errorCode == 404) { - msg = HttpStatus.NOT_FOUND.getReasonPhrase(); - } else if (errorCode == 400) { - msg = HttpStatus.BAD_REQUEST.getReasonPhrase(); - } else if (errorCode == 401) { - msg = HttpStatus.UNAUTHORIZED.getReasonPhrase(); - } else if (errorCode == 403) { - msg = HttpStatus.FORBIDDEN.getReasonPhrase(); - } else if(errorCode == 405){ - msg = HttpStatus.METHOD_NOT_ALLOWED.getReasonPhrase(); - } else { - msg = super.getError().getMessage(); - } - error = new Error(super.getError().getClass().getName(), - errorCode, msg); - } else if(super.getError() instanceof RenderException){ - //500 - error = new Error(super.getError().getClass().getName(), - HttpStatus.INTERNAL_SERVER_ERROR.value(), super.getError().getMessage()); - } else { - //500 - error = new Error(super.getError().getClass().getName(), - HttpStatus.INTERNAL_SERVER_ERROR.value(), super.getError().getMessage()); - } - Render jsonRender = RenderManager.me().getRenderFactory().getJsonRender(error); - jsonRender.setContext(super.request, super.response, super.getAction() == null? "" : super.getAction().getViewPath()); - super.response.setStatus(error.getCode()); - jsonRender.render(); - } - - - -} diff --git a/src/main/java/io/jboot/components/restful/HttpStatus.java b/src/main/java/io/jboot/components/restful/HttpStatus.java deleted file mode 100644 index aa54f020..00000000 --- a/src/main/java/io/jboot/components/restful/HttpStatus.java +++ /dev/null @@ -1,405 +0,0 @@ -package io.jboot.components.restful; - -public enum HttpStatus { - - /** - * {@code 100 Continue}. - * @see HTTP/1.1: Semantics and Content, section 6.2.1 - */ - CONTINUE(100, "Continue"), - /** - * {@code 101 Switching Protocols}. - * @see HTTP/1.1: Semantics and Content, section 6.2.2 - */ - SWITCHING_PROTOCOLS(101, "Switching Protocols"), - /** - * {@code 102 Processing}. - * @see WebDAV - */ - PROCESSING(102, "Processing"), - /** - * {@code 103 Checkpoint}. - * @see A proposal for supporting - * resumable POST/PUT HTTP requests in HTTP/1.0 - */ - CHECKPOINT(103, "Checkpoint"), - - // 2xx Success - - /** - * {@code 200 OK}. - * @see HTTP/1.1: Semantics and Content, section 6.3.1 - */ - OK(200, "OK"), - /** - * {@code 201 Created}. - * @see HTTP/1.1: Semantics and Content, section 6.3.2 - */ - CREATED(201, "Created"), - /** - * {@code 202 Accepted}. - * @see HTTP/1.1: Semantics and Content, section 6.3.3 - */ - ACCEPTED(202, "Accepted"), - /** - * {@code 203 Non-Authoritative Information}. - * @see HTTP/1.1: Semantics and Content, section 6.3.4 - */ - NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"), - /** - * {@code 204 No Content}. - * @see HTTP/1.1: Semantics and Content, section 6.3.5 - */ - NO_CONTENT(204, "No Content"), - /** - * {@code 205 Reset Content}. - * @see HTTP/1.1: Semantics and Content, section 6.3.6 - */ - RESET_CONTENT(205, "Reset Content"), - /** - * {@code 206 Partial Content}. - * @see HTTP/1.1: Range Requests, section 4.1 - */ - PARTIAL_CONTENT(206, "Partial Content"), - /** - * {@code 207 Multi-Status}. - * @see WebDAV - */ - MULTI_STATUS(207, "Multi-Status"), - /** - * {@code 208 Already Reported}. - * @see WebDAV Binding Extensions - */ - ALREADY_REPORTED(208, "Already Reported"), - /** - * {@code 226 IM Used}. - * @see Delta encoding in HTTP - */ - IM_USED(226, "IM Used"), - - // 3xx Redirection - - /** - * {@code 300 Multiple Choices}. - * @see HTTP/1.1: Semantics and Content, section 6.4.1 - */ - MULTIPLE_CHOICES(300, "Multiple Choices"), - /** - * {@code 301 Moved Permanently}. - * @see HTTP/1.1: Semantics and Content, section 6.4.2 - */ - MOVED_PERMANENTLY(301, "Moved Permanently"), - /** - * {@code 302 Found}. - * @see HTTP/1.1: Semantics and Content, section 6.4.3 - */ - FOUND(302, "Found"), - /** - * {@code 302 Moved Temporarily}. - * @see HTTP/1.0, section 9.3 - * @deprecated in favor of {@link #FOUND} which will be returned from {@code HttpStatus.valueOf(302)} - */ - @Deprecated - MOVED_TEMPORARILY(302, "Moved Temporarily"), - /** - * {@code 303 See Other}. - * @see HTTP/1.1: Semantics and Content, section 6.4.4 - */ - SEE_OTHER(303, "See Other"), - /** - * {@code 304 Not Modified}. - * @see HTTP/1.1: Conditional Requests, section 4.1 - */ - NOT_MODIFIED(304, "Not Modified"), - /** - * {@code 305 Use Proxy}. - * @see HTTP/1.1: Semantics and Content, section 6.4.5 - * @deprecated due to security concerns regarding in-band configuration of a proxy - */ - @Deprecated - USE_PROXY(305, "Use Proxy"), - /** - * {@code 307 Temporary Redirect}. - * @see HTTP/1.1: Semantics and Content, section 6.4.7 - */ - TEMPORARY_REDIRECT(307, "Temporary Redirect"), - /** - * {@code 308 Permanent Redirect}. - * @see RFC 7238 - */ - PERMANENT_REDIRECT(308, "Permanent Redirect"), - - // --- 4xx Client Error --- - - /** - * {@code 400 Bad Request}. - * @see HTTP/1.1: Semantics and Content, section 6.5.1 - */ - BAD_REQUEST(400, "Bad Request"), - /** - * {@code 401 Unauthorized}. - * @see HTTP/1.1: Authentication, section 3.1 - */ - UNAUTHORIZED(401, "Unauthorized"), - /** - * {@code 402 Payment Required}. - * @see HTTP/1.1: Semantics and Content, section 6.5.2 - */ - PAYMENT_REQUIRED(402, "Payment Required"), - /** - * {@code 403 Forbidden}. - * @see HTTP/1.1: Semantics and Content, section 6.5.3 - */ - FORBIDDEN(403, "Forbidden"), - /** - * {@code 404 Not Found}. - * @see HTTP/1.1: Semantics and Content, section 6.5.4 - */ - NOT_FOUND(404, "Not Found"), - /** - * {@code 405 Method Not Allowed}. - * @see HTTP/1.1: Semantics and Content, section 6.5.5 - */ - METHOD_NOT_ALLOWED(405, "Method Not Allowed"), - /** - * {@code 406 Not Acceptable}. - * @see HTTP/1.1: Semantics and Content, section 6.5.6 - */ - NOT_ACCEPTABLE(406, "Not Acceptable"), - /** - * {@code 407 Proxy Authentication Required}. - * @see HTTP/1.1: Authentication, section 3.2 - */ - PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"), - /** - * {@code 408 Request Timeout}. - * @see HTTP/1.1: Semantics and Content, section 6.5.7 - */ - REQUEST_TIMEOUT(408, "Request Timeout"), - /** - * {@code 409 Conflict}. - * @see HTTP/1.1: Semantics and Content, section 6.5.8 - */ - CONFLICT(409, "Conflict"), - /** - * {@code 410 Gone}. - * @see - * HTTP/1.1: Semantics and Content, section 6.5.9 - */ - GONE(410, "Gone"), - /** - * {@code 411 Length Required}. - * @see - * HTTP/1.1: Semantics and Content, section 6.5.10 - */ - LENGTH_REQUIRED(411, "Length Required"), - /** - * {@code 412 Precondition failed}. - * @see - * HTTP/1.1: Conditional Requests, section 4.2 - */ - PRECONDITION_FAILED(412, "Precondition Failed"), - /** - * {@code 413 Payload Too Large}. - * @since 4.1 - * @see - * HTTP/1.1: Semantics and Content, section 6.5.11 - */ - PAYLOAD_TOO_LARGE(413, "Payload Too Large"), - /** - * {@code 413 Request Entity Too Large}. - * @see HTTP/1.1, section 10.4.14 - * @deprecated in favor of {@link #PAYLOAD_TOO_LARGE} which will be - * returned from {@code HttpStatus.valueOf(413)} - */ - @Deprecated - REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large"), - /** - * {@code 414 URI Too Long}. - * @since 4.1 - * @see - * HTTP/1.1: Semantics and Content, section 6.5.12 - */ - URI_TOO_LONG(414, "URI Too Long"), - /** - * {@code 414 Request-URI Too Long}. - * @see HTTP/1.1, section 10.4.15 - * @deprecated in favor of {@link #URI_TOO_LONG} which will be returned from {@code HttpStatus.valueOf(414)} - */ - @Deprecated - REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"), - /** - * {@code 415 Unsupported Media Type}. - * @see - * HTTP/1.1: Semantics and Content, section 6.5.13 - */ - UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), - /** - * {@code 416 Requested Range Not Satisfiable}. - * @see HTTP/1.1: Range Requests, section 4.4 - */ - REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested range not satisfiable"), - /** - * {@code 417 Expectation Failed}. - * @see - * HTTP/1.1: Semantics and Content, section 6.5.14 - */ - EXPECTATION_FAILED(417, "Expectation Failed"), - /** - * {@code 418 I'm a teapot}. - * @see HTCPCP/1.0 - */ - I_AM_A_TEAPOT(418, "I'm a teapot"), - /** - * @deprecated See - * - * WebDAV Draft Changes - */ - @Deprecated - INSUFFICIENT_SPACE_ON_RESOURCE(419, "Insufficient Space On Resource"), - /** - * @deprecated See - * - * WebDAV Draft Changes - */ - @Deprecated - METHOD_FAILURE(420, "Method Failure"), - /** - * @deprecated - * See - * WebDAV Draft Changes - */ - @Deprecated - DESTINATION_LOCKED(421, "Destination Locked"), - /** - * {@code 422 Unprocessable Entity}. - * @see WebDAV - */ - UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"), - /** - * {@code 423 Locked}. - * @see WebDAV - */ - LOCKED(423, "Locked"), - /** - * {@code 424 Failed Dependency}. - * @see WebDAV - */ - FAILED_DEPENDENCY(424, "Failed Dependency"), - /** - * {@code 426 Upgrade Required}. - * @see Upgrading to TLS Within HTTP/1.1 - */ - UPGRADE_REQUIRED(426, "Upgrade Required"), - /** - * {@code 428 Precondition Required}. - * @see Additional HTTP Status Codes - */ - PRECONDITION_REQUIRED(428, "Precondition Required"), - /** - * {@code 429 Too Many Requests}. - * @see Additional HTTP Status Codes - */ - TOO_MANY_REQUESTS(429, "Too Many Requests"), - /** - * {@code 431 Request Header Fields Too Large}. - * @see Additional HTTP Status Codes - */ - REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"), - /** - * {@code 451 Unavailable For Legal Reasons}. - * @see - * An HTTP Status Code to Report Legal Obstacles - * @since 4.3 - */ - UNAVAILABLE_FOR_LEGAL_REASONS(451, "Unavailable For Legal Reasons"), - - // --- 5xx Server Error --- - - /** - * {@code 500 Internal Server Error}. - * @see HTTP/1.1: Semantics and Content, section 6.6.1 - */ - INTERNAL_SERVER_ERROR(500, "Internal Server Error"), - /** - * {@code 501 Not Implemented}. - * @see HTTP/1.1: Semantics and Content, section 6.6.2 - */ - NOT_IMPLEMENTED(501, "Not Implemented"), - /** - * {@code 502 Bad Gateway}. - * @see HTTP/1.1: Semantics and Content, section 6.6.3 - */ - BAD_GATEWAY(502, "Bad Gateway"), - /** - * {@code 503 Service Unavailable}. - * @see HTTP/1.1: Semantics and Content, section 6.6.4 - */ - SERVICE_UNAVAILABLE(503, "Service Unavailable"), - /** - * {@code 504 Gateway Timeout}. - * @see HTTP/1.1: Semantics and Content, section 6.6.5 - */ - GATEWAY_TIMEOUT(504, "Gateway Timeout"), - /** - * {@code 505 HTTP Version Not Supported}. - * @see HTTP/1.1: Semantics and Content, section 6.6.6 - */ - HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version not supported"), - /** - * {@code 506 Variant Also Negotiates} - * @see Transparent Content Negotiation - */ - VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates"), - /** - * {@code 507 Insufficient Storage} - * @see WebDAV - */ - INSUFFICIENT_STORAGE(507, "Insufficient Storage"), - /** - * {@code 508 Loop Detected} - * @see WebDAV Binding Extensions - */ - LOOP_DETECTED(508, "Loop Detected"), - /** - * {@code 509 Bandwidth Limit Exceeded} - */ - BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded"), - /** - * {@code 510 Not Extended} - * @see HTTP Extension Framework - */ - NOT_EXTENDED(510, "Not Extended"), - /** - * {@code 511 Network Authentication Required}. - * @see Additional HTTP Status Codes - */ - NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required"); - - - private final int value; - - private final String reasonPhrase; - - - HttpStatus(int value, String reasonPhrase) { - this.value = value; - this.reasonPhrase = reasonPhrase; - } - - - /** - * Return the integer value of this status code. - */ - public int value() { - return this.value; - } - - /** - * Return the reason phrase of this status code. - */ - public String getReasonPhrase() { - return this.reasonPhrase; - } - -} diff --git a/src/main/java/io/jboot/components/restful/JbootRestfulManager.java b/src/main/java/io/jboot/components/restful/JbootRestfulManager.java deleted file mode 100644 index 4a2f1ed5..00000000 --- a/src/main/java/io/jboot/components/restful/JbootRestfulManager.java +++ /dev/null @@ -1,238 +0,0 @@ -package io.jboot.components.restful; - -import com.jfinal.aop.Interceptor; -import com.jfinal.aop.InterceptorManager; -import com.jfinal.config.Routes; -import com.jfinal.core.Controller; -import com.jfinal.core.NotAction; -import io.jboot.components.restful.annotation.DeleteMapping; -import io.jboot.components.restful.annotation.GetMapping; -import io.jboot.components.restful.annotation.PostMapping; -import io.jboot.components.restful.annotation.PutMapping; -import io.jboot.components.restful.exception.RequestMethodErrorException; -import io.jboot.utils.StrUtil; -import io.jboot.web.controller.annotation.RequestMapping; - -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class JbootRestfulManager { - - public static class Config { - private boolean mappingSupperClass; - private String baseViewPath; - private Interceptor[] routeInterceptors; - private List routes; - - public Config() { - } - - public Config(boolean mappingSupperClass, String baseViewPath, - Interceptor[] routeInterceptors, List routes) { - this.mappingSupperClass = mappingSupperClass; - this.baseViewPath = baseViewPath; - this.routeInterceptors = routeInterceptors; - this.routes = routes; - } - - public boolean isMappingSupperClass() { - return mappingSupperClass; - } - - public Config setMappingSupperClass(boolean mappingSupperClass) { - this.mappingSupperClass = mappingSupperClass; - return this; - } - - public String getBaseViewPath() { - return baseViewPath; - } - - public Config setBaseViewPath(String baseViewPath) { - this.baseViewPath = baseViewPath; - return this; - } - - public Interceptor[] getRouteInterceptors() { - return routeInterceptors; - } - - public Config setRouteInterceptors(Interceptor[] routeInterceptors) { - this.routeInterceptors = routeInterceptors; - return this; - } - - public List getRoutes() { - return routes; - } - - public Config setRoutes(List routes) { - this.routes = routes; - return this; - } - } - - private static JbootRestfulManager me = new JbootRestfulManager(); - - private Map restfulActions = new HashMap<>(2048, 0.5F); - - protected static final String SLASH = "/"; - - private RestfulErrorRender restfulErrorRender = new DefaultRestfulErrorRender(); - - public static JbootRestfulManager me() { - return me; - } - - public void init(Config config) { - if (config.getRoutes() == null || config.getRoutes().isEmpty()) { - return; - } - InterceptorManager interMan = InterceptorManager.me(); - Class dc; - // 初始化自定义的restful controller - for (Routes.Route route : config.getRoutes()) { - Class controllerClass = route.getControllerClass(); - Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass); - - boolean declaredMethods = config.isMappingSupperClass() - ? controllerClass.getSuperclass() == Controller.class - : true; - - String baseRequestMapping = SLASH; - if (controllerClass.getAnnotation(RequestMapping.class) != null) { - RequestMapping requestMapping = controllerClass.getAnnotation(RequestMapping.class); - if (requestMapping.value().startsWith(SLASH)) { - baseRequestMapping = requestMapping.value(); - } else { - baseRequestMapping = baseRequestMapping + requestMapping.value(); - } - } - - Method[] methods = (declaredMethods ? controllerClass.getDeclaredMethods() : controllerClass.getMethods()); - for (Method method : methods) { - if (declaredMethods) { - if (!Modifier.isPublic(method.getModifiers())) { - continue; - } - } else { - dc = method.getDeclaringClass(); - if (dc == Controller.class || dc == Object.class) { - continue; - } - } - //去除mapping - if (method.getAnnotation(NotAction.class) != null) { - continue; - } - Interceptor[] actionInters = interMan.buildControllerActionInterceptor(config.getRouteInterceptors(), controllerInters, controllerClass, method); - - String actionKey = baseRequestMapping; - - //GET 判断 - GetMapping getMapping = method.getAnnotation(GetMapping.class); - PostMapping postMapping = method.getAnnotation(PostMapping.class); - PutMapping putMapping = method.getAnnotation(PutMapping.class); - DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class); - String requestMethod = "", mappingValue = ""; - if (getMapping != null) { - requestMethod = "GET"; - if (StrUtil.isNotBlank(getMapping.value())) { - mappingValue = getMapping.value(); - } - } else if (postMapping != null) { - requestMethod = "POST"; - if (StrUtil.isNotBlank(postMapping.value())) { - mappingValue = postMapping.value(); - } - } else if (putMapping != null) { - requestMethod = "PUT"; - if (StrUtil.isNotBlank(putMapping.value())) { - mappingValue = putMapping.value(); - } - } else if (deleteMapping != null) { - requestMethod = "DELETE"; - if (StrUtil.isNotBlank(deleteMapping.value())) { - mappingValue = deleteMapping.value(); - } - } else { - //默认为get请求 - requestMethod = "GET"; - mappingValue = SLASH; - } - if (StrUtil.isNotBlank(mappingValue)) { - if (!actionKey.endsWith(SLASH)) { - actionKey = actionKey + SLASH; - } - if (mappingValue.startsWith(SLASH)) { - mappingValue = mappingValue.substring(1); - } - actionKey = actionKey + mappingValue; - } else { - if (actionKey.endsWith(SLASH)) { - actionKey = actionKey.substring(0, actionKey.length() - 1); - } - } - RestfulAction action = new RestfulAction(baseRequestMapping, actionKey, controllerClass, - method, method.getName(), actionInters, route.getFinalViewPath(config.getBaseViewPath())); - String key = requestMethod + ":" + actionKey; - -// RestfulAction restfulAction = new RestfulAction(action, actionKey, requestMethod); - if (restfulActions.put(key, action) != null) { - //已经存在指定的key - throw new RuntimeException(buildMsg(actionKey, controllerClass, method)); - } - } - } - } - - protected String buildMsg(String actionKey, Class controllerClass, Method method) { - StringBuilder sb = new StringBuilder("The action \"") - .append(controllerClass.getName()).append(".") - .append(method.getName()).append("()\" can not be mapped, ") - .append("actionKey \"").append(actionKey).append("\" is already in use."); - - String msg = sb.toString(); - System.err.println("\nException: " + msg); - return msg; - } - - public RestfulAction getRestfulAction(String target, String requestMethod) { - String actionKey = requestMethod + ":" + target; - //先直接获取 - RestfulAction restfulAction = restfulActions.get(actionKey); - if (restfulAction == null) { - //路径判断 - String[] paths = actionKey.split(":")[1].replace(requestMethod, "").split(SLASH); - for (String _actionKey : restfulActions.keySet()) { - String _requestMethod = _actionKey.split(":")[0]; - String _target = _actionKey.split(":")[1]; - System.out.println("---------> target:"+target+",_target:"+_target+",_requestMethod:"+_requestMethod+",requestMethod:" + requestMethod); - if( target.equals(_target) && !_requestMethod.equals(requestMethod) ){ - //请求方法不正确 - throw new RequestMethodErrorException(_actionKey, _requestMethod, target, requestMethod); - } - String[] _paths = _actionKey.split(":")[1].replace(requestMethod, "").split(SLASH); - if (_actionKey.startsWith(requestMethod) && - _actionKey.contains("{") && _actionKey.contains("}") - && paths.length == _paths.length && RestfulUtils.comparePaths(_paths, paths)) { - restfulAction = restfulActions.get(_actionKey); - break; - } - } - } - return restfulAction; - } - - public RestfulErrorRender getRestfulErrorRender() { - return restfulErrorRender; - } - - public JbootRestfulManager setRestfulErrorRender(RestfulErrorRender restfulErrorRender) { - this.restfulErrorRender = restfulErrorRender; - return this; - } -} diff --git a/src/main/java/io/jboot/components/restful/ResponseEntity.java b/src/main/java/io/jboot/components/restful/ResponseEntity.java deleted file mode 100644 index 90aad644..00000000 --- a/src/main/java/io/jboot/components/restful/ResponseEntity.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.jboot.components.restful; - -import java.util.HashMap; -import java.util.Map; - -public class ResponseEntity { - - //响应的数据 - private T data; - - //自定义响应头部信息 - private Map headers = new HashMap<>(); - - //默认http状态 - private HttpStatus httpStatus = HttpStatus.OK; - - public T getData() { - return data; - } - - public ResponseEntity setData(T data) { - this.data = data; - return this; - } - - public ResponseEntity addHeaders(Map headers) { - this.headers.putAll(headers); - return this; - } - - public ResponseEntity addHeader(String key, String value) { - this.headers.put(key, value); - return this; - } - - public Map getHeaders() { - return headers; - } - - public ResponseEntity setHttpStatus(HttpStatus httpStatus) { - this.httpStatus = httpStatus; - return this; - } - - public HttpStatus getHttpStatus() { - return httpStatus; - } - - public ResponseEntity() { - } - - public ResponseEntity(T data, Map headers, HttpStatus httpStatus) { - this.data = data; - this.headers = headers; - this.httpStatus = httpStatus; - } - - public ResponseEntity(Map headers, HttpStatus httpStatus) { - this.headers = headers; - this.httpStatus = httpStatus; - } - - public ResponseEntity(T data) { - this.data = data; - } - -} diff --git a/src/main/java/io/jboot/components/restful/RestfulAction.java b/src/main/java/io/jboot/components/restful/RestfulAction.java deleted file mode 100644 index 7c2ccd4e..00000000 --- a/src/main/java/io/jboot/components/restful/RestfulAction.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.jboot.components.restful; - -import com.jfinal.aop.Interceptor; -import com.jfinal.core.Action; -import com.jfinal.core.Controller; - -import java.lang.reflect.Method; - -public class RestfulAction extends Action { - - public RestfulAction(String controllerKey, String actionKey, Class controllerClass, Method method, String methodName, Interceptor[] interceptors, String viewPath) { - super(controllerKey, actionKey, controllerClass, method, methodName, interceptors, viewPath); - } -} diff --git a/src/main/java/io/jboot/components/restful/RestfulCallback.java b/src/main/java/io/jboot/components/restful/RestfulCallback.java deleted file mode 100644 index 00b6bd66..00000000 --- a/src/main/java/io/jboot/components/restful/RestfulCallback.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.jboot.components.restful; - -import com.jfinal.core.Action; -import com.jfinal.proxy.Callback; - -public class RestfulCallback implements Callback { - - private Action action; - private Object target; - - public RestfulCallback(Action restfulAction, Object target) { - this.action = restfulAction; - this.target = target; - } - - @Override - public Object call(Object[] args) throws Throwable { - return action.getMethod().invoke(target, args); - } - -} diff --git a/src/main/java/io/jboot/components/restful/RestfulErrorRender.java b/src/main/java/io/jboot/components/restful/RestfulErrorRender.java deleted file mode 100644 index d2154da2..00000000 --- a/src/main/java/io/jboot/components/restful/RestfulErrorRender.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.jboot.components.restful; - -import com.jfinal.core.Action; -import com.jfinal.render.Render; - - -public abstract class RestfulErrorRender extends Render { - - private String target; - - private Action action; - - private Exception error; - - public void init (String target, Action action, Exception error) { - this.target = target; - this.action = action; - this.error = error; - } - - protected RestfulErrorRender() { - } - - protected String getTarget() { - return target; - } - - protected Action getAction() { - return action; - } - - protected Exception getError() { - return error; - } -} diff --git a/src/main/java/io/jboot/components/restful/RestfulHandler.java b/src/main/java/io/jboot/components/restful/RestfulHandler.java deleted file mode 100644 index a7637393..00000000 --- a/src/main/java/io/jboot/components/restful/RestfulHandler.java +++ /dev/null @@ -1,247 +0,0 @@ -package io.jboot.components.restful; - -import com.jfinal.aop.Invocation; -import com.jfinal.core.Action; -import com.jfinal.core.Controller; -import com.jfinal.log.Log; -import io.jboot.components.restful.annotation.ResponseHeader; -import io.jboot.components.restful.annotation.ResponseHeaders; -import io.jboot.utils.ArrayUtil; -import io.jboot.utils.StrUtil; -import io.jboot.web.handler.JbootActionHandler; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -public class RestfulHandler extends JbootActionHandler { - - private static final Log log = Log.getLog(JbootActionHandler.class); - - @Override - public Action getAction(String target, String[] urlPara, HttpServletRequest request) { - Action action = super.getAction(target, urlPara); - if (action == null) { - if (action.getActionKey().equals("/") && action.getMethodName().equals("index") - && StrUtil.isNotBlank(target) && !target.equals(action.getActionKey())) { - action = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); - } - } - - - return action; - } - - @Override - public Invocation getInvocation(Action action, Controller controller) { - if (action instanceof RestfulAction) { - Object[] args = RestfulUtils.parseActionMethodParameters(action.getActionKey(), action.getActionKey(), - action.getMethod(), controller.getRequest(), controller.getRawData()); - return new RestfulInvocation(action, controller, args); - } else { - return super.getInvocation(action, controller); - } - } - - - // @Override -// public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { -// if (target.indexOf('.') != -1) { -// return; -// } -// -// isHandled[0] = true; -// String[] urlPara = {null}; -// Action action = getAction(target, urlPara); -// RestfulAction restfulAction = null; -// if (action != null && action.getActionKey().equals("/") && action.getMethodName().equals("index") -// && StrUtil.isNotBlank(target) && !target.equals(action.getActionKey())) { -// //如果被默认的"/"拦截,并且为index方法,但是请求的url又和actionKey不匹配,则有可能是restful请求 -// try { -// restfulAction = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); -// } catch (RequestMethodErrorException e) { -// handleActionException(target, request, response, action, e); -// return; -// } -// if (restfulAction != null) { -// action = null; -// } -// } -// //如果无法从内置的action中获取action则尝试从restful管理的action中获取 -// if (action == null) { -// // 尝试从restful 获取action -// try { -// if (restfulAction == null) { -// restfulAction = JbootRestfulManager.me().getRestfulAction(target, request.getMethod()); -// } -// } catch (RequestMethodErrorException e) { -// handleActionException(target, request, response, action, e); -// return; -// } -// if (restfulAction != null) { -// //restful 风格的请求处理 -// restfulRequest(restfulAction, target, request, response, isHandled); -// return; -// } -// if (log.isWarnEnabled()) { -// String qs = request.getQueryString(); -// log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs)); -// } -// renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); -// return; -// } -// //在获取到了 -// super.handle(target, request, response, isHandled); -// } - -// private void restfulRequest(RestfulAction restfulAction, String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { -// Action action = restfulAction.getAction(); -// Controller controller = null; -// try { -// controller = controllerFactory.getController(action.getControllerClass()); -// //注入依赖 -// if (injectDependency) { -// com.jfinal.aop.Aop.inject(controller); -// } -// //绑定controller本身到当前线程 -// JbootControllerContext.hold(controller); -// //初始化controller -// CPI._init_(controller, action, request, response, null); -// -// //解析参数 -// Object[] args = RestfulUtils.parseActionMethodParameters(target, restfulAction.getActionKey(), -// action.getMethod(), request, controller.getRawData()); -// RestfulCallback restfulCallback = new RestfulCallback(restfulAction, controller); -// -// Invocation invocation = new Invocation(controller, action.getMethod(), action.getInterceptors(), -// restfulCallback, args); -// -// RestfulInvocation fixedInvocation; -// if (devMode) { -// if (ActionReporter.isReportAfterInvocation(request)) { -// fixedInvocation = invokeInvocation(invocation); -// ActionReporter.report(target, controller, action); -// } else { -// ActionReporter.report(target, controller, action); -// fixedInvocation = invokeInvocation(invocation); -// } -// } else { -// fixedInvocation = invokeInvocation(invocation); -// } -// -// Object returnValue = fixedInvocation.getReturnValue(); -// -// // 判断是否带有@DownloadResponse -// DownloadResponse downloadResponse = action.getMethod().getAnnotation(DownloadResponse.class); -// if (returnValue == null && downloadResponse == null) { -// //无返回结果的 restful 请求 -// controller.renderNull(); -// } -// // 如果标记了@DownloadResponse,并且返回值不为空,会被认为自行处理了下载行为 -// if (downloadResponse != null) { -// return; -// } -// if (returnValue != null) { -// //初始化返回值 -// initRenderValue(returnValue, controller); -// } -// -// Render render = controller.getRender(); -// if (render instanceof ForwardActionRender) { -// String actionUrl = ((ForwardActionRender) render).getActionUrl(); -// if (target.equals(actionUrl)) { -// throw new RuntimeException("The forward action url is the same as before."); -// } else { -// handle(actionUrl, request, response, isHandled); -// } -// return; -// } -// -// if (render == null) { -// render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); -// } -// -// //初始化自定义头部 -// initResponseHeaders(response, fixedInvocation.getMethod()); -// //响应开始 -// render.setContext(request, response, action.getViewPath()).render(); -// -// } catch (RenderException | ParameterNullErrorException | ParameterParseErrorException | ActionException e) { -// handleActionException(target, request, response, action, e); -// } catch (Exception e) { -// String info = ClassUtil.buildMethodString(action.getMethod()); -// if (log.isErrorEnabled()) { -// String qs = request.getQueryString(); -// String targetInfo = qs == null ? target : target + "?" + qs; -// log.error(info + " : " + targetInfo, e); -// } -// //自定义错误处理 -// handleActionException(target, request, response, action, e); -// } finally { -// JbootControllerContext.release(); -// controllerFactory.recycle(controller); -// } -// } - -// private void handleActionException(String target, HttpServletRequest request, HttpServletResponse response, -// Action action, Exception e) { -// RestfulErrorRender restfulErrorRender = JbootRestfulManager.me().getRestfulErrorRender(); -// restfulErrorRender.setContext(request, response, action == null ? "" : action.getViewPath()); -// restfulErrorRender.init(target, action, e); -// restfulErrorRender.render(); -// } - - -// protected RestfulInvocation invokeInvocation(Invocation inv) { -// RestfulInvocation fixedInvocation = new RestfulInvocation(inv); -// fixedInvocation.invoke(); -// return fixedInvocation; -// } - - @Override - public void setResponse(HttpServletResponse response, Action action) { - ResponseHeader[] responseHeaders = action.getMethod().getAnnotationsByType(ResponseHeader.class); - ResponseHeaders responseHeadersList = action.getMethod().getAnnotation(ResponseHeaders.class); - if (responseHeadersList != null && responseHeadersList.value().length > 0) { - if (responseHeaders != null && responseHeaders.length > 0) { - responseHeaders = ArrayUtil.concat(responseHeaders, responseHeadersList.value()); - } else { - responseHeaders = responseHeadersList.value(); - } - } - if (responseHeaders.length > 0) { - for (ResponseHeader header : responseHeaders) { - response.setHeader(header.key(), header.value()); - } - } - } - -// protected void initRenderValue(Object o, Controller controller) { -// if (o.getClass().equals(String.class) -// || o.getClass().equals(int.class) -// || o.getClass().equals(double.class) -// || o.getClass().equals(byte.class) -// || o.getClass().equals(long.class) -// || o.getClass().equals(float.class) -// || o.getClass().equals(short.class) -// || o.getClass().equals(char.class) -// || o.getClass().equals(boolean.class)) { -// controller.renderText(String.valueOf(o)); -// } else if (o.getClass().equals(File.class)) { -// controller.renderFile((File) o); -// } else if (o.getClass().equals(ResponseEntity.class)) { -// ResponseEntity responseEntity = (ResponseEntity) o; -// //设置自定义头部信息 -// Map headers = responseEntity.getHeaders(); -// -// headers.forEach((k, v) -> controller.getResponse().setHeader(k, v)); -// //设置http状态代码 -// controller.getResponse().setStatus(responseEntity.getHttpStatus().value()); -// initRenderValue(responseEntity.getData(), controller); -// } else if (o instanceof Render) { //如果是render类型直接设置render -// controller.render((Render) o); -// } else { -// controller.renderJson(o); -// } -// } - -} diff --git a/src/main/java/io/jboot/components/restful/RestfulInvocation.java b/src/main/java/io/jboot/components/restful/RestfulInvocation.java deleted file mode 100644 index fe0df0d6..00000000 --- a/src/main/java/io/jboot/components/restful/RestfulInvocation.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.jboot.components.restful; - -import com.jfinal.aop.Invocation; -import com.jfinal.core.Action; -import com.jfinal.core.Controller; - -public class RestfulInvocation extends Invocation { - - private Action action; - - - public RestfulInvocation(Action action, Controller controller, Object[] args) { - super(controller, action.getMethod(), action.getInterceptors(), new RestfulCallback(action, controller), args); - this.action = action; - } - - - @Override - public Controller getController() { - return super.getTarget(); - } - - /** - * Return the action key. - * actionKey = controllerKey + methodName - */ - @Override - public String getActionKey() { - return action.getActionKey(); - } - - /** - * Return the controller key. - */ - @Override - public String getControllerKey() { - return action.getControllerKey(); - } - - /** - * Return view path of this controller. - */ - @Override - public String getViewPath() { - return action.getViewPath(); - } - - /** - * return true if it is action invocation. - */ - @Override - public boolean isActionInvocation() { - return action != null; - } - - -} diff --git a/src/main/java/io/jboot/components/restful/RestfulUtils.java b/src/main/java/io/jboot/components/restful/RestfulUtils.java deleted file mode 100644 index cbb64f62..00000000 --- a/src/main/java/io/jboot/components/restful/RestfulUtils.java +++ /dev/null @@ -1,201 +0,0 @@ -package io.jboot.components.restful; - - -import com.jfinal.kit.JsonKit; -import io.jboot.components.restful.annotation.PathVariable; -import io.jboot.components.restful.annotation.RequestBody; -import io.jboot.components.restful.annotation.RequestHeader; -import io.jboot.components.restful.annotation.RequestParam; -import io.jboot.components.restful.exception.ParameterNullErrorException; -import io.jboot.components.restful.exception.ParameterParseErrorException; -import io.jboot.utils.StrUtil; - -import javax.servlet.http.HttpServletRequest; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.util.HashMap; -import java.util.Map; - -public class RestfulUtils { - - /** - * 从url中解析路径参数 - * - * @param url - * @param actionKey - * @return - */ - public static Map parsePathVariables(String url, String actionKey) { - if (actionKey.contains("{") && actionKey.contains("}")) { - Map pathVariables = new HashMap<>(); - String[] paths = url.split("/"); - String[] _paths = actionKey.split("/"); - for (int i = 0; i < paths.length; i++) { - if (_paths[i].startsWith("{") && _paths[i].endsWith("}")) { - String pathKey = _paths[i].substring(1, _paths[i].length() - 1); - String value = paths[i]; - pathVariables.put(pathKey, value); - } - } - return pathVariables; - } else { - return null; - } - } - - /** - * 转换请求action请求的参数信息 - * - * @param target - * @param actionKey - * @param actionMethod - * @param request - * @param rawData - * @return - * @throws ParameterNullErrorException - * @throws ParameterParseErrorException - */ - public static Object[] parseActionMethodParameters(String target, String actionKey, Method actionMethod, HttpServletRequest request, String rawData) - throws ParameterNullErrorException, ParameterParseErrorException { - Object[] args = new Object[actionMethod.getParameters().length]; - for (int i = 0; i < actionMethod.getParameters().length; i++) { - Parameter parameter = actionMethod.getParameters()[i]; - RequestParam requestParam = parameter.getAnnotation(RequestParam.class); - RequestBody requestBody = parameter.getAnnotation(RequestBody.class); - RequestHeader requestHeader = parameter.getAnnotation(RequestHeader.class); - PathVariable pathVariable = parameter.getAnnotation(PathVariable.class); - String parameterName = parameter.getName(); - String values[]; - if (requestParam != null) { - if (StrUtil.isNotBlank(requestParam.value())) { - parameterName = requestParam.value(); - } - values = request.getParameterValues(parameterName); - parameter.getType(); - args[i] = parseRequestParamToParameter(values, parameterName, parameter.getType()); - if (args[i] == null && requestParam.required()) { - //要求参数为空,但是却并没有提供参数 - throw new ParameterNullErrorException(parameterName); - } - } else if (requestBody != null) { - args[i] = parseRequestBodyToParameter(rawData, parameterName, parameter.getType()); - } else if (requestHeader != null) { - if (StrUtil.isNotBlank(requestHeader.value())) { - parameterName = requestHeader.value(); - } - String value = request.getHeader(parameterName); - args[i] = parseRequestHeaderToParameter(value, parameterName, parameter.getType()); - if (args[i] == null && requestHeader.required()) { - //要求参数为空,但是却并没有提供参数 - throw new ParameterNullErrorException(parameterName); - } - } else if (pathVariable != null) { - if (StrUtil.isNotBlank(pathVariable.value())) { - parameterName = pathVariable.value(); - } - args[i] = parsePathVariableToParameter(target, actionKey, parameterName, parameter.getType()); - } else { - args[i] = null; - } - } - return args; - } - - /** - * 比对url请求路径 - * - * @param sourcePaths action配置的原路径 - * @param targetPaths 请求的目标路径 - * @return - */ - public static boolean comparePaths(String[] sourcePaths, String[] targetPaths) { - int matchingCount = 0; - for (int i = 0; i < sourcePaths.length; i++) { - if (sourcePaths[i].equals(targetPaths[i]) - || (sourcePaths[i].startsWith("{") && sourcePaths[i].endsWith("}"))) { - matchingCount += 1; - } - } - return matchingCount == sourcePaths.length; - } - - private static Object parseRequestParamToParameter(String[] value, String name, Class parameterTypeClass) { - if(parameterTypeClass.isArray()){ - Object [] objects = new Object[value.length]; - for (int i = 0; i < value.length; i++) { - objects[i] = parseCommonValue(value[i], name, parameterTypeClass); - } - return objects; - } else { - if(value != null && value.length > 0){ - return parseCommonValue(value[0], name, parameterTypeClass); - } - } - - return null; - } - - private static Object parseRequestHeaderToParameter(String header, String name, Class parameterTypeClass) { - return parseCommonValue(header, name, parameterTypeClass); - } - - private static Object parseRequestBodyToParameter(String body, String name, Class parameterTypeClass) { - //先当作基本数据来转换 - Object value = parseCommonValue(body, name, parameterTypeClass); - if(value == null){ - value = JsonKit.parse(body, parameterTypeClass); - } - return value; - } - - private static Object parsePathVariableToParameter(String target, String actionKey, String parameterName, Class parameterTypeClass) { - Map pathVariables = parsePathVariables(target, actionKey); - String value = pathVariables.get(parameterName); - return parseCommonValue(value, parameterName, parameterTypeClass); - } - - /** - * 转换基本类型参数,目前支持string,int,double,float,boolean,long基本类型数据 - * @param value - * @param name - * @param parameterTypeClass - * @return - */ - private static Object parseCommonValue(String value, String name, Class parameterTypeClass) { - if (StrUtil.isBlank(value)) { - return null; - } - if (parameterTypeClass.equals(String.class)) { - return value; - } else if (parameterTypeClass.equals(int.class)) { - try { - return Integer.valueOf(value); - } catch (NumberFormatException e) { - throw new ParameterParseErrorException(value, name, parameterTypeClass); - } - } else if (parameterTypeClass.equals(double.class)) { - try { - return Double.valueOf(value); - } catch (NumberFormatException e) { - throw new ParameterParseErrorException(value, name, parameterTypeClass); - } - } else if (parameterTypeClass.equals(float.class)) { - try { - return Float.valueOf(value); - } catch (NumberFormatException e) { - throw new ParameterParseErrorException(value, name, parameterTypeClass); - } - } else if (parameterTypeClass.equals(boolean.class)) { - return Boolean.valueOf(value); - } else if (parameterTypeClass.equals(long.class)) { - try { - return Long.valueOf(value); - } catch (NumberFormatException e) { - throw new ParameterParseErrorException(value, name, parameterTypeClass); - } - } else { - return null; - } - } - -} diff --git a/src/main/java/io/jboot/components/restful/annotation/DeleteMapping.java b/src/main/java/io/jboot/components/restful/annotation/DeleteMapping.java deleted file mode 100644 index 2483f1c3..00000000 --- a/src/main/java/io/jboot/components/restful/annotation/DeleteMapping.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.jboot.components.restful.annotation; - -import java.lang.annotation.*; - -/** - * 删除delete 方法注解 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@Documented -public @interface DeleteMapping { - - /** - * url mapping - * @return - */ - String value() default ""; - -} diff --git a/src/main/java/io/jboot/components/restful/annotation/DownloadResponse.java b/src/main/java/io/jboot/components/restful/annotation/DownloadResponse.java deleted file mode 100644 index c4aa83e8..00000000 --- a/src/main/java/io/jboot/components/restful/annotation/DownloadResponse.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.jboot.components.restful.annotation; - -import java.lang.annotation.*; - -/** - * 标注controller action是一个下载响应,并且需要action自行处理response - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@Documented -public @interface DownloadResponse { -} diff --git a/src/main/java/io/jboot/components/restful/annotation/GetMapping.java b/src/main/java/io/jboot/components/restful/annotation/GetMapping.java deleted file mode 100644 index c571a745..00000000 --- a/src/main/java/io/jboot/components/restful/annotation/GetMapping.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.jboot.components.restful.annotation; - -import java.lang.annotation.*; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@Documented -public @interface GetMapping { - - /** - * url mapping - * @return - */ - String value() default ""; - -} diff --git a/src/main/java/io/jboot/components/restful/annotation/PathVariable.java b/src/main/java/io/jboot/components/restful/annotation/PathVariable.java deleted file mode 100644 index 68a96249..00000000 --- a/src/main/java/io/jboot/components/restful/annotation/PathVariable.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.jboot.components.restful.annotation; - -import java.lang.annotation.*; - -/** - * 路径参数注解 - * /user/{id}/cards - * 支持如下类型参数注入: - * string - * int - * double - * float - * boolean - * long - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -@Documented -public @interface PathVariable { - - /** - * 如果为空则默认为参数名本身 - * @return - */ - String value() default ""; - -} diff --git a/src/main/java/io/jboot/components/restful/annotation/PostMapping.java b/src/main/java/io/jboot/components/restful/annotation/PostMapping.java deleted file mode 100644 index e0f9a45a..00000000 --- a/src/main/java/io/jboot/components/restful/annotation/PostMapping.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.jboot.components.restful.annotation; - -import java.lang.annotation.*; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@Documented -public @interface PostMapping { - - /** - * url mapping - * @return - */ - String value() default ""; - -} diff --git a/src/main/java/io/jboot/components/restful/annotation/PutMapping.java b/src/main/java/io/jboot/components/restful/annotation/PutMapping.java deleted file mode 100644 index 8d0c66d2..00000000 --- a/src/main/java/io/jboot/components/restful/annotation/PutMapping.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.jboot.components.restful.annotation; - -import java.lang.annotation.*; - -/** - * Put 请求方法定义 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@Documented -public @interface PutMapping { - - /** - * url mapping - * @return - */ - String value() default ""; - -} diff --git a/src/main/java/io/jboot/components/restful/annotation/RequestBody.java b/src/main/java/io/jboot/components/restful/annotation/RequestBody.java deleted file mode 100644 index 73327a66..00000000 --- a/src/main/java/io/jboot/components/restful/annotation/RequestBody.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.jboot.components.restful.annotation; - -import java.lang.annotation.*; - -/** - * 请求体参数注解 - * 支持如下类型参数: - * string / string[] - * int / int[] - * double / double[] - * float / float[] - * boolean / boolean[] - * long / long[] - * object / object[] - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -@Documented -public @interface RequestBody { -} diff --git a/src/main/java/io/jboot/components/restful/annotation/RequestHeader.java b/src/main/java/io/jboot/components/restful/annotation/RequestHeader.java deleted file mode 100644 index 5a967970..00000000 --- a/src/main/java/io/jboot/components/restful/annotation/RequestHeader.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.jboot.components.restful.annotation; - -import java.lang.annotation.*; - -/** - * 请求头部信息注解 - * 支持如下类型参数注入: - * string - * int - * double - * float - * boolean - * long - * - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -@Documented -public @interface RequestHeader { - - /** - * 如果为空则默认为参数名本身 - * @return - */ - String value() default ""; - - boolean required() default false; - -} diff --git a/src/main/java/io/jboot/components/restful/annotation/RequestParam.java b/src/main/java/io/jboot/components/restful/annotation/RequestParam.java deleted file mode 100644 index e4a16089..00000000 --- a/src/main/java/io/jboot/components/restful/annotation/RequestParam.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.jboot.components.restful.annotation; - -import java.lang.annotation.*; - -/** - * 请求参数注解 - * 支持如下类型参数: - * string / string[] - * int / int[] - * double / double[] - * float / float[] - * boolean / boolean[] - * long / long[] - * - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -@Documented -public @interface RequestParam { - - /** - * 如果为空则默认为参数名本身 - * @return - */ - String value() default ""; - - boolean required() default false; - -} diff --git a/src/main/java/io/jboot/components/restful/annotation/ResponseHeader.java b/src/main/java/io/jboot/components/restful/annotation/ResponseHeader.java deleted file mode 100644 index 626f22bf..00000000 --- a/src/main/java/io/jboot/components/restful/annotation/ResponseHeader.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.jboot.components.restful.annotation; - -import java.lang.annotation.*; - -/** - * 自定义响应头 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@Documented -public @interface ResponseHeader { - - String key(); - - String value(); - -} diff --git a/src/main/java/io/jboot/components/restful/annotation/ResponseHeaders.java b/src/main/java/io/jboot/components/restful/annotation/ResponseHeaders.java deleted file mode 100644 index d51f43ae..00000000 --- a/src/main/java/io/jboot/components/restful/annotation/ResponseHeaders.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.jboot.components.restful.annotation; - -import java.lang.annotation.*; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@Documented -public @interface ResponseHeaders { - - ResponseHeader[] value(); - -} diff --git a/src/main/java/io/jboot/components/restful/annotation/RestController.java b/src/main/java/io/jboot/components/restful/annotation/RestController.java deleted file mode 100644 index 6650c476..00000000 --- a/src/main/java/io/jboot/components/restful/annotation/RestController.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.jboot.components.restful.annotation; - -import java.lang.annotation.*; - -/** - * rest controller 标识 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Documented -public @interface RestController { -} diff --git a/src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java b/src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java deleted file mode 100644 index 081867cc..00000000 --- a/src/main/java/io/jboot/components/restful/exception/ParameterNullErrorException.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.jboot.components.restful.exception; - -/** - * 参数为空错误 - */ -public class ParameterNullErrorException extends RuntimeException { - - private String parameterName; - - - public String getParameterName() { - return parameterName; - } - - public ParameterNullErrorException(String parameterName) { - super("Parameter '"+parameterName+"' specifies a forced check, but the value is null"); - this.parameterName = parameterName; - } - - public ParameterNullErrorException(Exception e) { - super(e); - } - -} diff --git a/src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java b/src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java deleted file mode 100644 index 2a025ab6..00000000 --- a/src/main/java/io/jboot/components/restful/exception/ParameterParseErrorException.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.jboot.components.restful.exception; - -/** - * 参数类型错误 - */ -public class ParameterParseErrorException extends RuntimeException { - - - private String parameterValue; - - private String parameterName; - - private Class parameterType; - - public String getParameterValue() { - return parameterValue; - } - - public String getParameterName() { - return parameterName; - } - - public Class getParameterType() { - return parameterType; - } - - public ParameterParseErrorException(String parameterValue, String parameterName, Class parameterType) { - super("Error resolving parameter '" + parameterName + "', unable to match value '" - + parameterValue + "' to specified type '" + parameterType.getName() + "'"); - this.parameterValue = parameterValue; - this.parameterName = parameterName; - this.parameterType = parameterType; - } - - public ParameterParseErrorException(Exception e) { - super(e); - } - -} diff --git a/src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java b/src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java deleted file mode 100644 index e72f83c9..00000000 --- a/src/main/java/io/jboot/components/restful/exception/RequestMethodErrorException.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.jboot.components.restful.exception; - -/** - * 请求方法错误 - */ -public class RequestMethodErrorException extends RuntimeException { - - private String actionKey; - - private String actionMethod; - - private String target; - - private String targetMethod; - - public String getActionKey() { - return actionKey; - } - - public String getActionMethod() { - return actionMethod; - } - - public String getTarget() { - return target; - } - - public String getTargetMethod() { - return targetMethod; - } - - public RequestMethodErrorException(String actionKey, String actionMethod, String target, String targetMethod) { - super("'" + target + "' is specified as a '" + actionMethod + "' request. '" + targetMethod + "' requests are not supported"); - this.actionKey = actionKey; - this.actionMethod = actionMethod; - this.target = target; - this.targetMethod = targetMethod; - } -} diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index d5209810..204d9dde 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -36,9 +36,6 @@ import io.jboot.app.config.support.nacos.NacosConfigManager; import io.jboot.components.gateway.JbootGatewayHandler; import io.jboot.components.gateway.JbootGatewayManager; import io.jboot.components.limiter.LimiterManager; -import io.jboot.components.restful.JbootRestfulManager; -import io.jboot.components.restful.RestfulHandler; -import io.jboot.components.restful.annotation.RestController; import io.jboot.components.rpc.JbootrpcManager; import io.jboot.components.schedule.JbootScheduleManager; import io.jboot.core.listener.JbootAppListenerManager; @@ -76,9 +73,6 @@ import java.util.Properties; public class JbootCoreConfig extends JFinalConfig { private List routeList = new ArrayList<>(); - private List restfulRoutes = new ArrayList<>(); - - private JbootRestfulManager.Config restfulConfig = new JbootRestfulManager.Config(); public JbootCoreConfig() { @@ -179,13 +173,6 @@ public class JbootCoreConfig extends JFinalConfig { continue; } - //检查是否是restful类型的controller,如果是则加入restful专门指定的routes - RestController restController = clazz.getAnnotation(RestController.class); - if (restController != null) { - restfulRoutes.add(new Routes.Route(value, clazz, value)); - continue; - } - String viewPath = AnnotationUtil.get(mapping.viewPath()); if (StrUtil.isNotBlank(viewPath)) { @@ -207,18 +194,6 @@ public class JbootCoreConfig extends JFinalConfig { JbootControllerManager.me().setMapping(route.getControllerKey(), route.getControllerClass()); } - if (!restfulRoutes.isEmpty()) { - //处理restful专属的routes - restfulConfig.setRoutes(restfulRoutes) - .setBaseViewPath(routes.getBaseViewPath()) - .setMappingSupperClass(routes.getMappingSuperClass()) - .setRouteInterceptors(routes.getInterceptors()); - for (Routes.Route route : restfulRoutes) { - JbootControllerManager.me().setMapping(route.getControllerKey(), route.getControllerClass()); - } - routeList.addAll(restfulRoutes); - } - routeList.addAll(routes.getRouteItemList()); } @@ -293,15 +268,10 @@ public class JbootCoreConfig extends JFinalConfig { handlers.add(new JbootGatewayHandler()); handlers.add(new JbootFilterHandler()); handlers.add(new JbootHandler()); -// handlers.setActionHandler(new RestfulHandler()); //若用户自己没配置 ActionHandler,默认使用 JbootActionHandler if (handlers.getActionHandler() == null) { - if (restfulRoutes.isEmpty()) { - handlers.setActionHandler(new RestfulHandler()); - } else { - handlers.setActionHandler(new JbootActionHandler()); - } + handlers.setActionHandler(new JbootActionHandler()); } } @@ -324,8 +294,6 @@ public class JbootCoreConfig extends JFinalConfig { JbootSeataManager.me().init(); SentinelManager.me().init(); JbootGatewayManager.me().init(); - JbootRestfulManager.me().init(restfulConfig); - JbootAppListenerManager.me().onStart(); } diff --git a/src/test/java/io/jboot/test/restful/RestfulController.java b/src/test/java/io/jboot/test/restful/RestfulController.java deleted file mode 100644 index 08a2367d..00000000 --- a/src/test/java/io/jboot/test/restful/RestfulController.java +++ /dev/null @@ -1,117 +0,0 @@ -package io.jboot.test.restful; - -import com.jfinal.aop.Before; -import com.jfinal.aop.Inject; -import com.jfinal.aop.Interceptor; -import com.jfinal.aop.Invocation; -import com.jfinal.core.NotAction; -import com.jfinal.kit.JsonKit; -import com.jfinal.kit.StrKit; -import io.jboot.components.restful.HttpStatus; -import io.jboot.components.restful.ResponseEntity; -import io.jboot.components.restful.annotation.*; -import io.jboot.web.controller.JbootController; -import io.jboot.web.controller.annotation.RequestMapping; -import io.jboot.web.cors.EnableCORS; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -@RestController -@RequestMapping("/restful") -public class RestfulController extends JbootController { - - public static class Data implements Serializable { - private String id; - private String name; - private int age; - - public Data(String id, String name, int age) { - this.id = id; - this.name = name; - this.age = age; - } - - public String getId() { - return id; - } - - public String getName() { - return name; - } - - public int getAge() { - return age; - } - } - - public static class RestfulInterceptor implements Interceptor { - - public void intercept(Invocation inv) { - System.out.println("--------> restful request begin"); - inv.invoke(); - System.out.println("--------> restful request end"); - } - } - - @Inject - private RestfulService restfulService; - - @NotAction - public List initData(){ - List users = new ArrayList<>(); - users.add(new RestfulController.Data("1", "tom", 18)); - users.add(new RestfulController.Data("2", "andy", 29)); - users.add(new RestfulController.Data("3", "max", 13)); - return users; - } - - //GET /restful - @GetMapping - @EnableCORS - @Before({RestfulInterceptor.class}) - @ResponseHeaders({@ResponseHeader(key = "d-head-1", value = "a"), @ResponseHeader(key = "d-head-2", value = "b")}) - public List users(){ - return initData(); - } - - // GET /restful/randomKey - @GetMapping("/randomKey") - public String randomKey(){ - return restfulService.getRandomKey(); - } - - // GET /restful/users - @GetMapping("/users") - public ResponseEntity> entityUsers(){ - return new ResponseEntity<>(initData()).addHeader("x-token", StrKit.getRandomUUID()).setHttpStatus(HttpStatus.ACCEPTED); - } - - // PUT /restful - @PutMapping - public void create(@RequestBody RestfulController.Data data){ - System.out.println("get request body data:\n" + JsonKit.toJson(data)); - } - - // PUT /restful/createList - @PutMapping("/createList") - public void createUsers(@RequestBody List users){ - System.out.println("get request body data:\n" + JsonKit.toJson(users)); - } - - // DELETE /restful/:id - @DeleteMapping("/{id}") - public void delete(@PathVariable("id") String id){ - System.out.println("delete by id : " + id ); - } - - // DELETE /restful/delete - @DeleteMapping("/delete") - public void deleteByName(@RequestParam(value = "name", required = true) String name, - @RequestHeader String token){ - System.out.println("delete by name : " + name); - System.out.println("get token header : " + token); - } - -} diff --git a/src/test/java/io/jboot/test/restful/RestfulService.java b/src/test/java/io/jboot/test/restful/RestfulService.java deleted file mode 100644 index aea7b1cd..00000000 --- a/src/test/java/io/jboot/test/restful/RestfulService.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.jboot.test.restful; - -public interface RestfulService { - String getRandomKey(); -} diff --git a/src/test/java/io/jboot/test/restful/RestfulServiceImpl.java b/src/test/java/io/jboot/test/restful/RestfulServiceImpl.java deleted file mode 100644 index 1c042af3..00000000 --- a/src/test/java/io/jboot/test/restful/RestfulServiceImpl.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.jboot.test.restful; - -import com.jfinal.kit.StrKit; -import io.jboot.aop.annotation.Bean; - -@Bean -public class RestfulServiceImpl implements RestfulService { - @Override - public String getRandomKey() { - return StrKit.getRandomUUID(); - } -} -- Gitee From 451c0264df0ca43ef2bb711e18db207250e62d12 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 4 Apr 2020 09:23:38 +0800 Subject: [PATCH 0198/1965] v3.1.5 --- .../io/jboot/test/controller/IndexController.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/test/java/io/jboot/test/controller/IndexController.java b/src/test/java/io/jboot/test/controller/IndexController.java index fcbdf8cd..f23b7c76 100644 --- a/src/test/java/io/jboot/test/controller/IndexController.java +++ b/src/test/java/io/jboot/test/controller/IndexController.java @@ -1,6 +1,7 @@ package io.jboot.test.controller; import com.jfinal.kit.PathKit; +import io.jboot.test.db.model.User; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; @@ -18,4 +19,14 @@ public class IndexController extends JbootController { public void error500(){ } + + public String rhello(){ + return "hello world"; + } + + public User ruser(){ + User user = new User(); + user.put("aa","bbb"); + return user; + } } -- Gitee From 73ac39624b7e4c013deb88158b6b9708cbb6e67f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 4 Apr 2020 16:26:00 +0800 Subject: [PATCH 0199/1965] v3.1.5 --- .../components/gateway/GatewayHttpProxy.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index 5c4c53f2..5a06ab0d 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -81,6 +81,10 @@ public class GatewayHttpProxy { */ configConnection(conn, req); + + /** + * 发送 http 请求 + */ conn.connect(); /** @@ -108,16 +112,15 @@ public class GatewayHttpProxy { private void copyRequestStreamToConnection(HttpServletRequest req, HttpURLConnection conn) throws IOException { + + // 如果不是 post 请求,不需要复制 + if ("get".equalsIgnoreCase(req.getMethod())) { + return; + } + OutputStream outStream = null; InputStream inStream = null; try { - - // 如果不是 post 请求,不需要复制 - if ("get".equalsIgnoreCase(req.getMethod())) { - return; - } - - conn.setDoOutput(true); outStream = conn.getOutputStream(); inStream = req.getInputStream(); int n; @@ -202,7 +205,11 @@ public class GatewayHttpProxy { conn.setConnectTimeout(connectTimeOut); conn.setInstanceFollowRedirects(true); conn.setUseCaches(false); -// conn.setDefaultUseCaches(); + + //不是 get 请求 + if (!"get".equalsIgnoreCase(req.getMethod())) { + conn.setDoOutput(true); + } conn.setRequestMethod(req.getMethod()); Enumeration headerNames = req.getHeaderNames(); -- Gitee From 2d0ca683f1f8e2fe1360cbd7aa9eb21d12775bd4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 4 Apr 2020 16:36:50 +0800 Subject: [PATCH 0200/1965] v3.1.5 --- .../components/gateway/GatewayHttpProxy.java | 44 +++++++------------ 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index 5a06ab0d..8845a07d 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -81,16 +81,18 @@ public class GatewayHttpProxy { */ configConnection(conn, req); + conn.setRequestMethod(req.getMethod()); - /** - * 发送 http 请求 - */ - conn.connect(); - - /** - * 复制 post 请求内容到目标服务器 - */ - copyRequestStreamToConnection(req, conn); + // get 请求 + if ("get".equalsIgnoreCase(req.getMethod())) { + conn.connect(); + } + // post 请求 + else { + conn.setDoOutput(true); + conn.setDoInput(true); + copyRequestStreamToConnection(req, conn); + } /** @@ -99,7 +101,7 @@ public class GatewayHttpProxy { configResponse(resp, conn); /** - * 复制目标流到 Response + * 复制目标相应流到 Response */ copyStreamToResponse(conn, resp); @@ -112,21 +114,15 @@ public class GatewayHttpProxy { private void copyRequestStreamToConnection(HttpServletRequest req, HttpURLConnection conn) throws IOException { - - // 如果不是 post 请求,不需要复制 - if ("get".equalsIgnoreCase(req.getMethod())) { - return; - } - OutputStream outStream = null; InputStream inStream = null; try { outStream = conn.getOutputStream(); inStream = req.getInputStream(); - int n; + int len; byte[] buffer = new byte[1024]; - while (-1 != (n = inStream.read(buffer))) { - outStream.write(buffer, 0, n); + while ((len = inStream.read(buffer)) != -1) { + outStream.write(buffer, 0, len); } } finally { @@ -152,7 +148,6 @@ public class GatewayHttpProxy { } finally { quetlyClose(inStream, reader); } - } @@ -185,7 +180,6 @@ public class GatewayHttpProxy { } private static InputStream getInputStream(HttpURLConnection conn) throws IOException { - InputStream stream = conn.getResponseCode() >= 400 ? conn.getErrorStream() : conn.getInputStream(); @@ -195,7 +189,6 @@ public class GatewayHttpProxy { } else { return stream; } - } @@ -203,14 +196,9 @@ public class GatewayHttpProxy { conn.setReadTimeout(readTimeOut); conn.setConnectTimeout(connectTimeOut); - conn.setInstanceFollowRedirects(true); + conn.setInstanceFollowRedirects(false); conn.setUseCaches(false); - //不是 get 请求 - if (!"get".equalsIgnoreCase(req.getMethod())) { - conn.setDoOutput(true); - } - conn.setRequestMethod(req.getMethod()); Enumeration headerNames = req.getHeaderNames(); while (headerNames.hasMoreElements()) { -- Gitee From 72a2c2ef0a713c0c9824dc89660499ed48054fb0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 4 Apr 2020 16:52:49 +0800 Subject: [PATCH 0201/1965] v3.1.5 --- .../io/jboot/components/gateway/GatewaySentinelProcesser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java b/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java index 7e7de2b8..5aefb152 100644 --- a/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java +++ b/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java @@ -37,7 +37,7 @@ public class GatewaySentinelProcesser { Entry entry = null; String resourceName = GatewayUtil.buildResource(req); try { - entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN); + entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_API_GATEWAY, EntryType.IN); runnable.run(); } catch (BlockException ex) { processBlocked(config, req, resp); -- Gitee From f6ea6cd69fe394dc21a33b820012349ffd0cbe8f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 4 Apr 2020 16:55:07 +0800 Subject: [PATCH 0202/1965] v3.1.5 --- src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index 8845a07d..07a1ae42 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -81,7 +81,6 @@ public class GatewayHttpProxy { */ configConnection(conn, req); - conn.setRequestMethod(req.getMethod()); // get 请求 if ("get".equalsIgnoreCase(req.getMethod())) { @@ -200,6 +199,7 @@ public class GatewayHttpProxy { conn.setUseCaches(false); conn.setRequestMethod(req.getMethod()); + Enumeration headerNames = req.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); -- Gitee From 83e46ea8722ac6bd74ab25453ebcb89c1475ff04 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 4 Apr 2020 17:40:23 +0800 Subject: [PATCH 0203/1965] v3.1.5 --- .../components/gateway/GatewayHttpProxy.java | 17 ++++++++++++++--- src/test/resources/jboot.properties | 14 ++++++-------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index 07a1ae42..eabd9f87 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -171,8 +171,16 @@ public class GatewayHttpProxy { Set headerNames = headerFields.keySet(); for (String headerName : headerNames) { //需要排除 Content-Encoding,因为 Server 可能已经使用 gzip 压缩,但是此代理已经对 gzip 内容进行解压了 - if (StrUtil.isNotBlank(headerName) && !"Content-Encoding".equalsIgnoreCase(headerName)) { - resp.setHeader(headerName, conn.getHeaderField(headerName)); + //需要排除 Content-Type,因为会可能会进行多次设置 + if (StrUtil.isBlank(headerName) + || "Content-Encoding".equalsIgnoreCase(headerName) + || "Content-Type".equalsIgnoreCase(headerName)) { + continue; + } else { + String headerFieldValue = conn.getHeaderField(headerName); + if (StrUtil.isNotBlank(headerFieldValue)) { + resp.setHeader(headerName, headerFieldValue); + } } } } @@ -204,7 +212,10 @@ public class GatewayHttpProxy { while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); if (StrUtil.isNotBlank(headerName)) { - conn.setRequestProperty(headerName, req.getHeader(headerName)); + String headerFieldValue = req.getHeader(headerName); + if (StrUtil.isNotBlank(headerFieldValue)) { + conn.setRequestProperty(headerName, headerFieldValue); + } } } } diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index 151711f4..378fb0f8 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -38,15 +38,13 @@ config.test.test.ccc.type = type3 # 配置 gateway ,访问首页的时候目标地址设置为 baidu 的地址 -jboot.gateway.enable = true -jboot.gateway.uri = https://www.baidu.com -#jboot.gateway.proxyRetries = 2 -jboot.gateway.pathEquals = / +#jboot.gateway.enable = true +#jboot.gateway.uri = https://www.baidu.com +#jboot.gateway.pathEquals = / jboot.gateway.gitee.enable = true -jboot.gateway.gitee.uri= https://gitee.com -jboot.gateway.gitee.sentinelEnable= true -jboot.gateway.gitee.sentinelBlockPage= / -jboot.gateway.gitee.pathStartsWith = /fuhai +jboot.gateway.gitee.uri= https://search.gitee.com +jboot.gateway.gitee.queryContains = q + jboot.rpc.type = local -- Gitee From 7ed5610db071c90cb0923485cf691d75350c2f1c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 4 Apr 2020 18:11:22 +0800 Subject: [PATCH 0204/1965] v3.1.5 --- .../io/jboot/test/controller/IndexController.java | 12 ++++-------- src/test/resources/jboot.properties | 9 +++------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/test/java/io/jboot/test/controller/IndexController.java b/src/test/java/io/jboot/test/controller/IndexController.java index f23b7c76..d402d718 100644 --- a/src/test/java/io/jboot/test/controller/IndexController.java +++ b/src/test/java/io/jboot/test/controller/IndexController.java @@ -1,7 +1,6 @@ package io.jboot.test.controller; import com.jfinal.kit.PathKit; -import io.jboot.test.db.model.User; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; @@ -20,13 +19,10 @@ public class IndexController extends JbootController { } - public String rhello(){ - return "hello world"; + public String ping(){ + return "ping:" + getPara("ping"); } - public User ruser(){ - User user = new User(); - user.put("aa","bbb"); - return user; - } + + } diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index 378fb0f8..d285e323 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -38,13 +38,10 @@ config.test.test.ccc.type = type3 # 配置 gateway ,访问首页的时候目标地址设置为 baidu 的地址 -#jboot.gateway.enable = true -#jboot.gateway.uri = https://www.baidu.com -#jboot.gateway.pathEquals = / +jboot.gateway.enable = true +jboot.gateway.uri = https://www.baidu.com +jboot.gateway.pathEquals = / -jboot.gateway.gitee.enable = true -jboot.gateway.gitee.uri= https://search.gitee.com -jboot.gateway.gitee.queryContains = q jboot.rpc.type = local -- Gitee From cb0bd0864012328e9ab0d310770cefb4797b7962 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 4 Apr 2020 18:37:21 +0800 Subject: [PATCH 0205/1965] add group and version config in jbootRpcConfig --- .../jboot/components/rpc/JbootrpcConfig.java | 66 +++++++++++++++++-- .../components/rpc/dubbo/JbootDubborpc.java | 22 ++++++- .../components/rpc/motan/JbootMotanrpc.java | 32 ++++++--- 3 files changed, 103 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java index 78b01fd1..d132fcdc 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java @@ -32,15 +32,27 @@ public class JbootrpcConfig { //用于直连时的配置,直连一般只用于测试环境 //com.service.AAAService:127.0.0.1:8080,com.service.XXXService:127.0.0.1:8080 - private Map urls; + private Map urls; //服务的provider指定,可以通过注解 @RPCBean 指定,也可以通过此处指定,此处的配置优先于注解 //com.service.AAAService:providerName,com.service.XXXService:providerName - private Map providers; + private Map providers; //服务的consumer指定,可以通过注解 @RPCInject 指定,也可以通过此处指定,此处的配置优先于注解 //com.service.AAAService:providerName,com.service.XXXService:providerName - private Map consumers; + private Map consumers; + + //当不配置的时候,默认版本号 + private String defaultVersion = "1.0.0"; + + //指定的服务的版本号 + private Map versions; + + //当不指定的时候,默认分组 + private String defaultGroup; + + //指定的服务的分组 + private Map groups; //本地自动暴露 @RPCBean 的 service private boolean autoExportEnable = true; @@ -62,7 +74,7 @@ public class JbootrpcConfig { this.urls = urls; } - public String getUrl(String serviceClass){ + public String getUrl(String serviceClass) { return urls == null ? null : urls.get(serviceClass); } @@ -74,7 +86,7 @@ public class JbootrpcConfig { this.providers = providers; } - public String getProvider(String serviceClass){ + public String getProvider(String serviceClass) { return providers == null ? null : providers.get(serviceClass); } @@ -86,10 +98,52 @@ public class JbootrpcConfig { this.consumers = consumers; } - public String getConsumer(String serviceClass){ + public String getConsumer(String serviceClass) { return consumers == null ? null : consumers.get(serviceClass); } + public String getDefaultVersion() { + return defaultVersion; + } + + public void setDefaultVersion(String defaultVersion) { + this.defaultVersion = defaultVersion; + } + + public Map getVersions() { + return versions; + } + + public void setVersions(Map versions) { + this.versions = versions; + } + + public String getVersion(String className) { + String version = versions == null ? null : versions.get(className); + return version == null ? defaultVersion : version; + } + + public String getDefaultGroup() { + return defaultGroup; + } + + public void setDefaultGroup(String defaultGroup) { + this.defaultGroup = defaultGroup; + } + + public Map getGroups() { + return groups; + } + + public void setGroups(Map groups) { + this.groups = groups; + } + + public String getGroup(String className) { + String group = groups == null ? null : groups.get(className); + return group == null ? defaultGroup : group; + } + public boolean isAutoExportEnable() { return autoExportEnable; } diff --git a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java index 169e2bec..1ce4648f 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java @@ -41,15 +41,23 @@ public class JbootDubborpc extends JbootrpcBase { reference.setInterface(interfaceClass); String directUrl = rpcConfig.getUrl(interfaceClass.getName()); - if (StrUtil.isNotBlank(directUrl)){ + if (StrUtil.isNotBlank(directUrl)) { reference.setUrl(directUrl); } String consumer = rpcConfig.getConsumer(interfaceClass.getName()); - if (consumer != null){ + if (consumer != null) { reference.setConsumer(DubboUtil.getConsumer(consumer)); } + if (reference.getGroup() == null) { + reference.setGroup(rpcConfig.getGroup(interfaceClass.getName())); + } + + if (reference.getVersion() == null) { + reference.setVersion(rpcConfig.getVersion(interfaceClass.getName())); + } + return reference.get(); } @@ -61,10 +69,18 @@ public class JbootDubborpc extends JbootrpcBase { service.setRef((T) object); String provider = rpcConfig.getProvider(interfaceClass.getName()); - if (provider != null){ + if (provider != null) { service.setProvider(DubboUtil.getProvider(provider)); } + if (service.getGroup() == null) { + service.setGroup(rpcConfig.getGroup(interfaceClass.getName())); + } + + if (service.getVersion() == null) { + service.setVersion(rpcConfig.getVersion(interfaceClass.getName())); + } + service.export(); return true; } diff --git a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java index f9c1c4b4..9d4282c6 100644 --- a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java +++ b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java @@ -51,6 +51,14 @@ public class JbootMotanrpc extends JbootrpcBase { referer.setBasicReferer(MotanUtil.getBaseReferer(consumer)); } + if (referer.getGroup() == null) { + referer.setGroup(rpcConfig.getGroup(interfaceClass.getName())); + } + + if (referer.getVersion() == null) { + referer.setVersion(rpcConfig.getVersion(interfaceClass.getName())); + } + return referer.getRef(); } @@ -62,19 +70,27 @@ public class JbootMotanrpc extends JbootrpcBase { MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, false); - ServiceConfig serviceConfig = MotanUtil.toServiceConfig(config); - serviceConfig.setInterface(interfaceClass); - serviceConfig.setRef((T) object); - serviceConfig.setShareChannel(true); - serviceConfig.setExport(defaultConfig.getExport(interfaceClass.getName())); - serviceConfig.setHost(defaultConfig.getHost(interfaceClass.getName())); + ServiceConfig service = MotanUtil.toServiceConfig(config); + service.setInterface(interfaceClass); + service.setRef((T) object); + service.setShareChannel(true); + service.setExport(defaultConfig.getExport(interfaceClass.getName())); + service.setHost(defaultConfig.getHost(interfaceClass.getName())); String provider = rpcConfig.getProvider(interfaceClass.getName()); if (provider != null) { - serviceConfig.setBasicService(MotanUtil.getBaseService(provider)); + service.setBasicService(MotanUtil.getBaseService(provider)); + } + + if (service.getGroup() == null) { + service.setGroup(rpcConfig.getGroup(interfaceClass.getName())); + } + + if (service.getVersion() == null) { + service.setVersion(rpcConfig.getVersion(interfaceClass.getName())); } - serviceConfig.export(); + service.export(); MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true); } -- Gitee From b5e16bac5ae61b53f0718df89a82985f1afe0a18 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 4 Apr 2020 18:49:21 +0800 Subject: [PATCH 0206/1965] update gateway docs --- doc/docs/gateway.md | 71 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/doc/docs/gateway.md b/doc/docs/gateway.md index d5192ec0..331836e2 100644 --- a/doc/docs/gateway.md +++ b/doc/docs/gateway.md @@ -6,6 +6,7 @@ - path路由 - host路由 - query路由 +- 多个 Gateway 配置 - 其他 @@ -187,6 +188,74 @@ jboot.gateway.queryContains = aaa 以上配置中,如果用户访问 `www.xxx.com/controller?aaa=bbb` 会自动路由到 `http://youdomain:8080/controller?aaa=bbb` ,或者用户访问 `www.xxx.com/controller?aaa=ccc` 也会路由到 `http://youdomain:8080/controller?aaa=ccc`,因为 query 都包含了 `aaa=**` 的请求,但是如果用户访问 `www.xxx.com/controller?other=aaa`不会路由。 + + +## 多个 Gateway 配置 + +``` +jboot.gateway.aaa.name = name +jboot.gateway.aaa.uri = http://youdomain:8080 +jboot.gateway.aaa.enable = true +jboot.gateway.aaa.sentinelEnable = false +jboot.gateway.aaa.sentinelBlockPage = /block +jboot.gateway.aaa.proxyReadTimeout = 10000 +jboot.gateway.aaa.proxyConnectTimeout = 5000 +jboot.gateway.aaa.proxyContentType = text/html;charset=utf-8 +jboot.gateway.aaa.interceptors = com.xxx.Interceptor1,com.xxx.Interceptor2 +jboot.gateway.aaa.pathEquals = /path +jboot.gateway.aaa.pathContains = /path +jboot.gateway.aaa.pathStartsWith = /path +jboot.gateway.aaa.pathEndswith = /path +jboot.gateway.aaa.hostEquals = xxx.com +jboot.gateway.aaa.hostContains = xxx.com +jboot.gateway.aaa.hostStartsWith = xxx.com +jboot.gateway.aaa.hostEndswith = xxx.com +jboot.gateway.aaa.queryEquals = aa:bb,cc:dd +jboot.gateway.aaa.queryContains = aa,bb + + +jboot.gateway.bbb.name = name +jboot.gateway.bbb.uri = http://youdomain:8080 +jboot.gateway.bbb.enable = true +jboot.gateway.bbb.sentinelEnable = false +jboot.gateway.bbb.sentinelBlockPage = /block +jboot.gateway.bbb.proxyReadTimeout = 10000 +jboot.gateway.bbb.proxyConnectTimeout = 5000 +jboot.gateway.bbb.proxyContentType = text/html;charset=utf-8 +jboot.gateway.bbb.interceptors = com.xxx.Interceptor1,com.xxx.Interceptor2 +jboot.gateway.bbb.pathEquals = /path +jboot.gateway.bbb.pathContains = /path +jboot.gateway.bbb.pathStartsWith = /path +jboot.gateway.bbb.pathEndswith = /path +jboot.gateway.bbb.hostEquals = xxx.com +jboot.gateway.bbb.hostContains = xxx.com +jboot.gateway.bbb.hostStartsWith = xxx.com +jboot.gateway.bbb.hostEndswith = xxx.com +jboot.gateway.bbb.queryEquals = aa:bb,cc:dd +jboot.gateway.bbb.queryContains = aa,bb + + +jboot.gateway.xxx.name = name +jboot.gateway.xxx.uri = http://youdomain:8080 +jboot.gateway.xxx.enable = true +jboot.gateway.xxx.sentinelEnable = false +jboot.gateway.xxx.sentinelBlockPage = /block +jboot.gateway.xxx.proxyReadTimeout = 10000 +jboot.gateway.xxx.proxyConnectTimeout = 5000 +jboot.gateway.xxx.proxyContentType = text/html;charset=utf-8 +jboot.gateway.xxx.interceptors = com.xxx.Interceptor1,com.xxx.Interceptor2 +jboot.gateway.xxx.pathEquals = /path +jboot.gateway.xxx.pathContains = /path +jboot.gateway.xxx.pathStartsWith = /path +jboot.gateway.xxx.pathEndswith = /path +jboot.gateway.xxx.hostEquals = xxx.com +jboot.gateway.xxx.hostContains = xxx.com +jboot.gateway.xxx.hostStartsWith = xxx.com +jboot.gateway.xxx.hostEndswith = xxx.com +jboot.gateway.xxx.queryEquals = aa:bb,cc:dd +jboot.gateway.xxx.queryContains = aa,bb +``` + ## 其他 当配置中,如果一个内容存在多个值的时候,需要用英文逗号(,)隔开。 @@ -202,4 +271,4 @@ jboot.gateway.pathContains = /user,/article 当 path 中,只要存在 `/user` 或者 存在 `/article` 都会匹配到该路由,比如 `www.xxx.com/user/xxx` 或者 `www.xxx.com/article/xxx` 都会匹配到。 -其他同理。 +其他同理。 \ No newline at end of file -- Gitee From c680514e16081996643f95d4629a198b0a8a7139 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 4 Apr 2020 18:49:39 +0800 Subject: [PATCH 0207/1965] update rpc docs --- doc/docs/rpc.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/docs/rpc.md b/doc/docs/rpc.md index 1d0f3275..b394a4d9 100644 --- a/doc/docs/rpc.md +++ b/doc/docs/rpc.md @@ -79,6 +79,10 @@ jboot.rpc.type = dubbo jboot.rpc.urls = com.yourdomain.AAAService:127.0.0.1:8080,com.yourdomain.XXXService:127.0.0.1:8080 jboot.rpc.providers = com.yourdomain.AAAService:providerName,com.yourdomain.XXXService:providerName jboot.rpc.consumers = com.yourdomain.AAAService:consumerName,com.yourdomain.XXXService:consumerName +jboot.rpc.defaultVersion = 1.0.0 +jboot.rpc.versions = com.yourdomain.AAAService:version1,com.yourdomain.XXXService:version2 +jboot.rpc.defaultGroup = +jboot.rpc.groups = com.yourdomain.AAAService:version1,com.yourdomain.XXXService:version2 jboot.rpc.autoExportEnable = true ``` @@ -86,6 +90,10 @@ jboot.rpc.autoExportEnable = true - jboot.rpc.urls : 一般不用配置,只有直连模式下才会去配置,此处是配置 Service接口和URL地址的映射关系。 - jboot.rpc.providers : 配置 Service 和 Provider 的映射关系( Motan下配置的是 Service 和 BasicService 的映射关系)。 - jboot.rpc.consumers : 配置 Reference 和 consumer 的映射关系( Motan下配置的是 Referer 和 BaseReferer 的映射关系)。 +- jboot.rpc.defaultVersion : 当service不配置版本时,默认的版本号,默认值为 1.0.0 +- jboot.rpc.versions : 每个服务对应的版本号 +- jboot.rpc.defaultGroup : 当服务不配置 group 时,默认的 gourp +- jboot.rpc.groups : 每个服务对应的 group - jboot.rpc.autoExportEnable : 当 Jboot 启动的时候,是否自动暴露 @RPCBean 注解的接口。 -- Gitee From 8ec65a7f289b44ffe92cd36fdc5fd54a5f06bdee Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 4 Apr 2020 20:22:43 +0800 Subject: [PATCH 0208/1965] v3.1.5 release (^.^)YYa!! --- changes.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/changes.txt b/changes.txt index 37e45638..cb4498bb 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,16 @@ +jboot v3.1.5: +新增:新增 Motan RPC 的 export 和 host 的相关配置以及test代码 +新增:JbootJson 支持驼峰式 JsonKey 输出,感谢Gitee的 @herowjun +新增:新增 Controller 对返回值自动渲染的功能 +新增:JbootRPCConfig 新增默认 version 和 group 配置的支持 +优化:ClassScanner 添加无需扫码的 jar 排除,速度更快 +修复:ClassScanner 在 Windows 平台下可能存在重复扫码而拖慢启动速度的问题 +修复:门户网关 GatewayHttpProxy 在 POST 时的某些情况下会出现无法正确代理的问题 +文档:完善 RPC 配置的相关文档 +文档:完善 Gateway 配置的相关文档 + + + jboot v3.1.4: 优化:重构 ClassScanner ,提高在 fatjar 模式的扫描性能 优化:重构 ConfigManager ,以便更好的支持 fatjar 模式下的配置文件读取 -- Gitee From 5bdf7fb0bed1bd4cf82a288db7ccd361848b5452 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 4 Apr 2020 20:42:38 +0800 Subject: [PATCH 0209/1965] add fatjar build docks --- README.md | 2 +- doc/docs/build.md | 111 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 106 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8a644d48..56653b10 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ public class HelloworldController extends JbootController { - [事件机制](./doc/docs/event.md) - [SPI扩展机制](./doc/docs/spi.md) - [代码生成器](./doc/docs/codegen.md) -- [项目构建](./doc/docs/build.md) +- [项目打包](./doc/docs/build.md) - [项目部署](./doc/docs/deploy.md) - [Jboot与Docker](./doc/docs/docker.md) - [1.x 升级到 2.x 教程](./doc/docs/upgrade.md) diff --git a/doc/docs/build.md b/doc/docs/build.md index 840323c0..afd0cecb 100644 --- a/doc/docs/build.md +++ b/doc/docs/build.md @@ -1,11 +1,12 @@ -# 项目构建 +# 项目打包 ## 目录 -- 单模块 maven 项目构建 -- 多模块 maven 项目构建 +- 单模块 maven 项目打包 +- 多模块 maven 项目打包 +- fatjar 打包(全部打包到一个jar里) -## 单模块 maven 项目构建 +## 单模块 maven 项目打包 在单一模块的maven项目开发中,我们通常在 `src/main/resources` 编写我们的配置文件,因此,在 maven 构建的时候,我们需要添加如下配置: @@ -175,7 +176,7 @@ fi 复制该文件夹到服务器,然后执行里面的 `jboot.sh start` 命令即可上线。 -## 多模块 maven 项目构建 +## 多模块 maven 项目打包 多模块项目在以上配置的基础上,添加 `maven-resources-plugin` maven 插件,用于拷贝其他maven模块的资源文件和html等内容到此运行模块。 @@ -210,4 +211,102 @@ maven 配置如下: ``` -这部分可以参考 jpress 项目,网址:https://gitee.com/fuhai/jpress/blob/v2.0/starter/pom.xml \ No newline at end of file +这部分可以参考 jpress 项目,网址:https://gitee.com/fuhai/jpress/blob/v2.0/starter/pom.xml + +## fatjar 打包(全部打包到一个jar里) + +fatjar 打包指的是,把所有资源(html、css、js)以及项目依赖全部打包到一个 jar 包里,这样我们可以通过 +命令 `java -jar xxx.jar` 启动,更加方便部署,特别是方便在微服务下的多模块部署。 + +fatjar 打包第一步,在 pom.xml 添加如下配置 + +```xml + + + + src/main/resources + + **/*.* + + false + + + + src/main/webapp + + **/*.* + + false + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + UTF-8 + -parameters + + + + + + maven-resources-plugin + + + copy-resources + validate + + copy-resources + + + ${basedir}/target/classes/webapp + + + ${basedir}/src/main/webapp + + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.0 + + + make-assembly + package + + single + + + + + io.jboot.app.JbootApplication + + + + + + jar-with-dependencies + + + + + + + +``` + +第二步:通过 Maven 打包 + +执行命令 `mvn clean package` 进行打包,在 pom.xml 对应的模块下会生成一个 xxx-with-dependencies.jar 的 jar 包,复制该 jar +到服务器上,执行 `java -jar xxx-with-dependencies.jar` 即可启动项目。 \ No newline at end of file -- Gitee From e9453fc170cb2eda2977597257fc4200de8861d6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 4 Apr 2020 20:57:39 +0800 Subject: [PATCH 0210/1965] add default group for rpc service --- src/main/java/io/jboot/components/rpc/JbootrpcConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java index d132fcdc..e3b30916 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java @@ -49,7 +49,7 @@ public class JbootrpcConfig { private Map versions; //当不指定的时候,默认分组 - private String defaultGroup; + private String defaultGroup = "jboot"; //指定的服务的分组 private Map groups; -- Gitee From eaee41978335ae657632c472617e03e2ecea2897 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 4 Apr 2020 21:08:31 +0800 Subject: [PATCH 0211/1965] v3.1.5 release (^.^)YYa!! --- changes.txt | 1 + doc/docs/rpc.md | 4 ++-- src/main/java/io/jboot/components/rpc/JbootrpcConfig.java | 6 +++--- .../java/io/jboot/components/rpc/motan/MotanrpcConfig.java | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/changes.txt b/changes.txt index cb4498bb..761b76aa 100644 --- a/changes.txt +++ b/changes.txt @@ -8,6 +8,7 @@ jboot v3.1.5: 修复:门户网关 GatewayHttpProxy 在 POST 时的某些情况下会出现无法正确代理的问题 文档:完善 RPC 配置的相关文档 文档:完善 Gateway 配置的相关文档 +文档:添加 fatjar 打包的相关文档 diff --git a/doc/docs/rpc.md b/doc/docs/rpc.md index b394a4d9..677456d1 100644 --- a/doc/docs/rpc.md +++ b/doc/docs/rpc.md @@ -80,9 +80,9 @@ jboot.rpc.urls = com.yourdomain.AAAService:127.0.0.1:8080,com.yourdomain.XXXServ jboot.rpc.providers = com.yourdomain.AAAService:providerName,com.yourdomain.XXXService:providerName jboot.rpc.consumers = com.yourdomain.AAAService:consumerName,com.yourdomain.XXXService:consumerName jboot.rpc.defaultVersion = 1.0.0 -jboot.rpc.versions = com.yourdomain.AAAService:version1,com.yourdomain.XXXService:version2 +jboot.rpc.versions = com.yourdomain.AAAService:1.0.0,com.yourdomain.XXXService:1.0.1 jboot.rpc.defaultGroup = -jboot.rpc.groups = com.yourdomain.AAAService:version1,com.yourdomain.XXXService:version2 +jboot.rpc.groups = com.yourdomain.AAAService:group1,com.yourdomain.XXXService:group2 jboot.rpc.autoExportEnable = true ``` diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java index e3b30916..9c340558 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java @@ -49,7 +49,7 @@ public class JbootrpcConfig { private Map versions; //当不指定的时候,默认分组 - private String defaultGroup = "jboot"; + private String defaultGroup; //指定的服务的分组 private Map groups; @@ -119,7 +119,7 @@ public class JbootrpcConfig { } public String getVersion(String className) { - String version = versions == null ? null : versions.get(className); + String version = versions == null || versions.isEmpty() ? null : versions.get(className); return version == null ? defaultVersion : version; } @@ -140,7 +140,7 @@ public class JbootrpcConfig { } public String getGroup(String className) { - String group = groups == null ? null : groups.get(className); + String group = groups == null || groups.isEmpty() ? null : groups.get(className); return group == null ? defaultGroup : group; } diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java b/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java index 1f96175f..258c8f1f 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java @@ -77,7 +77,7 @@ public class MotanrpcConfig { } public String getExport(String className) { - String export = exports == null ? null : exports.get(className); + String export = exports == null || exports.isEmpty() ? null : exports.get(className); return StrUtil.isNotBlank(export) ? export : defaultExport; } @@ -90,7 +90,7 @@ public class MotanrpcConfig { } public String getHost(String className) { - String host = hosts == null ? null : hosts.get(className); + String host = hosts == null || hosts.isEmpty() ? null : hosts.get(className); return StrUtil.isNotBlank(host) ? host : defaultHost; } } -- Gitee From 3c15a54062e423f70fc2ce29bbbde4cb86372f2c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 5 Apr 2020 08:46:07 +0800 Subject: [PATCH 0212/1965] v3.1.5 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 1 + doc/docs/deploy.md | 88 ++++++++++++++++++++++++- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 92 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 56653b10..f6d8e45b 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.1.4 + 3.1.5 ``` diff --git a/changes.txt b/changes.txt index 761b76aa..577f3c8c 100644 --- a/changes.txt +++ b/changes.txt @@ -9,6 +9,7 @@ jboot v3.1.5: 文档:完善 RPC 配置的相关文档 文档:完善 Gateway 配置的相关文档 文档:添加 fatjar 打包的相关文档 +文档:完善 fatjar 部署运行的相关文档 diff --git a/doc/docs/deploy.md b/doc/docs/deploy.md index 6486679f..21a2448c 100644 --- a/doc/docs/deploy.md +++ b/doc/docs/deploy.md @@ -2,15 +2,101 @@ ## 目录 - 描述 +- 通过 脚本 运行 - 通过 Jar 运行 - 通过 Tomcat 运行 ## 描述 +本文档提供了 3 种部署方式,对应 Jboot 里的 3 种[打包方式](./build.md)。 + +## 通过 脚本 运行 + +在 [打包方式](./build.md) 文档中,我们可以把项目打包成一个 .zip 的压缩包项目,里面带有 jboot.sh (和 jboot.bat) 执行脚本, +只需要我们解压 .zip 压缩文件,通过如下命令就可以对 jboot 项目进行启动和停止。 + +```shell script +# 启动 +./jboot.sh start + +# 停止 +./jboot.sh stop + +# 重启 +./jboot.sh restart +``` + +在 Windows 系统中,通过如下命令执行 + +```shell script +# 启动 +jboot.bat start + +# 停止 +jboot.bat stop + +# 重启 +jboot.bat restart +``` ## 通过 Jar 运行 -Jboot 通过依赖 `JFinal-Undertow` 内置了 Undertow 服务器,可以直接通过 Jar 的方式进行运行,这部分直接参考文档:https://www.jfinal.com/doc/1-3 即可。 +在 [打包方式](./build.md) 文档中,我们可以把所有的资源文件(html、css、js、配置文件 等)以及项目的所有依赖打包到一个 jar 包 +里去,打包成功后,可以通过如下命令运行。 + +启动(前台启动,命令窗口不能关闭) +```shell script +java -jar xxx.jar +``` +> 当前ssh窗口(命令窗口)被锁定,可按 `CTRL + C` 打断程序运行,或直接关闭窗口,程序退出。 + +启动(后台启动,命令窗口不能关闭) +```shell script +java -jar xxx.jar & +``` +> & 代表在后台运行。当前ssh窗口不被锁定,但是当窗口关闭时,程序中止运行。 + + +启动(后台启动) +```shell script +nohup java -jar xxx.jar & +``` +>nohup 意思是不挂断运行命令,当账户退出或终端关闭时,程序仍然运行,当用 nohup 命令执行作业时,缺省情况下该作业的所有输出被重定向到nohup.out的文件中,除非另外指定了输出文件。 + +启动(后台启动) +```shell script +nohup java -jar xxx.jar>temp.txt & +``` +>command >out.file 是将 command 的输出重定向到 out.file 文件,即输出内容不打印到屏幕上,而是输出到 out.file 文件中。 +>以上命令,就是把 java 启动的输出,输入到 temp.txt 文件里,而不是在屏幕上。 + + +另外:可以通过如下命令实时查看日志: + +```shell script +tail -f temp.text +``` + +可通过jobs命令查看后台运行任务 + +```shell script +jobs +``` + +jobs 命令就会列出所有后台执行的作业,并且每个作业前面都有个编号。如果想将某个作业调回前台控制,只需要 fg + 编号即可。 + +```shell script +fg 33 +``` + +查看程序端口的进程的pid + +```shell script +netstat -nlp | grep:8080 +``` + + + ## 通过 Tomcat 运行 diff --git a/doc/docs/install.md b/doc/docs/install.md index aa964ced..04152d49 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.1.4 + 3.1.5 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 82fb65b7..603ec045 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.1.4 + 3.1.5 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 28a9284b..7d120266 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.1.4"; + public static String VERSION = "3.1.5"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From cf6d6099df369f0a269b16b5c93af7972c1f4959 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 5 Apr 2020 09:00:29 +0800 Subject: [PATCH 0213/1965] v3.1.5 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 176a2ff2..153cfae9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.5-SNAPSHOT + 3.1.5 jar jboot -- Gitee From 32bda08afd5ae8b019bb77eddb3a318e910425f6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 6 Apr 2020 14:21:54 +0800 Subject: [PATCH 0214/1965] v3.1.6 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 153cfae9..b72cb4c1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.5 + 3.1.6-SNAPSHOT jar jboot -- Gitee From 65e6b745e2124599c937f37a28d8e1653a604b24 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 6 Apr 2020 15:26:25 +0800 Subject: [PATCH 0215/1965] optimize JbootReturnValueRender --- .../jboot/web/handler/JbootActionHandler.java | 2 +- .../web/render/JbootReturnValueRender.java | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index c5a0a4df..114861e8 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -120,7 +120,7 @@ public class JbootActionHandler extends ActionHandler { } if (render == null - && invocation.getReturnValue() != null + && void.class != action.getMethod().getReturnType() && renderManager.getRenderFactory() instanceof JbootRenderFactory) { JbootRenderFactory jrf = (JbootRenderFactory) renderManager.getRenderFactory(); render = jrf.getReturnValueRender(action, invocation.getReturnValue()); diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index caab4d13..d95d9635 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -17,10 +17,7 @@ package io.jboot.web.render; import com.jfinal.core.Action; import com.jfinal.kit.JsonKit; -import com.jfinal.render.FileRender; -import com.jfinal.render.JsonRender; -import com.jfinal.render.Render; -import com.jfinal.render.TextRender; +import com.jfinal.render.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -42,14 +39,20 @@ public class JbootReturnValueRender extends Render { private Render render; public JbootReturnValueRender(Action action, Object returnValue) { + this.action = action; - if (isBaseType(returnValue)) { + + if (returnValue == null) { + this.value = null; + } else if (isBaseType(returnValue)) { this.value = String.valueOf(returnValue); } else { this.value = returnValue; } - if (this.value instanceof File) { + if (this.value == null) { + this.render = new NullRender(); + } else if (this.value instanceof File) { this.render = new FileRender((File) value); } else if (this.value instanceof String) { this.render = new TextRender((String) value); @@ -80,9 +83,6 @@ public class JbootReturnValueRender extends Render { private boolean isBaseType(Object value) { - if (value == null) { - return true; - } Class c = value.getClass(); return c == String.class || c == char.class || c == Integer.class || c == int.class -- Gitee From abf3000f9c5159eaee229de65b7d40848f049bdd Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 6 Apr 2020 15:31:55 +0800 Subject: [PATCH 0216/1965] optimize JbootGatewayConfig --- .../gateway/JbootGatewayConfig.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index 048dfc7c..6b89656c 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -15,6 +15,7 @@ */ package io.jboot.components.gateway; +import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; @@ -233,6 +234,7 @@ public class JbootGatewayConfig implements Serializable { private GatewayInterceptor[] inters; + public GatewayInterceptor[] getInters() { if (interceptors == null || interceptors.length == 0) { return null; @@ -255,8 +257,28 @@ public class JbootGatewayConfig implements Serializable { } + private Boolean configOk = null; + public boolean isConfigOk() { - return StrUtil.isNotBlank(uri); + if (configOk != null) { + return configOk; + } + synchronized (this) { + if (configOk == null) { + configOk = StrUtil.isNotBlank(uri); + if (configOk) { + ensureUriPatterCorrect(); + } + } + } + return configOk; + } + + private void ensureUriPatterCorrect() { + if (!uri.toLowerCase().startsWith("http://") + && !uri.toLowerCase().startsWith("https://")) { + throw new JbootIllegalConfigException("gateway uri must start with http:// or https://"); + } } -- Gitee From caaff17d0ce3cf9e21ba211e25b110782f9e45c2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 6 Apr 2020 15:32:20 +0800 Subject: [PATCH 0217/1965] add gateway test code --- .../jboot/test/gateway/GatewayController.java | 26 +++++++++++++++++++ src/test/resources/jboot.properties | 6 ++--- 2 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 src/test/java/io/jboot/test/gateway/GatewayController.java diff --git a/src/test/java/io/jboot/test/gateway/GatewayController.java b/src/test/java/io/jboot/test/gateway/GatewayController.java new file mode 100644 index 00000000..7cca21d8 --- /dev/null +++ b/src/test/java/io/jboot/test/gateway/GatewayController.java @@ -0,0 +1,26 @@ +package io.jboot.test.gateway; + +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/4/5 + */ +@RequestMapping("/gateway") +public class GatewayController extends JbootController { + + public void index(){ + + } + + + public void render(){ + Map map = new HashMap(); + map.putAll(getParas()); + renderJson(map); + } +} diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index d285e323..f391de5e 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -37,10 +37,10 @@ config.test.test.ccc.name = name3 config.test.test.ccc.type = type3 -# 配置 gateway ,访问首页的时候目标地址设置为 baidu 的地址 +# 配置 gateway ,当访问 /gateway 的时候,自动路由到 /gateway/render jboot.gateway.enable = true -jboot.gateway.uri = https://www.baidu.com -jboot.gateway.pathEquals = / +jboot.gateway.uri = http://127.0.0.1:9999/gateway/render +jboot.gateway.pathEquals = /gateway -- Gitee From ba81fdee9a4e025512788856bbc1978105665366 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 6 Apr 2020 16:55:33 +0800 Subject: [PATCH 0218/1965] upgrade dependencies --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b72cb4c1..b412af37 100644 --- a/pom.xml +++ b/pom.xml @@ -66,10 +66,10 @@ 4.4.13 4.1.48.Final 1.7.1 - 2.7.5 + 2.7.6 3.11.0.Final 1.1.0 - 1.1.7 + 1.1.8 4.1.5 1.8 -- Gitee From 07e07f0ae1aed55481de3ab5294eb15d47a91696 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 6 Apr 2020 16:59:31 +0800 Subject: [PATCH 0219/1965] upgrade dependencies --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b412af37..282ffdd2 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ 1.13.1 2.10.6 2.8.1 - 3.9 + 3.10 2.6 1.14 4.5.12 @@ -275,7 +275,7 @@ com.alibaba.nacos nacos-client - 1.2.0 + 1.2.1 provided -- Gitee From 37bf6d20f522b93a01e09122d717ff68a783e65d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 6 Apr 2020 18:18:57 +0800 Subject: [PATCH 0220/1965] fixed aop docs error --- doc/docs/aop.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/docs/aop.md b/doc/docs/aop.md index ec0c8868..0ec52a65 100644 --- a/doc/docs/aop.md +++ b/doc/docs/aop.md @@ -216,7 +216,7 @@ public class AppConfiguration { ``` -这样,在一个 Jboot 应用中,就会存在两份 `CommentService` 他们的名称分别为:myCommentServiceFromConfiguration 和 myCommentService2(当只用了注解 @Bean 但是为添加 name 参数时,名称为方法名) +这样,在一个 Jboot 应用中,就会存在两份 `CommentService` 他们的名称分别为:myCommentServiceFromConfiguration 和 myCommentService2(当只用了注解 @Bean 但是未添加 name 参数时,name 的值为方法的名称) 这样,我们就可以在 Controller 里,通过 `@Inject` 配合 `@Bean(name = ... )` 进行注入,例如: -- Gitee From 67e88a98776dc12228f8d01927c5fe1fef097c5f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 6 Apr 2020 18:22:51 +0800 Subject: [PATCH 0221/1965] add ResponseEntity support --- src/main/java/io/jboot/web/HttpStatus.java | 423 ++++++++++++++++++ .../java/io/jboot/web/ResponseEntity.java | 105 +++++ .../web/render/JbootReturnValueRender.java | 9 +- 3 files changed, 535 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/jboot/web/HttpStatus.java create mode 100644 src/main/java/io/jboot/web/ResponseEntity.java diff --git a/src/main/java/io/jboot/web/HttpStatus.java b/src/main/java/io/jboot/web/HttpStatus.java new file mode 100644 index 00000000..e1e8833c --- /dev/null +++ b/src/main/java/io/jboot/web/HttpStatus.java @@ -0,0 +1,423 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/4/6 + */ +public enum HttpStatus { + + /** + * {@code 100 Continue}. + * @see HTTP/1.1: Semantics and Content, section 6.2.1 + */ + CONTINUE(100, "Continue"), + /** + * {@code 101 Switching Protocols}. + * @see HTTP/1.1: Semantics and Content, section 6.2.2 + */ + SWITCHING_PROTOCOLS(101, "Switching Protocols"), + /** + * {@code 102 Processing}. + * @see WebDAV + */ + PROCESSING(102, "Processing"), + /** + * {@code 103 Checkpoint}. + * @see A proposal for supporting + * resumable POST/PUT HTTP requests in HTTP/1.0 + */ + CHECKPOINT(103, "Checkpoint"), + + // 2xx Success + + /** + * {@code 200 OK}. + * @see HTTP/1.1: Semantics and Content, section 6.3.1 + */ + OK(200, "OK"), + /** + * {@code 201 Created}. + * @see HTTP/1.1: Semantics and Content, section 6.3.2 + */ + CREATED(201, "Created"), + /** + * {@code 202 Accepted}. + * @see HTTP/1.1: Semantics and Content, section 6.3.3 + */ + ACCEPTED(202, "Accepted"), + /** + * {@code 203 Non-Authoritative Information}. + * @see HTTP/1.1: Semantics and Content, section 6.3.4 + */ + NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"), + /** + * {@code 204 No Content}. + * @see HTTP/1.1: Semantics and Content, section 6.3.5 + */ + NO_CONTENT(204, "No Content"), + /** + * {@code 205 Reset Content}. + * @see HTTP/1.1: Semantics and Content, section 6.3.6 + */ + RESET_CONTENT(205, "Reset Content"), + /** + * {@code 206 Partial Content}. + * @see HTTP/1.1: Range Requests, section 4.1 + */ + PARTIAL_CONTENT(206, "Partial Content"), + /** + * {@code 207 Multi-Status}. + * @see WebDAV + */ + MULTI_STATUS(207, "Multi-Status"), + /** + * {@code 208 Already Reported}. + * @see WebDAV Binding Extensions + */ + ALREADY_REPORTED(208, "Already Reported"), + /** + * {@code 226 IM Used}. + * @see Delta encoding in HTTP + */ + IM_USED(226, "IM Used"), + + // 3xx Redirection + + /** + * {@code 300 Multiple Choices}. + * @see HTTP/1.1: Semantics and Content, section 6.4.1 + */ + MULTIPLE_CHOICES(300, "Multiple Choices"), + /** + * {@code 301 Moved Permanently}. + * @see HTTP/1.1: Semantics and Content, section 6.4.2 + */ + MOVED_PERMANENTLY(301, "Moved Permanently"), + /** + * {@code 302 Found}. + * @see HTTP/1.1: Semantics and Content, section 6.4.3 + */ + FOUND(302, "Found"), + /** + * {@code 302 Moved Temporarily}. + * @see HTTP/1.0, section 9.3 + * @deprecated in favor of {@link #FOUND} which will be returned from {@code HttpStatus.valueOf(302)} + */ + @Deprecated + MOVED_TEMPORARILY(302, "Moved Temporarily"), + /** + * {@code 303 See Other}. + * @see HTTP/1.1: Semantics and Content, section 6.4.4 + */ + SEE_OTHER(303, "See Other"), + /** + * {@code 304 Not Modified}. + * @see HTTP/1.1: Conditional Requests, section 4.1 + */ + NOT_MODIFIED(304, "Not Modified"), + /** + * {@code 305 Use Proxy}. + * @see HTTP/1.1: Semantics and Content, section 6.4.5 + * @deprecated due to security concerns regarding in-band configuration of a proxy + */ + @Deprecated + USE_PROXY(305, "Use Proxy"), + /** + * {@code 307 Temporary Redirect}. + * @see HTTP/1.1: Semantics and Content, section 6.4.7 + */ + TEMPORARY_REDIRECT(307, "Temporary Redirect"), + /** + * {@code 308 Permanent Redirect}. + * @see RFC 7238 + */ + PERMANENT_REDIRECT(308, "Permanent Redirect"), + + // --- 4xx Client Error --- + + /** + * {@code 400 Bad Request}. + * @see HTTP/1.1: Semantics and Content, section 6.5.1 + */ + BAD_REQUEST(400, "Bad Request"), + /** + * {@code 401 Unauthorized}. + * @see HTTP/1.1: Authentication, section 3.1 + */ + UNAUTHORIZED(401, "Unauthorized"), + /** + * {@code 402 Payment Required}. + * @see HTTP/1.1: Semantics and Content, section 6.5.2 + */ + PAYMENT_REQUIRED(402, "Payment Required"), + /** + * {@code 403 Forbidden}. + * @see HTTP/1.1: Semantics and Content, section 6.5.3 + */ + FORBIDDEN(403, "Forbidden"), + /** + * {@code 404 Not Found}. + * @see HTTP/1.1: Semantics and Content, section 6.5.4 + */ + NOT_FOUND(404, "Not Found"), + /** + * {@code 405 Method Not Allowed}. + * @see HTTP/1.1: Semantics and Content, section 6.5.5 + */ + METHOD_NOT_ALLOWED(405, "Method Not Allowed"), + /** + * {@code 406 Not Acceptable}. + * @see HTTP/1.1: Semantics and Content, section 6.5.6 + */ + NOT_ACCEPTABLE(406, "Not Acceptable"), + /** + * {@code 407 Proxy Authentication Required}. + * @see HTTP/1.1: Authentication, section 3.2 + */ + PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"), + /** + * {@code 408 Request Timeout}. + * @see HTTP/1.1: Semantics and Content, section 6.5.7 + */ + REQUEST_TIMEOUT(408, "Request Timeout"), + /** + * {@code 409 Conflict}. + * @see HTTP/1.1: Semantics and Content, section 6.5.8 + */ + CONFLICT(409, "Conflict"), + /** + * {@code 410 Gone}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.9 + */ + GONE(410, "Gone"), + /** + * {@code 411 Length Required}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.10 + */ + LENGTH_REQUIRED(411, "Length Required"), + /** + * {@code 412 Precondition failed}. + * @see + * HTTP/1.1: Conditional Requests, section 4.2 + */ + PRECONDITION_FAILED(412, "Precondition Failed"), + /** + * {@code 413 Payload Too Large}. + * @since 4.1 + * @see + * HTTP/1.1: Semantics and Content, section 6.5.11 + */ + PAYLOAD_TOO_LARGE(413, "Payload Too Large"), + /** + * {@code 413 Request Entity Too Large}. + * @see HTTP/1.1, section 10.4.14 + * @deprecated in favor of {@link #PAYLOAD_TOO_LARGE} which will be + * returned from {@code HttpStatus.valueOf(413)} + */ + @Deprecated + REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large"), + /** + * {@code 414 URI Too Long}. + * @since 4.1 + * @see + * HTTP/1.1: Semantics and Content, section 6.5.12 + */ + URI_TOO_LONG(414, "URI Too Long"), + /** + * {@code 414 Request-URI Too Long}. + * @see HTTP/1.1, section 10.4.15 + * @deprecated in favor of {@link #URI_TOO_LONG} which will be returned from {@code HttpStatus.valueOf(414)} + */ + @Deprecated + REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"), + /** + * {@code 415 Unsupported Media Type}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.13 + */ + UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), + /** + * {@code 416 Requested Range Not Satisfiable}. + * @see HTTP/1.1: Range Requests, section 4.4 + */ + REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested range not satisfiable"), + /** + * {@code 417 Expectation Failed}. + * @see + * HTTP/1.1: Semantics and Content, section 6.5.14 + */ + EXPECTATION_FAILED(417, "Expectation Failed"), + /** + * {@code 418 I'm a teapot}. + * @see HTCPCP/1.0 + */ + I_AM_A_TEAPOT(418, "I'm a teapot"), + /** + * @deprecated See + * + * WebDAV Draft Changes + */ + @Deprecated + INSUFFICIENT_SPACE_ON_RESOURCE(419, "Insufficient Space On Resource"), + /** + * @deprecated See + * + * WebDAV Draft Changes + */ + @Deprecated + METHOD_FAILURE(420, "Method Failure"), + /** + * @deprecated + * See + * WebDAV Draft Changes + */ + @Deprecated + DESTINATION_LOCKED(421, "Destination Locked"), + /** + * {@code 422 Unprocessable Entity}. + * @see WebDAV + */ + UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"), + /** + * {@code 423 Locked}. + * @see WebDAV + */ + LOCKED(423, "Locked"), + /** + * {@code 424 Failed Dependency}. + * @see WebDAV + */ + FAILED_DEPENDENCY(424, "Failed Dependency"), + /** + * {@code 426 Upgrade Required}. + * @see Upgrading to TLS Within HTTP/1.1 + */ + UPGRADE_REQUIRED(426, "Upgrade Required"), + /** + * {@code 428 Precondition Required}. + * @see Additional HTTP Status Codes + */ + PRECONDITION_REQUIRED(428, "Precondition Required"), + /** + * {@code 429 Too Many Requests}. + * @see Additional HTTP Status Codes + */ + TOO_MANY_REQUESTS(429, "Too Many Requests"), + /** + * {@code 431 Request Header Fields Too Large}. + * @see Additional HTTP Status Codes + */ + REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"), + /** + * {@code 451 Unavailable For Legal Reasons}. + * @see + * An HTTP Status Code to Report Legal Obstacles + * @since 4.3 + */ + UNAVAILABLE_FOR_LEGAL_REASONS(451, "Unavailable For Legal Reasons"), + + // --- 5xx Server Error --- + + /** + * {@code 500 Internal Server Error}. + * @see HTTP/1.1: Semantics and Content, section 6.6.1 + */ + INTERNAL_SERVER_ERROR(500, "Internal Server Error"), + /** + * {@code 501 Not Implemented}. + * @see HTTP/1.1: Semantics and Content, section 6.6.2 + */ + NOT_IMPLEMENTED(501, "Not Implemented"), + /** + * {@code 502 Bad Gateway}. + * @see HTTP/1.1: Semantics and Content, section 6.6.3 + */ + BAD_GATEWAY(502, "Bad Gateway"), + /** + * {@code 503 Service Unavailable}. + * @see HTTP/1.1: Semantics and Content, section 6.6.4 + */ + SERVICE_UNAVAILABLE(503, "Service Unavailable"), + /** + * {@code 504 Gateway Timeout}. + * @see HTTP/1.1: Semantics and Content, section 6.6.5 + */ + GATEWAY_TIMEOUT(504, "Gateway Timeout"), + /** + * {@code 505 HTTP Version Not Supported}. + * @see HTTP/1.1: Semantics and Content, section 6.6.6 + */ + HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version not supported"), + /** + * {@code 506 Variant Also Negotiates} + * @see Transparent Content Negotiation + */ + VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates"), + /** + * {@code 507 Insufficient Storage} + * @see WebDAV + */ + INSUFFICIENT_STORAGE(507, "Insufficient Storage"), + /** + * {@code 508 Loop Detected} + * @see WebDAV Binding Extensions + */ + LOOP_DETECTED(508, "Loop Detected"), + /** + * {@code 509 Bandwidth Limit Exceeded} + */ + BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded"), + /** + * {@code 510 Not Extended} + * @see HTTP Extension Framework + */ + NOT_EXTENDED(510, "Not Extended"), + /** + * {@code 511 Network Authentication Required}. + * @see Additional HTTP Status Codes + */ + NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required"); + + + private final int value; + + private final String reasonPhrase; + + + HttpStatus(int value, String reasonPhrase) { + this.value = value; + this.reasonPhrase = reasonPhrase; + } + + + /** + * Return the integer value of this status code. + */ + public int value() { + return this.value; + } + + /** + * Return the reason phrase of this status code. + */ + public String getReasonPhrase() { + return this.reasonPhrase; + } +} diff --git a/src/main/java/io/jboot/web/ResponseEntity.java b/src/main/java/io/jboot/web/ResponseEntity.java new file mode 100644 index 00000000..1e9153d3 --- /dev/null +++ b/src/main/java/io/jboot/web/ResponseEntity.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/4/6 + */ +public class ResponseEntity { + + //响应的数据 + private Object body; + + //自定义响应头部信息 + private Map headers; + + //默认http状态 + private HttpStatus httpStatus = HttpStatus.OK; + + + public ResponseEntity() { + } + + public ResponseEntity(Object body) { + this.body = body; + } + + public ResponseEntity(Map headers, HttpStatus httpStatus) { + this.headers = headers; + this.httpStatus = httpStatus; + } + + public ResponseEntity(Object body, Map headers, HttpStatus httpStatus) { + this.body = body; + this.headers = headers; + this.httpStatus = httpStatus; + } + + public ResponseEntity body(Object body) { + this.body = body; + return this; + } + + public ResponseEntity header(String key, String value) { + if (this.headers == null) { + this.headers = new HashMap<>(); + } + this.headers.put(key, value); + return this; + } + + public ResponseEntity status(int status) { + for (HttpStatus httpStatus : HttpStatus.values()) { + if (Objects.equals(httpStatus.value(), status)) { + this.httpStatus = httpStatus; + break; + } + } + return this; + } + + + public ResponseEntity status(HttpStatus status) { + this.httpStatus = status; + return this; + } + + + public T getBody() { + return (T) body; + } + + public Map getHeaders() { + return headers; + } + + public HttpStatus getHttpStatus() { + return httpStatus; + } + + + public static ResponseEntity ok() { + ResponseEntity responseEntity = new ResponseEntity(); + return responseEntity.status(HttpStatus.OK); + } + + +} diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index d95d9635..158dbd1a 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -18,6 +18,7 @@ package io.jboot.web.render; import com.jfinal.core.Action; import com.jfinal.kit.JsonKit; import com.jfinal.render.*; +import io.jboot.web.ResponseEntity; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -52,12 +53,16 @@ public class JbootReturnValueRender extends Render { if (this.value == null) { this.render = new NullRender(); - } else if (this.value instanceof File) { - this.render = new FileRender((File) value); + } else if (this.value instanceof ResponseEntity) { + //render ResponseEntity } else if (this.value instanceof String) { this.render = new TextRender((String) value); } else if (this.value instanceof Date) { this.render = new TextRender(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) value)); + } else if (this.value instanceof File) { + this.render = new FileRender((File) value); + } else if (this.value instanceof Render) { + this.render = (Render) value; } else { this.render = new JsonRender(JsonKit.toJson(value)); } -- Gitee From 634e98eec6543cb953f52e0f422fac3d9a303817 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 7 Apr 2020 10:10:17 +0800 Subject: [PATCH 0222/1965] optimize ClassScanner.java --- src/main/java/io/jboot/utils/ClassScanner.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 1fce8cb1..56cb08dd 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -83,6 +83,7 @@ public class ClassScanner { excludeJars.add("commons-cli"); excludeJars.add("commons-math"); excludeJars.add("commons-jxpath"); + excludeJars.add("commons-compress"); excludeJars.add("audience-"); excludeJars.add("hessian-"); excludeJars.add("metrics-"); @@ -214,6 +215,10 @@ public class ClassScanner { excludeJars.add("hutool-"); excludeJars.add("jakarta."); excludeJars.add("protostuff-"); + excludeJars.add("poi-"); + excludeJars.add("easypoi-"); + excludeJars.add("ognl-"); + excludeJars.add("xmlbeans-"); } @@ -256,7 +261,7 @@ public class ClassScanner { addUnscanClass("com.rabbitmq."); addUnscanClass("com.squareup."); addUnscanClass("com.typesafe."); - addUnscanClass("com.weibo."); + addUnscanClass("com.weibo.api.motan."); addUnscanClass("com.zaxxer."); addUnscanClass("com.mysql."); addUnscanClass("org.gjt."); -- Gitee From face4d15e83c29f7f094c3a903febafb78e02070 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 7 Apr 2020 10:29:07 +0800 Subject: [PATCH 0223/1965] add ResponseEntity Render support --- .../web/render/JbootResponseEntityRender.java | 65 +++++++++++++++++++ .../web/render/JbootReturnValueRender.java | 2 +- 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/jboot/web/render/JbootResponseEntityRender.java diff --git a/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java b/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java new file mode 100644 index 00000000..69efd942 --- /dev/null +++ b/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.render; + +import com.jfinal.kit.JsonKit; +import com.jfinal.render.Render; +import com.jfinal.render.RenderException; +import io.jboot.web.ResponseEntity; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Map; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/4/7 + */ +public class JbootResponseEntityRender extends Render { + + private ResponseEntity responseEntity; + + public JbootResponseEntityRender(ResponseEntity responseEntity) { + this.responseEntity = responseEntity; + } + + @Override + public void render() { + + PrintWriter writer = null; + try { + //默认输出 json,但是 responseEntity 可以配置 Header 覆盖这个输出 + response.setHeader("Cache-Control", "no-cache"); + response.setHeader("Content-Type", "application/json; charset=utf-8"); + response.setStatus(responseEntity.getHttpStatus().value()); + + Map headers = responseEntity.getHeaders(); + if (headers != null && !headers.isEmpty()) { + for (Map.Entry entry : headers.entrySet()) { + response.setHeader(entry.getKey(), entry.getValue()); + } + } + + String jsonText = responseEntity.getBody() == null ? "" : JsonKit.toJson(responseEntity.getBody()); + writer = response.getWriter(); + writer.write(jsonText); + // writer.flush(); + } catch (IOException e) { + throw new RenderException(e); + } + + } +} diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index 158dbd1a..3e923a00 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -54,7 +54,7 @@ public class JbootReturnValueRender extends Render { if (this.value == null) { this.render = new NullRender(); } else if (this.value instanceof ResponseEntity) { - //render ResponseEntity + this.render = new JbootResponseEntityRender((ResponseEntity) value); } else if (this.value instanceof String) { this.render = new TextRender((String) value); } else if (this.value instanceof Date) { -- Gitee From 021e15ac16dc606c52b2ab943b6b19b8752e3b25 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 7 Apr 2020 16:01:27 +0800 Subject: [PATCH 0224/1965] fixed RPCUtils.copyFields() method error --- .../rpc/JbootrpcReferenceConfig.java | 2 +- .../components/rpc/JbootrpcServiceConfig.java | 2 +- .../rpc/{Utils.java => RPCUtils.java} | 16 +++++++-------- .../jboot/components/rpc/dubbo/DubboUtil.java | 20 +++++++++---------- .../jboot/components/rpc/motan/MotanUtil.java | 18 ++++++++--------- 5 files changed, 29 insertions(+), 29 deletions(-) rename src/main/java/io/jboot/components/rpc/{Utils.java => RPCUtils.java} (96%) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java index ba719583..07431fe9 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java @@ -133,7 +133,7 @@ public class JbootrpcReferenceConfig implements Serializable { } public JbootrpcReferenceConfig(RPCInject inject) { - Utils.appendAnnotation(RPCInject.class, inject, this); + RPCUtils.appendAnnotation(RPCInject.class, inject, this); } public String getVersion() { diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java index fe8db2da..0c0ac4a1 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java @@ -124,7 +124,7 @@ public class JbootrpcServiceConfig implements Serializable { } public JbootrpcServiceConfig(RPCBean bean) { - Utils.appendAnnotation(RPCBean.class, bean, this); + RPCUtils.appendAnnotation(RPCBean.class, bean, this); } public String getVersion() { diff --git a/src/main/java/io/jboot/components/rpc/Utils.java b/src/main/java/io/jboot/components/rpc/RPCUtils.java similarity index 96% rename from src/main/java/io/jboot/components/rpc/Utils.java rename to src/main/java/io/jboot/components/rpc/RPCUtils.java index f2d061f4..2bbf240a 100644 --- a/src/main/java/io/jboot/components/rpc/Utils.java +++ b/src/main/java/io/jboot/components/rpc/RPCUtils.java @@ -23,24 +23,20 @@ import org.apache.dubbo.common.utils.StringUtils; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * @author michael yang (fuhai999@gmail.com) * @Date: 2020/3/20 */ -public class Utils { +public class RPCUtils { /** * 根据注解来设置对象内容,参考 dubbo 下的 AbstractConfig - * + * 参考 org.apache.dubbo.config.AbstractConfig#appendAnnotation * @param annotationClass * @param annotation * @param appendTo - * @see org.apache.dubbo.config.AbstractConfig#appendAnnotation */ public static void appendAnnotation(Class annotationClass, Object annotation, Object appendTo) { Method[] methods = annotationClass.getMethods(); @@ -93,7 +89,11 @@ public class Utils { try { String setterName = "set" + StrUtil.firstCharToUpperCase(field.getName()); Method method = copyTo.getClass().getMethod(setterName, getBoxedClass(field.getType())); - method.invoke(copyTo, field.get(copyFrom)); + + field.setAccessible(true); + Object data = field.get(copyFrom); + + method.invoke(copyTo, data); } catch (Exception e) { // ignore } diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index 91d03b68..7ad01f83 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -20,7 +20,7 @@ import io.jboot.app.config.JbootConfigManager; import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; -import io.jboot.components.rpc.Utils; +import io.jboot.components.rpc.RPCUtils; import io.jboot.utils.StrUtil; import org.apache.dubbo.config.*; import org.apache.dubbo.config.bootstrap.DubboBootstrap; @@ -138,14 +138,14 @@ class DubboUtil { //方法配置 配置 Map methodConfigs = configs(MethodConfig.class, "jboot.rpc.dubbo.method"); - Utils.setChildConfig(methodConfigs, argumentConfigs, "jboot.rpc.dubbo.method", "argument"); + RPCUtils.setChildConfig(methodConfigs, argumentConfigs, "jboot.rpc.dubbo.method", "argument"); //消费者 配置 Map consumerConfigs = configs(ConsumerConfig.class, "jboot.rpc.dubbo.consumer"); - Utils.setChildConfig(consumerConfigs, methodConfigs, "jboot.rpc.dubbo.consumer", "method"); - Utils.setChildConfig(consumerConfigs, protocolConfigs, "jboot.rpc.dubbo.consumer", "protocol"); - Utils.setChildConfig(consumerConfigs, registryConfigs, "jboot.rpc.dubbo.consumer", "registry"); + RPCUtils.setChildConfig(consumerConfigs, methodConfigs, "jboot.rpc.dubbo.consumer", "method"); + RPCUtils.setChildConfig(consumerConfigs, protocolConfigs, "jboot.rpc.dubbo.consumer", "protocol"); + RPCUtils.setChildConfig(consumerConfigs, registryConfigs, "jboot.rpc.dubbo.consumer", "registry"); if (consumerConfigs != null && !consumerConfigs.isEmpty()) { @@ -159,9 +159,9 @@ class DubboUtil { //服务提供者 配置 Map providerConfigs = configs(ProviderConfig.class, "jboot.rpc.dubbo.provider"); - Utils.setChildConfig(providerConfigs, methodConfigs, "jboot.rpc.dubbo.provider", "method"); - Utils.setChildConfig(providerConfigs, protocolConfigs, "jboot.rpc.dubbo.provider", "protocol"); - Utils.setChildConfig(providerConfigs, registryConfigs, "jboot.rpc.dubbo.provider", "registry"); + RPCUtils.setChildConfig(providerConfigs, methodConfigs, "jboot.rpc.dubbo.provider", "method"); + RPCUtils.setChildConfig(providerConfigs, protocolConfigs, "jboot.rpc.dubbo.provider", "protocol"); + RPCUtils.setChildConfig(providerConfigs, registryConfigs, "jboot.rpc.dubbo.provider", "registry"); if (providerConfigs != null && !providerConfigs.isEmpty()) { providerConfigMap.putAll(providerConfigs); @@ -176,7 +176,7 @@ class DubboUtil { public static ReferenceConfig toReferenceConfig(JbootrpcReferenceConfig rc) { ReferenceConfig referenceConfig = new ReferenceConfig(); - Utils.copyFields(rc, referenceConfig); + RPCUtils.copyFields(rc, referenceConfig); //reference coonsumer if (rc.getConsumer() != null) { @@ -195,7 +195,7 @@ class DubboUtil { public static ServiceConfig toServiceConfig(JbootrpcServiceConfig sc) { ServiceConfig serviceConfig = new ServiceConfig(); - Utils.copyFields(sc, serviceConfig); + RPCUtils.copyFields(sc, serviceConfig); //service provider if (StrUtil.isNotBlank(sc.getProvider())) { diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java index 85a79d81..974f60fa 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java @@ -20,7 +20,7 @@ import com.weibo.api.motan.util.MotanFrameworkUtil; import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; -import io.jboot.components.rpc.Utils; +import io.jboot.components.rpc.RPCUtils; import io.jboot.utils.StrUtil; import java.util.ArrayList; @@ -65,9 +65,9 @@ public class MotanUtil { //baseService 配置 Map serviceConfigs = configs(BasicServiceInterfaceConfig.class, "jboot.rpc.motan.service"); - Utils.setChildConfig(serviceConfigs, methodConfigs, "jboot.rpc.motan.service", "method"); - Utils.setChildConfig(serviceConfigs, protocolConfigs, "jboot.rpc.motan.service", "protocol"); - Utils.setChildConfig(serviceConfigs, registryConfigs, "jboot.rpc.motan.service", "registry"); + RPCUtils.setChildConfig(serviceConfigs, methodConfigs, "jboot.rpc.motan.service", "method"); + RPCUtils.setChildConfig(serviceConfigs, protocolConfigs, "jboot.rpc.motan.service", "protocol"); + RPCUtils.setChildConfig(serviceConfigs, registryConfigs, "jboot.rpc.motan.service", "registry"); if (serviceConfigs != null && !serviceConfigs.isEmpty()) { @@ -76,9 +76,9 @@ public class MotanUtil { //baseReferer 配置 Map refererConfigs = configs(BasicRefererInterfaceConfig.class, "jboot.rpc.motan.referer"); - Utils.setChildConfig(refererConfigs, methodConfigs, "jboot.rpc.motan.referer", "method"); - Utils.setChildConfig(refererConfigs, protocolConfigs, "jboot.rpc.motan.referer", "protocol"); - Utils.setChildConfig(refererConfigs, registryConfigs, "jboot.rpc.motan.referer", "registry"); + RPCUtils.setChildConfig(refererConfigs, methodConfigs, "jboot.rpc.motan.referer", "method"); + RPCUtils.setChildConfig(refererConfigs, protocolConfigs, "jboot.rpc.motan.referer", "protocol"); + RPCUtils.setChildConfig(refererConfigs, registryConfigs, "jboot.rpc.motan.referer", "registry"); if (refererConfigs != null && !refererConfigs.isEmpty()) { baseRefererConfigMap.putAll(refererConfigs); @@ -90,7 +90,7 @@ public class MotanUtil { public static RefererConfig toRefererConfig(JbootrpcReferenceConfig rc) { RefererConfig refererConfig = new RefererConfig(); - Utils.copyFields(rc, refererConfig); + RPCUtils.copyFields(rc, refererConfig); //referer protocol @@ -135,7 +135,7 @@ public class MotanUtil { public static ServiceConfig toServiceConfig(JbootrpcServiceConfig sc) { ServiceConfig serviceConfig = new ServiceConfig(); - Utils.copyFields(sc, serviceConfig); + RPCUtils.copyFields(sc, serviceConfig); //service protocol -- Gitee From aca4ee2cc8b37e687e2a1df5f7f9abfb9e0d9046 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 7 Apr 2020 16:05:38 +0800 Subject: [PATCH 0225/1965] fixed RPCUtils.copyFields() method error --- src/main/java/io/jboot/components/rpc/RPCUtils.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/RPCUtils.java b/src/main/java/io/jboot/components/rpc/RPCUtils.java index 2bbf240a..c32b1dfe 100644 --- a/src/main/java/io/jboot/components/rpc/RPCUtils.java +++ b/src/main/java/io/jboot/components/rpc/RPCUtils.java @@ -34,6 +34,7 @@ public class RPCUtils { /** * 根据注解来设置对象内容,参考 dubbo 下的 AbstractConfig * 参考 org.apache.dubbo.config.AbstractConfig#appendAnnotation + * * @param annotationClass * @param annotation * @param appendTo @@ -69,8 +70,8 @@ public class RPCUtils { // ignore } } - } catch (Throwable e) { - e.printStackTrace(); + } catch (Exception ex) { + ex.printStackTrace(); } } } @@ -94,8 +95,10 @@ public class RPCUtils { Object data = field.get(copyFrom); method.invoke(copyTo, data); - } catch (Exception e) { + } catch (NoSuchMethodException ex) { // ignore + } catch (Exception ex) { + ex.printStackTrace(); } } } @@ -138,7 +141,6 @@ public class RPCUtils { Method method = entry.getValue().getClass().getMethod(setterMethodName, List.class); method.invoke(entry.getValue(), argCfgList); - } catch (Exception e) { e.printStackTrace(); } -- Gitee From b931c2e410aaf19bd427f3282f175b15600ee25d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 8 Apr 2020 17:05:04 +0800 Subject: [PATCH 0226/1965] fixed JbootRpcApplication can not init plugins and interceptors --- .../io/jboot/app/JbootRpcApplication.java | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/app/JbootRpcApplication.java b/src/main/java/io/jboot/app/JbootRpcApplication.java index 4b49bde8..e852a106 100644 --- a/src/main/java/io/jboot/app/JbootRpcApplication.java +++ b/src/main/java/io/jboot/app/JbootRpcApplication.java @@ -15,9 +15,13 @@ */ package io.jboot.app; +import com.jfinal.config.Interceptors; +import com.jfinal.config.Plugins; +import com.jfinal.plugin.IPlugin; import io.jboot.app.config.JbootConfigManager; import io.jboot.core.JbootCoreConfig; +import java.util.List; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; @@ -45,18 +49,47 @@ public class JbootRpcApplication { } - static class RPCServer extends Thread { private JbootCoreConfig coreConfig; + private final Plugins plugins = new Plugins(); + private final Interceptors interceptors = new Interceptors(); + public RPCServer(JbootCoreConfig coreConfig) { this.coreConfig = coreConfig; doInit(); } - private void doInit(){ - this.coreConfig.onStart(); + private void doInit() { + //aop interceptors + coreConfig.configInterceptor(interceptors); + + //plugins + coreConfig.configPlugin(plugins); + startPlugins(); + + //on start + coreConfig.onStart(); + } + + private void startPlugins() { + List pluginList = plugins.getPluginList(); + if (pluginList == null) { + return; + } + + for (IPlugin plugin : pluginList) { + try { + if (plugin.start() == false) { + String message = "Plugin start error: " + plugin.getClass().getName(); + throw new RuntimeException(message); + } + } catch (Exception e) { + String message = "Plugin start error: " + plugin.getClass().getName() + ". \n" + e.getMessage(); + throw new RuntimeException(message, e); + } + } } @Override @@ -65,7 +98,7 @@ public class JbootRpcApplication { await(); } - private void await() { + private void await() { try { LOCK.lock(); STOP.await(); -- Gitee From 7a4c577355b2d59dc54fa124bee6b488cf7b6d71 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 8 Apr 2020 17:10:05 +0800 Subject: [PATCH 0227/1965] fixed JbootRpcApplication can not init plugins and interceptors --- src/main/java/io/jboot/app/JbootRpcApplication.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/app/JbootRpcApplication.java b/src/main/java/io/jboot/app/JbootRpcApplication.java index e852a106..710d3b7c 100644 --- a/src/main/java/io/jboot/app/JbootRpcApplication.java +++ b/src/main/java/io/jboot/app/JbootRpcApplication.java @@ -51,8 +51,7 @@ public class JbootRpcApplication { static class RPCServer extends Thread { - private JbootCoreConfig coreConfig; - + private final JbootCoreConfig coreConfig; private final Plugins plugins = new Plugins(); private final Interceptors interceptors = new Interceptors(); @@ -94,7 +93,7 @@ public class JbootRpcApplication { @Override public void run() { - addHook(coreConfig); + initShutdownHook(); await(); } @@ -109,10 +108,10 @@ public class JbootRpcApplication { } } - private void addHook(JbootCoreConfig config) { + private void initShutdownHook() { Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { - config.onStop(); + coreConfig.onStop(); } catch (Exception e) { System.err.println("jboot rpc stop exception : " + e.toString()); } -- Gitee From 0dc89bc471bab9b83a76640eed04ad932db1dc74 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 8 Apr 2020 17:21:59 +0800 Subject: [PATCH 0228/1965] add unScanClassPrefix config in ClassScanner --- .../java/io/jboot/utils/ClassScanner.java | 126 ++++++++++-------- 1 file changed, 68 insertions(+), 58 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 56cb08dd..801c34a6 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -222,68 +222,68 @@ public class ClassScanner { } - public static void addUnscanClass(String prefix) { + public static void addUnscanClassPrefix(String prefix) { excludeClasses.add(prefix.trim()); } static { - addUnscanClass("com.jfinal."); - addUnscanClass("org.aopalliance."); - addUnscanClass("org.apache."); - addUnscanClass("org.nustaq."); - addUnscanClass("net.sf."); - addUnscanClass("org.slf4j."); - addUnscanClass("org.antlr."); - addUnscanClass("org.jboss."); - addUnscanClass("org.javassist."); - addUnscanClass("org.hamcrest."); - addUnscanClass("org.jsoup."); - addUnscanClass("org.objenesis."); - addUnscanClass("org.ow2."); - addUnscanClass("org.reactivest."); - addUnscanClass("org.yaml."); - addUnscanClass("org.checker"); - addUnscanClass("org.codehaus"); - addUnscanClass("ch.qos."); - addUnscanClass("com.alibaba.csp."); - addUnscanClass("com.alibaba.nacos."); - addUnscanClass("com.alibaba.druid."); - addUnscanClass("com.alibaba.fastjson."); - addUnscanClass("com.aliyun.open"); - addUnscanClass("com.caucho"); - addUnscanClass("com.codahale"); - addUnscanClass("com.ctrip.framework.apollo"); - addUnscanClass("com.ecwid."); - addUnscanClass("com.esotericsoftware."); - addUnscanClass("com.fasterxml."); - addUnscanClass("com.github."); - addUnscanClass("com.google."); - addUnscanClass("com.rabbitmq."); - addUnscanClass("com.squareup."); - addUnscanClass("com.typesafe."); - addUnscanClass("com.weibo.api.motan."); - addUnscanClass("com.zaxxer."); - addUnscanClass("com.mysql."); - addUnscanClass("org.gjt."); - addUnscanClass("io.dropwizard"); - addUnscanClass("io.jsonwebtoken"); - addUnscanClass("io.lettuce"); - addUnscanClass("reactor.adapter"); - addUnscanClass("io.prometheus"); - addUnscanClass("io.seata."); - addUnscanClass("io.swagger."); - addUnscanClass("io.undertow."); - addUnscanClass("it.sauronsoftware"); - addUnscanClass("javax."); - addUnscanClass("java."); - addUnscanClass("junit."); - addUnscanClass("jline."); - addUnscanClass("redis."); - addUnscanClass("lombok."); - addUnscanClass("net.oschina.j2cache"); - addUnscanClass("cn.hutool."); - addUnscanClass("com.dyuproject."); - addUnscanClass("io.protostuff."); + excludeClasses.add("com.jfinal."); + excludeClasses.add("org.aopalliance."); + excludeClasses.add("org.apache."); + excludeClasses.add("org.nustaq."); + excludeClasses.add("net.sf."); + excludeClasses.add("org.slf4j."); + excludeClasses.add("org.antlr."); + excludeClasses.add("org.jboss."); + excludeClasses.add("org.javassist."); + excludeClasses.add("org.hamcrest."); + excludeClasses.add("org.jsoup."); + excludeClasses.add("org.objenesis."); + excludeClasses.add("org.ow2."); + excludeClasses.add("org.reactivest."); + excludeClasses.add("org.yaml."); + excludeClasses.add("org.checker"); + excludeClasses.add("org.codehaus"); + excludeClasses.add("ch.qos."); + excludeClasses.add("com.alibaba.csp."); + excludeClasses.add("com.alibaba.nacos."); + excludeClasses.add("com.alibaba.druid."); + excludeClasses.add("com.alibaba.fastjson."); + excludeClasses.add("com.aliyun.open"); + excludeClasses.add("com.caucho"); + excludeClasses.add("com.codahale"); + excludeClasses.add("com.ctrip.framework.apollo"); + excludeClasses.add("com.ecwid."); + excludeClasses.add("com.esotericsoftware."); + excludeClasses.add("com.fasterxml."); + excludeClasses.add("com.github."); + excludeClasses.add("com.google."); + excludeClasses.add("com.rabbitmq."); + excludeClasses.add("com.squareup."); + excludeClasses.add("com.typesafe."); + excludeClasses.add("com.weibo.api.motan."); + excludeClasses.add("com.zaxxer."); + excludeClasses.add("com.mysql."); + excludeClasses.add("org.gjt."); + excludeClasses.add("io.dropwizard"); + excludeClasses.add("io.jsonwebtoken"); + excludeClasses.add("io.lettuce"); + excludeClasses.add("reactor.adapter"); + excludeClasses.add("io.prometheus"); + excludeClasses.add("io.seata."); + excludeClasses.add("io.swagger."); + excludeClasses.add("io.undertow."); + excludeClasses.add("it.sauronsoftware"); + excludeClasses.add("javax."); + excludeClasses.add("java."); + excludeClasses.add("junit."); + excludeClasses.add("jline."); + excludeClasses.add("redis."); + excludeClasses.add("lombok."); + excludeClasses.add("net.oschina.j2cache"); + excludeClasses.add("cn.hutool."); + excludeClasses.add("com.dyuproject."); + excludeClasses.add("io.protostuff."); } static { @@ -306,6 +306,16 @@ public class ClassScanner { } } } + + String unScanClassPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.unScanClassPrefix"); + if (unScanClassPrefix != null) { + String[] prefixes = unScanClassPrefix.split(","); + for (String prefix : prefixes) { + if (prefix != null && prefix.trim().length() > 0) { + addUnscanClassPrefix(prefix.trim()); + } + } + } } public static List> scanSubClass(Class pclazz) { -- Gitee From 9a24a56d56695f13f0214273b5315b8f31f0961a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Apr 2020 11:30:17 +0800 Subject: [PATCH 0229/1965] upgrade dependencies --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 282ffdd2..dfa192f3 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 4.5.12 4.4.13 4.1.48.Final - 1.7.1 + 1.7.2 2.7.6 3.11.0.Final 1.1.0 -- Gitee From fe65b174d1aca9bf54fa33e4df1d0bc5e65c61e1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Apr 2020 11:36:55 +0800 Subject: [PATCH 0230/1965] v3.1.6 --- src/main/java/io/jboot/components/rpc/RPCUtils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/rpc/RPCUtils.java b/src/main/java/io/jboot/components/rpc/RPCUtils.java index c32b1dfe..c2053cb9 100644 --- a/src/main/java/io/jboot/components/rpc/RPCUtils.java +++ b/src/main/java/io/jboot/components/rpc/RPCUtils.java @@ -94,7 +94,9 @@ public class RPCUtils { field.setAccessible(true); Object data = field.get(copyFrom); - method.invoke(copyTo, data); + if (data != null) { + method.invoke(copyTo, data); + } } catch (NoSuchMethodException ex) { // ignore } catch (Exception ex) { -- Gitee From 40ad53005fe7856444038456f7ee7727d7ea9b3d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Apr 2020 13:02:11 +0800 Subject: [PATCH 0231/1965] v3.1.6 --- .../support/nacos/NacosConfigManager.java | 9 +- .../support/nacos/NacosServerConfig.java | 190 ++++++++++++++++++ 2 files changed, 193 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java index 011f9d23..b9b961df 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java @@ -54,10 +54,8 @@ public class NacosConfigManager { } try { - Properties properties = new Properties(); - properties.put("serverAddr", nacosServerConfig.getServerAddr()); - ConfigService configService = NacosFactory.createConfigService(properties); + ConfigService configService = NacosFactory.createConfigService(nacosServerConfig.toProperties()); String content = configService.getConfig(nacosServerConfig.getDataId() , nacosServerConfig.getGroup(), 3000); @@ -68,7 +66,7 @@ public class NacosConfigManager { } } - new NacosConfigIniter(this).initListener(configService,nacosServerConfig); + new NacosConfigIniter(this).initListener(configService, nacosServerConfig); } catch (Exception e) { e.printStackTrace(); @@ -78,6 +76,7 @@ public class NacosConfigManager { /** * 接收的 nacos 服务器消息 + * * @param configInfo */ public void doReceiveConfigInfo(String configInfo) { @@ -111,8 +110,6 @@ public class NacosConfigManager { } - - private Properties str2Properties(String content) { try { Properties properties = new Properties(); diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java b/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java index 3a76a104..720d1f06 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java @@ -19,11 +19,35 @@ package io.jboot.app.config.support.nacos; import io.jboot.app.config.annotation.ConfigModel; import io.jboot.utils.StrUtil; +import java.util.Properties; + +/** + * @see com.alibaba.nacos.api.PropertyKeyConst + */ @ConfigModel(prefix = "jboot.config.nacos") public class NacosServerConfig { private boolean enable = false; + + private String isUseCloudNamespaceParsing; + private String isUseEndpointParsingRule; + private String endpoint; + private String endpointPort; + private String namespace; + private String username; + private String password; + private String accessKey; + private String secretKey; + private String ramRoleName; private String serverAddr; + private String contextPath; + private String clusterName; + private String encode; + private String configLongPollTimeout; + private String configRetryTime; + private String maxRetry; + private String enableRemoteSyncConfig; + private String dataId; private String group; @@ -35,6 +59,86 @@ public class NacosServerConfig { this.enable = enable; } + public String getIsUseCloudNamespaceParsing() { + return isUseCloudNamespaceParsing; + } + + public void setIsUseCloudNamespaceParsing(String isUseCloudNamespaceParsing) { + this.isUseCloudNamespaceParsing = isUseCloudNamespaceParsing; + } + + public String getIsUseEndpointParsingRule() { + return isUseEndpointParsingRule; + } + + public void setIsUseEndpointParsingRule(String isUseEndpointParsingRule) { + this.isUseEndpointParsingRule = isUseEndpointParsingRule; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public String getEndpointPort() { + return endpointPort; + } + + public void setEndpointPort(String endpointPort) { + this.endpointPort = endpointPort; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getAccessKey() { + return accessKey; + } + + public void setAccessKey(String accessKey) { + this.accessKey = accessKey; + } + + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public String getRamRoleName() { + return ramRoleName; + } + + public void setRamRoleName(String ramRoleName) { + this.ramRoleName = ramRoleName; + } + public String getServerAddr() { return serverAddr; } @@ -43,6 +147,62 @@ public class NacosServerConfig { this.serverAddr = serverAddr; } + public String getContextPath() { + return contextPath; + } + + public void setContextPath(String contextPath) { + this.contextPath = contextPath; + } + + public String getClusterName() { + return clusterName; + } + + public void setClusterName(String clusterName) { + this.clusterName = clusterName; + } + + public String getEncode() { + return encode; + } + + public void setEncode(String encode) { + this.encode = encode; + } + + public String getConfigLongPollTimeout() { + return configLongPollTimeout; + } + + public void setConfigLongPollTimeout(String configLongPollTimeout) { + this.configLongPollTimeout = configLongPollTimeout; + } + + public String getConfigRetryTime() { + return configRetryTime; + } + + public void setConfigRetryTime(String configRetryTime) { + this.configRetryTime = configRetryTime; + } + + public String getMaxRetry() { + return maxRetry; + } + + public void setMaxRetry(String maxRetry) { + this.maxRetry = maxRetry; + } + + public String getEnableRemoteSyncConfig() { + return enableRemoteSyncConfig; + } + + public void setEnableRemoteSyncConfig(String enableRemoteSyncConfig) { + this.enableRemoteSyncConfig = enableRemoteSyncConfig; + } + public String getDataId() { return dataId; } @@ -59,6 +219,36 @@ public class NacosServerConfig { this.group = group; } + public Properties toProperties() { + Properties properties = new Properties(); + putProperties(properties,"isUseCloudNamespaceParsing",isUseCloudNamespaceParsing); + putProperties(properties,"isUseEndpointParsingRule",isUseEndpointParsingRule); + putProperties(properties,"endpoint",endpoint); + putProperties(properties,"endpointPort",endpointPort); + putProperties(properties,"namespace",namespace); + putProperties(properties,"username",username); + putProperties(properties,"password",password); + putProperties(properties,"accessKey",accessKey); + putProperties(properties,"secretKey",secretKey); + putProperties(properties,"ramRoleName",ramRoleName); + putProperties(properties,"serverAddr",serverAddr); + putProperties(properties,"contextPath",contextPath); + putProperties(properties,"clusterName",clusterName); + putProperties(properties,"encode",encode); + putProperties(properties,"configLongPollTimeout",configLongPollTimeout); + putProperties(properties,"configRetryTime",configRetryTime); + putProperties(properties,"maxRetry",maxRetry); + putProperties(properties,"enableRemoteSyncConfig",enableRemoteSyncConfig); + return properties; + } + + private void putProperties(Properties p, String key, String value) { + if (StrUtil.isNotBlank(value)) { + p.put(key, value); + } + } + + public boolean isConfigOk() { return StrUtil.areNotEmpty(serverAddr, dataId, group); } -- Gitee From d0435d90d6d5478fa2c5e7bbb9601b3180a1ec9b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Apr 2020 13:17:57 +0800 Subject: [PATCH 0232/1965] update config docs --- doc/docs/config.md | 66 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/doc/docs/config.md b/doc/docs/config.md index ffd11d06..e0b4a380 100644 --- a/doc/docs/config.md +++ b/doc/docs/config.md @@ -169,11 +169,30 @@ Component1Config config = Jboot.config(Component1Config.class); ## 开启 Nacos 分布式配置中心 -**第一步,启动 nacos** -相关文档在 https://nacos.io/zh-cn/ +**第一步,添加 nacos 客户端的 Maven 依赖** + +```xml + + com.alibaba.nacos + nacos-client + 1.2.1 + +``` + + + +**第二步:启动 nacos** + +如何启动 nacos 的相关文档在 + +https://nacos.io/zh-cn/docs/quick-start.html + +或者 + +https://nacos.io/zh-cn/docs/quick-start-docker.html -**第二步,在 jboot.properties 添加如下配置** +**第三步,在 jboot.properties 添加如下配置** ```java jboot.config.nacos.enable = true @@ -182,12 +201,47 @@ jboot.config.nacos.dataId = jboot jboot.config.nacos.group = jboot ``` +支持如下更多配置,但是最简单的只需要以上配置就可以正常运行 + + +``` +jboot.config.nacos.isUseCloudNamespaceParsing = xxx +jboot.config.nacos.isUseEndpointParsingRule = xxx +jboot.config.nacos.endpoint = xxx +jboot.config.nacos.endpointPort = xxx +jboot.config.nacos.namespace = xxx +jboot.config.nacos.username = xxx +jboot.config.nacos.password = xxx +jboot.config.nacos.accessKey = xxx +jboot.config.nacos.secretKey = xxx +jboot.config.nacos.ramRoleName = xxx +jboot.config.nacos.serverAddr = xxx +jboot.config.nacos.contextPath = xxx +jboot.config.nacos.clusterName = xxx +jboot.config.nacos.encode = xxx +jboot.config.nacos.configLongPollTimeout = xxx +jboot.config.nacos.configRetryTime = xxx +jboot.config.nacos.maxRetry = xxx +jboot.config.nacos.enableRemoteSyncConfig = xxx +``` + ## 开启 Apollo 分布式配置中心 -**第一步,启动 Apollo** +**第一步:添加 Apollo 客户端的 Maven 依赖** + +```xml + + com.ctrip.framework.apollo + apollo-client + 1.6.0 + +``` + +**第二步,启动 Apollo** + 相关文档在 https://github.com/ctripcorp/apollo/wiki/Quick-Start -**第二步,在 jboot.properties 添加如下配置** +**第三步,在 jboot.properties 添加如下配置** ``` jboot.config.apollo.enable = true @@ -252,7 +306,7 @@ public MyConfigDecriptor implements JbootConfigDecryptor { 修改为大写,符号点(.)修改为下划线(_)。 -## Jboot 所有配置参考 +## Jboot 其他配置参考 ``` undertow.devMode=true # 设置undertow为开发模式 -- Gitee From f53f730074fea140996c2452146a2c50afed0bb4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Apr 2020 15:41:03 +0800 Subject: [PATCH 0233/1965] v3.1.6 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 14 ++++++++++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f6d8e45b..49317d23 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.1.5 + 3.1.6 ``` diff --git a/changes.txt b/changes.txt index 577f3c8c..1eb1a4ab 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,17 @@ +jboot v3.1.6: +新增:JbootReturnValueRender,用于可以在 Action 里进行返回值渲染 +新增:ResponseEntity,可以直接在 Action 返回其进行渲染 +优化:对 JbootGatewayConfig 网关配置进行优化,用户配置了错误的 uri 时会给出提示 +优化:升级 Dubbo、Motan、Sentinel、Nacos 等到最新版本 +优化:ClassScanner 添加一些常用的排除 jar 和 class +优化:更多关于 Nacos 分布式配置中心的配置支持 +修复:RPCUtils.copyFields() 无法正确复制配置内容的问题 +修复:JbootRpcApplication 无法正确启动插件和拦截器的问题 +文档:修改 AOP 文档的内容错误问题 +文档:完善分布式配置中心对 Nacos 以及 Apollo 支持的相关文档 + + + jboot v3.1.5: 新增:新增 Motan RPC 的 export 和 host 的相关配置以及test代码 新增:JbootJson 支持驼峰式 JsonKey 输出,感谢Gitee的 @herowjun diff --git a/doc/docs/install.md b/doc/docs/install.md index 04152d49..69d8f49f 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.1.5 + 3.1.6 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 603ec045..7a55efbd 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.1.5 + 3.1.6 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 7d120266..5d3327ec 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.1.5"; + public static String VERSION = "3.1.6"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 793fe74ec56c48f2b0fa71520fe47e01f11f7a70 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Apr 2020 15:42:38 +0800 Subject: [PATCH 0234/1965] v3.1.6 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dfa192f3..c7a35937 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.6-SNAPSHOT + 3.1.6 jar jboot -- Gitee From 85a9d6adfecb30dd3be795df47c8948a447668a5 Mon Sep 17 00:00:00 2001 From: wangjun Date: Fri, 10 Apr 2020 16:55:04 +0800 Subject: [PATCH 0235/1965] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BB=8Erequest?= =?UTF-8?q?=E9=87=8C=E9=9D=A2=E8=8E=B7=E5=8F=96jwt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/support/jwt/JwtInterceptor.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/io/jboot/support/jwt/JwtInterceptor.java b/src/main/java/io/jboot/support/jwt/JwtInterceptor.java index 94412627..5f0b4f20 100644 --- a/src/main/java/io/jboot/support/jwt/JwtInterceptor.java +++ b/src/main/java/io/jboot/support/jwt/JwtInterceptor.java @@ -44,6 +44,9 @@ public class JwtInterceptor implements FixedInterceptor { HttpServletRequest request = inv.getController().getRequest(); String token = request.getHeader(JwtManager.me().getHttpHeaderName()); + if(StrUtil.isBlank(token)) + token = request.getParameter(JwtManager.me().getHttpHeaderName()); + if (StrUtil.isBlank(token)) { processInvoke(inv, null); return; -- Gitee From 74d646c2b5dfa7f7d992afae47a3bd90c0877dd3 Mon Sep 17 00:00:00 2001 From: wangjun Date: Fri, 10 Apr 2020 17:26:29 +0800 Subject: [PATCH 0236/1965] =?UTF-8?q?jwt=E5=A2=9E=E5=8A=A0=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E4=BB=8Erequest=E9=87=8C=E9=9D=A2=E8=8E=B7=E5=8F=96jw?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/support/jwt/JwtInterceptor.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/support/jwt/JwtInterceptor.java b/src/main/java/io/jboot/support/jwt/JwtInterceptor.java index 5f0b4f20..ce3a0445 100644 --- a/src/main/java/io/jboot/support/jwt/JwtInterceptor.java +++ b/src/main/java/io/jboot/support/jwt/JwtInterceptor.java @@ -44,8 +44,7 @@ public class JwtInterceptor implements FixedInterceptor { HttpServletRequest request = inv.getController().getRequest(); String token = request.getHeader(JwtManager.me().getHttpHeaderName()); - if(StrUtil.isBlank(token)) - token = request.getParameter(JwtManager.me().getHttpHeaderName()); + if(StrUtil.isBlank(token)) token = request.getParameter("token"); if (StrUtil.isBlank(token)) { processInvoke(inv, null); -- Gitee From c32eabb45350525615ccd403497b01cd166ab973 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 10 Apr 2020 17:37:30 +0800 Subject: [PATCH 0237/1965] v3.1.7 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c7a35937..092402fb 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.6 + 3.1.7-SNAPSHOT jar jboot -- Gitee From 2d2663b1f949826265d54dbb50b328e5918772eb Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 10 Apr 2020 17:38:36 +0800 Subject: [PATCH 0238/1965] add get jwt from parameter support --- src/main/java/io/jboot/support/jwt/JwtConfig.java | 10 ++++++++++ src/main/java/io/jboot/support/jwt/JwtInterceptor.java | 4 +++- src/main/java/io/jboot/support/jwt/JwtManager.java | 4 ++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/support/jwt/JwtConfig.java b/src/main/java/io/jboot/support/jwt/JwtConfig.java index a6876411..f454abc1 100644 --- a/src/main/java/io/jboot/support/jwt/JwtConfig.java +++ b/src/main/java/io/jboot/support/jwt/JwtConfig.java @@ -27,6 +27,8 @@ import io.jboot.utils.StrUtil; public class JwtConfig { private String httpHeaderName = "Jwt"; + private String httpParameterKey; + private String secret; /** @@ -43,6 +45,14 @@ public class JwtConfig { this.httpHeaderName = httpHeaderName; } + public String getHttpParameterKey() { + return httpParameterKey; + } + + public void setHttpParameterKey(String httpParameterKey) { + this.httpParameterKey = httpParameterKey; + } + public String getSecret() { return secret; } diff --git a/src/main/java/io/jboot/support/jwt/JwtInterceptor.java b/src/main/java/io/jboot/support/jwt/JwtInterceptor.java index ce3a0445..611d8125 100644 --- a/src/main/java/io/jboot/support/jwt/JwtInterceptor.java +++ b/src/main/java/io/jboot/support/jwt/JwtInterceptor.java @@ -44,7 +44,9 @@ public class JwtInterceptor implements FixedInterceptor { HttpServletRequest request = inv.getController().getRequest(); String token = request.getHeader(JwtManager.me().getHttpHeaderName()); - if(StrUtil.isBlank(token)) token = request.getParameter("token"); + if (StrUtil.isBlank(token) && StrUtil.isNotBlank(JwtManager.me().getHttpParameterKey())) { + token = request.getParameter(JwtManager.me().getHttpParameterKey()); + } if (StrUtil.isBlank(token)) { processInvoke(inv, null); diff --git a/src/main/java/io/jboot/support/jwt/JwtManager.java b/src/main/java/io/jboot/support/jwt/JwtManager.java index e2725328..3195bebc 100644 --- a/src/main/java/io/jboot/support/jwt/JwtManager.java +++ b/src/main/java/io/jboot/support/jwt/JwtManager.java @@ -64,6 +64,10 @@ public class JwtManager { return getConfig().getHttpHeaderName(); } + public String getHttpParameterKey() { + return getConfig().getHttpParameterKey(); + } + public Map parseJwtToken(String token) { SecretKey secretKey = generalKey(); try { -- Gitee From 7f44838593804aea1e7cfd5c9e58257d7e5ba8d5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 12 Apr 2020 17:38:31 +0800 Subject: [PATCH 0239/1965] =?UTF-8?q?fixed=EF=BC=9AgetAction()=20cat=20not?= =?UTF-8?q?=20work=20in=20ActionHandler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/web/handler/JbootActionHandler.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 114861e8..0a1d9599 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -39,21 +39,39 @@ public class JbootActionHandler extends ActionHandler { private static final Log log = Log.getLog(JbootActionHandler.class); /** - * 方便子类复写、从而可以实现 自定义Action的功能 - * + * 方便子类复写、从而可以实现 自定义 Action 的功能 * @param target * @param urlPara + * @param request * @return */ public Action getAction(String target, String[] urlPara, HttpServletRequest request) { - return actionMapping.getAction(target, urlPara); + return this.getAction(target, urlPara); } + /** + * 方便子类复写、从而可以实现 自定义 Action 的功能 + * @param target + * @param urlPara + * @return + */ + @Override + protected Action getAction(String target, String[] urlPara) { + return super.getAction(target, urlPara); + } + + /** + * 方便子类复写、从而可以实现 自定义 Invocation 的功能 + * @param action + * @param controller + * @return + */ public Invocation getInvocation(Action action, Controller controller) { return new Invocation(action, controller); } + public void setResponse(HttpServletResponse response, Action action) { } -- Gitee From 4e7a183f897dcbd65b4a77adc807fc692e521036 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 13 Apr 2020 10:08:34 +0800 Subject: [PATCH 0240/1965] v3.1.7 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 49317d23..a1f772f6 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.1.6 + 3.1.7 ``` diff --git a/changes.txt b/changes.txt index 1eb1a4ab..f9d0846b 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.1.7: +新增:JWT 可以通过 request para 传入数据的支持 +修复:复写 ActionHandler.getAction 可能无效的问题 + + + jboot v3.1.6: 新增:JbootReturnValueRender,用于可以在 Action 里进行返回值渲染 新增:ResponseEntity,可以直接在 Action 返回其进行渲染 diff --git a/doc/docs/install.md b/doc/docs/install.md index 69d8f49f..dbad59d0 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.1.6 + 3.1.7 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 7a55efbd..ce42f9f8 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.1.6 + 3.1.7 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 5d3327ec..57e13ca9 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.1.6"; + public static String VERSION = "3.1.7"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 11ae8684a4fe900bb5fbc304b6eb90e58ac13ed3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 13 Apr 2020 10:33:12 +0800 Subject: [PATCH 0241/1965] v3.1.7 release (^.^)YYa!! --- changes.txt | 1 + pom.xml | 10 +++++----- .../components/schedule/JbootScheduleManager.java | 7 +++++-- src/main/java/io/jboot/utils/ClassScanner.java | 14 ++++++++++++++ 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/changes.txt b/changes.txt index f9d0846b..712a0fdd 100644 --- a/changes.txt +++ b/changes.txt @@ -1,6 +1,7 @@ jboot v3.1.7: 新增:JWT 可以通过 request para 传入数据的支持 修复:复写 ActionHandler.getAction 可能无效的问题 +优化:排除 Jboot 内部不必要的 Class 扫描 diff --git a/pom.xml b/pom.xml index 092402fb..438e9a6e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.7-SNAPSHOT + 3.1.7 jar jboot @@ -42,7 +42,7 @@ 4.8 - 2019.8 + 2020.4 2.0 2.5 3.3.0 @@ -70,7 +70,7 @@ 3.11.0.Final 1.1.0 1.1.8 - 4.1.5 + 4.1.6 1.8 1.8 @@ -231,7 +231,7 @@ com.alibaba druid - 1.1.21 + 1.1.22 provided @@ -366,7 +366,7 @@ com.aliyun.openservices ons-client - 1.8.4.Final + 1.8.5.Final provided diff --git a/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java b/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java index b019ff03..70096e41 100644 --- a/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java +++ b/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java @@ -79,8 +79,11 @@ public class JbootScheduleManager { private void initSchedules() { List> runnableClass = ClassScanner.scanSubClass(Runnable.class, true); - if (runnableClass != null) - for (Class rc : runnableClass) addSchedule(rc); + if (runnableClass != null) { + for (Class rc : runnableClass) { + addSchedule(rc); + } + } } public void addSchedule(Class runnableClass) { diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 801c34a6..3eea3417 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -284,6 +284,20 @@ public class ClassScanner { excludeClasses.add("cn.hutool."); excludeClasses.add("com.dyuproject."); excludeClasses.add("io.protostuff."); + excludeClasses.add("io.jboot.core."); + excludeClasses.add("io.jboot.web."); + excludeClasses.add("io.jboot.objects."); + excludeClasses.add("io.jboot.utils."); + excludeClasses.add("io.jboot.codegen."); + excludeClasses.add("io.jboot.wechat."); + excludeClasses.add("io.jboot.components."); + excludeClasses.add("io.jboot.support."); + excludeClasses.add("io.jboot.exception."); + excludeClasses.add("io.jboot.db."); + excludeClasses.add("io.jboot.aop."); + excludeClasses.add("io.jboot.app."); + excludeClasses.add("io.jboot.service."); + excludeClasses.add("io.jboot.Jboot"); } static { -- Gitee From f88dd70547248511cc9a790a3d4620dd61fd180d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 13 Apr 2020 10:34:26 +0800 Subject: [PATCH 0242/1965] v3.1.7 release (^.^)YYa!! --- src/main/java/io/jboot/utils/ClassScanner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 3eea3417..8032452d 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -42,7 +42,7 @@ public class ClassScanner { } static { - addScanJarPrefix("jboot"); +// addScanJarPrefix("jboot"); } public static void addUnscanJarPrefix(String prefix) { -- Gitee From b7aca7e10be70a7442229fbf0d2628b605b36e48 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 13 Apr 2020 12:52:03 +0800 Subject: [PATCH 0243/1965] v3.1.8 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 438e9a6e..845447a6 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.7 + 3.1.8-SNAPSHOT jar jboot -- Gitee From 6509a9185e188330de852c3302166cb08e9e30a2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 13 Apr 2020 12:54:54 +0800 Subject: [PATCH 0244/1965] modify DAO type in JbootServiceBase --- src/main/java/io/jboot/service/JbootServiceBase.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/service/JbootServiceBase.java b/src/main/java/io/jboot/service/JbootServiceBase.java index 38ccdc11..49bcb622 100644 --- a/src/main/java/io/jboot/service/JbootServiceBase.java +++ b/src/main/java/io/jboot/service/JbootServiceBase.java @@ -36,7 +36,7 @@ public class JbootServiceBase> protected static final int ACTION_DEL = 2; protected static final int ACTION_UPDATE = 3; - protected M DAO = null; + protected JbootModel DAO = null; public JbootServiceBase() { DAO = initDao(); @@ -59,7 +59,7 @@ public class JbootServiceBase> } - public M getDao() { + public JbootModel getDao() { return DAO; } -- Gitee From 787d4715ea6b06d75a09ba36d38e45e7f516d6b0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 13 Apr 2020 17:49:52 +0800 Subject: [PATCH 0245/1965] optimize Config Prop --- src/main/java/io/jboot/app/config/Prop.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/io/jboot/app/config/Prop.java b/src/main/java/io/jboot/app/config/Prop.java index 56aa0ffc..446afe91 100644 --- a/src/main/java/io/jboot/app/config/Prop.java +++ b/src/main/java/io/jboot/app/config/Prop.java @@ -34,8 +34,6 @@ class Prop { inputStream = Utils.getClassLoader().getResourceAsStream(fileName); if (inputStream != null) { properties.load(new InputStreamReader(inputStream, encoding)); - } else { - System.err.println("warning: can not load properties file in classpath, file name :" + fileName); } } catch (Exception e) { System.err.println("warning: can not load properties file in classpath, file name :" + fileName); @@ -44,7 +42,6 @@ class Prop { try { inputStream.close(); } catch (IOException e) { - e.printStackTrace(); } } } -- Gitee From 4c0323117b894c4de08200bb5786fb5b8c62058c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 13 Apr 2020 18:13:11 +0800 Subject: [PATCH 0246/1965] optimize rpc component --- .../java/io/jboot/components/rpc/JbootrpcBase.java | 9 +++++++-- .../java/io/jboot/components/rpc/JbootrpcManager.java | 10 +++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java index b84d7c3b..1474855a 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java @@ -36,7 +36,12 @@ public abstract class JbootrpcBase implements Jbootrpc { if (object == null) { synchronized (this) { if (objectCache.get(key) == null) { - startIfNecessary(); + + // onStart 方法是在 app 启动完成后,Jboot 主动去调用的 + // 但是,在某些场景可能存在没有等 app 启动完成就去获取 Service 的情况 + // 此时,需要主动先调用下 onStart 方法 + callStartMethodIfNecessary(); + object = onServiceCreate(interfaceClass, config); if (object != null) { objectCache.put(key, object); @@ -47,7 +52,7 @@ public abstract class JbootrpcBase implements Jbootrpc { return object; } - protected void startIfNecessary() { + protected void callStartMethodIfNecessary() { if (!started) { synchronized (this) { if (!started) { diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java index ed0d95fa..3072fe2d 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java @@ -47,7 +47,7 @@ public class JbootrpcManager { public Jbootrpc getJbootrpc() { if (jbootrpc == null) { if (!defaultConfig.isConfigOk()) { - throw new JbootRpcException("jboot rpc config is error, please config jboot.rpc.type = xxx in jboot.properties"); + throw new JbootRpcException("jboot rpc config is error, please set up jboot.rpc.type config value"); } jbootrpc = createJbootrpc(defaultConfig.getType()); } @@ -76,7 +76,7 @@ public class JbootrpcManager { } } - public void stop(){ + public void stop() { if (defaultConfig.isConfigOk()) { getJbootrpc().onStop(); } @@ -111,9 +111,9 @@ public class JbootrpcManager { continue; } - if (jbootrpc.serviceExport(inter, Aop.get(clazz), new JbootrpcServiceConfig(rpcBean))){ - if (Jboot.isDevMode()){ - System.err.println("rpc service[" + inter+"] has exported ok!"); + if (jbootrpc.serviceExport(inter, Aop.get(clazz), new JbootrpcServiceConfig(rpcBean))) { + if (Jboot.isDevMode()) { + System.out.println("rpc service[" + inter + "] has exported ok!"); } } } -- Gitee From bf1954bd14de3741b3e18db05fcd6b387d6e3ec1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 14 Apr 2020 10:35:14 +0800 Subject: [PATCH 0247/1965] rename PRCUtils to PRCUtil --- .../rpc/JbootrpcReferenceConfig.java | 2 +- .../components/rpc/JbootrpcServiceConfig.java | 2 +- .../rpc/{RPCUtils.java => RPCUtil.java} | 2 +- .../jboot/components/rpc/dubbo/DubboUtil.java | 20 +++++++++---------- .../jboot/components/rpc/motan/MotanUtil.java | 18 ++++++++--------- 5 files changed, 22 insertions(+), 22 deletions(-) rename src/main/java/io/jboot/components/rpc/{RPCUtils.java => RPCUtil.java} (99%) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java index 07431fe9..b31be214 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java @@ -133,7 +133,7 @@ public class JbootrpcReferenceConfig implements Serializable { } public JbootrpcReferenceConfig(RPCInject inject) { - RPCUtils.appendAnnotation(RPCInject.class, inject, this); + RPCUtil.appendAnnotation(RPCInject.class, inject, this); } public String getVersion() { diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java index 0c0ac4a1..c82e8a4c 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java @@ -124,7 +124,7 @@ public class JbootrpcServiceConfig implements Serializable { } public JbootrpcServiceConfig(RPCBean bean) { - RPCUtils.appendAnnotation(RPCBean.class, bean, this); + RPCUtil.appendAnnotation(RPCBean.class, bean, this); } public String getVersion() { diff --git a/src/main/java/io/jboot/components/rpc/RPCUtils.java b/src/main/java/io/jboot/components/rpc/RPCUtil.java similarity index 99% rename from src/main/java/io/jboot/components/rpc/RPCUtils.java rename to src/main/java/io/jboot/components/rpc/RPCUtil.java index c2053cb9..431ec5be 100644 --- a/src/main/java/io/jboot/components/rpc/RPCUtils.java +++ b/src/main/java/io/jboot/components/rpc/RPCUtil.java @@ -29,7 +29,7 @@ import java.util.*; * @author michael yang (fuhai999@gmail.com) * @Date: 2020/3/20 */ -public class RPCUtils { +public class RPCUtil { /** * 根据注解来设置对象内容,参考 dubbo 下的 AbstractConfig diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index 7ad01f83..4d157f7a 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -20,7 +20,7 @@ import io.jboot.app.config.JbootConfigManager; import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; -import io.jboot.components.rpc.RPCUtils; +import io.jboot.components.rpc.RPCUtil; import io.jboot.utils.StrUtil; import org.apache.dubbo.config.*; import org.apache.dubbo.config.bootstrap.DubboBootstrap; @@ -138,14 +138,14 @@ class DubboUtil { //方法配置 配置 Map methodConfigs = configs(MethodConfig.class, "jboot.rpc.dubbo.method"); - RPCUtils.setChildConfig(methodConfigs, argumentConfigs, "jboot.rpc.dubbo.method", "argument"); + RPCUtil.setChildConfig(methodConfigs, argumentConfigs, "jboot.rpc.dubbo.method", "argument"); //消费者 配置 Map consumerConfigs = configs(ConsumerConfig.class, "jboot.rpc.dubbo.consumer"); - RPCUtils.setChildConfig(consumerConfigs, methodConfigs, "jboot.rpc.dubbo.consumer", "method"); - RPCUtils.setChildConfig(consumerConfigs, protocolConfigs, "jboot.rpc.dubbo.consumer", "protocol"); - RPCUtils.setChildConfig(consumerConfigs, registryConfigs, "jboot.rpc.dubbo.consumer", "registry"); + RPCUtil.setChildConfig(consumerConfigs, methodConfigs, "jboot.rpc.dubbo.consumer", "method"); + RPCUtil.setChildConfig(consumerConfigs, protocolConfigs, "jboot.rpc.dubbo.consumer", "protocol"); + RPCUtil.setChildConfig(consumerConfigs, registryConfigs, "jboot.rpc.dubbo.consumer", "registry"); if (consumerConfigs != null && !consumerConfigs.isEmpty()) { @@ -159,9 +159,9 @@ class DubboUtil { //服务提供者 配置 Map providerConfigs = configs(ProviderConfig.class, "jboot.rpc.dubbo.provider"); - RPCUtils.setChildConfig(providerConfigs, methodConfigs, "jboot.rpc.dubbo.provider", "method"); - RPCUtils.setChildConfig(providerConfigs, protocolConfigs, "jboot.rpc.dubbo.provider", "protocol"); - RPCUtils.setChildConfig(providerConfigs, registryConfigs, "jboot.rpc.dubbo.provider", "registry"); + RPCUtil.setChildConfig(providerConfigs, methodConfigs, "jboot.rpc.dubbo.provider", "method"); + RPCUtil.setChildConfig(providerConfigs, protocolConfigs, "jboot.rpc.dubbo.provider", "protocol"); + RPCUtil.setChildConfig(providerConfigs, registryConfigs, "jboot.rpc.dubbo.provider", "registry"); if (providerConfigs != null && !providerConfigs.isEmpty()) { providerConfigMap.putAll(providerConfigs); @@ -176,7 +176,7 @@ class DubboUtil { public static ReferenceConfig toReferenceConfig(JbootrpcReferenceConfig rc) { ReferenceConfig referenceConfig = new ReferenceConfig(); - RPCUtils.copyFields(rc, referenceConfig); + RPCUtil.copyFields(rc, referenceConfig); //reference coonsumer if (rc.getConsumer() != null) { @@ -195,7 +195,7 @@ class DubboUtil { public static ServiceConfig toServiceConfig(JbootrpcServiceConfig sc) { ServiceConfig serviceConfig = new ServiceConfig(); - RPCUtils.copyFields(sc, serviceConfig); + RPCUtil.copyFields(sc, serviceConfig); //service provider if (StrUtil.isNotBlank(sc.getProvider())) { diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java index 974f60fa..724c473b 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java @@ -20,7 +20,7 @@ import com.weibo.api.motan.util.MotanFrameworkUtil; import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; -import io.jboot.components.rpc.RPCUtils; +import io.jboot.components.rpc.RPCUtil; import io.jboot.utils.StrUtil; import java.util.ArrayList; @@ -65,9 +65,9 @@ public class MotanUtil { //baseService 配置 Map serviceConfigs = configs(BasicServiceInterfaceConfig.class, "jboot.rpc.motan.service"); - RPCUtils.setChildConfig(serviceConfigs, methodConfigs, "jboot.rpc.motan.service", "method"); - RPCUtils.setChildConfig(serviceConfigs, protocolConfigs, "jboot.rpc.motan.service", "protocol"); - RPCUtils.setChildConfig(serviceConfigs, registryConfigs, "jboot.rpc.motan.service", "registry"); + RPCUtil.setChildConfig(serviceConfigs, methodConfigs, "jboot.rpc.motan.service", "method"); + RPCUtil.setChildConfig(serviceConfigs, protocolConfigs, "jboot.rpc.motan.service", "protocol"); + RPCUtil.setChildConfig(serviceConfigs, registryConfigs, "jboot.rpc.motan.service", "registry"); if (serviceConfigs != null && !serviceConfigs.isEmpty()) { @@ -76,9 +76,9 @@ public class MotanUtil { //baseReferer 配置 Map refererConfigs = configs(BasicRefererInterfaceConfig.class, "jboot.rpc.motan.referer"); - RPCUtils.setChildConfig(refererConfigs, methodConfigs, "jboot.rpc.motan.referer", "method"); - RPCUtils.setChildConfig(refererConfigs, protocolConfigs, "jboot.rpc.motan.referer", "protocol"); - RPCUtils.setChildConfig(refererConfigs, registryConfigs, "jboot.rpc.motan.referer", "registry"); + RPCUtil.setChildConfig(refererConfigs, methodConfigs, "jboot.rpc.motan.referer", "method"); + RPCUtil.setChildConfig(refererConfigs, protocolConfigs, "jboot.rpc.motan.referer", "protocol"); + RPCUtil.setChildConfig(refererConfigs, registryConfigs, "jboot.rpc.motan.referer", "registry"); if (refererConfigs != null && !refererConfigs.isEmpty()) { baseRefererConfigMap.putAll(refererConfigs); @@ -90,7 +90,7 @@ public class MotanUtil { public static RefererConfig toRefererConfig(JbootrpcReferenceConfig rc) { RefererConfig refererConfig = new RefererConfig(); - RPCUtils.copyFields(rc, refererConfig); + RPCUtil.copyFields(rc, refererConfig); //referer protocol @@ -135,7 +135,7 @@ public class MotanUtil { public static ServiceConfig toServiceConfig(JbootrpcServiceConfig sc) { ServiceConfig serviceConfig = new ServiceConfig(); - RPCUtils.copyFields(sc, serviceConfig); + RPCUtil.copyFields(sc, serviceConfig); //service protocol -- Gitee From 249b22a8e67ac69b2ecee515fce7bae6751b8013 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 14 Apr 2020 10:35:51 +0800 Subject: [PATCH 0248/1965] v3.1.8 --- .../components/gateway/GatewayHttpProxy.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index eabd9f87..33d8f128 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -33,7 +33,10 @@ import java.util.Map; import java.util.Set; import java.util.zip.GZIPInputStream; - +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/24 + */ public class GatewayHttpProxy { private static final Log LOG = Log.getLog(GatewayHttpProxy.class); @@ -77,7 +80,7 @@ public class GatewayHttpProxy { conn = getConnection(url); /** - * 设置 http 请求头 + * 配置 HttpURLConnection 的 http 请求头 */ configConnection(conn, req); @@ -95,14 +98,14 @@ public class GatewayHttpProxy { /** - * 配置响应的 HTTP 头 + * 配置 HttpServletResponse 的 http 响应头 */ configResponse(resp, conn); /** - * 复制目标相应流到 Response + * 复制链接的 inputStream 流到 Response */ - copyStreamToResponse(conn, resp); + copyConnStreamToResponse(conn, resp); } finally { if (conn != null) { @@ -123,14 +126,13 @@ public class GatewayHttpProxy { while ((len = inStream.read(buffer)) != -1) { outStream.write(buffer, 0, len); } - } finally { quetlyClose(outStream, inStream); } } - private void copyStreamToResponse(HttpURLConnection conn, HttpServletResponse resp) throws IOException { + private void copyConnStreamToResponse(HttpURLConnection conn, HttpServletResponse resp) throws IOException { InputStream inStream = null; InputStreamReader reader = null; try { -- Gitee From 06aa313fd65163fc1541b5ac8435edadfa998b10 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 15 Apr 2020 11:26:09 +0800 Subject: [PATCH 0249/1965] v3.1.8 --- .../java/io/jboot/utils/ClassScanner.java | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 8032452d..b24e1f81 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -33,18 +33,21 @@ public class ClassScanner { private static final Set appClassesCache = new HashSet<>(); - public static final Set includeJars = new HashSet<>(); + public static final Set scanJars = new HashSet<>(); public static final Set excludeJars = new HashSet<>(); + + public static final Set scanClasses = new HashSet<>(); public static final Set excludeClasses = new HashSet<>(); public static void addScanJarPrefix(String prefix) { - includeJars.add(prefix.trim()); + scanJars.add(prefix.trim()); } static { -// addScanJarPrefix("jboot"); + scanJars.add("jboot"); } + public static void addUnscanJarPrefix(String prefix) { excludeJars.add(prefix.trim()); } @@ -300,6 +303,15 @@ public class ClassScanner { excludeClasses.add("io.jboot.Jboot"); } + + public static void addScanClassPrefix(String prefix) { + scanClasses.add(prefix.trim()); + } + + static { + scanClasses.add("io.jboot.support.shiro.directives"); + } + static { String scanJarPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.scanJarPrefix"); if (scanJarPrefix != null) { @@ -330,6 +342,17 @@ public class ClassScanner { } } } + + String scanClassPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.scanClassPrefix"); + if (scanClassPrefix != null) { + String[] prefixes = scanClassPrefix.split(","); + for (String prefix : prefixes) { + if (prefix != null && prefix.trim().length() > 0) { + addScanClassPrefix(prefix.trim()); + } + } + } + } public static List> scanSubClass(Class pclazz) { @@ -523,8 +546,13 @@ public class ClassScanner { //用于在进行 fatjar 打包时,提高性能 private static boolean isNotExcludeClass(String clazzName) { - for (String Prefix : excludeClasses) { - if (clazzName.startsWith(Prefix)) { + for (String prefix : scanClasses) { + if (clazzName.startsWith(prefix)) { + return true; + } + } + for (String prefix : excludeClasses) { + if (clazzName.startsWith(prefix)) { return false; } } @@ -602,7 +630,7 @@ public class ClassScanner { String jarName = new File(path).getName().toLowerCase(); - for (String include : includeJars) { + for (String include : scanJars) { if (jarName.startsWith(include)) { return true; } -- Gitee From 5f53ce9469ce499a9f5b2b255a8346a1f573a5db Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 15 Apr 2020 12:38:58 +0800 Subject: [PATCH 0250/1965] rename JbootRpcApplication to JbootSimpleApplication --- doc/docs/build.md | 2 +- .../java/io/jboot/app/ApplicationUtil.java | 72 ++++++++++++++++++ .../java/io/jboot/app/JbootApplication.java | 75 ++++--------------- .../io/jboot/app/JbootApplicationConfig.java | 2 +- ...ation.java => JbootSimpleApplication.java} | 9 ++- .../io/jboot/test/rpc/dubbo/DubboServer.java | 4 +- .../rpc/dubbonacos/DubboServer1NacosDemo.java | 4 +- .../rpc/dubbonacos/DubboServer2NacosDemo.java | 4 +- .../DubboServer1ZookeeperDemo.java | 4 +- .../DubboServer2ZookeeperDemo.java | 4 +- .../io/jboot/test/rpc/motan/MotanServer.java | 4 +- .../MotanServer1ZookeeperDemo.java | 4 +- .../MotanServer2ZookeeperDemo.java | 4 +- 13 files changed, 113 insertions(+), 79 deletions(-) create mode 100644 src/main/java/io/jboot/app/ApplicationUtil.java rename src/main/java/io/jboot/app/{JbootRpcApplication.java => JbootSimpleApplication.java} (93%) diff --git a/doc/docs/build.md b/doc/docs/build.md index afd0cecb..f3f8705f 100644 --- a/doc/docs/build.md +++ b/doc/docs/build.md @@ -292,7 +292,7 @@ fatjar 打包第一步,在 pom.xml 添加如下配置 io.jboot.app.JbootApplication - + diff --git a/src/main/java/io/jboot/app/ApplicationUtil.java b/src/main/java/io/jboot/app/ApplicationUtil.java new file mode 100644 index 00000000..a269cb39 --- /dev/null +++ b/src/main/java/io/jboot/app/ApplicationUtil.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.app; + +import io.jboot.app.config.JbootConfigManager; + +import java.net.URISyntaxException; +import java.net.URL; + +class ApplicationUtil { + + static JbootApplicationConfig getAppConfig(String[] args) { + JbootConfigManager.me().parseArgs(args); + return getConfig(JbootApplicationConfig.class); + } + + + static void printBannerInfo(JbootApplicationConfig appConfig) { + if (appConfig.isBannerEnable()) { + System.out.println(); + System.out.println(Banner.getText(appConfig.getBannerFile())); + System.out.println(); + } + } + + static void printApplicationInfo(JbootApplicationConfig appConfig) { + System.out.println(appConfig.toString()); + } + + static void printClassPath() { + try { + URL resourceURL = ApplicationUtil.class.getResource("/"); + if (resourceURL != null) { + System.out.println("Classpath : " + resourceURL.toURI().getPath()); + } else { + System.out.println("Classpath : application in one jar."); + } + } catch (URISyntaxException e) { + e.printStackTrace(); + } + } + + + static T getConfig(Class clazz) { + return JbootConfigManager.me().get(clazz); + } + + static String getConfigValue(String key) { + return JbootConfigManager.me().getConfigValue(key); + } + + + + static boolean isDevMode() { + return JbootConfigManager.me().isDevMode(); + } + + +} diff --git a/src/main/java/io/jboot/app/JbootApplication.java b/src/main/java/io/jboot/app/JbootApplication.java index 3f33ff62..b7b4ebea 100644 --- a/src/main/java/io/jboot/app/JbootApplication.java +++ b/src/main/java/io/jboot/app/JbootApplication.java @@ -23,8 +23,6 @@ import io.jboot.app.undertow.JbootUndertowConfig; import io.jboot.app.undertow.JbootUndertowServer; import javax.servlet.DispatcherType; -import java.net.URISyntaxException; -import java.net.URL; public class JbootApplication { @@ -42,11 +40,15 @@ public class JbootApplication { public static void start(UndertowServer server) { server.start(); - if (isDevMode()) { + if (ApplicationUtil.isDevMode()) { new JbootResourceLoader().start(); } } + public static void setBootArg(String key, Object value) { + JbootConfigManager.me().setBootArg(key, value); + } + /** * 创建 Undertow 服务器,public 用于可以给第三方创建创建着急的 Server * @@ -54,7 +56,7 @@ public class JbootApplication { * @return 返回 UndertowServer */ public static UndertowServer createServer(String[] args) { - JbootApplicationConfig appConfig = getAppConfig(args); + JbootApplicationConfig appConfig = ApplicationUtil.getAppConfig(args); return createServer(appConfig, createUndertowConfig(appConfig), null); } @@ -68,7 +70,7 @@ public class JbootApplication { * @return */ public static UndertowServer createServer(String[] args, JbootWebBuilderConfiger configer) { - JbootApplicationConfig appConfig = getAppConfig(args); + JbootApplicationConfig appConfig = ApplicationUtil.getAppConfig(args); return createServer(appConfig, createUndertowConfig(appConfig), configer); } @@ -77,13 +79,13 @@ public class JbootApplication { , UndertowConfig undertowConfig , JbootWebBuilderConfiger configer) { - printBannerInfo(appConfig); - printApplicationInfo(appConfig); - printClassPath(); + ApplicationUtil.printBannerInfo(appConfig); + ApplicationUtil.printApplicationInfo(appConfig); + ApplicationUtil.printClassPath(); return new JbootUndertowServer(undertowConfig) - .setDevMode(isDevMode()) + .setDevMode(ApplicationUtil.isDevMode()) .configWeb(webBuilder -> { tryAddMetricsSupport(webBuilder); tryAddShiroSupport(webBuilder); @@ -97,10 +99,6 @@ public class JbootApplication { - public static JbootApplicationConfig getAppConfig(String[] args) { - JbootConfigManager.me().parseArgs(args); - return getConfig(JbootApplicationConfig.class); - } public static UndertowConfig createUndertowConfig(JbootApplicationConfig appConfig) { UndertowConfig undertowConfig = new JbootUndertowConfig(appConfig.getJfinalConfig()); @@ -111,8 +109,8 @@ public class JbootApplication { private static void tryAddMetricsSupport(WebBuilder webBuilder) { - String url = getConfigValue("jboot.metric.url"); - String reporter = getConfigValue("jboot.metric.reporter"); + String url = ApplicationUtil.getConfigValue("jboot.metric.url"); + String reporter = ApplicationUtil.getConfigValue("jboot.metric.reporter"); if (url != null && reporter != null) { webBuilder.addServlet("MetricsAdminServlet", "com.codahale.metrics.servlets.AdminServlet") .addServletMapping("MetricsAdminServlet", url.endsWith("/*") ? url : url + "/*"); @@ -123,9 +121,9 @@ public class JbootApplication { private static void tryAddShiroSupport(WebBuilder webBuilder) { - String iniConfig = getConfigValue("jboot.shiro.ini"); + String iniConfig = ApplicationUtil.getConfigValue("jboot.shiro.ini"); if (iniConfig != null) { - String urlMapping = getConfigValue("jboot.shiro.urlMapping"); + String urlMapping = ApplicationUtil.getConfigValue("jboot.shiro.urlMapping"); if (urlMapping == null) { urlMapping = "/*"; } @@ -137,7 +135,7 @@ public class JbootApplication { } private static void tryAddWebSocketSupport(WebBuilder webBuilder) { - String websocketEndpoint = getConfigValue("jboot.web.webSocketEndpoint"); + String websocketEndpoint = ApplicationUtil.getConfigValue("jboot.web.webSocketEndpoint"); if (websocketEndpoint != null && websocketEndpoint.trim().length() > 0) { String[] classStrings = websocketEndpoint.split(","); for (String c : classStrings) { @@ -147,48 +145,7 @@ public class JbootApplication { } - private static void printBannerInfo(JbootApplicationConfig appConfig) { - if (appConfig.isBannerEnable()) { - System.out.println(); - System.out.println(Banner.getText(appConfig.getBannerFile())); - System.out.println(); - } - } - - private static void printApplicationInfo(JbootApplicationConfig appConfig) { - System.out.println(appConfig.toString()); - } - - private static void printClassPath() { - try { - URL resourceURL = JbootApplication.class.getResource("/"); - if (resourceURL != null) { - System.out.println("Classpath : " + resourceURL.toURI().getPath()); - } else { - System.out.println("Classpath : application in one jar."); - } - } catch (URISyntaxException e) { - e.printStackTrace(); - } - } - - - private static T getConfig(Class clazz) { - return JbootConfigManager.me().get(clazz); - } - - private static String getConfigValue(String key) { - return JbootConfigManager.me().getConfigValue(key); - } - - - public static void setBootArg(String key, Object value) { - JbootConfigManager.me().setBootArg(key, value); - } - private static boolean isDevMode() { - return JbootConfigManager.me().isDevMode(); - } } diff --git a/src/main/java/io/jboot/app/JbootApplicationConfig.java b/src/main/java/io/jboot/app/JbootApplicationConfig.java index 774d7307..c28c11b5 100644 --- a/src/main/java/io/jboot/app/JbootApplicationConfig.java +++ b/src/main/java/io/jboot/app/JbootApplicationConfig.java @@ -75,7 +75,7 @@ public class JbootApplicationConfig { " name='" + name + '\'' + ", mode='" + mode + '\'' + ", version='" + version + '\'' + - ", jfinalConfig='" + jfinalConfig + '\'' + + ", config='" + jfinalConfig + '\'' + " }"; } } diff --git a/src/main/java/io/jboot/app/JbootRpcApplication.java b/src/main/java/io/jboot/app/JbootSimpleApplication.java similarity index 93% rename from src/main/java/io/jboot/app/JbootRpcApplication.java rename to src/main/java/io/jboot/app/JbootSimpleApplication.java index 710d3b7c..def02497 100644 --- a/src/main/java/io/jboot/app/JbootRpcApplication.java +++ b/src/main/java/io/jboot/app/JbootSimpleApplication.java @@ -29,7 +29,7 @@ import java.util.concurrent.locks.ReentrantLock; * @author michael yang (fuhai999@gmail.com) * @Date: 2020/3/24 */ -public class JbootRpcApplication { +public class JbootSimpleApplication { private static final ReentrantLock LOCK = new ReentrantLock(); private static final Condition STOP = LOCK.newCondition(); @@ -43,7 +43,12 @@ public class JbootRpcApplication { } public static void run(String[] args) { - JbootConfigManager.me().parseArgs(args); + + JbootApplicationConfig appConfig = ApplicationUtil.getAppConfig(args); + ApplicationUtil.printBannerInfo(appConfig); + ApplicationUtil.printApplicationInfo(appConfig); + ApplicationUtil.printClassPath(); + JbootCoreConfig coreConfig = new JbootCoreConfig(); new RPCServer(coreConfig).start(); } diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java index 2487fc6d..d7f55b38 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java @@ -17,7 +17,7 @@ package io.jboot.test.rpc.dubbo; import io.jboot.app.JbootApplication; -import io.jboot.app.JbootRpcApplication; +import io.jboot.app.JbootSimpleApplication; public class DubboServer { @@ -35,7 +35,7 @@ public class DubboServer { JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "28080"); - JbootRpcApplication.run(args); + JbootSimpleApplication.run(args); System.out.println("DubboServer started..."); diff --git a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java index a37fd089..4d313d7b 100644 --- a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer1NacosDemo.java @@ -2,7 +2,7 @@ package io.jboot.test.rpc.dubbonacos; import io.jboot.app.JbootApplication; -import io.jboot.app.JbootRpcApplication; +import io.jboot.app.JbootSimpleApplication; public class DubboServer1NacosDemo { @@ -28,7 +28,7 @@ public class DubboServer1NacosDemo { - JbootRpcApplication.run(args); + JbootSimpleApplication.run(args); System.out.println("DubboServer1NacosDemo started..."); diff --git a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java index e4bf67f6..9e3b2a7c 100644 --- a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboServer2NacosDemo.java @@ -2,7 +2,7 @@ package io.jboot.test.rpc.dubbonacos; import io.jboot.app.JbootApplication; -import io.jboot.app.JbootRpcApplication; +import io.jboot.app.JbootSimpleApplication; public class DubboServer2NacosDemo { @@ -26,7 +26,7 @@ public class DubboServer2NacosDemo { //dubbo 的通信协议配置,如果port配置为-1,则会分配一个没有被占用的端口。 JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "28081"); - JbootRpcApplication.run(args); + JbootSimpleApplication.run(args); diff --git a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer1ZookeeperDemo.java b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer1ZookeeperDemo.java index ec8145e6..b03d0318 100644 --- a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer1ZookeeperDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer1ZookeeperDemo.java @@ -2,7 +2,7 @@ package io.jboot.test.rpc.dubbozookeeper; import io.jboot.app.JbootApplication; -import io.jboot.app.JbootRpcApplication; +import io.jboot.app.JbootSimpleApplication; public class DubboServer1ZookeeperDemo { @@ -27,7 +27,7 @@ public class DubboServer1ZookeeperDemo { - JbootRpcApplication.run(args); + JbootSimpleApplication.run(args); System.out.println("DubboServer1ZookeeperDemo started..."); diff --git a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer2ZookeeperDemo.java b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer2ZookeeperDemo.java index e9b59b60..62a80645 100644 --- a/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer2ZookeeperDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbozookeeper/DubboServer2ZookeeperDemo.java @@ -2,7 +2,7 @@ package io.jboot.test.rpc.dubbozookeeper; import io.jboot.app.JbootApplication; -import io.jboot.app.JbootRpcApplication; +import io.jboot.app.JbootSimpleApplication; public class DubboServer2ZookeeperDemo { @@ -27,7 +27,7 @@ public class DubboServer2ZookeeperDemo { //dubbo 的通信协议配置,如果port配置为-1,则会分配一个没有被占用的端口。 JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port", "28081"); - JbootRpcApplication.run(args); + JbootSimpleApplication.run(args); diff --git a/src/test/java/io/jboot/test/rpc/motan/MotanServer.java b/src/test/java/io/jboot/test/rpc/motan/MotanServer.java index 7e324864..34880dea 100644 --- a/src/test/java/io/jboot/test/rpc/motan/MotanServer.java +++ b/src/test/java/io/jboot/test/rpc/motan/MotanServer.java @@ -17,7 +17,7 @@ package io.jboot.test.rpc.motan; import io.jboot.app.JbootApplication; -import io.jboot.app.JbootRpcApplication; +import io.jboot.app.JbootSimpleApplication; public class MotanServer { @@ -36,7 +36,7 @@ public class MotanServer { - JbootRpcApplication.run(args); + JbootSimpleApplication.run(args); System.out.println("MotanServer started..."); diff --git a/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer1ZookeeperDemo.java b/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer1ZookeeperDemo.java index 595348f8..0d875123 100644 --- a/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer1ZookeeperDemo.java +++ b/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer1ZookeeperDemo.java @@ -2,7 +2,7 @@ package io.jboot.test.rpc.motanzookeeper; import io.jboot.app.JbootApplication; -import io.jboot.app.JbootRpcApplication; +import io.jboot.app.JbootSimpleApplication; public class MotanServer1ZookeeperDemo { @@ -25,7 +25,7 @@ public class MotanServer1ZookeeperDemo { - JbootRpcApplication.run(args); + JbootSimpleApplication.run(args); System.out.println("MotanServer1ZookeeperDemo started..."); diff --git a/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer2ZookeeperDemo.java b/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer2ZookeeperDemo.java index b18ea36b..42b462fc 100644 --- a/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer2ZookeeperDemo.java +++ b/src/test/java/io/jboot/test/rpc/motanzookeeper/MotanServer2ZookeeperDemo.java @@ -2,7 +2,7 @@ package io.jboot.test.rpc.motanzookeeper; import io.jboot.app.JbootApplication; -import io.jboot.app.JbootRpcApplication; +import io.jboot.app.JbootSimpleApplication; public class MotanServer2ZookeeperDemo { @@ -25,7 +25,7 @@ public class MotanServer2ZookeeperDemo { //export JbootApplication.setBootArg("jboot.rpc.motan.defaultExport", "default:28081"); - JbootRpcApplication.run(args); + JbootSimpleApplication.run(args); -- Gitee From fa0a2d43a2b4bfe3b490006436e4fcd06b2efd87 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 15 Apr 2020 16:15:14 +0800 Subject: [PATCH 0251/1965] add registerConfig() method in JbootGatewayManager for register a dynamic config --- .../components/gateway/GatewayInvocation.java | 2 +- .../gateway/JbootGatewayConfig.java | 6 ++-- .../gateway/JbootGatewayManager.java | 30 ++++++++++++++----- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java index 8589527f..29bdfeb6 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java +++ b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java @@ -36,7 +36,7 @@ public class GatewayInvocation { this.config = config; this.request = request; this.response = response; - this.inters = config.getInters(); + this.inters = config.buildInterceptors(); } public void invoke() { diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index 6b89656c..30ce9da8 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -235,7 +235,7 @@ public class JbootGatewayConfig implements Serializable { private GatewayInterceptor[] inters; - public GatewayInterceptor[] getInters() { + public GatewayInterceptor[] buildInterceptors() { if (interceptors == null || interceptors.length == 0) { return null; } @@ -267,14 +267,14 @@ public class JbootGatewayConfig implements Serializable { if (configOk == null) { configOk = StrUtil.isNotBlank(uri); if (configOk) { - ensureUriPatterCorrect(); + ensureUriConfigCorrect(); } } } return configOk; } - private void ensureUriPatterCorrect() { + private void ensureUriConfigCorrect() { if (!uri.toLowerCase().startsWith("http://") && !uri.toLowerCase().startsWith("https://")) { throw new JbootIllegalConfigException("gateway uri must start with http:// or https://"); diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java index 04dc4777..3d9fe694 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -19,10 +19,9 @@ import io.jboot.app.config.JbootConfigUtil; import io.jboot.utils.StrUtil; import javax.servlet.http.HttpServletRequest; -import java.util.HashSet; import java.util.Iterator; import java.util.Map; -import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * @author michael yang (fuhai999@gmail.com) @@ -36,28 +35,45 @@ public class JbootGatewayManager { return me; } - private Set configs; + private Map configMap; public void init() { Map configMap = JbootConfigUtil.getConfigModels(JbootGatewayConfig.class, "jboot.gateway"); if (configMap != null && !configMap.isEmpty()) { - configs = new HashSet<>(); + for (Map.Entry e : configMap.entrySet()) { JbootGatewayConfig config = e.getValue(); if (config.isConfigOk() && config.isEnable()) { if (StrUtil.isNotBlank(config.getName())) { config.setName(e.getKey()); } - configs.add(config); + registerConfig(config); } } } } + public synchronized void registerConfig(JbootGatewayConfig config) { + if (configMap == null) { + configMap = new ConcurrentHashMap<>(); + } + configMap.put(config.getName(), config); + } + + + public JbootGatewayConfig getConfig(String name) { + return configMap == null ? null : configMap.get(name); + } + + + public Map getConfigMap() { + return configMap; + } + public JbootGatewayConfig matchingConfig(HttpServletRequest req) { - if (configs != null && !configs.isEmpty()) { - Iterator iterator = configs.iterator(); + if (configMap != null && !configMap.isEmpty()) { + Iterator iterator = configMap.values().iterator(); while (iterator.hasNext()) { JbootGatewayConfig config = iterator.next(); if (config.matches(req)) { -- Gitee From 07bd2ad728f7ea74e477c1ad7d7c9c0ae40c02b2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Apr 2020 11:07:43 +0800 Subject: [PATCH 0252/1965] feature: add json conent config in gateway if Sentinel is blocked --- .../gateway/GatewaySentinelProcesser.java | 19 ++++++++++++++++--- .../gateway/JbootGatewayConfig.java | 10 ++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java b/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java index 5aefb152..0b7f373a 100644 --- a/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java +++ b/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java @@ -18,6 +18,7 @@ package io.jboot.components.gateway; import com.alibaba.csp.sentinel.*; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.util.StringUtil; +import com.jfinal.kit.JsonKit; import com.jfinal.kit.LogKit; import io.jboot.utils.StrUtil; @@ -25,6 +26,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; +import java.util.Map; /** * @author michael yang (fuhai999@gmail.com) @@ -59,11 +61,13 @@ public class GatewaySentinelProcesser { } try { - if (StringUtil.isBlank(config.getSentinelBlockPage())) { - writeDefaultBlockedPage(resp); - } else { + if (StringUtil.isNotBlank(config.getSentinelBlockPage())) { String redirectUrl = config.getSentinelBlockPage() + "?http_referer=" + url.toString(); resp.sendRedirect(redirectUrl); + } else if (config.getSentinelBlockJsonMap() != null && !config.getSentinelBlockJsonMap().isEmpty()) { + writeDefaultBlockedJson(resp, config.getSentinelBlockJsonMap()); + } else { + writeDefaultBlockedPage(resp); } } catch (IOException ex) { LogKit.error(ex.toString(), ex); @@ -71,6 +75,15 @@ public class GatewaySentinelProcesser { } + protected static final String contentType = "application/json; charset=utf-8"; + + private static void writeDefaultBlockedJson(HttpServletResponse resp, Map map) throws IOException { + resp.setStatus(200); + resp.setContentType(contentType); + PrintWriter out = resp.getWriter(); + out.print(JsonKit.toJson(map)); + } + private static void writeDefaultBlockedPage(HttpServletResponse resp) throws IOException { resp.setStatus(200); diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index 30ce9da8..2721ef84 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -41,6 +41,8 @@ public class JbootGatewayConfig implements Serializable { private boolean sentinelEnable = false; // sentinel 被限流后跳转地址 private String sentinelBlockPage; + // sentinel 被渲染的json内容,如果配置 sentinelBlockPage,则 sentinelBlockJsonMap 配置无效 + private Map sentinelBlockJsonMap; // 设置 http 代理的参数 private int proxyReadTimeout = 10000; //10s @@ -112,6 +114,14 @@ public class JbootGatewayConfig implements Serializable { this.sentinelBlockPage = sentinelBlockPage; } + public Map getSentinelBlockJsonMap() { + return sentinelBlockJsonMap; + } + + public void setSentinelBlockJsonMap(Map sentinelBlockJsonMap) { + this.sentinelBlockJsonMap = sentinelBlockJsonMap; + } + public int getProxyReadTimeout() { return proxyReadTimeout; } -- Gitee From a01b685b19ff5652d755781094d7ac5c98881bb1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Apr 2020 11:36:36 +0800 Subject: [PATCH 0253/1965] optimize DubboUtil.java --- .../jboot/components/rpc/dubbo/DubboUtil.java | 43 +++---------------- 1 file changed, 6 insertions(+), 37 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index 4d157f7a..0a6ebf64 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -85,21 +85,13 @@ class DubboUtil { //元数据 配置 Map metadataReportConfigs = configs(MetadataReportConfig.class, "jboot.rpc.dubbo.metadata-report"); if (metadataReportConfigs != null && !metadataReportConfigs.isEmpty()) { - if (metadataReportConfigs.size() == 1) { - dubboBootstrap.metadataReport(getDefault(metadataReportConfigs)); - } else { - dubboBootstrap.metadataReports(toList(metadataReportConfigs)); - } + dubboBootstrap.metadataReports(toList(metadataReportConfigs)); } //配置中心配置 Map configCenterConfigs = configs(ConfigCenterConfig.class, "jboot.rpc.dubbo.config-center"); if (configCenterConfigs != null && !configCenterConfigs.isEmpty()) { - if (configCenterConfigs.size() == 1) { - dubboBootstrap.configCenter(getDefault(configCenterConfigs)); - } else { - dubboBootstrap.configCenters(toList(configCenterConfigs)); - } + dubboBootstrap.configCenters(toList(configCenterConfigs)); } @@ -107,22 +99,14 @@ class DubboUtil { Map protocolConfigs = configs(ProtocolConfig.class, "jboot.rpc.dubbo.protocol"); if (protocolConfigs != null && !protocolConfigs.isEmpty()) { protocolConfigMap.putAll(protocolConfigs); - if (protocolConfigs.size() == 1) { - dubboBootstrap.protocol(getDefault(protocolConfigs)); - } else { - dubboBootstrap.protocols(toList(protocolConfigs)); - } + dubboBootstrap.protocols(toList(protocolConfigs)); } //服务注册中心 配置 Map registryConfigs = configs(RegistryConfig.class, "jboot.rpc.dubbo.registry"); if (registryConfigs != null && !registryConfigs.isEmpty()) { registryConfigMap.putAll(registryConfigs); - if (registryConfigs.size() == 1) { - dubboBootstrap.registry(getDefault(registryConfigs)); - } else { - dubboBootstrap.registries(toList(registryConfigs)); - } + dubboBootstrap.registries(toList(registryConfigs)); } //没有配置注册中心,一般只用于希望此服务网提供直连的方式给客户端使用 else { @@ -150,11 +134,7 @@ class DubboUtil { if (consumerConfigs != null && !consumerConfigs.isEmpty()) { consumerConfigMap.putAll(consumerConfigs); - if (consumerConfigs.size() == 1) { - dubboBootstrap.consumer(getDefault(consumerConfigs)); - } else { - dubboBootstrap.consumers(toList(consumerConfigs)); - } + dubboBootstrap.consumers(toList(consumerConfigs)); } //服务提供者 配置 @@ -165,11 +145,7 @@ class DubboUtil { if (providerConfigs != null && !providerConfigs.isEmpty()) { providerConfigMap.putAll(providerConfigs); - if (providerConfigs.size() == 1) { - dubboBootstrap.provider(getDefault(providerConfigs)); - } else { - dubboBootstrap.providers(toList(providerConfigs)); - } + dubboBootstrap.providers(toList(providerConfigs)); } } @@ -234,13 +210,6 @@ class DubboUtil { return JbootConfigUtil.getConfigModels(clazz, prefix); } - private static T getDefault(Map map) { - AbstractConfig config = (AbstractConfig) map.values().iterator().next(); - if (config != null){ - config.setId(map.keySet().iterator().next()); - } - return (T) config; - } private static List toList(Map map) { List list = new ArrayList<>(map.size()); -- Gitee From 51b62f18d30775575138daf6b54444cee0a8689b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Apr 2020 11:52:40 +0800 Subject: [PATCH 0254/1965] optimize gateway/rpc/config docs --- doc/docs/config.md | 64 ++---------------------- doc/docs/gateway.md | 2 + doc/docs/rpc.md | 115 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 60 deletions(-) diff --git a/doc/docs/config.md b/doc/docs/config.md index e0b4a380..e490e744 100644 --- a/doc/docs/config.md +++ b/doc/docs/config.md @@ -306,6 +306,10 @@ public MyConfigDecriptor implements JbootConfigDecryptor { 修改为大写,符号点(.)修改为下划线(_)。 +## RPC 配置 + +参考 [这里](./rpc.md) + ## Jboot 其他配置参考 ``` @@ -384,66 +388,6 @@ jboot.datasource.activeRecordPluginClass jboot.datasource.needAddMapping = true //是否需要添加到映射,当不添加映射的时候,只能通过 model.use("xxx").save()这种方式去调用该数据源 -jboot.rpc.type -jboot.rpc.callMode -jboot.rpc.requestTimeOut -jboot.rpc.registryType -jboot.rpc.registryAddress -jboot.rpc.registryName -jboot.rpc.registryUserName -jboot.rpc.registryPassword -jboot.rpc.registryFile -jboot.rpc.registryCheck -jboot.rpc.consumerCheck -jboot.rpc.providerCheck -jboot.rpc.directUrl -jboot.rpc.host -jboot.rpc.defaultPort -jboot.rpc.defaultGroup -jboot.rpc.defaultVersion -jboot.rpc.proxy -jboot.rpc.filter -jboot.rpc.serialization -jboot.rpc.retries -jboot.rpc.autoExportEnable - -jboot.rpc.dubbo.protocolName -jboot.rpc.dubbo.protocolServer -jboot.rpc.dubbo.protocolContextPath -jboot.rpc.dubbo.protocolTransporter -jboot.rpc.dubbo.protocolThreads -jboot.rpc.dubbo.protocolHost -jboot.rpc.dubbo.protocolPort -jboot.rpc.dubbo.protocolContextpath -jboot.rpc.dubbo.protocolThreadpool -jboot.rpc.dubbo.protocolIothreads -jboot.rpc.dubbo.protocolQueues -jboot.rpc.dubbo.protocolAccepts -jboot.rpc.dubbo.protocolCodec -jboot.rpc.dubbo.protocolSerialization -jboot.rpc.dubbo.protocolCharset -jboot.rpc.dubbo.protocolPayload -jboot.rpc.dubbo.protocolBuffer -jboot.rpc.dubbo.protocolHeartbeat -jboot.rpc.dubbo.protocolAccesslog -jboot.rpc.dubbo.protocolExchanger -jboot.rpc.dubbo.protocolDispatcher -jboot.rpc.dubbo.protocolNetworker -jboot.rpc.dubbo.protocolClient -jboot.rpc.dubbo.protocolTelnet -jboot.rpc.dubbo.protocolPrompt -jboot.rpc.dubbo.protocolStatus -jboot.rpc.dubbo.protocolRegister -jboot.rpc.dubbo.protocolKeepAlive -jboot.rpc.dubbo.protocolOptimizer -jboot.rpc.dubbo.protocolExtension -jboot.rpc.dubbo.protocolIsDefault -jboot.rpc.dubbo.qosEnable -jboot.rpc.dubbo.qosPort -jboot.rpc.dubbo.qosAcceptForeignIp - -jboot.rpc.zbus.serviceName -jboot.rpc.zbus.serviceToken jboot.mq.type jboot.mq.channel diff --git a/doc/docs/gateway.md b/doc/docs/gateway.md index 331836e2..8c31a76c 100644 --- a/doc/docs/gateway.md +++ b/doc/docs/gateway.md @@ -23,6 +23,7 @@ jboot.gateway.enable = true jboot.gateway.sentinelEnable = false jboot.gateway.sentinelBlockPage = /block +jboot.gateway.sentinelBlockJsonMap = message:xxxx;code:200 jboot.gateway.proxyReadTimeout = 10000 jboot.gateway.proxyConnectTimeout = 5000 @@ -49,6 +50,7 @@ jboot.gateway.queryContains = aa,bb - enable 是否启用该路由 - sentinelEnable 是否启用 sentinel 限流功能 - sentinelBlockPage 若该路由被限流后,网页自动跳转到哪个网址 +- sentinelBlockJsonMap 若该路由被限流后,自动渲染的 jsonMap,若 sentinelBlockPage 已经配置,则 sentinelBlockJsonMap 配置无效 - proxyReadTimeout 发生路由后,默认的请求超时时间,默认为 10 秒 - proxyConnectTimeout 发生路由后,默认的连接超时时间,默认为 5 秒 - proxyContentType 发生路由后,返回给浏览器的 http-content-type,默认为:text/html;charset=utf-8 diff --git a/doc/docs/rpc.md b/doc/docs/rpc.md index 677456d1..af1e4c5c 100644 --- a/doc/docs/rpc.md +++ b/doc/docs/rpc.md @@ -333,3 +333,118 @@ http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-consumer.html jboot.rpc.dubbo.consumer.timeout = 127.0.0.1 ``` +#### SSL + +对应的配置类: org.apache.dubbo.config.SslConfig + + +对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.ssl` + +例如: + +``` +jboot.rpc.dubbo.ssl.serverKeyCertChainPath = xxx +``` + + +#### monitor + +http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-monitor.html + + +对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.monitor` + +例如: + +``` +jboot.rpc.dubbo.monitor.protocol = xxx +``` + + +#### metrics + + +对应的配置类: org.apache.dubbo.config.MetricsConfig + + +对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.metrics` + +例如: + +``` +jboot.rpc.dubbo.metrics.protocol = xxx +``` + + +#### module + + +http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-module.html + + +对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.module` + +例如: + +``` +jboot.rpc.dubbo.module.name = xxx +``` + + +#### MetadataReport + + +对应的配置类: org.apache.dubbo.config.MetadataReportConfig + + +对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.metadata-report` + +例如: + +``` +jboot.rpc.dubbo.metadata-report.group = xxx +``` + + +#### ConfigCenter + + +http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-config-center.html + + +对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.config-center` + +例如: + +``` +jboot.rpc.dubbo.config-center.group = xxx +``` + +#### method + + +http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-method.html + + +对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.method` + +例如: + +``` +jboot.rpc.dubbo.method.name = xxx +``` + +#### argument + + +http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-argument.html + + +对应的 jboot 的配置前缀为: `jboot.rpc.dubbo.argument` + +例如: + +``` +jboot.rpc.dubbo.argument.name = xxx +``` + -- Gitee From 9390b992faf767f4206fa9ddb869dfd6d3b4ff98 Mon Sep 17 00:00:00 2001 From: yangyao Date: Thu, 16 Apr 2020 13:59:19 +0800 Subject: [PATCH 0255/1965] =?UTF-8?q?=E4=BD=BF=E7=94=A8JsonKit=E6=9D=A5?= =?UTF-8?q?=E6=9B=BF=E6=8D=A2JSON=E7=9A=84=E7=9B=B4=E6=8E=A5=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=EF=BC=8C=E8=A7=A3=E5=86=B3=E5=BC=80=E5=8F=91=E5=9C=BA?= =?UTF-8?q?=E6=99=AF=E4=B8=AD=E4=BD=BF=E7=94=A8=E5=85=B6=E5=AE=83json?= =?UTF-8?q?=E5=BA=93=E7=9A=84=E6=83=85=E5=86=B5=E4=B8=8B=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E4=BC=9A=E5=87=BA=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/support/jwt/JwtManager.java | 7 +++---- src/main/java/io/jboot/web/controller/JbootController.java | 3 ++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/support/jwt/JwtManager.java b/src/main/java/io/jboot/support/jwt/JwtManager.java index 3195bebc..9eed78d6 100644 --- a/src/main/java/io/jboot/support/jwt/JwtManager.java +++ b/src/main/java/io/jboot/support/jwt/JwtManager.java @@ -15,7 +15,7 @@ */ package io.jboot.support.jwt; -import com.alibaba.fastjson.JSON; +import com.jfinal.kit.JsonKit; import io.jboot.Jboot; import io.jboot.exception.JbootException; import io.jboot.utils.StrUtil; @@ -80,8 +80,7 @@ public class JwtManager { if (StrUtil.isBlank(jsonString)) { return null; } - - return JSON.parseObject(jsonString, HashMap.class); + return JsonKit.parse(jsonString, HashMap.class); } catch (SignatureException | MalformedJwtException e) { // don't trust the JWT! @@ -108,7 +107,7 @@ public class JwtManager { Date now = new Date(nowMillis); map.put(JwtInterceptor.ISUUED_AT, nowMillis); - String subject = JSON.toJSONString(map); + String subject = JsonKit.toJson(map); JwtBuilder builder = Jwts.builder() .setIssuedAt(now) diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 988e0899..fb1ad960 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -19,6 +19,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.jfinal.core.Controller; import com.jfinal.core.NotAction; +import com.jfinal.kit.JsonKit; import io.jboot.support.jwt.JwtManager; import io.jboot.utils.RequestUtil; import io.jboot.utils.StrUtil; @@ -194,7 +195,7 @@ public class JbootController extends Controller { */ @NotAction public T getRawObject(Class tClass) { - return StrUtil.isBlank(getRawData()) ? null : JSON.parseObject(getRawData(), tClass); + return StrUtil.isBlank(getRawData()) ? null : JsonKit.parse(getRawData(), tClass); } -- Gitee From 0c76156dfea481a282208eea956f134b54d12bda Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Apr 2020 16:18:33 +0800 Subject: [PATCH 0256/1965] fixed : JbootGatewayManager default name is error --- .../java/io/jboot/components/gateway/JbootGatewayManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java index 3d9fe694..e1b15388 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -44,7 +44,7 @@ public class JbootGatewayManager { for (Map.Entry e : configMap.entrySet()) { JbootGatewayConfig config = e.getValue(); if (config.isConfigOk() && config.isEnable()) { - if (StrUtil.isNotBlank(config.getName())) { + if (StrUtil.isBlank(config.getName())) { config.setName(e.getKey()); } registerConfig(config); -- Gitee From ca44e96ed44a6cdc9bf413acd689d89b329ab717 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Apr 2020 16:19:04 +0800 Subject: [PATCH 0257/1965] add default consumer config support --- .../io/jboot/components/rpc/dubbo/DubboUtil.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index 0a6ebf64..b91142e5 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -55,7 +55,7 @@ class DubboUtil { applicationConfig.setName("jboot"); } //默认关闭 qos - if (applicationConfig.getQosEnable() == null){ + if (applicationConfig.getQosEnable() == null) { applicationConfig.setQosEnable(false); } @@ -154,10 +154,18 @@ class DubboUtil { ReferenceConfig referenceConfig = new ReferenceConfig(); RPCUtil.copyFields(rc, referenceConfig); - //reference coonsumer + // reference consumer if (rc.getConsumer() != null) { referenceConfig.setConsumer(consumerConfigMap.get(rc.getConsumer())); } + // set default consumer + else { + for (ConsumerConfig consumerConfig : consumerConfigMap.values()) { + if (consumerConfig.getDefault() != null && consumerConfig.getDefault()) { + referenceConfig.setConsumer(consumerConfig); + } + } + } //service registry -- Gitee From 0cff44a6bae7b8a55afe552eb59cf7daf5b1a6b6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Apr 2020 16:21:56 +0800 Subject: [PATCH 0258/1965] =?UTF-8?q?fixed=EF=BC=9APRCUtil=20can=20not=20a?= =?UTF-8?q?ppend=20some=20Annotation=20propertie?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/components/rpc/RPCUtil.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/RPCUtil.java b/src/main/java/io/jboot/components/rpc/RPCUtil.java index 431ec5be..921e14bc 100644 --- a/src/main/java/io/jboot/components/rpc/RPCUtil.java +++ b/src/main/java/io/jboot/components/rpc/RPCUtil.java @@ -44,6 +44,9 @@ public class RPCUtil { for (Method method : methods) { if (method.getDeclaringClass() != Object.class && method.getReturnType() != void.class + && !"toString".equals(method.getName()) + && !"hashCode".equals(method.getName()) + && !"annotationType".equals(method.getName()) && method.getParameterTypes().length == 0 && Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())) { @@ -56,18 +59,29 @@ public class RPCUtil { Object value = method.invoke(annotation); if (value != null && !value.equals(method.getDefaultValue())) { Class parameterType = getBoxedClass(method.getReturnType()); - if ("filter".equals(property) || "listener".equals(property)) { + if ("filter".equals(property) || "listener".equals(property) || "registry".equals(property)) { parameterType = String.class; value = StringUtils.join((String[]) value, ","); } else if ("parameters".equals(property)) { parameterType = Map.class; value = CollectionUtil.string2Map((String) value); } + + Method setterMethod = null; try { - Method setterMethod = appendTo.getClass().getMethod(setter, parameterType); - setterMethod.invoke(appendTo, value); + setterMethod = appendTo.getClass().getMethod(setter, parameterType); } catch (NoSuchMethodException e) { - // ignore + } + + if (setterMethod == null) { + try { + setterMethod = appendTo.getClass().getMethod(setter, method.getReturnType()); + } catch (NoSuchMethodException ex) { + } + } + + if (setterMethod != null) { + setterMethod.invoke(appendTo, value); } } } catch (Exception ex) { -- Gitee From d059311ee074acd3f7c271a3c3269f82553733d6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Apr 2020 16:22:23 +0800 Subject: [PATCH 0259/1965] add demos --- .../io/jboot/test/rpc/dubbo/DubboClient.java | 2 +- .../io/jboot/test/rpc/motan/MotanClient.java | 3 +- .../test/rpc/motan/MotanInterceptor.java | 78 +++++++++---------- 3 files changed, 41 insertions(+), 42 deletions(-) diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java index bc8e806e..de04fc60 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java @@ -44,7 +44,7 @@ public class DubboClient extends JbootController { } - @RPCInject + @RPCInject(timeout = 3300) private BlogService blogService; @Before(DubboInterceptor.class) diff --git a/src/test/java/io/jboot/test/rpc/motan/MotanClient.java b/src/test/java/io/jboot/test/rpc/motan/MotanClient.java index a85bed2a..68b4fd26 100644 --- a/src/test/java/io/jboot/test/rpc/motan/MotanClient.java +++ b/src/test/java/io/jboot/test/rpc/motan/MotanClient.java @@ -16,7 +16,6 @@ package io.jboot.test.rpc.motan; -import com.jfinal.aop.Before; import io.jboot.app.JbootApplication; import io.jboot.components.rpc.annotation.RPCInject; import io.jboot.test.rpc.commons.BlogService; @@ -47,7 +46,7 @@ public class MotanClient extends JbootController { @RPCInject private BlogService blogService; - @Before(MotanInterceptor.class) +// @Before(MotanInterceptor.class) public void index() { System.out.println("blogService:" + blogService); diff --git a/src/test/java/io/jboot/test/rpc/motan/MotanInterceptor.java b/src/test/java/io/jboot/test/rpc/motan/MotanInterceptor.java index 2b14b072..f0ea854b 100644 --- a/src/test/java/io/jboot/test/rpc/motan/MotanInterceptor.java +++ b/src/test/java/io/jboot/test/rpc/motan/MotanInterceptor.java @@ -1,39 +1,39 @@ -/** - * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.test.rpc.motan; - -import com.jfinal.aop.Interceptor; -import com.jfinal.aop.Invocation; -import io.jboot.components.rpc.annotation.RPCInject; -import io.jboot.test.rpc.commons.BlogService; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/24 - */ -public class MotanInterceptor implements Interceptor { - - @RPCInject(check = false) - private BlogService blogService; - - @Override - public void intercept(Invocation inv) { - - System.out.println("intercept : " + blogService); - - inv.invoke(); - } -} +///** +// * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). +// *

+// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// *

+// * http://www.apache.org/licenses/LICENSE-2.0 +// *

+// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +//package io.jboot.test.rpc.motan; +// +//import com.jfinal.aop.Interceptor; +//import com.jfinal.aop.Invocation; +//import io.jboot.components.rpc.annotation.RPCInject; +//import io.jboot.test.rpc.commons.BlogService; +// +///** +// * @author michael yang (fuhai999@gmail.com) +// * @Date: 2020/3/24 +// */ +//public class MotanInterceptor implements Interceptor { +// +// @RPCInject(check = false) +// private BlogService blogService; +// +// @Override +// public void intercept(Invocation inv) { +// +// System.out.println("intercept : " + blogService); +// +// inv.invoke(); +// } +//} -- Gitee From 4033d04193c7786fc8daf87c16121114518cf0b7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Apr 2020 18:02:30 +0800 Subject: [PATCH 0260/1965] fixed : default provider can not config correct --- .../java/io/jboot/components/rpc/dubbo/DubboUtil.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index b91142e5..853d663d 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -181,10 +181,18 @@ class DubboUtil { ServiceConfig serviceConfig = new ServiceConfig(); RPCUtil.copyFields(sc, serviceConfig); - //service provider + // service provider if (StrUtil.isNotBlank(sc.getProvider())) { serviceConfig.setProviderIds(sc.getProvider()); } + // set default provider + else { + for (ProviderConfig providerConfig : providerConfigMap.values()) { + if (providerConfig.isDefault() != null && providerConfig.isDefault()) { + serviceConfig.setProvider(providerConfig); + } + } + } //service protocol if (StrUtil.isNotBlank(sc.getProtocol())) { -- Gitee From a8761009b891b866fe4049029e834ac893932fde Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Apr 2020 18:54:37 +0800 Subject: [PATCH 0261/1965] add multi uri config support --- .../jboot/components/gateway/GatewayUtil.java | 2 +- .../gateway/JbootGatewayConfig.java | 30 ++++++++++++++----- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayUtil.java b/src/main/java/io/jboot/components/gateway/GatewayUtil.java index 55665e94..10819f3a 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayUtil.java +++ b/src/main/java/io/jboot/components/gateway/GatewayUtil.java @@ -29,7 +29,7 @@ public class GatewayUtil { private static final String PATH_SPLIT = "/"; public static String buildProxyUrl(JbootGatewayConfig config,HttpServletRequest request){ - StringBuilder url = new StringBuilder(config.getUri()); + StringBuilder url = new StringBuilder(config.getRandomUri()); if (StrUtil.isNotBlank(request.getRequestURI())) { url.append(request.getRequestURI()); } diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index 2721ef84..eb26e8e0 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -24,6 +24,7 @@ import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.concurrent.ThreadLocalRandom; /** * @author michael yang (fuhai999@gmail.com) @@ -34,7 +35,7 @@ public class JbootGatewayConfig implements Serializable { public static final String DEFAULT_PROXY_CONTENT_TYPE = "text/html;charset=utf-8"; private String name; - private String uri; + private String[] uri; private boolean enable = false; // 是否启用 sentinel 限流 @@ -42,7 +43,7 @@ public class JbootGatewayConfig implements Serializable { // sentinel 被限流后跳转地址 private String sentinelBlockPage; // sentinel 被渲染的json内容,如果配置 sentinelBlockPage,则 sentinelBlockJsonMap 配置无效 - private Map sentinelBlockJsonMap; + private Map sentinelBlockJsonMap; // 设置 http 代理的参数 private int proxyReadTimeout = 10000; //10s @@ -82,14 +83,25 @@ public class JbootGatewayConfig implements Serializable { this.name = name; } - public String getUri() { + + public String[] getUri() { return uri; } - public void setUri(String uri) { + public void setUri(String[] uri) { this.uri = uri; } + public String getRandomUri() { + if (uri == null || uri.length == 0) { + return null; + } else if (uri.length == 1) { + return uri[0]; + } else { + return uri[ThreadLocalRandom.current().nextInt(uri.length)]; + } + } + public boolean isEnable() { return enable; } @@ -275,7 +287,7 @@ public class JbootGatewayConfig implements Serializable { } synchronized (this) { if (configOk == null) { - configOk = StrUtil.isNotBlank(uri); + configOk = uri != null && uri.length > 0; if (configOk) { ensureUriConfigCorrect(); } @@ -285,9 +297,11 @@ public class JbootGatewayConfig implements Serializable { } private void ensureUriConfigCorrect() { - if (!uri.toLowerCase().startsWith("http://") - && !uri.toLowerCase().startsWith("https://")) { - throw new JbootIllegalConfigException("gateway uri must start with http:// or https://"); + for (String u : uri) { + if (!u.toLowerCase().startsWith("http://") + && !u.toLowerCase().startsWith("https://")) { + throw new JbootIllegalConfigException("gateway uri must start with http:// or https://"); + } } } -- Gitee From 45b109fc43a17ae6580719fbe352028f9660af09 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Apr 2020 19:00:31 +0800 Subject: [PATCH 0262/1965] add multi uri config support --- doc/docs/gateway.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/docs/gateway.md b/doc/docs/gateway.md index 8c31a76c..bc6a9669 100644 --- a/doc/docs/gateway.md +++ b/doc/docs/gateway.md @@ -46,7 +46,7 @@ jboot.gateway.queryContains = aa,bb ``` - name 设置路由的名称 -- uri 设置路由目标网址 +- uri 设置路由目标网址,可以配置多个 uri,多个 uri 用英文逗号(,) 隔开,当有多个 uri 的时候,系统会随机使用其中一个去访问 - enable 是否启用该路由 - sentinelEnable 是否启用 sentinel 限流功能 - sentinelBlockPage 若该路由被限流后,网页自动跳转到哪个网址 -- Gitee From f335e011b6fbaa25216ef09945aaefe127d6b4da Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Apr 2020 19:01:54 +0800 Subject: [PATCH 0263/1965] update gateway docs --- doc/docs/gateway.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/docs/gateway.md b/doc/docs/gateway.md index bc6a9669..2cb5f467 100644 --- a/doc/docs/gateway.md +++ b/doc/docs/gateway.md @@ -46,7 +46,7 @@ jboot.gateway.queryContains = aa,bb ``` - name 设置路由的名称 -- uri 设置路由目标网址,可以配置多个 uri,多个 uri 用英文逗号(,) 隔开,当有多个 uri 的时候,系统会随机使用其中一个去访问 +- uri 设置路由目标网址,可以配置多个 uri,多个 uri 用英文逗号(,) 隔开,当有多个 uri 的时候,系统会 **随机** 使用其中一个去访问 - enable 是否启用该路由 - sentinelEnable 是否启用 sentinel 限流功能 - sentinelBlockPage 若该路由被限流后,网页自动跳转到哪个网址 -- Gitee From 10d48c8b40594c464e2736cfbaa97fe1ae789b9c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 17 Apr 2020 10:27:46 +0800 Subject: [PATCH 0264/1965] fixed nacos config sometimes garbled --- .../io/jboot/app/config/support/nacos/NacosConfigManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java index b9b961df..62c67d93 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java @@ -21,8 +21,8 @@ import io.jboot.Jboot; import io.jboot.app.config.JbootConfigManager; import io.jboot.utils.StrUtil; -import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.StringReader; import java.util.HashSet; import java.util.Objects; import java.util.Properties; @@ -113,7 +113,7 @@ public class NacosConfigManager { private Properties str2Properties(String content) { try { Properties properties = new Properties(); - properties.load(new ByteArrayInputStream(content.getBytes())); + properties.load(new StringReader(content)); return properties; } catch (IOException e) { e.printStackTrace(); -- Gitee From b1f7cfb1f9db4d264b565fe81339a24ba4318be8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 17 Apr 2020 10:36:47 +0800 Subject: [PATCH 0265/1965] add removeConfig method in GatewayManager --- .../io/jboot/components/gateway/JbootGatewayManager.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java index e1b15388..52fe1934 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -62,6 +62,11 @@ public class JbootGatewayManager { } + public JbootGatewayConfig removeConfig(String name) { + return configMap == null ? null : configMap.remove(name); + } + + public JbootGatewayConfig getConfig(String name) { return configMap == null ? null : configMap.get(name); } -- Gitee From 0ba2e3b9b3d97b732e891876ea6183b147a79c5d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 17 Apr 2020 10:45:21 +0800 Subject: [PATCH 0266/1965] add default protocol and default registry setting support --- .../jboot/components/rpc/dubbo/DubboUtil.java | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index 853d663d..108ec5a1 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -150,18 +150,18 @@ class DubboUtil { } - public static ReferenceConfig toReferenceConfig(JbootrpcReferenceConfig rc) { + public static ReferenceConfig toReferenceConfig(JbootrpcReferenceConfig jbootReferenceConfig) { ReferenceConfig referenceConfig = new ReferenceConfig(); - RPCUtil.copyFields(rc, referenceConfig); + RPCUtil.copyFields(jbootReferenceConfig, referenceConfig); // reference consumer - if (rc.getConsumer() != null) { - referenceConfig.setConsumer(consumerConfigMap.get(rc.getConsumer())); + if (jbootReferenceConfig.getConsumer() != null) { + referenceConfig.setConsumer(consumerConfigMap.get(jbootReferenceConfig.getConsumer())); } // set default consumer else { for (ConsumerConfig consumerConfig : consumerConfigMap.values()) { - if (consumerConfig.getDefault() != null && consumerConfig.getDefault()) { + if (consumerConfig.isDefault() != null && consumerConfig.isDefault()) { referenceConfig.setConsumer(consumerConfig); } } @@ -169,21 +169,29 @@ class DubboUtil { //service registry - if (StrUtil.isNotBlank(rc.getRegistry())) { - referenceConfig.setRegistryIds(rc.getRegistry()); + if (StrUtil.isNotBlank(jbootReferenceConfig.getRegistry())) { + referenceConfig.setRegistryIds(jbootReferenceConfig.getRegistry()); + } + // set default registry + else { + for (RegistryConfig registryConfig : registryConfigMap.values()) { + if (registryConfig.isDefault() != null && registryConfig.isDefault()) { + referenceConfig.setRegistry(registryConfig); + } + } } return referenceConfig; } - public static ServiceConfig toServiceConfig(JbootrpcServiceConfig sc) { + public static ServiceConfig toServiceConfig(JbootrpcServiceConfig jbootServiceConfig) { ServiceConfig serviceConfig = new ServiceConfig(); - RPCUtil.copyFields(sc, serviceConfig); + RPCUtil.copyFields(jbootServiceConfig, serviceConfig); // service provider - if (StrUtil.isNotBlank(sc.getProvider())) { - serviceConfig.setProviderIds(sc.getProvider()); + if (StrUtil.isNotBlank(jbootServiceConfig.getProvider())) { + serviceConfig.setProviderIds(jbootServiceConfig.getProvider()); } // set default provider else { @@ -194,14 +202,30 @@ class DubboUtil { } } - //service protocol - if (StrUtil.isNotBlank(sc.getProtocol())) { - serviceConfig.setProtocolIds(sc.getProtocol()); + // service protocol + if (StrUtil.isNotBlank(jbootServiceConfig.getProtocol())) { + serviceConfig.setProtocolIds(jbootServiceConfig.getProtocol()); + } + // set default protocol + else { + for (ProtocolConfig protocolConfig : protocolConfigMap.values()) { + if (protocolConfig.isDefault() != null && protocolConfig.isDefault()) { + serviceConfig.setProtocol(protocolConfig); + } + } } - //service registry - if (StrUtil.isNotBlank(sc.getRegistry())) { - serviceConfig.setRegistryIds(sc.getRegistry()); + // service registry + if (StrUtil.isNotBlank(jbootServiceConfig.getRegistry())) { + serviceConfig.setRegistryIds(jbootServiceConfig.getRegistry()); + } + // set default registry + else { + for (RegistryConfig registryConfig : registryConfigMap.values()) { + if (registryConfig.isDefault() != null && registryConfig.isDefault()) { + serviceConfig.setRegistry(registryConfig); + } + } } return serviceConfig; -- Gitee From f4fe380ea83308a3e05745f1dc974e71965dd347 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 17 Apr 2020 11:00:21 +0800 Subject: [PATCH 0267/1965] remove dubbo StringUtils from RPCUtil --- .../java/io/jboot/components/rpc/RPCUtil.java | 8 ++-- src/main/java/io/jboot/utils/StrUtil.java | 37 +++++++++++++++++-- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/RPCUtil.java b/src/main/java/io/jboot/components/rpc/RPCUtil.java index 921e14bc..d5ca3da3 100644 --- a/src/main/java/io/jboot/components/rpc/RPCUtil.java +++ b/src/main/java/io/jboot/components/rpc/RPCUtil.java @@ -18,12 +18,14 @@ package io.jboot.components.rpc; import io.jboot.Jboot; import io.jboot.utils.CollectionUtil; import io.jboot.utils.StrUtil; -import org.apache.dubbo.common.utils.StringUtils; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * @author michael yang (fuhai999@gmail.com) @@ -61,7 +63,7 @@ public class RPCUtil { Class parameterType = getBoxedClass(method.getReturnType()); if ("filter".equals(property) || "listener".equals(property) || "registry".equals(property)) { parameterType = String.class; - value = StringUtils.join((String[]) value, ","); + value = StrUtil.join((String[]) value, ","); } else if ("parameters".equals(property)) { parameterType = Map.class; value = CollectionUtil.string2Map((String) value); diff --git a/src/main/java/io/jboot/utils/StrUtil.java b/src/main/java/io/jboot/utils/StrUtil.java index 26bd83dc..8648667e 100644 --- a/src/main/java/io/jboot/utils/StrUtil.java +++ b/src/main/java/io/jboot/utils/StrUtil.java @@ -25,10 +25,7 @@ import org.apache.commons.lang3.StringUtils; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -307,4 +304,36 @@ public class StrUtil extends StrKit { } + public static String join(String[] array, String split) { + if (array == null || array.length == 0) { + return EMPTY; + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + sb.append(split); + } + sb.append(array[i]); + } + return sb.toString(); + } + + public static String join(Collection coll, String split) { + if (coll == null || coll.isEmpty()) { + return EMPTY; + } + + StringBuilder sb = new StringBuilder(); + boolean isFirst = true; + for (String s : coll) { + if (isFirst) { + isFirst = false; + } else { + sb.append(split); + } + sb.append(s); + } + return sb.toString(); + } + } -- Gitee From 503199acee48d2808cdcb9dad0d2e2aaf409f00e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 19 Apr 2020 15:11:52 +0800 Subject: [PATCH 0268/1965] fixed some consumer properties not effective for rpc --- .../rpc/JbootrpcReferenceConfig.java | 18 +++---- .../components/rpc/JbootrpcServiceConfig.java | 14 ++--- .../java/io/jboot/components/rpc/RPCUtil.java | 53 ++++++++++--------- 3 files changed, 43 insertions(+), 42 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java index b31be214..1394fdc3 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java @@ -45,13 +45,13 @@ public class JbootrpcReferenceConfig implements Serializable { /** * Whether to enable generic invocation, default value is false */ - private boolean generic = false; + private Boolean generic; /** * Check if service provider is available during boot up, default value is true */ - private boolean check = true; + private Boolean check; /** @@ -59,7 +59,7 @@ public class JbootrpcReferenceConfig implements Serializable { *

* see Constants#DEFAULT_RETRIES */ - private int retries = 2; + private Integer retries; /** @@ -67,23 +67,23 @@ public class JbootrpcReferenceConfig implements Serializable { *

* see Constants#DEFAULT_LOADBALANCE */ - private String loadbalance = "random"; + private String loadbalance; /** * Whether to enable async invocation, default value is false */ - private boolean async = false; + private Boolean async; /** * Maximum active requests allowed, default value is 0 */ - private int actives = 0; + private Integer actives; /** * Timeout value for service invocation, default value is 0 */ - private int timeout = 0; + private Integer timeout; /** * Application associated name @@ -114,7 +114,7 @@ public class JbootrpcReferenceConfig implements Serializable { /** * the default value is "" */ - private String protocol = ""; + private String protocol; /** * Service tag name @@ -123,7 +123,7 @@ public class JbootrpcReferenceConfig implements Serializable { /** * The id - * + *

* default value is empty */ private String id; diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java index c82e8a4c..b9abc38f 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java @@ -44,7 +44,7 @@ public class JbootrpcServiceConfig implements Serializable { /** * Whether to export service, default value is true */ - private boolean export = true; + private Boolean export; /** * Service token, default value is false @@ -54,34 +54,34 @@ public class JbootrpcServiceConfig implements Serializable { /** * Whether the service is deprecated, default value is false */ - private boolean deprecated = false; + private Boolean deprecated; /** * Whether to register the service to register center, default value is true */ - private boolean register = true; + private Boolean register; /** * Service weight value, default value is 0 */ - private int weight = 0; + private Integer weight; /** * Service doc, default value is "" */ - private String document = ""; + private String document; /** * Service invocation retry times */ - private int retries = 2; + private int retries; /** * Load balance strategy, legal values include: random, roundrobin, leastactive */ - private String loadbalance = "random"; + private String loadbalance; /** diff --git a/src/main/java/io/jboot/components/rpc/RPCUtil.java b/src/main/java/io/jboot/components/rpc/RPCUtil.java index d5ca3da3..01410d30 100644 --- a/src/main/java/io/jboot/components/rpc/RPCUtil.java +++ b/src/main/java/io/jboot/components/rpc/RPCUtil.java @@ -60,28 +60,16 @@ public class RPCUtil { String setter = "set" + property.substring(0, 1).toUpperCase() + property.substring(1); Object value = method.invoke(annotation); if (value != null && !value.equals(method.getDefaultValue())) { - Class parameterType = getBoxedClass(method.getReturnType()); + Method setterMethod = null; if ("filter".equals(property) || "listener".equals(property) || "registry".equals(property)) { - parameterType = String.class; value = StrUtil.join((String[]) value, ","); + setterMethod = getMethod(appendTo.getClass(), setter, String.class); } else if ("parameters".equals(property)) { - parameterType = Map.class; value = CollectionUtil.string2Map((String) value); + setterMethod = getMethod(appendTo.getClass(), setter, Map.class); + } else { + setterMethod = getMethod(appendTo.getClass(), setter, method.getReturnType()); } - - Method setterMethod = null; - try { - setterMethod = appendTo.getClass().getMethod(setter, parameterType); - } catch (NoSuchMethodException e) { - } - - if (setterMethod == null) { - try { - setterMethod = appendTo.getClass().getMethod(setter, method.getReturnType()); - } catch (NoSuchMethodException ex) { - } - } - if (setterMethod != null) { setterMethod.invoke(appendTo, value); } @@ -105,20 +93,33 @@ public class RPCUtil { for (Field field : fields) { try { String setterName = "set" + StrUtil.firstCharToUpperCase(field.getName()); - Method method = copyTo.getClass().getMethod(setterName, getBoxedClass(field.getType())); - - field.setAccessible(true); - Object data = field.get(copyFrom); + Method method = getMethod(copyTo.getClass(), setterName, field.getType()); - if (data != null) { - method.invoke(copyTo, data); + if (method != null) { + field.setAccessible(true); + Object value = field.get(copyFrom); + if (value != null && !value.equals("0") && !value.equals("")) { + method.invoke(copyTo, value); + } } - } catch (NoSuchMethodException ex) { - // ignore } catch (Exception ex) { - ex.printStackTrace(); + // ignore + } + } + } + + + private static Method getMethod(Class clazz, String methodName, Class type) { + try { + return clazz.getMethod(methodName, getBoxedClass(type)); + } catch (NoSuchMethodException e) { + try { + return clazz.getMethod(methodName, type); + } catch (NoSuchMethodException ex) { } } + + return null; } -- Gitee From 66442bcb242d143384e839c109e3ca1791849672 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 19 Apr 2020 15:12:33 +0800 Subject: [PATCH 0269/1965] 3.1.8 --- src/main/java/io/jboot/utils/StrUtil.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/io/jboot/utils/StrUtil.java b/src/main/java/io/jboot/utils/StrUtil.java index 8648667e..cc2ff601 100644 --- a/src/main/java/io/jboot/utils/StrUtil.java +++ b/src/main/java/io/jboot/utils/StrUtil.java @@ -304,6 +304,7 @@ public class StrUtil extends StrKit { } + public static String join(String[] array, String split) { if (array == null || array.length == 0) { return EMPTY; @@ -318,6 +319,8 @@ public class StrUtil extends StrKit { return sb.toString(); } + + public static String join(Collection coll, String split) { if (coll == null || coll.isEmpty()) { return EMPTY; -- Gitee From 6c39c8f88bea6a0d36cc1d2f1039e71107c44a05 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 19 Apr 2020 18:16:16 +0800 Subject: [PATCH 0270/1965] v3.1.8 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 23 +++++++++++++++++++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- src/test/resources/jboot.properties | 5 +++++ 6 files changed, 32 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a1f772f6..406d9809 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.1.7 + 3.1.8 ``` diff --git a/changes.txt b/changes.txt index 712a0fdd..5d823e5d 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,26 @@ +jboot v3.1.8: +新增:Gateway 新增动态注册路 和 移除由配置的功能 +新增:Gateway 被 Sentinel 拦截后自定义返回 Json 的功能 +新增:Gateway 新增对多个 host 的支持,默认走随机匹配的负载均衡机制 +新增:新增 Dubbo 下的对 consumer/provider/register/protocol 的默认配置的支持 +优化:修改 JbootServiceBase 的 DAO 属性类型为 JbootModel +优化:优化 JbootrpcBase 和 Prop 的一些输出日志 +优化:重命名 PRCUtils 为 PRCUtil,保持 Jboot 工具类统一 +优化:重命名 JbootRpcApplication 为 JbootSimpleApplication +优化:重构 DubboUtil 代码,使之更加简洁 +优化:使用JsonKit来替换 FastJson 的直接使用,解决开发场景中使用其它json库的情况下,不会出错,感谢 @ yangyao +修复:修复 ClassScanner 扫描不到 Shiro 指令的问题 +修复:JbootGatewayManager 默认名字配置错误的问题 +修复:PRCUtil 无法添加某些注解注解属性导致@RPCInject某些参数无效的问题 +修复:启用分布式配置 Nacos 时,在 Nacos 配置的中文会出现乱码的问题 +修复:使用 Motan RPC 框架时,出现引用错误的问题 +修复:Dubbo 下的一些 consumer 配置失效的问题 +文档:优化 gateway 的相关文档 +文档:优化 config配置 的相关文档 +文档:优化 rpc 的相关文档 + + + jboot v3.1.7: 新增:JWT 可以通过 request para 传入数据的支持 修复:复写 ActionHandler.getAction 可能无效的问题 diff --git a/doc/docs/install.md b/doc/docs/install.md index dbad59d0..a7643acc 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.1.7 + 3.1.8 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index ce42f9f8..3a7c2923 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.1.7 + 3.1.8 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 57e13ca9..9b77c59d 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.1.7"; + public static String VERSION = "3.1.8"; public static final String ATTR_REQUEST = "REQUEST"; diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index f391de5e..e2cd4644 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -43,5 +43,10 @@ jboot.gateway.uri = http://127.0.0.1:9999/gateway/render jboot.gateway.pathEquals = /gateway +jboot.rpc.dubbo.consumer.timeout = 55555 +jboot.rpc.dubbo.consumer.default = true + + + jboot.rpc.type = local -- Gitee From 08c59b67176dfefbff298e8d35d1cd0eea8384e4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 19 Apr 2020 18:16:45 +0800 Subject: [PATCH 0271/1965] v3.1.8 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 845447a6..44262fb5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.8-SNAPSHOT + 3.1.8 jar jboot -- Gitee From 32b57e10ab2e78c36398792156c99d608090bde9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 19 Apr 2020 18:21:22 +0800 Subject: [PATCH 0272/1965] v3.1.8 release (^.^)YYa!! --- changes.txt | 1 + pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/changes.txt b/changes.txt index 5d823e5d..41c83585 100644 --- a/changes.txt +++ b/changes.txt @@ -9,6 +9,7 @@ jboot v3.1.8: 优化:重命名 JbootRpcApplication 为 JbootSimpleApplication 优化:重构 DubboUtil 代码,使之更加简洁 优化:使用JsonKit来替换 FastJson 的直接使用,解决开发场景中使用其它json库的情况下,不会出错,感谢 @ yangyao +优化:升级 Guava 等相关的 Maven 依赖到最新版本 修复:修复 ClassScanner 扫描不到 Shiro 指令的问题 修复:JbootGatewayManager 默认名字配置错误的问题 修复:PRCUtil 无法添加某些注解注解属性导致@RPCInject某些参数无效的问题 diff --git a/pom.xml b/pom.xml index 44262fb5..a24a7aff 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ 1.7.30 2.57 1.2.68 - 28.2-jre + 29.0-jre 2.2.5 2.10.2 1.13.1 @@ -592,7 +592,7 @@ net.oschina.j2cache j2cache-core - 2.8.0-release + 2.8.1-release provided -- Gitee From 9bcb3afb86171bf3d5948f5236025413679bcb2d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 19 Apr 2020 18:48:36 +0800 Subject: [PATCH 0273/1965] v3.1.8 release (^.^)YYa!! --- src/main/java/io/jboot/core/JbootCoreConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 204d9dde..227e25c2 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -203,7 +203,7 @@ public class JbootCoreConfig extends JFinalConfig { //通过 java -jar xxx.jar 在单独的jar里运行 if (runInFatjar()) { engine.setToClassPathSourceFactory(); - engine.setBaseTemplatePath(null); + engine.setBaseTemplatePath("webapp"); } List directiveClasses = ClassScanner.scanClass(); -- Gitee From 8ba17dc0a246d0ae05e1046d4f0ac2a2fa666694 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 19 Apr 2020 20:35:48 +0800 Subject: [PATCH 0274/1965] v3.1.8 release (^.^)YYa!! --- changes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changes.txt b/changes.txt index 41c83585..b5c9ada9 100644 --- a/changes.txt +++ b/changes.txt @@ -16,6 +16,7 @@ jboot v3.1.8: 修复:启用分布式配置 Nacos 时,在 Nacos 配置的中文会出现乱码的问题 修复:使用 Motan RPC 框架时,出现引用错误的问题 修复:Dubbo 下的一些 consumer 配置失效的问题 +修复:修复 fatjar 打包的时候,需要单独配置 BaseTemplatePath,否则出错的问题 文档:优化 gateway 的相关文档 文档:优化 config配置 的相关文档 文档:优化 rpc 的相关文档 -- Gitee From afafa5471c3765fec13fe55f759f75bbe1f16f9c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 20 Apr 2020 08:55:46 +0800 Subject: [PATCH 0275/1965] v3.1.9-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a24a7aff..9760b9c5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.8 + 3.1.9-SNAPSHOT jar jboot -- Gitee From 663e375174254226eb159fb430dc6509cdd4f63d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 20 Apr 2020 17:20:10 +0800 Subject: [PATCH 0276/1965] fixed: ArrayIndexOutOfBoundsException if config value first char is '{' --- src/main/java/io/jboot/app/config/Utils.java | 25 +++++++++++--------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/jboot/app/config/Utils.java b/src/main/java/io/jboot/app/config/Utils.java index 5d2c37a0..99fcbf17 100644 --- a/src/main/java/io/jboot/app/config/Utils.java +++ b/src/main/java/io/jboot/app/config/Utils.java @@ -55,17 +55,20 @@ class Utils { ConfigPart part = null; int index = 0; for (char c : chars){ - if (c == '{' && chars[index-1] == '$' && part == null){ - part = new ConfigPart(); - part.setStart(index); - }else if ( c == '}' && part != null){ - part.setEnd(index); - configParts.add(part); - part = null; - }else if (part != null){ - part.append(c); - if (c ==':' && part.getKeyValueIndexOf() == 0){ - part.setKeyValueIndexOf(index - part.getStart()); + //第一个字符是 '{' 会出现 ArrayIndexOutOfBoundsException 错误 + if (index > 0) { + if (c == '{' && chars[index - 1] == '$' && part == null) { + part = new ConfigPart(); + part.setStart(index); + } else if (c == '}' && part != null) { + part.setEnd(index); + configParts.add(part); + part = null; + } else if (part != null) { + part.append(c); + if (c == ':' && part.getKeyValueIndexOf() == 0) { + part.setKeyValueIndexOf(index - part.getStart()); + } } } index ++; -- Gitee From 9db00993513f99c713b5cb1f8f2ae3db544748f6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 22 Apr 2020 10:22:01 +0800 Subject: [PATCH 0277/1965] fixed: ArrayIndexOutOfBoundsException if config value first char is '{' --- src/main/java/io/jboot/app/config/Utils.java | 32 +++++++++----------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/jboot/app/config/Utils.java b/src/main/java/io/jboot/app/config/Utils.java index 99fcbf17..2e2c760b 100644 --- a/src/main/java/io/jboot/app/config/Utils.java +++ b/src/main/java/io/jboot/app/config/Utils.java @@ -46,32 +46,30 @@ class Utils { return null; } - public static List parseParts(String string){ - if (StrUtil.isBlank(string)){ + public static List parseParts(String string) { + if (StrUtil.isBlank(string)) { return null; } List configParts = new LinkedList<>(); char[] chars = string.toCharArray(); ConfigPart part = null; int index = 0; - for (char c : chars){ + for (char c : chars) { //第一个字符是 '{' 会出现 ArrayIndexOutOfBoundsException 错误 - if (index > 0) { - if (c == '{' && chars[index - 1] == '$' && part == null) { - part = new ConfigPart(); - part.setStart(index); - } else if (c == '}' && part != null) { - part.setEnd(index); - configParts.add(part); - part = null; - } else if (part != null) { - part.append(c); - if (c == ':' && part.getKeyValueIndexOf() == 0) { - part.setKeyValueIndexOf(index - part.getStart()); - } + if (c == '{' && index > 0 && chars[index - 1] == '$' && part == null) { + part = new ConfigPart(); + part.setStart(index); + } else if (c == '}' && part != null) { + part.setEnd(index); + configParts.add(part); + part = null; + } else if (part != null) { + part.append(c); + if (c == ':' && part.getKeyValueIndexOf() == 0) { + part.setKeyValueIndexOf(index - part.getStart()); } } - index ++; + index++; } return configParts; } -- Gitee From 4c9463df30f65a6d7c651354d8ec1c25ab8867ea Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 22 Apr 2020 10:24:02 +0800 Subject: [PATCH 0278/1965] upgrade seata --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9760b9c5..0074b758 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 1.7.2 2.7.6 3.11.0.Final - 1.1.0 + 1.2.0 1.1.8 4.1.6 -- Gitee From 211727dc36261175ccedc9c6187999107e7e214d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 24 Apr 2020 17:35:45 +0800 Subject: [PATCH 0279/1965] remove rpc service export success message --- src/main/java/io/jboot/components/rpc/JbootrpcManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java index 3072fe2d..d9c1d7eb 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java @@ -113,7 +113,7 @@ public class JbootrpcManager { if (jbootrpc.serviceExport(inter, Aop.get(clazz), new JbootrpcServiceConfig(rpcBean))) { if (Jboot.isDevMode()) { - System.out.println("rpc service[" + inter + "] has exported ok!"); +// System.out.println("rpc service[" + inter + "] has exported ok!"); } } } -- Gitee From fc5339c487c0ca9e31ad7000d50fa660a70727ce Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 25 Apr 2020 10:42:21 +0800 Subject: [PATCH 0280/1965] v3.1.9 --- .../io/jboot/components/schedule/JbootCron4jPlugin.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/components/schedule/JbootCron4jPlugin.java b/src/main/java/io/jboot/components/schedule/JbootCron4jPlugin.java index 7dcbe99e..b8912891 100644 --- a/src/main/java/io/jboot/components/schedule/JbootCron4jPlugin.java +++ b/src/main/java/io/jboot/components/schedule/JbootCron4jPlugin.java @@ -92,7 +92,7 @@ public class JbootCron4jPlugin implements IPlugin { throw new IllegalArgumentException("Task 必须是 Runnable、ITask、ProcessTask 或者 Task 类型"); } - boolean taskDaemon = configProp.getBoolean(taskName + ".daemon", true); + boolean taskDaemon = configProp.getBoolean(taskName + ".daemon", false); boolean taskEnable = configProp.getBoolean(taskName + ".enable", true); taskInfoList.add(new JbootCron4jPlugin.TaskInfo(taskCron, taskObj, taskDaemon, taskEnable)); } @@ -108,7 +108,7 @@ public class JbootCron4jPlugin implements IPlugin { } public JbootCron4jPlugin addTask(String cron, Runnable task) { - return addTask(cron, task, true, true); + return addTask(cron, task, false, true); } public JbootCron4jPlugin addTask(String cron, ProcessTask processTask, boolean daemon, boolean enable) { @@ -121,7 +121,7 @@ public class JbootCron4jPlugin implements IPlugin { } public JbootCron4jPlugin addTask(String cron, ProcessTask processTask) { - return addTask(cron, processTask, true, true); + return addTask(cron, processTask, false, true); } public JbootCron4jPlugin addTask(String cron, Task task, boolean daemon, boolean enable) { @@ -134,7 +134,7 @@ public class JbootCron4jPlugin implements IPlugin { } public JbootCron4jPlugin addTask(String cron, Task task) { - return addTask(cron, task, true, true); + return addTask(cron, task, false, true); } @Override -- Gitee From 6c26c85bcbebebbd0f2dd4fe620f0bd854fc29c2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 25 Apr 2020 14:34:54 +0800 Subject: [PATCH 0281/1965] rename app/config/Utils to ConfigUtil --- .../config/{Utils.java => ConfigUtil.java} | 6 ++-- .../jboot/app/config/JbootConfigManager.java | 36 +++++++++---------- src/main/java/io/jboot/app/config/Prop.java | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) rename src/main/java/io/jboot/app/config/{Utils.java => ConfigUtil.java} (97%) diff --git a/src/main/java/io/jboot/app/config/Utils.java b/src/main/java/io/jboot/app/config/ConfigUtil.java similarity index 97% rename from src/main/java/io/jboot/app/config/Utils.java rename to src/main/java/io/jboot/app/config/ConfigUtil.java index 2e2c760b..3e9ddbeb 100644 --- a/src/main/java/io/jboot/app/config/Utils.java +++ b/src/main/java/io/jboot/app/config/ConfigUtil.java @@ -24,7 +24,7 @@ import java.lang.reflect.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -class Utils { +public class ConfigUtil { public static boolean isBlank(String string) { @@ -112,7 +112,7 @@ class Utils { rootClassPath = new File(path).getAbsolutePath(); } catch (Exception e) { try { - String path = Utils.class.getProtectionDomain().getCodeSource().getLocation().getPath(); + String path = ConfigUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath(); path = java.net.URLDecoder.decode(path, "UTF-8"); if (path.endsWith(File.separator)) { path = path.substring(0, path.length() - 1); @@ -135,7 +135,7 @@ class Utils { public static ClassLoader getClassLoader() { ClassLoader ret = Thread.currentThread().getContextClassLoader(); - return ret != null ? ret : Utils.class.getClassLoader(); + return ret != null ? ret : ConfigUtil.class.getClassLoader(); } public static void doNothing(Throwable ex) { diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 134eea8b..753e51a6 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -68,7 +68,7 @@ public class JbootConfigManager { String mode = getConfigValue("jboot.app.mode"); - if (Utils.isNotBlank(mode)) { + if (ConfigUtil.isNotBlank(mode)) { String p = String.format("jboot-%s.properties", mode); mainProperties.putAll(new Prop(p).getProperties()); } @@ -163,7 +163,7 @@ public class JbootConfigManager { mainProperties.putAll(properties); String mode = getConfigValue(properties, "jboot.app.mode"); - if (Utils.isNotBlank(mode)) { + if (ConfigUtil.isNotBlank(mode)) { String p = String.format("jboot-%s.properties", mode); mainProperties.putAll(new Prop(p).getProperties()); } @@ -181,23 +181,23 @@ public class JbootConfigManager { * @return */ public T createConfigObject(Class clazz, String prefix, String file){ - Object configObject = Utils.newInstance(clazz); - List setMethods = Utils.getClassSetMethods(clazz); + Object configObject = ConfigUtil.newInstance(clazz); + List setMethods = ConfigUtil.getClassSetMethods(clazz); if (setMethods != null) { for (Method method : setMethods) { String key = buildKey(prefix, method); String value = getConfigValue(key); - if (Utils.isNotBlank(file)) { + if (ConfigUtil.isNotBlank(file)) { Prop prop = new Prop(file); String filePropValue = getConfigValue(prop.getProperties(), key); - if (Utils.isNotBlank(filePropValue)) { + if (ConfigUtil.isNotBlank(filePropValue)) { value = filePropValue; } } - if (Utils.isNotBlank(value)) { + if (ConfigUtil.isNotBlank(value)) { Object val = convert(method.getParameterTypes()[0], value, method.getGenericParameterTypes()[0]); if (val != null) { try { @@ -215,12 +215,12 @@ public class JbootConfigManager { public Object convert(Class clazz, String s, Type genericType) { - return Utils.convert(clazz, s, genericType); + return ConfigUtil.convert(clazz, s, genericType); } private String buildKey(String prefix, Method method) { - String key = Utils.firstCharToLowerCase(method.getName().substring(3)); - if (Utils.isNotBlank(prefix)) { + String key = ConfigUtil.firstCharToLowerCase(method.getName().substring(3)); + if (ConfigUtil.isNotBlank(prefix)) { key = prefix.trim() + "." + key; } return key; @@ -239,7 +239,7 @@ public class JbootConfigManager { String originalValue = getOriginalConfigValue(properties, key); String stringValue = decryptor != null ? decryptor.decrypt(key, originalValue) : originalValue; - List configParts = Utils.parseParts(stringValue); + List configParts = ConfigUtil.parseParts(stringValue); if (configParts == null || configParts.isEmpty()) { return stringValue; } @@ -266,20 +266,20 @@ public class JbootConfigManager { //优先读取分布式配置内容 if (remoteProperties != null) { value = (String) remoteProperties.get(key); - if (Utils.isNotBlank(value)) { + if (ConfigUtil.isNotBlank(value)) { return value.trim(); } } //boot arg value = getBootArg(key); - if (Utils.isNotBlank(value)) { + if (ConfigUtil.isNotBlank(value)) { return value.trim(); } //env value = System.getenv(key); - if (Utils.isNotBlank(value)) { + if (ConfigUtil.isNotBlank(value)) { return value.trim(); } @@ -288,19 +288,19 @@ public class JbootConfigManager { // 例如:jboot.datasource.url 转换为 JBOOT_DATASOURCE_URL String tempKey = key.toUpperCase().replace('.', '_'); value = System.getenv(tempKey); - if (Utils.isNotBlank(value)) { + if (ConfigUtil.isNotBlank(value)) { return value.trim(); } //system property value = System.getProperty(key); - if (Utils.isNotBlank(value)) { + if (ConfigUtil.isNotBlank(value)) { return value.trim(); } //user properties value = (String) properties.get(key); - if (Utils.isNotBlank(value)) { + if (ConfigUtil.isNotBlank(value)) { return value.trim(); } @@ -378,7 +378,7 @@ public class JbootConfigManager { listenerClassMapping.put(listener, forClass); String prefix = configModel.prefix(); - List setMethods = Utils.getClassSetMethods(forClass); + List setMethods = ConfigUtil.getClassSetMethods(forClass); if (setMethods != null) { for (Method method : setMethods) { String key = buildKey(prefix, method); diff --git a/src/main/java/io/jboot/app/config/Prop.java b/src/main/java/io/jboot/app/config/Prop.java index 446afe91..7c6bf35b 100644 --- a/src/main/java/io/jboot/app/config/Prop.java +++ b/src/main/java/io/jboot/app/config/Prop.java @@ -31,7 +31,7 @@ class Prop { properties = new Properties(); InputStream inputStream = null; try { - inputStream = Utils.getClassLoader().getResourceAsStream(fileName); + inputStream = ConfigUtil.getClassLoader().getResourceAsStream(fileName); if (inputStream != null) { properties.load(new InputStreamReader(inputStream, encoding)); } -- Gitee From 12947a82f0f9ca721f7ced8dc16fd80e3921c25e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 25 Apr 2020 14:36:38 +0800 Subject: [PATCH 0282/1965] Restructure AnnotationUtil.get() method and remove getConfigValueByKeyString() method --- .../java/io/jboot/aop/JbootAopFactory.java | 3 +- .../java/io/jboot/utils/AnnotationUtil.java | 34 ++++++++----------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 9f35239a..8ae94546 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -24,6 +24,7 @@ import com.jfinal.log.Log; import com.jfinal.plugin.activerecord.Model; import com.jfinal.proxy.Proxy; import com.jfinal.proxy.ProxyManager; +import io.jboot.Jboot; import io.jboot.aop.annotation.*; import io.jboot.app.config.JbootConfigManager; import io.jboot.app.config.annotation.ConfigModel; @@ -221,7 +222,7 @@ public class JbootAopFactory extends AopFactory { private String getConfigValue(String key, Object targetObject, Field field) { - return AnnotationUtil.getConfigValueByKeyString(key); + return Jboot.configValue(key); } diff --git a/src/main/java/io/jboot/utils/AnnotationUtil.java b/src/main/java/io/jboot/utils/AnnotationUtil.java index 7bdd9a08..ed8b1d12 100644 --- a/src/main/java/io/jboot/utils/AnnotationUtil.java +++ b/src/main/java/io/jboot/utils/AnnotationUtil.java @@ -1,13 +1,13 @@ package io.jboot.utils; +import io.jboot.app.config.ConfigPart; +import io.jboot.app.config.ConfigUtil; import io.jboot.app.config.JbootConfigManager; -public class AnnotationUtil { - - private static final String EXPR_PREFIX = "${"; - private static final String EXPR_SUFFIX = "}"; +import java.util.List; +public class AnnotationUtil { public static String get(String value) { if (StrUtil.isBlank(value)) { @@ -16,25 +16,19 @@ public class AnnotationUtil { value = value.trim(); } - if (value.startsWith(EXPR_PREFIX) && value.endsWith(EXPR_SUFFIX)) { - String key = value.substring(2, value.length() - 1); - return getConfigValueByKeyString(key); - } - - return value; - } + List configParts = ConfigUtil.parseParts(value); + if (configParts == null || configParts.isEmpty()) { + return value; + } - public static String getConfigValueByKeyString(String key) { - int indexOf = key.indexOf(":"); - String defaultValue = null; - if (indexOf != -1) { - defaultValue = key.substring(indexOf + 1); - key = key.substring(0, indexOf); + for (ConfigPart cp : configParts) { + String configValue = JbootConfigManager.me().getConfigValue(cp.getKey()); + configValue = StrUtil.isNotBlank(configValue) ? value : cp.getDefaultValue(); + value = value.replace(cp.getPartString(), configValue); } - String configValue = JbootConfigManager.me().getConfigValue(key.trim()); - String returnValue = StrUtil.obtainDefaultIfBlank(configValue, defaultValue); - return StrUtil.isBlank(returnValue) ? null : returnValue.trim(); + + return value; } -- Gitee From dc1736b430402aa54f536d60866cb348fc95b321 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 25 Apr 2020 15:38:06 +0800 Subject: [PATCH 0283/1965] Restructure AnnotationUtil.get() method --- src/main/java/io/jboot/utils/AnnotationUtil.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/utils/AnnotationUtil.java b/src/main/java/io/jboot/utils/AnnotationUtil.java index ed8b1d12..970db9f9 100644 --- a/src/main/java/io/jboot/utils/AnnotationUtil.java +++ b/src/main/java/io/jboot/utils/AnnotationUtil.java @@ -18,14 +18,12 @@ public class AnnotationUtil { List configParts = ConfigUtil.parseParts(value); - if (configParts == null || configParts.isEmpty()) { - return value; - } - - for (ConfigPart cp : configParts) { - String configValue = JbootConfigManager.me().getConfigValue(cp.getKey()); - configValue = StrUtil.isNotBlank(configValue) ? value : cp.getDefaultValue(); - value = value.replace(cp.getPartString(), configValue); + if (configParts != null) { + for (ConfigPart cp : configParts) { + String configValue = JbootConfigManager.me().getConfigValue(cp.getKey()); + configValue = StrUtil.isNotBlank(configValue) ? configValue : cp.getDefaultValue(); + value = value.replace(cp.getPartString(), configValue); + } } return value; -- Gitee From 4e41c79f5bb6309ca44e32db42c442d773386324 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 25 Apr 2020 17:08:41 +0800 Subject: [PATCH 0284/1965] remove JbootConfigManager.convert() method --- src/main/java/io/jboot/Jboot.java | 16 ++++++++++++++++ src/main/java/io/jboot/aop/JbootAopFactory.java | 3 ++- .../io/jboot/app/config/JbootConfigManager.java | 7 +------ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/Jboot.java b/src/main/java/io/jboot/Jboot.java index 9057ceee..6f86851f 100644 --- a/src/main/java/io/jboot/Jboot.java +++ b/src/main/java/io/jboot/Jboot.java @@ -32,6 +32,7 @@ import io.jboot.components.serializer.JbootSerializerManager; import io.jboot.support.metric.JbootMetricManager; import io.jboot.support.redis.JbootRedis; import io.jboot.support.redis.JbootRedisManager; +import io.jboot.utils.StrUtil; public class Jboot { @@ -145,6 +146,19 @@ public class Jboot { } + /** + * 读取某个配置信息 + * + * @param key + * @param defaultValue 当获取不到的时候发挥此默认值 + * @return + */ + public static String configValue(String key, String defaultValue) { + String value = configValue(key); + return StrUtil.isNotBlank(value) ? value : defaultValue; + } + + /** * 获取 RPC 服务 * @@ -190,6 +204,7 @@ public class Jboot { /** * 根据类名获取 Aop 下的 Bean + * * @param clazz * @param * @return @@ -200,6 +215,7 @@ public class Jboot { /** * 根据名称获取 Aop 下的 Bean + * * @param name * @param * @return diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 8ae94546..739a6652 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -26,6 +26,7 @@ import com.jfinal.proxy.Proxy; import com.jfinal.proxy.ProxyManager; import io.jboot.Jboot; import io.jboot.aop.annotation.*; +import io.jboot.app.config.ConfigUtil; import io.jboot.app.config.JbootConfigManager; import io.jboot.app.config.annotation.ConfigModel; import io.jboot.components.event.JbootEventListener; @@ -213,7 +214,7 @@ public class JbootAopFactory extends AopFactory { String value = getConfigValue(key, targetObject, field); if (StrUtil.isNotBlank(value)) { - Object fieldInjectedObject = JbootConfigManager.me().convert(fieldInjectedClass, value, field.getGenericType()); + Object fieldInjectedObject = ConfigUtil.convert(fieldInjectedClass, value, field.getGenericType()); if (fieldInjectedObject != null) { setFieldValue(field, targetObject, fieldInjectedObject); } diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 753e51a6..b4986fc7 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -20,7 +20,6 @@ import io.jboot.app.config.annotation.ConfigModel; import io.jboot.utils.StrUtil; import java.lang.reflect.Method; -import java.lang.reflect.Type; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -198,7 +197,7 @@ public class JbootConfigManager { } if (ConfigUtil.isNotBlank(value)) { - Object val = convert(method.getParameterTypes()[0], value, method.getGenericParameterTypes()[0]); + Object val = ConfigUtil.convert(method.getParameterTypes()[0], value, method.getGenericParameterTypes()[0]); if (val != null) { try { method.invoke(configObject, val); @@ -214,10 +213,6 @@ public class JbootConfigManager { } - public Object convert(Class clazz, String s, Type genericType) { - return ConfigUtil.convert(clazz, s, genericType); - } - private String buildKey(String prefix, Method method) { String key = ConfigUtil.firstCharToLowerCase(method.getName().substring(3)); if (ConfigUtil.isNotBlank(prefix)) { -- Gitee From 856a1e9d70c4fac62490d63d3f274ac11994b23b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Apr 2020 16:02:09 +0800 Subject: [PATCH 0285/1965] add onStartFinish() method in JbootAppListener.class --- src/main/java/io/jboot/core/JbootCoreConfig.java | 3 +++ .../java/io/jboot/core/listener/JbootAppListener.java | 2 ++ .../io/jboot/core/listener/JbootAppListenerBase.java | 5 +++++ .../jboot/core/listener/JbootAppListenerManager.java | 11 +++++++++++ 4 files changed, 21 insertions(+) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 227e25c2..99bb3faa 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -296,6 +296,9 @@ public class JbootCoreConfig extends JFinalConfig { JbootGatewayManager.me().init(); JbootAppListenerManager.me().onStart(); + + //使用场景:需要等所有组件 onStart() 完成之后,再去执行某些工作的时候 + JbootAppListenerManager.me().onStartFinish(); } @Override diff --git a/src/main/java/io/jboot/core/listener/JbootAppListener.java b/src/main/java/io/jboot/core/listener/JbootAppListener.java index fe3c4984..21a0ccda 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListener.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListener.java @@ -46,6 +46,8 @@ public interface JbootAppListener { public void onStart(); + public void onStartFinish(); + public void onStop(); } diff --git a/src/main/java/io/jboot/core/listener/JbootAppListenerBase.java b/src/main/java/io/jboot/core/listener/JbootAppListenerBase.java index f6248492..8649bfd1 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListenerBase.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListenerBase.java @@ -75,6 +75,11 @@ public class JbootAppListenerBase implements JbootAppListener { } + @Override + public void onStartFinish() { + + } + @Override public void onStop() { diff --git a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java index 63301b2b..0eab085b 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java @@ -174,6 +174,17 @@ public class JbootAppListenerManager implements JbootAppListener { } } + @Override + public void onStartFinish() { + for (JbootAppListener listener : listeners) { + try { + listener.onStartFinish(); + } catch (Throwable ex) { + log.error(ex.toString(), ex); + } + } + } + @Override public void onStop() { for (JbootAppListener listener : listeners) { -- Gitee From 56f008748e34d82f6a914e1b0fe50888ebb33608 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Apr 2020 16:04:05 +0800 Subject: [PATCH 0286/1965] add onStartFinish() method in JbootAppListener.class --- src/test/java/io/jboot/test/app/TestAppListener.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/io/jboot/test/app/TestAppListener.java b/src/test/java/io/jboot/test/app/TestAppListener.java index 53cf40b0..ecaf72bf 100644 --- a/src/test/java/io/jboot/test/app/TestAppListener.java +++ b/src/test/java/io/jboot/test/app/TestAppListener.java @@ -60,6 +60,11 @@ public class TestAppListener implements JbootAppListener { System.out.println("TestAppListener.onStart"); } + @Override + public void onStartFinish() { + System.out.println("TestAppListener.onStartFinish"); + } + @Override public void onStop() { System.out.println("TestAppListener.onStop"); -- Gitee From cc4c27e672b8c9c664037567b2c7039ddc63f678 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Apr 2020 17:44:30 +0800 Subject: [PATCH 0287/1965] v3.1.9 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 12 ++++++++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 406d9809..126fe132 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.1.8 + 3.1.9 ``` diff --git a/changes.txt b/changes.txt index b5c9ada9..854455da 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,15 @@ +jboot v3.1.9: +新增:Jboot.configValue(key,default) 方法 +新增:JbootAppListener.onStartFinish()方法,用于不同的 Module 在 onStart 进行操作。 +修复:当在配置文件中配置的内容为 '{' 字符开头的时候会出现 ArrayIndexOutOfBoundsException 异常的问题 +优化:升级 Seata 到 v1.2.0 最新版本 +优化:移除 rpc 服务暴露成功后的日志输出 +优化:统一 JbootCron4jPlugin.addTask() 中的 deamon 参数默认为 false +优化:重命名 app/config/Utils 为 ConfigUtil +优化:重构 Restructure AnnotationUtil.get(),以便支持更加灵活的参数配置 + + + jboot v3.1.8: 新增:Gateway 新增动态注册路 和 移除由配置的功能 新增:Gateway 被 Sentinel 拦截后自定义返回 Json 的功能 diff --git a/doc/docs/install.md b/doc/docs/install.md index a7643acc..aefa2699 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.1.8 + 3.1.9 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 3a7c2923..1a082abf 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.1.8 + 3.1.9 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 9b77c59d..082750b0 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.1.8"; + public static String VERSION = "3.1.9"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From c4f48ac9ff3fb77ee7b98344c28eff22f41d8ad4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Apr 2020 17:44:52 +0800 Subject: [PATCH 0288/1965] v3.1.9 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0074b758..e7f9c2f8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.9-SNAPSHOT + 3.1.9 jar jboot -- Gitee From 9b5be5356edccc3d0da1b610afdb292fb3826f44 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Apr 2020 17:54:36 +0800 Subject: [PATCH 0289/1965] v3.2.0 SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e7f9c2f8..09c564f2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.1.9 + 3.2.0-SNAPSHOT jar jboot -- Gitee From 95ffc68cfd952d399d2abb40b0d8da46a8f7c09c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 28 Apr 2020 18:39:43 +0800 Subject: [PATCH 0290/1965] update config docs --- doc/docs/config.md | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/doc/docs/config.md b/doc/docs/config.md index e490e744..3c7a5a8c 100644 --- a/doc/docs/config.md +++ b/doc/docs/config.md @@ -7,6 +7,7 @@ - 描述 - 读取配置 - 注入配置 +- 动态配置 - 注解配置 - 配置实体类 - 开启 Nacos 分布式配置中心 @@ -61,6 +62,39 @@ public class AopController extends JbootController { } ``` +## 动态配置 +在 Jboot 的所有配置中,我们可以通过 ${key} 来指定替换为 value。 + +示例1: + +```xml +key1 = value1 +key2 = ${key1}/abc +``` + +那么读取到的 key2 的值为 `value1/abc`。 + +示例2: + +```xml +key1 = value1 +key2 = ${key1}/abc +key3 = abc/${key2}/xyz +``` +那么,key2 的值为 `value1/abc` ,key3 的值为 `abc/value1/abc/xyz` + + +示例2: + +```xml +key1 = value1 +key2 = ${otherkey}/abc +``` +那么,因为系统中找不到 otherkey 的值,key2 的值为 `/abc`,如果我们在系统中,通过 `java -jar xxx.jar --otherkey=othervalue`, +那么, key2 的值为 `othervalue/abc` + + + ## 注解配置 在应用开发中,我们通常会使用注解,Jboot 内置了多个注解。 @@ -95,7 +129,7 @@ public class UserServiceProvider extends UserService{ 例如: ```java -@RequestMapping("${user.mapping}") +@RequestMapping("${user.mapping}/abc") public class UserController extends Controller{ //.... } @@ -110,7 +144,7 @@ user.mapping = /user 其作用是等效于: ```java -@RequestMapping("/user") +@RequestMapping("/use/abcr") public class UserController extends Controller{ //.... } -- Gitee From 9e299de2d8745995c9a6c82de73652c92803d3d7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 2 May 2020 14:21:31 +0800 Subject: [PATCH 0291/1965] upgrade dependencies --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 09c564f2..4ef40cf8 100644 --- a/pom.xml +++ b/pom.xml @@ -46,8 +46,8 @@ 2.0 2.5 3.3.0 - 3.4.2 - 2.10.3 + 3.4.3 + 2.11.0 5.1.48 2.0.29.Final 1.7.30 @@ -58,13 +58,13 @@ 2.10.2 1.13.1 2.10.6 - 2.8.1 + 2.8.2 3.10 2.6 1.14 4.5.12 4.4.13 - 4.1.48.Final + 4.1.49.Final 1.7.2 2.7.6 3.11.0.Final @@ -592,7 +592,7 @@ net.oschina.j2cache j2cache-core - 2.8.1-release + 2.8.2-release provided -- Gitee From c42743715f0c894183d828660aa585aa823bcdb4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 7 May 2020 13:44:31 +0800 Subject: [PATCH 0292/1965] add GatewayInvocation.getProxy() support --- .../components/gateway/GatewayHttpProxy.java | 9 +++++++++ .../components/gateway/GatewayInterceptor.java | 4 +++- .../components/gateway/GatewayInvocation.java | 17 +++++++++-------- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index 33d8f128..be182138 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -45,6 +45,7 @@ public class GatewayHttpProxy { private int connectTimeOut; private int retries; private String contentType; + private Exception exception; public GatewayHttpProxy(JbootGatewayConfig config) { this.readTimeOut = config.getProxyReadTimeout(); @@ -68,6 +69,7 @@ public class GatewayHttpProxy { } while (exception != null && triesCount-- > 0); if (exception != null) { + this.exception = exception; LOG.error(exception.toString(), exception); } } @@ -277,4 +279,11 @@ public class GatewayHttpProxy { }; + public Exception getException() { + return exception; + } + + public boolean hasException() { + return exception != null; + } } diff --git a/src/main/java/io/jboot/components/gateway/GatewayInterceptor.java b/src/main/java/io/jboot/components/gateway/GatewayInterceptor.java index 0cf758ab..457af4f2 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayInterceptor.java +++ b/src/main/java/io/jboot/components/gateway/GatewayInterceptor.java @@ -19,6 +19,8 @@ package io.jboot.components.gateway; * GatewayInterceptor. */ public interface GatewayInterceptor { - void intercept(GatewayInvocation inv); + + void intercept(GatewayInvocation inv); + } diff --git a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java index 29bdfeb6..cb509a62 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java +++ b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java @@ -28,6 +28,7 @@ public class GatewayInvocation { private GatewayInterceptor[] inters; private HttpServletRequest request; private HttpServletResponse response; + private GatewayHttpProxy proxy; private int index = 0; @@ -36,6 +37,7 @@ public class GatewayInvocation { this.config = config; this.request = request; this.response = response; + this.proxy = new GatewayHttpProxy(config); this.inters = config.buildInterceptors(); } @@ -47,18 +49,13 @@ public class GatewayInvocation { if (index < inters.length) { inters[index++].intercept(this); } else if (index++ >= inters.length) { - try { - doInvoke(); - } catch (Exception e) { - throw e; - } + doInvoke(); } } - protected void doInvoke() { + protected void doInvoke(){ Runnable runnable = () -> { - new GatewayHttpProxy(config) - .sendRequest(GatewayUtil.buildProxyUrl(config, request), request, response); + proxy.sendRequest(GatewayUtil.buildProxyUrl(config, request), request, response); }; if (config.isSentinelEnable()) { new GatewaySentinelProcesser().process(runnable, config, request, response); @@ -107,4 +104,8 @@ public class GatewayInvocation { public void setIndex(int index) { this.index = index; } + + public GatewayHttpProxy getProxy() { + return proxy; + } } -- Gitee From e1ba376e64fd12227ef57cf88bc56cb3594f3ec8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 7 May 2020 13:47:21 +0800 Subject: [PATCH 0293/1965] add GatewayInvocation.hasException() method --- .../java/io/jboot/components/gateway/GatewayHttpProxy.java | 3 --- .../java/io/jboot/components/gateway/GatewayInvocation.java | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index be182138..d0fd418d 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -283,7 +283,4 @@ public class GatewayHttpProxy { return exception; } - public boolean hasException() { - return exception != null; - } } diff --git a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java index cb509a62..27810d5c 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java +++ b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java @@ -108,4 +108,8 @@ public class GatewayInvocation { public GatewayHttpProxy getProxy() { return proxy; } + + public boolean hasException() { + return proxy.getException() != null; + } } -- Gitee From f5a20fa7c62d2bc62997aa517024791da7145295 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 15 May 2020 09:38:25 +0800 Subject: [PATCH 0294/1965] upgrade dependencies --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4ef40cf8..8219ced4 100644 --- a/pom.xml +++ b/pom.xml @@ -46,7 +46,7 @@ 2.0 2.5 3.3.0 - 3.4.3 + 3.4.5 2.11.0 5.1.48 2.0.29.Final @@ -70,7 +70,7 @@ 3.11.0.Final 1.2.0 1.1.8 - 4.1.6 + 4.1.7 1.8 1.8 -- Gitee From 1898170a77f9babedd6db9dfb96673ba9151bff1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 20 May 2020 09:12:11 +0800 Subject: [PATCH 0295/1965] upgrade dubbo --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8219ced4..87f2bba1 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,7 @@ 4.4.13 4.1.49.Final 1.7.2 - 2.7.6 + 2.7.7 3.11.0.Final 1.2.0 1.1.8 -- Gitee From 7757536edf367bb88105e112296ee5a0e58dbd30 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 20 May 2020 13:34:00 +0800 Subject: [PATCH 0296/1965] =?UTF-8?q?add=20getParaToBigInteger()=E3=80=81g?= =?UTF-8?q?etParaToBigDecimal()=20methods=20in=20controller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/web/controller/JbootController.java | 121 +++++++++++++++++- 1 file changed, 120 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index fb1ad960..801bbe8e 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -17,13 +17,18 @@ package io.jboot.web.controller; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.jfinal.core.ActionException; import com.jfinal.core.Controller; import com.jfinal.core.NotAction; import com.jfinal.kit.JsonKit; +import com.jfinal.kit.StrKit; +import com.jfinal.render.RenderManager; import io.jboot.support.jwt.JwtManager; import io.jboot.utils.RequestUtil; import io.jboot.utils.StrUtil; +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; @@ -182,7 +187,7 @@ public class JbootController extends Controller { @NotAction - public String getCurrentUrl(){ + public String getCurrentUrl() { return RequestUtil.getCurrentUrl(getRequest()); } @@ -268,4 +273,118 @@ public class JbootController extends Controller { return StrUtil.escapeHtml(value); } + + private BigInteger toBigInteger(String value, BigInteger defaultValue) { + try { + if (StrKit.isBlank(value)) { + return defaultValue; + } + value = value.trim(); + if (value.startsWith("N") || value.startsWith("n")) { + return BigInteger.ZERO.subtract(new BigInteger(value)); + } + return new BigInteger(value); + } catch (Exception e) { + throw new ActionException(400, RenderManager.me().getRenderFactory().getErrorRender(400), "Can not parse the parameter \"" + value + "\" to BigInteger value."); + } + } + + /** + * Returns the value of a request parameter and convert to BigInteger. + * + * @param name a String specifying the name of the parameter + * @return a Integer representing the single value of the parameter + */ + public BigInteger getParaToBigInteger(String name) { + return toBigInteger(getTrimPara(name), null); + } + + /** + * Returns the value of a request parameter and convert to BigInteger with a default value if it is null. + * + * @param name a String specifying the name of the parameter + * @return a Integer representing the single value of the parameter + */ + public BigInteger getParaToBigInteger(String name, BigInteger defaultValue) { + return toBigInteger(getTrimPara(name), defaultValue); + } + + + /** + * Returns the value of a request parameter and convert to BigInteger. + * + * @param name a String specifying the name of the parameter + * @return a Integer representing the single value of the parameter + */ + public BigInteger getBigInteger(String name) { + return toBigInteger(getTrimPara(name), null); + } + + /** + * Returns the value of a request parameter and convert to BigInteger with a default value if it is null. + * + * @param name a String specifying the name of the parameter + * @return a Integer representing the single value of the parameter + */ + public BigInteger getBigInteger(String name, BigInteger defaultValue) { + return toBigInteger(getTrimPara(name), defaultValue); + } + + + private BigDecimal toBigDecimal(String value, BigDecimal defaultValue) { + try { + if (StrKit.isBlank(value)) { + return defaultValue; + } + value = value.trim(); + if (value.startsWith("N") || value.startsWith("n")) { + return BigDecimal.ZERO.subtract(new BigDecimal(value)); + } + return new BigDecimal(value); + } catch (Exception e) { + throw new ActionException(400, RenderManager.me().getRenderFactory().getErrorRender(400), "Can not parse the parameter \"" + value + "\" to BigDecimal value."); + } + } + + /** + * Returns the value of a request parameter and convert to BigDecimal. + * + * @param name a String specifying the name of the parameter + * @return a Integer representing the single value of the parameter + */ + public BigDecimal getParaToBigDecimal(String name) { + return toBigDecimal(getTrimPara(name), null); + } + + /** + * Returns the value of a request parameter and convert to BigDecimal with a default value if it is null. + * + * @param name a String specifying the name of the parameter + * @return a Integer representing the single value of the parameter + */ + public BigDecimal getParaToBigDecimal(String name, BigDecimal defaultValue) { + return toBigDecimal(getTrimPara(name), defaultValue); + } + + + /** + * Returns the value of a request parameter and convert to BigDecimal. + * + * @param name a String specifying the name of the parameter + * @return a Integer representing the single value of the parameter + */ + public BigDecimal getBigDecimal(String name) { + return toBigDecimal(getTrimPara(name), null); + } + + /** + * Returns the value of a request parameter and convert to BigDecimal with a default value if it is null. + * + * @param name a String specifying the name of the parameter + * @return a Integer representing the single value of the parameter + */ + public BigDecimal getBigDecimal(String name, BigDecimal defaultValue) { + return toBigDecimal(getTrimPara(name), defaultValue); + } + } -- Gitee From dd99053c86a70cb09a99e762a29c1047c5c6281b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 24 May 2020 11:47:16 +0800 Subject: [PATCH 0297/1965] 3.2.0 --- src/main/java/io/jboot/web/JbootJson.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index 77223329..9f099130 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -15,8 +15,8 @@ */ package io.jboot.web; -import com.alibaba.fastjson.JSON; import com.jfinal.json.JFinalJson; +import com.jfinal.kit.JsonKit; import com.jfinal.kit.StrKit; import io.jboot.Jboot; @@ -32,26 +32,27 @@ public class JbootJson extends JFinalJson { protected String mapToJson(Map map, int depth) { optimizeMapAttrs(map); - if(isCamelCaseJsonStyleEnable){ + if (isCamelCaseJsonStyleEnable) { return toCamelCase(map, depth); } return map == null || map.isEmpty() ? "null" : super.mapToJson(map, depth); } - private String toCamelCase(Map map, int depth){ + private String toCamelCase(Map map, int depth) { StringBuilder sb = new StringBuilder(); boolean first = true; Iterator iter = map.entrySet().iterator(); sb.append('{'); - while(iter.hasNext()){ - if(first) + while (iter.hasNext()) { + if (first) { first = false; - else + } else { sb.append(','); + } - Map.Entry entry = (Map.Entry)iter.next(); - toKeyValue(StrKit.toCamelCase(String.valueOf(entry.getKey())),entry.getValue(), sb, depth); + Map.Entry entry = (Map.Entry) iter.next(); + toKeyValue(StrKit.toCamelCase(String.valueOf(entry.getKey())), entry.getValue(), sb, depth); } sb.append('}'); return sb.toString(); @@ -80,6 +81,6 @@ public class JbootJson extends JFinalJson { @Override public T parse(String jsonString, Class type) { - return JSON.parseObject(jsonString, type); + return JsonKit.parse(jsonString, type); } } -- Gitee From 0f6962b37b5220c47d8a13631f6bf2254b5860d6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 25 May 2020 14:36:36 +0800 Subject: [PATCH 0298/1965] add multi datasource docs --- doc/docs/db.md | 103 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/doc/docs/db.md b/doc/docs/db.md index 4b86e87a..0a522c2c 100644 --- a/doc/docs/db.md +++ b/doc/docs/db.md @@ -13,6 +13,7 @@ Jboot 数据库功能基于 JFinal 的 ActiveRecordPlugin 插件和 Apache shard - 分页查询 - 批量插入 - 事务操作 +- 多数据源 - 读写分离 - 分库分表 - 分布式事务 @@ -168,6 +169,108 @@ public List findListBy(int userAge,String articleTitle){ } ``` + +## 多数据源 + +默认单数据源的情况下,我们需要在 `jboot.properties` 添加如下配置: + +``` +jboot.datasource.type=mysql +jboot.datasource.url=jdbc:mysql://127.0.0.1:3306/jbootdemo +jboot.datasource.user=root +jboot.datasource.password=your_password +``` + +如果是多个数据源,我们可以在 `jboot.datasource.` 后面添加数据源的名称,例如: + +``` +jboot.datasource.a1.type=mysql +jboot.datasource.a1.turl=jdbc:mysql://127.0.0.1:3306/jboot1 +jboot.datasource.a1.tuser=root +jboot.datasource.a1.tpassword=your_password + +jboot.datasource.a2.type=mysql +jboot.datasource.a2.turl=jdbc:mysql://127.0.0.1:3306/jboot2 +jboot.datasource.a2.tuser=root +jboot.datasource.a2.tpassword=your_password +``` + +这表示,我们又增加了数据源 `a1` 和数据源 `a2`,在使用的时候,我们只需要做一下使用: + +```java +Company company = new Company(); +company.setCid("1"); +company.setName("name"); + +company.use("a1").save(); +``` + +`company.use("a1").save();` 表示使用数据源 `a1` 进行保存。 + + +**需要注意的是:** + +在多数据源应用中,很多时候,我们的一个 Model 只有对应一个数据源,而不是一个 Model 对应多个数据源。假设 `Company` 只有在 `a1` 数据源中存在,在其他数据源并不存在,我们需要把 `a1` 数据源的配置修改如下: + +``` +jboot.datasource.a1.type=mysql +jboot.datasource.a1.url=jdbc:mysql://127.0.0.1:3306/jboot1 +jboot.datasource.a1.user=root +jboot.datasource.a1.password=your_password +jboot.datasource.a1.table=company + +jboot.datasource.a2.type=mysql +jboot.datasource.a2.url=jdbc:mysql://127.0.0.1:3306/jboot2 +jboot.datasource.a2.user=root +jboot.datasource.a2.password=your_password +jboot.datasource.a1.table=user,xxx(其他非company表) +``` + +这样,`company` 在 `a1` 数据源中存在,Jboot在初始化的时候,并不会去检查 `company` 在其他数据源中是否存在,同时,代码操作 `company` 的时候,不再需要 `use()` ,代码如下: +```java +Company company = new Company(); +company.setCid("1"); +company.setName("name"); + +//company.use("a1").save(); +company.save(); +``` + +代码中不再需要 `use("a1")` 指定数据源,因为 `company` 只有一个数据源。 + +更多关于 datasource 的配置如下: + +``` +jboot.datasource.name //数据源名称 +jboot.datasource.type //数据源类型 +jboot.datasource.url //数据源URL地址 +jboot.datasource.user +jboot.datasource.password +jboot.datasource.driverClassName = "com.mysql.jdbc.Driver" +jboot.datasource.connectionInitSql +jboot.datasource.poolName +jboot.datasource.cachePrepStmts = true +jboot.datasource.prepStmtCacheSize = 500 +jboot.datasource.prepStmtCacheSqlLimit = 2048 +jboot.datasource.maximumPoolSize = 10 +jboot.datasource.maxLifetime +jboot.datasource.idleTimeout +jboot.datasource.minimumIdle = 0 +jboot.datasource.sqlTemplatePath // sql 模板存放路径,用到 jfinal sql独立文件的时候 +jboot.datasource.sqlTemplate +jboot.datasource.factory +jboot.datasource.shardingConfigYaml //分库分表的配置文件 +jboot.datasource.dbProFactory +jboot.datasource.containerFactory +jboot.datasource.transactionLevel +jboot.datasource.table //此数据源包含哪些表 +jboot.datasource.exTable //该数据源排除哪些表 +jboot.datasource.dialectClass +jboot.datasource.activeRecordPluginClass +jboot.datasource.needAddMapping = true //是否需要添加到映射,当不添加映射的时候,只能通过 model.use("xxx").save()这种方式去调用该数据源 +``` + + ## 读写分离 在 Jboot 应用中,读写分离建议使用两个数据源,分别是读的数据源和写的数据源,写的数据源必须支持可读可写。 -- Gitee From af66ed24ec5f8ee8708d68ae7f3bafa95bca5037 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 25 May 2020 17:00:38 +0800 Subject: [PATCH 0299/1965] add @NotAction annotation to JbootController methods --- .../java/io/jboot/web/controller/JbootController.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 801bbe8e..1f547951 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -295,6 +295,7 @@ public class JbootController extends Controller { * @param name a String specifying the name of the parameter * @return a Integer representing the single value of the parameter */ + @NotAction public BigInteger getParaToBigInteger(String name) { return toBigInteger(getTrimPara(name), null); } @@ -305,6 +306,7 @@ public class JbootController extends Controller { * @param name a String specifying the name of the parameter * @return a Integer representing the single value of the parameter */ + @NotAction public BigInteger getParaToBigInteger(String name, BigInteger defaultValue) { return toBigInteger(getTrimPara(name), defaultValue); } @@ -316,6 +318,7 @@ public class JbootController extends Controller { * @param name a String specifying the name of the parameter * @return a Integer representing the single value of the parameter */ + @NotAction public BigInteger getBigInteger(String name) { return toBigInteger(getTrimPara(name), null); } @@ -326,6 +329,7 @@ public class JbootController extends Controller { * @param name a String specifying the name of the parameter * @return a Integer representing the single value of the parameter */ + @NotAction public BigInteger getBigInteger(String name, BigInteger defaultValue) { return toBigInteger(getTrimPara(name), defaultValue); } @@ -352,6 +356,7 @@ public class JbootController extends Controller { * @param name a String specifying the name of the parameter * @return a Integer representing the single value of the parameter */ + @NotAction public BigDecimal getParaToBigDecimal(String name) { return toBigDecimal(getTrimPara(name), null); } @@ -362,6 +367,7 @@ public class JbootController extends Controller { * @param name a String specifying the name of the parameter * @return a Integer representing the single value of the parameter */ + @NotAction public BigDecimal getParaToBigDecimal(String name, BigDecimal defaultValue) { return toBigDecimal(getTrimPara(name), defaultValue); } @@ -373,6 +379,7 @@ public class JbootController extends Controller { * @param name a String specifying the name of the parameter * @return a Integer representing the single value of the parameter */ + @NotAction public BigDecimal getBigDecimal(String name) { return toBigDecimal(getTrimPara(name), null); } @@ -383,6 +390,7 @@ public class JbootController extends Controller { * @param name a String specifying the name of the parameter * @return a Integer representing the single value of the parameter */ + @NotAction public BigDecimal getBigDecimal(String name, BigDecimal defaultValue) { return toBigDecimal(getTrimPara(name), defaultValue); } -- Gitee From 605a47899bdde55512801c9c5ca72aa66e92d9c3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 25 May 2020 17:13:31 +0800 Subject: [PATCH 0300/1965] v3.2.0 --- changes.txt | 9 +++++++++ pom.xml | 16 ++++++++-------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/changes.txt b/changes.txt index 854455da..91e97cf2 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,12 @@ +jboot v3.2.0: +新增:JbootController 新增 getParaToBigInteger()、getParaToBigDecimal() 等方法 +新增:门户网关新增 hasException() 方法,用于判断目标地址是否可以正常访问 +优化:升级 JFinal、jackson、HikariCP、Dubbo 等相关依赖到最新版本 +文档:配置相关文档添加动态配置的相关描述 +文档:数据库配置相关添加多数据源的相关描述 + + + jboot v3.1.9: 新增:Jboot.configValue(key,default) 方法 新增:JbootAppListener.onStartFinish()方法,用于不同的 Module 在 onStart 进行操作。 diff --git a/pom.xml b/pom.xml index 87f2bba1..abdcc067 100644 --- a/pom.xml +++ b/pom.xml @@ -49,28 +49,28 @@ 3.4.5 2.11.0 5.1.48 - 2.0.29.Final + 2.1.1.Final 1.7.30 - 2.57 + 3.0.1 1.2.68 29.0-jre 2.2.5 2.10.2 1.13.1 2.10.6 - 2.8.2 + 2.8.4 3.10 2.6 1.14 4.5.12 4.4.13 - 4.1.49.Final + 4.1.50.Final 1.7.2 2.7.7 3.11.0.Final 1.2.0 1.1.8 - 4.1.7 + 4.1.9 1.8 1.8 @@ -352,7 +352,7 @@ io.lettuce lettuce-core - 5.2.2.RELEASE + 5.3.0.RELEASE provided @@ -373,7 +373,7 @@ com.rabbitmq amqp-client - 5.7.3 + 5.9.0 provided @@ -381,7 +381,7 @@ org.apache.shardingsphere sharding-jdbc-core - 4.0.1 + 4.1.0 provided -- Gitee From e4d768249666675da88c4e7b03a649863c2714ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=B2=E4=B8=96=E4=BC=9F?= Date: Mon, 25 May 2020 19:34:11 +0800 Subject: [PATCH 0301/1965] ceshi --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index abdcc067..45836252 100644 --- a/pom.xml +++ b/pom.xml @@ -9,6 +9,7 @@ 3.2.0-SNAPSHOT jar + jboot http://jboot.io Jboot is a similar SpringCloud project base on JFinal, Dubbo and Undertow. -- Gitee From 937cc3bc230a2aad718e5d8dafceedb301169a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=B2=E4=B8=96=E4=BC=9F?= Date: Mon, 25 May 2020 19:38:22 +0800 Subject: [PATCH 0302/1965] ceshi --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index 45836252..abdcc067 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,6 @@ 3.2.0-SNAPSHOT jar - jboot http://jboot.io Jboot is a similar SpringCloud project base on JFinal, Dubbo and Undertow. -- Gitee From a791a2d588c88f144845f5378b5bf2a8a6bad224 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 26 May 2020 09:23:22 +0800 Subject: [PATCH 0303/1965] v3.2.0 release (^.^)YYa!! --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index abdcc067..7d422b24 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.0-SNAPSHOT + 3.2.0 jar jboot @@ -41,7 +41,7 @@ - 4.8 + 4.9 2020.4 2.0 2.5 -- Gitee From 56ad8a46e7155c6439fac8a92cca41699ac2c012 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 26 May 2020 09:24:42 +0800 Subject: [PATCH 0304/1965] v3.2.0 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 126fe132..3630e3c2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.1.9 + 3.2.0 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index aefa2699..22799605 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.1.9 + 3.2.0 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 1a082abf..8c717126 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.1.9 + 3.2.0 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 082750b0..05b3be7d 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.1.9"; + public static String VERSION = "3.2.0"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From ad391da676b28dc8cbf6d1d531cddcdbead9f0ff Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 26 May 2020 09:32:53 +0800 Subject: [PATCH 0305/1965] v3.2.0 release (^.^)YYa!! --- pom.xml | 2 +- src/main/java/io/jboot/web/JbootJson.java | 58 ++--------------------- 2 files changed, 6 insertions(+), 54 deletions(-) diff --git a/pom.xml b/pom.xml index 7d422b24..caa8d0fe 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ 5.1.48 2.1.1.Final 1.7.30 - 3.0.1 + 2.57 1.2.68 29.0-jre 2.2.5 diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index 9f099130..a1dfb740 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -15,72 +15,24 @@ */ package io.jboot.web; +import com.alibaba.fastjson.JSON; import com.jfinal.json.JFinalJson; -import com.jfinal.kit.JsonKit; -import com.jfinal.kit.StrKit; import io.jboot.Jboot; -import java.util.Iterator; -import java.util.Map; - public class JbootJson extends JFinalJson { private static boolean isCamelCaseJsonStyleEnable = Jboot.config(JbootWebConfig.class).isCamelCaseJsonStyleEnable(); - @Override - protected String mapToJson(Map map, int depth) { - optimizeMapAttrs(map); - + public JbootJson() { + setSkipNullValueField(true); if (isCamelCaseJsonStyleEnable) { - return toCamelCase(map, depth); - } - return map == null || map.isEmpty() ? "null" : super.mapToJson(map, depth); - } - - private String toCamelCase(Map map, int depth) { - StringBuilder sb = new StringBuilder(); - boolean first = true; - Iterator iter = map.entrySet().iterator(); - - sb.append('{'); - while (iter.hasNext()) { - if (first) { - first = false; - } else { - sb.append(','); - } - - Map.Entry entry = (Map.Entry) iter.next(); - toKeyValue(StrKit.toCamelCase(String.valueOf(entry.getKey())), entry.getValue(), sb, depth); + setModelAndRecordFieldNameToCamelCase(); } - sb.append('}'); - return sb.toString(); } - - /** - * 优化 map 的属性 - * - * @param map - */ - private void optimizeMapAttrs(Map map) { - if (map == null || map.isEmpty()) { - return; - } - Iterator iter = map.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = (Map.Entry) iter.next(); - //移除 null 值的属性 - if (entry.getValue() == null) { - iter.remove(); - } - } - } - - @Override public T parse(String jsonString, Class type) { - return JsonKit.parse(jsonString, type); + return JSON.parseObject(jsonString, type); } } -- Gitee From 6da363e24006bb3760a8ac743949abbaa3561c57 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 26 May 2020 14:21:51 +0800 Subject: [PATCH 0306/1965] v3.2.1 snapshot --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index caa8d0fe..ef69a858 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.0 + 3.2.1-SNAPSHOT jar jboot @@ -43,7 +43,7 @@ 4.9 2020.4 - 2.0 + 2.1 2.5 3.3.0 3.4.5 -- Gitee From 87bb6ef9f46bed7f748f8967b311506bb222c6d0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 26 May 2020 14:42:14 +0800 Subject: [PATCH 0307/1965] add docs for rpc restful export --- doc/docs/rpc.md | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/doc/docs/rpc.md b/doc/docs/rpc.md index af1e4c5c..5a725133 100644 --- a/doc/docs/rpc.md +++ b/doc/docs/rpc.md @@ -9,6 +9,7 @@ - 添加依赖 - 配置 - 开始使用 +- restful 暴露 - 高级功能 ## 添加依赖 @@ -207,6 +208,90 @@ public class DubboClient extends JbootController{ ``` +## restful 暴露 + +在某些情况下,我们希望 rpc service 通过 restful 协议暴露给其他客户端(或者其他编程语言)去使用,我们需要添加如下的依赖。 + +PS:目前只有 dubbo 支持了 restful 协议,其他 rpc 框架暂时不支持。 + + +```xml + + + org.jboss.resteasy + resteasy-jdk-http + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-jaxrs + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-client + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-netty4 + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-jackson-provider + ${resteasy.version} + + +``` + +第二步需要在 接口添加 相关注解 + +```java +@Path("users") // #1 +@Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) // #2 +@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) +public interface UserService { + @GET // #3 + @Path("{id: \\d+}") + User getUser(@PathParam("id") Long id); + + @POST // #4 + @Path("register") + Long registerUser(User user); +} +``` + +具体参考:http://dubbo.apache.org/zh-cn/blog/dubbo-rest.html + + +第三步,在 jboot.properties 添加 restful 协议: + +``` +jboot.rpc.dubbo.protocol.name = dubbo +jboot.rpc.dubbo.protocol.host = 127.0.0.1 +jboot.rpc.dubbo.protocol.port = 28080 +``` + +jboot.rpc.dubbo.protocol.rest.name = rest +jboot.rpc.dubbo.protocol.rest.host = 127.0.0.1 +jboot.rpc.dubbo.protocol.rest.port = 8080 +jboot.rpc.dubbo.protocol.rest.server = netty +``` + +第四步:给 Service 配置暴露协议 + +``` +jboot.rpc.dubbo.provider.protocal = default,rest //使用 dubbo 和 rest 两种协议同时暴露 +jboot.rpc.dubbo.provider.default = true // 给应用配置默认的 provider +``` + + + ## 高级功能 ### 更多的 dubbo 配置 -- Gitee From 64750f10b14e55f88dd84faeed841480353ff155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=B2=E4=B8=96=E4=BC=9F?= Date: Tue, 26 May 2020 16:47:39 +0800 Subject: [PATCH 0308/1965] =?UTF-8?q?druid=20=E6=95=B0=E6=8D=AE=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/db/datasource/DataSourceConfig.java | 88 +++++++++++++++++++ .../db/datasource/DruidDataSourceFactory.java | 9 +- 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java index 8c0b4e12..efe9f40a 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java @@ -15,6 +15,7 @@ */ package io.jboot.db.datasource; +import com.alibaba.druid.pool.DruidDataSource; import com.jfinal.plugin.activerecord.DbKit; import io.jboot.utils.StrUtil; @@ -46,6 +47,20 @@ public class DataSourceConfig { private Long idleTimeout; private Integer minimumIdle = 0; + // 配置获取连接等待超时的时间 + private long maxWait = DruidDataSource.DEFAULT_MAX_WAIT; + + // 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + private long timeBetweenEvictionRunsMillis = DruidDataSource.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; + // 配置连接在池中最小生存的时间 + private long minEvictableIdleTimeMillis = DruidDataSource.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; + // 配置发生错误时多久重连 + private long timeBetweenConnectErrorMillis = DruidDataSource.DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS; + private String validationQuery = "select 1"; + private boolean testWhileIdle = true; + private boolean testOnBorrow = false; + private boolean testOnReturn = false; + private String sqlTemplatePath; private String sqlTemplate; private String factory; //HikariDataSourceFactory.class.getName(); @@ -310,4 +325,77 @@ public class DataSourceConfig { public void setActiveRecordPluginClass(String activeRecordPluginClass) { this.activeRecordPluginClass = activeRecordPluginClass; } + + public long getMaxWait() { + return maxWait; + } + + public void setMaxWait(long maxWait) { + this.maxWait = maxWait; + } + + public long getTimeBetweenEvictionRunsMillis() { + return timeBetweenEvictionRunsMillis; + } + + public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) { + this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; + } + + public long getMinEvictableIdleTimeMillis() { + return minEvictableIdleTimeMillis; + } + + public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) { + this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; + } + + public long getTimeBetweenConnectErrorMillis() { + return timeBetweenConnectErrorMillis; + } + + public void setTimeBetweenConnectErrorMillis(long timeBetweenConnectErrorMillis) { + this.timeBetweenConnectErrorMillis = timeBetweenConnectErrorMillis; + } + + public String getValidationQuery() { + if(this.url.startsWith("jdbc:oracle")){ + return "select 1 from dual"; + }else if(this.url.startsWith("jdbc:db2")){ + return "select 1 from sysibm.sysdummy1"; + }else if(this.url.startsWith("jdbc:hsqldb")){ + return "select 1 from INFORMATION_SCHEMA.SYSTEM_USERS"; + }else if(this.url.startsWith("jdbc:derby")){ + return "select 1 from INFORMATION_SCHEMA.SYSTEM_USERS"; + } + return "select 1"; + } + + public void setValidationQuery(String validationQuery) { + this.validationQuery = validationQuery; + } + + public boolean isTestWhileIdle() { + return testWhileIdle; + } + + public void setTestWhileIdle(boolean testWhileIdle) { + this.testWhileIdle = testWhileIdle; + } + + public boolean isTestOnBorrow() { + return testOnBorrow; + } + + public void setTestOnBorrow(boolean testOnBorrow) { + this.testOnBorrow = testOnBorrow; + } + + public boolean isTestOnReturn() { + return testOnReturn; + } + + public void setTestOnReturn(boolean testOnReturn) { + this.testOnReturn = testOnReturn; + } } diff --git a/src/main/java/io/jboot/db/datasource/DruidDataSourceFactory.java b/src/main/java/io/jboot/db/datasource/DruidDataSourceFactory.java index 871bd8a5..63ec8343 100644 --- a/src/main/java/io/jboot/db/datasource/DruidDataSourceFactory.java +++ b/src/main/java/io/jboot/db/datasource/DruidDataSourceFactory.java @@ -40,7 +40,14 @@ public class DruidDataSourceFactory implements DataSourceFactory { druidDataSource.setPassword(config.getPassword()); druidDataSource.setDriverClassName(config.getDriverClassName()); druidDataSource.setMaxActive(config.getMaximumPoolSize()); - + druidDataSource.setMaxWait(config.getMaxWait()); + druidDataSource.setTimeBetweenEvictionRunsMillis(config.getTimeBetweenEvictionRunsMillis()); + druidDataSource.setMinEvictableIdleTimeMillis(config.getMinEvictableIdleTimeMillis()); + druidDataSource.setTimeBetweenConnectErrorMillis(config.getTimeBetweenConnectErrorMillis()); + druidDataSource.setValidationQuery(config.getValidationQuery()); + druidDataSource.setTestWhileIdle(config.isTestWhileIdle()); + druidDataSource.setTestOnBorrow(config.isTestOnBorrow()); + druidDataSource.setTestOnReturn(config.isTestOnReturn()); if (config.getMinimumIdle() != null) { druidDataSource.setMinIdle(config.getMinimumIdle()); } -- Gitee From a17d541ecc6d3ebe42e0b59f4b2fbcf430712ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=B2=E4=B8=96=E4=BC=9F?= Date: Tue, 26 May 2020 19:37:38 +0800 Subject: [PATCH 0309/1965] =?UTF-8?q?druid=20=E6=95=B0=E6=8D=AE=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/db/datasource/DataSourceConfig.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java index efe9f40a..ba15d55f 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java @@ -15,7 +15,6 @@ */ package io.jboot.db.datasource; -import com.alibaba.druid.pool.DruidDataSource; import com.jfinal.plugin.activerecord.DbKit; import io.jboot.utils.StrUtil; @@ -48,14 +47,14 @@ public class DataSourceConfig { private Integer minimumIdle = 0; // 配置获取连接等待超时的时间 - private long maxWait = DruidDataSource.DEFAULT_MAX_WAIT; + private long maxWait = -1; // 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 - private long timeBetweenEvictionRunsMillis = DruidDataSource.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; + private long timeBetweenEvictionRunsMillis = 60 * 1000L; // 配置连接在池中最小生存的时间 - private long minEvictableIdleTimeMillis = DruidDataSource.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; + private long minEvictableIdleTimeMillis = 1000L * 60L * 30L; // 配置发生错误时多久重连 - private long timeBetweenConnectErrorMillis = DruidDataSource.DEFAULT_TIME_BETWEEN_CONNECT_ERROR_MILLIS; + private long timeBetweenConnectErrorMillis = 500; private String validationQuery = "select 1"; private boolean testWhileIdle = true; private boolean testOnBorrow = false; -- Gitee From 1f52a9732f8c4ae46350c36bdbd70fa26ad0f896 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 27 May 2020 10:21:30 +0800 Subject: [PATCH 0310/1965] add docs for rpc restful export --- doc/docs/rpc.md | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/doc/docs/rpc.md b/doc/docs/rpc.md index 5a725133..aeaebc00 100644 --- a/doc/docs/rpc.md +++ b/doc/docs/rpc.md @@ -218,37 +218,56 @@ PS:目前只有 dubbo 支持了 restful 协议,其他 rpc 框架暂时不支 ```xml - org.jboss.resteasy - resteasy-jdk-http - ${resteasy.version} + io.netty + netty-all + ${io.netty.version} org.jboss.resteasy resteasy-jaxrs - ${resteasy.version} + ${org.jboss.resteasy.version} org.jboss.resteasy resteasy-client - ${resteasy.version} + ${org.jboss.resteasy.version} org.jboss.resteasy resteasy-netty4 - ${resteasy.version} + ${org.jboss.resteasy.version} + + + + javax.validation + validation-api + 1.1.0.Final org.jboss.resteasy resteasy-jackson-provider - ${resteasy.version} + ${org.jboss.resteasy.version} + + + + org.jboss.resteasy + resteasy-jaxb-provider + ${org.jboss.resteasy.version} ``` +其中版本号对应为 + +```xml +4.1.9.Final +3.9.0.Final +``` + 第二步需要在 接口添加 相关注解 ```java @@ -266,7 +285,7 @@ public interface UserService { } ``` -具体参考:http://dubbo.apache.org/zh-cn/blog/dubbo-rest.html +这部分的内容,可以添加在接口里,也可以添加在实现类里,具体参考:http://dubbo.apache.org/zh-cn/blog/dubbo-rest.html 第三步,在 jboot.properties 添加 restful 协议: -- Gitee From 67cc3973eb5eb24470fbf39bdf3467845140dbbd Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 27 May 2020 16:54:59 +0800 Subject: [PATCH 0311/1965] optimize : add logger print without slf4j config --- .../java/io/jboot/core/log/JdkLogger.java | 221 ++++++++++++++++++ .../io/jboot/core/log/Slf4jLogFactory.java | 22 +- 2 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 src/main/java/io/jboot/core/log/JdkLogger.java diff --git a/src/main/java/io/jboot/core/log/JdkLogger.java b/src/main/java/io/jboot/core/log/JdkLogger.java new file mode 100644 index 00000000..db29bec3 --- /dev/null +++ b/src/main/java/io/jboot/core/log/JdkLogger.java @@ -0,0 +1,221 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.core.log; + +import com.jfinal.log.Log; +import com.jfinal.log.LogInfo; +import io.jboot.exception.JbootExceptionHolder; + +import java.util.logging.Level; + +/** + * JdkLogger from JdkLog. + */ +public class JdkLogger extends Log { + + private java.util.logging.Logger log; + private String clazzName; + + JdkLogger(Class clazz) { + log = java.util.logging.Logger.getLogger(clazz.getName()); + clazzName = clazz.getName(); + } + + JdkLogger(String name) { + log = java.util.logging.Logger.getLogger(name); + clazzName = name; + } + + @Override + public void trace(String message) { + log.logp(Level.FINEST, clazzName, Thread.currentThread().getStackTrace()[1].getMethodName(), message); + } + + @Override + public void trace(String message, Throwable t) { + log.logp(Level.FINEST, clazzName, Thread.currentThread().getStackTrace()[1].getMethodName(), message, t); + } + + @Override + public void debug(String message) { + log.logp(Level.FINE, clazzName, Thread.currentThread().getStackTrace()[1].getMethodName(), message); + } + + @Override + public void debug(String message, Throwable t) { + log.logp(Level.FINE, clazzName, Thread.currentThread().getStackTrace()[1].getMethodName(), message, t); + } + + @Override + public void info(String message) { + log.logp(Level.INFO, clazzName, Thread.currentThread().getStackTrace()[1].getMethodName(), message); + } + + @Override + public void info(String message, Throwable t) { + log.logp(Level.INFO, clazzName, Thread.currentThread().getStackTrace()[1].getMethodName(), message, t); + } + + @Override + public void warn(String message) { + log.logp(Level.WARNING, clazzName, Thread.currentThread().getStackTrace()[1].getMethodName(), message); + } + + @Override + public void warn(String message, Throwable t) { + log.logp(Level.WARNING, clazzName, Thread.currentThread().getStackTrace()[1].getMethodName(), message, t); + } + + @Override + public void error(String message) { + log.logp(Level.SEVERE, clazzName, Thread.currentThread().getStackTrace()[1].getMethodName(), message); + } + + @Override + public void error(String message, Throwable t) { + JbootExceptionHolder.hold(message, t); + log.logp(Level.SEVERE, clazzName, Thread.currentThread().getStackTrace()[1].getMethodName(), message, t); + } + + /** + * JdkLog fatal is the same as the error. + */ + @Override + public void fatal(String message) { + log.logp(Level.SEVERE, clazzName, Thread.currentThread().getStackTrace()[1].getMethodName(), message); + } + + /** + * JdkLog fatal is the same as the error. + */ + @Override + public void fatal(String message, Throwable t) { + JbootExceptionHolder.hold(message, t); + log.logp(Level.SEVERE, clazzName, Thread.currentThread().getStackTrace()[1].getMethodName(), message, t); + } + + @Override + public boolean isTraceEnabled() { + return log.isLoggable(Level.FINEST); + } + + @Override + public boolean isDebugEnabled() { + return log.isLoggable(Level.FINE); + } + + @Override + public boolean isInfoEnabled() { + return log.isLoggable(Level.INFO); + } + + @Override + public boolean isWarnEnabled() { + return log.isLoggable(Level.WARNING); + } + + @Override + public boolean isErrorEnabled() { + return log.isLoggable(Level.SEVERE); + } + + @Override + public boolean isFatalEnabled() { + return log.isLoggable(Level.SEVERE); + } + + // ------------------------------------------------------- + + /* + * 以下方法与前面的两个 trace 方法必须覆盖父类中的实现,否则日志中的类名为 + * com.jfinal.log.Log 而非所需要的日志发生地点的类名 + */ + + @Override + public void trace(String format, Object... args) { + if (isTraceEnabled()) { + if (endsWithThrowable(args)) { + LogInfo li = parse(format, args); + trace(li.message, li.throwable); + } else { + trace(String.format(format, args)); + } + } + } + + @Override + public void debug(String format, Object... args) { + if (isDebugEnabled()) { + if (endsWithThrowable(args)) { + LogInfo li = parse(format, args); + debug(li.message, li.throwable); + } else { + debug(String.format(format, args)); + } + } + } + + @Override + public void info(String format, Object... args) { + if (isInfoEnabled()) { + if (endsWithThrowable(args)) { + LogInfo li = parse(format, args); + info(li.message, li.throwable); + } else { + info(String.format(format, args)); + } + } + } + + @Override + public void warn(String format, Object... args) { + if (isWarnEnabled()) { + if (endsWithThrowable(args)) { + LogInfo li = parse(format, args); + warn(li.message, li.throwable); + } else { + warn(String.format(format, args)); + } + } + } + + @Override + public void error(String format, Object... args) { + if (isErrorEnabled()) { + if (endsWithThrowable(args)) { + LogInfo li = parse(format, args); + error(li.message, li.throwable); + } else { + error(String.format(format, args)); + } + } + } + + @Override + public void fatal(String format, Object... args) { + if (isFatalEnabled()) { + if (endsWithThrowable(args)) { + LogInfo li = parse(format, args); + fatal(li.message, li.throwable); + } else { + fatal(String.format(format, args)); + } + } + } +} + + + diff --git a/src/main/java/io/jboot/core/log/Slf4jLogFactory.java b/src/main/java/io/jboot/core/log/Slf4jLogFactory.java index 87b760d7..0aefb027 100644 --- a/src/main/java/io/jboot/core/log/Slf4jLogFactory.java +++ b/src/main/java/io/jboot/core/log/Slf4jLogFactory.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -18,6 +18,7 @@ package io.jboot.core.log; import com.jfinal.log.Log; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.helpers.NOPLoggerFactory; import org.slf4j.spi.LocationAwareLogger; /** @@ -25,16 +26,31 @@ import org.slf4j.spi.LocationAwareLogger; */ public class Slf4jLogFactory extends com.jfinal.log.Slf4jLogFactory { + private boolean useJdkLogger; + + public Slf4jLogFactory() { + this.useJdkLogger = LoggerFactory.getILoggerFactory() instanceof NOPLoggerFactory; + } @Override public Log getLog(Class clazz) { + if (useJdkLogger) { + return new JdkLogger(clazz); + } + Logger log = LoggerFactory.getLogger(clazz); - return log instanceof LocationAwareLogger ? new Slf4jLogger((LocationAwareLogger)log) : new Slf4jSimpleLogger(log); + return log instanceof LocationAwareLogger ? new Slf4jLogger((LocationAwareLogger) log) : new Slf4jSimpleLogger(log); } @Override public Log getLog(String name) { + if (useJdkLogger) { + return new JdkLogger(name); + } + Logger log = LoggerFactory.getLogger(name); - return log instanceof LocationAwareLogger ? new Slf4jLogger((LocationAwareLogger)log) : new Slf4jSimpleLogger(log); + return log instanceof LocationAwareLogger ? new Slf4jLogger((LocationAwareLogger) log) : new Slf4jSimpleLogger(log); } + + } -- Gitee From 4f637a22332fc987a4250f8855e8e0c83ff537b7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 27 May 2020 16:56:34 +0800 Subject: [PATCH 0312/1965] optimize : add logger print without slf4j config --- pom.xml | 26 ++++++++++----------- src/test/java/io/jboot/test/HelloWorld.java | 4 ++++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index ef69a858..d0c60867 100644 --- a/pom.xml +++ b/pom.xml @@ -616,19 +616,19 @@ provided - - ch.qos.logback - logback-core - 1.2.3 - provided - - - - ch.qos.logback - logback-classic - 1.2.3 - provided - + + + + + + + + + + + + + diff --git a/src/test/java/io/jboot/test/HelloWorld.java b/src/test/java/io/jboot/test/HelloWorld.java index e2063243..1a5f665a 100644 --- a/src/test/java/io/jboot/test/HelloWorld.java +++ b/src/test/java/io/jboot/test/HelloWorld.java @@ -30,6 +30,10 @@ public class HelloWorld extends JbootController { renderText("hello world"); } + public void ex(){ + throw new NullPointerException("log test"); + } + public static void main(String[] args){ JbootApplication.run(args); } -- Gitee From 4971f2c0d8ec215c2a05050c8b6af628434638a7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 27 May 2020 17:22:26 +0800 Subject: [PATCH 0313/1965] add JbootRedirectRender --- .../web/render/JbootRedirect301Render.java | 31 +++++++ .../jboot/web/render/JbootRedirectRender.java | 92 +++++++++++++++++++ .../jboot/web/render/JbootRenderFactory.java | 20 ++++ 3 files changed, 143 insertions(+) create mode 100644 src/main/java/io/jboot/web/render/JbootRedirect301Render.java create mode 100644 src/main/java/io/jboot/web/render/JbootRedirectRender.java diff --git a/src/main/java/io/jboot/web/render/JbootRedirect301Render.java b/src/main/java/io/jboot/web/render/JbootRedirect301Render.java new file mode 100644 index 00000000..5fbc98a6 --- /dev/null +++ b/src/main/java/io/jboot/web/render/JbootRedirect301Render.java @@ -0,0 +1,31 @@ +package io.jboot.web.render; + +import javax.servlet.http.HttpServletResponse; + +/** + * Redirect301Render. + */ +public class JbootRedirect301Render extends JbootRedirectRender { + + public JbootRedirect301Render(String url) { + super(url); + } + + public JbootRedirect301Render(String url, boolean withQueryString) { + super(url, withQueryString); + } + + @Override + public void render() { + String finalUrl = buildFinalUrl(); + + response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); + response.setHeader("Location", finalUrl); + } +} + + + + + + diff --git a/src/main/java/io/jboot/web/render/JbootRedirectRender.java b/src/main/java/io/jboot/web/render/JbootRedirectRender.java new file mode 100644 index 00000000..bdbda9ff --- /dev/null +++ b/src/main/java/io/jboot/web/render/JbootRedirectRender.java @@ -0,0 +1,92 @@ +package io.jboot.web.render; + +import com.jfinal.kit.StrKit; +import com.jfinal.render.RedirectRender; + +/** + * RedirectRender with status: 302 Found. + * + * + * 注意:使用 nginx 代理实现 https 的场景,解决 https 被重定向到了 http 的问题,需要在 nginx 中添加如下配置: + * proxy_set_header X-Forwarded-Proto $scheme; + * proxy_set_header X-Forwarded-Port $server_port; + * + * + * PS:nginx 将 http 重定向到 https 的配置为: + * proxy_redirect http:// https://; + * 注意: 需要同时支持 http 与 https 的场景不能使用该配置 + * + */ +public class JbootRedirectRender extends RedirectRender { + + public JbootRedirectRender(String url) { + super(url); + } + + public JbootRedirectRender(String url, boolean withQueryString) { + super(url, withQueryString); + } + + @Override + public String buildFinalUrl() { + String ret; + // 如果一个url为/login/connect?goto=http://www.jfinal.com,则有错误 + // ^((https|http|ftp|rtsp|mms)?://)$ ==> indexOf 取值为 (3, 5) + if (contextPath != null && (url.indexOf("://") == -1 || url.indexOf("://") > 5)) { + ret = contextPath + url; + } else { + ret = url; + } + + if (withQueryString) { + String queryString = request.getQueryString(); + if (queryString != null) { + if (ret.indexOf('?') == -1) { + ret = ret + "?" + queryString; + } else { + ret = ret + "&" + queryString; + } + } + } + + // 跳过 http/https 已指定过协议类型的 url,用于支持跨域名重定向 + if (ret.toLowerCase().startsWith("http")) { + return ret; + } + + /** + * 注意:nginx 代理 https 的场景,需要使用如下配置: + * proxy_set_header X-Forwarded-Proto $scheme; + * proxy_set_header X-Forwarded-Port $server_port; + */ + if ("https".equalsIgnoreCase(request.getHeader("X-Forwarded-Proto"))) { + String serverName = request.getServerName(); + + /** + * 获取 nginx 端通过配置 proxy_set_header X-Forwarded-Port $server_port; + * 传递过来的端口号,保障重定向时端口号是正确的 + */ + String port = request.getHeader("X-Forwarded-Port"); + if (StrKit.notBlank(port)) { + serverName = serverName + ":" + port; + } + + if (ret.charAt(0) != '/') { + return "https://" + serverName + "/" + ret; + } else { + return "https://" + serverName + ret; + } + + } else { + return ret; + } + } +} + + + + + + + + diff --git a/src/main/java/io/jboot/web/render/JbootRenderFactory.java b/src/main/java/io/jboot/web/render/JbootRenderFactory.java index 5d85878b..374d3f66 100644 --- a/src/main/java/io/jboot/web/render/JbootRenderFactory.java +++ b/src/main/java/io/jboot/web/render/JbootRenderFactory.java @@ -105,6 +105,26 @@ public class JbootRenderFactory extends RenderFactory { } + @Override + public Render getRedirectRender(String url) { + return new JbootRedirectRender(url); + } + + @Override + public Render getRedirectRender(String url, boolean withQueryString) { + return new JbootRedirectRender(url, withQueryString); + } + + @Override + public Render getRedirect301Render(String url) { + return new JbootRedirect301Render(url); + } + + @Override + public Render getRedirect301Render(String url, boolean withQueryString) { + return new JbootRedirect301Render(url, withQueryString); + } + public Render getReturnValueRender(Action action, Object returnValue) { return new JbootReturnValueRender(action, returnValue); } -- Gitee From c0c5efceda1f6f707ef77ba8b2affdffb4743807 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 27 May 2020 18:11:07 +0800 Subject: [PATCH 0314/1965] remove UrlParaValidate --- .../web/validate/ParaValidateInterceptor.java | 26 ------------ .../jboot/web/validate/UrlParaValidate.java | 40 ------------------- .../test/validate/ValidateController.java | 12 +----- 3 files changed, 1 insertion(+), 77 deletions(-) delete mode 100644 src/main/java/io/jboot/web/validate/UrlParaValidate.java diff --git a/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java b/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java index 1413e51b..c918f2ac 100644 --- a/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java @@ -39,11 +39,6 @@ public class ParaValidateInterceptor implements FixedInterceptor { Method method = inv.getMethod(); - UrlParaValidate urlParaValidate = method.getAnnotation(UrlParaValidate.class); - if (urlParaValidate != null && !validateUrlPara(inv, urlParaValidate)) { - return; - } - EmptyValidate emptyParaValidate = method.getAnnotation(EmptyValidate.class); if (emptyParaValidate != null && !validateEmpty(inv, emptyParaValidate)) { @@ -56,27 +51,6 @@ public class ParaValidateInterceptor implements FixedInterceptor { } inv.invoke(); - - } - - private boolean validateUrlPara(Invocation inv, UrlParaValidate urlParaValidate) { - Controller controller = inv.getController(); - if (controller.getPara() != null) { - return true; - } - - - renderError(inv.getController() - , AnnotationUtil.get(urlParaValidate.renderType()) - , null - , AnnotationUtil.get(urlParaValidate.message()) - , AnnotationUtil.get(urlParaValidate.redirectUrl()) - , AnnotationUtil.get(urlParaValidate.htmlPath()) - , urlParaValidate.errorCode() - ); - - - return false; } diff --git a/src/main/java/io/jboot/web/validate/UrlParaValidate.java b/src/main/java/io/jboot/web/validate/UrlParaValidate.java deleted file mode 100644 index 522bb583..00000000 --- a/src/main/java/io/jboot/web/validate/UrlParaValidate.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.web.validate; - -import java.lang.annotation.*; - -/** - * Url Para 非空验证 - * @author michael yang - */ -@Documented -@Target(ElementType.METHOD) -@Inherited -@Retention(RetentionPolicy.RUNTIME) -public @interface UrlParaValidate { - - String message() default ""; - - String redirectUrl() default ""; - - String htmlPath() default ""; - - String renderType() default ValidateRenderType.DEFAULT; - - int errorCode() default 1; - -} diff --git a/src/test/java/io/jboot/test/validate/ValidateController.java b/src/test/java/io/jboot/test/validate/ValidateController.java index 9b759be7..e14ac8d7 100644 --- a/src/test/java/io/jboot/test/validate/ValidateController.java +++ b/src/test/java/io/jboot/test/validate/ValidateController.java @@ -4,7 +4,6 @@ import com.jfinal.core.Controller; import io.jboot.web.controller.annotation.RequestMapping; import io.jboot.web.validate.EmptyValidate; import io.jboot.web.validate.Form; -import io.jboot.web.validate.UrlParaValidate; import io.jboot.web.validate.ValidateRenderType; @RequestMapping("/validate") @@ -15,18 +14,9 @@ public class ValidateController extends Controller { renderText("index"); } - @UrlParaValidate - public void test1(){ - renderText("test1"); - } - - @UrlParaValidate(renderType = ValidateRenderType.TEXT,message = "test2 was verification failed") - public void test2(){ - renderText("test2"); - } @EmptyValidate(value = @Form(name = "form"),renderType = ValidateRenderType.JSON) - public void test3(){ + public void test1(){ renderText("test3"); } } -- Gitee From eb52a0d97ee5515db3c2fd0db052dcdeff978448 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 27 May 2020 18:16:04 +0800 Subject: [PATCH 0315/1965] remove Jboot @EnableCORS annotation, use jfinal replace --- .../swagger/JbootSwaggerController.java | 2 +- .../io/jboot/web/cors/CORSInterceptor.java | 15 ++- .../java/io/jboot/web/cors/EnableCORS.java | 98 +++++++++---------- .../io/jboot/test/cors/CorsController.java | 2 +- 4 files changed, 61 insertions(+), 56 deletions(-) diff --git a/src/main/java/io/jboot/support/swagger/JbootSwaggerController.java b/src/main/java/io/jboot/support/swagger/JbootSwaggerController.java index a3faff7c..373a1600 100644 --- a/src/main/java/io/jboot/support/swagger/JbootSwaggerController.java +++ b/src/main/java/io/jboot/support/swagger/JbootSwaggerController.java @@ -18,9 +18,9 @@ package io.jboot.support.swagger; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializeConfig; import com.google.common.collect.Maps; +import com.jfinal.ext.cors.EnableCORS; import io.jboot.Jboot; import io.jboot.web.controller.JbootController; -import io.jboot.web.cors.EnableCORS; import io.swagger.models.Swagger; import io.swagger.models.properties.RefProperty; diff --git a/src/main/java/io/jboot/web/cors/CORSInterceptor.java b/src/main/java/io/jboot/web/cors/CORSInterceptor.java index 76b62198..42903df6 100644 --- a/src/main/java/io/jboot/web/cors/CORSInterceptor.java +++ b/src/main/java/io/jboot/web/cors/CORSInterceptor.java @@ -16,6 +16,7 @@ package io.jboot.web.cors; import com.jfinal.aop.Invocation; +import com.jfinal.ext.cors.EnableCORS; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.StrUtil; import io.jboot.web.fixedinterceptor.FixedInterceptor; @@ -35,19 +36,17 @@ public class CORSInterceptor implements FixedInterceptor { @Override public void intercept(Invocation inv) { - EnableCORS enableCORS = inv.getMethod().getAnnotation(EnableCORS.class); - - if (enableCORS == null) { - enableCORS = inv.getController().getClass().getAnnotation(EnableCORS.class); - } + EnableCORS enableCORS = getAnnotation(inv); if (enableCORS == null) { inv.invoke(); return; } + doConfigCORS(inv, enableCORS); + String method = inv.getController().getRequest().getMethod(); if (METHOD_OPTIONS.equals(method)) { inv.getController().renderText(""); @@ -56,6 +55,12 @@ public class CORSInterceptor implements FixedInterceptor { } } + + private EnableCORS getAnnotation(Invocation inv) { + EnableCORS enableCORS = inv.getController().getClass().getAnnotation(EnableCORS.class); + return enableCORS != null ? enableCORS : inv.getMethod().getAnnotation(EnableCORS.class); + } + private void doConfigCORS(Invocation inv, EnableCORS enableCORS) { HttpServletResponse response = inv.getController().getResponse(); diff --git a/src/main/java/io/jboot/web/cors/EnableCORS.java b/src/main/java/io/jboot/web/cors/EnableCORS.java index c5552494..6b820636 100644 --- a/src/main/java/io/jboot/web/cors/EnableCORS.java +++ b/src/main/java/io/jboot/web/cors/EnableCORS.java @@ -1,49 +1,49 @@ -/** - * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.web.cors; - -import java.lang.annotation.*; - -/** - * @author Michael Yang 杨福海 (fuhai999@gmail.com) - * @version V1.0 - * @Package io.jboot.web.cors - *

- * detail : https://developer.mozilla.org/en-US/docs/Glossary/CORS - */ -@Inherited -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD, ElementType.TYPE}) -public @interface EnableCORS { - - String allowOrigin() default "*"; - - String allowCredentials() default "true"; - - String allowHeaders() default "Origin,X-Requested-With,Content-Type,Accept,Authorization,Jwt"; - - String allowMethods() default "GET,PUT,POST,DELETE,PATCH,OPTIONS"; - - String exposeHeaders() default ""; - - String requestHeaders() default ""; - - String requestMethod() default ""; - - String origin() default ""; - - String maxAge() default "3600"; -} +///** +// * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). +// *

+// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// *

+// * http://www.apache.org/licenses/LICENSE-2.0 +// *

+// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +//package io.jboot.web.cors; +// +//import java.lang.annotation.*; +// +///** +// * @author Michael Yang 杨福海 (fuhai999@gmail.com) +// * @version V1.0 +// * @Package io.jboot.web.cors +// *

+// * detail : https://developer.mozilla.org/en-US/docs/Glossary/CORS +// */ +//@Inherited +//@Retention(RetentionPolicy.RUNTIME) +//@Target({ElementType.METHOD, ElementType.TYPE}) +//public @interface EnableCORS { +// +// String allowOrigin() default "*"; +// +// String allowCredentials() default "true"; +// +// String allowHeaders() default "Origin,X-Requested-With,Content-Type,Accept,Authorization,Jwt"; +// +// String allowMethods() default "GET,PUT,POST,DELETE,PATCH,OPTIONS"; +// +// String exposeHeaders() default ""; +// +// String requestHeaders() default ""; +// +// String requestMethod() default ""; +// +// String origin() default ""; +// +// String maxAge() default "3600"; +//} diff --git a/src/test/java/io/jboot/test/cors/CorsController.java b/src/test/java/io/jboot/test/cors/CorsController.java index b09de83d..6412e570 100644 --- a/src/test/java/io/jboot/test/cors/CorsController.java +++ b/src/test/java/io/jboot/test/cors/CorsController.java @@ -1,9 +1,9 @@ package io.jboot.test.cors; +import com.jfinal.ext.cors.EnableCORS; import com.jfinal.kit.Ret; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; -import io.jboot.web.cors.EnableCORS; @RequestMapping("/cors") public class CorsController extends JbootController { -- Gitee From 4c42dda54a5386bf0fdfe38ec784397c7a4ca6da Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 28 May 2020 11:27:22 +0800 Subject: [PATCH 0316/1965] v3.2.1 --- src/main/java/io/jboot/db/model/JbootModel.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 87852286..5fafc90d 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -602,10 +602,10 @@ public class JbootModel> extends Model { private transient Table table; - public Table _getTable(boolean validateNull) { + public Table _getTable(boolean validateMapping) { if (table == null) { table = super._getTable(); - if (table == null && validateNull) { + if (table == null && validateMapping) { throw new JbootException( String.format("class %s can not mapping to database table,maybe application cannot connect to database. " , _getUsefulClass().getName())); -- Gitee From b42bd5a55d950fa28c001bff9098d4bfee670da6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 28 May 2020 16:24:25 +0800 Subject: [PATCH 0317/1965] v3.2.1 --- src/main/java/io/jboot/utils/ClassScanner.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index b24e1f81..af509802 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -222,6 +222,8 @@ public class ClassScanner { excludeJars.add("easypoi-"); excludeJars.add("ognl-"); excludeJars.add("xmlbeans-"); + excludeJars.add("master-slave-core-"); + excludeJars.add("shadow-core-rewrite-"); } -- Gitee From 610022f5d498796201ad902fe16be96c2100e469 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 29 May 2020 10:43:00 +0800 Subject: [PATCH 0318/1965] v3.2.1 release (^.^)YYa!! --- pom.xml | 2 +- src/main/java/io/jboot/web/session/JbootSessionConfig.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d0c60867..dcd707ec 100644 --- a/pom.xml +++ b/pom.xml @@ -44,7 +44,7 @@ 4.9 2020.4 2.1 - 2.5 + 2.6 3.3.0 3.4.5 2.11.0 diff --git a/src/main/java/io/jboot/web/session/JbootSessionConfig.java b/src/main/java/io/jboot/web/session/JbootSessionConfig.java index 0d4495c3..e4a657b2 100644 --- a/src/main/java/io/jboot/web/session/JbootSessionConfig.java +++ b/src/main/java/io/jboot/web/session/JbootSessionConfig.java @@ -38,6 +38,7 @@ public class JbootSessionConfig { private String cacheName = DEFAULT_SESSION_CACHE_NAME; private String cacheType = Jboot.config(JbootCacheConfig.class).getType(); + public String getCookieName() { return cookieName; } -- Gitee From 0a7f37308e03a7fae2d674250dd956de727bb470 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 29 May 2020 10:52:59 +0800 Subject: [PATCH 0319/1965] v3.2.1 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 13 +++++++++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- src/main/java/io/jboot/web/JbootJson.java | 10 ++++++++++ 7 files changed, 28 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3630e3c2..1618e6bc 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.2.0 + 3.2.1 ``` diff --git a/changes.txt b/changes.txt index 91e97cf2..f4559d8c 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,16 @@ +jboot v3.2.1: +优化:升级 JFinal-Undertow、JFinal-Weixin 到最新版本 +优化:完善支持更多关于 druid 的数据源配置 +优化:当未配置任何第三方日志组件的时候,自动使用 JDK 日志进行输出 +优化:添加 JbootRedirectRender,防止 nginx -> jboot 跳转时的错误问题 +优化:移除 @ValidatePara 注解 和 UrlParaValidate 验证拦截器 +优化:移除 Jboot 的 @EnableCORS 注解,使用 JFinal 自带的来替代 +优化:修改某些变量命名不直观的问题 +优化:默认情况下完全禁用 Fastjson 的 autoType 功能 +文档:添加 dubbo rpc 下的 restful 配置文档 + + + jboot v3.2.0: 新增:JbootController 新增 getParaToBigInteger()、getParaToBigDecimal() 等方法 新增:门户网关新增 hasException() 方法,用于判断目标地址是否可以正常访问 diff --git a/doc/docs/install.md b/doc/docs/install.md index 22799605..6e4befaa 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.2.0 + 3.2.1 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 8c717126..2fe55c2e 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.2.0 + 3.2.1 ``` diff --git a/pom.xml b/pom.xml index dcd707ec..fd8ccfdd 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.1-SNAPSHOT + 3.2.1 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 05b3be7d..5b08d049 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.2.0"; + public static String VERSION = "3.2.1"; public static final String ATTR_REQUEST = "REQUEST"; diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index a1dfb740..9c1be59d 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -16,6 +16,7 @@ package io.jboot.web; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.parser.ParserConfig; import com.jfinal.json.JFinalJson; import io.jboot.Jboot; @@ -25,10 +26,19 @@ public class JbootJson extends JFinalJson { private static boolean isCamelCaseJsonStyleEnable = Jboot.config(JbootWebConfig.class).isCamelCaseJsonStyleEnable(); public JbootJson() { + + //完全禁用 autoType,提升安全性 + ParserConfig.getGlobalInstance().setSafeMode(true); + + //跳过 null 值输出到浏览器,提高传输性能 setSkipNullValueField(true); + + //默认设置为 CamelCase 的属性模式 if (isCamelCaseJsonStyleEnable) { setModelAndRecordFieldNameToCamelCase(); } + + } @Override -- Gitee From cd31ead954fd8b9b28400b5fdb727d42c7d77ead Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 2 Jun 2020 09:36:40 +0800 Subject: [PATCH 0320/1965] update docs --- doc/docs/config.md | 2 +- doc/docs/db.md | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/doc/docs/config.md b/doc/docs/config.md index 3c7a5a8c..ca271dcf 100644 --- a/doc/docs/config.md +++ b/doc/docs/config.md @@ -398,7 +398,7 @@ jboot.datasource.type jboot.datasource.url jboot.datasource.user jboot.datasource.password -jboot.datasource.driverClassName = "com.mysql.jdbc.Driver" +jboot.datasource.driverClassName = com.mysql.jdbc.Driver jboot.datasource.connectionInitSql jboot.datasource.poolName jboot.datasource.cachePrepStmts = true diff --git a/doc/docs/db.md b/doc/docs/db.md index 0a522c2c..081df6b5 100644 --- a/doc/docs/db.md +++ b/doc/docs/db.md @@ -185,14 +185,14 @@ jboot.datasource.password=your_password ``` jboot.datasource.a1.type=mysql -jboot.datasource.a1.turl=jdbc:mysql://127.0.0.1:3306/jboot1 -jboot.datasource.a1.tuser=root -jboot.datasource.a1.tpassword=your_password +jboot.datasource.a1.url=jdbc:mysql://127.0.0.1:3306/jboot1 +jboot.datasource.a1.user=root +jboot.datasource.a1.password=your_password jboot.datasource.a2.type=mysql -jboot.datasource.a2.turl=jdbc:mysql://127.0.0.1:3306/jboot2 -jboot.datasource.a2.tuser=root -jboot.datasource.a2.tpassword=your_password +jboot.datasource.a2.url=jdbc:mysql://127.0.0.1:3306/jboot2 +jboot.datasource.a2.user=root +jboot.datasource.a2.password=your_password ``` 这表示,我们又增加了数据源 `a1` 和数据源 `a2`,在使用的时候,我们只需要做一下使用: @@ -210,7 +210,7 @@ company.use("a1").save(); **需要注意的是:** -在多数据源应用中,很多时候,我们的一个 Model 只有对应一个数据源,而不是一个 Model 对应多个数据源。假设 `Company` 只有在 `a1` 数据源中存在,在其他数据源并不存在,我们需要把 `a1` 数据源的配置修改如下: +在多数据源应用中,很多时候,我们的一个 Model 只有对应一个数据源,而不是一个 Model 对应多个数据源。假设 `company` 只有在 `a1` 数据源中存在,在其他数据源并不存在,我们需要把 `a1` 数据源的配置修改如下: ``` jboot.datasource.a1.type=mysql @@ -223,10 +223,12 @@ jboot.datasource.a2.type=mysql jboot.datasource.a2.url=jdbc:mysql://127.0.0.1:3306/jboot2 jboot.datasource.a2.user=root jboot.datasource.a2.password=your_password -jboot.datasource.a1.table=user,xxx(其他非company表) +jboot.datasource.a2.table=user,xxx (其他非company表) ``` 这样,`company` 在 `a1` 数据源中存在,Jboot在初始化的时候,并不会去检查 `company` 在其他数据源中是否存在,同时,代码操作 `company` 的时候,不再需要 `use()` ,代码如下: + + ```java Company company = new Company(); company.setCid("1"); @@ -236,7 +238,7 @@ company.setName("name"); company.save(); ``` -代码中不再需要 `use("a1")` 指定数据源,因为 `company` 只有一个数据源。 +代码中不再需要 `use("a1")` 指定数据源,因为 `company` 表只有一个数据源。 更多关于 datasource 的配置如下: @@ -246,7 +248,7 @@ jboot.datasource.type //数据源类型 jboot.datasource.url //数据源URL地址 jboot.datasource.user jboot.datasource.password -jboot.datasource.driverClassName = "com.mysql.jdbc.Driver" +jboot.datasource.driverClassName = com.mysql.jdbc.Driver jboot.datasource.connectionInitSql jboot.datasource.poolName jboot.datasource.cachePrepStmts = true -- Gitee From ee5a299107a27ce28fdd851791bf16d68d8e4115 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 3 Jun 2020 15:02:24 +0800 Subject: [PATCH 0321/1965] add Columns.append() method for append other columns --- src/main/java/io/jboot/db/model/Columns.java | 47 ++++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 3b6560bd..266dcf96 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -296,7 +296,7 @@ public class Columns implements Serializable { * @return */ public Columns group(Columns columns) { - if (columns == this){ + if (columns == this) { throw new IllegalArgumentException("Columns.group(...) need a new Columns"); } if (!columns.isEmpty()) { @@ -312,7 +312,7 @@ public class Columns implements Serializable { * @return */ public Columns groupIf(Columns columns, boolean conditon) { - if (columns == this){ + if (columns == this) { throw new IllegalArgumentException("Columns.group(...) need a new Columns"); } if (conditon && !columns.isEmpty()) { @@ -435,7 +435,6 @@ public class Columns implements Serializable { } - public Columns or() { this.add(new Or()); return this; @@ -461,6 +460,36 @@ public class Columns implements Serializable { } + /** + * 追加 新的 columns + * + * @param columns + * @return + */ + public Columns append(Columns columns) { + if (columns != null && !columns.isEmpty()) { + for (Column column : columns.getList()) { + add(column); + } + } + return this; + } + + + /** + * 追加 新的 columns + * + * @param columns + * @return + */ + public Columns appendIf(Columns columns, boolean condition) { + if (condition) { + append(columns); + } + return this; + } + + public boolean isEmpty() { return cols == null || cols.isEmpty(); } @@ -497,7 +526,7 @@ public class Columns implements Serializable { if (column instanceof Or) { Column before = i > 0 ? columns.get(i - 1) : null; - if (before != null && !(before instanceof Or) ) { + if (before != null && !(before instanceof Or)) { s.append("or").append(SQL_CACHE_SEPARATOR); } } else if (column instanceof Group) { @@ -598,11 +627,11 @@ public class Columns implements Serializable { public static void main(String[] args) { - Columns columns = Columns.create().or().or().or().eq("aa","bb").or().or().or().notIn("aaa",123,456,789).like("titile","a"); + Columns columns = Columns.create().or().or().or().eq("aa", "bb").or().or().or().notIn("aaa", 123, 456, 789).like("titile", "a"); columns.group(Columns.create().or().or().sqlPart("aa=bb")); - columns.group(Columns.create("aa","bb").eq("cc","dd") - .group(Columns.create("aa","bb").eq("cc","dd")) - .group(Columns.create("aa","bb").eq("cc","dd").group(Columns.create("aa","bb").eq("cc","dd")))); + columns.group(Columns.create("aa", "bb").eq("cc", "dd") + .group(Columns.create("aa", "bb").eq("cc", "dd")) + .group(Columns.create("aa", "bb").eq("cc", "dd").group(Columns.create("aa", "bb").eq("cc", "dd")))); columns.ge("age", 10); columns.or(); @@ -616,7 +645,7 @@ public class Columns implements Serializable { columns.or(); columns.or(); columns.group(Columns.create().likeAppendPercent("name", "null").or().or().or() - .eq("age", "18").eq("ddd","ddd")); + .eq("age", "18").eq("ddd", "ddd")); columns.or(); columns.or(); -- Gitee From 238f6445f2e07d501fb7faa9d039dc9a9e1d0235 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 3 Jun 2020 15:03:12 +0800 Subject: [PATCH 0322/1965] 3.2.2 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fd8ccfdd..9039a0b0 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.1 + 3.2.2-SNAPSHOT jar jboot -- Gitee From 1aa386491878939d7872f2bcff7e35bd26bacb92 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 4 Jun 2020 09:28:28 +0800 Subject: [PATCH 0323/1965] set JbootWebConfig.camelCaseJsonStyleEnable = true --- src/main/java/io/jboot/web/JbootWebConfig.java | 2 +- .../java/io/jboot/test/json/JsonTester.java | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/test/java/io/jboot/test/json/JsonTester.java diff --git a/src/main/java/io/jboot/web/JbootWebConfig.java b/src/main/java/io/jboot/web/JbootWebConfig.java index 287f5855..d9525686 100644 --- a/src/main/java/io/jboot/web/JbootWebConfig.java +++ b/src/main/java/io/jboot/web/JbootWebConfig.java @@ -30,7 +30,7 @@ public class JbootWebConfig { private String cookieEncryptKey = DEFAULT_COOKIE_ENCRYPT_KEY; private String webSocketEndpoint; - private boolean camelCaseJsonStyleEnable = false; + private boolean camelCaseJsonStyleEnable = true; public String getCookieEncryptKey() { return cookieEncryptKey; diff --git a/src/test/java/io/jboot/test/json/JsonTester.java b/src/test/java/io/jboot/test/json/JsonTester.java new file mode 100644 index 00000000..4a39b8b3 --- /dev/null +++ b/src/test/java/io/jboot/test/json/JsonTester.java @@ -0,0 +1,18 @@ +package io.jboot.test.json; + +import io.jboot.test.db.model.User; +import io.jboot.web.JbootJson; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public class JsonTester { + + public static void main(String[] args) { + User user = new User(); + user.put("id",100); + user.put("tenant_id","xxx"); + + System.out.println(new JbootJson().toJson(user)); + } +} -- Gitee From a648337470da7d5c041b1028e71752d635732bbf Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 4 Jun 2020 09:45:27 +0800 Subject: [PATCH 0324/1965] upgrade dependencies --- pom.xml | 8 ++++---- src/main/java/io/jboot/web/JbootJson.java | 8 +------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index 9039a0b0..5d559a1b 100644 --- a/pom.xml +++ b/pom.xml @@ -49,10 +49,10 @@ 3.4.5 2.11.0 5.1.48 - 2.1.1.Final + 2.1.2.Final 1.7.30 2.57 - 1.2.68 + 1.2.70 29.0-jre 2.2.5 2.10.2 @@ -60,14 +60,14 @@ 2.10.6 2.8.4 3.10 - 2.6 + 2.7 1.14 4.5.12 4.4.13 4.1.50.Final 1.7.2 2.7.7 - 3.11.0.Final + 3.12.1.Final 1.2.0 1.1.8 4.1.9 diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index 9c1be59d..a491e082 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -16,20 +16,16 @@ package io.jboot.web; import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.parser.ParserConfig; import com.jfinal.json.JFinalJson; import io.jboot.Jboot; public class JbootJson extends JFinalJson { - private static boolean isCamelCaseJsonStyleEnable = Jboot.config(JbootWebConfig.class).isCamelCaseJsonStyleEnable(); + private boolean isCamelCaseJsonStyleEnable = Jboot.config(JbootWebConfig.class).isCamelCaseJsonStyleEnable(); public JbootJson() { - //完全禁用 autoType,提升安全性 - ParserConfig.getGlobalInstance().setSafeMode(true); - //跳过 null 值输出到浏览器,提高传输性能 setSkipNullValueField(true); @@ -37,8 +33,6 @@ public class JbootJson extends JFinalJson { if (isCamelCaseJsonStyleEnable) { setModelAndRecordFieldNameToCamelCase(); } - - } @Override -- Gitee From 1e0f05d586db08e2f74d2b37ac231185b59e5961 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 5 Jun 2020 11:18:20 +0800 Subject: [PATCH 0325/1965] add joins for findCountByColumns and deleteByColumns --- src/main/java/io/jboot/db/dbpro/JbootDbPro.java | 4 ++-- .../java/io/jboot/db/dialect/JbootAnsiSqlDialect.java | 8 ++++---- src/main/java/io/jboot/db/dialect/JbootDialect.java | 4 ++-- .../java/io/jboot/db/dialect/JbootMysqlDialect.java | 8 ++++---- .../java/io/jboot/db/dialect/JbootOracleDialect.java | 8 ++++---- .../io/jboot/db/dialect/JbootPostgreSqlDialect.java | 8 ++++---- .../io/jboot/db/dialect/JbootSqlServerDialect.java | 8 ++++---- .../java/io/jboot/db/dialect/JbootSqlite3Dialect.java | 8 ++++---- src/main/java/io/jboot/db/model/JbootModel.java | 4 ++-- src/main/java/io/jboot/db/model/SqlBuilder.java | 10 +++++++--- 10 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java index b413727f..11c85d56 100644 --- a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java +++ b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java @@ -114,14 +114,14 @@ public class JbootDbPro extends DbPro { public List find(String tableName, Columns columns, String orderBy, Object limit) { JbootDialect dialect = (JbootDialect) getConfig().getDialect(); - String sql = dialect.forFindByColumns(null,tableName, "*", columns.getList(), orderBy, limit); + String sql = dialect.forFindByColumns(null, tableName, "*", columns.getList(), orderBy, limit); return columns.isEmpty() ? find(sql) : find(sql, columns.getValueArray()); } public int delete(String tableName, Columns columns) { JbootDialect dialect = (JbootDialect) getConfig().getDialect(); - String sql = dialect.forDeleteByColumns(tableName, columns.getList()); + String sql = dialect.forDeleteByColumns(null, tableName, columns.getList()); return columns.isEmpty() ? delete(sql) : delete(sql, columns.getValueArray()); } diff --git a/src/main/java/io/jboot/db/dialect/JbootAnsiSqlDialect.java b/src/main/java/io/jboot/db/dialect/JbootAnsiSqlDialect.java index 9c938b62..0fcc24de 100644 --- a/src/main/java/io/jboot/db/dialect/JbootAnsiSqlDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootAnsiSqlDialect.java @@ -39,13 +39,13 @@ public class JbootAnsiSqlDialect extends AnsiSqlDialect implements JbootDialect } @Override - public String forFindCountByColumns(String table, List columns) { - return SqlBuilder.forFindCountByColumns(table, columns, ' '); + public String forFindCountByColumns(List joins, String table, List columns) { + return SqlBuilder.forFindCountByColumns(joins, table, columns, ' '); } @Override - public String forDeleteByColumns(String table, List columns) { - return SqlBuilder.forDeleteByColumns(table, columns, ' '); + public String forDeleteByColumns(List joins, String table, List columns) { + return SqlBuilder.forDeleteByColumns(joins,table, columns, ' '); } diff --git a/src/main/java/io/jboot/db/dialect/JbootDialect.java b/src/main/java/io/jboot/db/dialect/JbootDialect.java index 6ba3f23c..62796bc2 100644 --- a/src/main/java/io/jboot/db/dialect/JbootDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootDialect.java @@ -27,10 +27,10 @@ public interface JbootDialect { public String forFindByColumns(List joins, String table, String loadColumns, List columns, String orderBy, Object limit); - public String forFindCountByColumns(String table, List columns); + public String forFindCountByColumns(List joins, String table, List columns); - public String forDeleteByColumns(String table, List columns); + public String forDeleteByColumns(List joins, String table, List columns); public String forPaginateSelect(String loadColumns); diff --git a/src/main/java/io/jboot/db/dialect/JbootMysqlDialect.java b/src/main/java/io/jboot/db/dialect/JbootMysqlDialect.java index f2c83333..32859e76 100644 --- a/src/main/java/io/jboot/db/dialect/JbootMysqlDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootMysqlDialect.java @@ -37,13 +37,13 @@ public class JbootMysqlDialect extends MysqlDialect implements JbootDialect { } @Override - public String forFindCountByColumns(String table, List columns) { - return SqlBuilder.forFindCountByColumns(table, columns, '`'); + public String forFindCountByColumns(List joins, String table, List columns) { + return SqlBuilder.forFindCountByColumns(joins, table, columns, '`'); } @Override - public String forDeleteByColumns(String table, List columns) { - return SqlBuilder.forDeleteByColumns(table, columns, '`'); + public String forDeleteByColumns(List joins, String table, List columns) { + return SqlBuilder.forDeleteByColumns(joins, table, columns, '`'); } diff --git a/src/main/java/io/jboot/db/dialect/JbootOracleDialect.java b/src/main/java/io/jboot/db/dialect/JbootOracleDialect.java index b8e3228d..865a1277 100644 --- a/src/main/java/io/jboot/db/dialect/JbootOracleDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootOracleDialect.java @@ -59,13 +59,13 @@ public class JbootOracleDialect extends OracleDialect implements JbootDialect { } @Override - public String forFindCountByColumns(String table, List columns) { - return SqlBuilder.forFindCountByColumns(table, columns, ' '); + public String forFindCountByColumns(List joins, String table, List columns) { + return SqlBuilder.forFindCountByColumns(joins, table, columns, ' '); } @Override - public String forDeleteByColumns(String table, List columns) { - return SqlBuilder.forDeleteByColumns(table,columns,' '); + public String forDeleteByColumns(List joins, String table, List columns) { + return SqlBuilder.forDeleteByColumns(joins, table,columns,' '); } diff --git a/src/main/java/io/jboot/db/dialect/JbootPostgreSqlDialect.java b/src/main/java/io/jboot/db/dialect/JbootPostgreSqlDialect.java index e4dfda37..def8506e 100644 --- a/src/main/java/io/jboot/db/dialect/JbootPostgreSqlDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootPostgreSqlDialect.java @@ -51,13 +51,13 @@ public class JbootPostgreSqlDialect extends PostgreSqlDialect implements JbootDi } @Override - public String forFindCountByColumns(String table, List columns) { - return SqlBuilder.forFindCountByColumns(table, columns, '"'); + public String forFindCountByColumns(List joins, String table, List columns) { + return SqlBuilder.forFindCountByColumns(joins, table, columns, '"'); } @Override - public String forDeleteByColumns(String table, List columns) { - return SqlBuilder.forDeleteByColumns(table,columns,'"'); + public String forDeleteByColumns(List joins, String table, List columns) { + return SqlBuilder.forDeleteByColumns(joins, table,columns,'"'); } diff --git a/src/main/java/io/jboot/db/dialect/JbootSqlServerDialect.java b/src/main/java/io/jboot/db/dialect/JbootSqlServerDialect.java index c29cd464..f77db453 100644 --- a/src/main/java/io/jboot/db/dialect/JbootSqlServerDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootSqlServerDialect.java @@ -63,13 +63,13 @@ public class JbootSqlServerDialect extends SqlServerDialect implements JbootDial } @Override - public String forFindCountByColumns(String table, List columns) { - return SqlBuilder.forFindCountByColumns(table, columns, ' '); + public String forFindCountByColumns(List joins, String table, List columns) { + return SqlBuilder.forFindCountByColumns(joins, table, columns, ' '); } @Override - public String forDeleteByColumns(String table, List columns) { - return SqlBuilder.forDeleteByColumns(table, columns, ' '); + public String forDeleteByColumns(List joins, String table, List columns) { + return SqlBuilder.forDeleteByColumns(joins, table, columns, ' '); } diff --git a/src/main/java/io/jboot/db/dialect/JbootSqlite3Dialect.java b/src/main/java/io/jboot/db/dialect/JbootSqlite3Dialect.java index 317c706a..a4e0611f 100644 --- a/src/main/java/io/jboot/db/dialect/JbootSqlite3Dialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootSqlite3Dialect.java @@ -38,13 +38,13 @@ public class JbootSqlite3Dialect extends Sqlite3Dialect implements JbootDialect } @Override - public String forFindCountByColumns(String table, List columns) { - return SqlBuilder.forFindCountByColumns(table, columns, ' '); + public String forFindCountByColumns(List joins, String table, List columns) { + return SqlBuilder.forFindCountByColumns(joins, table, columns, ' '); } @Override - public String forDeleteByColumns(String table, List columns) { - return SqlBuilder.forDeleteByColumns(table, columns, ' '); + public String forDeleteByColumns(List joins, String table, List columns) { + return SqlBuilder.forDeleteByColumns(joins, table, columns, ' '); } diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 5fafc90d..bd91f162 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -313,7 +313,7 @@ public class JbootModel> extends Model { public boolean deleteByColumns(List columns) { - String sql = _getDialect().forDeleteByColumns(_getTableName(), columns); + String sql = _getDialect().forDeleteByColumns(joins, _getTableName(), columns); return Db.use(_getConfig().getName()).update(sql, Util.getValueArray(columns)) >= 1; } @@ -569,7 +569,7 @@ public class JbootModel> extends Model { } public long findCountByColumns(List columns) { - String sql = _getDialect().forFindCountByColumns(_getTableName(), columns); + String sql = _getDialect().forFindCountByColumns(joins, _getTableName(), columns); Long value = Db.use(_getConfig().getName()).queryLong(sql, Util.getValueArray(columns)); return value == null ? 0 : value; } diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index 3c255ee1..797281a4 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -34,10 +34,13 @@ public class SqlBuilder { buildWhereSql(sqlBuilder, columns, '`'); } - public static String forDeleteByColumns(String table, List columns, char separator) { + public static String forDeleteByColumns(List joins, String table, List columns, char separator) { StringBuilder sql = new StringBuilder(45); sql.append("DELETE FROM ").append(separator).append(table).append(separator); - SqlBuilder.buildWhereSql(sql, columns, ' '); + + buildJoinSql(sql, joins, ' '); + buildWhereSql(sql, columns, ' '); + return sql.toString(); } @@ -238,12 +241,13 @@ public class SqlBuilder { } - public static String forFindCountByColumns(String table, List columns, char separator) { + public static String forFindCountByColumns(List joins, String table, List columns, char separator) { StringBuilder sqlBuilder = new StringBuilder("SELECT count(*) FROM ") .append(separator) .append(table) .append(separator); + buildJoinSql(sqlBuilder, joins, separator); buildWhereSql(sqlBuilder, columns, separator); return sqlBuilder.toString(); -- Gitee From 044729123dd8913910055e901b5062ce1401cbb0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 5 Jun 2020 17:45:59 +0800 Subject: [PATCH 0326/1965] add in(list) and notIn(list) for columns --- src/main/java/io/jboot/db/model/Columns.java | 30 ++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 266dcf96..ee0473c5 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -249,6 +249,21 @@ public class Columns implements Serializable { return this; } + + /** + * in list + * + * @param name + * @param list + * @return + */ + public Columns in(String name, List list) { + if (list != null && !list.isEmpty()) { + in(name, list.toArray()); + } + return this; + } + /** * not int arrays * @@ -262,6 +277,21 @@ public class Columns implements Serializable { } + /** + * not in list + * + * @param name + * @param list + * @return + */ + public Columns notIn(String name, List list) { + if (list != null && !list.isEmpty()) { + notIn(name, list.toArray()); + } + return this; + } + + /** * between * -- Gitee From 7cf578bf1276772e57468a978736484aeb458482 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 5 Jun 2020 18:56:12 +0800 Subject: [PATCH 0327/1965] init remote config in server start before --- .../jboot/app/config/JbootConfigManager.java | 6 ++++++ .../support/apollo/ApolloConfigManager.java | 10 +++++----- .../support/nacos/NacosConfigIniter.java | 7 +++++-- .../support/nacos/NacosConfigManager.java | 19 +++++++++---------- .../java/io/jboot/core/JbootCoreConfig.java | 5 ----- 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index b4986fc7..23f1c418 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -17,6 +17,8 @@ package io.jboot.app.config; import com.jfinal.kit.LogKit; import io.jboot.app.config.annotation.ConfigModel; +import io.jboot.app.config.support.apollo.ApolloConfigManager; +import io.jboot.app.config.support.nacos.NacosConfigManager; import io.jboot.utils.StrUtil; import java.lang.reflect.Method; @@ -71,6 +73,10 @@ public class JbootConfigManager { String p = String.format("jboot-%s.properties", mode); mainProperties.putAll(new Prop(p).getProperties()); } + + + NacosConfigManager.me().init(this); + ApolloConfigManager.me().init(this); } public JbootConfigDecryptor getDecryptor() { diff --git a/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java b/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java index 42c3cf18..a4c12f06 100644 --- a/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java @@ -36,9 +36,9 @@ public class ApolloConfigManager { return ME; } - public void init() { + public void init(JbootConfigManager configManager) { - ApolloServerConfig apolloServerConfig = Jboot.config(ApolloServerConfig.class); + ApolloServerConfig apolloServerConfig = configManager.get(ApolloServerConfig.class); if (!apolloServerConfig.isEnable() || !apolloServerConfig.isConfigOk()){ return; } @@ -49,17 +49,17 @@ public class ApolloConfigManager { if (propNames != null && !propNames.isEmpty()) { for (String name : propNames) { String value = config.getProperty(name, null); - JbootConfigManager.me().setRemoteProperty(name, value); + configManager.setRemoteProperty(name, value); } } config.addChangeListener(changeEvent -> { for (String key : changeEvent.changedKeys()) { ConfigChange change = changeEvent.getChange(key); - JbootConfigManager.me().setRemoteProperty(change.getPropertyName(), change.getNewValue()); + configManager.setRemoteProperty(change.getPropertyName(), change.getNewValue()); } - JbootConfigManager.me().notifyChangeListeners(changeEvent.changedKeys()); + configManager.notifyChangeListeners(changeEvent.changedKeys()); }); } diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java index acff7e4b..e996e70d 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java @@ -17,6 +17,7 @@ package io.jboot.app.config.support.nacos; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.Listener; +import io.jboot.app.config.JbootConfigManager; import java.util.concurrent.Executor; @@ -27,10 +28,12 @@ import java.util.concurrent.Executor; public class NacosConfigIniter { private NacosConfigManager manager; + private JbootConfigManager configManager; - public NacosConfigIniter(NacosConfigManager manager) { + public NacosConfigIniter(NacosConfigManager manager, JbootConfigManager configManager) { this.manager = manager; + this.configManager = configManager; } public void initListener(ConfigService configService, NacosServerConfig config) { @@ -44,7 +47,7 @@ public class NacosConfigIniter { @Override public void receiveConfigInfo(String configInfo) { - manager.doReceiveConfigInfo(configInfo); + manager.doReceiveConfigInfo(configManager, configInfo); } }); } catch (Exception e) { diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java index 62c67d93..26074234 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java @@ -17,7 +17,6 @@ package io.jboot.app.config.support.nacos; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; -import io.jboot.Jboot; import io.jboot.app.config.JbootConfigManager; import io.jboot.utils.StrUtil; @@ -46,9 +45,9 @@ public class NacosConfigManager { /** * 初始化 nacos 配置监听 */ - public void init() { + public void init(JbootConfigManager configManager) { - NacosServerConfig nacosServerConfig = Jboot.config(NacosServerConfig.class); + NacosServerConfig nacosServerConfig = configManager.get(NacosServerConfig.class); if (!nacosServerConfig.isEnable() || !nacosServerConfig.isConfigOk()) { return; } @@ -62,11 +61,11 @@ public class NacosConfigManager { if (StrUtil.isNotBlank(content)) { contentProperties = str2Properties(content); if (contentProperties != null) { - JbootConfigManager.me().setRemoteProperties(contentProperties); + configManager.setRemoteProperties(contentProperties); } } - new NacosConfigIniter(this).initListener(configService, nacosServerConfig); + new NacosConfigIniter(this, configManager).initListener(configService, nacosServerConfig); } catch (Exception e) { e.printStackTrace(); @@ -79,7 +78,7 @@ public class NacosConfigManager { * * @param configInfo */ - public void doReceiveConfigInfo(String configInfo) { + public void doReceiveConfigInfo(JbootConfigManager configManager, String configInfo) { Properties properties = str2Properties(configInfo); Set changedKeys = new HashSet<>(); if (contentProperties == null) { @@ -89,8 +88,8 @@ public class NacosConfigManager { changedKeys.add(key.toString()); } - JbootConfigManager.me().setRemoteProperties(properties); - JbootConfigManager.me().notifyChangeListeners(changedKeys); + configManager.setRemoteProperties(properties); + configManager.notifyChangeListeners(changedKeys); } else { @@ -101,11 +100,11 @@ public class NacosConfigManager { if (!Objects.equals(newValue, oldValue)) { changedKeys.add(key.toString()); contentProperties.put(key, newValue); - JbootConfigManager.me().setRemoteProperty(key.toString(), newValue); + configManager.setRemoteProperty(key.toString(), newValue); } } - JbootConfigManager.me().notifyChangeListeners(changedKeys); + configManager.notifyChangeListeners(changedKeys); } } diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 99bb3faa..4adac7cb 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -30,9 +30,7 @@ import io.jboot.aop.JbootAopFactory; import io.jboot.aop.JbootAopInterceptor; import io.jboot.aop.jfinal.JfinalHandlers; import io.jboot.aop.jfinal.JfinalPlugins; -import io.jboot.app.config.support.apollo.ApolloConfigManager; import io.jboot.app.config.support.apollo.ApolloServerConfig; -import io.jboot.app.config.support.nacos.NacosConfigManager; import io.jboot.components.gateway.JbootGatewayHandler; import io.jboot.components.gateway.JbootGatewayManager; import io.jboot.components.limiter.LimiterManager; @@ -79,9 +77,6 @@ public class JbootCoreConfig extends JFinalConfig { initSystemProperties(); - ApolloConfigManager.me().init(); - NacosConfigManager.me().init(); - AopManager.me().setInjectDependency(true); AopManager.me().setAopFactory(JbootAopFactory.me()); Aop.inject(this); -- Gitee From cd191f037e40ceb8bea165cc6250978a5c6d922d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 9 Jun 2020 18:29:46 +0800 Subject: [PATCH 0328/1965] upgrade nacos-client --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5d559a1b..668c214c 100644 --- a/pom.xml +++ b/pom.xml @@ -275,7 +275,7 @@ com.alibaba.nacos nacos-client - 1.2.1 + 1.3.0 provided -- Gitee From a88e7480239f284c60149be431a04c2c38346712 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 9 Jun 2020 18:35:49 +0800 Subject: [PATCH 0329/1965] upgrade nacos-client --- doc/docs/config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/docs/config.md b/doc/docs/config.md index ca271dcf..d82155f1 100644 --- a/doc/docs/config.md +++ b/doc/docs/config.md @@ -209,7 +209,7 @@ Component1Config config = Jboot.config(Component1Config.class); com.alibaba.nacos nacos-client - 1.2.1 + 1.3.0 ``` -- Gitee From 068df837755248d2eaa22b6eebb18d27ecfaa395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=B2=E4=B8=96=E4=BC=9F?= Date: Tue, 9 Jun 2020 19:29:57 +0800 Subject: [PATCH 0330/1965] dev --- .../java/io/jboot/app/undertow/JbootUndertowConfig.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java b/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java index a2eb6f37..85878fa6 100644 --- a/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java +++ b/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java @@ -18,6 +18,7 @@ package io.jboot.app.undertow; import com.jfinal.server.undertow.PropExt; import com.jfinal.server.undertow.UndertowConfig; import com.jfinal.server.undertow.hotswap.HotSwapResolver; +import io.jboot.Jboot; import io.jboot.app.config.JbootConfigManager; import java.io.IOException; @@ -25,6 +26,7 @@ import java.net.ServerSocket; public class JbootUndertowConfig extends UndertowConfig { + protected static final String DEV_MODE = "undertow.devMode"; protected static final String UNDERTOW_PORT = "undertow.port"; protected static final String UNDERTOW_HOST = "undertow.host"; protected static final String UNDERTOW_RESOURCEPATH = "undertow.resourcePath"; @@ -61,7 +63,9 @@ public class JbootUndertowConfig extends UndertowConfig { propExt.getProperties().put(UNDERTOW_PORT, availablePort.toString()); JbootConfigManager.me().setBootArg(UNDERTOW_PORT, availablePort.toString()); } - + if (Jboot.isDevMode()){ + propExt.getProperties().put(DEV_MODE, true); + } String host = propExt.get(UNDERTOW_HOST); if (host == null || host.trim().length() == 0) { propExt.getProperties().put(UNDERTOW_HOST, "0.0.0.0"); -- Gitee From 52e7b2fd4a3ab9462198c12b41dc6f57ec853a2a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 10 Jun 2020 09:55:39 +0800 Subject: [PATCH 0331/1965] update rpc docs --- doc/docs/rpc.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/docs/rpc.md b/doc/docs/rpc.md index aeaebc00..ba4dcb86 100644 --- a/doc/docs/rpc.md +++ b/doc/docs/rpc.md @@ -111,7 +111,6 @@ jboot.rpc.autoExportEnable = true **定义接口** ```java - public interface BlogService { public String findById(); @@ -121,9 +120,8 @@ public interface BlogService { **编写实现类** -```java - +```java @RPCBean public class BlogServiceProvider implements BlogService { @@ -141,8 +139,8 @@ public class BlogServiceProvider implements BlogService { **启动 Server 暴露服务** -```java +```java public class DubboServer { public static void main(String[] args) { @@ -173,7 +171,9 @@ public class DubboServer { } ``` ->备注:以上的 JbootApplication.setBootArg() 里设置的内容,都可以配置到 jboot.properties 里。 +>备注:以上的 JbootApplication.setBootArg() 里设置的内容,都可以配置到 jboot.properties 里。例如: `JbootApplication.setBootArg("jboot.rpc.autoExportEnable", true);` 在 jboot.properties +>里对应的配置是 `jboot.rpc.autoExportEnable = true` 。 + **启动客户端、通过 RPC 调用 Server 提供的服务** @@ -294,7 +294,6 @@ public interface UserService { jboot.rpc.dubbo.protocol.name = dubbo jboot.rpc.dubbo.protocol.host = 127.0.0.1 jboot.rpc.dubbo.protocol.port = 28080 -``` jboot.rpc.dubbo.protocol.rest.name = rest jboot.rpc.dubbo.protocol.rest.host = 127.0.0.1 @@ -304,6 +303,7 @@ jboot.rpc.dubbo.protocol.rest.server = netty 第四步:给 Service 配置暴露协议 + ``` jboot.rpc.dubbo.provider.protocal = default,rest //使用 dubbo 和 rest 两种协议同时暴露 jboot.rpc.dubbo.provider.default = true // 给应用配置默认的 provider -- Gitee From 48ebda7d959238f8be4fd8c2651aea70b8544b6a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 10 Jun 2020 13:13:56 +0800 Subject: [PATCH 0332/1965] =?UTF-8?q?fixed=20:=20RPC=20=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=80=BC=E5=86=85=E5=AE=B9=E6=9C=89=20${}=20?= =?UTF-8?q?=E4=B8=8D=E7=94=9F=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/components/rpc/RPCUtil.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/io/jboot/components/rpc/RPCUtil.java b/src/main/java/io/jboot/components/rpc/RPCUtil.java index 01410d30..2a3adc93 100644 --- a/src/main/java/io/jboot/components/rpc/RPCUtil.java +++ b/src/main/java/io/jboot/components/rpc/RPCUtil.java @@ -16,6 +16,7 @@ package io.jboot.components.rpc; import io.jboot.Jboot; +import io.jboot.utils.AnnotationUtil; import io.jboot.utils.CollectionUtil; import io.jboot.utils.StrUtil; @@ -70,6 +71,12 @@ public class RPCUtil { } else { setterMethod = getMethod(appendTo.getClass(), setter, method.getReturnType()); } + + //fixed : 值内容有 ${} 不生效的问题 + if (value instanceof String) { + value = AnnotationUtil.get((String) value); + } + if (setterMethod != null) { setterMethod.invoke(appendTo, value); } -- Gitee From 564812a96acd43fa5d1917c1550d1889179727f9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 11 Jun 2020 17:11:15 +0800 Subject: [PATCH 0333/1965] v3.2.2 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 13 +++++++++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- src/main/java/io/jboot/components/rpc/RPCUtil.java | 2 +- 6 files changed, 18 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1618e6bc..03b7828e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.2.1 + 3.2.2 ``` diff --git a/changes.txt b/changes.txt index f4559d8c..21d59663 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,16 @@ +jboot v3.2.2: +优化:当项目启动的时候,优先初始化中央配置,以防止 undertow 端口等无法在中央仓库配置的问题 +优化:新增 Columns.in(list) 和 Columns.notIn(list) 方法 +优化:新增 Columns.append() 方法用于追加一个新的 columns +优化:Json输出默认使用驼峰的字段风格 +优化:升级 Nacos、Fastjson 等到最新版本 +修复:通过 Columns 查询 count,当有 left join 时会出现数量不正确的问题 +修复:当 RPC 注解有 ${} 时,无法读取配置内容的问题 +文档:修改配置文件里的示例带有双引号的错误配置 +文档:优化 PRC 的相关文档 + + + jboot v3.2.1: 优化:升级 JFinal-Undertow、JFinal-Weixin 到最新版本 优化:完善支持更多关于 druid 的数据源配置 diff --git a/doc/docs/install.md b/doc/docs/install.md index 6e4befaa..f5ea0299 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.2.1 + 3.2.2 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 2fe55c2e..115aa28e 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.2.1 + 3.2.2 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 5b08d049..bc7c0dfd 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.2.1"; + public static String VERSION = "3.2.2"; public static final String ATTR_REQUEST = "REQUEST"; diff --git a/src/main/java/io/jboot/components/rpc/RPCUtil.java b/src/main/java/io/jboot/components/rpc/RPCUtil.java index 2a3adc93..b580ac0b 100644 --- a/src/main/java/io/jboot/components/rpc/RPCUtil.java +++ b/src/main/java/io/jboot/components/rpc/RPCUtil.java @@ -36,7 +36,7 @@ public class RPCUtil { /** * 根据注解来设置对象内容,参考 dubbo 下的 AbstractConfig - * 参考 org.apache.dubbo.config.AbstractConfig#appendAnnotation + * 参考 {{@org.apache.dubbo.config.AbstractConfig#appendAnnotation }} * * @param annotationClass * @param annotation -- Gitee From f04f3c29074d7076f84d05b9419b168d403cf34c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 11 Jun 2020 17:11:37 +0800 Subject: [PATCH 0334/1965] v3.2.2 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 668c214c..a487f4f2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.2-SNAPSHOT + 3.2.2 jar jboot -- Gitee From 829e4eb78df6a671ea7cbda8cd02b7637dbe4ee1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 15 Jun 2020 12:20:37 +0800 Subject: [PATCH 0335/1965] v3.2.3 snapshot --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a487f4f2..06b84a09 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.2 + 3.2.3-SNAPSHOT jar jboot @@ -52,7 +52,7 @@ 2.1.2.Final 1.7.30 2.57 - 1.2.70 + 1.2.71 29.0-jre 2.2.5 2.10.2 -- Gitee From d89ec8f66ec4387cf9fa556982242c5578986994 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 15 Jun 2020 12:21:18 +0800 Subject: [PATCH 0336/1965] =?UTF-8?q?fixed:=20Model=20=E7=9A=84=20get=20?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E4=B8=8D=E8=BE=93=E5=87=BA=20json=20?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/web/JbootJson.java | 92 +++++++++++++++++++ .../java/io/jboot/test/db/model/User.java | 8 ++ .../java/io/jboot/test/json/JsonTester.java | 2 + 3 files changed, 102 insertions(+) diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index a491e082..3622d0dc 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -17,8 +17,19 @@ package io.jboot.web; import com.alibaba.fastjson.JSON; import com.jfinal.json.JFinalJson; +import com.jfinal.json.JFinalJsonKit; +import com.jfinal.kit.StrKit; +import com.jfinal.plugin.activerecord.CPI; +import com.jfinal.plugin.activerecord.Model; import io.jboot.Jboot; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + public class JbootJson extends JFinalJson { @@ -33,8 +44,89 @@ public class JbootJson extends JFinalJson { if (isCamelCaseJsonStyleEnable) { setModelAndRecordFieldNameToCamelCase(); } + + + setToJsonFactory(o -> { + if (o instanceof Model) { + return jbootModelJson; + } else { + return null; + } + }); + } + + + protected JFinalJsonKit.ToJson jbootModelJson = (value, depth, ret) -> { + if (JFinalJsonKit.checkDepth(depth--, ret)) { + return; + } + + Map attrs = new HashMap<>(); + + fillMapToAttr(CPI.getAttrs(value), attrs); + fillBeanAttrs(value, attrs); + + JFinalJsonKit.mapToJson(attrs, depth, ret); + }; + + + protected void fillMapToAttr(Map fillMap, Map toAttr) { + if (fillMap != null && !fillMap.isEmpty()) { + for (Map.Entry entry : fillMap.entrySet()) { + String fieldName = entry.getKey(); + if (isCamelCaseJsonStyleEnable) { + fieldName = StrKit.toCamelCase(fieldName, true); + } + toAttr.put(fieldName, entry.getValue()); + } + } } + + protected void fillBeanAttrs(Object bean, Map attrs) { + List fields = new ArrayList<>(); + List methods = new ArrayList<>(); + + Method[] methodArray = bean.getClass().getMethods(); + for (Method m : methodArray) { + if (m.getParameterCount() != 0 || m.getReturnType() == void.class) { + continue; + } + + String methodName = m.getName(); + int indexOfGet = methodName.indexOf("get"); + if (indexOfGet == 0 && methodName.length() > 3) { // Only getter + String attrName = methodName.substring(3); + if (!attrName.equals("Class")) { // Ignore Object.getClass() + fields.add(StrKit.firstCharToLowerCase(attrName)); + methods.add(m); + } + } else { + int indexOfIs = methodName.indexOf("is"); + if (indexOfIs == 0 && methodName.length() > 2) { + String attrName = methodName.substring(2); + fields.add(StrKit.firstCharToLowerCase(attrName)); + methods.add(m); + } + } + } + + if (fields.size() > 0) { + int index = 0; + for (String field : fields) { + try { + Object value = methods.get(index++).invoke(bean); + attrs.put(field, value); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + } + } + + @Override public T parse(String jsonString, Class type) { return JSON.parseObject(jsonString, type); diff --git a/src/test/java/io/jboot/test/db/model/User.java b/src/test/java/io/jboot/test/db/model/User.java index 761b61fe..7c962643 100644 --- a/src/test/java/io/jboot/test/db/model/User.java +++ b/src/test/java/io/jboot/test/db/model/User.java @@ -5,4 +5,12 @@ import io.jboot.db.model.JbootModel; @Table(tableName = "user",primaryKey = "id") public class User extends JbootModel { + + public String getSexString(){ + return "男"; + } + + public String getId(){ + return "111"; + } } diff --git a/src/test/java/io/jboot/test/json/JsonTester.java b/src/test/java/io/jboot/test/json/JsonTester.java index 4a39b8b3..796c7781 100644 --- a/src/test/java/io/jboot/test/json/JsonTester.java +++ b/src/test/java/io/jboot/test/json/JsonTester.java @@ -13,6 +13,8 @@ public class JsonTester { user.put("id",100); user.put("tenant_id","xxx"); + user.put("other_user",new User()); + System.out.println(new JbootJson().toJson(user)); } } -- Gitee From f0dd74df41567bfe8f1e9ed26c313c3a70fc1ba1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 15 Jun 2020 12:35:55 +0800 Subject: [PATCH 0337/1965] =?UTF-8?q?fixed:=20Model=20=E7=9A=84=20getter?= =?UTF-8?q?=20=E6=96=B9=E6=B3=95=E4=B8=8D=E8=BE=93=E5=87=BA=20json=20?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/web/JbootJson.java | 87 ++++++++++++++++------- 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index 3622d0dc..37733273 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -33,7 +33,8 @@ import java.util.Map; public class JbootJson extends JFinalJson { - private boolean isCamelCaseJsonStyleEnable = Jboot.config(JbootWebConfig.class).isCamelCaseJsonStyleEnable(); + protected boolean isCamelCaseJsonStyleEnable = Jboot.config(JbootWebConfig.class).isCamelCaseJsonStyleEnable(); + protected Map methodAndFieldsCache = new HashMap<>(); public JbootJson() { @@ -84,38 +85,23 @@ public class JbootJson extends JFinalJson { protected void fillBeanAttrs(Object bean, Map attrs) { - List fields = new ArrayList<>(); - List methods = new ArrayList<>(); - Method[] methodArray = bean.getClass().getMethods(); - for (Method m : methodArray) { - if (m.getParameterCount() != 0 || m.getReturnType() == void.class) { - continue; - } - - String methodName = m.getName(); - int indexOfGet = methodName.indexOf("get"); - if (indexOfGet == 0 && methodName.length() > 3) { // Only getter - String attrName = methodName.substring(3); - if (!attrName.equals("Class")) { // Ignore Object.getClass() - fields.add(StrKit.firstCharToLowerCase(attrName)); - methods.add(m); - } - } else { - int indexOfIs = methodName.indexOf("is"); - if (indexOfIs == 0 && methodName.length() > 2) { - String attrName = methodName.substring(2); - fields.add(StrKit.firstCharToLowerCase(attrName)); - methods.add(m); + MethodsAndFieldsWrapper wrapper = methodAndFieldsCache.get(bean.getClass()); + if (wrapper == null) { + synchronized (this) { + if (wrapper == null) { + wrapper = new MethodsAndFieldsWrapper(bean.getClass()); + } else { + wrapper = methodAndFieldsCache.get(bean.getClass()); } } } - if (fields.size() > 0) { + if (wrapper.fields.size() > 0) { int index = 0; - for (String field : fields) { + for (String field : wrapper.fields) { try { - Object value = methods.get(index++).invoke(bean); + Object value = wrapper.methods.get(index++).invoke(bean); attrs.put(field, value); } catch (IllegalAccessException e) { e.printStackTrace(); @@ -127,6 +113,55 @@ public class JbootJson extends JFinalJson { } + public static class MethodsAndFieldsWrapper { + + private Class reflectiveClass; + + private List fields = new ArrayList<>(); + private List methods = new ArrayList<>(); + + public MethodsAndFieldsWrapper(Class reflectiveClass) { + this.reflectiveClass = reflectiveClass; + + Method[] methodArray = reflectiveClass.getMethods(); + for (Method m : methodArray) { + if (m.getParameterCount() != 0 || m.getReturnType() == void.class) { + continue; + } + + String methodName = m.getName(); + int indexOfGet = methodName.indexOf("get"); + if (indexOfGet == 0 && methodName.length() > 3) { // Only getter + String attrName = methodName.substring(3); + if (!attrName.equals("Class")) { // Ignore Object.getClass() + fields.add(StrKit.firstCharToLowerCase(attrName)); + methods.add(m); + } + } else { + int indexOfIs = methodName.indexOf("is"); + if (indexOfIs == 0 && methodName.length() > 2) { + String attrName = methodName.substring(2); + fields.add(StrKit.firstCharToLowerCase(attrName)); + methods.add(m); + } + } + } + } + + public Class getReflectiveClass() { + return reflectiveClass; + } + + public List getFields() { + return fields; + } + + public List getMethods() { + return methods; + } + } + + @Override public T parse(String jsonString, Class type) { return JSON.parseObject(jsonString, type); -- Gitee From b003fb6d21540daff4a91b9b0f3cbc1bdbe9f98d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 15 Jun 2020 12:40:51 +0800 Subject: [PATCH 0338/1965] =?UTF-8?q?fixed:=20Model=20=E7=9A=84=20getter?= =?UTF-8?q?=20=E6=96=B9=E6=B3=95=E4=B8=8D=E8=BE=93=E5=87=BA=20json=20?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/web/JbootJson.java | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index 37733273..cda62f99 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -62,29 +62,29 @@ public class JbootJson extends JFinalJson { return; } - Map attrs = new HashMap<>(); + Map map = new HashMap<>(); - fillMapToAttr(CPI.getAttrs(value), attrs); - fillBeanAttrs(value, attrs); + fillMapToMap(CPI.getAttrs(value), map); + fillBeanGetterValueToMap(value, map); - JFinalJsonKit.mapToJson(attrs, depth, ret); + JFinalJsonKit.mapToJson(map, depth, ret); }; - protected void fillMapToAttr(Map fillMap, Map toAttr) { + protected void fillMapToMap(Map fillMap, Map toMap) { if (fillMap != null && !fillMap.isEmpty()) { for (Map.Entry entry : fillMap.entrySet()) { String fieldName = entry.getKey(); if (isCamelCaseJsonStyleEnable) { fieldName = StrKit.toCamelCase(fieldName, true); } - toAttr.put(fieldName, entry.getValue()); + toMap.put(fieldName, entry.getValue()); } } } - protected void fillBeanAttrs(Object bean, Map attrs) { + protected void fillBeanGetterValueToMap(Object bean, Map toMap) { MethodsAndFieldsWrapper wrapper = methodAndFieldsCache.get(bean.getClass()); if (wrapper == null) { @@ -102,7 +102,7 @@ public class JbootJson extends JFinalJson { for (String field : wrapper.fields) { try { Object value = wrapper.methods.get(index++).invoke(bean); - attrs.put(field, value); + toMap.put(field, value); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { @@ -115,13 +115,10 @@ public class JbootJson extends JFinalJson { public static class MethodsAndFieldsWrapper { - private Class reflectiveClass; - private List fields = new ArrayList<>(); private List methods = new ArrayList<>(); public MethodsAndFieldsWrapper(Class reflectiveClass) { - this.reflectiveClass = reflectiveClass; Method[] methodArray = reflectiveClass.getMethods(); for (Method m : methodArray) { @@ -148,9 +145,6 @@ public class JbootJson extends JFinalJson { } } - public Class getReflectiveClass() { - return reflectiveClass; - } public List getFields() { return fields; -- Gitee From 578d5e52fd6205dc03ddb0c9f09e9c2d197a0f08 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 15 Jun 2020 16:34:11 +0800 Subject: [PATCH 0339/1965] v3.2.3 release (^.^)YYa!! --- changes.txt | 6 ++++++ pom.xml | 2 +- src/main/java/io/jboot/web/JbootJson.java | 15 +++++++++++---- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/changes.txt b/changes.txt index 21d59663..c8cc8a86 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.2.3: +优化:升级 fastsjon 到最新版本 1.2.71 +修复:Model 的 getter 方法无法输出 json 的问题 + + + jboot v3.2.2: 优化:当项目启动的时候,优先初始化中央配置,以防止 undertow 端口等无法在中央仓库配置的问题 优化:新增 Columns.in(list) 和 Columns.notIn(list) 方法 diff --git a/pom.xml b/pom.xml index 06b84a09..65b5a429 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.3-SNAPSHOT + 3.2.3 jar jboot diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index cda62f99..b3186f04 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -49,7 +49,7 @@ public class JbootJson extends JFinalJson { setToJsonFactory(o -> { if (o instanceof Model) { - return jbootModelJson; + return jbootModelToJson; } else { return null; } @@ -57,15 +57,18 @@ public class JbootJson extends JFinalJson { } - protected JFinalJsonKit.ToJson jbootModelJson = (value, depth, ret) -> { + protected JFinalJsonKit.ToJson jbootModelToJson = (model, depth, ret) -> { if (JFinalJsonKit.checkDepth(depth--, ret)) { return; } Map map = new HashMap<>(); - fillMapToMap(CPI.getAttrs(value), map); - fillBeanGetterValueToMap(value, map); + fillMapToMap(CPI.getAttrs(model), map); + fillBeanGetterValueToMap(model, map); + + + optimizeMapAttrs(model, map); JFinalJsonKit.mapToJson(map, depth, ret); }; @@ -113,6 +116,10 @@ public class JbootJson extends JFinalJson { } + protected void optimizeMapAttrs(Model model, Map map) { + } + + public static class MethodsAndFieldsWrapper { private List fields = new ArrayList<>(); -- Gitee From 79dd6d3b054c0eae818637488a64413e34cfc76f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 15 Jun 2020 16:34:59 +0800 Subject: [PATCH 0340/1965] v3.2.3 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 03b7828e..6ea0ccbd 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.2.2 + 3.2.3 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index f5ea0299..66078541 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.2.2 + 3.2.3 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 115aa28e..25c6a452 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.2.2 + 3.2.3 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index bc7c0dfd..979c64dc 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.2.2"; + public static String VERSION = "3.2.3"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 5971af2abac8d2c3fdd794a28f797f8955e21457 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 18 Jun 2020 15:28:00 +0800 Subject: [PATCH 0341/1965] v3.2.4 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 65b5a429..ec49a101 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.3 + 3.2.4-SNAPSHOT jar jboot -- Gitee From 70ff3c4320c4ee30a99177e9bec996bc9bd6e4df Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 18 Jun 2020 16:58:48 +0800 Subject: [PATCH 0342/1965] v3.2.4 --- src/main/java/io/jboot/utils/ClassScanner.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index af509802..34275ebe 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -224,6 +224,8 @@ public class ClassScanner { excludeJars.add("xmlbeans-"); excludeJars.add("master-slave-core-"); excludeJars.add("shadow-core-rewrite-"); + excludeJars.add("apiguardian-api-"); + excludeJars.add("opentest4j-"); } -- Gitee From 9255a4b7858ad35459a7f25f643025f5421f05b7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 19 Jun 2020 10:47:28 +0800 Subject: [PATCH 0343/1965] v3.2.4 --- src/main/java/io/jboot/web/JbootJson.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index b3186f04..a2c49a0c 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -25,6 +25,7 @@ import io.jboot.Jboot; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -129,7 +130,9 @@ public class JbootJson extends JFinalJson { Method[] methodArray = reflectiveClass.getMethods(); for (Method m : methodArray) { - if (m.getParameterCount() != 0 || m.getReturnType() == void.class) { + if (m.getParameterCount() != 0 + || m.getReturnType() == void.class + || !Modifier.isPublic(m.getModifiers())) { continue; } -- Gitee From 2f68e4fa5aa05745fe603773f108d06d44e1291f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 19 Jun 2020 14:46:20 +0800 Subject: [PATCH 0344/1965] add camelCaseToLowerCaseAnyway config to JbootJson --- src/main/java/io/jboot/web/JbootJson.java | 5 +++-- src/main/java/io/jboot/web/JbootWebConfig.java | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index a2c49a0c..225e4b79 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -35,6 +35,7 @@ import java.util.Map; public class JbootJson extends JFinalJson { protected boolean isCamelCaseJsonStyleEnable = Jboot.config(JbootWebConfig.class).isCamelCaseJsonStyleEnable(); + protected boolean camelCaseToLowerCaseAnyway = Jboot.config(JbootWebConfig.class).isCamelCaseToLowerCaseAnyway(); protected Map methodAndFieldsCache = new HashMap<>(); public JbootJson() { @@ -44,7 +45,7 @@ public class JbootJson extends JFinalJson { //默认设置为 CamelCase 的属性模式 if (isCamelCaseJsonStyleEnable) { - setModelAndRecordFieldNameToCamelCase(); + setModelAndRecordFieldNameConverter((fieldName) -> StrKit.toCamelCase(fieldName, camelCaseToLowerCaseAnyway)); } @@ -80,7 +81,7 @@ public class JbootJson extends JFinalJson { for (Map.Entry entry : fillMap.entrySet()) { String fieldName = entry.getKey(); if (isCamelCaseJsonStyleEnable) { - fieldName = StrKit.toCamelCase(fieldName, true); + fieldName = StrKit.toCamelCase(fieldName, camelCaseToLowerCaseAnyway); } toMap.put(fieldName, entry.getValue()); } diff --git a/src/main/java/io/jboot/web/JbootWebConfig.java b/src/main/java/io/jboot/web/JbootWebConfig.java index d9525686..ec6e130a 100644 --- a/src/main/java/io/jboot/web/JbootWebConfig.java +++ b/src/main/java/io/jboot/web/JbootWebConfig.java @@ -31,6 +31,7 @@ public class JbootWebConfig { private String cookieEncryptKey = DEFAULT_COOKIE_ENCRYPT_KEY; private String webSocketEndpoint; private boolean camelCaseJsonStyleEnable = true; + private boolean camelCaseToLowerCaseAnyway = false; public String getCookieEncryptKey() { return cookieEncryptKey; @@ -55,4 +56,12 @@ public class JbootWebConfig { public void setCamelCaseJsonStyleEnable(boolean camelCaseJsonStyleEnable) { this.camelCaseJsonStyleEnable = camelCaseJsonStyleEnable; } + + public boolean isCamelCaseToLowerCaseAnyway() { + return camelCaseToLowerCaseAnyway; + } + + public void setCamelCaseToLowerCaseAnyway(boolean camelCaseToLowerCaseAnyway) { + this.camelCaseToLowerCaseAnyway = camelCaseToLowerCaseAnyway; + } } -- Gitee From fffecb6226089cabd1f34e4c565dea3dc01a3416 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 22 Jun 2020 09:26:12 +0800 Subject: [PATCH 0345/1965] v3.2.4 release (^.^)YYa!! --- src/test/java/io/jboot/test/json/JsonTester.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/io/jboot/test/json/JsonTester.java b/src/test/java/io/jboot/test/json/JsonTester.java index 796c7781..a1279b66 100644 --- a/src/test/java/io/jboot/test/json/JsonTester.java +++ b/src/test/java/io/jboot/test/json/JsonTester.java @@ -14,6 +14,7 @@ public class JsonTester { user.put("tenant_id","xxx"); user.put("other_user",new User()); + user.put("myAbcDef",new User()); System.out.println(new JbootJson().toJson(user)); } -- Gitee From 832aed5d96a19c85707ea5f3c762e5ee7e45c45a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 22 Jun 2020 09:28:43 +0800 Subject: [PATCH 0346/1965] v3.2.4 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6ea0ccbd..4e7a84a6 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.2.3 + 3.2.4 ``` diff --git a/changes.txt b/changes.txt index c8cc8a86..9179c864 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.2.4: +优化:ClassScanner 和 JbootJson +新增:为 JbootJson 新增 camelCaseToLowerCaseAnyway 配置,默认为 false + + + jboot v3.2.3: 优化:升级 fastsjon 到最新版本 1.2.71 修复:Model 的 getter 方法无法输出 json 的问题 diff --git a/doc/docs/install.md b/doc/docs/install.md index 66078541..63b17ba1 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.2.3 + 3.2.4 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 25c6a452..143e7634 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.2.3 + 3.2.4 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 979c64dc..ca8ee1a8 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.2.3"; + public static String VERSION = "3.2.4"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 821358fc3177519eb62c36c968011a31929c07b2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 22 Jun 2020 09:29:03 +0800 Subject: [PATCH 0347/1965] v3.2.4 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ec49a101..e73b29a2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.4-SNAPSHOT + 3.2.4 jar jboot -- Gitee From 53fa9edb2bb139d3388ba764dee6a9a67a4e4e4f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 29 Jun 2020 15:45:27 +0800 Subject: [PATCH 0348/1965] =?UTF-8?q?fixed=20=EF=BC=9A=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=20JbootAppListener=20=E6=97=A0=E6=B3=95=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E6=AD=A3=E7=A1=AE=E6=B3=A8=E5=85=A5=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/core/listener/JbootAppListenerManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java index 0eab085b..e2ceacb4 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java @@ -54,7 +54,7 @@ public class JbootAppListenerManager implements JbootAppListener { continue; } - JbootAppListener listener = ClassUtil.newInstance(clazz, false); + JbootAppListener listener = ClassUtil.newInstance(clazz); if (listener != null) { listeners.add(listener); } -- Gitee From 1b3b92e7eab88f0e46c81d369aeb777790f257f7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 1 Jul 2020 12:29:53 +0800 Subject: [PATCH 0349/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9AColumns=20?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E7=9B=B4=E6=8E=A5=20safeMode=20=EF=BC=8C?= =?UTF-8?q?=E5=BD=93=E4=BC=A0=E5=85=A5=E7=9A=84=E6=9F=A5=E8=AF=A2=E5=80=BC?= =?UTF-8?q?=20null=20=E5=80=BC=E7=9A=84=E6=97=B6=E5=80=99=EF=BC=8C?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E8=BF=94=E5=9B=9E=E7=A9=BA=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/Columns.java | 75 ++++++++++++++++++- src/main/java/io/jboot/db/model/Group.java | 2 +- .../java/io/jboot/db/model/JbootModel.java | 32 ++++++-- src/main/java/io/jboot/db/model/Util.java | 11 +++ .../io/jboot/test/db/simple/DbController.java | 12 +++ 5 files changed, 123 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index ee0473c5..c75735c4 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -34,11 +34,42 @@ public class Columns implements Serializable { private List cols; + /** + * 在很多场景下,只会根据字段来查询,如果字段值为 null 的情况,Columns 会直接忽略 null 值,此时会造成结果不准确的情况 + * + * 比如 : + * ``` + * public ShopInfo findFirstByAccountId(BigInteger accountId) { + * return findFirstByColumns(Columns.create("account_id", accountId)); + * } + * ``` + * 根据账户 id 来查询账户的 门店,此时 如果传入 null 值,则返回了 第一个门店,和我们想要的结果集是不同的。 + * + * 准确的结果,应该是当用户传入 null 值的时候,应该直接 返回 null 。 + * + * 此时,我们可以使用如下代码进行查询。 + * + * ``` + * public ShopInfo findFirstByAccountId(BigInteger accountId) { + * return findFirstByColumns(Columns.safeMode().eq("account_id", accountId)); + * } + * ``` + * + * 使用 safeMode 的时候,默认传入的值必须全部不为空,才能返回结果,否则直接返回 null 。 + */ + private boolean useSafeMode = false; + private boolean hasNullOrEmptyValue = false; + public static Columns create() { return new Columns(); } + + public static Columns safeMode() { + return new Columns().useSafeMode(); + } + public static Columns create(Column column) { Columns that = new Columns(); that.add(column); @@ -67,7 +98,11 @@ public class Columns implements Serializable { //do not add null value column if (column.hasPara() && column.getValue() == null) { - return; + if (useSafeMode) { + hasNullOrEmptyValue = true; + } else { + return; + } } if (this.cols == null) { @@ -129,6 +164,9 @@ public class Columns implements Serializable { */ public Columns likeAppendPercent(String name, Object value) { if (value == null || StrUtil.isBlank(value.toString())) { + if (useSafeMode) { + hasNullOrEmptyValue = true; + } //do nothing return this; } @@ -245,6 +283,7 @@ public class Columns implements Serializable { * @return */ public Columns in(String name, Object... arrays) { + Util.checkNullParas(this,arrays); this.add(Column.create(name, arrays, Column.LOGIC_IN)); return this; } @@ -272,6 +311,7 @@ public class Columns implements Serializable { * @return */ public Columns notIn(String name, Object... arrays) { + Util.checkNullParas(this,arrays); this.add(Column.create(name, arrays, Column.LOGIC_NOT_IN)); return this; } @@ -301,6 +341,7 @@ public class Columns implements Serializable { * @return */ public Columns between(String name, Object start, Object end) { + Util.checkNullParas(this,start,end); this.add(Column.create(name, new Object[]{start, end}, Column.LOGIC_BETWEEN)); return this; } @@ -314,6 +355,7 @@ public class Columns implements Serializable { * @return */ public Columns notBetween(String name, Object start, Object end) { + Util.checkNullParas(this,start,end); this.add(Column.create(name, new Object[]{start, end}, Column.LOGIC_NOT_BETWEEN)); return this; } @@ -373,6 +415,7 @@ public class Columns implements Serializable { * @return */ public Columns sqlPart(String sql, Object... paras) { + Util.checkNullParas(this,paras); if (StrUtil.isNotBlank(sql)) { this.add(new SqlPart(sql, paras)); } @@ -402,6 +445,7 @@ public class Columns implements Serializable { * @return */ public Columns sqlPartIf(String sql, boolean condition, Object... paras) { + Util.checkNullParas(this,paras); if (condition && StrUtil.isNotBlank(sql)) { this.add(new SqlPart(sql, paras)); } @@ -429,6 +473,7 @@ public class Columns implements Serializable { * @return */ public Columns sqlPartWithoutLink(String sql, Object... paras) { + Util.checkNullParas(this,paras); if (StrUtil.isNotBlank(sql)) { this.add(new SqlPart(sql, paras, true)); } @@ -458,6 +503,7 @@ public class Columns implements Serializable { * @return */ public Columns sqlPartWithoutLinkIf(String sql, boolean condition, Object... paras) { + Util.checkNullParas(this,paras); if (condition && StrUtil.isNotBlank(sql)) { this.add(new SqlPart(sql, paras, true)); } @@ -472,6 +518,8 @@ public class Columns implements Serializable { public Columns ors(String name, String logic, Object... values) { + Util.checkNullParas(this,values); + for (int i = 0; i < values.length; i++) { Object value = values[i]; if (value != null) { @@ -519,6 +567,27 @@ public class Columns implements Serializable { return this; } + public boolean isUseSafeMode() { + return useSafeMode; + } + + public Columns useSafeMode() { + this.useSafeMode = true; + return this; + } + + public Columns unUseSafeMode() { + this.useSafeMode = false; + return this; + } + + public boolean hasNullOrEmptyValue() { + return hasNullOrEmptyValue; + } + + public void setHasNullOrEmptyValue(boolean hasNullOrEmptyValue) { + this.hasNullOrEmptyValue = hasNullOrEmptyValue; + } public boolean isEmpty() { return cols == null || cols.isEmpty(); @@ -657,7 +726,7 @@ public class Columns implements Serializable { public static void main(String[] args) { - Columns columns = Columns.create().or().or().or().eq("aa", "bb").or().or().or().notIn("aaa", 123, 456, 789).like("titile", "a"); + Columns columns = Columns.create().useSafeMode().or().or().or().eq("aa", "bb").or().or().or().notIn("aaa", 123, 456, 789).like("titile", "a"); columns.group(Columns.create().or().or().sqlPart("aa=bb")); columns.group(Columns.create("aa", "bb").eq("cc", "dd") .group(Columns.create("aa", "bb").eq("cc", "dd")) @@ -675,7 +744,7 @@ public class Columns implements Serializable { columns.or(); columns.or(); columns.group(Columns.create().likeAppendPercent("name", "null").or().or().or() - .eq("age", "18").eq("ddd", "ddd")); + .eq("age", "18").eq("ddd", null)); columns.or(); columns.or(); diff --git a/src/main/java/io/jboot/db/model/Group.java b/src/main/java/io/jboot/db/model/Group.java index 260e7ae8..0c00acdd 100644 --- a/src/main/java/io/jboot/db/model/Group.java +++ b/src/main/java/io/jboot/db/model/Group.java @@ -39,7 +39,7 @@ public class Group extends Column { @Override public Object getValue() { - if (columns == null || columns.isEmpty()){ + if (columns == null || columns.isEmpty() || (columns.isUseSafeMode() && columns.hasNullOrEmptyValue())){ return null; }else { return Util.getValueArray(columns.getList()); diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index bd91f162..047568dc 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -426,6 +426,12 @@ public class JbootModel> extends Model { } public M findFirstByColumns(Columns columns, String orderby, String loadColumns) { + if (columns.isUseSafeMode() && columns.hasNullOrEmptyValue()) { + if (LOG.isDebugEnabled()) { + LOG.debug("columns has null or empty value in safeMode,return null in findFirstByColumns()"); + } + return null; + } String sql = _getDialect().forFindByColumns(joins, _getTableName(), loadColumns, columns.getList(), orderby, 1); return columns.isEmpty() ? findFirst(sql) : findFirst(sql, columns.getValueArray()); } @@ -505,6 +511,12 @@ public class JbootModel> extends Model { } public List findListByColumns(Columns columns, String orderBy, Integer count, String loadColumns) { + if (columns.isUseSafeMode() && columns.hasNullOrEmptyValue()) { + if (LOG.isDebugEnabled()) { + LOG.debug("columns has null or empty value in safeMode,return null in findListByColumns()"); + } + return null; + } String sql = _getDialect().forFindByColumns(joins, _getTableName(), loadColumns, columns.getList(), orderBy, count); return columns.isEmpty() ? find(sql) : find(sql, columns.getValueArray()); } @@ -550,6 +562,13 @@ public class JbootModel> extends Model { } public Page paginateByColumns(int pageNumber, int pageSize, Columns columns, String orderBy, String loadColumns) { + if (columns.isUseSafeMode() && columns.hasNullOrEmptyValue()) { + if (LOG.isDebugEnabled()) { + LOG.debug("columns has null or empty value in safeMode,return null in paginateByColumns()"); + } + return null; + } + String selectPartSql = _getDialect().forPaginateSelect(loadColumns); String fromPartSql = _getDialect().forPaginateFrom(joins, _getTableName(), columns.getList(), orderBy); @@ -565,12 +584,15 @@ public class JbootModel> extends Model { public long findCountByColumns(Columns columns) { - return findCountByColumns(columns.getList()); - } + if (columns.isUseSafeMode() && columns.hasNullOrEmptyValue()) { + if (LOG.isDebugEnabled()) { + LOG.debug("columns has null or empty value in safeMode,return 0 in findCountByColumns()"); + } + return 0; + } - public long findCountByColumns(List columns) { - String sql = _getDialect().forFindCountByColumns(joins, _getTableName(), columns); - Long value = Db.use(_getConfig().getName()).queryLong(sql, Util.getValueArray(columns)); + String sql = _getDialect().forFindCountByColumns(joins, _getTableName(), columns.getList()); + Long value = Db.use(_getConfig().getName()).queryLong(sql, Util.getValueArray(columns.getList())); return value == null ? 0 : value; } diff --git a/src/main/java/io/jboot/db/model/Util.java b/src/main/java/io/jboot/db/model/Util.java index 52537322..23e42fcb 100644 --- a/src/main/java/io/jboot/db/model/Util.java +++ b/src/main/java/io/jboot/db/model/Util.java @@ -118,4 +118,15 @@ class Util { b.append("-"); } } + + + static void checkNullParas(Columns columns, Object... paras) { + if (columns.isUseSafeMode()) { + for (Object obj : paras) { + if (obj == null) { + columns.setHasNullOrEmptyValue(true); + } + } + } + } } diff --git a/src/test/java/io/jboot/test/db/simple/DbController.java b/src/test/java/io/jboot/test/db/simple/DbController.java index 05e57c92..c411f0a3 100644 --- a/src/test/java/io/jboot/test/db/simple/DbController.java +++ b/src/test/java/io/jboot/test/db/simple/DbController.java @@ -132,4 +132,16 @@ public class DbController extends JbootController { renderJson(Ret.ok()); } + + public void safeMode(){ + User dao = new User(); + + Columns columns = Columns.safeMode(); + columns.in("user.`id`",1,2,3,4); + columns.likeAppendPercent("login_name",null); + + + List users = dao.leftJoin("article").as("a").on("user.id=a.user_id").findListByColumns(columns); + renderJson(users); + } } -- Gitee From afd11067e8f8a71a1c15185970ca4356e3156414 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 1 Jul 2020 12:30:56 +0800 Subject: [PATCH 0350/1965] 3.2.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e73b29a2..9f1a968c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.4 + 3.2.5-snapshot jar jboot -- Gitee From 1aec52b60d073c73f5f0c9dbba5ee4eed1d6bf08 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 2 Jul 2020 10:28:37 +0800 Subject: [PATCH 0351/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9AColumns=20?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E7=9B=B4=E6=8E=A5=20safeMode=20=EF=BC=8C?= =?UTF-8?q?=E5=BD=93=E4=BC=A0=E5=85=A5=E7=9A=84=E6=9F=A5=E8=AF=A2=E5=80=BC?= =?UTF-8?q?=20null=20=E5=80=BC=E7=9A=84=E6=97=B6=E5=80=99=EF=BC=8C?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E8=BF=94=E5=9B=9E=E7=A9=BA=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/Columns.java | 47 ++++++++++---------- src/main/java/io/jboot/db/model/Util.java | 1 + 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index c75735c4..99cedc80 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -36,26 +36,26 @@ public class Columns implements Serializable { /** * 在很多场景下,只会根据字段来查询,如果字段值为 null 的情况,Columns 会直接忽略 null 值,此时会造成结果不准确的情况 - * + *

* 比如 : * ``` - * public ShopInfo findFirstByAccountId(BigInteger accountId) { - * return findFirstByColumns(Columns.create("account_id", accountId)); - * } + * public ShopInfo findFirstByAccountId(BigInteger accountId) { + * return findFirstByColumns(Columns.create("account_id", accountId)); + * } * ``` * 根据账户 id 来查询账户的 门店,此时 如果传入 null 值,则返回了 第一个门店,和我们想要的结果集是不同的。 - * + *

* 准确的结果,应该是当用户传入 null 值的时候,应该直接 返回 null 。 - * + *

* 此时,我们可以使用如下代码进行查询。 - * - * ``` - * public ShopInfo findFirstByAccountId(BigInteger accountId) { - * return findFirstByColumns(Columns.safeMode().eq("account_id", accountId)); - * } - * ``` - * - * 使用 safeMode 的时候,默认传入的值必须全部不为空,才能返回结果,否则直接返回 null 。 + *

+ * ``` + * public ShopInfo findFirstByAccountId(BigInteger accountId) { + * return findFirstByColumns(Columns.safeMode().eq("account_id", accountId)); + * } + * ``` + *

+ * 使用 safeMode 的时候,默认传入的值必须全部不为空,才能返回结果,否则直接返回 null 。 */ private boolean useSafeMode = false; private boolean hasNullOrEmptyValue = false; @@ -283,7 +283,7 @@ public class Columns implements Serializable { * @return */ public Columns in(String name, Object... arrays) { - Util.checkNullParas(this,arrays); + Util.checkNullParas(this, arrays); this.add(Column.create(name, arrays, Column.LOGIC_IN)); return this; } @@ -311,7 +311,7 @@ public class Columns implements Serializable { * @return */ public Columns notIn(String name, Object... arrays) { - Util.checkNullParas(this,arrays); + Util.checkNullParas(this, arrays); this.add(Column.create(name, arrays, Column.LOGIC_NOT_IN)); return this; } @@ -341,7 +341,7 @@ public class Columns implements Serializable { * @return */ public Columns between(String name, Object start, Object end) { - Util.checkNullParas(this,start,end); + Util.checkNullParas(this, start, end); this.add(Column.create(name, new Object[]{start, end}, Column.LOGIC_BETWEEN)); return this; } @@ -355,7 +355,7 @@ public class Columns implements Serializable { * @return */ public Columns notBetween(String name, Object start, Object end) { - Util.checkNullParas(this,start,end); + Util.checkNullParas(this, start, end); this.add(Column.create(name, new Object[]{start, end}, Column.LOGIC_NOT_BETWEEN)); return this; } @@ -415,7 +415,7 @@ public class Columns implements Serializable { * @return */ public Columns sqlPart(String sql, Object... paras) { - Util.checkNullParas(this,paras); + Util.checkNullParas(this, paras); if (StrUtil.isNotBlank(sql)) { this.add(new SqlPart(sql, paras)); } @@ -445,7 +445,7 @@ public class Columns implements Serializable { * @return */ public Columns sqlPartIf(String sql, boolean condition, Object... paras) { - Util.checkNullParas(this,paras); + Util.checkNullParas(this, paras); if (condition && StrUtil.isNotBlank(sql)) { this.add(new SqlPart(sql, paras)); } @@ -473,7 +473,7 @@ public class Columns implements Serializable { * @return */ public Columns sqlPartWithoutLink(String sql, Object... paras) { - Util.checkNullParas(this,paras); + Util.checkNullParas(this, paras); if (StrUtil.isNotBlank(sql)) { this.add(new SqlPart(sql, paras, true)); } @@ -503,7 +503,7 @@ public class Columns implements Serializable { * @return */ public Columns sqlPartWithoutLinkIf(String sql, boolean condition, Object... paras) { - Util.checkNullParas(this,paras); + Util.checkNullParas(this, paras); if (condition && StrUtil.isNotBlank(sql)) { this.add(new SqlPart(sql, paras, true)); } @@ -518,8 +518,7 @@ public class Columns implements Serializable { public Columns ors(String name, String logic, Object... values) { - Util.checkNullParas(this,values); - + Util.checkNullParas(this, values); for (int i = 0; i < values.length; i++) { Object value = values[i]; if (value != null) { diff --git a/src/main/java/io/jboot/db/model/Util.java b/src/main/java/io/jboot/db/model/Util.java index 23e42fcb..3639046c 100644 --- a/src/main/java/io/jboot/db/model/Util.java +++ b/src/main/java/io/jboot/db/model/Util.java @@ -125,6 +125,7 @@ class Util { for (Object obj : paras) { if (obj == null) { columns.setHasNullOrEmptyValue(true); + break; } } } -- Gitee From e34673a35ca3340e86e1cb3392987b5dec525ade Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 2 Jul 2020 10:37:40 +0800 Subject: [PATCH 0352/1965] v3.2.5 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4e7a84a6..584aeefc 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.2.4 + 3.2.5 ``` diff --git a/changes.txt b/changes.txt index 9179c864..4c0253f4 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.2.5: +新增:Columns 查询添加 safeMode ,使用 safeMode 当传入的查询值 null 值的 sql 参数时,不对齐忽略直接返回空数据。 +修复:JbootAppListener 无法进行正确注入的问题 + + + jboot v3.2.4: 优化:ClassScanner 和 JbootJson 新增:为 JbootJson 新增 camelCaseToLowerCaseAnyway 配置,默认为 false diff --git a/doc/docs/install.md b/doc/docs/install.md index 63b17ba1..985be696 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.2.4 + 3.2.5 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 143e7634..f629c212 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.2.4 + 3.2.5 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index ca8ee1a8..e712cc08 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.2.4"; + public static String VERSION = "3.2.5"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 8efad6fbf28365106cf34ba39100aeefd66d0487 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 2 Jul 2020 10:37:55 +0800 Subject: [PATCH 0353/1965] v3.2.5 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9f1a968c..cf027ee7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.5-snapshot + 3.2.5 jar jboot -- Gitee From a7387fbefeed305536654d8d1cb1585c75fc7c71 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 2 Jul 2020 10:40:08 +0800 Subject: [PATCH 0354/1965] v3.2.5 release (^.^)YYa!! --- changes.txt | 1 + pom.xml | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/changes.txt b/changes.txt index 4c0253f4..1fb171b0 100644 --- a/changes.txt +++ b/changes.txt @@ -1,5 +1,6 @@ jboot v3.2.5: 新增:Columns 查询添加 safeMode ,使用 safeMode 当传入的查询值 null 值的 sql 参数时,不对齐忽略直接返回空数据。 +优化:升级 fastjson、jackson Json 等到最新版本 修复:JbootAppListener 无法进行正确注入的问题 diff --git a/pom.xml b/pom.xml index cf027ee7..6b46700c 100644 --- a/pom.xml +++ b/pom.xml @@ -47,18 +47,18 @@ 2.6 3.3.0 3.4.5 - 2.11.0 + 2.11.1 5.1.48 2.1.2.Final 1.7.30 2.57 - 1.2.71 + 1.2.72 29.0-jre 2.2.5 2.10.2 1.13.1 2.10.6 - 2.8.4 + 2.8.5 3.10 2.7 1.14 -- Gitee From 2318fe5b951cee93243f52679b3d826de526422c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 2 Jul 2020 11:03:01 +0800 Subject: [PATCH 0355/1965] v3.2.6 release (^.^)YYa!! --- src/main/java/io/jboot/db/model/Columns.java | 18 +++++++++++------- src/main/java/io/jboot/db/model/Group.java | 4 ++-- .../java/io/jboot/db/model/JbootModel.java | 8 ++++---- src/main/java/io/jboot/db/model/Util.java | 2 +- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 99cedc80..66274ed3 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -58,7 +58,11 @@ public class Columns implements Serializable { * 使用 safeMode 的时候,默认传入的值必须全部不为空,才能返回结果,否则直接返回 null 。 */ private boolean useSafeMode = false; - private boolean hasNullOrEmptyValue = false; + + /** + * columns 里是否存在 安全模式下( useSafeMode = true )存在 null 值 + */ + private boolean hitUnsafeInSafeMode = false; public static Columns create() { @@ -99,7 +103,7 @@ public class Columns implements Serializable { //do not add null value column if (column.hasPara() && column.getValue() == null) { if (useSafeMode) { - hasNullOrEmptyValue = true; + hitUnsafeInSafeMode = true; } else { return; } @@ -165,7 +169,7 @@ public class Columns implements Serializable { public Columns likeAppendPercent(String name, Object value) { if (value == null || StrUtil.isBlank(value.toString())) { if (useSafeMode) { - hasNullOrEmptyValue = true; + hitUnsafeInSafeMode = true; } //do nothing return this; @@ -580,12 +584,12 @@ public class Columns implements Serializable { return this; } - public boolean hasNullOrEmptyValue() { - return hasNullOrEmptyValue; + public boolean isHitUnsafeInSafeMode() { + return hitUnsafeInSafeMode; } - public void setHasNullOrEmptyValue(boolean hasNullOrEmptyValue) { - this.hasNullOrEmptyValue = hasNullOrEmptyValue; + public void setHitUnsafeInSafeMode(boolean hitUnsafeInSafeMode) { + this.hitUnsafeInSafeMode = hitUnsafeInSafeMode; } public boolean isEmpty() { diff --git a/src/main/java/io/jboot/db/model/Group.java b/src/main/java/io/jboot/db/model/Group.java index 0c00acdd..59bdee50 100644 --- a/src/main/java/io/jboot/db/model/Group.java +++ b/src/main/java/io/jboot/db/model/Group.java @@ -39,9 +39,9 @@ public class Group extends Column { @Override public Object getValue() { - if (columns == null || columns.isEmpty() || (columns.isUseSafeMode() && columns.hasNullOrEmptyValue())){ + if (columns == null || columns.isEmpty() || columns.isHitUnsafeInSafeMode()) { return null; - }else { + } else { return Util.getValueArray(columns.getList()); } } diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 047568dc..f1ce675e 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -426,7 +426,7 @@ public class JbootModel> extends Model { } public M findFirstByColumns(Columns columns, String orderby, String loadColumns) { - if (columns.isUseSafeMode() && columns.hasNullOrEmptyValue()) { + if (columns.isHitUnsafeInSafeMode()) { if (LOG.isDebugEnabled()) { LOG.debug("columns has null or empty value in safeMode,return null in findFirstByColumns()"); } @@ -511,7 +511,7 @@ public class JbootModel> extends Model { } public List findListByColumns(Columns columns, String orderBy, Integer count, String loadColumns) { - if (columns.isUseSafeMode() && columns.hasNullOrEmptyValue()) { + if (columns.isHitUnsafeInSafeMode()) { if (LOG.isDebugEnabled()) { LOG.debug("columns has null or empty value in safeMode,return null in findListByColumns()"); } @@ -562,7 +562,7 @@ public class JbootModel> extends Model { } public Page paginateByColumns(int pageNumber, int pageSize, Columns columns, String orderBy, String loadColumns) { - if (columns.isUseSafeMode() && columns.hasNullOrEmptyValue()) { + if (columns.isHitUnsafeInSafeMode()) { if (LOG.isDebugEnabled()) { LOG.debug("columns has null or empty value in safeMode,return null in paginateByColumns()"); } @@ -584,7 +584,7 @@ public class JbootModel> extends Model { public long findCountByColumns(Columns columns) { - if (columns.isUseSafeMode() && columns.hasNullOrEmptyValue()) { + if (columns.isHitUnsafeInSafeMode()) { if (LOG.isDebugEnabled()) { LOG.debug("columns has null or empty value in safeMode,return 0 in findCountByColumns()"); } diff --git a/src/main/java/io/jboot/db/model/Util.java b/src/main/java/io/jboot/db/model/Util.java index 3639046c..5bd52250 100644 --- a/src/main/java/io/jboot/db/model/Util.java +++ b/src/main/java/io/jboot/db/model/Util.java @@ -124,7 +124,7 @@ class Util { if (columns.isUseSafeMode()) { for (Object obj : paras) { if (obj == null) { - columns.setHasNullOrEmptyValue(true); + columns.setHitUnsafeInSafeMode(true); break; } } -- Gitee From b6cd8e02298b8e080887b5aee702b8b5cf049148 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 2 Jul 2020 11:05:08 +0800 Subject: [PATCH 0356/1965] v3.2.6 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 5 +++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 584aeefc..2b4937b7 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.2.5 + 3.2.6 ``` diff --git a/changes.txt b/changes.txt index 1fb171b0..e00b3aa9 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,8 @@ +jboot v3.2.6: +修复:Columns 调用 unUseSafeMode 后可能存在问题的 bug + + + jboot v3.2.5: 新增:Columns 查询添加 safeMode ,使用 safeMode 当传入的查询值 null 值的 sql 参数时,不对齐忽略直接返回空数据。 优化:升级 fastjson、jackson Json 等到最新版本 diff --git a/doc/docs/install.md b/doc/docs/install.md index 985be696..4f0f1a19 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.2.5 + 3.2.6 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index f629c212..3c3d7881 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.2.5 + 3.2.6 ``` diff --git a/pom.xml b/pom.xml index 6b46700c..1676dc6d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.5 + 3.2.6 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index e712cc08..d55c84cc 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.2.5"; + public static String VERSION = "3.2.6"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 6f3dcdd7c8da7b05f7eeccd9b877ef29e7c16003 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 3 Jul 2020 10:11:52 +0800 Subject: [PATCH 0357/1965] v3.2.7 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1676dc6d..3753a4ef 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.6 + 3.2.7-SNAPSHOT jar jboot -- Gitee From b1f5ef7e1024569df2af4b6983cb60aa5f27caf3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 3 Jul 2020 10:13:50 +0800 Subject: [PATCH 0358/1965] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20JbootModel.build?= =?UTF-8?q?IdCacheKey=20=E4=B8=BA=20protected=20=E4=BF=AE=E9=A5=B0?= =?UTF-8?q?=EF=BC=8C=E6=96=B9=E4=BE=BF=E5=9C=A8=E6=9F=90=E4=BA=9B=E6=83=85?= =?UTF-8?q?=E5=86=B5=E4=B8=8B=E8=BF=9B=E8=A1=8C=E9=87=8D=E5=86=99=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/JbootModel.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index f1ce675e..3649c975 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -253,7 +253,7 @@ public class JbootModel> extends Model { protected M loadByCache(Object... idValues) { try { return config.getIdCache().get(_getTableName() - , buildCacheKey(idValues) + , buildIdCacheKey(idValues) , () -> JbootModel.super.findByIds(idValues) , config.getIdCacheTime()); } catch (Exception ex) { @@ -267,9 +267,9 @@ public class JbootModel> extends Model { protected void safeDeleteCache(Object... idValues) { try { config.getIdCache().remove(_getTableName() - , buildCacheKey(idValues)); + , buildIdCacheKey(idValues)); } catch (Exception ex) { - LOG.error("remove cache exception by name [" + _getTableName() + "] and key [" + buildCacheKey(idValues) + "]", ex); + LOG.error("remove cache exception by name [" + _getTableName() + "] and key [" + buildIdCacheKey(idValues) + "]", ex); } } @@ -366,7 +366,7 @@ public class JbootModel> extends Model { } - private static String buildCacheKey(Object... idValues) { + protected String buildIdCacheKey(Object... idValues) { if (idValues == null || idValues.length == 0) { return null; } -- Gitee From b15ae070d2766a3f0d47536fccdedefcce50204c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 3 Jul 2020 10:23:42 +0800 Subject: [PATCH 0359/1965] v3.2.7 snapshot --- .../io/jboot/db/model/JbootModelConfig.java | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModelConfig.java b/src/main/java/io/jboot/db/model/JbootModelConfig.java index a7461c28..c353c291 100644 --- a/src/main/java/io/jboot/db/model/JbootModelConfig.java +++ b/src/main/java/io/jboot/db/model/JbootModelConfig.java @@ -37,12 +37,6 @@ public class JbootModelConfig { private String columnCreated = "created"; private String columnModified = "modified"; - /** - * 是否启用 id 缓存,如果启用,当根据 id 查询的时候,会自动存入缓存 - * 下次再通过 id 查询的时候,直接从缓存中获取 Model - */ - private boolean idCacheEnable = true; - /** * id 缓存的时间,默认为 1 个小时,单位:秒 */ @@ -60,6 +54,13 @@ public class JbootModelConfig { private String primarykeyValueGeneratorClass; + /** + * 是否启用 id 缓存,如果启用,当根据 id 查询的时候,会自动存入缓存 + * 下次再通过 id 查询的时候,直接从缓存中获取 Model + */ + private boolean idCacheEnable = true; + + private String idCacheType = Jboot.config(JbootCacheConfig.class).getType(); @@ -103,6 +104,22 @@ public class JbootModelConfig { this.idCacheTime = idCacheTime; } + public String getFilterClass() { + return filterClass; + } + + public void setFilterClass(String filterClass) { + this.filterClass = filterClass; + } + + public String getPrimarykeyValueGeneratorClass() { + return primarykeyValueGeneratorClass; + } + + public void setPrimarykeyValueGeneratorClass(String primarykeyValueGeneratorClass) { + this.primarykeyValueGeneratorClass = primarykeyValueGeneratorClass; + } + public boolean isIdCacheEnable() { return idCacheEnable; } @@ -119,21 +136,6 @@ public class JbootModelConfig { this.idCacheType = idCacheType; } - public String getFilterClass() { - return filterClass; - } - - public void setFilterClass(String filterClass) { - this.filterClass = filterClass; - } - - public String getPrimarykeyValueGeneratorClass() { - return primarykeyValueGeneratorClass; - } - - public void setPrimarykeyValueGeneratorClass(String primarykeyValueGeneratorClass) { - this.primarykeyValueGeneratorClass = primarykeyValueGeneratorClass; - } private JbootModelFilter filter; public JbootModelFilter getFilter() { -- Gitee From 22048a40a58e3efa61662fa9ddab6ee8d659264c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 3 Jul 2020 12:14:54 +0800 Subject: [PATCH 0360/1965] =?UTF-8?q?fixed=EF=BC=9AColumns=20safeMode=20ca?= =?UTF-8?q?n=20not=20work=20in=20deleteByColumns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/JbootModel.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 3649c975..0d0e8d6b 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -308,6 +308,13 @@ public class JbootModel> extends Model { public boolean deleteByColumns(Columns columns) { + if (columns.isHitUnsafeInSafeMode()) { + if (LOG.isDebugEnabled()) { + LOG.debug("columns has null or empty value in safeMode,return false in deleteByColumns()"); + } + return false; + } + return deleteByColumns(columns.getList()); } -- Gitee From bb6e038221b14a3fc420ea96e68620e7ea2dc412 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 3 Jul 2020 13:01:06 +0800 Subject: [PATCH 0361/1965] throw NullPointerException is has null para value in Columns for safeMode --- src/main/java/io/jboot/db/model/Columns.java | 49 ++++++------------- src/main/java/io/jboot/db/model/Group.java | 2 +- .../java/io/jboot/db/model/JbootModel.java | 33 ------------- src/main/java/io/jboot/db/model/Util.java | 13 ++++- 4 files changed, 26 insertions(+), 71 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 66274ed3..272ec342 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -59,11 +59,6 @@ public class Columns implements Serializable { */ private boolean useSafeMode = false; - /** - * columns 里是否存在 安全模式下( useSafeMode = true )存在 null 值 - */ - private boolean hitUnsafeInSafeMode = false; - public static Columns create() { return new Columns(); @@ -102,11 +97,7 @@ public class Columns implements Serializable { //do not add null value column if (column.hasPara() && column.getValue() == null) { - if (useSafeMode) { - hitUnsafeInSafeMode = true; - } else { - return; - } + return; } if (this.cols == null) { @@ -117,11 +108,6 @@ public class Columns implements Serializable { } - public Columns add(String name, Object value) { - return eq(name, value); - } - - /** * equals * @@ -130,6 +116,7 @@ public class Columns implements Serializable { * @return */ public Columns eq(String name, Object value) { + Util.checkNullParas(this, name, value); this.add(Column.create(name, value)); return this; } @@ -142,6 +129,7 @@ public class Columns implements Serializable { * @return */ public Columns ne(String name, Object value) { + Util.checkNullParas(this, name, value); this.add(Column.create(name, value, Column.LOGIC_NOT_EQUALS)); return this; } @@ -155,6 +143,7 @@ public class Columns implements Serializable { * @return */ public Columns like(String name, Object value) { + Util.checkNullParas(this, name, value); this.add(Column.create(name, value, Column.LOGIC_LIKE)); return this; } @@ -167,13 +156,7 @@ public class Columns implements Serializable { * @return */ public Columns likeAppendPercent(String name, Object value) { - if (value == null || StrUtil.isBlank(value.toString())) { - if (useSafeMode) { - hitUnsafeInSafeMode = true; - } - //do nothing - return this; - } + Util.checkNullParas(this, name, value); this.add(Column.create(name, "%" + value + "%", Column.LOGIC_LIKE)); return this; } @@ -186,6 +169,7 @@ public class Columns implements Serializable { * @return */ public Columns gt(String name, Object value) { + Util.checkNullParas(this, name, value); this.add(Column.create(name, value, Column.LOGIC_GT)); return this; } @@ -198,6 +182,7 @@ public class Columns implements Serializable { * @return */ public Columns ge(String name, Object value) { + Util.checkNullParas(this, name, value); this.add(Column.create(name, value, Column.LOGIC_GE)); return this; } @@ -210,6 +195,7 @@ public class Columns implements Serializable { * @return */ public Columns lt(String name, Object value) { + Util.checkNullParas(this, name, value); this.add(Column.create(name, value, Column.LOGIC_LT)); return this; } @@ -222,6 +208,7 @@ public class Columns implements Serializable { * @return */ public Columns le(String name, Object value) { + Util.checkNullParas(this, name, value); this.add(Column.create(name, value, Column.LOGIC_LE)); return this; } @@ -287,7 +274,7 @@ public class Columns implements Serializable { * @return */ public Columns in(String name, Object... arrays) { - Util.checkNullParas(this, arrays); + Util.checkNullParas(this, name, arrays); this.add(Column.create(name, arrays, Column.LOGIC_IN)); return this; } @@ -315,7 +302,7 @@ public class Columns implements Serializable { * @return */ public Columns notIn(String name, Object... arrays) { - Util.checkNullParas(this, arrays); + Util.checkNullParas(this, name, arrays); this.add(Column.create(name, arrays, Column.LOGIC_NOT_IN)); return this; } @@ -345,7 +332,7 @@ public class Columns implements Serializable { * @return */ public Columns between(String name, Object start, Object end) { - Util.checkNullParas(this, start, end); + Util.checkNullParas(this, name, start, end); this.add(Column.create(name, new Object[]{start, end}, Column.LOGIC_BETWEEN)); return this; } @@ -359,7 +346,7 @@ public class Columns implements Serializable { * @return */ public Columns notBetween(String name, Object start, Object end) { - Util.checkNullParas(this, start, end); + Util.checkNullParas(this, name, start, end); this.add(Column.create(name, new Object[]{start, end}, Column.LOGIC_NOT_BETWEEN)); return this; } @@ -522,7 +509,7 @@ public class Columns implements Serializable { public Columns ors(String name, String logic, Object... values) { - Util.checkNullParas(this, values); + Util.checkNullParas(this, name, values); for (int i = 0; i < values.length; i++) { Object value = values[i]; if (value != null) { @@ -584,14 +571,6 @@ public class Columns implements Serializable { return this; } - public boolean isHitUnsafeInSafeMode() { - return hitUnsafeInSafeMode; - } - - public void setHitUnsafeInSafeMode(boolean hitUnsafeInSafeMode) { - this.hitUnsafeInSafeMode = hitUnsafeInSafeMode; - } - public boolean isEmpty() { return cols == null || cols.isEmpty(); } diff --git a/src/main/java/io/jboot/db/model/Group.java b/src/main/java/io/jboot/db/model/Group.java index 59bdee50..918985ac 100644 --- a/src/main/java/io/jboot/db/model/Group.java +++ b/src/main/java/io/jboot/db/model/Group.java @@ -39,7 +39,7 @@ public class Group extends Column { @Override public Object getValue() { - if (columns == null || columns.isEmpty() || columns.isHitUnsafeInSafeMode()) { + if (columns == null || columns.isEmpty()) { return null; } else { return Util.getValueArray(columns.getList()); diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 0d0e8d6b..22523cf5 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -308,13 +308,6 @@ public class JbootModel> extends Model { public boolean deleteByColumns(Columns columns) { - if (columns.isHitUnsafeInSafeMode()) { - if (LOG.isDebugEnabled()) { - LOG.debug("columns has null or empty value in safeMode,return false in deleteByColumns()"); - } - return false; - } - return deleteByColumns(columns.getList()); } @@ -433,12 +426,6 @@ public class JbootModel> extends Model { } public M findFirstByColumns(Columns columns, String orderby, String loadColumns) { - if (columns.isHitUnsafeInSafeMode()) { - if (LOG.isDebugEnabled()) { - LOG.debug("columns has null or empty value in safeMode,return null in findFirstByColumns()"); - } - return null; - } String sql = _getDialect().forFindByColumns(joins, _getTableName(), loadColumns, columns.getList(), orderby, 1); return columns.isEmpty() ? findFirst(sql) : findFirst(sql, columns.getValueArray()); } @@ -518,12 +505,6 @@ public class JbootModel> extends Model { } public List findListByColumns(Columns columns, String orderBy, Integer count, String loadColumns) { - if (columns.isHitUnsafeInSafeMode()) { - if (LOG.isDebugEnabled()) { - LOG.debug("columns has null or empty value in safeMode,return null in findListByColumns()"); - } - return null; - } String sql = _getDialect().forFindByColumns(joins, _getTableName(), loadColumns, columns.getList(), orderBy, count); return columns.isEmpty() ? find(sql) : find(sql, columns.getValueArray()); } @@ -569,13 +550,6 @@ public class JbootModel> extends Model { } public Page paginateByColumns(int pageNumber, int pageSize, Columns columns, String orderBy, String loadColumns) { - if (columns.isHitUnsafeInSafeMode()) { - if (LOG.isDebugEnabled()) { - LOG.debug("columns has null or empty value in safeMode,return null in paginateByColumns()"); - } - return null; - } - String selectPartSql = _getDialect().forPaginateSelect(loadColumns); String fromPartSql = _getDialect().forPaginateFrom(joins, _getTableName(), columns.getList(), orderBy); @@ -591,13 +565,6 @@ public class JbootModel> extends Model { public long findCountByColumns(Columns columns) { - if (columns.isHitUnsafeInSafeMode()) { - if (LOG.isDebugEnabled()) { - LOG.debug("columns has null or empty value in safeMode,return 0 in findCountByColumns()"); - } - return 0; - } - String sql = _getDialect().forFindCountByColumns(joins, _getTableName(), columns.getList()); Long value = Db.use(_getConfig().getName()).queryLong(sql, Util.getValueArray(columns.getList())); return value == null ? 0 : value; diff --git a/src/main/java/io/jboot/db/model/Util.java b/src/main/java/io/jboot/db/model/Util.java index 5bd52250..b25814a2 100644 --- a/src/main/java/io/jboot/db/model/Util.java +++ b/src/main/java/io/jboot/db/model/Util.java @@ -120,12 +120,21 @@ class Util { } + static void checkNullParas(Columns columns, String name, Object... paras) { + if (columns.isUseSafeMode()) { + for (Object obj : paras) { + if (obj == null) { + throw new NullPointerException("column \"" + name + "\" para is null, must has not null para value in columns for safeMode."); + } + } + } + } + static void checkNullParas(Columns columns, Object... paras) { if (columns.isUseSafeMode()) { for (Object obj : paras) { if (obj == null) { - columns.setHitUnsafeInSafeMode(true); - break; + throw new NullPointerException("must has not null para value in columns for safeMode."); } } } -- Gitee From f12fcce1b4d370637b7c89e381c41d836c4353a1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 3 Jul 2020 14:25:56 +0800 Subject: [PATCH 0362/1965] v3.2.7 release (^.^)YYa!! --- changes.txt | 6 ++++++ pom.xml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index e00b3aa9..6712c810 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.2.7: +优化:修改 JbootModel.buildIdCacheKey 为 protected 修饰,方便在某些情况下进行重写。 +优化:Columns 在 safeMode 模式下,当传入 null 值时,直接抛出空指针异常,更加方便开发调试。 + + + jboot v3.2.6: 修复:Columns 调用 unUseSafeMode 后可能存在问题的 bug diff --git a/pom.xml b/pom.xml index 3753a4ef..5e50c4a3 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.7-SNAPSHOT + 3.2.7 jar jboot -- Gitee From c24103b1e9cd5f2e6f908355077ed55e889813d2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 3 Jul 2020 14:26:35 +0800 Subject: [PATCH 0363/1965] v3.2.7 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2b4937b7..8af8c237 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.2.6 + 3.2.7 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 4f0f1a19..5ac9a931 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.2.6 + 3.2.7 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 3c3d7881..66bd1d28 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.2.6 + 3.2.7 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index d55c84cc..bc3f31ea 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.2.6"; + public static String VERSION = "3.2.7"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From e3e5c30cb600e1dfc8c55e214aaa4ee95221c9f1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 3 Jul 2020 14:29:57 +0800 Subject: [PATCH 0364/1965] v3.2.7 release (^.^)YYa!! --- src/main/java/io/jboot/db/model/Util.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Util.java b/src/main/java/io/jboot/db/model/Util.java index b25814a2..633887cb 100644 --- a/src/main/java/io/jboot/db/model/Util.java +++ b/src/main/java/io/jboot/db/model/Util.java @@ -124,7 +124,7 @@ class Util { if (columns.isUseSafeMode()) { for (Object obj : paras) { if (obj == null) { - throw new NullPointerException("column \"" + name + "\" para is null, must has not null para value in columns for safeMode."); + throw new NullPointerException("column \"" + name + "\" para is null, Columns must has not null para value in safeMode."); } } } @@ -134,7 +134,7 @@ class Util { if (columns.isUseSafeMode()) { for (Object obj : paras) { if (obj == null) { - throw new NullPointerException("must has not null para value in columns for safeMode."); + throw new NullPointerException("Columns must has not null para value in safeMode."); } } } -- Gitee From 5262e2e28157f6728c5d373c17ac5882f3620b55 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 3 Jul 2020 15:54:20 +0800 Subject: [PATCH 0365/1965] v3.2.8 snapshot --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5e50c4a3..ce53cac6 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.7 + 3.2.8-SNAPSHOT jar jboot @@ -48,7 +48,7 @@ 3.3.0 3.4.5 2.11.1 - 5.1.48 + 5.1.49 2.1.2.Final 1.7.30 2.57 -- Gitee From eade8b41905c7d44a0c4c1001b6c52b19a305955 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 6 Jul 2020 10:17:13 +0800 Subject: [PATCH 0366/1965] =?UTF-8?q?fixed=EF=BC=9AColumns.likeAppendPrece?= =?UTF-8?q?nt=20bugs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/Columns.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 272ec342..40995544 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -157,6 +157,9 @@ public class Columns implements Serializable { */ public Columns likeAppendPercent(String name, Object value) { Util.checkNullParas(this, name, value); + if (value == null || (value instanceof String && StrUtil.isBlank((String) value))){ + return this; + } this.add(Column.create(name, "%" + value + "%", Column.LOGIC_LIKE)); return this; } -- Gitee From 1a580d117d78ceca7c802abf46e9804934b1b33e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 6 Jul 2020 16:34:44 +0800 Subject: [PATCH 0367/1965] add print jwt parse error log --- src/main/java/io/jboot/support/jwt/JwtManager.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/support/jwt/JwtManager.java b/src/main/java/io/jboot/support/jwt/JwtManager.java index 9eed78d6..2edbdce5 100644 --- a/src/main/java/io/jboot/support/jwt/JwtManager.java +++ b/src/main/java/io/jboot/support/jwt/JwtManager.java @@ -16,6 +16,7 @@ package io.jboot.support.jwt; import com.jfinal.kit.JsonKit; +import com.jfinal.log.Log; import io.jboot.Jboot; import io.jboot.exception.JbootException; import io.jboot.utils.StrUtil; @@ -36,6 +37,7 @@ import java.util.Map; public class JwtManager { private static final JwtManager me = new JwtManager(); + private static final Log LOG = Log.getLog(JwtManager.class); public static JwtManager me() { return me; @@ -76,19 +78,19 @@ public class JwtManager { .parseClaimsJws(token).getBody(); String jsonString = claims.getSubject(); - - if (StrUtil.isBlank(jsonString)) { - return null; + if (StrUtil.isNotBlank(jsonString)) { + return JsonKit.parse(jsonString, HashMap.class); } - return JsonKit.parse(jsonString, HashMap.class); - } catch (SignatureException | MalformedJwtException e) { // don't trust the JWT! // jwt 签名错误或解析错误,可能是伪造的,不能相信 + LOG.error("do not trast the jwt!",e); } catch (ExpiredJwtException e) { // jwt 已经过期 + LOG.error("jwt is expired!",e); } catch (Throwable ex) { //其他错误 + LOG.error("jwt parseJwtToken error, return null."); } return null; -- Gitee From 704f1da024ec86ab6a5700c6e9b55a4f7bd4a930 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 6 Jul 2020 17:25:25 +0800 Subject: [PATCH 0368/1965] add Columns.toWherePartSql() methods --- src/main/java/io/jboot/db/model/Columns.java | 32 ++++++++++++++++++- .../java/io/jboot/db/model/SqlBuilder.java | 10 ++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 40995544..d2994411 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -157,7 +157,7 @@ public class Columns implements Serializable { */ public Columns likeAppendPercent(String name, Object value) { Util.checkNullParas(this, name, value); - if (value == null || (value instanceof String && StrUtil.isBlank((String) value))){ + if (value == null || (value instanceof String && StrUtil.isBlank((String) value))) { return this; } this.add(Column.create(name, "%" + value + "%", Column.LOGIC_LIKE)); @@ -688,6 +688,29 @@ public class Columns implements Serializable { } } + + /** + * 输出 where 后面的 sql 部分,风格是 mysql 的风格SQL + * @return + */ + public String toWherePartSql() { + return toWherePartSql('`', false); + } + + + /** + * 输出 where 部分的 sql + * @param separator 字段分隔符 + * @param withWhereKeyword 是否带上 "where 关键字" + * @return + */ + public String toWherePartSql(char separator, boolean withWhereKeyword) { + StringBuilder sb = new StringBuilder(); + SqlBuilder.buildWhereSql(sb, getList(), separator, withWhereKeyword); + return sb.toString(); + } + + /** * 这个只是用于调试 * @@ -698,6 +721,11 @@ public class Columns implements Serializable { return dialect.forFindByColumns(null, "table", "*", getList(), null, null); } + + /** + * 这个只是用于调试 + * @return + */ public String toSqlServerSql() { JbootSqlServerDialect dialect = new JbootSqlServerDialect(); return dialect.forFindByColumns(null, "table", "*", getList(), null, null); @@ -751,6 +779,8 @@ public class Columns implements Serializable { System.out.println(columns.getCacheKey()); System.out.println(Arrays.toString(columns.getValueArray())); System.out.println(columns.toMysqlSql()); + System.out.println("-----------"); + System.out.println(columns.toWherePartSql('"',true)); } diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index 797281a4..ae3c88e0 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -38,15 +38,19 @@ public class SqlBuilder { StringBuilder sql = new StringBuilder(45); sql.append("DELETE FROM ").append(separator).append(table).append(separator); - buildJoinSql(sql, joins, ' '); - buildWhereSql(sql, columns, ' '); + buildJoinSql(sql, joins, separator); + buildWhereSql(sql, columns, separator); return sql.toString(); } public static void buildWhereSql(StringBuilder sqlBuilder, List columns, char separator) { + buildWhereSql(sqlBuilder, columns, separator, true); + } + + public static void buildWhereSql(StringBuilder sqlBuilder, List columns, char separator, boolean withWhereKeyword) { if (ArrayUtil.isNullOrEmpty(columns)) { return; } @@ -57,7 +61,7 @@ public class SqlBuilder { String whereSql = whereSqlBuilder.toString(); if (StrUtil.isNotBlank(whereSql)) { - sqlBuilder.append(" WHERE ").append(whereSql); + sqlBuilder.append(withWhereKeyword ? " WHERE " : "").append(whereSql); } } -- Gitee From c577744c77d482076723443a789d92d6e77831cf Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 7 Jul 2020 12:39:28 +0800 Subject: [PATCH 0369/1965] add ObjectFunc.java --- src/main/java/io/jboot/utils/ObjectFunc.java | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/io/jboot/utils/ObjectFunc.java diff --git a/src/main/java/io/jboot/utils/ObjectFunc.java b/src/main/java/io/jboot/utils/ObjectFunc.java new file mode 100644 index 00000000..774fe848 --- /dev/null +++ b/src/main/java/io/jboot/utils/ObjectFunc.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.utils; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public interface ObjectFunc{ + public Object get(T Object); +} -- Gitee From 9738febb494d7ced58eb7af2ee8e83f1b42bc37c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 7 Jul 2020 12:40:19 +0800 Subject: [PATCH 0370/1965] rename joinById() to joinByColumnValue() --- .../io/jboot/service/JbootServiceBase.java | 7 +- .../io/jboot/service/JbootServiceJoiner.java | 24 ++--- .../jboot/service/JbootServiceJoinerImpl.java | 98 ++++++++++--------- 3 files changed, 67 insertions(+), 62 deletions(-) diff --git a/src/main/java/io/jboot/service/JbootServiceBase.java b/src/main/java/io/jboot/service/JbootServiceBase.java index 49bcb622..e16cedde 100644 --- a/src/main/java/io/jboot/service/JbootServiceBase.java +++ b/src/main/java/io/jboot/service/JbootServiceBase.java @@ -299,14 +299,15 @@ public class JbootServiceBase> /** * 复写 JbootServiceJoinerImpl 的方法 * - * @param id + * @param columnValue * @return */ @Override - protected JbootModel joinById(Object id) { - return findById(id); + protected JbootModel joinByColumnValue(Object columnValue,Model sourceModel) { + return findById(columnValue); } + /** * 用于给子类复写,用于刷新缓存 * diff --git a/src/main/java/io/jboot/service/JbootServiceJoiner.java b/src/main/java/io/jboot/service/JbootServiceJoiner.java index 125434b7..2961f43d 100644 --- a/src/main/java/io/jboot/service/JbootServiceJoiner.java +++ b/src/main/java/io/jboot/service/JbootServiceJoiner.java @@ -26,31 +26,31 @@ import java.util.List; public interface JbootServiceJoiner { - public Page join(Page page, String joinOnField); + public Page join(Page page, String columnName); - public Page join(Page page, String joinOnField, String[] attrs); + public Page join(Page page, String columnName, String[] attrs); - public Page join(Page page, String joinOnField, String joinName); + public Page join(Page page, String columnName, String joinName); - public Page join(Page page, String joinOnField, String joinName, String[] attrs); + public Page join(Page page, String columnName, String joinName, String[] attrs); - public List join(List models, String joinOnField); + public List join(List models, String columnName); - public List join(List models, String joinOnField, String[] attrs); + public List join(List models, String columnName, String[] attrs); - public List join(List models, String joinOnField, String joinName); + public List join(List models, String columnName, String joinName); - public List join(List models, String joinOnField, String joinName, String[] attrs); + public List join(List models, String columnName, String joinName, String[] attrs); - public M join(M model, String joinOnField); + public M join(M model, String columnName); - public M join(M model, String joinOnField, String[] attrs); + public M join(M model, String columnName, String[] attrs); - public M join(M model, String joinOnField, String joinName); + public M join(M model, String columnName, String joinName); - public M join(M model, String joinOnField, String joinName, String[] attrs); + public M join(M model, String columnName, String joinName, String[] attrs); } diff --git a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java index c4ee9c7a..44327f39 100644 --- a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java +++ b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java @@ -28,37 +28,37 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { @Override - public Page join(Page page, String joinOnField) { - join(page.getList(), joinOnField); + public Page join(Page page, String columnName) { + join(page.getList(), columnName); return page; } @Override - public Page join(Page page, String joinOnField, String[] attrs) { - join(page.getList(), joinOnField, attrs); + public Page join(Page page, String columnName, String[] attrs) { + join(page.getList(), columnName, attrs); return page; } @Override - public Page join(Page page, String joinOnField, String joinName) { - join(page.getList(), joinOnField, joinName); + public Page join(Page page, String columnName, String joinName) { + join(page.getList(), columnName, joinName); return page; } @Override - public Page join(Page page, String joinOnField, String joinName, String[] attrs) { - join(page.getList(), joinOnField, joinName, attrs); + public Page join(Page page, String columnName, String joinName, String[] attrs) { + join(page.getList(), columnName, joinName, attrs); return page; } @Override - public List join(List models, String joinOnField) { + public List join(List models, String columnName) { if (ArrayUtil.isNotEmpty(models)) { for (Model m : models) { - join(m, joinOnField); + join(m, columnName); } } return models; @@ -66,10 +66,10 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { @Override - public List join(List models, String joinOnField, String[] attrs) { + public List join(List models, String columnName, String[] attrs) { if (ArrayUtil.isNotEmpty(models)) { for (Model m : models) { - join(m, joinOnField, attrs); + join(m, columnName, attrs); } } return models; @@ -77,10 +77,10 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { @Override - public List join(List models, String joinOnField, String joinName) { + public List join(List models, String columnName, String joinName) { if (ArrayUtil.isNotEmpty(models)) { for (Model m : models) { - join(m, joinOnField, joinName); + join(m, columnName, joinName); } } return models; @@ -88,10 +88,10 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { @Override - public List join(List models, String joinOnField, String joinName, String[] attrs) { + public List join(List models, String columnName, String joinName, String[] attrs) { if (ArrayUtil.isNotEmpty(models)) { for (Model m : models) { - join(m, joinOnField, joinName, attrs); + join(m, columnName, joinName, attrs); } } return models; @@ -101,18 +101,19 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { /** * 添加关联数据到某个model中去,避免关联查询,提高性能。 * - * @param model 要添加到的model - * @param joinOnField model对于的关联字段 + * @param model 要添加到的model + * @param columnName model对于的关联字段 */ @Override - public M join(M model, String joinOnField) { - if (model == null) - return model; - Object id = model.get(joinOnField); - if (id == null) { + public M join(M model, String columnName) { + if (model == null) { + return null; + } + Object columnValue = model.get(columnName); + if (columnValue == null) { return model; } - Model m = joinById(id); + Model m = joinByColumnValue(columnValue, model); if (m != null) { model.put(StrKit.firstCharToLowerCase(m.getClass().getSimpleName()), m); } @@ -123,18 +124,19 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { * 添加关联数据到某个model中去,避免关联查询,提高性能。 * * @param model - * @param joinOnField + * @param columnName * @param attrs */ @Override - public M join(M model, String joinOnField, String[] attrs) { - if (model == null) - return model; - Object id = model.get(joinOnField); - if (id == null) { + public M join(M model, String columnName, String[] attrs) { + if (model == null) { + return null; + } + Object columnValue = model.get(columnName); + if (columnValue == null) { return model; } - JbootModel m = joinById(id); + JbootModel m = joinByColumnValue(columnValue, model); if (m != null) { m = m.copy(); m.keep(attrs); @@ -148,18 +150,19 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { * 添加关联数据到某个model中去,避免关联查询,提高性能。 * * @param model - * @param joinOnField + * @param columnName * @param joinName */ @Override - public M join(M model, String joinOnField, String joinName) { - if (model == null) - return model; - Object id = model.get(joinOnField); - if (id == null) { + public M join(M model, String columnName, String joinName) { + if (model == null) { + return null; + } + Object columnValue = model.get(columnName); + if (columnValue == null) { return model; } - Model m = joinById(id); + Model m = joinByColumnValue(columnValue, model); if (m != null) { model.put(joinName, m); } @@ -171,19 +174,20 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { * 添加关联数据到某个model中去,避免关联查询,提高性能。 * * @param model - * @param joinOnField + * @param columnName * @param joinName * @param attrs */ @Override - public M join(M model, String joinOnField, String joinName, String[] attrs) { - if (model == null) - return model; - Object id = model.get(joinOnField); - if (id == null) { + public M join(M model, String columnName, String joinName, String[] attrs) { + if (model == null) { + return null; + } + Object columnValue = model.get(columnName); + if (columnValue == null) { return model; } - JbootModel m = joinById(id); + JbootModel m = joinByColumnValue(columnValue, model); if (m != null) { m = m.copy(); m.keep(attrs); @@ -196,10 +200,10 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { /** * 可以让子类去复写joinById ,比如默认只 join 部分字段等 * - * @param id + * @param columnValue * @return */ - protected abstract JbootModel joinById(Object id); + protected abstract JbootModel joinByColumnValue(Object columnValue, Model sourceModel); } -- Gitee From 3af0a7ade24607fc3700d500b4b76bd145f44072 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 8 Jul 2020 13:40:07 +0800 Subject: [PATCH 0371/1965] add Columns.toWherePartSql() methods --- src/main/java/io/jboot/db/model/Columns.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index d2994411..95117782 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -698,6 +698,18 @@ public class Columns implements Serializable { } + + /** + * 输出 where 后面的 sql 部分,风格是 mysql 的风格SQL + * @param withWhereKeyword 是否带上 where 关键字 + * @return + */ + public String toWherePartSql(boolean withWhereKeyword) { + return toWherePartSql('`', withWhereKeyword); + } + + + /** * 输出 where 部分的 sql * @param separator 字段分隔符 @@ -711,6 +723,7 @@ public class Columns implements Serializable { } + /** * 这个只是用于调试 * -- Gitee From 91f24bc65190e3d79f8e8c7a9f990e99a2bd583b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 8 Jul 2020 16:44:34 +0800 Subject: [PATCH 0372/1965] add JbootServiceJoiner.joinMany methods --- doc/docs/db.md | 116 +++++- doc/docs/imgs/db001.jpg | Bin 0 -> 43521 bytes .../io/jboot/service/JbootServiceBase.java | 8 +- .../io/jboot/service/JbootServiceJoiner.java | 115 +++++- .../jboot/service/JbootServiceJoinerImpl.java | 383 +++++++++++++++++- 5 files changed, 585 insertions(+), 37 deletions(-) create mode 100644 doc/docs/imgs/db001.jpg diff --git a/doc/docs/db.md b/doc/docs/db.md index 081df6b5..7780b04a 100644 --- a/doc/docs/db.md +++ b/doc/docs/db.md @@ -8,10 +8,10 @@ Jboot 数据库功能基于 JFinal 的 ActiveRecordPlugin 插件和 Apache shard - 基本增删改查 - Db + Record 模式 - Model 映射方式 - - Column 查询方式 + - Columns 查询方式 - 关联查询 - 分页查询 -- 批量插入 +- 一对一、一对多、多对一、多对对 - 事务操作 - 多数据源 - 读写分离 @@ -170,6 +170,118 @@ public List findListBy(int userAge,String articleTitle){ ``` + +## 一对一、一对多、多对一、多对对 + +在 Jboot 中,提供了 Join 系列方法,我们在 Service 层可以直接使用 Join 进行 一对一、一对多、多对一、多对对 的查询操作。 + + + +假设存在这样的关系:一篇文章只有一个作者,一个作者可以写多篇文章,一篇文章可以归属多个文章分类、一个文章分类有可以包含多篇文章。 + + + +那么,表结构设计如下: + + + +文章表 article : + +```sql +CREATE TABLE `article` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `title` varchar(128) DEFAULT NULL COMMENT '文章标题', + `author_id` int(11) unsigned NOT NULL COMMENT '文章作者ID', + `content` varchar(128) DEFAULT NULL COMMENT '文章内容', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章信息表'; +``` + + + +文章作者表 author : + +```sql +CREATE TABLE `author` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `username` varchar(128) DEFAULT NULL COMMENT '作者名称', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章作者表'; +``` + + + +文章分类表 category : + +```sql +CREATE TABLE `category` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `title` varchar(128) DEFAULT NULL COMMENT '文章分类名称', + `description` varchar(128) DEFAULT NULL COMMENT '文章分类描述', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章分类表'; +``` + + + +文章分类和分类的 多对对关系表: article_category_mapping : + +```sql +CREATE TABLE `article_category` ( + `article_id` int(11) unsigned NOT NULL COMMENT '主键ID', + `category_id` int(11) unsigned NOT NULL COMMENT '文章分类名称', +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章分类和分类的 多对对关系表'; +``` + + + +这样,在我们的代码里应该存在 3 个Service,分别是: + +- ArticleService 用于查询文章相关的服务 +- CategoryService 用于查询文章分类相关的服务 +- AuthorService 用于查询作者的服务 + + + +当在一个网页中显示文字列表,文章列表里,及包含了作者信息,也包含了多个分类,如下图所示: + +![](./imgs/db001.jpg) + + + +那么,查询代码如下(伪代码): + + + +```java +//分页查询所有的文章 +Page

articlePage = articleService.paginate(...); + +//查询文章的作者 +authorService.join(articlePage,"author_id"); + +//查询文章的分类 +categoryService.joinManyByTable(articlePage,"article_category","category_id","article_id") + +``` + + + +如果,在某些场景下,我们要查询作者,以及作者下的文章列表,代码如下: + +```java +//分页查询出作者信息 +Page authorPage = arthorService.paginate(...); + +articleService.joinMany(authorPage,"author_id"); +``` + + + + + + + ## 多数据源 默认单数据源的情况下,我们需要在 `jboot.properties` 添加如下配置: diff --git a/doc/docs/imgs/db001.jpg b/doc/docs/imgs/db001.jpg new file mode 100644 index 0000000000000000000000000000000000000000..53d6df98f98623af093f0801ed6601169f3942c9 GIT binary patch literal 43521 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 b1ir>(IE~}~6TkAi$VWL>|3CRr|DOII75U&0 literal 0 HcmV?d00001 diff --git a/src/main/java/io/jboot/service/JbootServiceBase.java b/src/main/java/io/jboot/service/JbootServiceBase.java index e16cedde..b38b568c 100644 --- a/src/main/java/io/jboot/service/JbootServiceBase.java +++ b/src/main/java/io/jboot/service/JbootServiceBase.java @@ -303,7 +303,7 @@ public class JbootServiceBase> * @return */ @Override - protected JbootModel joinByColumnValue(Object columnValue,Model sourceModel) { + protected JbootModel joinByColumnValue(Object columnValue,JbootModel sourceModel) { return findById(columnValue); } @@ -317,4 +317,10 @@ public class JbootServiceBase> */ public void shouldUpdateCache(int action, Model model, Object id) { } + + + @Override + protected List joinManyByColumnValue(String targetColumnName, Object columnValue, M model) { + return (List) findListByColumns(Columns.safeMode().eq(targetColumnName,columnValue)); + } } diff --git a/src/main/java/io/jboot/service/JbootServiceJoiner.java b/src/main/java/io/jboot/service/JbootServiceJoiner.java index 2961f43d..77c61dcc 100644 --- a/src/main/java/io/jboot/service/JbootServiceJoiner.java +++ b/src/main/java/io/jboot/service/JbootServiceJoiner.java @@ -15,8 +15,9 @@ */ package io.jboot.service; -import com.jfinal.plugin.activerecord.Model; import com.jfinal.plugin.activerecord.Page; +import io.jboot.db.model.JbootModel; +import io.jboot.utils.ObjectFunc; import java.util.List; @@ -26,31 +27,119 @@ import java.util.List; public interface JbootServiceJoiner { - public Page join(Page page, String columnName); + public Page join(Page page, String columnName); - public Page join(Page page, String columnName, String[] attrs); + public Page join(Page page, String columnName, String[] attrs); - public Page join(Page page, String columnName, String joinName); + public Page join(Page page, String columnName, String joinName); - public Page join(Page page, String columnName, String joinName, String[] attrs); + public Page join(Page page, String columnName, String joinName, String[] attrs); - public List join(List models, String columnName); + public List join(List models, String columnName); - public List join(List models, String columnName, String[] attrs); + public List join(List models, String columnName, String[] attrs); - public List join(List models, String columnName, String joinName); + public List join(List models, String columnName, String joinName); - public List join(List models, String columnName, String joinName, String[] attrs); + public List join(List models, String columnName, String joinName, String[] attrs); - public M join(M model, String columnName); + public M join(M model, String columnName); - public M join(M model, String columnName, String[] attrs); + public M join(M model, String columnName, String[] attrs); - public M join(M model, String columnName, String joinName); + public M join(M model, String columnName, String joinName); - public M join(M model, String columnName, String joinName, String[] attrs); + public M join(M model, String columnName, String joinName, String[] attrs); + + + public Page joinMany(Page page, String targetColumnName); + + public Page joinMany(Page page, String targetColumnName, String[] attrs); + + public Page joinMany(Page page, String targetColumnName, String joinName); + + public Page joinMany(Page page, String targetColumnName, String joinName, String[] attrs); + + + public List joinMany(List models, String targetColumnName); + + public List joinMany(List models, String targetColumnName, String[] attrs); + + public List joinMany(List models, String targetColumnName, String joinName); + + public List joinMany(List models, String targetColumnName, String joinName, String[] attrs); + + + public M joinMany(M model, String targetColumnName); + + public M joinMany(M model, String targetColumnName, String[] attrs); + + public M joinMany(M model, String targetColumnName, String joinName); + + public M joinMany(M model, String targetColumnName, String joinName, String[] attrs); + + + + public Page joinMany(Page page, ObjectFunc modelValueGetter, String targetColumnName); + + public Page joinMany(Page page, ObjectFunc modelValueGetter, String targetColumnName, String[] attrs); + + public Page joinMany(Page page, ObjectFunc modelValueGetter, String targetColumnName, String joinName); + + public Page joinMany(Page page, ObjectFunc modelValueGetter, String targetColumnName, String joinName, String[] attrs); + + + public List joinMany(List models, ObjectFunc modelValueGetter, String targetColumnName); + + public List joinMany(List models, ObjectFunc modelValueGetter, String targetColumnName, String[] attrs); + + public List joinMany(List models, ObjectFunc modelValueGetter, String targetColumnName, String joinName); + + public List joinMany(List models, ObjectFunc modelValueGetter, String targetColumnName, String joinName, String[] attrs); + + + public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName); + + public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName, String[] attrs); + + public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName, String joinName); + + public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName, String joinName, String[] attrs); + + +// +// +// +// +// +// +// public Page joinManyByTable(Page page, String tableName,); +// +// public Page joinManyByTable(Page page, String targetColumnName, String[] attrs); +// +// public Page joinManyByTable(Page page, String targetColumnName, String joinName); +// +// public Page joinManyByTable(Page page, String targetColumnName, String joinName, String[] attrs); +// +// +// public List joinManyByTable(List models, String targetColumnName); +// +// public List joinManyByTable(List models, String targetColumnName, String[] attrs); +// +// public List joinManyByTable(List models, String targetColumnName, String joinName); +// +// public List joinManyByTable(List models, String targetColumnName, String joinName, String[] attrs); +// +// +// public M joinManyByTable(M model, String targetColumnName); +// +// public M joinManyByTable(M model, String targetColumnName, String[] attrs); +// +// public M joinManyByTable(M model, String targetColumnName, String joinName); +// +// public M joinManyByTable(M model, String targetColumnName, String joinName, String[] attrs); } diff --git a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java index 44327f39..a098f2fc 100644 --- a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java +++ b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java @@ -16,11 +16,12 @@ package io.jboot.service; import com.jfinal.kit.StrKit; -import com.jfinal.plugin.activerecord.Model; import com.jfinal.plugin.activerecord.Page; import io.jboot.db.model.JbootModel; import io.jboot.utils.ArrayUtil; +import io.jboot.utils.ObjectFunc; +import java.util.ArrayList; import java.util.List; @@ -28,36 +29,36 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { @Override - public Page join(Page page, String columnName) { + public Page join(Page page, String columnName) { join(page.getList(), columnName); return page; } @Override - public Page join(Page page, String columnName, String[] attrs) { + public Page join(Page page, String columnName, String[] attrs) { join(page.getList(), columnName, attrs); return page; } @Override - public Page join(Page page, String columnName, String joinName) { + public Page join(Page page, String columnName, String joinName) { join(page.getList(), columnName, joinName); return page; } @Override - public Page join(Page page, String columnName, String joinName, String[] attrs) { + public Page join(Page page, String columnName, String joinName, String[] attrs) { join(page.getList(), columnName, joinName, attrs); return page; } @Override - public List join(List models, String columnName) { + public List join(List models, String columnName) { if (ArrayUtil.isNotEmpty(models)) { - for (Model m : models) { + for (JbootModel m : models) { join(m, columnName); } } @@ -66,9 +67,9 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { @Override - public List join(List models, String columnName, String[] attrs) { + public List join(List models, String columnName, String[] attrs) { if (ArrayUtil.isNotEmpty(models)) { - for (Model m : models) { + for (JbootModel m : models) { join(m, columnName, attrs); } } @@ -77,9 +78,9 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { @Override - public List join(List models, String columnName, String joinName) { + public List join(List models, String columnName, String joinName) { if (ArrayUtil.isNotEmpty(models)) { - for (Model m : models) { + for (JbootModel m : models) { join(m, columnName, joinName); } } @@ -88,9 +89,9 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { @Override - public List join(List models, String columnName, String joinName, String[] attrs) { + public List join(List models, String columnName, String joinName, String[] attrs) { if (ArrayUtil.isNotEmpty(models)) { - for (Model m : models) { + for (JbootModel m : models) { join(m, columnName, joinName, attrs); } } @@ -105,7 +106,7 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { * @param columnName model对于的关联字段 */ @Override - public M join(M model, String columnName) { + public M join(M model, String columnName) { if (model == null) { return null; } @@ -113,7 +114,7 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { if (columnValue == null) { return model; } - Model m = joinByColumnValue(columnValue, model); + JbootModel m = joinByColumnValue(columnValue, model); if (m != null) { model.put(StrKit.firstCharToLowerCase(m.getClass().getSimpleName()), m); } @@ -128,7 +129,7 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { * @param attrs */ @Override - public M join(M model, String columnName, String[] attrs) { + public M join(M model, String columnName, String[] attrs) { if (model == null) { return null; } @@ -154,7 +155,7 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { * @param joinName */ @Override - public M join(M model, String columnName, String joinName) { + public M join(M model, String columnName, String joinName) { if (model == null) { return null; } @@ -162,7 +163,7 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { if (columnValue == null) { return model; } - Model m = joinByColumnValue(columnValue, model); + JbootModel m = joinByColumnValue(columnValue, model); if (m != null) { model.put(joinName, m); } @@ -179,7 +180,7 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { * @param attrs */ @Override - public M join(M model, String columnName, String joinName, String[] attrs) { + public M join(M model, String columnName, String joinName, String[] attrs) { if (model == null) { return null; } @@ -198,12 +199,352 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { /** - * 可以让子类去复写joinById ,比如默认只 join 部分字段等 + * 可以让子类去复写 joinByColumnValue ,比如默认只 join 部分字段等,或者不是根据主键进行查询等 * * @param columnValue * @return */ - protected abstract JbootModel joinByColumnValue(Object columnValue, Model sourceModel); + protected abstract JbootModel joinByColumnValue(Object columnValue, JbootModel sourceModel); + + +/////////////////joinMany start///////////////////////////// + + + @Override + public Page joinMany(Page page, String targetColumnName) { + joinMany(page.getList(), targetColumnName); + return page; + } + + @Override + public Page joinMany(Page page, String targetColumnName, String[] attrs) { + joinMany(page.getList(), targetColumnName, attrs); + return page; + } + + + @Override + public Page joinMany(Page page, String targetColumnName, String joinName) { + joinMany(page.getList(), targetColumnName, joinName); + return page; + } + + + @Override + public Page joinMany(Page page, String targetColumnName, String joinName, String[] attrs) { + joinMany(page.getList(), targetColumnName, joinName, attrs); + return page; + } + + + @Override + public List joinMany(List models, String targetColumnName) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinMany(m, targetColumnName); + } + } + return models; + } + + + @Override + public List joinMany(List models, String targetColumnName, String[] attrs) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinMany(m, targetColumnName, attrs); + } + } + return models; + } + + + @Override + public List joinMany(List models, String targetColumnName, String joinName) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinMany(m, targetColumnName, joinName); + } + } + return models; + } + + + @Override + public List joinMany(List models, String targetColumnName, String joinName, String[] attrs) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinMany(m, targetColumnName, joinName, attrs); + } + } + return models; + } + + + /** + * 添加关联数据到某个model中去,避免关联查询,提高性能。 + * + * @param model 要添加到的model + * @param targetColumnName model对于的关联字段 + */ + @Override + public M joinMany(M model, String targetColumnName) { + if (model == null) { + return null; + } + Object columnValue = model._getIdValue(); + if (columnValue == null) { + return model; + } + List list = joinManyByColumnValue(targetColumnName, columnValue, model); + if (list != null && !list.isEmpty()) { + JbootModel joinModel = list.get(0); + model.put(StrKit.firstCharToLowerCase(joinModel.getClass().getSimpleName() + "List"), list); + } + return model; + } + + /** + * 添加关联数据到某个model中去,避免关联查询,提高性能。 + * + * @param model + * @param targetColumnName + * @param attrs + */ + @Override + public M joinMany(M model, String targetColumnName, String[] attrs) { + if (model == null) { + return null; + } + Object columnValue = model._getIdValue(); + if (columnValue == null) { + return model; + } + + List list = joinManyByColumnValue(targetColumnName, columnValue, model); + if (list != null && !list.isEmpty()) { + JbootModel joinModel = list.get(0); + model.put(StrKit.firstCharToLowerCase(joinModel.getClass().getSimpleName() + "List"), keepModelListAttrs(list, attrs)); + } + + return model; + } + + + /** + * 添加关联数据到某个model中去,避免关联查询,提高性能。 + * + * @param model + * @param targetColumnName + * @param joinName + */ + @Override + public M joinMany(M model, String targetColumnName, String joinName) { + if (model == null) { + return null; + } + Object columnValue = model._getIdValue(); + if (columnValue == null) { + return model; + } + + List list = joinManyByColumnValue(targetColumnName, columnValue, model); + if (list != null && !list.isEmpty()) { + model.put(joinName, list); + } + + return model; + } + + + /** + * 添加关联数据到某个model中去,避免关联查询,提高性能。 + * + * @param model + * @param targetColumnName + * @param joinName + * @param attrs + */ + @Override + public M joinMany(M model, String targetColumnName, String joinName, String[] attrs) { + if (model == null) { + return null; + } + Object columnValue = model._getIdValue(); + if (columnValue == null) { + return model; + } + + List list = joinManyByColumnValue(targetColumnName, columnValue, model); + if (list != null && !list.isEmpty()) { + model.put(joinName, keepModelListAttrs(list, attrs)); + } + + return model; + } + + + protected List keepModelListAttrs(List list, String[] attrs) { + if (list == null || list.isEmpty()) { + return null; + } + List retList = new ArrayList<>(list.size()); + for (M model : list) { + retList.add((M) model.copy().keep(attrs)); + } + return retList; + } + + + protected abstract List joinManyByColumnValue(String targetColumnName, Object columnValue, M model); + + + @Override + public Page joinMany(Page page, ObjectFunc modelValueGetter, String targetColumnName) { + joinMany(page.getList(), modelValueGetter, targetColumnName); + return page; + } + + @Override + public Page joinMany(Page page, ObjectFunc modelValueGetter, String targetColumnName, String[] attrs) { + joinMany(page.getList(), modelValueGetter, targetColumnName, attrs); + return page; + } + + + @Override + public Page joinMany(Page page, ObjectFunc modelValueGetter, String targetColumnName, String joinName) { + joinMany(page.getList(), modelValueGetter, targetColumnName, joinName); + return page; + } + + + @Override + public Page joinMany(Page page, ObjectFunc modelValueGetter, String targetColumnName, String joinName, String[] attrs) { + joinMany(page.getList(), modelValueGetter, targetColumnName, joinName, attrs); + return page; + } + + + @Override + public List joinMany(List models, ObjectFunc modelValueGetter, String targetColumnName) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinMany(m, modelValueGetter, targetColumnName); + } + } + return models; + } + + + @Override + public List joinMany(List models, ObjectFunc modelValueGetter, String targetColumnName, String[] attrs) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinMany(m, modelValueGetter, targetColumnName, attrs); + } + } + return models; + } + + + @Override + public List joinMany(List models, ObjectFunc modelValueGetter, String targetColumnName, String joinName) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinMany(m, modelValueGetter, targetColumnName, joinName); + } + } + return models; + } + + + @Override + public List joinMany(List models, ObjectFunc modelValueGetter, String targetColumnName, String joinName, String[] attrs) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinMany(m, modelValueGetter, targetColumnName, joinName, attrs); + } + } + return models; + } + + + @Override + public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName) { + if (model == null) { + return null; + } + Object columnValue = modelValueGetter.get(model); + if (columnValue == null) { + return model; + } + List list = joinManyByColumnValue(targetColumnName, columnValue, model); + if (list != null && !list.isEmpty()) { + JbootModel joinModel = list.get(0); + model.put(StrKit.firstCharToLowerCase(joinModel.getClass().getSimpleName() + "List"), list); + } + return model; + } + + @Override + public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName, String[] attrs) { + if (model == null) { + return null; + } + Object columnValue = modelValueGetter.get(model); + if (columnValue == null) { + return model; + } + + List list = joinManyByColumnValue(targetColumnName, columnValue, model); + if (list != null && !list.isEmpty()) { + JbootModel joinModel = list.get(0); + model.put(StrKit.firstCharToLowerCase(joinModel.getClass().getSimpleName() + "List"), keepModelListAttrs(list, attrs)); + } + + return model; + } + + + @Override + public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName, String joinName) { + if (model == null) { + return null; + } + Object columnValue = modelValueGetter.get(model); + if (columnValue == null) { + return model; + } + + List list = joinManyByColumnValue(targetColumnName, columnValue, model); + if (list != null && !list.isEmpty()) { + model.put(joinName, list); + } + + return model; + } + + + @Override + public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName, String joinName, String[] attrs) { + if (model == null) { + return null; + } + Object columnValue = modelValueGetter.get(model); + if (columnValue == null) { + return model; + } + + List list = joinManyByColumnValue(targetColumnName, columnValue, model); + if (list != null && !list.isEmpty()) { + model.put(joinName, keepModelListAttrs(list, attrs)); + } + + return model; + } + +/////////////////joinMany end///////////////////////////// } -- Gitee From 6ce1b09839e28111664aad07b2fad5a45bb579c9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 8 Jul 2020 16:48:37 +0800 Subject: [PATCH 0373/1965] add JbootServiceJoiner.joinMany methods --- src/main/java/io/jboot/service/JbootServiceBase.java | 2 +- src/main/java/io/jboot/service/JbootServiceJoinerImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/service/JbootServiceBase.java b/src/main/java/io/jboot/service/JbootServiceBase.java index b38b568c..76de8aaf 100644 --- a/src/main/java/io/jboot/service/JbootServiceBase.java +++ b/src/main/java/io/jboot/service/JbootServiceBase.java @@ -320,7 +320,7 @@ public class JbootServiceBase> @Override - protected List joinManyByColumnValue(String targetColumnName, Object columnValue, M model) { + protected List joinManyByColumnValue(String targetColumnName, Object columnValue, M sourceModel) { return (List) findListByColumns(Columns.safeMode().eq(targetColumnName,columnValue)); } } diff --git a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java index a098f2fc..981da7a9 100644 --- a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java +++ b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java @@ -396,7 +396,7 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { } - protected abstract List joinManyByColumnValue(String targetColumnName, Object columnValue, M model); + protected abstract List joinManyByColumnValue(String targetColumnName, Object columnValue, M sourceModel); @Override -- Gitee From 4a57251a592fb642552f0721b400646aa57c8e70 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 8 Jul 2020 17:25:39 +0800 Subject: [PATCH 0374/1965] add JbootServiceJoiner.joinManyByTable methods --- .../io/jboot/service/JbootServiceJoiner.java | 84 ++-- .../jboot/service/JbootServiceJoinerImpl.java | 402 ++++++++++-------- 2 files changed, 284 insertions(+), 202 deletions(-) diff --git a/src/main/java/io/jboot/service/JbootServiceJoiner.java b/src/main/java/io/jboot/service/JbootServiceJoiner.java index 77c61dcc..8302c5aa 100644 --- a/src/main/java/io/jboot/service/JbootServiceJoiner.java +++ b/src/main/java/io/jboot/service/JbootServiceJoiner.java @@ -81,7 +81,6 @@ public interface JbootServiceJoiner { public M joinMany(M model, String targetColumnName, String joinName, String[] attrs); - public Page joinMany(Page page, ObjectFunc modelValueGetter, String targetColumnName); public Page joinMany(Page page, ObjectFunc modelValueGetter, String targetColumnName, String[] attrs); @@ -109,37 +108,58 @@ public interface JbootServiceJoiner { public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName, String joinName, String[] attrs); -// -// -// -// -// -// -// public Page joinManyByTable(Page page, String tableName,); -// -// public Page joinManyByTable(Page page, String targetColumnName, String[] attrs); -// -// public Page joinManyByTable(Page page, String targetColumnName, String joinName); -// -// public Page joinManyByTable(Page page, String targetColumnName, String joinName, String[] attrs); -// -// -// public List joinManyByTable(List models, String targetColumnName); -// -// public List joinManyByTable(List models, String targetColumnName, String[] attrs); -// -// public List joinManyByTable(List models, String targetColumnName, String joinName); -// -// public List joinManyByTable(List models, String targetColumnName, String joinName, String[] attrs); -// -// -// public M joinManyByTable(M model, String targetColumnName); -// -// public M joinManyByTable(M model, String targetColumnName, String[] attrs); -// -// public M joinManyByTable(M model, String targetColumnName, String joinName); -// -// public M joinManyByTable(M model, String targetColumnName, String joinName, String[] attrs); + public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName); + + public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName, String[] attrs); + + public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName, String joinName); + + public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs); + + + public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName); + + public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName, String[] attrs); + + public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName, String joinName); + + public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs); + + + public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName); + + public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName, String[] attrs); + + public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName, String joinName); + + public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs); + + + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName); + + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String[] attrs); + + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName); + + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs); + + + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName); + + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String[] attrs); + + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName); + + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs); + + + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName); + + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String[] attrs); + + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName); + + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs); } diff --git a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java index 981da7a9..0bc3f881 100644 --- a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java +++ b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java @@ -17,9 +17,13 @@ package io.jboot.service; import com.jfinal.kit.StrKit; import com.jfinal.plugin.activerecord.Page; +import com.jfinal.plugin.activerecord.Record; +import io.jboot.db.JbootDb; +import io.jboot.db.model.Columns; import io.jboot.db.model.JbootModel; import io.jboot.utils.ArrayUtil; import io.jboot.utils.ObjectFunc; +import io.jboot.utils.StrUtil; import java.util.ArrayList; import java.util.List; @@ -107,18 +111,7 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { */ @Override public M join(M model, String columnName) { - if (model == null) { - return null; - } - Object columnValue = model.get(columnName); - if (columnValue == null) { - return model; - } - JbootModel m = joinByColumnValue(columnValue, model); - if (m != null) { - model.put(StrKit.firstCharToLowerCase(m.getClass().getSimpleName()), m); - } - return model; + return join(model, columnName, null, null); } /** @@ -130,20 +123,7 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { */ @Override public M join(M model, String columnName, String[] attrs) { - if (model == null) { - return null; - } - Object columnValue = model.get(columnName); - if (columnValue == null) { - return model; - } - JbootModel m = joinByColumnValue(columnValue, model); - if (m != null) { - m = m.copy(); - m.keep(attrs); - model.put(StrKit.firstCharToLowerCase(m.getClass().getSimpleName()), m); - } - return model; + return join(model, columnName, null, attrs); } @@ -156,18 +136,7 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { */ @Override public M join(M model, String columnName, String joinName) { - if (model == null) { - return null; - } - Object columnValue = model.get(columnName); - if (columnValue == null) { - return model; - } - JbootModel m = joinByColumnValue(columnValue, model); - if (m != null) { - model.put(joinName, m); - } - return model; + return join(model, columnName, joinName, null); } @@ -190,9 +159,8 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { } JbootModel m = joinByColumnValue(columnValue, model); if (m != null) { - m = m.copy(); - m.keep(attrs); - model.put(joinName, m); + joinName = StrUtil.isNotBlank(joinName) ? joinName : StrKit.firstCharToLowerCase(m.getClass().getSimpleName()); + model.put(joinName, ArrayUtil.isNotEmpty(attrs) ? m.copy().keep(attrs) : m); } return model; } @@ -281,124 +249,28 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { } - /** - * 添加关联数据到某个model中去,避免关联查询,提高性能。 - * - * @param model 要添加到的model - * @param targetColumnName model对于的关联字段 - */ @Override public M joinMany(M model, String targetColumnName) { - if (model == null) { - return null; - } - Object columnValue = model._getIdValue(); - if (columnValue == null) { - return model; - } - List list = joinManyByColumnValue(targetColumnName, columnValue, model); - if (list != null && !list.isEmpty()) { - JbootModel joinModel = list.get(0); - model.put(StrKit.firstCharToLowerCase(joinModel.getClass().getSimpleName() + "List"), list); - } - return model; + return joinMany(model, null, targetColumnName, null, null); } - /** - * 添加关联数据到某个model中去,避免关联查询,提高性能。 - * - * @param model - * @param targetColumnName - * @param attrs - */ @Override public M joinMany(M model, String targetColumnName, String[] attrs) { - if (model == null) { - return null; - } - Object columnValue = model._getIdValue(); - if (columnValue == null) { - return model; - } - - List list = joinManyByColumnValue(targetColumnName, columnValue, model); - if (list != null && !list.isEmpty()) { - JbootModel joinModel = list.get(0); - model.put(StrKit.firstCharToLowerCase(joinModel.getClass().getSimpleName() + "List"), keepModelListAttrs(list, attrs)); - } - - return model; + return joinMany(model, null, targetColumnName, null, attrs); } - - /** - * 添加关联数据到某个model中去,避免关联查询,提高性能。 - * - * @param model - * @param targetColumnName - * @param joinName - */ @Override public M joinMany(M model, String targetColumnName, String joinName) { - if (model == null) { - return null; - } - Object columnValue = model._getIdValue(); - if (columnValue == null) { - return model; - } - - List list = joinManyByColumnValue(targetColumnName, columnValue, model); - if (list != null && !list.isEmpty()) { - model.put(joinName, list); - } - - return model; + return joinMany(model, null, targetColumnName, joinName, null); } - /** - * 添加关联数据到某个model中去,避免关联查询,提高性能。 - * - * @param model - * @param targetColumnName - * @param joinName - * @param attrs - */ @Override public M joinMany(M model, String targetColumnName, String joinName, String[] attrs) { - if (model == null) { - return null; - } - Object columnValue = model._getIdValue(); - if (columnValue == null) { - return model; - } - - List list = joinManyByColumnValue(targetColumnName, columnValue, model); - if (list != null && !list.isEmpty()) { - model.put(joinName, keepModelListAttrs(list, attrs)); - } - - return model; - } - - - protected List keepModelListAttrs(List list, String[] attrs) { - if (list == null || list.isEmpty()) { - return null; - } - List retList = new ArrayList<>(list.size()); - for (M model : list) { - retList.add((M) model.copy().keep(attrs)); - } - return retList; + return joinMany(model, null, targetColumnName, joinName, attrs); } - protected abstract List joinManyByColumnValue(String targetColumnName, Object columnValue, M sourceModel); - - @Override public Page joinMany(Page page, ObjectFunc modelValueGetter, String targetColumnName) { joinMany(page.getList(), modelValueGetter, targetColumnName); @@ -472,79 +344,269 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { @Override public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName) { + return joinMany(model, modelValueGetter, targetColumnName, null, null); + } + + @Override + public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName, String[] attrs) { + return joinMany(model, modelValueGetter, targetColumnName, null, attrs); + } + + + @Override + public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName, String joinName) { + return joinMany(model, modelValueGetter, targetColumnName, joinName, null); + } + + + @Override + public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName, String joinName, String[] attrs) { if (model == null) { return null; } - Object columnValue = modelValueGetter.get(model); + Object columnValue = modelValueGetter != null ? modelValueGetter.get(model) : model._getIdValue(); if (columnValue == null) { return model; } + List list = joinManyByColumnValue(targetColumnName, columnValue, model); if (list != null && !list.isEmpty()) { - JbootModel joinModel = list.get(0); - model.put(StrKit.firstCharToLowerCase(joinModel.getClass().getSimpleName() + "List"), list); + joinName = StrUtil.isNotBlank(joinName) ? joinName : list.get(0).getClass().getSimpleName() + "List"; + model.put(joinName, ArrayUtil.isNotEmpty(attrs) ? keepModelListAttrs(list, attrs) : list); } + return model; } - @Override - public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName, String[] attrs) { - if (model == null) { + + protected List keepModelListAttrs(List list, String[] attrs) { + if (list == null || list.isEmpty()) { return null; } - Object columnValue = modelValueGetter.get(model); - if (columnValue == null) { - return model; + List retList = new ArrayList<>(list.size()); + for (M model : list) { + retList.add((M) model.copy().keep(attrs)); } + return retList; + } - List list = joinManyByColumnValue(targetColumnName, columnValue, model); - if (list != null && !list.isEmpty()) { - JbootModel joinModel = list.get(0); - model.put(StrKit.firstCharToLowerCase(joinModel.getClass().getSimpleName() + "List"), keepModelListAttrs(list, attrs)); + + protected abstract List joinManyByColumnValue(String targetColumnName, Object columnValue, M sourceModel); + + +/////////////////joinMany end///////////////////////////// + + +/////////////////joinManyByTable start///////////////////////////// + + @Override + public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName) { + joinManyByTable(page.getList(), tableName, targetColumnName, columnName); + return page; + } + + @Override + public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName, String[] attrs) { + joinManyByTable(page.getList(), tableName, targetColumnName, columnName, attrs); + return page; + } + + @Override + public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName, String joinName) { + joinManyByTable(page.getList(), tableName, targetColumnName, columnName, joinName); + return page; + } + + + @Override + public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs) { + joinManyByTable(page.getList(), tableName, targetColumnName, columnName, joinName, attrs); + return page; + } + + + @Override + public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinManyByTable(m, tableName, targetColumnName, columnName); + } + } + return models; + } + + @Override + public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName, String[] attrs) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinManyByTable(m, tableName, targetColumnName, columnName, attrs); + } } + return models; + } - return model; + @Override + public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName, String joinName) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinManyByTable(m, tableName, targetColumnName, columnName, joinName); + } + } + return models; } @Override - public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName, String joinName) { - if (model == null) { - return null; + public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinManyByTable(m, tableName, targetColumnName, columnName, joinName, attrs); + } } - Object columnValue = modelValueGetter.get(model); - if (columnValue == null) { - return model; + return models; + } + + + @Override + public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName) { + return joinManyByTable(model, tableName, targetColumnName, columnName, null, null); + } + + @Override + public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName, String[] attrs) { + return joinManyByTable(model, tableName, targetColumnName, columnName, null, attrs); + } + + @Override + public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName, String joinName) { + return joinManyByTable(model, tableName, targetColumnName, columnName, joinName, null); + } + + @Override + public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs) { + return joinManyByTable(model, null, tableName, targetColumnName, columnName, joinName, attrs); + } + + + @Override + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName) { + joinManyByTable(page.getList(), modelValueGetter, tableName, targetColumnName, columnName); + return page; + } + + @Override + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String[] attrs) { + joinManyByTable(page.getList(), modelValueGetter, tableName, targetColumnName, columnName, attrs); + return page; + } + + @Override + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName) { + joinManyByTable(page.getList(), modelValueGetter, tableName, targetColumnName, columnName, joinName); + return page; + } + + + @Override + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs) { + joinManyByTable(page.getList(), modelValueGetter, tableName, targetColumnName, columnName, joinName, attrs); + return page; + } + + + @Override + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinManyByTable(m, modelValueGetter, tableName, targetColumnName, columnName); + } } + return models; + } - List list = joinManyByColumnValue(targetColumnName, columnValue, model); - if (list != null && !list.isEmpty()) { - model.put(joinName, list); + @Override + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String[] attrs) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinManyByTable(m, modelValueGetter, tableName, targetColumnName, columnName, attrs); + } + } + return models; + } + + @Override + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinManyByTable(m, modelValueGetter, tableName, targetColumnName, columnName, joinName); + } } + return models; + } - return model; + + @Override + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs) { + if (ArrayUtil.isNotEmpty(models)) { + for (M m : models) { + joinManyByTable(m, modelValueGetter, tableName, targetColumnName, columnName, joinName, attrs); + } + } + return models; } @Override - public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName, String joinName, String[] attrs) { + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName) { + return joinManyByTable(model, modelValueGetter, tableName, targetColumnName, columnName, null, null); + } + + @Override + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String[] attrs) { + return joinManyByTable(model, modelValueGetter, tableName, targetColumnName, columnName, null, attrs); + } + + @Override + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName) { + return joinManyByTable(model, modelValueGetter, tableName, targetColumnName, columnName, joinName, null); + } + + @Override + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs) { if (model == null) { return null; } - Object columnValue = modelValueGetter.get(model); + + Object columnValue = modelValueGetter != null ? modelValueGetter.get(model) : model._getIdValue(); if (columnValue == null) { return model; } - List list = joinManyByColumnValue(targetColumnName, columnValue, model); + List middleTableRecords = JbootDb.find(tableName, Columns.create(columnName, columnValue)); + if (middleTableRecords == null || middleTableRecords.isEmpty()) { + return model; + } + + List list = new ArrayList(); + for (Record record : middleTableRecords) { + Object targetTableIdValue = record.get(targetColumnName); + if (targetTableIdValue != null) { + JbootModel data = joinByColumnValue(targetTableIdValue, model); + if (data != null) { + list.add(data); + } + } + } + if (list != null && !list.isEmpty()) { - model.put(joinName, keepModelListAttrs(list, attrs)); + joinName = StrUtil.isNotBlank(joinName) ? joinName : list.get(0).getClass().getSimpleName() + "List"; + model.put(joinName, ArrayUtil.isNotEmpty(attrs) ? keepModelListAttrs(list, attrs) : list); } return model; } -/////////////////joinMany end///////////////////////////// +/////////////////joinManyByTable end///////////////////////////// } -- Gitee From b15fe769e888fec3888c86479e20871f748f5bb1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 8 Jul 2020 17:34:05 +0800 Subject: [PATCH 0375/1965] add JbootServiceJoiner.joinManyByTable methods --- .../io/jboot/service/JbootServiceBase.java | 6 ++--- .../jboot/service/JbootServiceJoinerImpl.java | 25 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/jboot/service/JbootServiceBase.java b/src/main/java/io/jboot/service/JbootServiceBase.java index 76de8aaf..b9ea1980 100644 --- a/src/main/java/io/jboot/service/JbootServiceBase.java +++ b/src/main/java/io/jboot/service/JbootServiceBase.java @@ -303,7 +303,7 @@ public class JbootServiceBase> * @return */ @Override - protected JbootModel joinByColumnValue(Object columnValue,JbootModel sourceModel) { + protected JbootModel joinByValue(Object columnValue, JbootModel sourceModel) { return findById(columnValue); } @@ -320,7 +320,7 @@ public class JbootServiceBase> @Override - protected List joinManyByColumnValue(String targetColumnName, Object columnValue, M sourceModel) { - return (List) findListByColumns(Columns.safeMode().eq(targetColumnName,columnValue)); + protected List joinManyByValue(String columnName, Object value, M sourceModel){ + return (List) findListByColumns(Columns.create(columnName,value)); } } diff --git a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java index 0bc3f881..8778dd72 100644 --- a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java +++ b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java @@ -153,11 +153,11 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { if (model == null) { return null; } - Object columnValue = model.get(columnName); - if (columnValue == null) { + Object value = model.get(columnName); + if (value == null) { return model; } - JbootModel m = joinByColumnValue(columnValue, model); + JbootModel m = joinByValue(value, model); if (m != null) { joinName = StrUtil.isNotBlank(joinName) ? joinName : StrKit.firstCharToLowerCase(m.getClass().getSimpleName()); model.put(joinName, ArrayUtil.isNotEmpty(attrs) ? m.copy().keep(attrs) : m); @@ -168,11 +168,12 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { /** * 可以让子类去复写 joinByColumnValue ,比如默认只 join 部分字段等,或者不是根据主键进行查询等 + * 一般情况下,传入的 columnValue 是主键的值,但是也有可能不是,要看场景,如果不是的情况下可以通过 sourceModel 来进行判断 * * @param columnValue * @return */ - protected abstract JbootModel joinByColumnValue(Object columnValue, JbootModel sourceModel); + protected abstract JbootModel joinByValue(Object columnValue, JbootModel sourceModel); /////////////////joinMany start///////////////////////////// @@ -364,12 +365,12 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { if (model == null) { return null; } - Object columnValue = modelValueGetter != null ? modelValueGetter.get(model) : model._getIdValue(); - if (columnValue == null) { + Object value = modelValueGetter != null ? modelValueGetter.get(model) : model._getIdValue(); + if (value == null) { return model; } - List list = joinManyByColumnValue(targetColumnName, columnValue, model); + List list = joinManyByValue(targetColumnName, value, model); if (list != null && !list.isEmpty()) { joinName = StrUtil.isNotBlank(joinName) ? joinName : list.get(0).getClass().getSimpleName() + "List"; model.put(joinName, ArrayUtil.isNotEmpty(attrs) ? keepModelListAttrs(list, attrs) : list); @@ -391,7 +392,7 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { } - protected abstract List joinManyByColumnValue(String targetColumnName, Object columnValue, M sourceModel); + protected abstract List joinManyByValue(String columnName, Object value, M sourceModel); /////////////////joinMany end///////////////////////////// @@ -589,16 +590,16 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { List list = new ArrayList(); for (Record record : middleTableRecords) { - Object targetTableIdValue = record.get(targetColumnName); - if (targetTableIdValue != null) { - JbootModel data = joinByColumnValue(targetTableIdValue, model); + Object targetTableValue = record.get(targetColumnName); + if (targetTableValue != null) { + JbootModel data = joinByValue(targetTableValue, model); if (data != null) { list.add(data); } } } - if (list != null && !list.isEmpty()) { + if (!list.isEmpty()) { joinName = StrUtil.isNotBlank(joinName) ? joinName : list.get(0).getClass().getSimpleName() + "List"; model.put(joinName, ArrayUtil.isNotEmpty(attrs) ? keepModelListAttrs(list, attrs) : list); } -- Gitee From 132d19ea0d07b401078f9df444345c5491ea943b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 8 Jul 2020 18:47:18 +0800 Subject: [PATCH 0376/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96JbootServiceBase.ja?= =?UTF-8?q?va=20=E6=97=A0=E6=B3=95=E5=88=9D=E5=A7=8B=E5=8C=96=20DAO=20?= =?UTF-8?q?=E6=97=B6=E7=9A=84=E9=94=99=E8=AF=AF=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/service/JbootServiceBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/service/JbootServiceBase.java b/src/main/java/io/jboot/service/JbootServiceBase.java index b9ea1980..991aa870 100644 --- a/src/main/java/io/jboot/service/JbootServiceBase.java +++ b/src/main/java/io/jboot/service/JbootServiceBase.java @@ -51,7 +51,7 @@ public class JbootServiceBase> protected M initDao() { Class modelClass = ClassUtil.getGenericClass(getClass()); if (modelClass == null) { - throw new JbootException("can not get model class name in JbootServiceBase"); + throw new JbootException("can not get model class name in " + ClassUtil.getUsefulClass(getClass())); } //默认不通过AOP构建DAO,提升性能,若特殊需要重写initDao()方法即可 -- Gitee From e2b790d3f77fa6dd9c0927a3fac3ed00878e0258 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 8 Jul 2020 18:47:48 +0800 Subject: [PATCH 0377/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=20joinMany=20?= =?UTF-8?q?=E7=9A=84=E5=B1=9E=E6=80=A7=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/service/JbootServiceJoinerImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java index 8778dd72..261a3cf3 100644 --- a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java +++ b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java @@ -372,7 +372,7 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { List list = joinManyByValue(targetColumnName, value, model); if (list != null && !list.isEmpty()) { - joinName = StrUtil.isNotBlank(joinName) ? joinName : list.get(0).getClass().getSimpleName() + "List"; + joinName = StrUtil.isNotBlank(joinName) ? joinName : StrKit.firstCharToLowerCase(list.get(0).getClass().getSimpleName()) + "List"; model.put(joinName, ArrayUtil.isNotEmpty(attrs) ? keepModelListAttrs(list, attrs) : list); } @@ -600,7 +600,7 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { } if (!list.isEmpty()) { - joinName = StrUtil.isNotBlank(joinName) ? joinName : list.get(0).getClass().getSimpleName() + "List"; + joinName = StrUtil.isNotBlank(joinName) ? joinName : StrKit.firstCharToLowerCase(list.get(0).getClass().getSimpleName()) + "List"; model.put(joinName, ArrayUtil.isNotEmpty(attrs) ? keepModelListAttrs(list, attrs) : list); } -- Gitee From 7a06af27d477b98a7546de115077dc8d1dd352e9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 8 Jul 2020 18:48:06 +0800 Subject: [PATCH 0378/1965] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20join=20=E7=9A=84?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/test/join/JoinController.java | 38 ++++++++++++++ .../io/jboot/test/join/model/Article.java | 50 +++++++++++++++++++ .../java/io/jboot/test/join/model/Author.java | 43 ++++++++++++++++ .../io/jboot/test/join/model/Category.java | 43 ++++++++++++++++ src/test/java/io/jboot/test/join/readme.txt | 0 .../test/join/service/ArticleService.java | 27 ++++++++++ .../test/join/service/AuthorService.java | 10 ++++ .../test/join/service/CategoryService.java | 10 ++++ 8 files changed, 221 insertions(+) create mode 100644 src/test/java/io/jboot/test/join/JoinController.java create mode 100644 src/test/java/io/jboot/test/join/model/Article.java create mode 100644 src/test/java/io/jboot/test/join/model/Author.java create mode 100644 src/test/java/io/jboot/test/join/model/Category.java create mode 100644 src/test/java/io/jboot/test/join/readme.txt create mode 100644 src/test/java/io/jboot/test/join/service/ArticleService.java create mode 100644 src/test/java/io/jboot/test/join/service/AuthorService.java create mode 100644 src/test/java/io/jboot/test/join/service/CategoryService.java diff --git a/src/test/java/io/jboot/test/join/JoinController.java b/src/test/java/io/jboot/test/join/JoinController.java new file mode 100644 index 00000000..3c86e389 --- /dev/null +++ b/src/test/java/io/jboot/test/join/JoinController.java @@ -0,0 +1,38 @@ +package io.jboot.test.join; + +import com.jfinal.aop.Aop; +import io.jboot.app.JbootApplication; +import io.jboot.test.join.model.Article; +import io.jboot.test.join.service.ArticleService; +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; + +import java.util.List; + +@RequestMapping("/join") +public class JoinController extends JbootController { + + + public static void main(String[] args) { + + //设置 数据源 的相关信息 + JbootApplication.setBootArg("jboot.datasource.type", "mysql"); + JbootApplication.setBootArg("jboot.datasource.url", "jdbc:mysql://127.0.0.1:3306/jboot_join_demo"); + JbootApplication.setBootArg("jboot.datasource.user", "root"); + JbootApplication.setBootArg("jboot.datasource.password", "123456"); + JbootApplication.setBootArg("jboot.model.unscanPackage", "*"); + JbootApplication.setBootArg("jboot.model.scanPackage", "io.jboot.test.join.model"); + JbootApplication.setBootArg("undertow.devMode", "false"); + + //启动应用程序 + JbootApplication.run(args); + + } + + + public void findListWithAuthorAndCategorys() { + List
articles = Aop.get(ArticleService.class).findListWithAuthorAndCategorys(); + renderJson(articles); + } + +} diff --git a/src/test/java/io/jboot/test/join/model/Article.java b/src/test/java/io/jboot/test/join/model/Article.java new file mode 100644 index 00000000..f13ddd93 --- /dev/null +++ b/src/test/java/io/jboot/test/join/model/Article.java @@ -0,0 +1,50 @@ +package io.jboot.test.join.model; + +import io.jboot.db.annotation.Table; +import io.jboot.db.model.JbootModel; + +@Table(tableName = "article",primaryKey = "id") +public class Article extends JbootModel
{ + + /** + * `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + * `author_id` int(11) unsigned DEFAULT NULL, + * `title` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + * `content` text COLLATE utf8mb4_unicode_ci, + */ + + public Long getId(){ + return getLong("id"); + } + + public void setId(Long id){ + set("id",id); + } + + public Long getAuthorId(){ + return getLong("author_id"); + } + + public void setAuthorId(Long id){ + set("author_id",id); + } + + + public String getTitle(){ + return getStr("title"); + } + + public void setTitle(String title){ + set("title",title); + } + + public String getContent(){ + return getStr("content"); + } + + public void setContent(String content){ + set("content",content); + } + + +} diff --git a/src/test/java/io/jboot/test/join/model/Author.java b/src/test/java/io/jboot/test/join/model/Author.java new file mode 100644 index 00000000..f384f17f --- /dev/null +++ b/src/test/java/io/jboot/test/join/model/Author.java @@ -0,0 +1,43 @@ +package io.jboot.test.join.model; + +import io.jboot.db.annotation.Table; +import io.jboot.db.model.JbootModel; + +@Table(tableName = "author",primaryKey = "id") +public class Author extends JbootModel { + + + /** + * `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + * `nickname` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + * `email` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + */ + + + + public Long getId(){ + return getLong("id"); + } + + public void setId(Long id){ + set("id",id); + } + + + public String getNickname(){ + return getStr("nickname"); + } + + public void setNickname(String nickname){ + set("nickname",nickname); + } + + public String getEmail(){ + return getStr("email"); + } + + public void setEmail(String email){ + set("email",email); + } + +} diff --git a/src/test/java/io/jboot/test/join/model/Category.java b/src/test/java/io/jboot/test/join/model/Category.java new file mode 100644 index 00000000..564d10d0 --- /dev/null +++ b/src/test/java/io/jboot/test/join/model/Category.java @@ -0,0 +1,43 @@ +package io.jboot.test.join.model; + +import io.jboot.db.annotation.Table; +import io.jboot.db.model.JbootModel; + +@Table(tableName = "category",primaryKey = "id") +public class Category extends JbootModel { + + + /** + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `title` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8mb4_unicode_ci, + */ + + + + public Long getId(){ + return getLong("id"); + } + + public void setId(Long id){ + set("id",id); + } + + + public String getTitle(){ + return getStr("title"); + } + + public void setTitle(String title){ + set("title",title); + } + + public String getDescription(){ + return getStr("description"); + } + + public void setDescription(String description){ + set("description",description); + } + +} diff --git a/src/test/java/io/jboot/test/join/readme.txt b/src/test/java/io/jboot/test/join/readme.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/test/java/io/jboot/test/join/service/ArticleService.java b/src/test/java/io/jboot/test/join/service/ArticleService.java new file mode 100644 index 00000000..b5213bcd --- /dev/null +++ b/src/test/java/io/jboot/test/join/service/ArticleService.java @@ -0,0 +1,27 @@ +package io.jboot.test.join.service; + +import com.jfinal.aop.Inject; +import io.jboot.service.JbootServiceBase; +import io.jboot.test.join.model.Article; + +import java.util.List; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public class ArticleService extends JbootServiceBase
{ + + @Inject + private AuthorService authorService; + + @Inject + private CategoryService categoryService; + + + public List
findListWithAuthorAndCategorys(){ + List
articles = DAO.findAll(); + authorService.join(articles,"author_id"); + categoryService.joinManyByTable(articles, "article_category","category_id","article_id"); + return articles; + } +} diff --git a/src/test/java/io/jboot/test/join/service/AuthorService.java b/src/test/java/io/jboot/test/join/service/AuthorService.java new file mode 100644 index 00000000..8d6a5f88 --- /dev/null +++ b/src/test/java/io/jboot/test/join/service/AuthorService.java @@ -0,0 +1,10 @@ +package io.jboot.test.join.service; + +import io.jboot.service.JbootServiceBase; +import io.jboot.test.join.model.Author; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public class AuthorService extends JbootServiceBase { +} diff --git a/src/test/java/io/jboot/test/join/service/CategoryService.java b/src/test/java/io/jboot/test/join/service/CategoryService.java new file mode 100644 index 00000000..6995b55d --- /dev/null +++ b/src/test/java/io/jboot/test/join/service/CategoryService.java @@ -0,0 +1,10 @@ +package io.jboot.test.join.service; + +import io.jboot.service.JbootServiceBase; +import io.jboot.test.join.model.Category; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public class CategoryService extends JbootServiceBase { +} -- Gitee From 373e6705f53c94ebf154e21c812804844df0a888 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 8 Jul 2020 18:50:58 +0800 Subject: [PATCH 0379/1965] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20join=20=E7=9A=84?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/test/join/JoinController.java | 8 ++++++++ .../io/jboot/test/join/service/AuthorService.java | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/test/java/io/jboot/test/join/JoinController.java b/src/test/java/io/jboot/test/join/JoinController.java index 3c86e389..b300bed8 100644 --- a/src/test/java/io/jboot/test/join/JoinController.java +++ b/src/test/java/io/jboot/test/join/JoinController.java @@ -3,7 +3,9 @@ package io.jboot.test.join; import com.jfinal.aop.Aop; import io.jboot.app.JbootApplication; import io.jboot.test.join.model.Article; +import io.jboot.test.join.model.Author; import io.jboot.test.join.service.ArticleService; +import io.jboot.test.join.service.AuthorService; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; @@ -35,4 +37,10 @@ public class JoinController extends JbootController { renderJson(articles); } + + public void findListWithArticle() { + List authors = Aop.get(AuthorService.class).findListWithArticle(); + renderJson(authors); + } + } diff --git a/src/test/java/io/jboot/test/join/service/AuthorService.java b/src/test/java/io/jboot/test/join/service/AuthorService.java index 8d6a5f88..73261e49 100644 --- a/src/test/java/io/jboot/test/join/service/AuthorService.java +++ b/src/test/java/io/jboot/test/join/service/AuthorService.java @@ -1,10 +1,24 @@ package io.jboot.test.join.service; +import com.jfinal.aop.Inject; import io.jboot.service.JbootServiceBase; import io.jboot.test.join.model.Author; +import java.util.List; + /** * @author michael yang (fuhai999@gmail.com) */ public class AuthorService extends JbootServiceBase { + + + @Inject + private ArticleService articleService; + + + public List findListWithArticle(){ + List authors = DAO.findAll(); + articleService.joinMany(authors,"author_id"); + return authors; + } } -- Gitee From e425c4b4d7f23406dc6d988e27aaf324ccc5fa4d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 8 Jul 2020 19:02:01 +0800 Subject: [PATCH 0380/1965] =?UTF-8?q?=E5=AE=8C=E5=96=84=20join=20=E7=B3=BB?= =?UTF-8?q?=E5=88=97=E6=96=B9=E6=B3=95=20=E5=8F=8A=E5=85=B6=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=92=8C=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/docs/db.md | 4 +- .../io/jboot/service/JbootServiceJoiner.java | 48 ++++----- .../jboot/service/JbootServiceJoinerImpl.java | 94 +++++++++--------- .../io/jboot/test/join/JoinController.java | 4 +- .../io/jboot/test/join/jboot_join_demo.sql | 99 +++++++++++++++++++ .../test/join/service/ArticleService.java | 2 +- 6 files changed, 176 insertions(+), 75 deletions(-) create mode 100644 src/test/java/io/jboot/test/join/jboot_join_demo.sql diff --git a/doc/docs/db.md b/doc/docs/db.md index 7780b04a..0caa1e65 100644 --- a/doc/docs/db.md +++ b/doc/docs/db.md @@ -261,7 +261,7 @@ Page
articlePage = articleService.paginate(...); authorService.join(articlePage,"author_id"); //查询文章的分类 -categoryService.joinManyByTable(articlePage,"article_category","category_id","article_id") +categoryService.joinManyByTable(articlePage,"article_category","article_id","category_id") ``` @@ -278,6 +278,8 @@ articleService.joinMany(authorPage,"author_id"); +具体代码参考:[这里](../../../src/test/java/io/jboot/test/join) + diff --git a/src/main/java/io/jboot/service/JbootServiceJoiner.java b/src/main/java/io/jboot/service/JbootServiceJoiner.java index 8302c5aa..80186bbe 100644 --- a/src/main/java/io/jboot/service/JbootServiceJoiner.java +++ b/src/main/java/io/jboot/service/JbootServiceJoiner.java @@ -108,58 +108,58 @@ public interface JbootServiceJoiner { public M joinMany(M model, ObjectFunc modelValueGetter, String targetColumnName, String joinName, String[] attrs); - public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName); + public Page joinManyByTable(Page page, String tableName, String columnName, String targetColumnName); - public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName, String[] attrs); + public Page joinManyByTable(Page page, String tableName, String columnName, String targetColumnName, String[] attrs); - public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName, String joinName); + public Page joinManyByTable(Page page, String tableName, String columnName, String targetColumnName, String joinName); - public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs); + public Page joinManyByTable(Page page, String tableName, String columnName, String targetColumnName, String joinName, String[] attrs); - public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName); + public List joinManyByTable(List models, String tableName, String columnName, String targetColumnName); - public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName, String[] attrs); + public List joinManyByTable(List models, String tableName, String columnName, String targetColumnName, String[] attrs); - public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName, String joinName); + public List joinManyByTable(List models, String tableName, String columnName, String targetColumnName, String joinName); - public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs); + public List joinManyByTable(List models, String tableName, String columnName, String targetColumnName, String joinName, String[] attrs); - public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName); + public M joinManyByTable(M model, String tableName, String columnName, String targetColumnName); - public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName, String[] attrs); + public M joinManyByTable(M model, String tableName, String columnName, String targetColumnName, String[] attrs); - public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName, String joinName); + public M joinManyByTable(M model, String tableName, String columnName, String targetColumnName, String joinName); - public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs); + public M joinManyByTable(M model, String tableName, String columnName, String targetColumnName, String joinName, String[] attrs); - public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName); + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName); - public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String[] attrs); + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String[] attrs); - public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName); + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String joinName); - public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs); + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String joinName, String[] attrs); - public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName); + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName); - public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String[] attrs); + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String[] attrs); - public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName); + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String joinName); - public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs); + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String joinName, String[] attrs); - public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName); + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName); - public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String[] attrs); + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String[] attrs); - public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName); + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String joinName); - public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs); + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String joinName, String[] attrs); } diff --git a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java index 261a3cf3..7ebdaa65 100644 --- a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java +++ b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java @@ -401,56 +401,56 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { /////////////////joinManyByTable start///////////////////////////// @Override - public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName) { - joinManyByTable(page.getList(), tableName, targetColumnName, columnName); + public Page joinManyByTable(Page page, String tableName, String columnName, String targetColumnName) { + joinManyByTable(page.getList(), tableName, columnName, targetColumnName); return page; } @Override - public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName, String[] attrs) { - joinManyByTable(page.getList(), tableName, targetColumnName, columnName, attrs); + public Page joinManyByTable(Page page, String tableName, String columnName, String targetColumnName, String[] attrs) { + joinManyByTable(page.getList(), tableName, columnName, targetColumnName, attrs); return page; } @Override - public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName, String joinName) { - joinManyByTable(page.getList(), tableName, targetColumnName, columnName, joinName); + public Page joinManyByTable(Page page, String tableName, String columnName, String targetColumnName, String joinName) { + joinManyByTable(page.getList(), tableName, columnName, targetColumnName, joinName); return page; } @Override - public Page joinManyByTable(Page page, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs) { - joinManyByTable(page.getList(), tableName, targetColumnName, columnName, joinName, attrs); + public Page joinManyByTable(Page page, String tableName, String columnName, String targetColumnName, String joinName, String[] attrs) { + joinManyByTable(page.getList(), tableName, columnName, targetColumnName, joinName, attrs); return page; } @Override - public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName) { + public List joinManyByTable(List models, String tableName, String columnName, String targetColumnName) { if (ArrayUtil.isNotEmpty(models)) { for (M m : models) { - joinManyByTable(m, tableName, targetColumnName, columnName); + joinManyByTable(m, tableName, columnName, targetColumnName); } } return models; } @Override - public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName, String[] attrs) { + public List joinManyByTable(List models, String tableName, String columnName, String targetColumnName, String[] attrs) { if (ArrayUtil.isNotEmpty(models)) { for (M m : models) { - joinManyByTable(m, tableName, targetColumnName, columnName, attrs); + joinManyByTable(m, tableName, columnName, targetColumnName, attrs); } } return models; } @Override - public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName, String joinName) { + public List joinManyByTable(List models, String tableName, String columnName, String targetColumnName, String joinName) { if (ArrayUtil.isNotEmpty(models)) { for (M m : models) { - joinManyByTable(m, tableName, targetColumnName, columnName, joinName); + joinManyByTable(m, tableName, columnName, targetColumnName, joinName); } } return models; @@ -458,10 +458,10 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { @Override - public List joinManyByTable(List models, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs) { + public List joinManyByTable(List models, String tableName, String columnName, String targetColumnName, String joinName, String[] attrs) { if (ArrayUtil.isNotEmpty(models)) { for (M m : models) { - joinManyByTable(m, tableName, targetColumnName, columnName, joinName, attrs); + joinManyByTable(m, tableName, columnName, targetColumnName, joinName, attrs); } } return models; @@ -469,77 +469,77 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { @Override - public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName) { - return joinManyByTable(model, tableName, targetColumnName, columnName, null, null); + public M joinManyByTable(M model, String tableName, String columnName, String targetColumnName) { + return joinManyByTable(model, tableName, columnName, targetColumnName, null, null); } @Override - public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName, String[] attrs) { - return joinManyByTable(model, tableName, targetColumnName, columnName, null, attrs); + public M joinManyByTable(M model, String tableName, String columnName, String targetColumnName, String[] attrs) { + return joinManyByTable(model, tableName, columnName, targetColumnName, null, attrs); } @Override - public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName, String joinName) { - return joinManyByTable(model, tableName, targetColumnName, columnName, joinName, null); + public M joinManyByTable(M model, String tableName, String columnName, String targetColumnName, String joinName) { + return joinManyByTable(model, tableName, columnName, targetColumnName, joinName, null); } @Override - public M joinManyByTable(M model, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs) { - return joinManyByTable(model, null, tableName, targetColumnName, columnName, joinName, attrs); + public M joinManyByTable(M model, String tableName, String columnName, String targetColumnName, String joinName, String[] attrs) { + return joinManyByTable(model, null, tableName, columnName, targetColumnName, joinName, attrs); } @Override - public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName) { - joinManyByTable(page.getList(), modelValueGetter, tableName, targetColumnName, columnName); + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName) { + joinManyByTable(page.getList(), modelValueGetter, tableName, columnName, targetColumnName); return page; } @Override - public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String[] attrs) { - joinManyByTable(page.getList(), modelValueGetter, tableName, targetColumnName, columnName, attrs); + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String[] attrs) { + joinManyByTable(page.getList(), modelValueGetter, tableName, columnName, targetColumnName, attrs); return page; } @Override - public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName) { - joinManyByTable(page.getList(), modelValueGetter, tableName, targetColumnName, columnName, joinName); + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String joinName) { + joinManyByTable(page.getList(), modelValueGetter, tableName, columnName, targetColumnName, joinName); return page; } @Override - public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs) { - joinManyByTable(page.getList(), modelValueGetter, tableName, targetColumnName, columnName, joinName, attrs); + public Page joinManyByTable(Page page, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String joinName, String[] attrs) { + joinManyByTable(page.getList(), modelValueGetter, tableName, columnName, targetColumnName, joinName, attrs); return page; } @Override - public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName) { + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName) { if (ArrayUtil.isNotEmpty(models)) { for (M m : models) { - joinManyByTable(m, modelValueGetter, tableName, targetColumnName, columnName); + joinManyByTable(m, modelValueGetter, tableName, columnName, targetColumnName); } } return models; } @Override - public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String[] attrs) { + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String[] attrs) { if (ArrayUtil.isNotEmpty(models)) { for (M m : models) { - joinManyByTable(m, modelValueGetter, tableName, targetColumnName, columnName, attrs); + joinManyByTable(m, modelValueGetter, tableName, columnName, targetColumnName, attrs); } } return models; } @Override - public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName) { + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String joinName) { if (ArrayUtil.isNotEmpty(models)) { for (M m : models) { - joinManyByTable(m, modelValueGetter, tableName, targetColumnName, columnName, joinName); + joinManyByTable(m, modelValueGetter, tableName, columnName, targetColumnName, joinName); } } return models; @@ -547,10 +547,10 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { @Override - public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs) { + public List joinManyByTable(List models, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String joinName, String[] attrs) { if (ArrayUtil.isNotEmpty(models)) { for (M m : models) { - joinManyByTable(m, modelValueGetter, tableName, targetColumnName, columnName, joinName, attrs); + joinManyByTable(m, modelValueGetter, tableName, columnName, targetColumnName, joinName, attrs); } } return models; @@ -558,22 +558,22 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { @Override - public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName) { - return joinManyByTable(model, modelValueGetter, tableName, targetColumnName, columnName, null, null); + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName) { + return joinManyByTable(model, modelValueGetter, tableName, columnName, targetColumnName, null, null); } @Override - public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String[] attrs) { - return joinManyByTable(model, modelValueGetter, tableName, targetColumnName, columnName, null, attrs); + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String[] attrs) { + return joinManyByTable(model, modelValueGetter, tableName, columnName, targetColumnName, null, attrs); } @Override - public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName) { - return joinManyByTable(model, modelValueGetter, tableName, targetColumnName, columnName, joinName, null); + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String joinName) { + return joinManyByTable(model, modelValueGetter, tableName, columnName, targetColumnName, joinName, null); } @Override - public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String targetColumnName, String columnName, String joinName, String[] attrs) { + public M joinManyByTable(M model, ObjectFunc modelValueGetter, String tableName, String columnName, String targetColumnName, String joinName, String[] attrs) { if (model == null) { return null; } diff --git a/src/test/java/io/jboot/test/join/JoinController.java b/src/test/java/io/jboot/test/join/JoinController.java index b300bed8..549e8344 100644 --- a/src/test/java/io/jboot/test/join/JoinController.java +++ b/src/test/java/io/jboot/test/join/JoinController.java @@ -32,13 +32,13 @@ public class JoinController extends JbootController { } - public void findListWithAuthorAndCategorys() { + public void articles() { List
articles = Aop.get(ArticleService.class).findListWithAuthorAndCategorys(); renderJson(articles); } - public void findListWithArticle() { + public void authors() { List authors = Aop.get(AuthorService.class).findListWithArticle(); renderJson(authors); } diff --git a/src/test/java/io/jboot/test/join/jboot_join_demo.sql b/src/test/java/io/jboot/test/join/jboot_join_demo.sql new file mode 100644 index 00000000..dd2cc15f --- /dev/null +++ b/src/test/java/io/jboot/test/join/jboot_join_demo.sql @@ -0,0 +1,99 @@ +# Dump of table article +# ------------------------------------------------------------ + +DROP TABLE IF EXISTS `article`; + +CREATE TABLE `article` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `author_id` int(11) unsigned DEFAULT NULL, + `title` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `content` text COLLATE utf8mb4_unicode_ci, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +LOCK TABLES `article` WRITE; +/*!40000 ALTER TABLE `article` DISABLE KEYS */; + +INSERT INTO `article` (`id`, `author_id`, `title`, `content`) +VALUES + (1,1,'文章1','内容111'), + (2,1,'文章2','内容2222'), + (3,2,'文章3','内容333'), + (4,2,'文章4','内容444'); + +/*!40000 ALTER TABLE `article` ENABLE KEYS */; +UNLOCK TABLES; + + +# Dump of table article_category +# ------------------------------------------------------------ + +DROP TABLE IF EXISTS `article_category`; + +CREATE TABLE `article_category` ( + `article_id` int(11) unsigned NOT NULL, + `category_id` int(11) unsigned DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +LOCK TABLES `article_category` WRITE; +/*!40000 ALTER TABLE `article_category` DISABLE KEYS */; + +INSERT INTO `article_category` (`article_id`, `category_id`) +VALUES + (1,1), + (1,2), + (2,2), + (3,1), + (3,2), + (4,1); + +/*!40000 ALTER TABLE `article_category` ENABLE KEYS */; +UNLOCK TABLES; + + +# Dump of table author +# ------------------------------------------------------------ + +DROP TABLE IF EXISTS `author`; + +CREATE TABLE `author` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `nickname` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `email` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +LOCK TABLES `author` WRITE; +/*!40000 ALTER TABLE `author` DISABLE KEYS */; + +INSERT INTO `author` (`id`, `nickname`, `email`) +VALUES + (1,'孙悟空','swk@gmail.com'), + (2,'猪八戒','zbj@gmail.com'); + +/*!40000 ALTER TABLE `author` ENABLE KEYS */; +UNLOCK TABLES; + + +# Dump of table category +# ------------------------------------------------------------ + +DROP TABLE IF EXISTS `category`; + +CREATE TABLE `category` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `title` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8mb4_unicode_ci, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +LOCK TABLES `category` WRITE; +/*!40000 ALTER TABLE `category` DISABLE KEYS */; + +INSERT INTO `category` (`id`, `title`, `description`) +VALUES + (1,'文章分类1','文章分类描述111'), + (2,'文章分类2','文章分类描述222'); + +/*!40000 ALTER TABLE `category` ENABLE KEYS */; +UNLOCK TABLES; diff --git a/src/test/java/io/jboot/test/join/service/ArticleService.java b/src/test/java/io/jboot/test/join/service/ArticleService.java index b5213bcd..4124dcef 100644 --- a/src/test/java/io/jboot/test/join/service/ArticleService.java +++ b/src/test/java/io/jboot/test/join/service/ArticleService.java @@ -21,7 +21,7 @@ public class ArticleService extends JbootServiceBase
{ public List
findListWithAuthorAndCategorys(){ List
articles = DAO.findAll(); authorService.join(articles,"author_id"); - categoryService.joinManyByTable(articles, "article_category","category_id","article_id"); + categoryService.joinManyByTable(articles, "article_category","article_id","category_id"); return articles; } } -- Gitee From 9011dba923067771a61c508087501f2cf2f28319 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 8 Jul 2020 19:07:52 +0800 Subject: [PATCH 0381/1965] =?UTF-8?q?=E5=AE=8C=E5=96=84=20join=20=E7=B3=BB?= =?UTF-8?q?=E5=88=97=E6=96=B9=E6=B3=95=20=E5=8F=8A=E5=85=B6=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=92=8C=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/docs/db.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/docs/db.md b/doc/docs/db.md index 0caa1e65..23ed6b6d 100644 --- a/doc/docs/db.md +++ b/doc/docs/db.md @@ -278,7 +278,7 @@ articleService.joinMany(authorPage,"author_id"); -具体代码参考:[这里](../../../src/test/java/io/jboot/test/join) +具体代码参考:[这里](../../src/test/java/io/jboot/test/join) -- Gitee From 065e961927958ff3ed53a71161405197c33797ec Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Jul 2020 10:15:31 +0800 Subject: [PATCH 0382/1965] rename rpc spi filter --- ...m.alibaba.dubbo.rpc.Filter => org.apache.dubbo.rpc.Filter} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/main/resources/META-INF/dubbo/internal/{com.alibaba.dubbo.rpc.Filter => org.apache.dubbo.rpc.Filter} (97%) diff --git a/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter b/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter similarity index 97% rename from src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter rename to src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter index ba82ef1e..938d350f 100644 --- a/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter +++ b/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter @@ -1,2 +1,2 @@ -seata=io.jboot.support.seata.filter.TransactionPropagationFilter - +seata=io.jboot.support.seata.filter.TransactionPropagationFilter + -- Gitee From c2b6c14819dd8d25a06c13d1705dc7d7827a216a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Jul 2020 10:23:34 +0800 Subject: [PATCH 0383/1965] =?UTF-8?q?=E5=AE=8C=E5=96=84=20join=20=E7=B3=BB?= =?UTF-8?q?=E5=88=97=E6=96=B9=E6=B3=95=20=E5=8F=8A=E5=85=B6=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=92=8C=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/test/join/JoinController.java | 13 +++++++++++-- .../io/jboot/test/join/service/ArticleService.java | 4 ++-- .../io/jboot/test/join/service/AuthorService.java | 2 +- .../io/jboot/test/join/service/CategoryService.java | 12 ++++++++++++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/test/java/io/jboot/test/join/JoinController.java b/src/test/java/io/jboot/test/join/JoinController.java index 549e8344..7d73156b 100644 --- a/src/test/java/io/jboot/test/join/JoinController.java +++ b/src/test/java/io/jboot/test/join/JoinController.java @@ -4,8 +4,10 @@ import com.jfinal.aop.Aop; import io.jboot.app.JbootApplication; import io.jboot.test.join.model.Article; import io.jboot.test.join.model.Author; +import io.jboot.test.join.model.Category; import io.jboot.test.join.service.ArticleService; import io.jboot.test.join.service.AuthorService; +import io.jboot.test.join.service.CategoryService; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; @@ -33,14 +35,21 @@ public class JoinController extends JbootController { public void articles() { - List
articles = Aop.get(ArticleService.class).findListWithAuthorAndCategorys(); + List
articles = Aop.get(ArticleService.class).findListWithAuthorAndCategories(); renderJson(articles); } public void authors() { - List authors = Aop.get(AuthorService.class).findListWithArticle(); + List authors = Aop.get(AuthorService.class).findListWithArticles(); renderJson(authors); } + + + public void categories() { + List categories = Aop.get(CategoryService.class).findListWithArticles(); + renderJson(categories); + } + } diff --git a/src/test/java/io/jboot/test/join/service/ArticleService.java b/src/test/java/io/jboot/test/join/service/ArticleService.java index 4124dcef..c74ae6d4 100644 --- a/src/test/java/io/jboot/test/join/service/ArticleService.java +++ b/src/test/java/io/jboot/test/join/service/ArticleService.java @@ -18,10 +18,10 @@ public class ArticleService extends JbootServiceBase
{ private CategoryService categoryService; - public List
findListWithAuthorAndCategorys(){ + public List
findListWithAuthorAndCategories(){ List
articles = DAO.findAll(); authorService.join(articles,"author_id"); - categoryService.joinManyByTable(articles, "article_category","article_id","category_id"); + categoryService.joinManyByTable(articles,"article_category","article_id","category_id"); return articles; } } diff --git a/src/test/java/io/jboot/test/join/service/AuthorService.java b/src/test/java/io/jboot/test/join/service/AuthorService.java index 73261e49..312246be 100644 --- a/src/test/java/io/jboot/test/join/service/AuthorService.java +++ b/src/test/java/io/jboot/test/join/service/AuthorService.java @@ -16,7 +16,7 @@ public class AuthorService extends JbootServiceBase { private ArticleService articleService; - public List findListWithArticle(){ + public List findListWithArticles(){ List authors = DAO.findAll(); articleService.joinMany(authors,"author_id"); return authors; diff --git a/src/test/java/io/jboot/test/join/service/CategoryService.java b/src/test/java/io/jboot/test/join/service/CategoryService.java index 6995b55d..cdb0f3f8 100644 --- a/src/test/java/io/jboot/test/join/service/CategoryService.java +++ b/src/test/java/io/jboot/test/join/service/CategoryService.java @@ -1,10 +1,22 @@ package io.jboot.test.join.service; +import com.jfinal.aop.Inject; import io.jboot.service.JbootServiceBase; import io.jboot.test.join.model.Category; +import java.util.List; + /** * @author michael yang (fuhai999@gmail.com) */ public class CategoryService extends JbootServiceBase { + + @Inject + private ArticleService articleService; + + public List findListWithArticles(){ + List categories = DAO.findAll(); + articleService.joinManyByTable(categories,"article_category","category_id","article_id"); + return categories; + } } -- Gitee From dbffe437d5bf9e4acae73100c7e21a81b9cc1f1b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Jul 2020 10:36:43 +0800 Subject: [PATCH 0384/1965] v3.2.8 --- src/main/java/io/jboot/core/JbootCoreConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 4adac7cb..838c6a27 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -140,7 +140,7 @@ public class JbootCoreConfig extends JFinalConfig { constants.setReportAfterInvocation(false); constants.setControllerFactory(JbootControllerManager.me()); - constants.setJsonFactory(() -> new JbootJson()); + constants.setJsonFactory(JbootJson::new); constants.setInjectDependency(true); -- Gitee From eb4926db241dd24cac189893b518ae7e279d5ccb Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Jul 2020 11:01:09 +0800 Subject: [PATCH 0385/1965] =?UTF-8?q?=E5=AE=8C=E5=96=84=20join=20=E7=B3=BB?= =?UTF-8?q?=E5=88=97=E6=96=B9=E6=B3=95=20=E5=8F=8A=E5=85=B6=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=92=8C=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/docs/db.md | 325 ++++++++++++++++-- doc/docs/imgs/db001.jpg | Bin 43521 -> 0 bytes .../jboot/service/JbootServiceJoinerImpl.java | 18 +- 3 files changed, 310 insertions(+), 33 deletions(-) delete mode 100644 doc/docs/imgs/db001.jpg diff --git a/doc/docs/db.md b/doc/docs/db.md index 23ed6b6d..d5b6bc1f 100644 --- a/doc/docs/db.md +++ b/doc/docs/db.md @@ -189,12 +189,12 @@ public List findListBy(int userAge,String articleTitle){ ```sql CREATE TABLE `article` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', - `title` varchar(128) DEFAULT NULL COMMENT '文章标题', - `author_id` int(11) unsigned NOT NULL COMMENT '文章作者ID', - `content` varchar(128) DEFAULT NULL COMMENT '文章内容', + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `author_id` int(11) unsigned DEFAULT NULL, + `title` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `content` text COLLATE utf8mb4_unicode_ci, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章信息表'; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` @@ -203,10 +203,11 @@ CREATE TABLE `article` ( ```sql CREATE TABLE `author` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', - `username` varchar(128) DEFAULT NULL COMMENT '作者名称', + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `nickname` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `email` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章作者表'; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` @@ -215,26 +216,62 @@ CREATE TABLE `author` ( ```sql CREATE TABLE `category` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', - `title` varchar(128) DEFAULT NULL COMMENT '文章分类名称', - `description` varchar(128) DEFAULT NULL COMMENT '文章分类描述', + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `title` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8mb4_unicode_ci, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章分类表'; +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` -文章分类和分类的 多对对关系表: article_category_mapping : +文章分类和分类的 多对对关系表: article_category: ```sql CREATE TABLE `article_category` ( - `article_id` int(11) unsigned NOT NULL COMMENT '主键ID', - `category_id` int(11) unsigned NOT NULL COMMENT '文章分类名称', -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章分类和分类的 多对对关系表'; + `article_id` int(11) unsigned NOT NULL, + `category_id` int(11) unsigned DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` +数据内容如下: + + + +```sql +INSERT INTO `article` (`id`, `author_id`, `title`, `content`) +VALUES + (1,1,'文章1','内容111'), + (2,1,'文章2','内容2222'), + (3,2,'文章3','内容333'), + (4,2,'文章4','内容444'); + +INSERT INTO `author` (`id`, `nickname`, `email`) +VALUES + (1,'孙悟空','swk@gmail.com'), + (2,'猪八戒','zbj@gmail.com'); + +INSERT INTO `category` (`id`, `title`, `description`) +VALUES + (1,'文章分类1','文章分类描述111'), + (2,'文章分类2','文章分类描述222'); + +INSERT INTO `article_category` (`article_id`, `category_id`) +VALUES + (1,1), + (1,2), + (2,2), + (3,1), + (3,2), + (4,1); +``` + + + + + 这样,在我们的代码里应该存在 3 个Service,分别是: - ArticleService 用于查询文章相关的服务 @@ -243,37 +280,265 @@ CREATE TABLE `article_category` ( -当在一个网页中显示文字列表,文章列表里,及包含了作者信息,也包含了多个分类,如下图所示: +代码如下: -![](./imgs/db001.jpg) +```java +public class ArticleService extends JbootServiceBase
{ + + @Inject + private AuthorService authorService; + + @Inject + private CategoryService categoryService; + + public List
findListWithAuthorAndCategories(){ + List
articles = DAO.findAll(); + + // 查询出每篇文章的作者 + authorService.join(articles,"author_id"); + + // 查询文章的所属分类 + categoryService.joinManyByTable(articles,"article_category","article_id","category_id"); + + return articles; + } +} +``` -那么,查询代码如下(伪代码): +ArticleService 输出的 Json 内容如下: + +```json +[ + { + "author":{ + "nickname":"孙悟空", + "id":1, + "email":"swk@gmail.com" + }, + "categoryList":[ + { + "description":"文章分类描述111", + "id":1, + "title":"文章分类1" + }, + { + "description":"文章分类描述222", + "id":2, + "title":"文章分类2" + } + ], + "id":1, + "authorId":1, + "title":"文章1", + "content":"内容111" + }, + { + "author":{ + "nickname":"孙悟空", + "id":1, + "email":"swk@gmail.com" + }, + "categoryList":[ + { + "description":"文章分类描述222", + "id":2, + "title":"文章分类2" + } + ], + "id":2, + "authorId":1, + "title":"文章2", + "content":"内容2222" + }, + { + "author":{ + "nickname":"猪八戒", + "id":2, + "email":"zbj@gmail.com" + }, + "categoryList":[ + { + "description":"文章分类描述111", + "id":1, + "title":"文章分类1" + }, + { + "description":"文章分类描述222", + "id":2, + "title":"文章分类2" + } + ], + "id":3, + "authorId":2, + "title":"文章3", + "content":"内容333" + }, + { + "author":{ + "nickname":"猪八戒", + "id":2, + "email":"zbj@gmail.com" + }, + "categoryList":[ + { + "description":"文章分类描述111", + "id":1, + "title":"文章分类1" + } + ], + "id":4, + "authorId":2, + "title":"文章4", + "content":"内容444" + } +] +``` -```java -//分页查询所有的文章 -Page
articlePage = articleService.paginate(...); -//查询文章的作者 -authorService.join(articlePage,"author_id"); -//查询文章的分类 -categoryService.joinManyByTable(articlePage,"article_category","article_id","category_id") +```java +public class AuthorService extends JbootServiceBase { + + @Inject + private ArticleService articleService; + + public List findListWithArticles(){ + List authors = DAO.findAll(); + + //查询每个作者的所有文章 + articleService.joinMany(authors,"author_id"); + + return authors; + } +} + +``` +AuthorService 输出的 Json 内容如下: + +```json +[ + { + "articleList":[ + { + "id":1, + "authorId":1, + "title":"文章1", + "content":"内容111" + }, + { + "id":2, + "authorId":1, + "title":"文章2", + "content":"内容2222" + } + ], + "nickname":"孙悟空", + "id":1, + "email":"swk@gmail.com" + }, + { + "articleList":[ + { + "id":3, + "authorId":2, + "title":"文章3", + "content":"内容333" + }, + { + "id":4, + "authorId":2, + "title":"文章4", + "content":"内容444" + } + ], + "nickname":"猪八戒", + "id":2, + "email":"zbj@gmail.com" + } +] ``` -如果,在某些场景下,我们要查询作者,以及作者下的文章列表,代码如下: + ```java -//分页查询出作者信息 -Page authorPage = arthorService.paginate(...); +public class CategoryService extends JbootServiceBase { + + @Inject + private ArticleService articleService; + + public List findListWithArticles(){ + List categories = DAO.findAll(); + + //查询每个分类的所有文章 + articleService.joinManyByTable(categories,"article_category","category_id","article_id"); + + return categories; + } +} +``` -articleService.joinMany(authorPage,"author_id"); +CategoryService 输出的 json 内容如下: + +```json +[ + { + "articleList":[ + { + "id":1, + "authorId":1, + "title":"文章1", + "content":"内容111" + }, + { + "id":3, + "authorId":2, + "title":"文章3", + "content":"内容333" + }, + { + "id":4, + "authorId":2, + "title":"文章4", + "content":"内容444" + } + ], + "description":"文章分类描述111", + "id":1, + "title":"文章分类1" + }, + { + "articleList":[ + { + "id":1, + "authorId":1, + "title":"文章1", + "content":"内容111" + }, + { + "id":2, + "authorId":1, + "title":"文章2", + "content":"内容2222" + }, + { + "id":3, + "authorId":2, + "title":"文章3", + "content":"内容333" + } + ], + "description":"文章分类描述222", + "id":2, + "title":"文章分类2" + } +] ``` diff --git a/doc/docs/imgs/db001.jpg b/doc/docs/imgs/db001.jpg deleted file mode 100644 index 53d6df98f98623af093f0801ed6601169f3942c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43521 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 b1ir>(IE~}~6TkAi$VWL>|3CRr|DOII75U&0 diff --git a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java index 7ebdaa65..291cc472 100644 --- a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java +++ b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java @@ -583,16 +583,16 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { return model; } - List middleTableRecords = JbootDb.find(tableName, Columns.create(columnName, columnValue)); + List middleTableRecords = findMiddleTableRecords(tableName, columnName, columnValue); if (middleTableRecords == null || middleTableRecords.isEmpty()) { return model; } - List list = new ArrayList(); + List list = new ArrayList(); for (Record record : middleTableRecords) { Object targetTableValue = record.get(targetColumnName); if (targetTableValue != null) { - JbootModel data = joinByValue(targetTableValue, model); + M data = (M) joinByValue(targetTableValue, model); if (data != null) { list.add(data); } @@ -608,6 +608,18 @@ public abstract class JbootServiceJoinerImpl implements JbootServiceJoiner { } + /** + * 查询中间表数据,方便子类复写,比如:通过缓存获取等 + * @param tableName + * @param columnName + * @param columnValue + * @return + */ + protected List findMiddleTableRecords(String tableName, String columnName, Object columnValue) { + return JbootDb.find(tableName, Columns.create(columnName, columnValue)); + } + + /////////////////joinManyByTable end///////////////////////////// } -- Gitee From a5d665faf2da8c4918ce6417c5da5b283d4b1d36 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Jul 2020 12:00:32 +0800 Subject: [PATCH 0386/1965] rename ModelCopier to ModelUtil --- .../cache/interceptor/JbootCacheInterceptor.java | 12 ++++++------ .../jboot/utils/{ModelCopier.java => ModelUtil.java} | 2 +- .../{ModelCopierTest.java => ModelUtilTest.java} | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) rename src/main/java/io/jboot/utils/{ModelCopier.java => ModelUtil.java} (99%) rename src/test/java/io/jboot/test/other/{ModelCopierTest.java => ModelUtilTest.java} (88%) diff --git a/src/main/java/io/jboot/components/cache/interceptor/JbootCacheInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/JbootCacheInterceptor.java index b79e58a4..3b36a95f 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/JbootCacheInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/JbootCacheInterceptor.java @@ -25,7 +25,7 @@ import io.jboot.db.model.JbootModel; import io.jboot.exception.JbootException; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.ClassUtil; -import io.jboot.utils.ModelCopier; +import io.jboot.utils.ModelUtil; import java.lang.reflect.Method; import java.util.List; @@ -91,16 +91,16 @@ public class JbootCacheInterceptor implements Interceptor { private Object getCopyObject(Invocation inv, Object data) { if (data instanceof List) { - return ModelCopier.copy((List) data); + return ModelUtil.copy((List) data); } else if (data instanceof Set) { - return ModelCopier.copy((Set) data); + return ModelUtil.copy((Set) data); } else if (data instanceof Page) { - return ModelCopier.copy((Page) data); + return ModelUtil.copy((Page) data); } else if (data instanceof JbootModel) { - return ModelCopier.copy((JbootModel) data); + return ModelUtil.copy((JbootModel) data); } else if (data.getClass().isArray() && JbootModel.class.isAssignableFrom(data.getClass().getComponentType())) { - return ModelCopier.copy((M[]) data); + return ModelUtil.copy((M[]) data); } else { throw newException(null, inv, data); } diff --git a/src/main/java/io/jboot/utils/ModelCopier.java b/src/main/java/io/jboot/utils/ModelUtil.java similarity index 99% rename from src/main/java/io/jboot/utils/ModelCopier.java rename to src/main/java/io/jboot/utils/ModelUtil.java index 3d5d5297..d1a587c9 100644 --- a/src/main/java/io/jboot/utils/ModelCopier.java +++ b/src/main/java/io/jboot/utils/ModelUtil.java @@ -29,7 +29,7 @@ import java.util.Set; * @author michael yang (fuhai999@gmail.com) * @Date: 2019/11/21 */ -public class ModelCopier { +public class ModelUtil { /** * copy model list diff --git a/src/test/java/io/jboot/test/other/ModelCopierTest.java b/src/test/java/io/jboot/test/other/ModelUtilTest.java similarity index 88% rename from src/test/java/io/jboot/test/other/ModelCopierTest.java rename to src/test/java/io/jboot/test/other/ModelUtilTest.java index 0d1f74a2..e1051f16 100644 --- a/src/test/java/io/jboot/test/other/ModelCopierTest.java +++ b/src/test/java/io/jboot/test/other/ModelUtilTest.java @@ -16,7 +16,7 @@ package io.jboot.test.other; import io.jboot.test.db.model.User; -import io.jboot.utils.ModelCopier; +import io.jboot.utils.ModelUtil; import java.util.ArrayList; import java.util.List; @@ -25,20 +25,20 @@ import java.util.List; * @author michael yang (fuhai999@gmail.com) * @Date: 2020/2/9 */ -public class ModelCopierTest { +public class ModelUtilTest { public static void main(String[] args) { List users = new ArrayList<>(); users.add(new User()); - List newUsers = ModelCopier.copy(users); + List newUsers = ModelUtil.copy(users); System.out.println("users == newUsers ---> " + (users == newUsers)); System.out.println("users.get(0) == newUsers.get(0) ---> " + (users.get(0) == newUsers.get(0))); User[] userArray = new User[]{ new User()}; - User[] newUserArray = ModelCopier.copy(userArray); + User[] newUserArray = ModelUtil.copy(userArray); System.out.println("userArray == newUserArray ---> " + (userArray == newUserArray)); -- Gitee From faab3502abf77b36789ca400c9a6c0ebd21762e6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Jul 2020 12:25:24 +0800 Subject: [PATCH 0387/1965] v3.2.8 release (^.^)YYa!! --- changes.txt | 14 ++++ .../io/jboot/service/JbootServiceBase.java | 49 ++++++++++++- src/main/java/io/jboot/utils/ModelUtil.java | 71 +++++++++++++++++-- 3 files changed, 128 insertions(+), 6 deletions(-) diff --git a/changes.txt b/changes.txt index 6712c810..0f74fec9 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,17 @@ +jboot v3.2.8: +新增:Columns.toWherePartSql() 方便构建 sql +新增:ObjectFunc.java 方便通过 Java8 lambda 调用 +新增:JbootServiceJoiner.joinMany 方法,方便进行一对多的查询 +新增:JbootServiceJoiner.joinManyByTable 方法,方便通过第三映射表进行多对多查询 +新增:JbootServiceJoiner.syncModels 方法,用过同步数据到数据库 +修复:Columns.likeAppendPrecent 传入空数据时结果出错的问题 +修复:Seata 对 dubbo 的 SPI 过滤器文件名错误的问题 +优化:当 jwt 解析出错时,输出错误的日志信息 +优化:重命名 joinById() 为 joinByValue(),因为传入的值不一定只是 id +文档:新增 一多一、一对多、多对对查询的相关文档 + + + jboot v3.2.7: 优化:修改 JbootModel.buildIdCacheKey 为 protected 修饰,方便在某些情况下进行重写。 优化:Columns 在 safeMode 模式下,当传入 null 值时,直接抛出空指针异常,更加方便开发调试。 diff --git a/src/main/java/io/jboot/service/JbootServiceBase.java b/src/main/java/io/jboot/service/JbootServiceBase.java index 991aa870..af9863bf 100644 --- a/src/main/java/io/jboot/service/JbootServiceBase.java +++ b/src/main/java/io/jboot/service/JbootServiceBase.java @@ -15,12 +15,15 @@ */ package io.jboot.service; +import com.jfinal.plugin.activerecord.Db; import com.jfinal.plugin.activerecord.Model; import com.jfinal.plugin.activerecord.Page; import io.jboot.db.model.Columns; import io.jboot.db.model.JbootModel; import io.jboot.exception.JbootException; import io.jboot.utils.ClassUtil; +import io.jboot.utils.ModelUtil; +import io.jboot.utils.ObjectFunc; import java.util.List; @@ -296,6 +299,48 @@ public class JbootServiceBase> } + /** + * 同步 model 数据到数据库 + * + * @param columns + * @param syncModels + * @param compareByAttrs + */ + public void syncModels(Columns columns, List syncModels, ObjectFunc... compareByAttrs) { + if (columns == null) { + throw new NullPointerException("columns must not be null"); + } + + if (syncModels == null || syncModels.isEmpty()) { + DAO.deleteByColumns(columns); + return; + } + + List existModels = findListByColumns(columns); + if (existModels == null || existModels.isEmpty()) { + Db.batchSave(syncModels, syncModels.size()); + return; + } + + + for (M existModel : existModels) { + if (!ModelUtil.isContainsModel(syncModels, existModel, compareByAttrs)) { + existModel.delete(); + } + } + + + for (M syncModel : syncModels) { + M existModel = ModelUtil.getContainsModel(existModels, syncModel, compareByAttrs); + if (existModel == null) { + syncModel.save(); + } else { + existModel._setAttrs(syncModel).update(); + } + } + } + + /** * 复写 JbootServiceJoinerImpl 的方法 * @@ -320,7 +365,7 @@ public class JbootServiceBase> @Override - protected List joinManyByValue(String columnName, Object value, M sourceModel){ - return (List) findListByColumns(Columns.create(columnName,value)); + protected List joinManyByValue(String columnName, Object value, M sourceModel) { + return (List) findListByColumns(Columns.create(columnName, value)); } } diff --git a/src/main/java/io/jboot/utils/ModelUtil.java b/src/main/java/io/jboot/utils/ModelUtil.java index d1a587c9..0ef6eff7 100644 --- a/src/main/java/io/jboot/utils/ModelUtil.java +++ b/src/main/java/io/jboot/utils/ModelUtil.java @@ -15,15 +15,13 @@ */ package io.jboot.utils; +import com.jfinal.plugin.activerecord.Model; import com.jfinal.plugin.activerecord.Page; import io.jboot.db.model.JbootModel; import io.jboot.exception.JbootException; import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; /** * @author michael yang (fuhai999@gmail.com) @@ -133,6 +131,7 @@ public class ModelUtil { } + private static T newInstance(Class clazz) { try { return clazz.newInstance(); @@ -141,4 +140,68 @@ public class ModelUtil { } } + + + + /** + * 判断 某个 Model list 里是否包含了某个 Model + * + * @param models model list + * @param compareModel 是否被包 list 含的对比 model + * @param compareByAttrs 需要对比的字段 + * @param + * @return true 包含,false 不包含 + */ + public static boolean isContainsModel(List models, T compareModel, ObjectFunc... compareByAttrs) { + if (models == null || models.isEmpty() || compareModel == null) { + return false; + } + + for (T item : models) { + boolean equals = true; + for (ObjectFunc getter : compareByAttrs) { + if (!Objects.equals(getter.get(item), getter.get(compareModel))) { + equals = false; + break; + } + } + if (equals) { + return true; + } + } + + return false; + } + + + /** + * 获取 某个 Model list 里包含的 Model + * + * @param models model list + * @param compareModel 是否被包 list 含的对比 model + * @param compareByAttrs 对比字段 + * @param + * @return 返回 model list 中对比成功的 model + */ + public static T getContainsModel(List models, T compareModel, ObjectFunc... compareByAttrs) { + if (models == null || models.isEmpty() || compareModel == null) { + return null; + } + + for (T item : models) { + boolean equals = true; + for (ObjectFunc getter : compareByAttrs) { + if (!Objects.equals(getter.get(item), getter.get(compareModel))) { + equals = false; + break; + } + } + if (equals) { + return item; + } + } + + return null; + } + } -- Gitee From 896e183df576532909a4a6fc864d442c973b0f00 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Jul 2020 12:26:23 +0800 Subject: [PATCH 0388/1965] v3.2.8 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8af8c237..628b9001 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.2.7 + 3.2.8 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 5ac9a931..5d83fb6c 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.2.7 + 3.2.8 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 66bd1d28..a747dbd6 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.2.7 + 3.2.8 ``` diff --git a/pom.xml b/pom.xml index ce53cac6..4ceecd72 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.8-SNAPSHOT + 3.2.8 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index bc3f31ea..d46cfc77 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.2.7"; + public static String VERSION = "3.2.8"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 8c0c99e25384e813d6b1887f9e59089670efc3a0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Jul 2020 16:27:40 +0800 Subject: [PATCH 0389/1965] v3.2.9 snapshot --- doc/docs/db.md | 54 +++++++++++++++++++++++++++++--------------------- pom.xml | 2 +- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/doc/docs/db.md b/doc/docs/db.md index d5b6bc1f..9950422a 100644 --- a/doc/docs/db.md +++ b/doc/docs/db.md @@ -189,12 +189,12 @@ public List findListBy(int userAge,String articleTitle){ ```sql CREATE TABLE `article` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `author_id` int(11) unsigned DEFAULT NULL, - `title` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `content` text COLLATE utf8mb4_unicode_ci, + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `author_id` int(11) unsigned DEFAULT NULL COMMENT '文章作者ID', + `title` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文章标题', + `content` text COLLATE utf8mb4_unicode_ci COMMENT '文章内容', PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` @@ -203,11 +203,11 @@ CREATE TABLE `article` ( ```sql CREATE TABLE `author` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `nickname` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `email` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `nickname` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '作者昵称', + `email` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '作者邮件', PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` @@ -216,11 +216,11 @@ CREATE TABLE `author` ( ```sql CREATE TABLE `category` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `title` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `description` text COLLATE utf8mb4_unicode_ci, + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `title` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '分类标题', + `description` text COLLATE utf8mb4_unicode_ci COMMENT '分类描述', PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` @@ -229,8 +229,8 @@ CREATE TABLE `category` ( ```sql CREATE TABLE `article_category` ( - `article_id` int(11) unsigned NOT NULL, - `category_id` int(11) unsigned DEFAULT NULL + `article_id` int(11) unsigned NOT NULL COMMENT '文章ID', + `category_id` int(11) unsigned DEFAULT NULL COMMENT '分类ID' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` @@ -247,17 +247,28 @@ VALUES (2,1,'文章2','内容2222'), (3,2,'文章3','内容333'), (4,2,'文章4','内容444'); +``` +有 4 篇文章。 + +```sql INSERT INTO `author` (`id`, `nickname`, `email`) VALUES (1,'孙悟空','swk@gmail.com'), (2,'猪八戒','zbj@gmail.com'); +``` +有 2 个作者。 +```sql INSERT INTO `category` (`id`, `title`, `description`) VALUES (1,'文章分类1','文章分类描述111'), (2,'文章分类2','文章分类描述222'); - +``` +有 2 个文章分类。 + + +```sql INSERT INTO `article_category` (`article_id`, `category_id`) VALUES (1,1), @@ -267,8 +278,7 @@ VALUES (3,2), (4,1); ``` - - +文章 1 有两个分类;文章 2 有一个分类; 文章 3 有两个分类; 文章 4 有一个分类。 @@ -280,9 +290,7 @@ VALUES -代码如下: - - +ArticleService 代码如下: ```java public class ArticleService extends JbootServiceBase
{ @@ -399,7 +407,7 @@ ArticleService 输出的 Json 内容如下: - +AuthorService 代码如下: ```java public class AuthorService extends JbootServiceBase { @@ -466,7 +474,7 @@ AuthorService 输出的 Json 内容如下: - +CategoryService 代码如下: ```java public class CategoryService extends JbootServiceBase { diff --git a/pom.xml b/pom.xml index 4ceecd72..03d6365a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.8 + 3.2.9-SNAPSHOT jar jboot -- Gitee From d15130cbee5dccda84ff5f0e7ab39dfc096e618e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 10 Jul 2020 12:20:15 +0800 Subject: [PATCH 0390/1965] =?UTF-8?q?fixed=EF=BC=9AColumns.in()=20can=20no?= =?UTF-8?q?t=20work=20with=20int[]=20or=20long[]=20Type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/Util.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Util.java b/src/main/java/io/jboot/db/model/Util.java index 633887cb..dc9e6f60 100644 --- a/src/main/java/io/jboot/db/model/Util.java +++ b/src/main/java/io/jboot/db/model/Util.java @@ -19,10 +19,7 @@ package io.jboot.db.model; import com.jfinal.ext.kit.DateKit; import io.jboot.utils.StrUtil; -import java.util.Collections; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; +import java.util.*; import java.util.regex.Matcher; class Util { @@ -45,7 +42,17 @@ class Util { if (value != null) { if (value.getClass().isArray()) { Object[] values = (Object[]) value; - Collections.addAll(paras, values); +// Collections.addAll(paras, values); + + for (Object v : values){ + if (v.getClass() == int[].class) { + Collections.addAll(paras, Arrays.stream((int[]) v).boxed().toArray()); + } else if (v.getClass() == long[].class) { + Collections.addAll(paras, Arrays.stream((long[]) v).boxed().toArray()); + } else { + paras.add(v); + } + } } else { paras.add(value); } -- Gitee From 1c4da5ab58dbf57b41597dbdfb4b127f5e34c1b7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 10 Jul 2020 12:36:13 +0800 Subject: [PATCH 0391/1965] =?UTF-8?q?fixed=EF=BC=9AColumns.in()=20can=20no?= =?UTF-8?q?t=20work=20with=20int[]=20or=20long[]=20Type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/db/model/SqlBuilder.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index ae3c88e0..cddcea8d 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -164,10 +164,24 @@ public class SqlBuilder { appendColumnName(sqlBuilder, column, separator); sqlBuilder.append('('); + Object[] values = (Object[]) column.getValue(); - for (int i = 0; i < values.length; i++) { + + //in 里的参数数量 + int paraCount = 0; + for (Object v : values){ + if (v.getClass() == int[].class) { + paraCount += ((int[])v).length; + } else if (v.getClass() == long[].class) { + paraCount += ((long[])v).length; + } else { + paraCount++; + } + } + + for (int i = 0; i < paraCount; i++) { sqlBuilder.append('?'); - if (i != values.length - 1) { + if (i != paraCount - 1) { sqlBuilder.append(','); } } -- Gitee From 142d1e2d7ba887530ea40b1b0448f4371365a97c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 10 Jul 2020 13:00:55 +0800 Subject: [PATCH 0392/1965] =?UTF-8?q?fixed=EF=BC=9AColumns.in()=20can=20no?= =?UTF-8?q?t=20work=20with=20int[]=20or=20long[]=20or=20short[]=20Type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/db/model/SqlBuilder.java | 8 ++++-- src/main/java/io/jboot/db/model/Util.java | 28 +++++++++++++++++-- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index cddcea8d..439d0a98 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -169,11 +169,13 @@ public class SqlBuilder { //in 里的参数数量 int paraCount = 0; - for (Object v : values){ + for (Object v : values) { if (v.getClass() == int[].class) { - paraCount += ((int[])v).length; + paraCount += ((int[]) v).length; } else if (v.getClass() == long[].class) { - paraCount += ((long[])v).length; + paraCount += ((long[]) v).length; + } else if (v.getClass() == short[].class) { + paraCount += ((short[]) v).length; } else { paraCount++; } diff --git a/src/main/java/io/jboot/db/model/Util.java b/src/main/java/io/jboot/db/model/Util.java index dc9e6f60..1c3aecfd 100644 --- a/src/main/java/io/jboot/db/model/Util.java +++ b/src/main/java/io/jboot/db/model/Util.java @@ -44,11 +44,13 @@ class Util { Object[] values = (Object[]) value; // Collections.addAll(paras, values); - for (Object v : values){ + for (Object v : values) { if (v.getClass() == int[].class) { - Collections.addAll(paras, Arrays.stream((int[]) v).boxed().toArray()); + addAll(paras, (int[]) v); } else if (v.getClass() == long[].class) { - Collections.addAll(paras, Arrays.stream((long[]) v).boxed().toArray()); + addAll(paras, (long[]) v); + } else if (v.getClass() == short[].class) { + addAll(paras, (short[]) v); } else { paras.add(v); } @@ -63,6 +65,26 @@ class Util { } + private static void addAll(List paras, int[] intArray) { + for (int i : intArray) { + paras.add(i); + } + } + + private static void addAll(List paras, long[] intArray) { + for (long i : intArray) { + paras.add(i); + } + } + + + private static void addAll(List paras, short[] intArray) { + for (short i : intArray) { + paras.add(i); + } + } + + static String replaceSqlPara(String sql, Object value) { // null if (value == null) { -- Gitee From ada657dc1f962653d05987828d82cc54fe82b72e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 10 Jul 2020 13:07:56 +0800 Subject: [PATCH 0393/1965] v3.2.9 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 628b9001..457b5c72 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.2.8 + 3.2.9 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 5d83fb6c..e9761aab 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.2.8 + 3.2.9 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index a747dbd6..2c725dc2 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.2.8 + 3.2.9 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index d46cfc77..1c2c881f 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.2.8"; + public static String VERSION = "3.2.9"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From e24348b53ff29545a6b9793578165574daa037d8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 10 Jul 2020 13:08:08 +0800 Subject: [PATCH 0394/1965] v3.2.9 release (^.^)YYa!! --- changes.txt | 5 +++++ pom.xml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 0f74fec9..008cc454 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,8 @@ +jboot v3.2.9: +修复:Columns.in() 对传入 int[] long[] short[] 不能正确支持的问题 + + + jboot v3.2.8: 新增:Columns.toWherePartSql() 方便构建 sql 新增:ObjectFunc.java 方便通过 Java8 lambda 调用 diff --git a/pom.xml b/pom.xml index 03d6365a..a9f4ac92 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.9-SNAPSHOT + 3.2.9 jar jboot -- Gitee From 383372e2e5cf9d726a28d07a10590f89c15e8000 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 13 Jul 2020 15:14:10 +0800 Subject: [PATCH 0395/1965] v3.3.0 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a9f4ac92..87457b58 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.2.9 + 3.3.0-SNAPSHOT jar jboot -- Gitee From 025f8338bd1f21dbe514ce455b66a13d61d9de27 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 13 Jul 2020 15:26:51 +0800 Subject: [PATCH 0396/1965] add ModelUtil.isSameModel method --- src/main/java/io/jboot/utils/ModelUtil.java | 37 ++++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/utils/ModelUtil.java b/src/main/java/io/jboot/utils/ModelUtil.java index 0ef6eff7..f3d5aa9e 100644 --- a/src/main/java/io/jboot/utils/ModelUtil.java +++ b/src/main/java/io/jboot/utils/ModelUtil.java @@ -79,6 +79,7 @@ public class ModelUtil { /** * copy model array + * * @param models * @param * @return @@ -131,7 +132,6 @@ public class ModelUtil { } - private static T newInstance(Class clazz) { try { return clazz.newInstance(); @@ -141,12 +141,10 @@ public class ModelUtil { } - - /** * 判断 某个 Model list 里是否包含了某个 Model * - * @param models model list + * @param models model list * @param compareModel 是否被包 list 含的对比 model * @param compareByAttrs 需要对比的字段 * @param @@ -177,7 +175,7 @@ public class ModelUtil { /** * 获取 某个 Model list 里包含的 Model * - * @param models model list + * @param models model list * @param compareModel 是否被包 list 含的对比 model * @param compareByAttrs 对比字段 * @param @@ -204,4 +202,33 @@ public class ModelUtil { return null; } + + /** + * 判断两个 Model 是否是同一个 Model,根据传入的 attrs 来进行对比 + * @param model1 + * @param model2 + * @param compareByAttrs + * @param + * @return + */ + public static boolean isSameModel(T model1, T model2, ObjectFunc... compareByAttrs) { + if (model1 == null || model2 == null) { + return false; + } + + if (compareByAttrs == null || compareByAttrs.length == 0){ + throw new IllegalArgumentException("compareByAttrs must not be null or empty"); + } + + + for (ObjectFunc getter : compareByAttrs) { + if (!Objects.equals(getter.get(model1), getter.get(model2))) { + return false; + } + } + + return true; + } + + } -- Gitee From 0867a6efa171563554e8f8b0415d25c0b3fe24a7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 13 Jul 2020 15:39:29 +0800 Subject: [PATCH 0397/1965] add ModelUtil.isSameModel method --- src/main/java/io/jboot/utils/ModelUtil.java | 31 +++++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/utils/ModelUtil.java b/src/main/java/io/jboot/utils/ModelUtil.java index f3d5aa9e..1eae0a97 100644 --- a/src/main/java/io/jboot/utils/ModelUtil.java +++ b/src/main/java/io/jboot/utils/ModelUtil.java @@ -158,6 +158,11 @@ public class ModelUtil { for (T item : models) { boolean equals = true; for (ObjectFunc getter : compareByAttrs) { + + if (getter == null) { + throw new IllegalArgumentException("compareByAttrs must not be null"); + } + if (!Objects.equals(getter.get(item), getter.get(compareModel))) { equals = false; break; @@ -189,6 +194,12 @@ public class ModelUtil { for (T item : models) { boolean equals = true; for (ObjectFunc getter : compareByAttrs) { + + if (getter == null) { + throw new IllegalArgumentException("compareByAttrs must not be null"); + } + + if (!Objects.equals(getter.get(item), getter.get(compareModel))) { equals = false; break; @@ -205,25 +216,33 @@ public class ModelUtil { /** * 判断两个 Model 是否是同一个 Model,根据传入的 attrs 来进行对比 + * * @param model1 * @param model2 * @param compareByAttrs * @param * @return */ - public static boolean isSameModel(T model1, T model2, ObjectFunc... compareByAttrs) { + public static boolean isSameModel(T model1, T model2, ObjectFunc compareByAttr, ObjectFunc... compareByAttrs) { if (model1 == null || model2 == null) { return false; } - if (compareByAttrs == null || compareByAttrs.length == 0){ - throw new IllegalArgumentException("compareByAttrs must not be null or empty"); + if (!Objects.equals(compareByAttr.get(model1), compareByAttr.get(model2))) { + return false; } + if (compareByAttrs != null && compareByAttrs.length > 0) { + for (ObjectFunc getter : compareByAttrs) { - for (ObjectFunc getter : compareByAttrs) { - if (!Objects.equals(getter.get(model1), getter.get(model2))) { - return false; + if (getter == null) { + throw new IllegalArgumentException("compareByAttrs must not be null"); + } + + + if (!Objects.equals(getter.get(model1), getter.get(model2))) { + return false; + } } } -- Gitee From 65deb82ae62c1a870d7261d81f5e6fb300628c4a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 14 Jul 2020 15:53:23 +0800 Subject: [PATCH 0398/1965] add ModelUtil.isSameModel method --- src/main/java/io/jboot/utils/ModelUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/utils/ModelUtil.java b/src/main/java/io/jboot/utils/ModelUtil.java index 1eae0a97..a65476d2 100644 --- a/src/main/java/io/jboot/utils/ModelUtil.java +++ b/src/main/java/io/jboot/utils/ModelUtil.java @@ -225,7 +225,7 @@ public class ModelUtil { */ public static boolean isSameModel(T model1, T model2, ObjectFunc compareByAttr, ObjectFunc... compareByAttrs) { if (model1 == null || model2 == null) { - return false; + return model1 == model2; } if (!Objects.equals(compareByAttr.get(model1), compareByAttr.get(model2))) { -- Gitee From 4386875a694d2d57eda591258cdda200cfcfa12e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 14 Jul 2020 19:21:26 +0800 Subject: [PATCH 0399/1965] move ModelUtil methods to ObjectUtil --- .../io/jboot/service/JbootServiceBase.java | 10 +- src/main/java/io/jboot/utils/ModelUtil.java | 114 +-------------- src/main/java/io/jboot/utils/ObjectUtil.java | 133 ++++++++++++++++++ 3 files changed, 142 insertions(+), 115 deletions(-) create mode 100644 src/main/java/io/jboot/utils/ObjectUtil.java diff --git a/src/main/java/io/jboot/service/JbootServiceBase.java b/src/main/java/io/jboot/service/JbootServiceBase.java index af9863bf..91e1e610 100644 --- a/src/main/java/io/jboot/service/JbootServiceBase.java +++ b/src/main/java/io/jboot/service/JbootServiceBase.java @@ -22,8 +22,8 @@ import io.jboot.db.model.Columns; import io.jboot.db.model.JbootModel; import io.jboot.exception.JbootException; import io.jboot.utils.ClassUtil; -import io.jboot.utils.ModelUtil; import io.jboot.utils.ObjectFunc; +import io.jboot.utils.ObjectUtil; import java.util.List; @@ -304,9 +304,9 @@ public class JbootServiceBase> * * @param columns * @param syncModels - * @param compareByAttrs + * @param compareAttrGetters */ - public void syncModels(Columns columns, List syncModels, ObjectFunc... compareByAttrs) { + public void syncModels(Columns columns, List syncModels, ObjectFunc... compareAttrGetters) { if (columns == null) { throw new NullPointerException("columns must not be null"); } @@ -324,14 +324,14 @@ public class JbootServiceBase> for (M existModel : existModels) { - if (!ModelUtil.isContainsModel(syncModels, existModel, compareByAttrs)) { + if (!ObjectUtil.isContainsObject(syncModels, existModel, compareAttrGetters)) { existModel.delete(); } } for (M syncModel : syncModels) { - M existModel = ModelUtil.getContainsModel(existModels, syncModel, compareByAttrs); + M existModel = ObjectUtil.getContainsObject(existModels, syncModel, compareAttrGetters); if (existModel == null) { syncModel.save(); } else { diff --git a/src/main/java/io/jboot/utils/ModelUtil.java b/src/main/java/io/jboot/utils/ModelUtil.java index a65476d2..5493d46a 100644 --- a/src/main/java/io/jboot/utils/ModelUtil.java +++ b/src/main/java/io/jboot/utils/ModelUtil.java @@ -15,13 +15,15 @@ */ package io.jboot.utils; -import com.jfinal.plugin.activerecord.Model; import com.jfinal.plugin.activerecord.Page; import io.jboot.db.model.JbootModel; import io.jboot.exception.JbootException; import java.lang.reflect.Array; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * @author michael yang (fuhai999@gmail.com) @@ -141,113 +143,5 @@ public class ModelUtil { } - /** - * 判断 某个 Model list 里是否包含了某个 Model - * - * @param models model list - * @param compareModel 是否被包 list 含的对比 model - * @param compareByAttrs 需要对比的字段 - * @param - * @return true 包含,false 不包含 - */ - public static boolean isContainsModel(List models, T compareModel, ObjectFunc... compareByAttrs) { - if (models == null || models.isEmpty() || compareModel == null) { - return false; - } - - for (T item : models) { - boolean equals = true; - for (ObjectFunc getter : compareByAttrs) { - - if (getter == null) { - throw new IllegalArgumentException("compareByAttrs must not be null"); - } - - if (!Objects.equals(getter.get(item), getter.get(compareModel))) { - equals = false; - break; - } - } - if (equals) { - return true; - } - } - - return false; - } - - - /** - * 获取 某个 Model list 里包含的 Model - * - * @param models model list - * @param compareModel 是否被包 list 含的对比 model - * @param compareByAttrs 对比字段 - * @param - * @return 返回 model list 中对比成功的 model - */ - public static T getContainsModel(List models, T compareModel, ObjectFunc... compareByAttrs) { - if (models == null || models.isEmpty() || compareModel == null) { - return null; - } - - for (T item : models) { - boolean equals = true; - for (ObjectFunc getter : compareByAttrs) { - - if (getter == null) { - throw new IllegalArgumentException("compareByAttrs must not be null"); - } - - - if (!Objects.equals(getter.get(item), getter.get(compareModel))) { - equals = false; - break; - } - } - if (equals) { - return item; - } - } - - return null; - } - - - /** - * 判断两个 Model 是否是同一个 Model,根据传入的 attrs 来进行对比 - * - * @param model1 - * @param model2 - * @param compareByAttrs - * @param - * @return - */ - public static boolean isSameModel(T model1, T model2, ObjectFunc compareByAttr, ObjectFunc... compareByAttrs) { - if (model1 == null || model2 == null) { - return model1 == model2; - } - - if (!Objects.equals(compareByAttr.get(model1), compareByAttr.get(model2))) { - return false; - } - - if (compareByAttrs != null && compareByAttrs.length > 0) { - for (ObjectFunc getter : compareByAttrs) { - - if (getter == null) { - throw new IllegalArgumentException("compareByAttrs must not be null"); - } - - - if (!Objects.equals(getter.get(model1), getter.get(model2))) { - return false; - } - } - } - - return true; - } - } diff --git a/src/main/java/io/jboot/utils/ObjectUtil.java b/src/main/java/io/jboot/utils/ObjectUtil.java new file mode 100644 index 00000000..b289afb4 --- /dev/null +++ b/src/main/java/io/jboot/utils/ObjectUtil.java @@ -0,0 +1,133 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.utils; + +import java.util.Collection; +import java.util.Objects; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public class ObjectUtil { + + + /** + * 判断 某个 objects 集合里是否包含了某个 object + * + * @param objects object 集合 + * @param compareObject 是否被包 集合 含的对比 object + * @param compareAttrGetters 需要对比的 getter + * @param + * @return true 包含,false 不包含 + */ + public static boolean isContainsObject(Collection objects, T compareObject, ObjectFunc... compareAttrGetters) { + if (objects == null || objects.isEmpty() || compareObject == null) { + return false; + } + + if (compareAttrGetters == null || compareAttrGetters.length == 0) { + throw new IllegalArgumentException("compareAttrGetters must not be null"); + } + + + for (T object : objects) { + if (isSameObject(object, compareObject, compareAttrGetters)) { + return true; + } + } + + return false; + } + + + /** + * 获取 某个 objects 集合里包含的 object + * + * @param objects object 集合 + * @param compareObject 是否被包 集合 含的对比 object + * @param compareAttrGetters 需要对比的 getter + * @param + * @return 返回 objects 结合中对比成功的 object + */ + public static T getContainsObject(Collection objects, T compareObject, ObjectFunc... compareAttrGetters) { + if (objects == null || objects.isEmpty() || compareObject == null) { + return null; + } + + if (compareAttrGetters == null || compareAttrGetters.length == 0) { + throw new IllegalArgumentException("compareAttrGetters must not be null"); + } + + + for (T object : objects) { + if (isSameObject(object, compareObject, compareAttrGetters)) { + return object; + } + } + + return null; + } + + + /** + * 判断两个 Object 是否是同一个 Object,根据传入的 getter 来进行对比 + * + * @param object1 + * @param object2 + * @param compareAttrGetters + * @param + * @return + */ + public static boolean isSameObject(T object1, T object2, ObjectFunc... compareAttrGetters) { + if (object1 == null || object2 == null) { + return object1 == object2; + } + + if (compareAttrGetters == null || compareAttrGetters.length == 0) { + throw new IllegalArgumentException("compareAttrGetters must not be null"); + } + + + for (ObjectFunc getter : compareAttrGetters) { + + if (getter == null) { + throw new IllegalArgumentException("compareAttrGetter must not be null"); + } + + + if (!Objects.equals(getter.get(object1), getter.get(object2))) { + return false; + } + } + + return true; + } + + + /** + * 判断两个 Object 是否是同一个 Object,根据传入的 getter 来进行对比 + * + * @param object1 + * @param object2 + * @param compareAttrGetters + * @param + * @return + */ + public static boolean notSameObject(T object1, T object2, ObjectFunc... compareAttrGetters) { + return !isSameObject(object1, object2, compareAttrGetters); + } + +} -- Gitee From b07f3f604178dd2c26222e202f369314313c1947 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 15 Jul 2020 13:28:19 +0800 Subject: [PATCH 0400/1965] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=E5=BD=93?= =?UTF-8?q?=E6=9C=89=E5=A4=9A=E6=95=B0=E6=8D=AE=E6=BA=90=E6=97=B6=EF=BC=8C?= =?UTF-8?q?join=20=E4=BC=9A=E5=8F=82=E6=95=B0=E6=95=B0=E6=8D=AE=E4=B8=8D?= =?UTF-8?q?=E6=AD=A3=E7=A1=AE=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/JbootModel.java | 9 ++++++++- .../io/jboot/test/db/simple/DbController.java | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 22523cf5..7f16994b 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -45,6 +45,7 @@ public class JbootModel> extends Model { private static boolean idCacheEnable = config.isIdCacheEnable(); protected List joins = null; + String currentConfigName = null; public Joiner leftJoin(String table) { return joining(Join.TYPE_LEFT, table, true); @@ -80,7 +81,7 @@ public class JbootModel> extends Model { protected Joiner joining(String type, String table, boolean condition) { - M model = joins == null ? copy() : (M) this; + M model = joins == null ? copy().superUse(currentConfigName) : (M) this; if (model.joins == null) { model.joins = new LinkedList<>(); } @@ -144,6 +145,7 @@ public class JbootModel> extends Model { m = this.get(DATASOURCE_CACHE_PREFIX + configName); if (m == null) { m = this.copy().superUse(configName); + m.currentConfigName = configName; this.put(DATASOURCE_CACHE_PREFIX + configName, m); } } @@ -152,6 +154,11 @@ public class JbootModel> extends Model { } + public M useFirst(String configName) { + M m = use(configName); + return m._getConfig() == null ? (M) this : m; + } + M superUse(String configName) { return super.use(configName); } diff --git a/src/test/java/io/jboot/test/db/simple/DbController.java b/src/test/java/io/jboot/test/db/simple/DbController.java index c411f0a3..9aa31946 100644 --- a/src/test/java/io/jboot/test/db/simple/DbController.java +++ b/src/test/java/io/jboot/test/db/simple/DbController.java @@ -144,4 +144,19 @@ public class DbController extends JbootController { List users = dao.leftJoin("article").as("a").on("user.id=a.user_id").findListByColumns(columns); renderJson(users); } + + + public void use(){ + User dao = new User(); + + Columns columns = Columns.create(); + columns.in("user.`id`",1,2,3,4); + + User newDao = (User) dao.useFirst("aaa"); + + +// List users = newDao.findListByColumns(columns); + List users = newDao.leftJoin("article").as("a").on("user.id=a.user_id").findListByColumns(columns); + renderJson(users); + } } -- Gitee From 6e5181de89d2b3b74bcb292f50a3bfe51fd69391 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 15 Jul 2020 13:50:51 +0800 Subject: [PATCH 0401/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9AJbootModel?= =?UTF-8?q?.useFirst()=20=E6=96=B9=E6=B3=95=EF=BC=8C=E6=9B=B4=E5=8A=A0?= =?UTF-8?q?=E6=96=B9=E4=BE=BF=E5=9C=A8=E8=AF=BB=E5=86=99=E5=88=86=E7=A6=BB?= =?UTF-8?q?=E7=9A=84=E5=9C=BA=E6=99=AF=E4=B8=8B=E8=BF=9B=E8=A1=8C=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/db/model/JbootModel.java | 61 ++++++++++++++----- .../io/jboot/test/db/simple/DbController.java | 3 +- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 7f16994b..0318daec 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -21,6 +21,7 @@ import com.jfinal.plugin.activerecord.dialect.Dialect; import io.jboot.db.SqlDebugger; import io.jboot.db.dialect.JbootDialect; import io.jboot.exception.JbootException; +import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.StrUtil; import java.lang.reflect.Array; @@ -45,7 +46,7 @@ public class JbootModel> extends Model { private static boolean idCacheEnable = config.isIdCacheEnable(); protected List joins = null; - String currentConfigName = null; + String datasourceName = null; public Joiner leftJoin(String table) { return joining(Join.TYPE_LEFT, table, true); @@ -81,7 +82,7 @@ public class JbootModel> extends Model { protected Joiner joining(String type, String table, boolean condition) { - M model = joins == null ? copy().superUse(currentConfigName) : (M) this; + M model = joins == null ? copy().superUse(datasourceName) : (M) this; if (model.joins == null) { model.joins = new LinkedList<>(); } @@ -139,26 +140,56 @@ public class JbootModel> extends Model { */ @Override public M use(String configName) { - M m = this.get(DATASOURCE_CACHE_PREFIX + configName); - if (m == null) { - synchronized (configName.intern()) { - m = this.get(DATASOURCE_CACHE_PREFIX + configName); - if (m == null) { - m = this.copy().superUse(configName); - m.currentConfigName = configName; - this.put(DATASOURCE_CACHE_PREFIX + configName, m); - } + return use(configName, true); + } + + + public M useFirst(String configName) { + M m = use(configName, false); + return m != null ? m : (M) this; + } + + + public M useFirst(String configName, String... configNames) { + M newDao = use(configName, false); + if (newDao != null) { + return newDao; + } + + for (String name : configNames) { + newDao = use(name, false); + if (newDao != null) { + return newDao; } } - return m; + return (M) this; } - public M useFirst(String configName) { - M m = use(configName); - return m._getConfig() == null ? (M) this : m; + private M use(String configName, boolean validateExist) { + M newDao = this.get(DATASOURCE_CACHE_PREFIX + configName); + if (newDao == null) { + synchronized (configName.intern()) { + newDao = this.get(DATASOURCE_CACHE_PREFIX + configName); + if (newDao == null) { + newDao = this.copy().superUse(configName); + if (newDao._getConfig() == null) { + if (validateExist) { + throw new JbootIllegalConfigException("the datasource \"" + configName + "\" not config well, please config it."); + } else { + return null; + } + } else { + newDao.datasourceName = configName; + this.put(DATASOURCE_CACHE_PREFIX + configName, newDao); + } + } + } + } + return newDao; } + M superUse(String configName) { return super.use(configName); } diff --git a/src/test/java/io/jboot/test/db/simple/DbController.java b/src/test/java/io/jboot/test/db/simple/DbController.java index 9aa31946..8a453642 100644 --- a/src/test/java/io/jboot/test/db/simple/DbController.java +++ b/src/test/java/io/jboot/test/db/simple/DbController.java @@ -152,7 +152,8 @@ public class DbController extends JbootController { Columns columns = Columns.create(); columns.in("user.`id`",1,2,3,4); - User newDao = (User) dao.useFirst("aaa"); + User newDao = (User) dao.use("aaa"); +// User newDao = (User) dao.useFirst("aaa"); // List users = newDao.findListByColumns(columns); -- Gitee From a158336f1b30cb1d4008fe283f6ccd2e1703d551 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 15 Jul 2020 17:08:33 +0800 Subject: [PATCH 0402/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9AJbootJson?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/web/JbootJson.java | 34 +++++++++---------- .../java/io/jboot/web/JbootWebConfig.java | 9 +++++ 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index 225e4b79..79bf47d2 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -22,6 +22,7 @@ import com.jfinal.kit.StrKit; import com.jfinal.plugin.activerecord.CPI; import com.jfinal.plugin.activerecord.Model; import io.jboot.Jboot; +import io.jboot.utils.StrUtil; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -36,6 +37,8 @@ public class JbootJson extends JFinalJson { protected boolean isCamelCaseJsonStyleEnable = Jboot.config(JbootWebConfig.class).isCamelCaseJsonStyleEnable(); protected boolean camelCaseToLowerCaseAnyway = Jboot.config(JbootWebConfig.class).isCamelCaseToLowerCaseAnyway(); + protected String jsonTimestampPattern = Jboot.config(JbootWebConfig.class).getJsonTimestampPattern(); + protected Map methodAndFieldsCache = new HashMap<>(); public JbootJson() { @@ -56,6 +59,10 @@ public class JbootJson extends JFinalJson { return null; } }); + + if (StrUtil.isNotBlank(jsonTimestampPattern)) { + setTimestampPattern(jsonTimestampPattern); + } } @@ -70,7 +77,7 @@ public class JbootJson extends JFinalJson { fillBeanGetterValueToMap(model, map); - optimizeMapAttrs(model, map); + optimizeMapAttrs(map); JFinalJsonKit.mapToJson(map, depth, ret); }; @@ -118,10 +125,18 @@ public class JbootJson extends JFinalJson { } - protected void optimizeMapAttrs(Model model, Map map) { + protected void optimizeMapAttrs(Map map) { } + @Override + public T parse(String jsonString, Class type) { + return JSON.parseObject(jsonString, type); + } + + + + public static class MethodsAndFieldsWrapper { private List fields = new ArrayList<>(); @@ -155,20 +170,5 @@ public class JbootJson extends JFinalJson { } } } - - - public List getFields() { - return fields; - } - - public List getMethods() { - return methods; - } - } - - - @Override - public T parse(String jsonString, Class type) { - return JSON.parseObject(jsonString, type); } } diff --git a/src/main/java/io/jboot/web/JbootWebConfig.java b/src/main/java/io/jboot/web/JbootWebConfig.java index ec6e130a..f880abf9 100644 --- a/src/main/java/io/jboot/web/JbootWebConfig.java +++ b/src/main/java/io/jboot/web/JbootWebConfig.java @@ -32,6 +32,7 @@ public class JbootWebConfig { private String webSocketEndpoint; private boolean camelCaseJsonStyleEnable = true; private boolean camelCaseToLowerCaseAnyway = false; + private String jsonTimestampPattern; public String getCookieEncryptKey() { return cookieEncryptKey; @@ -64,4 +65,12 @@ public class JbootWebConfig { public void setCamelCaseToLowerCaseAnyway(boolean camelCaseToLowerCaseAnyway) { this.camelCaseToLowerCaseAnyway = camelCaseToLowerCaseAnyway; } + + public String getJsonTimestampPattern() { + return jsonTimestampPattern; + } + + public void setJsonTimestampPattern(String jsonTimestampPattern) { + this.jsonTimestampPattern = jsonTimestampPattern; + } } -- Gitee From b82329fadbefa86d9f67897e5e8abe0e43c8b089 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 18 Jul 2020 16:21:19 +0800 Subject: [PATCH 0403/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9AJbootModel?= =?UTF-8?q?.useFirst()=20=E6=96=B9=E6=B3=95=EF=BC=8C=E6=9B=B4=E5=8A=A0?= =?UTF-8?q?=E6=96=B9=E4=BE=BF=E5=9C=A8=E8=AF=BB=E5=86=99=E5=88=86=E7=A6=BB?= =?UTF-8?q?=E7=9A=84=E5=9C=BA=E6=99=AF=E4=B8=8B=E8=BF=9B=E8=A1=8C=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/db/model/JbootModel.java | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 0318daec..a4867f67 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -144,20 +144,13 @@ public class JbootModel> extends Model { } - public M useFirst(String configName) { - M m = use(configName, false); - return m != null ? m : (M) this; - } - - - public M useFirst(String configName, String... configNames) { - M newDao = use(configName, false); - if (newDao != null) { - return newDao; + public M useFirst(String... configNames) { + if (configNames == null || configNames.length == 0) { + throw new IllegalArgumentException("configNames must not empty."); } for (String name : configNames) { - newDao = use(name, false); + M newDao = use(name, false); if (newDao != null) { return newDao; } @@ -209,11 +202,6 @@ public class JbootModel> extends Model { set(column_created, new Date()); } -// boolean needInitPrimaryKey = (String.class == _getPrimaryType() && null == get(_getPrimaryKey())); -// -// if (needInitPrimaryKey) { -// set(_getPrimaryKey(), generatePrimaryValue()); -// } // 生成主键,只对单一主键的表生成,如果是多主键,不生成。 String[] pkeys = _getPrimaryKeys(); -- Gitee From ae41fe0ca58cf26cb89d42d417cb9d8bbd56b764 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 21 Jul 2020 10:41:03 +0800 Subject: [PATCH 0404/1965] add JbootServiceBase.findListByIds() methods --- .../io/jboot/service/JbootServiceBase.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/main/java/io/jboot/service/JbootServiceBase.java b/src/main/java/io/jboot/service/JbootServiceBase.java index 91e1e610..2e5b6618 100644 --- a/src/main/java/io/jboot/service/JbootServiceBase.java +++ b/src/main/java/io/jboot/service/JbootServiceBase.java @@ -25,6 +25,7 @@ import io.jboot.utils.ClassUtil; import io.jboot.utils.ObjectFunc; import io.jboot.utils.ObjectUtil; +import java.util.ArrayList; import java.util.List; /** @@ -157,6 +158,58 @@ public class JbootServiceBase> } + public List findListByIds(Object... ids) { + if (ids == null || ids.length == 0) { + return null; + } + + List list = new ArrayList<>(); + for (Object id : ids) { + if (id.getClass() == int[].class) { + findListByIds(list, (int[]) id); + } else if (id.getClass() == long[].class) { + findListByIds(list, (long[]) id); + } else if (id.getClass() == short[].class) { + findListByIds(list, (short[]) id); + } else { + M model = findById(id); + if (model != null) { + list.add(model); + } + } + } + return list; + } + + private void findListByIds(List list, int[] ids) { + for (int id : ids) { + M model = findById(id); + if (model != null) { + list.add(model); + } + } + } + + private void findListByIds(List list, long[] ids) { + for (long id : ids) { + M model = findById(id); + if (model != null) { + list.add(model); + } + } + } + + + private void findListByIds(List list, short[] ids) { + for (short id : ids) { + M model = findById(id); + if (model != null) { + list.add(model); + } + } + } + + /** * 根据提交查询数据量 * -- Gitee From 784282898152bbe56842219dc19a45bbe05f7032 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 21 Jul 2020 10:43:05 +0800 Subject: [PATCH 0405/1965] optimize ... --- src/main/java/io/jboot/db/model/Util.java | 12 ++++++------ src/test/java/io/jboot/test/json/JsonTester.java | 12 ++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Util.java b/src/main/java/io/jboot/db/model/Util.java index 1c3aecfd..665dafa3 100644 --- a/src/main/java/io/jboot/db/model/Util.java +++ b/src/main/java/io/jboot/db/model/Util.java @@ -65,21 +65,21 @@ class Util { } - private static void addAll(List paras, int[] intArray) { - for (int i : intArray) { + private static void addAll(List paras, int[] ints) { + for (int i : ints) { paras.add(i); } } - private static void addAll(List paras, long[] intArray) { - for (long i : intArray) { + private static void addAll(List paras, long[] longs) { + for (long i : longs) { paras.add(i); } } - private static void addAll(List paras, short[] intArray) { - for (short i : intArray) { + private static void addAll(List paras, short[] shorts) { + for (short i : shorts) { paras.add(i); } } diff --git a/src/test/java/io/jboot/test/json/JsonTester.java b/src/test/java/io/jboot/test/json/JsonTester.java index a1279b66..7aff0f12 100644 --- a/src/test/java/io/jboot/test/json/JsonTester.java +++ b/src/test/java/io/jboot/test/json/JsonTester.java @@ -3,6 +3,9 @@ package io.jboot.test.json; import io.jboot.test.db.model.User; import io.jboot.web.JbootJson; +import java.util.HashMap; +import java.util.Map; + /** * @author michael yang (fuhai999@gmail.com) */ @@ -17,5 +20,14 @@ public class JsonTester { user.put("myAbcDef",new User()); System.out.println(new JbootJson().toJson(user)); + + + Map map = new HashMap(); + map.put("zhangsan",100); + map.put("lisi",200); + map.put("wangyu",300); + map.put("zhao_liu",300); + + System.out.println(new JbootJson().toJson(map)); } } -- Gitee From 7e0ceb209588ef0f39bdeda57a129edbee33a0c6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 21 Jul 2020 11:08:51 +0800 Subject: [PATCH 0406/1965] fixed: DAO.findListByIds() error if id type is String --- .../java/io/jboot/db/model/JbootModel.java | 49 +++++++++++++++- .../io/jboot/service/JbootServiceBase.java | 56 +++---------------- 2 files changed, 55 insertions(+), 50 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index a4867f67..50cda159 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -458,7 +458,54 @@ public class JbootModel> extends Model { public List findListByIds(Object... ids) { - return findListByColumns(Columns.create().in(_getPrimaryKey(), ids)); + if (ids == null || ids.length == 0) { + return null; + } + + List list = new ArrayList<>(); + for (Object id : ids) { + if (id.getClass() == int[].class) { + findListByIds(list, (int[]) id); + } else if (id.getClass() == long[].class) { + findListByIds(list, (long[]) id); + } else if (id.getClass() == short[].class) { + findListByIds(list, (short[]) id); + } else { + M model = findById(id); + if (model != null) { + list.add(model); + } + } + } + return list; + } + + private void findListByIds(List list, int[] ids) { + for (int id : ids) { + M model = findById(id); + if (model != null) { + list.add(model); + } + } + } + + private void findListByIds(List list, long[] ids) { + for (long id : ids) { + M model = findById(id); + if (model != null) { + list.add(model); + } + } + } + + + private void findListByIds(List list, short[] ids) { + for (short id : ids) { + M model = findById(id); + if (model != null) { + list.add(model); + } + } } diff --git a/src/main/java/io/jboot/service/JbootServiceBase.java b/src/main/java/io/jboot/service/JbootServiceBase.java index 2e5b6618..efad93b3 100644 --- a/src/main/java/io/jboot/service/JbootServiceBase.java +++ b/src/main/java/io/jboot/service/JbootServiceBase.java @@ -25,7 +25,6 @@ import io.jboot.utils.ClassUtil; import io.jboot.utils.ObjectFunc; import io.jboot.utils.ObjectUtil; -import java.util.ArrayList; import java.util.List; /** @@ -158,55 +157,14 @@ public class JbootServiceBase> } + /** + * 根据多个 id 查找多个对象 + * + * @param ids + * @return + */ public List findListByIds(Object... ids) { - if (ids == null || ids.length == 0) { - return null; - } - - List list = new ArrayList<>(); - for (Object id : ids) { - if (id.getClass() == int[].class) { - findListByIds(list, (int[]) id); - } else if (id.getClass() == long[].class) { - findListByIds(list, (long[]) id); - } else if (id.getClass() == short[].class) { - findListByIds(list, (short[]) id); - } else { - M model = findById(id); - if (model != null) { - list.add(model); - } - } - } - return list; - } - - private void findListByIds(List list, int[] ids) { - for (int id : ids) { - M model = findById(id); - if (model != null) { - list.add(model); - } - } - } - - private void findListByIds(List list, long[] ids) { - for (long id : ids) { - M model = findById(id); - if (model != null) { - list.add(model); - } - } - } - - - private void findListByIds(List list, short[] ids) { - for (short id : ids) { - M model = findById(id); - if (model != null) { - list.add(model); - } - } + return DAO.findListByIds(ids); } -- Gitee From 902e1317fc3ab488480ee564fb125e2c127bae0d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 21 Jul 2020 15:44:44 +0800 Subject: [PATCH 0407/1965] optimize... --- .../io/jboot/app/config/JbootConfigManager.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 23f1c418..6240643d 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -185,13 +185,13 @@ public class JbootConfigManager { * @param * @return */ - public T createConfigObject(Class clazz, String prefix, String file){ + public T createConfigObject(Class clazz, String prefix, String file) { Object configObject = ConfigUtil.newInstance(clazz); - List setMethods = ConfigUtil.getClassSetMethods(clazz); - if (setMethods != null) { - for (Method method : setMethods) { + List setterMethods = ConfigUtil.getClassSetMethods(clazz); + if (setterMethods != null) { + for (Method setterMethod : setterMethods) { - String key = buildKey(prefix, method); + String key = buildKey(prefix, setterMethod); String value = getConfigValue(key); if (ConfigUtil.isNotBlank(file)) { @@ -203,10 +203,10 @@ public class JbootConfigManager { } if (ConfigUtil.isNotBlank(value)) { - Object val = ConfigUtil.convert(method.getParameterTypes()[0], value, method.getGenericParameterTypes()[0]); + Object val = ConfigUtil.convert(setterMethod.getParameterTypes()[0], value, setterMethod.getGenericParameterTypes()[0]); if (val != null) { try { - method.invoke(configObject, val); + setterMethod.invoke(configObject, val); } catch (Exception e) { e.printStackTrace(); } @@ -485,7 +485,7 @@ public class JbootConfigManager { synchronized (this) { if (devMode == null) { String appMode = getConfigValue("jboot.app.mode"); - devMode = (null == appMode || "".equals(appMode.trim()) || "dev".equals(appMode)); + devMode = (null == appMode || "".equals(appMode.trim()) || "dev".equalsIgnoreCase(appMode.trim())); } } } -- Gitee From 91c62e7855d5b85afe9b10eada181d22e2d3c83a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 21 Jul 2020 16:57:17 +0800 Subject: [PATCH 0408/1965] restructure JbootConfigChangeListener --- .../app/config/JbootConfigChangeListener.java | 4 +- .../jboot/app/config/JbootConfigManager.java | 76 ++++++++++--------- .../support/apollo/ApolloConfigManager.java | 6 +- .../support/nacos/NacosConfigIniter.java | 2 +- .../support/nacos/NacosConfigManager.java | 21 ++--- 5 files changed, 53 insertions(+), 56 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigChangeListener.java b/src/main/java/io/jboot/app/config/JbootConfigChangeListener.java index 6ee403a4..45ac4201 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigChangeListener.java +++ b/src/main/java/io/jboot/app/config/JbootConfigChangeListener.java @@ -19,8 +19,8 @@ package io.jboot.app.config; * @author michael yang (fuhai999@gmail.com) * @Date: 2020/2/8 */ -public interface JbootConfigChangeListener { +public interface JbootConfigChangeListener { - public void onChange(T newObj,T oldObj); + public void onChange(String changeKey, String newValue, String oldValue); } diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 6240643d..d8bc379c 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -15,6 +15,8 @@ */ package io.jboot.app.config; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; import com.jfinal.kit.LogKit; import io.jboot.app.config.annotation.ConfigModel; import io.jboot.app.config.support.apollo.ApolloConfigManager; @@ -42,8 +44,10 @@ public class JbootConfigManager { private Map configCache = new ConcurrentHashMap<>(); - private Map changeListenerMap = new ConcurrentHashMap<>(); - private Map listenerClassMapping = new ConcurrentHashMap<>(); +// private Map changeListenerMap = new ConcurrentHashMap<>(); +// private Map listenerClassMapping = new ConcurrentHashMap<>(); + + private Multimap changeListenerMap = ArrayListMultimap.create(); //配置内容解密工具 private JbootConfigDecryptor decryptor; @@ -370,64 +374,68 @@ public class JbootConfigManager { remoteProperties.putAll(map); } - public void addConfigChangeListener(JbootConfigChangeListener listener, Class forClass) { - ConfigModel configModel = forClass.getAnnotation(ConfigModel.class); + + public void addConfigChangeListener(JbootConfigChangeListener listener, Class forClass) { + ConfigModel configModel = (ConfigModel) forClass.getAnnotation(ConfigModel.class); if (configModel == null) { throw new IllegalArgumentException("forClass:" + forClass + " has no @ConfigModel annotation"); } - listenerClassMapping.put(listener, forClass); - String prefix = configModel.prefix(); - List setMethods = ConfigUtil.getClassSetMethods(forClass); - if (setMethods != null) { - for (Method method : setMethods) { - String key = buildKey(prefix, method); + List setterMethods = ConfigUtil.getClassSetMethods(forClass); + if (setterMethods != null) { + for (Method setterMethod : setterMethods) { + String key = buildKey(prefix, setterMethod); changeListenerMap.put(key, listener); } } } - public void removeConfigChangeListener(JbootConfigChangeListener listener) { - listenerClassMapping.remove(listener); - for (Iterator> it = changeListenerMap.entrySet().iterator(); it.hasNext(); ) { - Map.Entry item = it.next(); - if (item.getValue().equals(listener)) { - it.remove(); - } + public void addConfigChangeListener(JbootConfigChangeListener listener, String... forKeys) { + if (listener == null) { + throw new NullPointerException("listener must not null."); } - } - public void notifyChangeListeners(Set changedKeys) { - if (changedKeys == null || changedKeys.isEmpty()) { - return; + if (forKeys == null || forKeys.length == 0) { + throw new NullPointerException("forKeys must not null or empty."); + } + + for (String forKey : forKeys) { + changeListenerMap.put(forKey, listener); } + } - List listeners = new ArrayList<>(); - for (String key : changedKeys) { - JbootConfigChangeListener listener = changeListenerMap.get(key); - if (listener != null && !listeners.contains(listener)) { - listeners.add(listener); + public void removeConfigChangeListener(JbootConfigChangeListener listener) { + for (String key : changeListenerMap.keySet()) { + Iterator iterator = changeListenerMap.get(key).iterator(); + while (iterator.hasNext()) { + JbootConfigChangeListener entry = iterator.next(); + if (listener == entry) { + iterator.remove(); + } } } + } - for (JbootConfigChangeListener listener : listeners) { - Class forClass = listenerClassMapping.get(listener); - ConfigModel configModel = forClass.getAnnotation(ConfigModel.class); + public void notifyChangeListeners(String key, String newValue, String oldValue) { + if (key == null) { + return; + } - Object oldObj = configCache.get(forClass.getName() + configModel.prefix()); - Object newObj = createConfigObject(forClass, configModel.prefix(), null); + Collection listeners = changeListenerMap.get(key); + if (listeners == null || listeners.isEmpty()) { + return; + } + for (JbootConfigChangeListener listener : listeners) { try { - listener.onChange(newObj, oldObj); + listener.onChange(key, newValue, oldValue); } catch (Throwable ex) { LogKit.error(ex.toString(), ex); - } finally { - configCache.put(forClass.getName() + configModel.prefix(), newObj); } } } diff --git a/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java b/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java index a4c12f06..891a25b4 100644 --- a/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java @@ -39,7 +39,7 @@ public class ApolloConfigManager { public void init(JbootConfigManager configManager) { ApolloServerConfig apolloServerConfig = configManager.get(ApolloServerConfig.class); - if (!apolloServerConfig.isEnable() || !apolloServerConfig.isConfigOk()){ + if (!apolloServerConfig.isEnable() || !apolloServerConfig.isConfigOk()) { return; } @@ -57,9 +57,9 @@ public class ApolloConfigManager { for (String key : changeEvent.changedKeys()) { ConfigChange change = changeEvent.getChange(key); configManager.setRemoteProperty(change.getPropertyName(), change.getNewValue()); - } - configManager.notifyChangeListeners(changeEvent.changedKeys()); + configManager.notifyChangeListeners(change.getPropertyName(), change.getNewValue(), change.getOldValue()); + } }); } diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java index e996e70d..09a5415b 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java @@ -47,7 +47,7 @@ public class NacosConfigIniter { @Override public void receiveConfigInfo(String configInfo) { - manager.doReceiveConfigInfo(configManager, configInfo); + manager.onReceiveConfigInfo(configManager, configInfo); } }); } catch (Exception e) { diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java index 26074234..4e3f3675 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java @@ -22,10 +22,8 @@ import io.jboot.utils.StrUtil; import java.io.IOException; import java.io.StringReader; -import java.util.HashSet; import java.util.Objects; import java.util.Properties; -import java.util.Set; /** * @author michael yang (fuhai999@gmail.com) @@ -74,37 +72,28 @@ public class NacosConfigManager { /** - * 接收的 nacos 服务器消息 - * + * 接收到 nacos 服务器消息 + * @param configManager * @param configInfo */ - public void doReceiveConfigInfo(JbootConfigManager configManager, String configInfo) { + public void onReceiveConfigInfo(JbootConfigManager configManager, String configInfo) { Properties properties = str2Properties(configInfo); - Set changedKeys = new HashSet<>(); if (contentProperties == null) { contentProperties = properties; - for (Object key : properties.keySet()) { - changedKeys.add(key.toString()); - } - configManager.setRemoteProperties(properties); - configManager.notifyChangeListeners(changedKeys); - } else { - for (Object key : properties.keySet()) { String newValue = properties.getProperty(key.toString()); String oldValue = contentProperties.getProperty(key.toString()); if (!Objects.equals(newValue, oldValue)) { - changedKeys.add(key.toString()); contentProperties.put(key, newValue); configManager.setRemoteProperty(key.toString(), newValue); + + configManager.notifyChangeListeners(key.toString(),newValue,oldValue); } } - - configManager.notifyChangeListeners(changedKeys); } } -- Gitee From 66cccdd554ea25a512a6b308b5fdcc0ed3268700 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 21 Jul 2020 17:00:28 +0800 Subject: [PATCH 0409/1965] restructure JbootConfigChangeListener --- .../jboot/app/config/JbootConfigManager.java | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index d8bc379c..5d9e37a7 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -40,14 +40,12 @@ public class JbootConfigManager { //分布式配置 private Map remoteProperties; -// private List readers; + //ConfigObje 缓存 class + prefix : object private Map configCache = new ConcurrentHashMap<>(); -// private Map changeListenerMap = new ConcurrentHashMap<>(); -// private Map listenerClassMapping = new ConcurrentHashMap<>(); - - private Multimap changeListenerMap = ArrayListMultimap.create(); + //监听器 + private Multimap listenersMultimap = ArrayListMultimap.create(); //配置内容解密工具 private JbootConfigDecryptor decryptor; @@ -386,7 +384,7 @@ public class JbootConfigManager { if (setterMethods != null) { for (Method setterMethod : setterMethods) { String key = buildKey(prefix, setterMethod); - changeListenerMap.put(key, listener); + listenersMultimap.put(key, listener); } } } @@ -403,14 +401,14 @@ public class JbootConfigManager { } for (String forKey : forKeys) { - changeListenerMap.put(forKey, listener); + listenersMultimap.put(forKey, listener); } } public void removeConfigChangeListener(JbootConfigChangeListener listener) { - for (String key : changeListenerMap.keySet()) { - Iterator iterator = changeListenerMap.get(key).iterator(); + for (String key : listenersMultimap.keySet()) { + Iterator iterator = listenersMultimap.get(key).iterator(); while (iterator.hasNext()) { JbootConfigChangeListener entry = iterator.next(); if (listener == entry) { @@ -426,7 +424,7 @@ public class JbootConfigManager { return; } - Collection listeners = changeListenerMap.get(key); + Collection listeners = listenersMultimap.get(key); if (listeners == null || listeners.isEmpty()) { return; } -- Gitee From f3e388a0d357a64371ea39305009fa484d3f0f4b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 21 Jul 2020 17:12:05 +0800 Subject: [PATCH 0410/1965] restructure JbootConfigChangeListener --- .../java/io/jboot/app/config/JbootConfigManager.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 5d9e37a7..5a02c693 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -407,15 +407,7 @@ public class JbootConfigManager { public void removeConfigChangeListener(JbootConfigChangeListener listener) { - for (String key : listenersMultimap.keySet()) { - Iterator iterator = listenersMultimap.get(key).iterator(); - while (iterator.hasNext()) { - JbootConfigChangeListener entry = iterator.next(); - if (listener == entry) { - iterator.remove(); - } - } - } + listenersMultimap.entries().removeIf(entry -> entry.getValue() == listener); } -- Gitee From 0ce407138ec3b593ecaa91d43df703e56749e9a3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 22 Jul 2020 11:47:35 +0800 Subject: [PATCH 0411/1965] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9ACookieUtil?= =?UTF-8?q?=20=E5=BD=93=E8=AE=BE=E7=BD=AE=E7=9A=84=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E4=B8=BA=200=20=E6=88=96=E8=80=85=20-1=20=E7=9A=84=E6=97=B6?= =?UTF-8?q?=E5=80=99=EF=BC=8C=E6=97=B6=E9=97=B4=E9=AA=8C=E8=AF=81=E4=B8=8D?= =?UTF-8?q?=E6=AD=A3=E7=A1=AE=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/utils/CookieUtil.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/utils/CookieUtil.java b/src/main/java/io/jboot/utils/CookieUtil.java index e1c349da..c4b58fea 100644 --- a/src/main/java/io/jboot/utils/CookieUtil.java +++ b/src/main/java/io/jboot/utils/CookieUtil.java @@ -168,16 +168,19 @@ public class CookieUtil { return null; } - long stime = Long.parseLong(saveTime); - long maxtime = Long.parseLong(maxAgeInSeconds) * 1000; + long maxTimeMillis = Long.parseLong(maxAgeInSeconds) * 1000; + //可能设置的时间为 0 或者 -1 + if (maxTimeMillis <= 0) { + return value; + } + + long saveTimeMillis = Long.parseLong(saveTime); // 查看是否过时 - if ((stime + maxtime) - System.currentTimeMillis() > 0) { + if ((saveTimeMillis + maxTimeMillis) - System.currentTimeMillis() > 0) { return value; } - /** - * 已经超时了 - */ + //已经超时了 else { return null; } -- Gitee From dba769c6dbdd6448a4145cafaacf008e61a65af1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 22 Jul 2020 18:20:00 +0800 Subject: [PATCH 0412/1965] =?UTF-8?q?JbootServiceBase.syncModels=20?= =?UTF-8?q?=E7=94=B1=E4=BC=A0=E5=85=A5=20List=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=20Collection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/service/JbootServiceBase.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/service/JbootServiceBase.java b/src/main/java/io/jboot/service/JbootServiceBase.java index efad93b3..6557af2b 100644 --- a/src/main/java/io/jboot/service/JbootServiceBase.java +++ b/src/main/java/io/jboot/service/JbootServiceBase.java @@ -25,6 +25,8 @@ import io.jboot.utils.ClassUtil; import io.jboot.utils.ObjectFunc; import io.jboot.utils.ObjectUtil; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; /** @@ -317,7 +319,7 @@ public class JbootServiceBase> * @param syncModels * @param compareAttrGetters */ - public void syncModels(Columns columns, List syncModels, ObjectFunc... compareAttrGetters) { + public void syncModels(Columns columns, Collection syncModels, ObjectFunc... compareAttrGetters) { if (columns == null) { throw new NullPointerException("columns must not be null"); } @@ -329,7 +331,7 @@ public class JbootServiceBase> List existModels = findListByColumns(columns); if (existModels == null || existModels.isEmpty()) { - Db.batchSave(syncModels, syncModels.size()); + Db.batchSave(new ArrayList<>(syncModels), syncModels.size()); return; } -- Gitee From 84b118a498e2042767db926ddca5d06e98065359 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 23 Jul 2020 11:08:07 +0800 Subject: [PATCH 0413/1965] upgrade seata version to 1.3.0 --- pom.xml | 2 +- .../support/seata/SeataGlobalTransactionManager.java | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 87457b58..ef2b8828 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 1.7.2 2.7.7 3.12.1.Final - 1.2.0 + 1.3.0 1.1.8 4.1.9 diff --git a/src/main/java/io/jboot/support/seata/SeataGlobalTransactionManager.java b/src/main/java/io/jboot/support/seata/SeataGlobalTransactionManager.java index 7cea3177..086ce405 100644 --- a/src/main/java/io/jboot/support/seata/SeataGlobalTransactionManager.java +++ b/src/main/java/io/jboot/support/seata/SeataGlobalTransactionManager.java @@ -16,17 +16,17 @@ package io.jboot.support.seata; import com.jfinal.log.Log; - import io.seata.common.util.StringUtils; import io.seata.config.ConfigurationFactory; -import io.seata.core.rpc.netty.RmRpcClient; -import io.seata.core.rpc.netty.ShutdownHook; -import io.seata.core.rpc.netty.TmRpcClient; +import io.seata.core.rpc.ShutdownHook; +import io.seata.core.rpc.netty.RmNettyRemotingClient; +import io.seata.core.rpc.netty.TmNettyRemotingClient; import io.seata.rm.RMClient; import io.seata.tm.TMClient; import io.seata.tm.api.DefaultFailureHandlerImpl; import io.seata.tm.api.FailureHandler; + public class SeataGlobalTransactionManager { @SuppressWarnings("unused") @@ -140,8 +140,8 @@ public class SeataGlobalTransactionManager { private void registerSpringShutdownHook() { ShutdownHook.removeRuntimeShutdownHook(); - ShutdownHook.getInstance().addDisposable(TmRpcClient.getInstance(applicationId, txServiceGroup)); - ShutdownHook.getInstance().addDisposable(RmRpcClient.getInstance(applicationId, txServiceGroup)); + ShutdownHook.getInstance().addDisposable(TmNettyRemotingClient.getInstance(applicationId, txServiceGroup)); + ShutdownHook.getInstance().addDisposable(RmNettyRemotingClient.getInstance(applicationId, txServiceGroup)); } public void destroy() { -- Gitee From 35271c87145d5f25c1409cdfbe19ab83d9482bf3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 23 Jul 2020 11:15:46 +0800 Subject: [PATCH 0414/1965] upgrade nacos apollo clients --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index ef2b8828..772407ba 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ 3.4.5 2.11.1 5.1.49 - 2.1.2.Final + 2.1.3.Final 1.7.30 2.57 1.2.72 @@ -59,7 +59,7 @@ 1.13.1 2.10.6 2.8.5 - 3.10 + 3.11 2.7 1.14 4.5.12 @@ -70,7 +70,7 @@ 3.12.1.Final 1.3.0 1.1.8 - 4.1.9 + 4.1.11 1.8 1.8 @@ -268,14 +268,14 @@ com.ctrip.framework.apollo apollo-client - 1.6.0 + 1.6.2 provided com.alibaba.nacos nacos-client - 1.3.0 + 1.3.1 provided -- Gitee From beba168d54ddcd06e885dfbe4608c2e30fd419ed Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 23 Jul 2020 11:16:46 +0800 Subject: [PATCH 0415/1965] update docs --- doc/docs/config.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/docs/config.md b/doc/docs/config.md index d82155f1..f5f22ec1 100644 --- a/doc/docs/config.md +++ b/doc/docs/config.md @@ -209,7 +209,7 @@ Component1Config config = Jboot.config(Component1Config.class); com.alibaba.nacos nacos-client - 1.3.0 + 1.3.1 ``` @@ -267,7 +267,7 @@ jboot.config.nacos.enableRemoteSyncConfig = xxx com.ctrip.framework.apollo apollo-client - 1.6.0 + 1.6.2 ``` -- Gitee From d57331605f75a248d0c3c780317211eaef972595 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 23 Jul 2020 13:27:59 +0800 Subject: [PATCH 0416/1965] add ClassScanner exclude jar and classes --- src/main/java/io/jboot/utils/ClassScanner.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 34275ebe..d828af5b 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -226,6 +226,7 @@ public class ClassScanner { excludeJars.add("shadow-core-rewrite-"); excludeJars.add("apiguardian-api-"); excludeJars.add("opentest4j-"); + excludeJars.add("freemarker-"); } @@ -305,6 +306,7 @@ public class ClassScanner { excludeClasses.add("io.jboot.app."); excludeClasses.add("io.jboot.service."); excludeClasses.add("io.jboot.Jboot"); + excludeClasses.add("freemarker."); } -- Gitee From 1cb14abd3c1faefb8c3450b7ad937e96f97b2b79 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 23 Jul 2020 17:08:17 +0800 Subject: [PATCH 0417/1965] optimize... --- .../io/jboot/components/rpc/JbootrpcManager.java | 6 +----- src/main/java/io/jboot/db/model/Columns.java | 14 +++++++++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java index d9c1d7eb..57bd05ba 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java @@ -111,11 +111,7 @@ public class JbootrpcManager { continue; } - if (jbootrpc.serviceExport(inter, Aop.get(clazz), new JbootrpcServiceConfig(rpcBean))) { - if (Jboot.isDevMode()) { -// System.out.println("rpc service[" + inter + "] has exported ok!"); - } - } + jbootrpc.serviceExport(inter, Aop.get(clazz), new JbootrpcServiceConfig(rpcBean)); } } } diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 95117782..3389e96d 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -65,9 +65,6 @@ public class Columns implements Serializable { } - public static Columns safeMode() { - return new Columns().useSafeMode(); - } public static Columns create(Column column) { Columns that = new Columns(); @@ -88,6 +85,17 @@ public class Columns implements Serializable { } + public static Columns safeMode() { + return new Columns().useSafeMode(); + } + + + public static Columns safeCreate(String name, Object value) { + return safeMode().eq(name,value); + } + + + /** * add new column in Columns * -- Gitee From 5e30d50491d62eef0daf4d5ed623c7b9e8e61342 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 24 Jul 2020 10:19:50 +0800 Subject: [PATCH 0418/1965] v3.3.0 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 17 +++++++++++++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 457b5c72..06013eaa 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.2.9 + 3.3.0 ``` diff --git a/changes.txt b/changes.txt index 008cc454..3f656ad3 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,20 @@ +jboot v3.3.0: +新增:ObjectUtil 工具类,用于对 Object 进行对比等操作 +新增:JbootModel.useFirst() 方法,更加方便在读写分离的场景下进行使用 +新增:JsonTimestampPattern 配置,方便控制 json 的日期输出 +新增:JbootServiceBase.findListByIds() 方法 +新增:Columns 新增 safeCreate 方法 +优化:重构 JbootConfigChangeListener ,方便监听远程配置的每个值的变化 +优化:JbootServiceBase.syncModels 由传入 List 修改为 Collection +优化:升级 Seata 到最新版本 1.3.0 +优化:升级远程配置 nacos、Apollo 到最新版本 +优化:删除 JbootrpcManager 里的某些无用的逻辑判断 +修复:当有多数据源时,join 会参数数据不正确的问题 +修复:当主键是 String 类型时,DAO.findListByIds() 没有返回数据的问题 +修复:CookieUtil 当设置的时间为 0 或者 -1 的时候,时间验证不正确的问题 + + + jboot v3.2.9: 修复:Columns.in() 对传入 int[] long[] short[] 不能正确支持的问题 diff --git a/doc/docs/install.md b/doc/docs/install.md index e9761aab..a5ded66c 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.2.9 + 3.3.0 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 2c725dc2..b491a715 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.2.9 + 3.3.0 ``` -- Gitee From 602d517251a760a5adbf96c1660eed62abe90a78 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 24 Jul 2020 10:20:32 +0800 Subject: [PATCH 0419/1965] v3.3.0 release (^.^)YYa!! --- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 772407ba..f67bf804 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.0-SNAPSHOT + 3.3.0 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 1c2c881f..66d0be9a 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.2.9"; + public static String VERSION = "3.3.0"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 55564bfd9e6eebfc39263291d89592cd16f77498 Mon Sep 17 00:00:00 2001 From: zxn <840267340@qq.com> Date: Sun, 26 Jul 2020 11:51:08 +0800 Subject: [PATCH 0420/1965] =?UTF-8?q?seata=20tcc=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E6=95=B4=E5=90=88=201=E3=80=81seata=E6=94=B9=E6=88=90=E4=BA=86?= =?UTF-8?q?all=202=E3=80=81=E6=B7=BB=E5=8A=A0tccActionInterceptor=E4=BB=A5?= =?UTF-8?q?=E5=8F=8Ahandler=203=E3=80=81tcc=E6=8B=A6=E6=88=AA=E5=99=A8?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=88=B0=E5=85=A8=E5=B1=80=E6=8B=A6=E6=88=AA?= =?UTF-8?q?=E5=99=A8=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 21 +- .../io/jboot/aop/JbootAopInterceptor.java | 5 +- .../interceptor/ActionInterceptorHandler.java | 187 ++++++++++++++++++ .../interceptor/TccActionInterceptor.java | 98 +++++++++ .../fixedinterceptor/FixedInterceptors.java | 2 + .../seata/account/AccountServiceProvider.java | 29 ++- .../test/seata/account/IAccountService.java | 7 + .../io/jboot/test/seata/commons/Account.java | 2 +- .../io/jboot/test/seata/commons/fescar_.sql | 1 + .../seata/service/TccActionOneService.java | 40 ++++ .../service/impl/TccActionOneServiceImpl.java | 55 ++++++ .../test/seata/starter/WebApplication.java | 22 +++ src/test/resources/file.conf | 2 +- 13 files changed, 464 insertions(+), 7 deletions(-) create mode 100644 src/main/java/io/jboot/support/seata/interceptor/ActionInterceptorHandler.java create mode 100644 src/main/java/io/jboot/support/seata/interceptor/TccActionInterceptor.java create mode 100644 src/test/java/io/jboot/test/seata/service/TccActionOneService.java create mode 100644 src/test/java/io/jboot/test/seata/service/impl/TccActionOneServiceImpl.java diff --git a/pom.xml b/pom.xml index f67bf804..cb801082 100644 --- a/pom.xml +++ b/pom.xml @@ -527,7 +527,7 @@ - + + + io.seata + seata-all + ${seata.version} diff --git a/src/main/java/io/jboot/aop/JbootAopInterceptor.java b/src/main/java/io/jboot/aop/JbootAopInterceptor.java index c7ca328d..aaf9913e 100644 --- a/src/main/java/io/jboot/aop/JbootAopInterceptor.java +++ b/src/main/java/io/jboot/aop/JbootAopInterceptor.java @@ -24,7 +24,9 @@ import io.jboot.components.cache.interceptor.JbootCachesEvictInterceptor; import io.jboot.components.limiter.LimiterInterceptor; import io.jboot.support.metric.JbootMetricInterceptor; import io.jboot.support.seata.interceptor.SeataGlobalTransactionalInterceptor; +import io.jboot.support.seata.interceptor.TccActionInterceptor; import io.jboot.support.sentinel.SentinelInterceptor; +import io.jboot.web.fixedinterceptor.FixedInterceptorWapper; import java.util.LinkedList; import java.util.List; @@ -39,7 +41,8 @@ public class JbootAopInterceptor implements Interceptor { new JbootCachesEvictInterceptor(), new JbootCachePutInterceptor(), new JbootCacheInterceptor(), - new SeataGlobalTransactionalInterceptor() + new SeataGlobalTransactionalInterceptor(), + new TccActionInterceptor() }; diff --git a/src/main/java/io/jboot/support/seata/interceptor/ActionInterceptorHandler.java b/src/main/java/io/jboot/support/seata/interceptor/ActionInterceptorHandler.java new file mode 100644 index 00000000..112c53f3 --- /dev/null +++ b/src/main/java/io/jboot/support/seata/interceptor/ActionInterceptorHandler.java @@ -0,0 +1,187 @@ +package io.jboot.support.seata.interceptor; + +import com.alibaba.fastjson.JSON; +import com.jfinal.aop.Invocation; +import io.seata.common.Constants; +import io.seata.common.exception.FrameworkException; +import io.seata.common.util.NetUtil; +import io.seata.common.util.ReflectionUtil; +import io.seata.core.model.BranchType; +import io.seata.rm.DefaultResourceManager; +import io.seata.rm.tcc.TCCResource; +import io.seata.rm.tcc.api.BusinessActionContext; +import io.seata.rm.tcc.api.TwoPhaseBusinessAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.HashMap; +import java.util.Map; + +/** + * Handler the TCC Participant Aspect : Setting Context, Creating Branch Record + * + * @author zhangsen + */ +public class ActionInterceptorHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(ActionInterceptorHandler.class); + + /** + * Handler the TCC Aspect + * + * @param method the method + * @param arguments the arguments + * @param businessAction the business action + * @return map map + * @throws Throwable the throwable + */ + public void proceed(Method method, Object[] arguments, String xid, TwoPhaseBusinessAction businessAction, + Invocation invocation) { + + //TCC name + String actionName = businessAction.name(); + BusinessActionContext actionContext = new BusinessActionContext(); + actionContext.setXid(xid); + //set action name + actionContext.setActionName(actionName); + Class[] types = method.getParameterTypes(); + Parameter[] parameters = invocation.getMethod().getParameters(); + int argIndex = 0; + for (Class cls : types) { + if (cls.getName().equals(BusinessActionContext.class.getName())) { + arguments[argIndex] = actionContext; + break; + } + argIndex++; + } + //Creating Branch Record + String branchId = doTccActionLogStore(method,parameters, arguments, businessAction, actionContext); + try { + registryResource(method, invocation.getTarget(),types, businessAction); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + actionContext.setBranchId(branchId); + + invocation.invoke(); + } + + /** + * Creating Branch Record + * + * @param method the method + * @param arguments the arguments + * @param businessAction the business action + * @param actionContext the action context + * @return the string + */ + protected String doTccActionLogStore(Method method, Parameter[] parameters , Object[] arguments, TwoPhaseBusinessAction businessAction, + BusinessActionContext actionContext) { + String actionName = actionContext.getActionName(); + String xid = actionContext.getXid(); + // + Map context = fetchActionRequestContext(method, arguments,parameters); + context.put(Constants.ACTION_START_TIME, System.currentTimeMillis()); + + //init business context + initBusinessContext(context, method, businessAction); + //Init running environment context + initFrameworkContext(context); + actionContext.setActionContext(context); + + //init applicationData + Map applicationContext = new HashMap<>(4); + applicationContext.put(Constants.TCC_ACTION_CONTEXT, context); + String applicationContextStr = JSON.toJSONString(applicationContext); + try { + //registry branch record + Long branchId = DefaultResourceManager.get().branchRegister(BranchType.TCC, actionName, null, xid, + applicationContextStr, null); + return String.valueOf(branchId); + } catch (Throwable t) { + String msg = String.format("TCC branch Register error, xid: %s", xid); + LOGGER.error(msg, t); + throw new FrameworkException(t, msg); + } + } + + /** + * Init running environment context + * + * @param context the context + */ + protected void initFrameworkContext(Map context) { + try { + context.put(Constants.HOST_NAME, NetUtil.getLocalIp()); + } catch (Throwable t) { + LOGGER.warn("getLocalIP error", t); + } + } + + /** + * Init business context + * + * @param context the context + * @param method the method + * @param businessAction the business action + */ + protected void initBusinessContext(Map context, Method method, + TwoPhaseBusinessAction businessAction) { + if (method != null) { + //the phase one method name + context.put(Constants.PREPARE_METHOD, method.getName()); + } + if (businessAction != null) { + //the phase two method name + context.put(Constants.COMMIT_METHOD, businessAction.commitMethod()); + context.put(Constants.ROLLBACK_METHOD, businessAction.rollbackMethod()); + context.put(Constants.ACTION_NAME, businessAction.name()); + } + } + + /** + * Extracting context data from parameters, add them to the context + * + * @param method the method + * @param arguments the arguments + * @return map map + */ + protected Map fetchActionRequestContext(Method method, Object[] arguments,Parameter[] parameters) { + Map context = new HashMap<>(8); + int x = 0; + for (Parameter p : parameters) { + if (!p.isNamePresent()) { + // 必须通过添加 -parameters 进行编译,才可以获取 Parameter 的编译前的名字 + throw new RuntimeException(" Maven or IDE config is error. see http://www.jfinal.com/doc/3-3 "); + } + if (!"io.seata.rm.tcc.api.BusinessActionContext".equals(p.getType().getName())) { + context.put(p.getName(), arguments[x]); + } + x++; + } + return context; + } + + public void registryResource(Method m, Object interfaceClass, Class[] arguments, TwoPhaseBusinessAction businessAction) throws NoSuchMethodException { + if (businessAction != null) { + TCCResource tccResource = new TCCResource(); + tccResource.setActionName(businessAction.name()); + tccResource.setTargetBean(interfaceClass); + tccResource.setPrepareMethod(m); + tccResource.setCommitMethodName(businessAction.commitMethod()); + tccResource.setCommitMethod(ReflectionUtil + .getMethod(interfaceClass.getClass(), businessAction.commitMethod(), + new Class[] {BusinessActionContext.class})); + tccResource.setRollbackMethodName(businessAction.rollbackMethod()); + tccResource.setRollbackMethod(ReflectionUtil + .getMethod(interfaceClass.getClass(), businessAction.rollbackMethod(), + new Class[] {BusinessActionContext.class})); + //registry tcc resource + DefaultResourceManager.get().registerResource(tccResource); + } + } + + +} diff --git a/src/main/java/io/jboot/support/seata/interceptor/TccActionInterceptor.java b/src/main/java/io/jboot/support/seata/interceptor/TccActionInterceptor.java new file mode 100644 index 00000000..82d7e7e4 --- /dev/null +++ b/src/main/java/io/jboot/support/seata/interceptor/TccActionInterceptor.java @@ -0,0 +1,98 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.support.seata.interceptor; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import io.jboot.web.fixedinterceptor.FixedInterceptor; +import io.seata.common.util.StringUtils; +import io.seata.core.context.RootContext; +import io.seata.core.model.BranchType; +import io.seata.rm.tcc.api.TwoPhaseBusinessAction; +import io.seata.rm.tcc.remoting.RemotingDesc; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; + + +/** + * TCC Interceptor + * + * @author zhangsen + */ +public class TccActionInterceptor implements Interceptor,FixedInterceptor { + + private static final Logger LOGGER = LoggerFactory.getLogger(TccActionInterceptor.class); + + private ActionInterceptorHandler actionInterceptorHandler = new ActionInterceptorHandler(); + + /** + * remoting bean info + */ + protected RemotingDesc remotingDesc; + + /** + * Instantiates a new Tcc action interceptor. + */ + public TccActionInterceptor() { + + } + + /** + * Instantiates a new Tcc action interceptor. + * + * @param remotingDesc the remoting desc + */ + public TccActionInterceptor(RemotingDesc remotingDesc) { + this.remotingDesc = remotingDesc; + } + + @Override + public void intercept(Invocation inv) { + if (!RootContext.inGlobalTransaction()) { + //not in transaction + inv.invoke(); + return ; + } + Method method = inv.getMethod(); + TwoPhaseBusinessAction businessAction = method.getAnnotation(TwoPhaseBusinessAction.class); + //try method + if (businessAction != null) { + //save the xid + String xid = RootContext.getXID(); + //clear the context + String previousBranchType = RootContext.getBranchType(); + RootContext.bindBranchType(BranchType.TCC); + try { + Object[] methodArgs = inv.getArgs(); + //Handler the TCC Aspect + actionInterceptorHandler.proceed(method, methodArgs, xid, businessAction, inv); + return; + } finally { + RootContext.unbindBranchType(); + //restore the TCC branchType if exists + if (StringUtils.equals(BranchType.TCC.name(), previousBranchType)) { + RootContext.bindBranchType(BranchType.TCC); + } + } + } else { + inv.invoke(); + } + return; + } + +} diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java index 3dad2418..ca716ea5 100644 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java +++ b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java @@ -20,6 +20,7 @@ import io.jboot.components.limiter.LimiterInterceptor; import io.jboot.support.jwt.JwtInterceptor; import io.jboot.support.metric.JbootMetricInterceptor; import io.jboot.support.seata.interceptor.SeataGlobalTransactionalInterceptor; +import io.jboot.support.seata.interceptor.TccActionInterceptor; import io.jboot.support.sentinel.SentinelInterceptor; import io.jboot.support.shiro.JbootShiroInterceptor; import io.jboot.web.validate.ParaValidateInterceptor; @@ -56,6 +57,7 @@ public class FixedInterceptors { new FixedInterceptorWapper(new JbootShiroInterceptor(), 50), new FixedInterceptorWapper(new JbootMetricInterceptor(), 60), new FixedInterceptorWapper(new SeataGlobalTransactionalInterceptor(), 80), + new FixedInterceptorWapper(new TccActionInterceptor(), 90), }; private List userInters = new ArrayList<>(); diff --git a/src/test/java/io/jboot/test/seata/account/AccountServiceProvider.java b/src/test/java/io/jboot/test/seata/account/AccountServiceProvider.java index 4f9037c9..78cd793e 100644 --- a/src/test/java/io/jboot/test/seata/account/AccountServiceProvider.java +++ b/src/test/java/io/jboot/test/seata/account/AccountServiceProvider.java @@ -1,24 +1,47 @@ package io.jboot.test.seata.account; +import io.jboot.aop.annotation.Bean; import io.jboot.components.rpc.annotation.RPCBean; import io.jboot.service.JbootServiceBase; import io.jboot.test.seata.commons.Account; @RPCBean +@Bean public class AccountServiceProvider extends JbootServiceBase implements IAccountService { - private static Account dao = new Account().dao(); + private static Account dao = new Account(); public boolean deposit(Integer accountId, Integer money) { Account account = dao.findById(accountId); - account.set("Money", account.getInt("Money") + money); + account.set("money", account.getInt("money") + money); if (money > 1000) { throw new RuntimeException(AccountServiceProvider.class.getSimpleName()+"Dubbo Seata Exception By Hobbit"); } - return account.update(); + return account.saveOrUpdate(); } + @Override + public boolean updateStore(String account, Integer money) { + Account account1 = dao.findFirst("select * from seata_account where account = ? ", account); + account1.set("store", account1.getInt("store") + money); + return account1.saveOrUpdate(); + } + + @Override + public boolean updateRollbackStore(String account, Integer money) { + Account account1 = dao.findFirst("select * from seata_account where account = ? ", account); + account1.set("store", account1.getInt("store") - money); + return account1.saveOrUpdate(); + } + + @Override + public boolean update(String account, Integer money) { + Account account1 = dao.findFirst("select * from seata_account where account = ? ", account); + account1.set("store", account1.getInt("store") - money); + account1.set("money", account1.getInt("money") - money); + return account1.saveOrUpdate(); + } } diff --git a/src/test/java/io/jboot/test/seata/account/IAccountService.java b/src/test/java/io/jboot/test/seata/account/IAccountService.java index 90d5f37e..ec971516 100644 --- a/src/test/java/io/jboot/test/seata/account/IAccountService.java +++ b/src/test/java/io/jboot/test/seata/account/IAccountService.java @@ -1,5 +1,12 @@ package io.jboot.test.seata.account; public interface IAccountService { + public boolean deposit(Integer accountId, Integer money); + + public boolean updateStore(String account, Integer money); + + public boolean updateRollbackStore(String account, Integer money); + + public boolean update(String accountId, Integer money); } diff --git a/src/test/java/io/jboot/test/seata/commons/Account.java b/src/test/java/io/jboot/test/seata/commons/Account.java index 8c26ca42..ee85a877 100644 --- a/src/test/java/io/jboot/test/seata/commons/Account.java +++ b/src/test/java/io/jboot/test/seata/commons/Account.java @@ -5,7 +5,7 @@ import com.jfinal.plugin.activerecord.IBean; import io.jboot.db.annotation.Table; import io.jboot.db.model.JbootModel; -@Table(tableName = "seata_account", primaryKey = "ID") +@Table(tableName = "seata_account", primaryKey = "id") public class Account extends JbootModel implements IBean { /** * diff --git a/src/test/java/io/jboot/test/seata/commons/fescar_.sql b/src/test/java/io/jboot/test/seata/commons/fescar_.sql index 1c69d007..63039bb2 100644 --- a/src/test/java/io/jboot/test/seata/commons/fescar_.sql +++ b/src/test/java/io/jboot/test/seata/commons/fescar_.sql @@ -10,6 +10,7 @@ CREATE TABLE `seata_account` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `Account` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `Money` int(11) NULL DEFAULT NULL, + `store` int(11) NULL DEFAULT NULL, PRIMARY KEY (`ID`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; diff --git a/src/test/java/io/jboot/test/seata/service/TccActionOneService.java b/src/test/java/io/jboot/test/seata/service/TccActionOneService.java new file mode 100644 index 00000000..b73596f0 --- /dev/null +++ b/src/test/java/io/jboot/test/seata/service/TccActionOneService.java @@ -0,0 +1,40 @@ +package io.jboot.test.seata.service; + +import io.seata.rm.tcc.api.BusinessActionContext; +import io.seata.rm.tcc.api.BusinessActionContextParameter; + +/** + * program: seata + * description: ${description} + * author: zxn + * create: 2020-07-25 22:29 + **/ +public interface TccActionOneService { + + /** + * Prepare boolean. + * + * @param actionContext the action context + * @param account + * @return the boolean + */ + + public boolean prepare(BusinessActionContext actionContext, @BusinessActionContextParameter(paramName = "account") String account, @BusinessActionContextParameter(paramName = "money") int money, + @BusinessActionContextParameter(paramName = "flag") boolean flag); + + /** + * Commit boolean. + * + * @param actionContext the action context + * @return the boolean + */ + public boolean commit(BusinessActionContext actionContext); + + /** + * Rollback boolean. + * + * @param actionContext the action context + * @return the boolean + */ + public boolean rollback(BusinessActionContext actionContext); +} diff --git a/src/test/java/io/jboot/test/seata/service/impl/TccActionOneServiceImpl.java b/src/test/java/io/jboot/test/seata/service/impl/TccActionOneServiceImpl.java new file mode 100644 index 00000000..d2f68a6a --- /dev/null +++ b/src/test/java/io/jboot/test/seata/service/impl/TccActionOneServiceImpl.java @@ -0,0 +1,55 @@ +package io.jboot.test.seata.service.impl; + +import com.jfinal.aop.Inject; +import com.jfinal.kit.StrKit; +import io.jboot.aop.annotation.Bean; +import io.jboot.test.seata.account.IAccountService; +import io.jboot.test.seata.service.TccActionOneService; +import io.seata.rm.tcc.api.BusinessActionContext; +import io.seata.rm.tcc.api.BusinessActionContextParameter; +import io.seata.rm.tcc.api.TwoPhaseBusinessAction; + +/** + * program: seata + * description: ${description} + * author: zxn + * create: 2020-07-25 22:39 + **/ +@Bean +public class TccActionOneServiceImpl implements TccActionOneService { + + @Inject + private IAccountService accountService; + + @Override + @TwoPhaseBusinessAction(name = "TccActionOne" , commitMethod = "commit", rollbackMethod = "rollback") + public boolean prepare(BusinessActionContext actionContext, String account,int money, boolean flag) { + System.out.println("actionContext获取Xid prepare>>> "+actionContext.getXid()); + System.out.println("actionContext获取TCC参数 prepare>>> "+actionContext.getActionContext("account")); + accountService.updateStore(account, money); + /* if (flag) { + throw new RuntimeException("you have fail"); + }*/ + return true; + } + + @Override + public boolean commit(BusinessActionContext actionContext) { + System.out.println("actionContext获取TCC参数 commit >>> "+ actionContext.getActionContext("account") + ": " + + actionContext.getActionContext("money")); + String account = (String) actionContext.getActionContext("account"); + int money = (int) actionContext.getActionContext("money"); + accountService.update(account, money); + return true; + } + + @Override + public boolean rollback(BusinessActionContext actionContext) { + System.out.println("actionContext获取TCC参数 rollback >>> "+ actionContext.getActionContext("account") + ": " + + actionContext.getActionContext("money")); + String account = (String) actionContext.getActionContext("account"); + int money = (int) actionContext.getActionContext("money"); + accountService.updateRollbackStore(account, money); + return true; + } +} diff --git a/src/test/java/io/jboot/test/seata/starter/WebApplication.java b/src/test/java/io/jboot/test/seata/starter/WebApplication.java index ac1201df..4132d4e8 100644 --- a/src/test/java/io/jboot/test/seata/starter/WebApplication.java +++ b/src/test/java/io/jboot/test/seata/starter/WebApplication.java @@ -2,7 +2,9 @@ package io.jboot.test.seata.starter; import com.jfinal.aop.Inject; import io.jboot.app.JbootApplication; +import io.jboot.support.seata.annotation.SeataGlobalTransactional; import io.jboot.test.seata.business.BusinessServiceProvider; +import io.jboot.test.seata.service.TccActionOneService; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; @@ -26,6 +28,13 @@ public class WebApplication extends JbootController { JbootApplication.setBootArg("jboot.seata.applicationId", "Dubbo_Seata_Business_Service"); JbootApplication.setBootArg("jboot.seata.txServiceGroup", "dubbo_seata_tx_group"); + JbootApplication.setBootArg("jboot.datasource.type", "mysql"); + JbootApplication.setBootArg("jboot.datasource.url", "jdbc:mysql://127.0.0.1:3306/mini?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull"); + JbootApplication.setBootArg("jboot.datasource.user", "root"); + JbootApplication.setBootArg("jboot.datasource.password", "zhang123"); + JbootApplication.setBootArg("jboot.model.unscanPackage", "*"); + JbootApplication.setBootArg("jboot.model.scanPackage", "io.jboot.test.seata.commons"); + JbootApplication.run(args); } @@ -33,6 +42,9 @@ public class WebApplication extends JbootController { @Inject private BusinessServiceProvider businessServiceProvider; + @Inject + private TccActionOneService tccActionOneService; + public void index() { System.out.println("WebApplication.index()"); @@ -40,4 +52,14 @@ public class WebApplication extends JbootController { businessServiceProvider.deposit(1); renderText("ok"); } + + @SeataGlobalTransactional(timeoutMills = 300000, name = "Seata_Business_Transactional_TccOne") + public void tccOne(){ + tccActionOneService.prepare(null, "Hobbit", 10, getParaToBoolean("flag")); + if (getParaToBoolean("flag")) { + throw new RuntimeException("you have fail"); + } + renderJson("you are sucess"); + } + } diff --git a/src/test/resources/file.conf b/src/test/resources/file.conf index 0660bf81..97512d1d 100644 --- a/src/test/resources/file.conf +++ b/src/test/resources/file.conf @@ -28,7 +28,7 @@ transport { } service { #vgroup->rgroup - vgroup_mapping.dubbo_seata_tx_group = "default" + vgroupMapping.dubbo_seata_tx_group = "default" #only support single node default.grouplist = "127.0.0.1:8091" #degrade current not support -- Gitee From ea45561ece8e8d4918f9006c43235512a59897a5 Mon Sep 17 00:00:00 2001 From: zxn <840267340@qq.com> Date: Sun, 26 Jul 2020 12:08:28 +0800 Subject: [PATCH 0421/1965] =?UTF-8?q?seata=20tcc=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E6=95=B4=E5=90=88=201=E3=80=81seata=E6=94=B9=E6=88=90=E4=BA=86?= =?UTF-8?q?all=202=E3=80=81=E6=B7=BB=E5=8A=A0tccActionInterceptor=E4=BB=A5?= =?UTF-8?q?=E5=8F=8Ahandler=203=E3=80=81tcc=E6=8B=A6=E6=88=AA=E5=99=A8?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=88=B0=E5=85=A8=E5=B1=80=E6=8B=A6=E6=88=AA?= =?UTF-8?q?=E5=99=A8=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 13 +++++++------ src/main/java/io/jboot/aop/JbootAopInterceptor.java | 3 +-- .../seata/interceptor/TccActionInterceptor.java | 2 +- .../web/fixedinterceptor/FixedInterceptors.java | 2 +- .../test/seata/account/AccountServiceProvider.java | 2 +- .../jboot/test/seata/account/IAccountService.java | 1 + .../java/io/jboot/test/seata/commons/Account.java | 1 + .../java/io/jboot/test/seata/commons/fescar_.sql | 2 +- .../test/seata/service/TccActionOneService.java | 2 +- .../seata/service/impl/TccActionOneServiceImpl.java | 4 ++-- .../io/jboot/test/seata/starter/WebApplication.java | 4 ++-- src/test/resources/file.conf | 2 +- 12 files changed, 20 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index cb801082..6a4085aa 100644 --- a/pom.xml +++ b/pom.xml @@ -586,14 +586,10 @@ seata-config-core - --> - - io.seata - seata-all - ${seata.version} + io.seata seata-common @@ -605,8 +601,13 @@ logback-classic - + --> + + io.seata + seata-all + ${seata.version} + net.oschina.j2cache diff --git a/src/main/java/io/jboot/aop/JbootAopInterceptor.java b/src/main/java/io/jboot/aop/JbootAopInterceptor.java index aaf9913e..b1cbb561 100644 --- a/src/main/java/io/jboot/aop/JbootAopInterceptor.java +++ b/src/main/java/io/jboot/aop/JbootAopInterceptor.java @@ -149,6 +149,5 @@ public class JbootAopInterceptor implements Interceptor { } } } - - + } diff --git a/src/main/java/io/jboot/support/seata/interceptor/TccActionInterceptor.java b/src/main/java/io/jboot/support/seata/interceptor/TccActionInterceptor.java index 82d7e7e4..2d35d8b5 100644 --- a/src/main/java/io/jboot/support/seata/interceptor/TccActionInterceptor.java +++ b/src/main/java/io/jboot/support/seata/interceptor/TccActionInterceptor.java @@ -64,7 +64,7 @@ public class TccActionInterceptor implements Interceptor,FixedInterceptor { @Override public void intercept(Invocation inv) { if (!RootContext.inGlobalTransaction()) { - //not in transaction + // not in transaction inv.invoke(); return ; } diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java index ca716ea5..d7fa8021 100644 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java +++ b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java @@ -57,7 +57,7 @@ public class FixedInterceptors { new FixedInterceptorWapper(new JbootShiroInterceptor(), 50), new FixedInterceptorWapper(new JbootMetricInterceptor(), 60), new FixedInterceptorWapper(new SeataGlobalTransactionalInterceptor(), 80), - new FixedInterceptorWapper(new TccActionInterceptor(), 90), + new FixedInterceptorWapper(new TccActionInterceptor(), 90) }; private List userInters = new ArrayList<>(); diff --git a/src/test/java/io/jboot/test/seata/account/AccountServiceProvider.java b/src/test/java/io/jboot/test/seata/account/AccountServiceProvider.java index 78cd793e..720af52e 100644 --- a/src/test/java/io/jboot/test/seata/account/AccountServiceProvider.java +++ b/src/test/java/io/jboot/test/seata/account/AccountServiceProvider.java @@ -17,7 +17,7 @@ public class AccountServiceProvider extends JbootServiceBase implements account.set("money", account.getInt("money") + money); if (money > 1000) { - throw new RuntimeException(AccountServiceProvider.class.getSimpleName()+"Dubbo Seata Exception By Hobbit"); + throw new RuntimeException(AccountServiceProvider.class.getSimpleName()+"Dubbo Seata Exception By Hobbit "); } return account.saveOrUpdate(); diff --git a/src/test/java/io/jboot/test/seata/account/IAccountService.java b/src/test/java/io/jboot/test/seata/account/IAccountService.java index ec971516..656ef2d4 100644 --- a/src/test/java/io/jboot/test/seata/account/IAccountService.java +++ b/src/test/java/io/jboot/test/seata/account/IAccountService.java @@ -9,4 +9,5 @@ public interface IAccountService { public boolean updateRollbackStore(String account, Integer money); public boolean update(String accountId, Integer money); + } diff --git a/src/test/java/io/jboot/test/seata/commons/Account.java b/src/test/java/io/jboot/test/seata/commons/Account.java index ee85a877..0b535ca1 100644 --- a/src/test/java/io/jboot/test/seata/commons/Account.java +++ b/src/test/java/io/jboot/test/seata/commons/Account.java @@ -12,4 +12,5 @@ public class Account extends JbootModel implements IBean { */ private static final long serialVersionUID = 1L; + } diff --git a/src/test/java/io/jboot/test/seata/commons/fescar_.sql b/src/test/java/io/jboot/test/seata/commons/fescar_.sql index 63039bb2..07c6c16e 100644 --- a/src/test/java/io/jboot/test/seata/commons/fescar_.sql +++ b/src/test/java/io/jboot/test/seata/commons/fescar_.sql @@ -10,7 +10,7 @@ CREATE TABLE `seata_account` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `Account` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `Money` int(11) NULL DEFAULT NULL, - `store` int(11) NULL DEFAULT NULL, + `store` int(10) NULL DEFAULT NULL, PRIMARY KEY (`ID`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; diff --git a/src/test/java/io/jboot/test/seata/service/TccActionOneService.java b/src/test/java/io/jboot/test/seata/service/TccActionOneService.java index b73596f0..69be518b 100644 --- a/src/test/java/io/jboot/test/seata/service/TccActionOneService.java +++ b/src/test/java/io/jboot/test/seata/service/TccActionOneService.java @@ -20,7 +20,7 @@ public interface TccActionOneService { */ public boolean prepare(BusinessActionContext actionContext, @BusinessActionContextParameter(paramName = "account") String account, @BusinessActionContextParameter(paramName = "money") int money, - @BusinessActionContextParameter(paramName = "flag") boolean flag); + @BusinessActionContextParameter(paramName = "flag") boolean flag); /** * Commit boolean. diff --git a/src/test/java/io/jboot/test/seata/service/impl/TccActionOneServiceImpl.java b/src/test/java/io/jboot/test/seata/service/impl/TccActionOneServiceImpl.java index d2f68a6a..63ca7c4c 100644 --- a/src/test/java/io/jboot/test/seata/service/impl/TccActionOneServiceImpl.java +++ b/src/test/java/io/jboot/test/seata/service/impl/TccActionOneServiceImpl.java @@ -27,9 +27,9 @@ public class TccActionOneServiceImpl implements TccActionOneService { System.out.println("actionContext获取Xid prepare>>> "+actionContext.getXid()); System.out.println("actionContext获取TCC参数 prepare>>> "+actionContext.getActionContext("account")); accountService.updateStore(account, money); - /* if (flag) { + if (flag) { throw new RuntimeException("you have fail"); - }*/ + } return true; } diff --git a/src/test/java/io/jboot/test/seata/starter/WebApplication.java b/src/test/java/io/jboot/test/seata/starter/WebApplication.java index 4132d4e8..970bfc0a 100644 --- a/src/test/java/io/jboot/test/seata/starter/WebApplication.java +++ b/src/test/java/io/jboot/test/seata/starter/WebApplication.java @@ -56,9 +56,9 @@ public class WebApplication extends JbootController { @SeataGlobalTransactional(timeoutMills = 300000, name = "Seata_Business_Transactional_TccOne") public void tccOne(){ tccActionOneService.prepare(null, "Hobbit", 10, getParaToBoolean("flag")); - if (getParaToBoolean("flag")) { + /*if (getParaToBoolean("flag")) { throw new RuntimeException("you have fail"); - } + }*/ renderJson("you are sucess"); } diff --git a/src/test/resources/file.conf b/src/test/resources/file.conf index 97512d1d..03888e60 100644 --- a/src/test/resources/file.conf +++ b/src/test/resources/file.conf @@ -28,7 +28,7 @@ transport { } service { #vgroup->rgroup - vgroupMapping.dubbo_seata_tx_group = "default" + vgroupMapping.dubbo_seata_tx_group ="default" #only support single node default.grouplist = "127.0.0.1:8091" #degrade current not support -- Gitee From 39ef9ddc3c5a6656e724bb0d775976cd0b7f888b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Jul 2020 17:15:26 +0800 Subject: [PATCH 0422/1965] v3.3.1 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f67bf804..dc1e40c9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.0 + 3.3.1-SNAPSHOT jar jboot -- Gitee From ca95401d1b2d37425c276c1b41a3265a57b7e0e3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Jul 2020 17:23:25 +0800 Subject: [PATCH 0423/1965] upgrade jfinal version to newest --- pom.xml | 2 +- src/main/java/io/jboot/web/handler/JbootActionHandler.java | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index dc1e40c9..59883d0a 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ - 4.9 + 4.9.01 2020.4 2.1 2.6 diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 0a1d9599..ddc4a390 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -105,9 +105,6 @@ public class JbootActionHandler extends ActionHandler { Controller controller = null; try { controller = controllerFactory.getController(action.getControllerClass()); - if (injectDependency) { - com.jfinal.aop.Aop.inject(controller); - } JbootControllerContext.hold(controller); // controller.init(request, response, urlPara[0]); CPI._init_(controller, action, request, response, urlPara[0]); -- Gitee From 3003e5c0fdb9e5dbeba1f818e2622670134b5bf9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Jul 2020 17:42:50 +0800 Subject: [PATCH 0424/1965] add seata tcc support --- .../io/jboot/aop/JbootAopInterceptor.java | 3 +- .../ActionInterceptorHandler.java | 25 ++++++++-- .../TccActionInterceptor.java | 48 +++++++++++-------- .../fixedinterceptor/FixedInterceptors.java | 2 +- 4 files changed, 50 insertions(+), 28 deletions(-) rename src/main/java/io/jboot/support/seata/{interceptor => tcc}/ActionInterceptorHandler.java (87%) rename src/main/java/io/jboot/support/seata/{interceptor => tcc}/TccActionInterceptor.java (65%) diff --git a/src/main/java/io/jboot/aop/JbootAopInterceptor.java b/src/main/java/io/jboot/aop/JbootAopInterceptor.java index b1cbb561..c668ad0f 100644 --- a/src/main/java/io/jboot/aop/JbootAopInterceptor.java +++ b/src/main/java/io/jboot/aop/JbootAopInterceptor.java @@ -24,9 +24,8 @@ import io.jboot.components.cache.interceptor.JbootCachesEvictInterceptor; import io.jboot.components.limiter.LimiterInterceptor; import io.jboot.support.metric.JbootMetricInterceptor; import io.jboot.support.seata.interceptor.SeataGlobalTransactionalInterceptor; -import io.jboot.support.seata.interceptor.TccActionInterceptor; +import io.jboot.support.seata.tcc.TccActionInterceptor; import io.jboot.support.sentinel.SentinelInterceptor; -import io.jboot.web.fixedinterceptor.FixedInterceptorWapper; import java.util.LinkedList; import java.util.List; diff --git a/src/main/java/io/jboot/support/seata/interceptor/ActionInterceptorHandler.java b/src/main/java/io/jboot/support/seata/tcc/ActionInterceptorHandler.java similarity index 87% rename from src/main/java/io/jboot/support/seata/interceptor/ActionInterceptorHandler.java rename to src/main/java/io/jboot/support/seata/tcc/ActionInterceptorHandler.java index 112c53f3..c9fbe5d3 100644 --- a/src/main/java/io/jboot/support/seata/interceptor/ActionInterceptorHandler.java +++ b/src/main/java/io/jboot/support/seata/tcc/ActionInterceptorHandler.java @@ -1,7 +1,23 @@ -package io.jboot.support.seata.interceptor; +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.support.seata.tcc; import com.alibaba.fastjson.JSON; import com.jfinal.aop.Invocation; +import com.jfinal.log.Log; import io.seata.common.Constants; import io.seata.common.exception.FrameworkException; import io.seata.common.util.NetUtil; @@ -11,8 +27,6 @@ import io.seata.rm.DefaultResourceManager; import io.seata.rm.tcc.TCCResource; import io.seata.rm.tcc.api.BusinessActionContext; import io.seata.rm.tcc.api.TwoPhaseBusinessAction; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.lang.reflect.Method; import java.lang.reflect.Parameter; @@ -21,12 +35,13 @@ import java.util.Map; /** * Handler the TCC Participant Aspect : Setting Context, Creating Branch Record + * 参考:https://github.com/seata/seata/blob/master/tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionInterceptorHandler.java * - * @author zhangsen + * @author zhangsen/菜农 commit: https://gitee.com/fuhai/jboot/commit/55564bfd9e6eebfc39263291d89592cd16f77498 */ public class ActionInterceptorHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(ActionInterceptorHandler.class); + private static final Log LOGGER = Log.getLog(TccActionInterceptor.class); /** * Handler the TCC Aspect diff --git a/src/main/java/io/jboot/support/seata/interceptor/TccActionInterceptor.java b/src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java similarity index 65% rename from src/main/java/io/jboot/support/seata/interceptor/TccActionInterceptor.java rename to src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java index 2d35d8b5..be851a28 100644 --- a/src/main/java/io/jboot/support/seata/interceptor/TccActionInterceptor.java +++ b/src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java @@ -1,42 +1,44 @@ -/* - * Copyright 1999-2019 Seata.io Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ -package io.jboot.support.seata.interceptor; +package io.jboot.support.seata.tcc; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; +import com.jfinal.log.Log; +import io.jboot.support.seata.JbootSeataManager; import io.jboot.web.fixedinterceptor.FixedInterceptor; import io.seata.common.util.StringUtils; import io.seata.core.context.RootContext; import io.seata.core.model.BranchType; import io.seata.rm.tcc.api.TwoPhaseBusinessAction; import io.seata.rm.tcc.remoting.RemotingDesc; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.lang.reflect.Method; /** * TCC Interceptor + *

+ * 参考: https://github.com/seata/seata/blob/develop/spring/src/main/java/io/seata/spring/tcc/TccActionInterceptor.java * * @author zhangsen */ -public class TccActionInterceptor implements Interceptor,FixedInterceptor { +public class TccActionInterceptor implements Interceptor, FixedInterceptor { - private static final Logger LOGGER = LoggerFactory.getLogger(TccActionInterceptor.class); + private static final Log LOGGER = Log.getLog(TccActionInterceptor.class); private ActionInterceptorHandler actionInterceptorHandler = new ActionInterceptorHandler(); @@ -63,10 +65,16 @@ public class TccActionInterceptor implements Interceptor,FixedInterceptor { @Override public void intercept(Invocation inv) { + + if (!JbootSeataManager.me().isEnable()) { + inv.invoke(); + return; + } + if (!RootContext.inGlobalTransaction()) { // not in transaction inv.invoke(); - return ; + return; } Method method = inv.getMethod(); TwoPhaseBusinessAction businessAction = method.getAnnotation(TwoPhaseBusinessAction.class); diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java index d7fa8021..dad82067 100644 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java +++ b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java @@ -20,7 +20,7 @@ import io.jboot.components.limiter.LimiterInterceptor; import io.jboot.support.jwt.JwtInterceptor; import io.jboot.support.metric.JbootMetricInterceptor; import io.jboot.support.seata.interceptor.SeataGlobalTransactionalInterceptor; -import io.jboot.support.seata.interceptor.TccActionInterceptor; +import io.jboot.support.seata.tcc.TccActionInterceptor; import io.jboot.support.sentinel.SentinelInterceptor; import io.jboot.support.shiro.JbootShiroInterceptor; import io.jboot.web.validate.ParaValidateInterceptor; -- Gitee From 0f693b625c298b2be21360bf8e54cdd3fd237634 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 28 Jul 2020 15:27:08 +0800 Subject: [PATCH 0425/1965] upgrade apache dubbo and add spring exclusions from seata --- pom.xml | 116 ++++++++++++++++---------------------------------------- 1 file changed, 33 insertions(+), 83 deletions(-) diff --git a/pom.xml b/pom.xml index acb83479..309b6330 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,7 @@ 4.4.13 4.1.50.Final 1.7.2 - 2.7.7 + 2.7.8 3.12.1.Final 1.3.0 1.1.8 @@ -526,87 +526,37 @@ - - - - - io.seata - seata-all - ${seata.version} @@ -636,19 +586,19 @@ provided - - - - - - + + + + + + - - - - - - + + + + + + -- Gitee From 04371ad0815c27b102d2535ccbf7f9747f2f84b5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 28 Jul 2020 16:03:28 +0800 Subject: [PATCH 0426/1965] add seata tcc support --- .../seata/tcc/TccActionInterceptor.java | 63 +--------------- .../support/seata/tcc/TccActionProcesser.java | 73 +++++++++++++++++++ 2 files changed, 74 insertions(+), 62 deletions(-) create mode 100644 src/main/java/io/jboot/support/seata/tcc/TccActionProcesser.java diff --git a/src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java b/src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java index be851a28..9eacde50 100644 --- a/src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java +++ b/src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java @@ -17,16 +17,8 @@ package io.jboot.support.seata.tcc; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; -import com.jfinal.log.Log; import io.jboot.support.seata.JbootSeataManager; import io.jboot.web.fixedinterceptor.FixedInterceptor; -import io.seata.common.util.StringUtils; -import io.seata.core.context.RootContext; -import io.seata.core.model.BranchType; -import io.seata.rm.tcc.api.TwoPhaseBusinessAction; -import io.seata.rm.tcc.remoting.RemotingDesc; - -import java.lang.reflect.Method; /** @@ -38,30 +30,6 @@ import java.lang.reflect.Method; */ public class TccActionInterceptor implements Interceptor, FixedInterceptor { - private static final Log LOGGER = Log.getLog(TccActionInterceptor.class); - - private ActionInterceptorHandler actionInterceptorHandler = new ActionInterceptorHandler(); - - /** - * remoting bean info - */ - protected RemotingDesc remotingDesc; - - /** - * Instantiates a new Tcc action interceptor. - */ - public TccActionInterceptor() { - - } - - /** - * Instantiates a new Tcc action interceptor. - * - * @param remotingDesc the remoting desc - */ - public TccActionInterceptor(RemotingDesc remotingDesc) { - this.remotingDesc = remotingDesc; - } @Override public void intercept(Invocation inv) { @@ -71,36 +39,7 @@ public class TccActionInterceptor implements Interceptor, FixedInterceptor { return; } - if (!RootContext.inGlobalTransaction()) { - // not in transaction - inv.invoke(); - return; - } - Method method = inv.getMethod(); - TwoPhaseBusinessAction businessAction = method.getAnnotation(TwoPhaseBusinessAction.class); - //try method - if (businessAction != null) { - //save the xid - String xid = RootContext.getXID(); - //clear the context - String previousBranchType = RootContext.getBranchType(); - RootContext.bindBranchType(BranchType.TCC); - try { - Object[] methodArgs = inv.getArgs(); - //Handler the TCC Aspect - actionInterceptorHandler.proceed(method, methodArgs, xid, businessAction, inv); - return; - } finally { - RootContext.unbindBranchType(); - //restore the TCC branchType if exists - if (StringUtils.equals(BranchType.TCC.name(), previousBranchType)) { - RootContext.bindBranchType(BranchType.TCC); - } - } - } else { - inv.invoke(); - } - return; + new TccActionProcesser().intercept(inv); } } diff --git a/src/main/java/io/jboot/support/seata/tcc/TccActionProcesser.java b/src/main/java/io/jboot/support/seata/tcc/TccActionProcesser.java new file mode 100644 index 00000000..36b480df --- /dev/null +++ b/src/main/java/io/jboot/support/seata/tcc/TccActionProcesser.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.support.seata.tcc; + +import com.jfinal.aop.Invocation; +import io.seata.common.util.StringUtils; +import io.seata.core.context.RootContext; +import io.seata.core.model.BranchType; +import io.seata.rm.tcc.api.TwoPhaseBusinessAction; + +import java.lang.reflect.Method; + + +/** + * TCC Interceptor + *

+ * 参考: https://github.com/seata/seata/blob/develop/spring/src/main/java/io/seata/spring/tcc/TccActionInterceptor.java + * + * @author zhangsen + */ +public class TccActionProcesser { + + + private ActionInterceptorHandler actionInterceptorHandler = new ActionInterceptorHandler(); + + + + public void intercept(Invocation inv) { + + if (!RootContext.inGlobalTransaction()) { + // not in transaction + inv.invoke(); + return; + } + Method method = inv.getMethod(); + TwoPhaseBusinessAction businessAction = method.getAnnotation(TwoPhaseBusinessAction.class); + //try method + if (businessAction != null) { + //save the xid + String xid = RootContext.getXID(); + //clear the context + String previousBranchType = RootContext.getBranchType(); + RootContext.bindBranchType(BranchType.TCC); + try { + Object[] methodArgs = inv.getArgs(); + //Handler the TCC Aspect + actionInterceptorHandler.proceed(method, methodArgs, xid, businessAction, inv); + } finally { + RootContext.unbindBranchType(); + //restore the TCC branchType if exists + if (StringUtils.equals(BranchType.TCC.name(), previousBranchType)) { + RootContext.bindBranchType(BranchType.TCC); + } + } + } else { + inv.invoke(); + } + } + +} -- Gitee From 54487b89bda9e28e6584d1ffb722621723687ec0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 29 Jul 2020 13:33:18 +0800 Subject: [PATCH 0427/1965] upgrade fastjson version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 309b6330..7897cf34 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ 2.1.3.Final 1.7.30 2.57 - 1.2.72 + 1.2.73 29.0-jre 2.2.5 2.10.2 -- Gitee From 4bd55ddf4ceaf5ca0bad5f750cdbe512d27352b7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 29 Jul 2020 13:49:01 +0800 Subject: [PATCH 0428/1965] optimize Columns --- src/main/java/io/jboot/db/model/Columns.java | 87 ++++++++++---------- 1 file changed, 42 insertions(+), 45 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 3389e96d..e61e66e2 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -101,11 +101,11 @@ public class Columns implements Serializable { * * @param column */ - public void add(Column column) { + public Columns add(Column column) { //do not add null value column if (column.hasPara() && column.getValue() == null) { - return; + return this; } if (this.cols == null) { @@ -113,6 +113,17 @@ public class Columns implements Serializable { } this.cols.add(column); + return this; + } + + + /** + * add Columns + * @param columns + * @return + */ + public Columns add(Columns columns) { + return append(columns); } @@ -125,8 +136,7 @@ public class Columns implements Serializable { */ public Columns eq(String name, Object value) { Util.checkNullParas(this, name, value); - this.add(Column.create(name, value)); - return this; + return add(Column.create(name, value)); } /** @@ -138,8 +148,7 @@ public class Columns implements Serializable { */ public Columns ne(String name, Object value) { Util.checkNullParas(this, name, value); - this.add(Column.create(name, value, Column.LOGIC_NOT_EQUALS)); - return this; + return add(Column.create(name, value, Column.LOGIC_NOT_EQUALS)); } @@ -152,8 +161,7 @@ public class Columns implements Serializable { */ public Columns like(String name, Object value) { Util.checkNullParas(this, name, value); - this.add(Column.create(name, value, Column.LOGIC_LIKE)); - return this; + return add(Column.create(name, value, Column.LOGIC_LIKE)); } /** @@ -168,8 +176,7 @@ public class Columns implements Serializable { if (value == null || (value instanceof String && StrUtil.isBlank((String) value))) { return this; } - this.add(Column.create(name, "%" + value + "%", Column.LOGIC_LIKE)); - return this; + return add(Column.create(name, "%" + value + "%", Column.LOGIC_LIKE)); } /** @@ -181,8 +188,7 @@ public class Columns implements Serializable { */ public Columns gt(String name, Object value) { Util.checkNullParas(this, name, value); - this.add(Column.create(name, value, Column.LOGIC_GT)); - return this; + return add(Column.create(name, value, Column.LOGIC_GT)); } /** @@ -194,8 +200,7 @@ public class Columns implements Serializable { */ public Columns ge(String name, Object value) { Util.checkNullParas(this, name, value); - this.add(Column.create(name, value, Column.LOGIC_GE)); - return this; + return add(Column.create(name, value, Column.LOGIC_GE)); } /** @@ -207,8 +212,7 @@ public class Columns implements Serializable { */ public Columns lt(String name, Object value) { Util.checkNullParas(this, name, value); - this.add(Column.create(name, value, Column.LOGIC_LT)); - return this; + return add(Column.create(name, value, Column.LOGIC_LT)); } /** @@ -220,8 +224,7 @@ public class Columns implements Serializable { */ public Columns le(String name, Object value) { Util.checkNullParas(this, name, value); - this.add(Column.create(name, value, Column.LOGIC_LE)); - return this; + return add(Column.create(name, value, Column.LOGIC_LE)); } @@ -232,8 +235,7 @@ public class Columns implements Serializable { * @return */ public Columns isNull(String name) { - this.add(Column.create(name, null, Column.LOGIC_IS_NULL)); - return this; + return add(Column.create(name, null, Column.LOGIC_IS_NULL)); } @@ -244,7 +246,7 @@ public class Columns implements Serializable { */ public Columns isNullIf(String name, boolean condition) { if (condition) { - this.add(Column.create(name, null, Column.LOGIC_IS_NULL)); + add(Column.create(name, null, Column.LOGIC_IS_NULL)); } return this; } @@ -257,8 +259,7 @@ public class Columns implements Serializable { * @return */ public Columns isNotNull(String name) { - this.add(Column.create(name, null, Column.LOGIC_IS_NOT_NULL)); - return this; + return add(Column.create(name, null, Column.LOGIC_IS_NOT_NULL)); } @@ -271,7 +272,7 @@ public class Columns implements Serializable { */ public Columns isNotNullIf(String name, boolean condition) { if (condition) { - this.add(Column.create(name, null, Column.LOGIC_IS_NOT_NULL)); + add(Column.create(name, null, Column.LOGIC_IS_NOT_NULL)); } return this; } @@ -286,8 +287,7 @@ public class Columns implements Serializable { */ public Columns in(String name, Object... arrays) { Util.checkNullParas(this, name, arrays); - this.add(Column.create(name, arrays, Column.LOGIC_IN)); - return this; + return add(Column.create(name, arrays, Column.LOGIC_IN)); } @@ -314,8 +314,7 @@ public class Columns implements Serializable { */ public Columns notIn(String name, Object... arrays) { Util.checkNullParas(this, name, arrays); - this.add(Column.create(name, arrays, Column.LOGIC_NOT_IN)); - return this; + return add(Column.create(name, arrays, Column.LOGIC_NOT_IN)); } @@ -344,8 +343,7 @@ public class Columns implements Serializable { */ public Columns between(String name, Object start, Object end) { Util.checkNullParas(this, name, start, end); - this.add(Column.create(name, new Object[]{start, end}, Column.LOGIC_BETWEEN)); - return this; + return add(Column.create(name, new Object[]{start, end}, Column.LOGIC_BETWEEN)); } /** @@ -358,8 +356,7 @@ public class Columns implements Serializable { */ public Columns notBetween(String name, Object start, Object end) { Util.checkNullParas(this, name, start, end); - this.add(Column.create(name, new Object[]{start, end}, Column.LOGIC_NOT_BETWEEN)); - return this; + return add(Column.create(name, new Object[]{start, end}, Column.LOGIC_NOT_BETWEEN)); } @@ -374,7 +371,7 @@ public class Columns implements Serializable { throw new IllegalArgumentException("Columns.group(...) need a new Columns"); } if (!columns.isEmpty()) { - this.add(new Group(columns)); + add(new Group(columns)); } return this; } @@ -390,7 +387,7 @@ public class Columns implements Serializable { throw new IllegalArgumentException("Columns.group(...) need a new Columns"); } if (conditon && !columns.isEmpty()) { - this.add(new Group(columns)); + add(new Group(columns)); } return this; } @@ -404,7 +401,7 @@ public class Columns implements Serializable { */ public Columns sqlPart(String sql) { if (StrUtil.isNotBlank(sql)) { - this.add(new SqlPart(sql)); + add(new SqlPart(sql)); } return this; } @@ -419,7 +416,7 @@ public class Columns implements Serializable { public Columns sqlPart(String sql, Object... paras) { Util.checkNullParas(this, paras); if (StrUtil.isNotBlank(sql)) { - this.add(new SqlPart(sql, paras)); + add(new SqlPart(sql, paras)); } return this; } @@ -433,7 +430,7 @@ public class Columns implements Serializable { */ public Columns sqlPartIf(String sql, boolean condition) { if (condition && StrUtil.isNotBlank(sql)) { - this.add(new SqlPart(sql)); + add(new SqlPart(sql)); } return this; } @@ -449,7 +446,7 @@ public class Columns implements Serializable { public Columns sqlPartIf(String sql, boolean condition, Object... paras) { Util.checkNullParas(this, paras); if (condition && StrUtil.isNotBlank(sql)) { - this.add(new SqlPart(sql, paras)); + add(new SqlPart(sql, paras)); } return this; } @@ -462,7 +459,7 @@ public class Columns implements Serializable { */ public Columns sqlPartWithoutLink(String sql) { if (StrUtil.isNotBlank(sql)) { - this.add(new SqlPart(sql, true)); + add(new SqlPart(sql, true)); } return this; } @@ -477,7 +474,7 @@ public class Columns implements Serializable { public Columns sqlPartWithoutLink(String sql, Object... paras) { Util.checkNullParas(this, paras); if (StrUtil.isNotBlank(sql)) { - this.add(new SqlPart(sql, paras, true)); + add(new SqlPart(sql, paras, true)); } return this; } @@ -491,7 +488,7 @@ public class Columns implements Serializable { */ public Columns sqlPartWithoutLinkIf(String sql, boolean condition) { if (condition && StrUtil.isNotBlank(sql)) { - this.add(new SqlPart(sql, true)); + add(new SqlPart(sql, true)); } return this; } @@ -507,14 +504,14 @@ public class Columns implements Serializable { public Columns sqlPartWithoutLinkIf(String sql, boolean condition, Object... paras) { Util.checkNullParas(this, paras); if (condition && StrUtil.isNotBlank(sql)) { - this.add(new SqlPart(sql, paras, true)); + add(new SqlPart(sql, paras, true)); } return this; } public Columns or() { - this.add(new Or()); + add(new Or()); return this; } @@ -524,9 +521,9 @@ public class Columns implements Serializable { for (int i = 0; i < values.length; i++) { Object value = values[i]; if (value != null) { - this.add(Column.create(name, value, logic)); + add(Column.create(name, value, logic)); if (i != values.length - 1) { - this.add(new Or()); + add(new Or()); } } } -- Gitee From 12e0e9206c4f9a609dad2d848bff4bcc27b72686 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 30 Jul 2020 17:51:27 +0800 Subject: [PATCH 0429/1965] upgrade sharding-jdbc --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7897cf34..1b357806 100644 --- a/pom.xml +++ b/pom.xml @@ -381,7 +381,7 @@ org.apache.shardingsphere sharding-jdbc-core - 4.1.0 + 4.1.1 provided -- Gitee From c3140e288604879ce25f5fa20661d2f4cc348c5f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 30 Jul 2020 17:56:02 +0800 Subject: [PATCH 0430/1965] optimize JbootUndertowConfig --- .../app/undertow/JbootUndertowConfig.java | 26 ++++++++++++++----- .../java/io/jboot/db/model/JbootModel.java | 5 +--- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java b/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java index 85878fa6..4cf583fb 100644 --- a/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java +++ b/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java @@ -18,7 +18,6 @@ package io.jboot.app.undertow; import com.jfinal.server.undertow.PropExt; import com.jfinal.server.undertow.UndertowConfig; import com.jfinal.server.undertow.hotswap.HotSwapResolver; -import io.jboot.Jboot; import io.jboot.app.config.JbootConfigManager; import java.io.IOException; @@ -55,23 +54,36 @@ public class JbootUndertowConfig extends UndertowConfig { .append(new PropExt(JbootConfigManager.me().getProperties())); String port = propExt.get(UNDERTOW_PORT); - Integer availablePort = getAvailablePort(); + + //当不配置端口号时,默认为 8080 if (port == null || port.trim().length() == 0) { propExt.getProperties().put(UNDERTOW_PORT, "8080"); JbootConfigManager.me().setBootArg(UNDERTOW_PORT, "8080"); - } else if ((port.trim().equals("*") || port.trim().equals("-1")) && availablePort != null) { - propExt.getProperties().put(UNDERTOW_PORT, availablePort.toString()); - JbootConfigManager.me().setBootArg(UNDERTOW_PORT, availablePort.toString()); } - if (Jboot.isDevMode()){ - propExt.getProperties().put(DEV_MODE, true); + + //当配置为 -1 或者 * 时,默认为随机端口号 + else if ((port.trim().equals("*") || port.trim().equals("-1"))) { + Integer availablePort = getAvailablePort(); + if (availablePort != null) { + propExt.getProperties().put(UNDERTOW_PORT, availablePort.toString()); + JbootConfigManager.me().setBootArg(UNDERTOW_PORT, availablePort.toString()); + } + } + + //当不配置 undertow 开发模式时,默认开发模式为 devMode + String devMode = propExt.get(DEV_MODE); + if (devMode == null || devMode.trim().length() == 0) { + propExt.getProperties().put(DEV_MODE, JbootConfigManager.me().isDevMode()); } + + //当不配置 host 时,默认为 0.0.0.0 String host = propExt.get(UNDERTOW_HOST); if (host == null || host.trim().length() == 0) { propExt.getProperties().put(UNDERTOW_HOST, "0.0.0.0"); JbootConfigManager.me().setBootArg(UNDERTOW_HOST, "0.0.0.0"); } + //当不配置资源路径时,默认为 classpath 下的 webapp String resPath = propExt.get(UNDERTOW_RESOURCEPATH); if (resPath == null || resPath.trim().length() == 0) { propExt.getProperties().put(UNDERTOW_RESOURCEPATH, "classpath:webapp," + this.resourcePath); diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 50cda159..c970259c 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -414,10 +414,7 @@ public class JbootModel> extends Model { protected JbootDialect _getDialect() { Config config = _getConfig(); if (config == null) { - throw new JbootException( - String.format("class %s can not mapping to database table, maybe cannot connect to database. " - , _getUsefulClass().getName())); - + throw new JbootException("class \"" + _getUsefulClass().getName() + "\" can not mapping to the table, maybe cannot connect to database. "); } return (JbootDialect) config.getDialect(); } -- Gitee From 42a043c57f31da502ca6ddaa042e62d3fad457b5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 31 Jul 2020 11:15:42 +0800 Subject: [PATCH 0431/1965] add MatchesValidate --- .../io/jboot/web/validate/MatchesForm.java | 39 ++++++ .../jboot/web/validate/MatchesValidate.java | 40 ++++++ .../web/validate/ParaValidateInterceptor.java | 126 +++++++++++++----- 3 files changed, 173 insertions(+), 32 deletions(-) create mode 100644 src/main/java/io/jboot/web/validate/MatchesForm.java create mode 100644 src/main/java/io/jboot/web/validate/MatchesValidate.java diff --git a/src/main/java/io/jboot/web/validate/MatchesForm.java b/src/main/java/io/jboot/web/validate/MatchesForm.java new file mode 100644 index 00000000..706c958a --- /dev/null +++ b/src/main/java/io/jboot/web/validate/MatchesForm.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.validate; + +import java.lang.annotation.*; + +/** + * 非空验证注解 + * @author michael yang + */ +@Documented +@Target(ElementType.METHOD) +@Inherited +@Retention(RetentionPolicy.RUNTIME) +public @interface MatchesForm { + + String name(); + + String regex(); + + String message() default ""; + + String type() default FormType.FORM_DATA; + + int errorCode() default 1; +} diff --git a/src/main/java/io/jboot/web/validate/MatchesValidate.java b/src/main/java/io/jboot/web/validate/MatchesValidate.java new file mode 100644 index 00000000..f05be4bc --- /dev/null +++ b/src/main/java/io/jboot/web/validate/MatchesValidate.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.validate; + +import java.lang.annotation.*; + +/** + * 正则验证注解 + * @author michael yang + */ +@Documented +@Target(ElementType.METHOD) +@Inherited +@Retention(RetentionPolicy.RUNTIME) +public @interface MatchesValidate { + + MatchesForm[] value(); + + String message() default ""; + + String redirectUrl() default ""; + + String htmlPath() default ""; + + String renderType() default ValidateRenderType.DEFAULT; + +} diff --git a/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java b/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java index c918f2ac..5b447aff 100644 --- a/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java @@ -39,12 +39,17 @@ public class ParaValidateInterceptor implements FixedInterceptor { Method method = inv.getMethod(); - EmptyValidate emptyParaValidate = method.getAnnotation(EmptyValidate.class); if (emptyParaValidate != null && !validateEmpty(inv, emptyParaValidate)) { return; } + + MatchesValidate matchesValidate = method.getAnnotation(MatchesValidate.class); + if (matchesValidate != null && !validatMatches(inv, matchesValidate)) { + return; + } + CaptchaValidate captchaValidate = method.getAnnotation(CaptchaValidate.class); if (captchaValidate != null && !validateCaptache(inv, captchaValidate)) { return; @@ -55,56 +60,81 @@ public class ParaValidateInterceptor implements FixedInterceptor { /** - * 对验证码进行验证 + * 非空判断验证 * * @param inv - * @param captchaValidate + * @param emptyParaValidate * @return */ - private boolean validateCaptache(Invocation inv, CaptchaValidate captchaValidate) { - String formName = AnnotationUtil.get(captchaValidate.form()); - if (StrUtil.isBlank(formName)) { - throw new IllegalArgumentException("@CaptchaValidate.form must not be empty in " + inv.getController().getClass().getName() + "." + inv.getMethodName()); + private boolean validateEmpty(Invocation inv, EmptyValidate emptyParaValidate) { + Form[] forms = emptyParaValidate.value(); + if (ArrayUtil.isNullOrEmpty(forms)) { + return true; } - Controller controller = inv.getController(); - if (controller.validateCaptcha(formName)) { - return true; - } + for (Form form : forms) { + String formName = AnnotationUtil.get(form.name()); + String formType = AnnotationUtil.get(form.type()); + if (StrUtil.isBlank(formName)) { + throw new IllegalArgumentException("@Form.value must not be empty in " + inv.getController().getClass().getName() + "." + inv.getMethodName()); + } + String value = null; + if (FormType.FORM_DATA.equalsIgnoreCase(formType)) { + value = inv.getController().getPara(formName); + } else if (FormType.RAW_DATA.equalsIgnoreCase(formType)) { + try { + JSONObject json = JSON.parseObject(inv.getController().getRawData()); + if (json != null) { + Object tmp = JSONPath.eval(json, "$." + formName); + if (tmp != null) { + value = tmp.toString(); + } + } + } catch (Exception e) { + value = null; + } + } else { + throw new IllegalArgumentException("@EmptyValidate not support form type : " + formType + ", " + + "see : io.jboot.web.controller.validate.FormType"); + } - renderError(inv.getController() - , AnnotationUtil.get(captchaValidate.renderType()) - , formName - , AnnotationUtil.get(captchaValidate.message()) - , AnnotationUtil.get(captchaValidate.redirectUrl()) - , AnnotationUtil.get(captchaValidate.htmlPath()) - , captchaValidate.errorCode() - ); + if (value == null || value.trim().length() == 0) { + renderError(inv.getController() + , AnnotationUtil.get(emptyParaValidate.renderType()) + , formName + , AnnotationUtil.get(form.message()) + , AnnotationUtil.get(emptyParaValidate.redirectUrl()) + , AnnotationUtil.get(emptyParaValidate.htmlPath()) + , form.errorCode() + ); + return false; + } + } - return false; + return true; } /** - * 非空判断验证 + * 正则验证 * * @param inv - * @param emptyParaValidate + * @param matchesValidate * @return */ - private boolean validateEmpty(Invocation inv, EmptyValidate emptyParaValidate) { - Form[] forms = emptyParaValidate.value(); + private boolean validatMatches(Invocation inv, MatchesValidate matchesValidate) { + MatchesForm[] forms = matchesValidate.value(); if (ArrayUtil.isNullOrEmpty(forms)) { return true; } - for (Form form : forms) { + for (MatchesForm form : forms) { String formName = AnnotationUtil.get(form.name()); String formType = AnnotationUtil.get(form.type()); if (StrUtil.isBlank(formName)) { - throw new IllegalArgumentException("@Form.value must not be empty in " + inv.getController().getClass().getName() + "." + inv.getMethodName()); + throw new IllegalArgumentException("@MatchesForm.value must not be empty in " + inv.getController().getClass().getName() + "." + inv.getMethodName()); } String value = null; if (FormType.FORM_DATA.equalsIgnoreCase(formType)) { @@ -122,17 +152,17 @@ public class ParaValidateInterceptor implements FixedInterceptor { value = null; } } else { - throw new IllegalArgumentException("para validate not support form type : " + formType + ", " + + throw new IllegalArgumentException("@MatchesValidate not support form type : " + formType + ", " + "see : io.jboot.web.controller.validate.FormType"); } - if (value == null || value.trim().length() == 0) { + if (value == null || !value.matches(form.regex())) { renderError(inv.getController() - , AnnotationUtil.get(emptyParaValidate.renderType()) + , AnnotationUtil.get(matchesValidate.renderType()) , formName , AnnotationUtil.get(form.message()) - , AnnotationUtil.get(emptyParaValidate.redirectUrl()) - , AnnotationUtil.get(emptyParaValidate.htmlPath()) + , AnnotationUtil.get(matchesValidate.redirectUrl()) + , AnnotationUtil.get(matchesValidate.htmlPath()) , form.errorCode() ); return false; @@ -143,6 +173,38 @@ public class ParaValidateInterceptor implements FixedInterceptor { } + /** + * 对验证码进行验证 + * + * @param inv + * @param captchaValidate + * @return + */ + private boolean validateCaptache(Invocation inv, CaptchaValidate captchaValidate) { + String formName = AnnotationUtil.get(captchaValidate.form()); + if (StrUtil.isBlank(formName)) { + throw new IllegalArgumentException("@CaptchaValidate.form must not be empty in " + inv.getController().getClass().getName() + "." + inv.getMethodName()); + } + + + Controller controller = inv.getController(); + if (controller.validateCaptcha(formName)) { + return true; + } + + renderError(inv.getController() + , AnnotationUtil.get(captchaValidate.renderType()) + , formName + , AnnotationUtil.get(captchaValidate.message()) + , AnnotationUtil.get(captchaValidate.redirectUrl()) + , AnnotationUtil.get(captchaValidate.htmlPath()) + , captchaValidate.errorCode() + ); + + return false; + } + + private void renderError(Controller controller, String renderType, String formName, String message, String redirectUrl, String htmlPath, int errorCode) { switch (renderType) { case ValidateRenderType.DEFAULT: @@ -153,7 +215,7 @@ public class ParaValidateInterceptor implements FixedInterceptor { .setIfNotNull("formName", formName) ); } else { - controller.renderError(404); + controller.renderText(formName + ":" + message); } break; case ValidateRenderType.JSON: -- Gitee From 8c720f5fea4e8e6eab73d3a49498ef5369aec003 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 31 Jul 2020 11:55:58 +0800 Subject: [PATCH 0432/1965] optimize validate message --- .../web/validate/ParaValidateInterceptor.java | 10 ++++--- .../test/validate/ValidateController.java | 27 ++++++++++++++----- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java b/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java index 5b447aff..8ded0de2 100644 --- a/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java @@ -21,6 +21,7 @@ import com.alibaba.fastjson.JSONPath; import com.jfinal.aop.Invocation; import com.jfinal.core.Controller; import com.jfinal.kit.Ret; +import com.jfinal.render.TextRender; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.ArrayUtil; import io.jboot.utils.RequestUtil; @@ -206,21 +207,22 @@ public class ParaValidateInterceptor implements FixedInterceptor { private void renderError(Controller controller, String renderType, String formName, String message, String redirectUrl, String htmlPath, int errorCode) { + String newMessage = StrUtil.isNotBlank(message) ? (formName + " validate failed: " + message) : (formName + " validate failed!"); switch (renderType) { case ValidateRenderType.DEFAULT: if (RequestUtil.isAjaxRequest(controller.getRequest())) { controller.renderJson( - Ret.fail("message", message) + Ret.fail("message", newMessage) .set("errorCode", errorCode) .setIfNotNull("formName", formName) ); } else { - controller.renderText(formName + ":" + message); + controller.renderError(403, new TextRender(newMessage)); } break; case ValidateRenderType.JSON: controller.renderJson( - Ret.fail("message", message) + Ret.fail("message", newMessage) .set("errorCode", errorCode) .setIfNotNull("formName", formName) ); @@ -232,7 +234,7 @@ public class ParaValidateInterceptor implements FixedInterceptor { controller.render(htmlPath); break; case ValidateRenderType.TEXT: - controller.renderText(message); + controller.renderText(newMessage); break; default: throw new IllegalArgumentException("can not process render : " + renderType); diff --git a/src/test/java/io/jboot/test/validate/ValidateController.java b/src/test/java/io/jboot/test/validate/ValidateController.java index e14ac8d7..78c98dc5 100644 --- a/src/test/java/io/jboot/test/validate/ValidateController.java +++ b/src/test/java/io/jboot/test/validate/ValidateController.java @@ -2,21 +2,36 @@ package io.jboot.test.validate; import com.jfinal.core.Controller; import io.jboot.web.controller.annotation.RequestMapping; -import io.jboot.web.validate.EmptyValidate; -import io.jboot.web.validate.Form; -import io.jboot.web.validate.ValidateRenderType; +import io.jboot.web.validate.*; @RequestMapping("/validate") public class ValidateController extends Controller { - public void index(){ + public void index() { renderText("index"); } - @EmptyValidate(value = @Form(name = "form"),renderType = ValidateRenderType.JSON) - public void test1(){ + @EmptyValidate(value = @Form(name = "form"), renderType = ValidateRenderType.JSON) + public void test1() { + renderText("test1"); + } + + + @MatchesValidate(value = @MatchesForm(name = "email", regex = "\\w+@(\\w+.)+[a-z]{2,3}", message = "请输入正确的邮箱地址")) + public void test2() { + renderText("test2"); + } + + @MatchesValidate(value = @MatchesForm(name = "email", regex = "\\w+@(\\w+.)+[a-z]{2,3}")) + public void test3() { renderText("test3"); } + + + @MatchesValidate(value = @MatchesForm(name = "email", regex = "\\w+@(\\w+.)+[a-z]{2,3}"), renderType = ValidateRenderType.JSON) + public void test4() { + renderText("test4"); + } } -- Gitee From 03461f416d15de60df09252e86ba96bd88c8e2de Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 31 Jul 2020 11:57:34 +0800 Subject: [PATCH 0433/1965] optimize ClassScanner --- src/main/java/io/jboot/utils/ClassScanner.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index d828af5b..7e4bfb59 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -227,6 +227,7 @@ public class ClassScanner { excludeJars.add("apiguardian-api-"); excludeJars.add("opentest4j-"); excludeJars.add("freemarker-"); + excludeJars.add("protobuf-"); } -- Gitee From 729274d358b0cc90670a8727b70f1fc5507895df Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 31 Jul 2020 13:37:39 +0800 Subject: [PATCH 0434/1965] add MatchesValidate --- .../java/io/jboot/web/validate/Regex.java | 296 ++++++++++++++++++ .../test/validate/ValidateController.java | 6 +- 2 files changed, 299 insertions(+), 3 deletions(-) create mode 100644 src/main/java/io/jboot/web/validate/Regex.java diff --git a/src/main/java/io/jboot/web/validate/Regex.java b/src/main/java/io/jboot/web/validate/Regex.java new file mode 100644 index 00000000..2a1d68f9 --- /dev/null +++ b/src/main/java/io/jboot/web/validate/Regex.java @@ -0,0 +1,296 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.validate; + +/** + * 正则表达式大全 + * @author michael yang (fuhai999@gmail.com) + */ +public class Regex { + + /** + * 汉字 + */ + public static final String CHINESE ="^[\\u4e00-\\u9fa5]+$"; + + /** + * 全部是英文 + */ + public static final String ENGLISH ="^[A-Za-z]+$"; + + + /** + * 英文或者数字 + */ + public static final String ENGLISH_NUMBERS ="^[A-Za-z0-9]+$"; + + + /** + * 英文、数字 或下划线 + */ + public static final String ENGLISH_NUMBERS_UNDERLINE ="^[A-Za-z0-9_]+$"; + + + + /** + * 中文、英文、数字、或下划线 + */ + public static final String CHINESE_ENGLISH_NUMBERS_UNDERLINE ="^[\\u4E00-\\u9FA5A-Za-z0-9_]+$"; + + /** + * 正数、负数 或 小数 + */ + public static final String DECIMAL ="^(\\-|\\+)?\\d+(\\.\\d+)?$"; + + + /** + * 密码长度 6~20 位数,字母、数字和下划线 + */ + public static final String PASSWORD ="^[a-zA-Z0-9_]\\w{5,19}$"; + + + /** + * 邮件地址 + */ + public static final String EMAIL ="^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"; + + + /** + * 域名 + */ + public static final String DOMAIN ="^((http://)|(https://))?([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}"; + + /** + * url 地址 + */ + public static final String URL ="[a-zA-z]+://[^\\s]*"; + + /** + * 手机号码 + */ + public static final String MOBILE ="^(1[3,4,5,6,7,8,9])\\d{9}$"; + + /** + * 电话号码 + */ + public static final String TELEPHONE ="^(\\(\\d{3,4}\\)|\\d{3,4}-|\\s)?\\d{7,14}$"; + + /** + * 身份证号码 + */ + public static final String ID_CARD ="^[1-9]\\d{5}(18|19|20|(3\\d))\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$"; + + + /** + * 日期格式 2020-02-0 + */ + public static final String DATE ="^[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))$"; + + + /** + * 日期格式 2020-02-02 23:12:23 或者 2020-02-02 23:12 + */ + public static final String DATE_TIME ="^[1-9]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])\\s+(20|21|22|23|[0-1]\\d):[0-5]\\d(:[0-5]\\d)?$"; + + + /** + * 时间格式(没有秒) 2020-02-02 23:12 + */ + public static final String DATE_TIME_HM ="^[1-9]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])\\s+(20|21|22|23|[0-1]\\d):[0-5]\\d$"; + + + + /** + * 时间格式 2020-02-02 23:12:23 + */ + public static final String DATE_TIME_HMS ="^[1-9]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])\\s+(20|21|22|23|[0-1]\\d):[0-5]\\d:[0-5]\\d$"; + + + + /** + * ip地址 + */ + public static final String IP ="^((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$"; + + + + public static void main(String[] args){ + + System.out.println("\"汉字\".matches(CHINESE) ---> " + ("汉字".matches(CHINESE))); + System.out.println("\"汉字123\".matches(CHINESE) ---> " + ("汉字123".matches(CHINESE))); + System.out.println(); + + + + System.out.println("\"abc\".matches(ENGLISH) ---> " + ("abc".matches(ENGLISH))); + System.out.println("\"abc123\".matches(ENGLISH) ---> " + ("abc123".matches(ENGLISH))); + System.out.println(); + + + + System.out.println("\"abc123\".matches(ENGLISH_NUMBERS) ---> " + ("abc123".matches(ENGLISH_NUMBERS))); + System.out.println("\"abc_123\".matches(ENGLISH_NUMBERS) ---> " + ("abc_123".matches(ENGLISH_NUMBERS))); + System.out.println("\"汉字abc123\".matches(ENGLISH_NUMBERS) ---> " + ("汉字汉字abc123".matches(ENGLISH_NUMBERS))); + System.out.println(); + + + + System.out.println("\"abc123\".matches(ENGLISH_NUMBERS_UNDERLINE) ---> " + ("abc123".matches(ENGLISH_NUMBERS_UNDERLINE))); + System.out.println("\"abc_123\".matches(ENGLISH_NUMBERS_UNDERLINE) ---> " + ("abc_123".matches(ENGLISH_NUMBERS_UNDERLINE))); + System.out.println("\"汉字abc123\".matches(ENGLISH_NUMBERS_UNDERLINE) ---> " + ("汉字汉字abc123".matches(ENGLISH_NUMBERS_UNDERLINE))); + System.out.println(); + + + + System.out.println("\"abc123\".matches(CHINESE_ENGLISH_NUMBERS_UNDERLINE) ---> " + ("abc123".matches(CHINESE_ENGLISH_NUMBERS_UNDERLINE))); + System.out.println("\"abc_123\".matches(CHINESE_ENGLISH_NUMBERS_UNDERLINE) ---> " + ("abc_123".matches(CHINESE_ENGLISH_NUMBERS_UNDERLINE))); + System.out.println("\"汉字abc123\".matches(CHINESE_ENGLISH_NUMBERS_UNDERLINE) ---> " + ("汉字汉字abc123".matches(CHINESE_ENGLISH_NUMBERS_UNDERLINE))); + System.out.println("\"汉字abc123#\".matches(CHINESE_ENGLISH_NUMBERS_UNDERLINE) ---> " + ("汉字abc123#".matches(CHINESE_ENGLISH_NUMBERS_UNDERLINE))); + System.out.println(); + + + + System.out.println("\"汉字\".matches(DECIMAL) ---> " + ("汉字".matches(DECIMAL))); + System.out.println("\"123\".matches(DECIMAL) ---> " + ("123".matches(DECIMAL))); + System.out.println("\"123.12\".matches(DECIMAL) ---> " + ("123.12".matches(DECIMAL))); + System.out.println("\"-12\".matches(DECIMAL) ---> " + ("-12".matches(DECIMAL))); + System.out.println("\"-12.12\".matches(DECIMAL) ---> " + ("-12.12".matches(DECIMAL))); + System.out.println(); + + + + System.out.println("\"123\".matches(PASSWORD) ---> " + ("123".matches(PASSWORD))); + System.out.println("\"123456\".matches(PASSWORD) ---> " + ("123456".matches(PASSWORD))); + System.out.println(); + + + + System.out.println("\"汉字\".matches(EMAIL) ---> " + ("汉字".matches(EMAIL))); + System.out.println("\"abc\".matches(EMAIL) ---> " + ("abc".matches(EMAIL))); + System.out.println("\"abc@gmail\".matches(EMAIL) ---> " + ("bc.abc@gmail".matches(EMAIL))); + System.out.println("\"abc.abc@gmail\".matches(EMAIL) ---> " + ("bc.abc@gmail".matches(EMAIL))); + System.out.println("\"abc@gmail.com\".matches(EMAIL) ---> " + ("bc.abc@gmail.com".matches(EMAIL))); + System.out.println("\"abc.abc@gmail.com\".matches(EMAIL) ---> " + ("bc.abc@gmail.com".matches(EMAIL))); + System.out.println(); + + + + System.out.println("\"yangfuhai\".matches(DOMAIN) ---> " + ("yangfuhai".matches(DOMAIN))); + System.out.println("\"yangfuhai.com\".matches(DOMAIN) ---> " + ("yangfuhai.com".matches(DOMAIN))); + System.out.println("\"www.yangfuhai.com\".matches(DOMAIN) ---> " + ("www.yangfuhai.com".matches(DOMAIN))); + System.out.println("\"http://www.yangfuhai.com\".matches(DOMAIN) ---> " + ("http://www.yangfuhai.com".matches(DOMAIN))); + System.out.println("\"http://www.yangfuhai.com/\".matches(DOMAIN) ---> " + ("http://www.yangfuhai.com/".matches(DOMAIN))); + System.out.println(); + + + + System.out.println("\"汉字\".matches(URL) ---> " + ("汉字".matches(URL))); + System.out.println("\"abc\".matches(URL) ---> " + ("abc".matches(URL))); + System.out.println("\"http://www\".matches(URL) ---> " + ("http://www".matches(URL))); + System.out.println("\"http://www.yangfuhai.com\".matches(URL) ---> " + ("http://www.yangfuhai.com".matches(URL))); + System.out.println(); + + + + System.out.println("\"汉字\".matches(MOBILE) ---> " + ("汉字".matches(MOBILE))); + System.out.println("\"12345\".matches(MOBILE) ---> " + ("12345".matches(MOBILE))); + System.out.println("\"11611223344\".matches(MOBILE) ---> " + ("11611223344".matches(MOBILE))); + System.out.println("\"12611223344\".matches(MOBILE) ---> " + ("12611223344".matches(MOBILE))); + System.out.println("\"13611223344\".matches(MOBILE) ---> " + ("13611223344".matches(MOBILE))); + System.out.println("\"14611223344\".matches(MOBILE) ---> " + ("14611223344".matches(MOBILE))); + System.out.println("\"15611223344\".matches(MOBILE) ---> " + ("15611223344".matches(MOBILE))); + System.out.println("\"16611223344\".matches(MOBILE) ---> " + ("16611223344".matches(MOBILE))); + System.out.println("\"17611223344\".matches(MOBILE) ---> " + ("17611223344".matches(MOBILE))); + System.out.println("\"18611223344\".matches(MOBILE) ---> " + ("18611223344".matches(MOBILE))); + System.out.println("\"19611223344\".matches(MOBILE) ---> " + ("19611223344".matches(MOBILE))); + System.out.println("\"196112233441\".matches(MOBILE) ---> " + ("196112233441".matches(MOBILE))); + System.out.println(); + + + + System.out.println("\"汉字\".matches(TELEPHONE) ---> " + ("汉字".matches(TELEPHONE))); + System.out.println("\"021-1234567\".matches(TELEPHONE) ---> " + ("021-1234567".matches(TELEPHONE))); + System.out.println("\"0855-1234567\".matches(TELEPHONE) ---> " + ("0855-1234567".matches(TELEPHONE))); + System.out.println("\"1234567\".matches(TELEPHONE) ---> " + ("1234567".matches(TELEPHONE))); + System.out.println(); + + + + System.out.println("\"汉字\".matches(ID_CARD) ---> " + ("汉字".matches(ID_CARD))); + System.out.println("\"522\".matches(ID_CARD) ---> " + ("522".matches(ID_CARD))); + System.out.println("\"522000000000000000\".matches(ID_CARD) ---> " + ("522000000000000000".matches(ID_CARD))); + System.out.println("\"52260119000125205x\".matches(ID_CARD) ---> " + ("52260119000125205x".matches(ID_CARD))); + System.out.println(); + + + + System.out.println("\"汉字\".matches(DATE) ---> " + ("汉字".matches(DATE))); + System.out.println("\"abc\".matches(DATE) ---> " + ("abc".matches(DATE))); + System.out.println("\"123\".matches(DATE) ---> " + ("123".matches(DATE))); + System.out.println("\"2020-02-02\".matches(DATE) ---> " + ("2020-02-02".matches(DATE))); + System.out.println("\"2020-02-02 \".matches(DATE) ---> " + ("2020-02-02 ".matches(DATE))); + System.out.println("\"2020-02-02 23:32\".matches(DATE) ---> " + ("2020-02-02 23:32".matches(DATE))); + System.out.println("\"2020-02-02 23:32:21\".matches(DATE) ---> " + ("2020-02-02 23:32:21".matches(DATE))); + System.out.println(); + + + + System.out.println("\"汉字\".matches(DATE_TIME) ---> " + ("汉字".matches(DATE_TIME))); + System.out.println("\"abc\".matches(DATE_TIME) ---> " + ("abc".matches(DATE_TIME))); + System.out.println("\"123\".matches(DATE_TIME) ---> " + ("123".matches(DATE_TIME))); + System.out.println("\"2020-02-02\".matches(DATE_TIME) ---> " + ("2020-02-02".matches(DATE_TIME))); + System.out.println("\"2020-02-02 \".matches(DATE_TIME) ---> " + ("2020-02-02 ".matches(DATE_TIME))); + System.out.println("\"2020-02-02 23:32\".matches(DATE_TIME) ---> " + ("2020-02-02 23:32".matches(DATE_TIME))); + System.out.println("\"2020-02-02 23:32:21\".matches(DATE_TIME) ---> " + ("2020-02-02 23:32:21".matches(DATE_TIME))); + System.out.println(); + + + + System.out.println("\"汉字\".matches(DATE_TIME_HM) ---> " + ("汉字".matches(DATE_TIME_HM))); + System.out.println("\"abc\".matches(DATE_TIME_HM) ---> " + ("abc".matches(DATE_TIME_HM))); + System.out.println("\"123\".matches(DATE_TIME_HM) ---> " + ("123".matches(DATE_TIME_HM))); + System.out.println("\"2020-02-02\".matches(DATE_TIME_HM) ---> " + ("2020-02-02".matches(DATE_TIME_HM))); + System.out.println("\"2020-02-02 \".matches(DATE_TIME_HM) ---> " + ("2020-02-02 ".matches(DATE_TIME_HM))); + System.out.println("\"2020-02-02 23:32\".matches(DATE_TIME_HM) ---> " + ("2020-02-02 23:32".matches(DATE_TIME_HM))); + System.out.println("\"2020-02-02 23:32:21\".matches(DATE_TIME_HM) ---> " + ("2020-02-02 23:32:21".matches(DATE_TIME_HM))); + System.out.println(); + + + + System.out.println("\"汉字\".matches(DATE_TIME_HMS) ---> " + ("汉字".matches(DATE_TIME_HMS))); + System.out.println("\"abc\".matches(DATE_TIME_HMS) ---> " + ("abc".matches(DATE_TIME_HMS))); + System.out.println("\"123\".matches(DATE_TIME_HMS) ---> " + ("123".matches(DATE_TIME_HMS))); + System.out.println("\"2020-02-02\".matches(DATE_TIME_HMS) ---> " + ("2020-02-02".matches(DATE_TIME_HMS))); + System.out.println("\"2020-02-02 \".matches(DATE_TIME_HMS) ---> " + ("2020-02-02 ".matches(DATE_TIME_HMS))); + System.out.println("\"2020-02-02 23:32\".matches(DATE_TIME_HMS) ---> " + ("2020-02-02 23:32".matches(DATE_TIME_HMS))); + System.out.println("\"2020-02-02 23:32:21\".matches(DATE_TIME_HMS) ---> " + ("2020-02-02 23:32:21".matches(DATE_TIME_HMS))); + System.out.println(); + + + + System.out.println("\"汉字\".matches(IP) ---> " + ("汉字".matches(IP))); + System.out.println("\"abc\".matches(IP) ---> " + ("abc".matches(IP))); + System.out.println("\"123\".matches(IP) ---> " + ("123".matches(IP))); + System.out.println("\"123.123.123\".matches(IP) ---> " + ("123.123.123".matches(IP))); + System.out.println("\"123.123.123.123\".matches(IP) ---> " + ("123.123.123.123".matches(IP))); + System.out.println("\"255.255.255.255\".matches(IP) ---> " + ("255.255.255.255".matches(IP))); + System.out.println("\"255.255.256.255\".matches(IP) ---> " + ("255.255.256.255".matches(IP))); + System.out.println("\"0.0.0.0\".matches(IP) ---> " + ("0.0.0.0".matches(IP))); + } + + + +} diff --git a/src/test/java/io/jboot/test/validate/ValidateController.java b/src/test/java/io/jboot/test/validate/ValidateController.java index 78c98dc5..fe3399a3 100644 --- a/src/test/java/io/jboot/test/validate/ValidateController.java +++ b/src/test/java/io/jboot/test/validate/ValidateController.java @@ -19,18 +19,18 @@ public class ValidateController extends Controller { } - @MatchesValidate(value = @MatchesForm(name = "email", regex = "\\w+@(\\w+.)+[a-z]{2,3}", message = "请输入正确的邮箱地址")) + @MatchesValidate(value = @MatchesForm(name = "email", regex = Regex.EMAIL, message = "请输入正确的邮箱地址")) public void test2() { renderText("test2"); } - @MatchesValidate(value = @MatchesForm(name = "email", regex = "\\w+@(\\w+.)+[a-z]{2,3}")) + @MatchesValidate(value = @MatchesForm(name = "email", regex = Regex.EMAIL)) public void test3() { renderText("test3"); } - @MatchesValidate(value = @MatchesForm(name = "email", regex = "\\w+@(\\w+.)+[a-z]{2,3}"), renderType = ValidateRenderType.JSON) + @MatchesValidate(value = @MatchesForm(name = "email", regex = Regex.EMAIL), renderType = ValidateRenderType.JSON) public void test4() { renderText("test4"); } -- Gitee From 1ae3e7d7decd5cdae7c737ff6bf67ad7dca092e4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 31 Jul 2020 13:41:17 +0800 Subject: [PATCH 0435/1965] add MatchesValidate --- src/main/java/io/jboot/utils/StrUtil.java | 15 +++++++-------- .../web/validate/ParaValidateInterceptor.java | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/utils/StrUtil.java b/src/main/java/io/jboot/utils/StrUtil.java index cc2ff601..a92e4d19 100644 --- a/src/main/java/io/jboot/utils/StrUtil.java +++ b/src/main/java/io/jboot/utils/StrUtil.java @@ -19,6 +19,7 @@ import com.jfinal.core.JFinal; import com.jfinal.kit.StrKit; import com.jfinal.log.Log; import com.jfinal.plugin.activerecord.Model; +import io.jboot.web.validate.Regex; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; @@ -170,16 +171,16 @@ public class StrUtil extends StrKit { if (isBlank(str)) { return false; } - boolean hasDot = false; + boolean hasDot = false; for (int i = str.length(); --i >= 0; ) { int chr = str.charAt(i); if ((chr < 48 || chr > 57) && chr != '.') { return false; } - if (chr == '.'){ - if (hasDot){ + if (chr == '.') { + if (hasDot) { return false; - }else { + } else { hasDot = true; } } @@ -194,7 +195,7 @@ public class StrUtil extends StrKit { * @return */ public static boolean isEmail(String email) { - return isBlank(email) ? false : Pattern.matches("\\w+@(\\w+.)+[a-z]{2,3}", email); + return isNotBlank(email) && email.matches(Regex.EMAIL); } @@ -205,7 +206,7 @@ public class StrUtil extends StrKit { * @return */ public static boolean isMobileNumber(String mobileNumber) { - return isBlank(mobileNumber) ? false : Pattern.matches("^(1[3,4,5,6,7,8,9])\\d{9}$", mobileNumber); + return isNotBlank(mobileNumber) && mobileNumber.matches(Regex.MOBILE); } @@ -304,7 +305,6 @@ public class StrUtil extends StrKit { } - public static String join(String[] array, String split) { if (array == null || array.length == 0) { return EMPTY; @@ -320,7 +320,6 @@ public class StrUtil extends StrKit { } - public static String join(Collection coll, String split) { if (coll == null || coll.isEmpty()) { return EMPTY; diff --git a/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java b/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java index 8ded0de2..585ed9f1 100644 --- a/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java @@ -157,7 +157,7 @@ public class ParaValidateInterceptor implements FixedInterceptor { "see : io.jboot.web.controller.validate.FormType"); } - if (value == null || !value.matches(form.regex())) { + if (value == null || !value.trim().matches(form.regex())) { renderError(inv.getController() , AnnotationUtil.get(matchesValidate.renderType()) , formName -- Gitee From 73b0007dd9dfdda0ead6a8f10945bc33941eefd8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 31 Jul 2020 13:44:54 +0800 Subject: [PATCH 0436/1965] rename MatchesValidate to RegexValidate --- .../web/validate/ParaValidateInterceptor.java | 19 +++++++++---------- .../{MatchesForm.java => RegexForm.java} | 2 +- ...atchesValidate.java => RegexValidate.java} | 4 ++-- .../test/validate/ValidateController.java | 6 +++--- 4 files changed, 15 insertions(+), 16 deletions(-) rename src/main/java/io/jboot/web/validate/{MatchesForm.java => RegexForm.java} (96%) rename src/main/java/io/jboot/web/validate/{MatchesValidate.java => RegexValidate.java} (94%) diff --git a/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java b/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java index 585ed9f1..14b15520 100644 --- a/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java @@ -45,9 +45,8 @@ public class ParaValidateInterceptor implements FixedInterceptor { return; } - - MatchesValidate matchesValidate = method.getAnnotation(MatchesValidate.class); - if (matchesValidate != null && !validatMatches(inv, matchesValidate)) { + RegexValidate regexValidate = method.getAnnotation(RegexValidate.class); + if (regexValidate != null && !validateRegex(inv, regexValidate)) { return; } @@ -121,17 +120,17 @@ public class ParaValidateInterceptor implements FixedInterceptor { * 正则验证 * * @param inv - * @param matchesValidate + * @param regexValidate * @return */ - private boolean validatMatches(Invocation inv, MatchesValidate matchesValidate) { - MatchesForm[] forms = matchesValidate.value(); + private boolean validateRegex(Invocation inv, RegexValidate regexValidate) { + RegexForm[] forms = regexValidate.value(); if (ArrayUtil.isNullOrEmpty(forms)) { return true; } - for (MatchesForm form : forms) { + for (RegexForm form : forms) { String formName = AnnotationUtil.get(form.name()); String formType = AnnotationUtil.get(form.type()); if (StrUtil.isBlank(formName)) { @@ -159,11 +158,11 @@ public class ParaValidateInterceptor implements FixedInterceptor { if (value == null || !value.trim().matches(form.regex())) { renderError(inv.getController() - , AnnotationUtil.get(matchesValidate.renderType()) + , AnnotationUtil.get(regexValidate.renderType()) , formName , AnnotationUtil.get(form.message()) - , AnnotationUtil.get(matchesValidate.redirectUrl()) - , AnnotationUtil.get(matchesValidate.htmlPath()) + , AnnotationUtil.get(regexValidate.redirectUrl()) + , AnnotationUtil.get(regexValidate.htmlPath()) , form.errorCode() ); return false; diff --git a/src/main/java/io/jboot/web/validate/MatchesForm.java b/src/main/java/io/jboot/web/validate/RegexForm.java similarity index 96% rename from src/main/java/io/jboot/web/validate/MatchesForm.java rename to src/main/java/io/jboot/web/validate/RegexForm.java index 706c958a..2b22f65d 100644 --- a/src/main/java/io/jboot/web/validate/MatchesForm.java +++ b/src/main/java/io/jboot/web/validate/RegexForm.java @@ -25,7 +25,7 @@ import java.lang.annotation.*; @Target(ElementType.METHOD) @Inherited @Retention(RetentionPolicy.RUNTIME) -public @interface MatchesForm { +public @interface RegexForm { String name(); diff --git a/src/main/java/io/jboot/web/validate/MatchesValidate.java b/src/main/java/io/jboot/web/validate/RegexValidate.java similarity index 94% rename from src/main/java/io/jboot/web/validate/MatchesValidate.java rename to src/main/java/io/jboot/web/validate/RegexValidate.java index f05be4bc..2c530bf1 100644 --- a/src/main/java/io/jboot/web/validate/MatchesValidate.java +++ b/src/main/java/io/jboot/web/validate/RegexValidate.java @@ -25,9 +25,9 @@ import java.lang.annotation.*; @Target(ElementType.METHOD) @Inherited @Retention(RetentionPolicy.RUNTIME) -public @interface MatchesValidate { +public @interface RegexValidate { - MatchesForm[] value(); + RegexForm[] value(); String message() default ""; diff --git a/src/test/java/io/jboot/test/validate/ValidateController.java b/src/test/java/io/jboot/test/validate/ValidateController.java index fe3399a3..0a1b6029 100644 --- a/src/test/java/io/jboot/test/validate/ValidateController.java +++ b/src/test/java/io/jboot/test/validate/ValidateController.java @@ -19,18 +19,18 @@ public class ValidateController extends Controller { } - @MatchesValidate(value = @MatchesForm(name = "email", regex = Regex.EMAIL, message = "请输入正确的邮箱地址")) + @RegexValidate(value = @RegexForm(name = "email", regex = Regex.EMAIL, message = "请输入正确的邮箱地址")) public void test2() { renderText("test2"); } - @MatchesValidate(value = @MatchesForm(name = "email", regex = Regex.EMAIL)) + @RegexValidate(value = @RegexForm(name = "email", regex = Regex.EMAIL)) public void test3() { renderText("test3"); } - @MatchesValidate(value = @MatchesForm(name = "email", regex = Regex.EMAIL), renderType = ValidateRenderType.JSON) + @RegexValidate(value = @RegexForm(name = "email", regex = Regex.EMAIL), renderType = ValidateRenderType.JSON) public void test4() { renderText("test4"); } -- Gitee From db79008e0f688edeea79db5de22f3a0c6e3acf97 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 31 Jul 2020 16:17:39 +0800 Subject: [PATCH 0437/1965] v3.3.1 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 06013eaa..d08f466e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.3.0 + 3.3.1 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index a5ded66c..a18ddcb7 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.3.0 + 3.3.1 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index b491a715..c066b868 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.3.0 + 3.3.1 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 66d0be9a..798621de 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.3.0"; + public static String VERSION = "3.3.1"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 4d6edc18523250ab2fa16e261588c427078c512b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 31 Jul 2020 16:18:05 +0800 Subject: [PATCH 0438/1965] v3.3.1 release (^.^)YYa!! --- changes.txt | 9 +++++++++ pom.xml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 3f656ad3..877e8f39 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,12 @@ +jboot v3.3.1: +新增:Seata tcc 的支持 +新增:RegexValidate 注解对 Controller 进行验证的支持 +优化:ClassScanner 排除对 protobuf 扫描 +优化:JbootUndertowConfig 排除对 Jboot.java 的依赖 +优化:升级 JFinal、FastJson 等到最新版本 + + + jboot v3.3.0: 新增:ObjectUtil 工具类,用于对 Object 进行对比等操作 新增:JbootModel.useFirst() 方法,更加方便在读写分离的场景下进行使用 diff --git a/pom.xml b/pom.xml index 1b357806..7efee524 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.1-SNAPSHOT + 3.3.1 jar jboot -- Gitee From 800439da3bf175065cd362056ea70098260aadee Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 3 Aug 2020 12:35:09 +0800 Subject: [PATCH 0439/1965] v3.3.2 snapshot --- pom.xml | 2 +- .../jboot/web/validate/ParaValidateInterceptor.java | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 7efee524..46d51725 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.1 + 3.3.2-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java b/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java index 14b15520..70582e96 100644 --- a/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java @@ -206,22 +206,24 @@ public class ParaValidateInterceptor implements FixedInterceptor { private void renderError(Controller controller, String renderType, String formName, String message, String redirectUrl, String htmlPath, int errorCode) { - String newMessage = StrUtil.isNotBlank(message) ? (formName + " validate failed: " + message) : (formName + " validate failed!"); + String reason = StrUtil.isNotBlank(message) ? (formName + " validate failed: " + message) : (formName + " validate failed!"); switch (renderType) { case ValidateRenderType.DEFAULT: if (RequestUtil.isAjaxRequest(controller.getRequest())) { controller.renderJson( - Ret.fail("message", newMessage) + Ret.fail("message", message) + .set("reason", reason) .set("errorCode", errorCode) .setIfNotNull("formName", formName) ); } else { - controller.renderError(403, new TextRender(newMessage)); + controller.renderError(403, new TextRender(reason)); } break; case ValidateRenderType.JSON: controller.renderJson( - Ret.fail("message", newMessage) + Ret.fail("message", message) + .set("reason", reason) .set("errorCode", errorCode) .setIfNotNull("formName", formName) ); @@ -233,7 +235,7 @@ public class ParaValidateInterceptor implements FixedInterceptor { controller.render(htmlPath); break; case ValidateRenderType.TEXT: - controller.renderText(newMessage); + controller.renderText(message); break; default: throw new IllegalArgumentException("can not process render : " + renderType); -- Gitee From bc38a105d68d1098a412037f7e2c9941b518966a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 3 Aug 2020 19:04:29 +0800 Subject: [PATCH 0440/1965] add JbootActionReporter --- pom.xml | 15 ++ .../jboot/web/handler/JbootActionHandler.java | 4 +- .../web/handler/JbootActionReporter.java | 164 ++++++++++++++++++ 3 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/jboot/web/handler/JbootActionReporter.java diff --git a/pom.xml b/pom.xml index 46d51725..8a7a0a0f 100644 --- a/pom.xml +++ b/pom.xml @@ -167,12 +167,27 @@ ${fst.version} + + + org.javassist + javassist + 3.21.0-GA + + + org.slf4j slf4j-api ${slf4j.version} + + + org.slf4j + slf4j-simple + ${slf4j.version} + + com.alibaba fastjson diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index ddc4a390..0a0d3308 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -114,9 +114,9 @@ public class JbootActionHandler extends ActionHandler { if (devMode) { if (ActionReporter.isReportAfterInvocation(request)) { invokeInvocation(invocation); - ActionReporter.report(target, controller, action); + JbootActionReporter.report(target, controller, action); } else { - ActionReporter.report(target, controller, action); + JbootActionReporter.report(target, controller, action); invokeInvocation(invocation); } } else { diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java new file mode 100644 index 00000000..da0d71ba --- /dev/null +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.jboot.web.handler; + +import com.jfinal.aop.Interceptor; +import com.jfinal.core.Action; +import com.jfinal.core.Const; +import com.jfinal.core.Controller; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import javassist.NotFoundException; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.io.Writer; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Enumeration; + +/** + * ActionReporter + */ +public class JbootActionReporter { + + private static final String title = "\nJFinal-" + Const.JFINAL_VERSION + " action report -------- "; + private static boolean reportAfterInvocation = true; + private static int maxOutputLengthOfParaValue = 512; + private static Writer writer = new SystemOutWriter(); + + private static final ThreadLocal sdf = new ThreadLocal() { + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } + }; + + public static void setReportAfterInvocation(boolean reportAfterInvocation) { + JbootActionReporter.reportAfterInvocation = reportAfterInvocation; + } + + public static void setMaxOutputLengthOfParaValue(int maxOutputLengthOfParaValue) { + if (maxOutputLengthOfParaValue < 16) { + throw new IllegalArgumentException("maxOutputLengthOfParaValue must more than 16"); + } + JbootActionReporter.maxOutputLengthOfParaValue = maxOutputLengthOfParaValue; + } + + public static void setWriter(Writer writer) { + if (writer == null) { + throw new IllegalArgumentException("writer can not be null"); + } + JbootActionReporter.writer = writer; + } + + public static boolean isReportAfterInvocation(HttpServletRequest request) { + if (reportAfterInvocation) { + return true; + } else { + String contentType = request.getContentType(); + if (contentType != null && contentType.toLowerCase().indexOf("multipart") != -1) { + return true; + } else { + return false; + } + } + } + + + + /** + * Report the action + */ + public static final void report(String target, Controller controller, Action action) throws NotFoundException { + CtClass ctClass = ClassPool.getDefault().get(action.getControllerClass().getCanonicalName()); + CtMethod ctMethod = ctClass.getDeclaredMethod(action.getMethodName()); + int lineNumber = ctMethod.getMethodInfo().getLineNumber(0); + + StringBuilder sb = new StringBuilder(title).append(sdf.get().format(new Date())).append(" --------------------------\n"); + sb.append("Url : ").append(controller.getRequest().getMethod()).append(" ").append(target).append("\n"); + Class cc = action.getControllerClass(); + sb.append("Controller : ").append(cc.getName()).append(".(").append(cc.getSimpleName()).append(".java:"+lineNumber+")"); + sb.append("\nMethod : ").append(action.getMethodName()).append("\n"); + + String urlParas = controller.getPara(); + if (urlParas != null) { + sb.append("UrlPara : ").append(urlParas).append("\n"); + } + + Interceptor[] inters = action.getInterceptors(); + if (inters.length > 0) { + sb.append("Interceptor : "); + for (int i=0; i 0) + sb.append("\n "); + Interceptor inter = inters[i]; + Class ic = inter.getClass(); + sb.append(ic.getName()).append(".(").append(ic.getSimpleName()).append(".java:"+lineNumber+")"); + } + sb.append("\n"); + } + + // print all parameters + HttpServletRequest request = controller.getRequest(); + Enumeration e = request.getParameterNames(); + if (e.hasMoreElements()) { + sb.append("Parameter : "); + while (e.hasMoreElements()) { + String name = e.nextElement(); + String[] values = request.getParameterValues(name); + if (values.length == 1) { + sb.append(name).append("="); + if (values[0] != null && values[0].length() > maxOutputLengthOfParaValue) { + sb.append(values[0].substring(0, maxOutputLengthOfParaValue)).append("..."); + } else { + sb.append(values[0]); + } + } + else { + sb.append(name).append("[]={"); + for (int i=0; i 0) + sb.append(","); + sb.append(values[i]); + } + sb.append("}"); + } + sb.append(" "); + } + sb.append("\n"); + } + sb.append("--------------------------------------------------------------------------------\n"); + + try { + writer.write(sb.toString()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + private static class SystemOutWriter extends Writer { + public void write(String str) throws IOException { + System.out.print(str); + } + public void write(char[] cbuf, int off, int len) throws IOException {} + public void flush() throws IOException {} + public void close() throws IOException {} + } +} + + -- Gitee From a0cac30325c99b78e3b58af67b79ecf21863393b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 3 Aug 2020 19:04:39 +0800 Subject: [PATCH 0441/1965] v3.3.2 snapshot --- src/main/java/io/jboot/core/log/Slf4jLogFactory.java | 12 +++++++++++- src/main/java/io/jboot/core/log/Slf4jLogger.java | 3 +-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/core/log/Slf4jLogFactory.java b/src/main/java/io/jboot/core/log/Slf4jLogFactory.java index 0aefb027..b42048bc 100644 --- a/src/main/java/io/jboot/core/log/Slf4jLogFactory.java +++ b/src/main/java/io/jboot/core/log/Slf4jLogFactory.java @@ -29,7 +29,17 @@ public class Slf4jLogFactory extends com.jfinal.log.Slf4jLogFactory { private boolean useJdkLogger; public Slf4jLogFactory() { - this.useJdkLogger = LoggerFactory.getILoggerFactory() instanceof NOPLoggerFactory; + boolean hasStaticLoggerBinder = false; + try { + Class.forName("org.slf4j.impl.StaticLoggerBinder"); + hasStaticLoggerBinder = true; + } catch (ClassNotFoundException e) {} + + if (!hasStaticLoggerBinder) { + useJdkLogger = true; + } else { + this.useJdkLogger = LoggerFactory.getILoggerFactory() instanceof NOPLoggerFactory; + } } @Override diff --git a/src/main/java/io/jboot/core/log/Slf4jLogger.java b/src/main/java/io/jboot/core/log/Slf4jLogger.java index 90393380..a9a0b90b 100644 --- a/src/main/java/io/jboot/core/log/Slf4jLogger.java +++ b/src/main/java/io/jboot/core/log/Slf4jLogger.java @@ -16,7 +16,6 @@ package io.jboot.core.log; import com.jfinal.log.Log; -import com.jfinal.log.Slf4jLog; import io.jboot.exception.JbootExceptionHolder; import org.slf4j.helpers.FormattingTuple; import org.slf4j.helpers.MessageFormatter; @@ -30,7 +29,7 @@ public class Slf4jLogger extends Log { private LocationAwareLogger log; private static final Object[] NULL_ARGS = new Object[0]; - private static final String callerFQCN = Slf4jLog.class.getName(); + private static final String callerFQCN = Slf4jLogger.class.getName(); public Slf4jLogger(LocationAwareLogger log) { this.log = log; -- Gitee From b49992b834e61d7b2488f2034b1f56305ea8c89f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 3 Aug 2020 19:30:47 +0800 Subject: [PATCH 0442/1965] rename ParaValidateInterceptor.java to ValidateInterceptor --- .../fixedinterceptor/FixedInterceptors.java | 4 +- ...erceptor.java => ValidateInterceptor.java} | 42 +++++++++++++++++-- 2 files changed, 41 insertions(+), 5 deletions(-) rename src/main/java/io/jboot/web/validate/{ParaValidateInterceptor.java => ValidateInterceptor.java} (85%) diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java index dad82067..c6b2adc0 100644 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java +++ b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java @@ -23,7 +23,7 @@ import io.jboot.support.seata.interceptor.SeataGlobalTransactionalInterceptor; import io.jboot.support.seata.tcc.TccActionInterceptor; import io.jboot.support.sentinel.SentinelInterceptor; import io.jboot.support.shiro.JbootShiroInterceptor; -import io.jboot.web.validate.ParaValidateInterceptor; +import io.jboot.web.validate.ValidateInterceptor; import io.jboot.web.cors.CORSInterceptor; import java.util.ArrayList; @@ -52,7 +52,7 @@ public class FixedInterceptors { new FixedInterceptorWapper(new SentinelInterceptor(), 9), new FixedInterceptorWapper(new LimiterInterceptor(), 10), new FixedInterceptorWapper(new CORSInterceptor(), 20), - new FixedInterceptorWapper(new ParaValidateInterceptor(), 30), + new FixedInterceptorWapper(new ValidateInterceptor(), 30), new FixedInterceptorWapper(new JwtInterceptor(), 40), new FixedInterceptorWapper(new JbootShiroInterceptor(), 50), new FixedInterceptorWapper(new JbootMetricInterceptor(), 60), diff --git a/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java b/src/main/java/io/jboot/web/validate/ValidateInterceptor.java similarity index 85% rename from src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java rename to src/main/java/io/jboot/web/validate/ValidateInterceptor.java index 70582e96..6d99348d 100644 --- a/src/main/java/io/jboot/web/validate/ParaValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/ValidateInterceptor.java @@ -21,7 +21,8 @@ import com.alibaba.fastjson.JSONPath; import com.jfinal.aop.Invocation; import com.jfinal.core.Controller; import com.jfinal.kit.Ret; -import com.jfinal.render.TextRender; +import com.jfinal.log.Log; +import io.jboot.Jboot; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.ArrayUtil; import io.jboot.utils.RequestUtil; @@ -33,7 +34,9 @@ import java.lang.reflect.Method; /** * 验证拦截器 */ -public class ParaValidateInterceptor implements FixedInterceptor { +public class ValidateInterceptor implements FixedInterceptor { + + private static final Log LOG = Log.getLog("Validate"); @Override public void intercept(Invocation inv) { @@ -42,16 +45,25 @@ public class ParaValidateInterceptor implements FixedInterceptor { EmptyValidate emptyParaValidate = method.getAnnotation(EmptyValidate.class); if (emptyParaValidate != null && !validateEmpty(inv, emptyParaValidate)) { + if (Jboot.isDevMode()){ + LOG.error(buildErrorMessage(inv,"@EmptyValidate")); + } return; } RegexValidate regexValidate = method.getAnnotation(RegexValidate.class); if (regexValidate != null && !validateRegex(inv, regexValidate)) { + if (Jboot.isDevMode()){ + LOG.error(buildErrorMessage(inv,"@RegexValidate")); + } return; } CaptchaValidate captchaValidate = method.getAnnotation(CaptchaValidate.class); if (captchaValidate != null && !validateCaptache(inv, captchaValidate)) { + if (Jboot.isDevMode()){ + LOG.error(buildErrorMessage(inv,"@CaptchaValidate")); + } return; } @@ -59,6 +71,18 @@ public class ParaValidateInterceptor implements FixedInterceptor { } + private static String buildErrorMessage(Invocation inv,String annotation){ + StringBuilder sb = new StringBuilder(); + sb.append("method \"").append(inv.getController().getClass().getName()) + .append(".") + .append(inv.getMethodName()) + .append("()\"") + .append(" has intercepted by annotation ") + .append(annotation); + return sb.toString(); + } + + /** * 非空判断验证 * @@ -217,7 +241,19 @@ public class ParaValidateInterceptor implements FixedInterceptor { .setIfNotNull("formName", formName) ); } else { - controller.renderError(403, new TextRender(reason)); +// controller.render(new ErrorRender(403,null){ +// @Override +// public void render() { +// response.setContentType(contentType); +// try { +// response.getOutputStream().w(reason); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// }); + controller.renderText(reason); + } break; case ValidateRenderType.JSON: -- Gitee From 246d2c0f1d8ec1d7d82d4012c64a203b60fe1791 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 3 Aug 2020 19:43:25 +0800 Subject: [PATCH 0443/1965] upgrade javassist version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8a7a0a0f..52c112ed 100644 --- a/pom.xml +++ b/pom.xml @@ -171,7 +171,7 @@ org.javassist javassist - 3.21.0-GA + 3.27.0-GA -- Gitee From 0c9779feda77fbdef7c7d025abe78700c3c069d6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 3 Aug 2020 20:03:05 +0800 Subject: [PATCH 0444/1965] add JbootActionReporter --- pom.xml | 10 ++++----- .../web/handler/JbootActionReporter.java | 21 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index 52c112ed..8745b767 100644 --- a/pom.xml +++ b/pom.xml @@ -182,11 +182,11 @@ - - org.slf4j - slf4j-simple - ${slf4j.version} - + + + + + com.alibaba diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index da0d71ba..989087a5 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -1,25 +1,24 @@ /** - * Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com). - * + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + *

+ * 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 io.jboot.web.handler; import com.jfinal.aop.Interceptor; import com.jfinal.core.Action; -import com.jfinal.core.Const; import com.jfinal.core.Controller; +import io.jboot.JbootConsts; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; @@ -32,12 +31,14 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.Enumeration; +//import com.jfinal.core.Const; + /** - * ActionReporter + * JbootActionReporter 参考 ActionReporter */ public class JbootActionReporter { - private static final String title = "\nJFinal-" + Const.JFINAL_VERSION + " action report -------- "; + private static final String title = "\nJboot-" + JbootConsts.VERSION + " action report -------- "; private static boolean reportAfterInvocation = true; private static int maxOutputLengthOfParaValue = 512; private static Writer writer = new SystemOutWriter(); @@ -89,7 +90,7 @@ public class JbootActionReporter { CtMethod ctMethod = ctClass.getDeclaredMethod(action.getMethodName()); int lineNumber = ctMethod.getMethodInfo().getLineNumber(0); - StringBuilder sb = new StringBuilder(title).append(sdf.get().format(new Date())).append(" --------------------------\n"); + StringBuilder sb = new StringBuilder(title).append(sdf.get().format(new Date())).append(" -------------------------\n"); sb.append("Url : ").append(controller.getRequest().getMethod()).append(" ").append(target).append("\n"); Class cc = action.getControllerClass(); sb.append("Controller : ").append(cc.getName()).append(".(").append(cc.getSimpleName()).append(".java:"+lineNumber+")"); -- Gitee From cd91c4f63505dbd69fb6668a6354d216af95078c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 3 Aug 2020 20:06:53 +0800 Subject: [PATCH 0445/1965] rename ParaValidateInterceptor.java to ValidateInterceptor --- .../io/jboot/web/validate/ValidateInterceptor.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/main/java/io/jboot/web/validate/ValidateInterceptor.java b/src/main/java/io/jboot/web/validate/ValidateInterceptor.java index 6d99348d..e48c7b43 100644 --- a/src/main/java/io/jboot/web/validate/ValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/ValidateInterceptor.java @@ -241,19 +241,7 @@ public class ValidateInterceptor implements FixedInterceptor { .setIfNotNull("formName", formName) ); } else { -// controller.render(new ErrorRender(403,null){ -// @Override -// public void render() { -// response.setContentType(contentType); -// try { -// response.getOutputStream().w(reason); -// } catch (IOException e) { -// e.printStackTrace(); -// } -// } -// }); controller.renderText(reason); - } break; case ValidateRenderType.JSON: -- Gitee From 28777217f048824bd56225d6aad45a8418c8b5e6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 3 Aug 2020 21:54:35 +0800 Subject: [PATCH 0446/1965] v3.3.2 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d08f466e..b42c9a27 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.3.1 + 3.3.2 ``` diff --git a/changes.txt b/changes.txt index 877e8f39..31ad1e42 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.3.2: +新增:新增 JbootActionReporter 用于代替 JFinal 的 ActionReporter,更精准的地位方法 +优化:重命名 ParaValidateInterceptor 为 ValidateInterceptor +修复:@EmptyValidate @RegexValidate 的 message 在 ajax 上提示不正确的问题 + + + jboot v3.3.1: 新增:Seata tcc 的支持 新增:RegexValidate 注解对 Controller 进行验证的支持 diff --git a/doc/docs/install.md b/doc/docs/install.md index a18ddcb7..445f83e6 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.3.1 + 3.3.2 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index c066b868..7c0afa93 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.3.1 + 3.3.2 ``` -- Gitee From 1fd921643ab49d7564b063b6d06467dc67325945 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 3 Aug 2020 21:54:53 +0800 Subject: [PATCH 0447/1965] v3.3.2 release (^.^)YYa!! --- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8745b767..727b9aed 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.2-SNAPSHOT + 3.3.2 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 798621de..dcf94cdd 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.3.1"; + public static String VERSION = "3.3.2"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 96b81981ddf1ec2354a0d6d7dbfccb265285b621 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 4 Aug 2020 11:14:16 +0800 Subject: [PATCH 0448/1965] v3.3.3 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 727b9aed..124e49b1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.2 + 3.3.3-SNAPSHOT jar jboot -- Gitee From feb7bb1f72b2c65e9fce73f4f72c46ba62b5df45 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 4 Aug 2020 11:15:14 +0800 Subject: [PATCH 0449/1965] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9AJbootActio?= =?UTF-8?q?nReporter=20=E5=AF=B9=20Interceptor=20=E8=BE=93=E5=87=BA?= =?UTF-8?q?=E7=9A=84=E8=A1=8C=E5=8F=B7=E5=92=8C=E6=89=80=E5=9C=A8=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E4=B8=8D=E6=AD=A3=E7=A1=AE=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/handler/JbootActionReporter.java | 267 ++++++++++-------- .../java/io/jboot/test/HelloInterceptor.java | 32 +++ src/test/java/io/jboot/test/HelloWorld.java | 16 ++ 3 files changed, 191 insertions(+), 124 deletions(-) create mode 100644 src/test/java/io/jboot/test/HelloInterceptor.java diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index 989087a5..33000220 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -31,135 +31,154 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.Enumeration; -//import com.jfinal.core.Const; /** * JbootActionReporter 参考 ActionReporter */ public class JbootActionReporter { - - private static final String title = "\nJboot-" + JbootConsts.VERSION + " action report -------- "; - private static boolean reportAfterInvocation = true; - private static int maxOutputLengthOfParaValue = 512; - private static Writer writer = new SystemOutWriter(); - - private static final ThreadLocal sdf = new ThreadLocal() { - protected SimpleDateFormat initialValue() { - return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - } - }; - - public static void setReportAfterInvocation(boolean reportAfterInvocation) { - JbootActionReporter.reportAfterInvocation = reportAfterInvocation; - } - - public static void setMaxOutputLengthOfParaValue(int maxOutputLengthOfParaValue) { - if (maxOutputLengthOfParaValue < 16) { - throw new IllegalArgumentException("maxOutputLengthOfParaValue must more than 16"); - } - JbootActionReporter.maxOutputLengthOfParaValue = maxOutputLengthOfParaValue; - } - - public static void setWriter(Writer writer) { - if (writer == null) { - throw new IllegalArgumentException("writer can not be null"); - } - JbootActionReporter.writer = writer; - } - - public static boolean isReportAfterInvocation(HttpServletRequest request) { - if (reportAfterInvocation) { - return true; - } else { - String contentType = request.getContentType(); - if (contentType != null && contentType.toLowerCase().indexOf("multipart") != -1) { - return true; - } else { - return false; - } - } - } - - - - /** - * Report the action - */ - public static final void report(String target, Controller controller, Action action) throws NotFoundException { - CtClass ctClass = ClassPool.getDefault().get(action.getControllerClass().getCanonicalName()); - CtMethod ctMethod = ctClass.getDeclaredMethod(action.getMethodName()); - int lineNumber = ctMethod.getMethodInfo().getLineNumber(0); - - StringBuilder sb = new StringBuilder(title).append(sdf.get().format(new Date())).append(" -------------------------\n"); - sb.append("Url : ").append(controller.getRequest().getMethod()).append(" ").append(target).append("\n"); - Class cc = action.getControllerClass(); - sb.append("Controller : ").append(cc.getName()).append(".(").append(cc.getSimpleName()).append(".java:"+lineNumber+")"); - sb.append("\nMethod : ").append(action.getMethodName()).append("\n"); - - String urlParas = controller.getPara(); - if (urlParas != null) { - sb.append("UrlPara : ").append(urlParas).append("\n"); - } - - Interceptor[] inters = action.getInterceptors(); - if (inters.length > 0) { - sb.append("Interceptor : "); - for (int i=0; i 0) - sb.append("\n "); - Interceptor inter = inters[i]; - Class ic = inter.getClass(); - sb.append(ic.getName()).append(".(").append(ic.getSimpleName()).append(".java:"+lineNumber+")"); - } - sb.append("\n"); - } - - // print all parameters - HttpServletRequest request = controller.getRequest(); - Enumeration e = request.getParameterNames(); - if (e.hasMoreElements()) { - sb.append("Parameter : "); - while (e.hasMoreElements()) { - String name = e.nextElement(); - String[] values = request.getParameterValues(name); - if (values.length == 1) { - sb.append(name).append("="); - if (values[0] != null && values[0].length() > maxOutputLengthOfParaValue) { - sb.append(values[0].substring(0, maxOutputLengthOfParaValue)).append("..."); - } else { - sb.append(values[0]); - } - } - else { - sb.append(name).append("[]={"); - for (int i=0; i 0) - sb.append(","); - sb.append(values[i]); - } - sb.append("}"); - } - sb.append(" "); - } - sb.append("\n"); - } - sb.append("--------------------------------------------------------------------------------\n"); - - try { - writer.write(sb.toString()); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - - private static class SystemOutWriter extends Writer { - public void write(String str) throws IOException { - System.out.print(str); - } - public void write(char[] cbuf, int off, int len) throws IOException {} - public void flush() throws IOException {} - public void close() throws IOException {} - } + + private static final String title = "\nJboot-" + JbootConsts.VERSION + " action report -------- "; + private static boolean reportAfterInvocation = true; + private static int maxOutputLengthOfParaValue = 512; + private static Writer writer = new SystemOutWriter(); + + private static final ThreadLocal sdf = new ThreadLocal() { + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } + }; + + public static void setReportAfterInvocation(boolean reportAfterInvocation) { + JbootActionReporter.reportAfterInvocation = reportAfterInvocation; + } + + public static void setMaxOutputLengthOfParaValue(int maxOutputLengthOfParaValue) { + if (maxOutputLengthOfParaValue < 16) { + throw new IllegalArgumentException("maxOutputLengthOfParaValue must more than 16"); + } + JbootActionReporter.maxOutputLengthOfParaValue = maxOutputLengthOfParaValue; + } + + public static void setWriter(Writer writer) { + if (writer == null) { + throw new IllegalArgumentException("writer can not be null"); + } + JbootActionReporter.writer = writer; + } + + public static boolean isReportAfterInvocation(HttpServletRequest request) { + if (reportAfterInvocation) { + return true; + } else { + String contentType = request.getContentType(); + if (contentType != null && contentType.toLowerCase().indexOf("multipart") != -1) { + return true; + } else { + return false; + } + } + } + + + /** + * Report the action + */ + public static final void report(String target, Controller controller, Action action) throws NotFoundException { + CtClass ctClass = ClassPool.getDefault().get(action.getControllerClass().getName()); + CtMethod ctMethod = ctClass.getDeclaredMethod(action.getMethodName()); + int lineNumber = ctMethod.getMethodInfo().getLineNumber(0); + + StringBuilder sb = new StringBuilder(title).append(sdf.get().format(new Date())).append(" -------------------------\n"); + sb.append("Url : ").append(controller.getRequest().getMethod()).append(" ").append(target).append("\n"); + Class cc = action.getControllerClass(); + sb.append("Controller : ").append(cc.getName()).append(".(").append(getClassFileName(cc)).append(".java:" + lineNumber + ")"); + sb.append("\nMethod : ").append(action.getMethodName()).append("\n"); + + String urlParas = controller.getPara(); + if (urlParas != null) { + sb.append("UrlPara : ").append(urlParas).append("\n"); + } + + Interceptor[] inters = action.getInterceptors(); + if (inters.length > 0) { + sb.append("Interceptor : "); + for (int i = 0; i < inters.length; i++) { + if (i > 0) { + sb.append("\n "); + } + Interceptor inter = inters[i]; + Class ic = inter.getClass(); + + CtClass icClass = ClassPool.getDefault().get(ic.getName()); + CtMethod icMethod = icClass.getDeclaredMethod("intercept"); + int icLineNumber = icMethod.getMethodInfo().getLineNumber(0); + sb.append(ic.getName()).append(".(").append(getClassFileName(ic)).append(".java:" + icLineNumber + ")"); + } + sb.append("\n"); + } + + // print all parameters + HttpServletRequest request = controller.getRequest(); + Enumeration e = request.getParameterNames(); + if (e.hasMoreElements()) { + sb.append("Parameter : "); + while (e.hasMoreElements()) { + String name = e.nextElement(); + String[] values = request.getParameterValues(name); + if (values.length == 1) { + sb.append(name).append("="); + if (values[0] != null && values[0].length() > maxOutputLengthOfParaValue) { + sb.append(values[0].substring(0, maxOutputLengthOfParaValue)).append("..."); + } else { + sb.append(values[0]); + } + } else { + sb.append(name).append("[]={"); + for (int i = 0; i < values.length; i++) { + if (i > 0) + sb.append(","); + sb.append(values[i]); + } + sb.append("}"); + } + sb.append(" "); + } + sb.append("\n"); + } + sb.append("--------------------------------------------------------------------------------\n"); + + try { + writer.write(sb.toString()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + + private static String getClassFileName(Class clazz) { + String classFileName = clazz.getName(); + if (classFileName.contains("$")) { + int indexOf = classFileName.contains(".") ? classFileName.lastIndexOf(".") + 1 : 0; + return classFileName.substring(indexOf, classFileName.indexOf("$")); + } else { + return clazz.getSimpleName(); + } + } + + private static class SystemOutWriter extends Writer { + public void write(String str) throws IOException { + System.out.print(str); + } + + public void write(char[] cbuf, int off, int len) throws IOException { + } + + public void flush() throws IOException { + } + + public void close() throws IOException { + } + } } diff --git a/src/test/java/io/jboot/test/HelloInterceptor.java b/src/test/java/io/jboot/test/HelloInterceptor.java new file mode 100644 index 00000000..5768c58b --- /dev/null +++ b/src/test/java/io/jboot/test/HelloInterceptor.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.test; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2019/12/12 + */ +public class HelloInterceptor implements Interceptor { + + @Override + public void intercept(Invocation inv) { + System.out.println("HelloInterceptor.intercept"); + inv.invoke(); + } +} \ No newline at end of file diff --git a/src/test/java/io/jboot/test/HelloWorld.java b/src/test/java/io/jboot/test/HelloWorld.java index 1a5f665a..64a0aee5 100644 --- a/src/test/java/io/jboot/test/HelloWorld.java +++ b/src/test/java/io/jboot/test/HelloWorld.java @@ -15,6 +15,9 @@ */ package io.jboot.test; +import com.jfinal.aop.Before; +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; import io.jboot.app.JbootApplication; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; @@ -26,6 +29,7 @@ import io.jboot.web.controller.annotation.RequestMapping; @RequestMapping("/hello") public class HelloWorld extends JbootController { + @Before({MyInterceptor.class,HelloInterceptor.class}) public void index(){ renderText("hello world"); } @@ -37,4 +41,16 @@ public class HelloWorld extends JbootController { public static void main(String[] args){ JbootApplication.run(args); } + + + public static class MyInterceptor implements Interceptor { + + @Override + public void intercept(Invocation inv) { + System.out.println("MyInterceptor.intercept"); + inv.invoke(); + } + } + + } \ No newline at end of file -- Gitee From b68f12e81c9ce0b73b60b36928d339b474e4dc2c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 4 Aug 2020 11:31:10 +0800 Subject: [PATCH 0450/1965] v3.3.3 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 5 +++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b42c9a27..7b758520 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.3.2 + 3.3.3 ``` diff --git a/changes.txt b/changes.txt index 31ad1e42..5e9d15d2 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,8 @@ +jboot v3.3.3: +修复:JbootActionReporter 对拦截器 Interceptor 输出的行号不正确的问题 + + + jboot v3.3.2: 新增:新增 JbootActionReporter 用于代替 JFinal 的 ActionReporter,更精准的地位方法 优化:重命名 ParaValidateInterceptor 为 ValidateInterceptor diff --git a/doc/docs/install.md b/doc/docs/install.md index 445f83e6..f895bc66 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.3.2 + 3.3.3 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 7c0afa93..9b42198e 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.3.2 + 3.3.3 ``` -- Gitee From 9fa85c1a8513e0d2cdb9d58f8bdf75f577165454 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 4 Aug 2020 11:31:28 +0800 Subject: [PATCH 0451/1965] v3.3.3 release (^.^)YYa!! --- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 124e49b1..7fcd6b20 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.3-SNAPSHOT + 3.3.3 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index dcf94cdd..828b42f9 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.3.2"; + public static String VERSION = "3.3.3"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 2dfa9bc1389b5f37c2d53fcbc687ade2868c1223 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 5 Aug 2020 11:53:34 +0800 Subject: [PATCH 0452/1965] v3.3.4 snapshot --- doc/docs/config.md | 4 ++-- pom.xml | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/docs/config.md b/doc/docs/config.md index f5f22ec1..ebeb333e 100644 --- a/doc/docs/config.md +++ b/doc/docs/config.md @@ -209,7 +209,7 @@ Component1Config config = Jboot.config(Component1Config.class); com.alibaba.nacos nacos-client - 1.3.1 + 1.3.2 ``` @@ -267,7 +267,7 @@ jboot.config.nacos.enableRemoteSyncConfig = xxx com.ctrip.framework.apollo apollo-client - 1.6.2 + 1.7.0 ``` diff --git a/pom.xml b/pom.xml index 7fcd6b20..88e41731 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.3 + 3.3.4-SNAPSHOT jar jboot @@ -47,7 +47,7 @@ 2.6 3.3.0 3.4.5 - 2.11.1 + 2.11.2 5.1.49 2.1.3.Final 1.7.30 @@ -246,7 +246,7 @@ com.alibaba druid - 1.1.22 + 1.1.23 provided @@ -283,14 +283,14 @@ com.ctrip.framework.apollo apollo-client - 1.6.2 + 1.7.0 provided com.alibaba.nacos nacos-client - 1.3.1 + 1.3.2 provided -- Gitee From c17d3e19f108e7320e7c060b7937de0e5b0e780a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 6 Aug 2020 20:18:16 +0800 Subject: [PATCH 0453/1965] fixed JbootActionReporter error sometime --- .../web/handler/JbootActionReporter.java | 11 +- .../web/handler/JbootActionReporterUtil.java | 142 ++++++++++++++++++ .../io/jboot/test/db/simple/DbController.java | 10 +- .../test/db/simple/SuperDbController.java | 19 +++ 4 files changed, 169 insertions(+), 13 deletions(-) create mode 100644 src/main/java/io/jboot/web/handler/JbootActionReporterUtil.java create mode 100644 src/test/java/io/jboot/test/db/simple/SuperDbController.java diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index 33000220..6f82597d 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -85,12 +85,13 @@ public class JbootActionReporter { */ public static final void report(String target, Controller controller, Action action) throws NotFoundException { CtClass ctClass = ClassPool.getDefault().get(action.getControllerClass().getName()); - CtMethod ctMethod = ctClass.getDeclaredMethod(action.getMethodName()); + String desc = JbootActionReporterUtil.getMethodDescWithoutName(action.getMethod()); + CtMethod ctMethod = ctClass.getMethod(action.getMethodName(), desc); int lineNumber = ctMethod.getMethodInfo().getLineNumber(0); StringBuilder sb = new StringBuilder(title).append(sdf.get().format(new Date())).append(" -------------------------\n"); sb.append("Url : ").append(controller.getRequest().getMethod()).append(" ").append(target).append("\n"); - Class cc = action.getControllerClass(); + Class cc = action.getMethod().getDeclaringClass(); sb.append("Controller : ").append(cc.getName()).append(".(").append(getClassFileName(cc)).append(".java:" + lineNumber + ")"); sb.append("\nMethod : ").append(action.getMethodName()).append("\n"); @@ -107,12 +108,12 @@ public class JbootActionReporter { sb.append("\n "); } Interceptor inter = inters[i]; - Class ic = inter.getClass(); + Class ic = inter.getClass(); CtClass icClass = ClassPool.getDefault().get(ic.getName()); - CtMethod icMethod = icClass.getDeclaredMethod("intercept"); + CtMethod icMethod = icClass.getMethod("intercept", "(Lcom/jfinal/aop/Invocation;)V"); int icLineNumber = icMethod.getMethodInfo().getLineNumber(0); - sb.append(ic.getName()).append(".(").append(getClassFileName(ic)).append(".java:" + icLineNumber + ")"); + sb.append(icMethod.getDeclaringClass().getName()).append(".(").append(getClassFileName(ic)).append(".java:" + icLineNumber + ")"); } sb.append("\n"); } diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporterUtil.java b/src/main/java/io/jboot/web/handler/JbootActionReporterUtil.java new file mode 100644 index 00000000..d3628814 --- /dev/null +++ b/src/main/java/io/jboot/web/handler/JbootActionReporterUtil.java @@ -0,0 +1,142 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.jboot.web.handler; + + +import java.lang.reflect.Method; + + +/** + * 参考 https://github.com/apache/dubbo/blob/master/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java + */ +public final class JbootActionReporterUtil { + + /** + * void(V). + */ + public static final char JVM_VOID = 'V'; + + /** + * boolean(Z). + */ + public static final char JVM_BOOLEAN = 'Z'; + + /** + * byte(B). + */ + public static final char JVM_BYTE = 'B'; + + /** + * char(C). + */ + public static final char JVM_CHAR = 'C'; + + /** + * double(D). + */ + public static final char JVM_DOUBLE = 'D'; + + /** + * float(F). + */ + public static final char JVM_FLOAT = 'F'; + + /** + * int(I). + */ + public static final char JVM_INT = 'I'; + + /** + * long(J). + */ + public static final char JVM_LONG = 'J'; + + /** + * short(S). + */ + public static final char JVM_SHORT = 'S'; + + + + + + /** + * get class desc. + * boolean[].class => "[Z" + * Object.class => "Ljava/lang/Object;" + * + * @param c class. + * @return desc. + */ + public static String getDesc(Class c) { + StringBuilder ret = new StringBuilder(); + + while (c.isArray()) { + ret.append('['); + c = c.getComponentType(); + } + + if (c.isPrimitive()) { + String t = c.getName(); + if ("void".equals(t)) { + ret.append(JVM_VOID); + } else if ("boolean".equals(t)) { + ret.append(JVM_BOOLEAN); + } else if ("byte".equals(t)) { + ret.append(JVM_BYTE); + } else if ("char".equals(t)) { + ret.append(JVM_CHAR); + } else if ("double".equals(t)) { + ret.append(JVM_DOUBLE); + } else if ("float".equals(t)) { + ret.append(JVM_FLOAT); + } else if ("int".equals(t)) { + ret.append(JVM_INT); + } else if ("long".equals(t)) { + ret.append(JVM_LONG); + } else if ("short".equals(t)) { + ret.append(JVM_SHORT); + } + } else { + ret.append('L'); + ret.append(c.getName().replace('.', '/')); + ret.append(';'); + } + return ret.toString(); + } + + + /** + * get method desc. + * "(I)I", "()V", "(Ljava/lang/String;Z)V" + * + * @param m method. + * @return desc. + */ + public static String getMethodDescWithoutName(Method m) { + StringBuilder ret = new StringBuilder(); + ret.append('('); + Class[] parameterTypes = m.getParameterTypes(); + for (int i = 0; i < parameterTypes.length; i++) { + ret.append(getDesc(parameterTypes[i])); + } + ret.append(')').append(getDesc(m.getReturnType())); + return ret.toString(); + } + + +} \ No newline at end of file diff --git a/src/test/java/io/jboot/test/db/simple/DbController.java b/src/test/java/io/jboot/test/db/simple/DbController.java index 8a453642..537201e3 100644 --- a/src/test/java/io/jboot/test/db/simple/DbController.java +++ b/src/test/java/io/jboot/test/db/simple/DbController.java @@ -1,20 +1,18 @@ package io.jboot.test.db.simple; import com.jfinal.kit.Ret; -import com.jfinal.plugin.activerecord.Db; import com.jfinal.plugin.activerecord.Record; import io.jboot.app.JbootApplication; import io.jboot.db.JbootDb; import io.jboot.db.model.Columns; import io.jboot.test.db.model.User; -import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; import java.util.Arrays; import java.util.List; @RequestMapping("/db") -public class DbController extends JbootController { +public class DbController extends SuperDbController { public static void main(String[] args) { @@ -39,12 +37,8 @@ public class DbController extends JbootController { } - public void index() { - List records = Db.find("select * from `user`"); - renderJson(records); - } - public void find1(){ + public void find1(User invocation){ User dao = new User(); diff --git a/src/test/java/io/jboot/test/db/simple/SuperDbController.java b/src/test/java/io/jboot/test/db/simple/SuperDbController.java new file mode 100644 index 00000000..0e5baf6b --- /dev/null +++ b/src/test/java/io/jboot/test/db/simple/SuperDbController.java @@ -0,0 +1,19 @@ +package io.jboot.test.db.simple; + +import com.jfinal.plugin.activerecord.Db; +import com.jfinal.plugin.activerecord.Record; +import io.jboot.web.controller.JbootController; + +import java.util.List; + +public class SuperDbController extends JbootController { + + + + + public void index() { + List records = Db.find("select * from `user`"); + renderJson(records); + } + +} -- Gitee From 238fd8e73ae4ec0799e2944cdc6ee29fbed5e575 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 6 Aug 2020 21:04:05 +0800 Subject: [PATCH 0454/1965] optimize JbootHttpImpl post data --- .../io/jboot/components/http/jboot/JbootHttpImpl.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index e967874c..d1dd3869 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -75,12 +75,11 @@ public class JbootHttpImpl implements JbootHttp { else { String postContent = request.getPostContent(); if (StrUtil.isNotEmpty(postContent)) { - DataOutputStream dos = new DataOutputStream(connection.getOutputStream()); - dos.write(postContent.getBytes(request.getCharset())); - dos.flush(); - dos.close(); + try (OutputStream outStream = connection.getOutputStream();) { + outStream.write(postContent.getBytes(request.getCharset())); + outStream.flush(); + } } - } } else { connection.setInstanceFollowRedirects(true); -- Gitee From 86991a794a1de780432ae30ab78712cdd036983b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 6 Aug 2020 21:05:30 +0800 Subject: [PATCH 0455/1965] v3.3.4 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7b758520..3d0dabed 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.3.3 + 3.3.4 ``` diff --git a/changes.txt b/changes.txt index 5e9d15d2..f2a92754 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.3.4: +修复:当 Action 定义在父类后,JbootActionReporter 获取不到 Method 而出错的问题 +优化:升级 nacos 和 Apollo 客户端到最新版本 +优化:JbootHttpImpl Post 提交数据资源可能存在不正常关闭的情况 + + + jboot v3.3.3: 修复:JbootActionReporter 对拦截器 Interceptor 输出的行号不正确的问题 diff --git a/doc/docs/install.md b/doc/docs/install.md index f895bc66..82cdd1e8 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.3.3 + 3.3.4 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 9b42198e..c685e139 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.3.3 + 3.3.4 ``` -- Gitee From 9133d1e5ff3880c6d29d08ab6a337f8a3fe8d734 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 6 Aug 2020 21:05:44 +0800 Subject: [PATCH 0456/1965] fixed JbootActionReporter error sometime --- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 88e41731..842eecd4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.4-SNAPSHOT + 3.3.4 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 828b42f9..6690d8d0 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.3.3"; + public static String VERSION = "3.3.4"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From d7303dac0f2ad47720942c3d633d2faf015f1bef Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 6 Aug 2020 21:24:01 +0800 Subject: [PATCH 0457/1965] v3.3.5 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 842eecd4..77c7ef06 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.4 + 3.3.5-SNAPSHOT jar jboot -- Gitee From e330a78d075f9bc8d1430e6c2e27be295df03720 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 12 Aug 2020 15:57:30 +0800 Subject: [PATCH 0458/1965] =?UTF-8?q?fixed=EF=BC=9A=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E9=97=A8=E6=88=B7=E7=BD=91=E5=85=B3=E4=B8=8B=E8=BD=BD=E6=96=87?= =?UTF-8?q?=E4=BB=B6=20=E6=88=96=E8=80=85=20=E6=B8=B2=E6=9F=93=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E5=8F=AF=E8=83=BD=E5=87=BA=E7=8E=B0=E4=B9=B1=E7=A0=81?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/gateway/GatewayHttpProxy.java | 56 ++++++++++--------- .../jboot/test/gateway/GatewayController.java | 19 ++++--- src/test/resources/jboot.properties | 1 + 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index d0fd418d..33656f6c 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -135,21 +135,22 @@ public class GatewayHttpProxy { private void copyConnStreamToResponse(HttpURLConnection conn, HttpServletResponse resp) throws IOException { + if (resp.isCommitted()) { + return; + } + InputStream inStream = null; - InputStreamReader reader = null; + OutputStream outStream = null; try { - if (!resp.isCommitted()) { - PrintWriter writer = resp.getWriter(); - inStream = getInputStream(conn); - reader = new InputStreamReader(inStream); - int len; - char[] buffer = new char[1024]; - while ((len = reader.read(buffer)) != -1) { - writer.write(buffer, 0, len); - } + inStream = getInputStream(conn); + outStream = resp.getOutputStream(); + byte[] buffer = new byte[1024]; + for (int len = -1; (len = inStream.read(buffer)) != -1; ) { + outStream.write(buffer, 0, len); } + outStream.flush(); } finally { - quetlyClose(inStream, reader); + quetlyClose(inStream, outStream); } } @@ -167,27 +168,35 @@ public class GatewayHttpProxy { private void configResponse(HttpServletResponse resp, HttpURLConnection conn) throws IOException { - resp.setContentType(contentType); + resp.setStatus(conn.getResponseCode()); + //conn 是否已经指定了 contentType,如果指定了,就用 conn 的,否则就用自己配置的 + boolean isContentTypeSetted = false; + Map> headerFields = conn.getHeaderFields(); if (headerFields != null && !headerFields.isEmpty()) { Set headerNames = headerFields.keySet(); for (String headerName : headerNames) { //需要排除 Content-Encoding,因为 Server 可能已经使用 gzip 压缩,但是此代理已经对 gzip 内容进行解压了 - //需要排除 Content-Type,因为会可能会进行多次设置 - if (StrUtil.isBlank(headerName) - || "Content-Encoding".equalsIgnoreCase(headerName) - || "Content-Type".equalsIgnoreCase(headerName)) { + if (StrUtil.isBlank(headerName) || "Content-Encoding".equalsIgnoreCase(headerName)) { continue; - } else { - String headerFieldValue = conn.getHeaderField(headerName); - if (StrUtil.isNotBlank(headerFieldValue)) { - resp.setHeader(headerName, headerFieldValue); + } + + String headerFieldValue = conn.getHeaderField(headerName); + if (StrUtil.isNotBlank(headerFieldValue)) { + resp.setHeader(headerName, headerFieldValue); + if ("Content-Type".equalsIgnoreCase(headerName)) { + isContentTypeSetted = true; } } } } + + //conn 没有 Content-Type,需要设置为手动配置的内容 + if (!isContentTypeSetted) { + resp.setContentType(contentType); + } } private static InputStream getInputStream(HttpURLConnection conn) throws IOException { @@ -271,12 +280,7 @@ public class GatewayHttpProxy { } }; - private static HostnameVerifier hnv = new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - }; + private static HostnameVerifier hnv = (hostname, session) -> true; public Exception getException() { diff --git a/src/test/java/io/jboot/test/gateway/GatewayController.java b/src/test/java/io/jboot/test/gateway/GatewayController.java index 7cca21d8..58cc5f9e 100644 --- a/src/test/java/io/jboot/test/gateway/GatewayController.java +++ b/src/test/java/io/jboot/test/gateway/GatewayController.java @@ -1,11 +1,9 @@ package io.jboot.test.gateway; +import com.jfinal.kit.Ret; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; -import java.util.HashMap; -import java.util.Map; - /** * @author michael yang (fuhai999@gmail.com) * @Date: 2020/4/5 @@ -13,14 +11,17 @@ import java.util.Map; @RequestMapping("/gateway") public class GatewayController extends JbootController { - public void index(){ - - } public void render(){ - Map map = new HashMap(); - map.putAll(getParas()); - renderJson(map); +// Map map = new HashMap(); +// map.putAll(getParas()); +// renderJson(Ret.ok().put("data",map)); + + renderJson(Ret.ok()); + +// renderFile(new File("/Users/michael/Desktop/NewFile.txt")); + +// renderCaptcha(); } } diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index e2cd4644..0ad9e61e 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -41,6 +41,7 @@ config.test.test.ccc.type = type3 jboot.gateway.enable = true jboot.gateway.uri = http://127.0.0.1:9999/gateway/render jboot.gateway.pathEquals = /gateway +#jboot.gateway.proxyContentType = image/jpeg jboot.rpc.dubbo.consumer.timeout = 55555 -- Gitee From eefaa85379154693e05756b1488824ba901cf10a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 12 Aug 2020 17:48:26 +0800 Subject: [PATCH 0459/1965] optimize http component --- .../components/http/JbootHttpRequest.java | 5 ++ .../components/http/JbootHttpResponse.java | 23 +++++---- .../components/http/jboot/JbootHttpImpl.java | 51 +++++++++---------- .../components/http/okhttp/OKHttpImpl.java | 5 +- 4 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/main/java/io/jboot/components/http/JbootHttpRequest.java b/src/main/java/io/jboot/components/http/JbootHttpRequest.java index 241cdeb2..837f05ee 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpRequest.java +++ b/src/main/java/io/jboot/components/http/JbootHttpRequest.java @@ -308,4 +308,9 @@ public class JbootHttpRequest { setRequestUrl(originUrl); } + + + public boolean isHttps() { + return requestUrl != null && requestUrl.toLowerCase().startsWith("https"); + } } diff --git a/src/main/java/io/jboot/components/http/JbootHttpResponse.java b/src/main/java/io/jboot/components/http/JbootHttpResponse.java index f76fd504..6af5f194 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpResponse.java +++ b/src/main/java/io/jboot/components/http/JbootHttpResponse.java @@ -25,7 +25,7 @@ public class JbootHttpResponse { private static final Log log = Log.getLog(JbootHttpResponse.class); private String content; - private OutputStream outputStream; + private OutputStream contentStream; private File file; private Throwable error; private Map> headers; @@ -33,7 +33,7 @@ public class JbootHttpResponse { private String contentType; public JbootHttpResponse() { - this.outputStream = new ByteArrayOutputStream(); + this.contentStream = new ByteArrayOutputStream(); } public JbootHttpResponse(File file) { @@ -47,7 +47,7 @@ public class JbootHttpResponse { try { this.file = file; - this.outputStream = new FileOutputStream(file); + this.contentStream = new FileOutputStream(file); } catch (Exception e) { throw new RuntimeException(e); } @@ -63,8 +63,8 @@ public class JbootHttpResponse { if (content != null) { return content; } - if (outputStream != null && outputStream instanceof ByteArrayOutputStream) { - return new String(((ByteArrayOutputStream) outputStream).toByteArray()); + if (contentStream != null && contentStream instanceof ByteArrayOutputStream) { + return new String(((ByteArrayOutputStream) contentStream).toByteArray()); } return null; } @@ -79,11 +79,11 @@ public class JbootHttpResponse { * * @param inputStream */ - public void pipe(InputStream inputStream) { + public void copyStream(InputStream inputStream) { try { byte[] buffer = new byte[1024]; for (int len = 0; (len = inputStream.read(buffer)) > 0; ) { - outputStream.write(buffer, 0, len); + contentStream.write(buffer, 0, len); } } catch (Throwable throwable) { log.error(throwable.toString(), throwable); @@ -94,17 +94,18 @@ public class JbootHttpResponse { /** * 结束response和释放资源 */ - public void finish() { - if (outputStream != null) { + public void close() { + if (contentStream != null) { try { - outputStream.flush(); - outputStream.close(); + contentStream.flush(); + contentStream.close(); } catch (IOException e) { e.printStackTrace(); } } } + public boolean isNotError() { return !isError(); } diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index d1dd3869..a5e77364 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -53,13 +53,17 @@ public class JbootHttpImpl implements JbootHttp { private void doProcess(JbootHttpRequest request, JbootHttpResponse response) { HttpURLConnection connection = null; - InputStream stream = null; + InputStream inStream = null; try { + //获取 http 链接 connection = getConnection(request); + + //配置 http 链接 configConnection(connection, request); + //post 请求 if (request.isPostRequest()) { connection.setRequestMethod("POST"); @@ -71,6 +75,7 @@ public class JbootHttpImpl implements JbootHttp { uploadData(request, connection); } } + //处理正常的post提交 else { String postContent = request.getPostContent(); @@ -81,31 +86,36 @@ public class JbootHttpImpl implements JbootHttp { } } } - } else { + } + + //get 请求 + else { connection.setInstanceFollowRedirects(true); connection.connect(); } - stream = getInputStream(connection); + inStream = getInputStream(connection); response.setContentType(connection.getContentType()); response.setResponseCode(connection.getResponseCode()); response.setHeaders(connection.getHeaderFields()); - response.pipe(stream); - response.finish(); + response.copyStream(inStream); } catch (Throwable ex) { LOG.warn(ex.toString(), ex); response.setError(ex); } finally { + + response.close(); + if (connection != null) { connection.disconnect(); } - if (stream != null) { + if (inStream != null) { try { - stream.close(); + inStream.close(); } catch (IOException e) { e.printStackTrace(); } @@ -194,23 +204,15 @@ public class JbootHttpImpl implements JbootHttp { } } - private static HttpURLConnection getConnection(JbootHttpRequest request) { - try { - if (request.isPostRequest() == false) { - request.initGetUrl(); - } - if (request.getRequestUrl().toLowerCase().startsWith("https")) { - return getHttpsConnection(request); - } else { - return getHttpConnection(request.getRequestUrl()); - } - } catch (Throwable ex) { - throw new JbootException(ex); + private static HttpURLConnection getConnection(JbootHttpRequest request) throws Exception { + if (request.isPostRequest() == false) { + request.initGetUrl(); } + return request.isHttps() ? getHttpsConnection(request) : getHttpConnection(request); } - private static HttpURLConnection getHttpConnection(String urlStr) throws Exception { - URL url = new URL(urlStr); + private static HttpURLConnection getHttpConnection(JbootHttpRequest request) throws Exception { + URL url = new URL(request.getRequestUrl()); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); return conn; } @@ -265,12 +267,7 @@ public class JbootHttpImpl implements JbootHttp { } }; - private static HostnameVerifier hnv = new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - }; + private static HostnameVerifier hnv = (hostname, session) -> true; } diff --git a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java index 3db1242f..0f9c30a1 100644 --- a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java +++ b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java @@ -70,6 +70,8 @@ public class OKHttpImpl implements JbootHttp { } catch (Throwable ex) { LOG.error(ex.toString(), ex); response.setError(ex); + }finally { + response.close(); } } @@ -121,8 +123,7 @@ public class OKHttpImpl implements JbootHttp { Response okHttpResponse = call.execute(); response.setResponseCode(okHttpResponse.code()); response.setContentType(okHttpResponse.body().contentType().type()); - response.pipe(okHttpResponse.body().byteStream()); - response.finish(); + response.copyStream(okHttpResponse.body().byteStream()); } -- Gitee From 4327e95794168f3d6582910f255b13691c6d03dd Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 12 Aug 2020 18:09:11 +0800 Subject: [PATCH 0460/1965] optimize http component --- .../components/http/JbootHttpResponse.java | 11 ++++++++++ .../components/http/jboot/JbootHttpImpl.java | 1 - .../io/jboot/test/http/HttpUtilTester.java | 22 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/test/java/io/jboot/test/http/HttpUtilTester.java diff --git a/src/main/java/io/jboot/components/http/JbootHttpResponse.java b/src/main/java/io/jboot/components/http/JbootHttpResponse.java index 6af5f194..d7a9b6e1 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpResponse.java +++ b/src/main/java/io/jboot/components/http/JbootHttpResponse.java @@ -154,4 +154,15 @@ public class JbootHttpResponse { this.contentType = contentType; } + + @Override + public String toString() { + return "JbootHttpResponse{" + + "\nfile=" + file + + "\nheaders=" + headers + + "\nresponseCode=" + responseCode + + "\ncontentType=" + contentType + + "\ncontent=" + getContent() + + "\n}"; + } } diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index a5e77364..9f7cc06e 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -66,7 +66,6 @@ public class JbootHttpImpl implements JbootHttp { //post 请求 if (request.isPostRequest()) { - connection.setRequestMethod("POST"); connection.setDoOutput(true); //处理文件上传的post提交 diff --git a/src/test/java/io/jboot/test/http/HttpUtilTester.java b/src/test/java/io/jboot/test/http/HttpUtilTester.java new file mode 100644 index 00000000..5b45b9e3 --- /dev/null +++ b/src/test/java/io/jboot/test/http/HttpUtilTester.java @@ -0,0 +1,22 @@ +package io.jboot.test.http; + +import io.jboot.components.http.JbootHttpManager; +import io.jboot.components.http.JbootHttpRequest; +import io.jboot.components.http.JbootHttpResponse; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public class HttpUtilTester { + + public static void main(String[] args) { + + JbootHttpRequest request = JbootHttpRequest.create("http://www.baidu.com", null, JbootHttpRequest.METHOD_GET); + JbootHttpResponse response = JbootHttpManager.me().getJbootHttp().handle(request); + + System.out.println(response.getHeaders().get("Location").get(0)); + System.out.println(response); + } + + +} \ No newline at end of file -- Gitee From d79dc3f5a11fb8fd0f5713f1f4d5b538ab161520 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 14 Aug 2020 12:08:46 +0800 Subject: [PATCH 0461/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=20JbootActionRepor?= =?UTF-8?q?ter=EF=BC=8C=E6=8C=87=E5=87=BA=E4=B8=8D=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E7=9A=84=E6=8B=A6=E6=88=AA=E5=99=A8=EF=BC=8C=E5=B9=B6=E6=89=93?= =?UTF-8?q?=E5=8D=B0=E5=BD=93=E5=89=8D=20Controller=20action=20=E7=9A=84?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/web/handler/ConsoleColor.java | 107 ++++++++++++++++++ .../jboot/web/handler/JbootActionHandler.java | 43 ++++--- .../web/handler/JbootActionReporter.java | 19 ++-- .../web/handler/JbootInvocationWarpper.java | 42 +++++++ src/test/java/io/jboot/test/HelloWorld.java | 2 +- 5 files changed, 187 insertions(+), 26 deletions(-) create mode 100644 src/main/java/io/jboot/web/handler/ConsoleColor.java create mode 100644 src/main/java/io/jboot/web/handler/JbootInvocationWarpper.java diff --git a/src/main/java/io/jboot/web/handler/ConsoleColor.java b/src/main/java/io/jboot/web/handler/ConsoleColor.java new file mode 100644 index 00000000..643092e8 --- /dev/null +++ b/src/main/java/io/jboot/web/handler/ConsoleColor.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.jboot.web.handler; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public enum ConsoleColor { + + //颜色结尾字符串,重置颜色的 + RESET("\033[0m"), + + // Regular Colors 普通颜色,不带加粗,背景色等 + BLACK("\033[0;30m"), // BLACK + RED("\033[0;31m"), // RED + GREEN("\033[0;32m"), // GREEN + YELLOW("\033[0;33m"), // YELLOW + BLUE("\033[0;34m"), // BLUE + MAGENTA("\033[0;35m"), // MAGENTA + CYAN("\033[0;36m"), // CYAN + WHITE("\033[0;37m"), // WHITE + + // Bold + BLACK_BOLD("\033[1;30m"), // BLACK + RED_BOLD("\033[1;31m"), // RED + GREEN_BOLD("\033[1;32m"), // GREEN + YELLOW_BOLD("\033[1;33m"), // YELLOW + BLUE_BOLD("\033[1;34m"), // BLUE + MAGENTA_BOLD("\033[1;35m"), // MAGENTA + CYAN_BOLD("\033[1;36m"), // CYAN + WHITE_BOLD("\033[1;37m"), // WHITE + + // Underline + BLACK_UNDERLINED("\033[4;30m"), // BLACK + RED_UNDERLINED("\033[4;31m"), // RED + GREEN_UNDERLINED("\033[4;32m"), // GREEN + YELLOW_UNDERLINED("\033[4;33m"), // YELLOW + BLUE_UNDERLINED("\033[4;34m"), // BLUE + MAGENTA_UNDERLINED("\033[4;35m"), // MAGENTA + CYAN_UNDERLINED("\033[4;36m"), // CYAN + WHITE_UNDERLINED("\033[4;37m"), // WHITE + + // Background + BLACK_BACKGROUND("\033[40m"), // BLACK + RED_BACKGROUND("\033[41m"), // RED + GREEN_BACKGROUND("\033[42m"), // GREEN + YELLOW_BACKGROUND("\033[43m"), // YELLOW + BLUE_BACKGROUND("\033[44m"), // BLUE + MAGENTA_BACKGROUND("\033[45m"), // MAGENTA + CYAN_BACKGROUND("\033[46m"), // CYAN + WHITE_BACKGROUND("\033[47m"), // WHITE + + // High Intensity + BLACK_BRIGHT("\033[0;90m"), // BLACK + RED_BRIGHT("\033[0;91m"), // RED + GREEN_BRIGHT("\033[0;92m"), // GREEN + YELLOW_BRIGHT("\033[0;93m"), // YELLOW + BLUE_BRIGHT("\033[0;94m"), // BLUE + MAGENTA_BRIGHT("\033[0;95m"), // MAGENTA + CYAN_BRIGHT("\033[0;96m"), // CYAN + WHITE_BRIGHT("\033[0;97m"), // WHITE + + // Bold High Intensity + BLACK_BOLD_BRIGHT("\033[1;90m"), // BLACK + RED_BOLD_BRIGHT("\033[1;91m"), // RED + GREEN_BOLD_BRIGHT("\033[1;92m"), // GREEN + YELLOW_BOLD_BRIGHT("\033[1;93m"), // YELLOW + BLUE_BOLD_BRIGHT("\033[1;94m"), // BLUE + MAGENTA_BOLD_BRIGHT("\033[1;95m"), // MAGENTA + CYAN_BOLD_BRIGHT("\033[1;96m"), // CYAN + WHITE_BOLD_BRIGHT("\033[1;97m"), // WHITE + + // High Intensity backgrounds + BLACK_BACKGROUND_BRIGHT("\033[0;100m"), // BLACK + RED_BACKGROUND_BRIGHT("\033[0;101m"), // RED + GREEN_BACKGROUND_BRIGHT("\033[0;102m"), // GREEN + YELLOW_BACKGROUND_BRIGHT("\033[0;103m"), // YELLOW + BLUE_BACKGROUND_BRIGHT("\033[0;104m"), // BLUE + MAGENTA_BACKGROUND_BRIGHT("\033[0;105m"), // MAGENTA + CYAN_BACKGROUND_BRIGHT("\033[0;106m"), // CYAN + WHITE_BACKGROUND_BRIGHT("\033[0;107m"); // WHITE + + private final String code; + + ConsoleColor(String code) { + this.code = code; + } + + @Override + public String toString() { + return code; + } +} \ No newline at end of file diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 0a0d3308..59029ac6 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -36,10 +36,11 @@ import javax.servlet.http.HttpServletResponse; public class JbootActionHandler extends ActionHandler { - private static final Log log = Log.getLog(JbootActionHandler.class); + private static final Log LOG = Log.getLog(JbootActionHandler.class); /** * 方便子类复写、从而可以实现 自定义 Action 的功能 + * * @param target * @param urlPara * @param request @@ -52,6 +53,7 @@ public class JbootActionHandler extends ActionHandler { /** * 方便子类复写、从而可以实现 自定义 Action 的功能 + * * @param target * @param urlPara * @return @@ -63,12 +65,13 @@ public class JbootActionHandler extends ActionHandler { /** * 方便子类复写、从而可以实现 自定义 Invocation 的功能 + * * @param action * @param controller * @return */ public Invocation getInvocation(Action action, Controller controller) { - return new Invocation(action, controller); + return devMode ? new JbootInvocationWarpper(action, controller) : new Invocation(action, controller); } @@ -93,9 +96,9 @@ public class JbootActionHandler extends ActionHandler { Action action = getAction(target, urlPara, request); if (action == null) { - if (log.isWarnEnabled()) { + if (LOG.isWarnEnabled()) { String qs = request.getQueryString(); - log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs)); + LOG.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs)); } renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); return; @@ -112,12 +115,18 @@ public class JbootActionHandler extends ActionHandler { // Invocation invocation = new Invocation(action, controller); Invocation invocation = getInvocation(action, controller); if (devMode) { - if (ActionReporter.isReportAfterInvocation(request)) { - invokeInvocation(invocation); - JbootActionReporter.report(target, controller, action); - } else { - JbootActionReporter.report(target, controller, action); +// if (ActionReporter.isReportAfterInvocation(request)) { +// invokeInvocation(invocation); +// JbootActionReporter.report(target, controller, action); +// } else { +// JbootActionReporter.report(target, controller, action); +// invokeInvocation(invocation); +// } + long time = System.currentTimeMillis(); + try { invokeInvocation(invocation); + } finally { + JbootActionReporter.report(target, controller, action, time); } } else { invokeInvocation(invocation); @@ -151,18 +160,18 @@ public class JbootActionHandler extends ActionHandler { render.setContext(request, response, action.getViewPath()).render(); } catch (RenderException e) { - if (log.isErrorEnabled()) { + if (LOG.isErrorEnabled()) { String qs = request.getQueryString(); - log.error(qs == null ? target : target + "?" + qs, e); + LOG.error(qs == null ? target : target + "?" + qs, e); } } catch (ActionException e) { handleActionException(target, request, response, action, e); } catch (Exception e) { - if (log.isErrorEnabled()) { + if (LOG.isErrorEnabled()) { String qs = request.getQueryString(); String targetInfo = qs == null ? target : target + "?" + qs; String info = ClassUtil.buildMethodString(action.getMethod()); - log.error(info + " : " + targetInfo, e); + LOG.error(info + " : " + targetInfo, e); } renderManager.getRenderFactory().getErrorRender(500).setContext(request, response, action.getViewPath()).render(); } finally { @@ -186,18 +195,18 @@ public class JbootActionHandler extends ActionHandler { } if (msg != null) { - if (log.isWarnEnabled()) { + if (LOG.isWarnEnabled()) { String qs = request.getQueryString(); msg = msg + (qs == null ? target : target + "?" + qs); if (e.getMessage() != null) { msg = msg + "\n" + e.getMessage(); } - log.warn(msg); + LOG.warn(msg); } } else { - if (log.isErrorEnabled()) { + if (LOG.isErrorEnabled()) { String qs = request.getQueryString(); - log.error(errorCode + " Error: " + (qs == null ? target : target + "?" + qs), e); + LOG.error(errorCode + " Error: " + (qs == null ? target : target + "?" + qs), e); } } diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index 6f82597d..1cad0fe4 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -30,6 +30,7 @@ import java.io.Writer; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Enumeration; +import java.util.List; /** @@ -42,11 +43,7 @@ public class JbootActionReporter { private static int maxOutputLengthOfParaValue = 512; private static Writer writer = new SystemOutWriter(); - private static final ThreadLocal sdf = new ThreadLocal() { - protected SimpleDateFormat initialValue() { - return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - } - }; + private static final ThreadLocal sdf = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); public static void setReportAfterInvocation(boolean reportAfterInvocation) { JbootActionReporter.reportAfterInvocation = reportAfterInvocation; @@ -83,13 +80,13 @@ public class JbootActionReporter { /** * Report the action */ - public static final void report(String target, Controller controller, Action action) throws NotFoundException { + public static final void report(String target, Controller controller, Action action, long time) throws NotFoundException { CtClass ctClass = ClassPool.getDefault().get(action.getControllerClass().getName()); String desc = JbootActionReporterUtil.getMethodDescWithoutName(action.getMethod()); CtMethod ctMethod = ctClass.getMethod(action.getMethodName(), desc); int lineNumber = ctMethod.getMethodInfo().getLineNumber(0); - StringBuilder sb = new StringBuilder(title).append(sdf.get().format(new Date())).append(" -------------------------\n"); + StringBuilder sb = new StringBuilder(title).append(sdf.get().format(new Date(time))).append(" -------------------------\n"); sb.append("Url : ").append(controller.getRequest().getMethod()).append(" ").append(target).append("\n"); Class cc = action.getMethod().getDeclaringClass(); sb.append("Controller : ").append(cc.getName()).append(".(").append(getClassFileName(cc)).append(".java:" + lineNumber + ")"); @@ -101,6 +98,7 @@ public class JbootActionReporter { } Interceptor[] inters = action.getInterceptors(); + List invokedInterceptors = JbootInvocationWarpper.getInvokedInterceptor(); if (inters.length > 0) { sb.append("Interceptor : "); for (int i = 0; i < inters.length; i++) { @@ -114,6 +112,9 @@ public class JbootActionReporter { CtMethod icMethod = icClass.getMethod("intercept", "(Lcom/jfinal/aop/Invocation;)V"); int icLineNumber = icMethod.getMethodInfo().getLineNumber(0); sb.append(icMethod.getDeclaringClass().getName()).append(".(").append(getClassFileName(ic)).append(".java:" + icLineNumber + ")"); + if (!invokedInterceptors.contains(inter)) { + sb.append(ConsoleColor.RED + " ---> not invoked! " + ConsoleColor.RESET); + } } sb.append("\n"); } @@ -146,12 +147,14 @@ public class JbootActionReporter { } sb.append("\n"); } - sb.append("--------------------------------------------------------------------------------\n"); + sb.append("----------------------------------- taked " + (System.currentTimeMillis() - time) + " ms --------------------------------\n"); try { writer.write(sb.toString()); } catch (IOException ex) { throw new RuntimeException(ex); + } finally { + JbootInvocationWarpper.clear(); } } diff --git a/src/main/java/io/jboot/web/handler/JbootInvocationWarpper.java b/src/main/java/io/jboot/web/handler/JbootInvocationWarpper.java new file mode 100644 index 00000000..75ea188e --- /dev/null +++ b/src/main/java/io/jboot/web/handler/JbootInvocationWarpper.java @@ -0,0 +1,42 @@ +package io.jboot.web.handler; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import com.jfinal.core.Action; +import com.jfinal.core.Controller; + +import java.util.LinkedList; +import java.util.List; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public class JbootInvocationWarpper extends Invocation { + + private Interceptor[] inters; + private int index = 0; + + private static ThreadLocal> invokedInterceptors = ThreadLocal.withInitial(LinkedList::new); + + public JbootInvocationWarpper(Action action, Controller controller) { + super(action, controller); + this.inters = action.getInterceptors(); + } + + @Override + public void invoke() { + if (index < inters.length) { + invokedInterceptors.get().add(inters[index++]); + } + super.invoke(); + } + + public static List getInvokedInterceptor() { + return invokedInterceptors.get(); + } + + public static void clear(){ + invokedInterceptors.get().clear(); + invokedInterceptors.remove(); + } +} diff --git a/src/test/java/io/jboot/test/HelloWorld.java b/src/test/java/io/jboot/test/HelloWorld.java index 64a0aee5..17c62b4e 100644 --- a/src/test/java/io/jboot/test/HelloWorld.java +++ b/src/test/java/io/jboot/test/HelloWorld.java @@ -48,7 +48,7 @@ public class HelloWorld extends JbootController { @Override public void intercept(Invocation inv) { System.out.println("MyInterceptor.intercept"); - inv.invoke(); + throw new NullPointerException(""); } } -- Gitee From 18e2122be73d6a11ddc0d563b9b7846828f5dbe5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 14 Aug 2020 12:26:17 +0800 Subject: [PATCH 0462/1965] v3.3.5 release (^.^)YYa!! --- src/test/java/io/jboot/test/HelloWorld.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/io/jboot/test/HelloWorld.java b/src/test/java/io/jboot/test/HelloWorld.java index 17c62b4e..bbfd8d67 100644 --- a/src/test/java/io/jboot/test/HelloWorld.java +++ b/src/test/java/io/jboot/test/HelloWorld.java @@ -32,6 +32,7 @@ public class HelloWorld extends JbootController { @Before({MyInterceptor.class,HelloInterceptor.class}) public void index(){ renderText("hello world"); +// throw new NullPointerException(); } public void ex(){ @@ -48,7 +49,8 @@ public class HelloWorld extends JbootController { @Override public void intercept(Invocation inv) { System.out.println("MyInterceptor.intercept"); - throw new NullPointerException(""); +// throw new NullPointerException(""); + inv.invoke(); } } -- Gitee From deb1dc60082dc452e35076ba819688d9f42a4bee Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 14 Aug 2020 12:27:01 +0800 Subject: [PATCH 0463/1965] v3.3.5 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3d0dabed..d8b887ff 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.3.4 + 3.3.5 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 82cdd1e8..43c6ab54 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.3.4 + 3.3.5 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index c685e139..a202b553 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.3.4 + 3.3.5 ``` diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 6690d8d0..634e585b 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.3.4"; + public static String VERSION = "3.3.5"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 5f3760e60e84e9ecdf4c24d4fe9a13265a073201 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 14 Aug 2020 12:27:28 +0800 Subject: [PATCH 0464/1965] v3.3.5 release (^.^)YYa!! --- changes.txt | 7 +++++++ pom.xml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index f2a92754..7b1f80b1 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.3.5: +修复:通过门户网关下载文件 或者 渲染图片可能出现乱码的问题 +优化:重构 Http 工具类里的 HttpRequest 里的某些方法 +优化:增强 JbootActionReporter 功能,使之可以输出未被执行的拦截器 以及 Controller 的执行时间 + + + jboot v3.3.4: 修复:当 Action 定义在父类后,JbootActionReporter 获取不到 Method 而出错的问题 优化:升级 nacos 和 Apollo 客户端到最新版本 diff --git a/pom.xml b/pom.xml index 77c7ef06..12cdac2a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.5-SNAPSHOT + 3.3.5 jar jboot -- Gitee From 353b6e2b2c670b4b542291a52e5d52babc270c67 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 14 Aug 2020 16:03:56 +0800 Subject: [PATCH 0465/1965] v3.3.6 snapshot --- pom.xml | 2 +- src/main/java/io/jboot/web/handler/JbootActionReporter.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 12cdac2a..8e0ed290 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.5 + 3.3.6-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index 1cad0fe4..b707ee75 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -113,7 +113,7 @@ public class JbootActionReporter { int icLineNumber = icMethod.getMethodInfo().getLineNumber(0); sb.append(icMethod.getDeclaringClass().getName()).append(".(").append(getClassFileName(ic)).append(".java:" + icLineNumber + ")"); if (!invokedInterceptors.contains(inter)) { - sb.append(ConsoleColor.RED + " ---> not invoked! " + ConsoleColor.RESET); + sb.append(ConsoleColor.RED + " ---> not invoke!" + ConsoleColor.RESET); } } sb.append("\n"); -- Gitee From d8baf8a65e45904481cfa3a0526ff0fb0353875b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 14 Aug 2020 16:19:32 +0800 Subject: [PATCH 0466/1965] v3.3.6 snapshot --- src/main/java/io/jboot/web/handler/JbootActionReporter.java | 6 ++++-- src/test/java/io/jboot/test/HelloWorld.java | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index b707ee75..716746f8 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -112,8 +112,10 @@ public class JbootActionReporter { CtMethod icMethod = icClass.getMethod("intercept", "(Lcom/jfinal/aop/Invocation;)V"); int icLineNumber = icMethod.getMethodInfo().getLineNumber(0); sb.append(icMethod.getDeclaringClass().getName()).append(".(").append(getClassFileName(ic)).append(".java:" + icLineNumber + ")"); - if (!invokedInterceptors.contains(inter)) { - sb.append(ConsoleColor.RED + " ---> not invoke!" + ConsoleColor.RESET); + if (invokedInterceptors.contains(inter)) { + sb.append(ConsoleColor.GREEN_BOLD_BRIGHT + " ---> invoked √" + ConsoleColor.RESET); + } else { + sb.append(ConsoleColor.RED_BOLD_BRIGHT + " ---> skipped ×" + ConsoleColor.RESET); } } sb.append("\n"); diff --git a/src/test/java/io/jboot/test/HelloWorld.java b/src/test/java/io/jboot/test/HelloWorld.java index bbfd8d67..7dffd370 100644 --- a/src/test/java/io/jboot/test/HelloWorld.java +++ b/src/test/java/io/jboot/test/HelloWorld.java @@ -49,8 +49,8 @@ public class HelloWorld extends JbootController { @Override public void intercept(Invocation inv) { System.out.println("MyInterceptor.intercept"); -// throw new NullPointerException(""); - inv.invoke(); + throw new NullPointerException(""); +// inv.invoke(); } } -- Gitee From 01766368ffeb16ba87180b8da89378165117742b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 14 Aug 2020 16:28:06 +0800 Subject: [PATCH 0467/1965] v3.3.6 snapshot --- src/main/java/io/jboot/web/handler/JbootActionReporter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index 716746f8..87026c23 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -39,6 +39,7 @@ import java.util.List; public class JbootActionReporter { private static final String title = "\nJboot-" + JbootConsts.VERSION + " action report -------- "; + private static final String interceptMethodDesc = "(Lcom/jfinal/aop/Invocation;)V"; private static boolean reportAfterInvocation = true; private static int maxOutputLengthOfParaValue = 512; private static Writer writer = new SystemOutWriter(); @@ -109,9 +110,10 @@ public class JbootActionReporter { Class ic = inter.getClass(); CtClass icClass = ClassPool.getDefault().get(ic.getName()); - CtMethod icMethod = icClass.getMethod("intercept", "(Lcom/jfinal/aop/Invocation;)V"); + CtMethod icMethod = icClass.getMethod("intercept", interceptMethodDesc); int icLineNumber = icMethod.getMethodInfo().getLineNumber(0); sb.append(icMethod.getDeclaringClass().getName()).append(".(").append(getClassFileName(ic)).append(".java:" + icLineNumber + ")"); + if (invokedInterceptors.contains(inter)) { sb.append(ConsoleColor.GREEN_BOLD_BRIGHT + " ---> invoked √" + ConsoleColor.RESET); } else { -- Gitee From a52e0582cc08fd67713a7090da40f4101590b0e2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 14 Aug 2020 17:25:24 +0800 Subject: [PATCH 0468/1965] v3.3.6 snapshot --- .../web/handler/JbootActionReporter.java | 5 +-- .../web/handler/JbootActionReporterUtil.java | 32 +++++++++++++++++-- .../web/handler/JbootInvocationWarpper.java | 6 ++-- src/test/java/io/jboot/test/HelloWorld.java | 9 +++++- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index 87026c23..2cc06122 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -88,10 +88,11 @@ public class JbootActionReporter { int lineNumber = ctMethod.getMethodInfo().getLineNumber(0); StringBuilder sb = new StringBuilder(title).append(sdf.get().format(new Date(time))).append(" -------------------------\n"); - sb.append("Url : ").append(controller.getRequest().getMethod()).append(" ").append(target).append("\n"); + sb.append("Request : ").append(controller.getRequest().getMethod()).append(" ").append(target).append("\n"); Class cc = action.getMethod().getDeclaringClass(); sb.append("Controller : ").append(cc.getName()).append(".(").append(getClassFileName(cc)).append(".java:" + lineNumber + ")"); - sb.append("\nMethod : ").append(action.getMethodName()).append("\n"); + sb.append("\nMethod : ").append(JbootActionReporterUtil.getMethodString(action.getMethod())).append("\n"); + String urlParas = controller.getPara(); if (urlParas != null) { diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporterUtil.java b/src/main/java/io/jboot/web/handler/JbootActionReporterUtil.java index d3628814..1ef9fce4 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporterUtil.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporterUtil.java @@ -18,6 +18,7 @@ package io.jboot.web.handler; import java.lang.reflect.Method; +import java.lang.reflect.Parameter; /** @@ -71,9 +72,6 @@ public final class JbootActionReporterUtil { public static final char JVM_SHORT = 'S'; - - - /** * get class desc. * boolean[].class => "[Z" @@ -139,4 +137,32 @@ public final class JbootActionReporterUtil { } + /** + * get method string + * + * @param m + * @return + */ + public static String getMethodString(Method m) { + StringBuilder ret = new StringBuilder(m.getName()); + ret.append('('); + Parameter[] parameters = m.getParameters(); + int index = 0; + for (Parameter p : parameters) { + if (!p.isNamePresent()) { + // 必须通过添加 -parameters 进行编译,才可以获取 Parameter 的编译前的名字 + throw new RuntimeException(" Maven or IDE config is error. see http://www.jfinal.com/doc/3-3 "); + } + ret.append(p.getType().getSimpleName()); + ret.append(" ").append(p.getName()); + if (index++ < parameters.length - 1) { + ret.append(", "); + } + } + + ret.append(')'); + return ret.toString(); + } + + } \ No newline at end of file diff --git a/src/main/java/io/jboot/web/handler/JbootInvocationWarpper.java b/src/main/java/io/jboot/web/handler/JbootInvocationWarpper.java index 75ea188e..d13277fb 100644 --- a/src/main/java/io/jboot/web/handler/JbootInvocationWarpper.java +++ b/src/main/java/io/jboot/web/handler/JbootInvocationWarpper.java @@ -5,7 +5,7 @@ import com.jfinal.aop.Invocation; import com.jfinal.core.Action; import com.jfinal.core.Controller; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; /** @@ -16,7 +16,7 @@ public class JbootInvocationWarpper extends Invocation { private Interceptor[] inters; private int index = 0; - private static ThreadLocal> invokedInterceptors = ThreadLocal.withInitial(LinkedList::new); + private static ThreadLocal> invokedInterceptors = ThreadLocal.withInitial(ArrayList::new); public JbootInvocationWarpper(Action action, Controller controller) { super(action, controller); @@ -31,10 +31,12 @@ public class JbootInvocationWarpper extends Invocation { super.invoke(); } + public static List getInvokedInterceptor() { return invokedInterceptors.get(); } + public static void clear(){ invokedInterceptors.get().clear(); invokedInterceptors.remove(); diff --git a/src/test/java/io/jboot/test/HelloWorld.java b/src/test/java/io/jboot/test/HelloWorld.java index 7dffd370..437733d8 100644 --- a/src/test/java/io/jboot/test/HelloWorld.java +++ b/src/test/java/io/jboot/test/HelloWorld.java @@ -39,6 +39,12 @@ public class HelloWorld extends JbootController { throw new NullPointerException("log test"); } + + @Before({HelloInterceptor.class}) + public void abc(String abc, long number){ + renderText("abc"); + } + public static void main(String[] args){ JbootApplication.run(args); } @@ -49,7 +55,8 @@ public class HelloWorld extends JbootController { @Override public void intercept(Invocation inv) { System.out.println("MyInterceptor.intercept"); - throw new NullPointerException(""); +// throw new NullPointerException(""); + inv.getController().renderText("aaa"); // inv.invoke(); } } -- Gitee From 68b39088de8588fe46a241c1abd1596291705b2e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 14 Aug 2020 17:58:55 +0800 Subject: [PATCH 0469/1965] v3.3.6 release (^.^)YYa!! --- src/main/java/io/jboot/web/handler/JbootActionReporter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index 2cc06122..22b7e900 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -116,9 +116,9 @@ public class JbootActionReporter { sb.append(icMethod.getDeclaringClass().getName()).append(".(").append(getClassFileName(ic)).append(".java:" + icLineNumber + ")"); if (invokedInterceptors.contains(inter)) { - sb.append(ConsoleColor.GREEN_BOLD_BRIGHT + " ---> invoked √" + ConsoleColor.RESET); + sb.append(ConsoleColor.GREEN_BRIGHT + " ---> invoked √" + ConsoleColor.RESET); } else { - sb.append(ConsoleColor.RED_BOLD_BRIGHT + " ---> skipped ×" + ConsoleColor.RESET); + sb.append(ConsoleColor.RED_BRIGHT + " ---> skipped ×" + ConsoleColor.RESET); } } sb.append("\n"); -- Gitee From 7c8eecd77d779cf2fe8cf6f10d2ba8c0e5586290 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 14 Aug 2020 18:00:27 +0800 Subject: [PATCH 0470/1965] v3.3.6 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d8b887ff..091aec3e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.3.5 + 3.3.6 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 43c6ab54..421f3edc 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.3.5 + 3.3.6 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index a202b553..34629af3 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.3.5 + 3.3.6 ``` diff --git a/pom.xml b/pom.xml index 8e0ed290..68b6d05b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.6-SNAPSHOT + 3.3.6 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 634e585b..3e4c7061 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.3.5"; + public static String VERSION = "3.3.6"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From dc58566eba257bf203ee7b5fef643c5c086756e5 Mon Sep 17 00:00:00 2001 From: flyinke Date: Sat, 15 Aug 2020 15:29:01 +0800 Subject: [PATCH 0471/1965] =?UTF-8?q?codegen=20serviceImplPackage=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E4=B8=8D=E7=94=A8=E5=9C=A8servicePackage=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/codegen/service/JbootServiceImplGenerator.java | 6 ++++-- src/test/java/io/jboot/test/codegen/GenTester.java | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java b/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java index 3c3447a7..842137c7 100644 --- a/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java +++ b/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java @@ -35,6 +35,7 @@ import java.util.Map; public class JbootServiceImplGenerator { private String basePackage; + private String implPackage; private String modelPackage; private MetaBuilder metaBuilder; @@ -53,9 +54,10 @@ public class JbootServiceImplGenerator { this.outputDir = buildOutPutDir(); } - public JbootServiceImplGenerator(String basePackage,String outputDir ,String modelPackage) { + public JbootServiceImplGenerator(String basePackage,String implPackage,String outputDir ,String modelPackage) { this.basePackage = basePackage; + this.implPackage = implPackage; this.modelPackage = modelPackage; this.template = "io/jboot/codegen/service/service_impl_template.tp"; this.metaBuilder = CodeGenHelpler.createMetaBuilder(); @@ -115,7 +117,7 @@ public class JbootServiceImplGenerator { protected void genBaseModelContent(TableMeta tableMeta) { - Kv data = Kv.by("serviceImplPackageName", basePackage + "." + implName); + Kv data = Kv.by("serviceImplPackageName", implPackage == null ? (basePackage + "." + implName) : implPackage); // data.set("generateChainSetter", generateChainSetter); data.set("tableMeta", tableMeta); data.set("basePackage", basePackage); diff --git a/src/test/java/io/jboot/test/codegen/GenTester.java b/src/test/java/io/jboot/test/codegen/GenTester.java index 7cece7a7..33002da3 100644 --- a/src/test/java/io/jboot/test/codegen/GenTester.java +++ b/src/test/java/io/jboot/test/codegen/GenTester.java @@ -38,7 +38,7 @@ public class GenTester { new JbootServiceInterfaceGenerator(servicePackage, serviceOutputDir, modelPackage).generate(); - new JbootServiceImplGenerator(servicePackage, serviceImplOutputDir, modelPackage).setImplName("provider").generate(); + new JbootServiceImplGenerator(servicePackage, serviceImplPackage,serviceImplOutputDir, modelPackage).setImplName("provider").generate(); } } -- Gitee From b7f8858c20e99a0b8d4366585bff4515c8b64727 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 17 Aug 2020 11:10:57 +0800 Subject: [PATCH 0472/1965] v3.3.7 snapshot --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 68b6d05b..5f690fd9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.6 + 3.3.7-SNAPSHOT jar jboot @@ -44,7 +44,7 @@ 4.9.01 2020.4 2.1 - 2.6 + 2.7 3.3.0 3.4.5 2.11.2 -- Gitee From 61ef8e17c91833011dc4bb65b7e0949fccc8af57 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 17 Aug 2020 17:02:36 +0800 Subject: [PATCH 0473/1965] v3.3.7 snapshot --- .../web/handler/JbootActionReporter.java | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index 22b7e900..c5dec2af 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -22,7 +22,6 @@ import io.jboot.JbootConsts; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; -import javassist.NotFoundException; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @@ -40,15 +39,11 @@ public class JbootActionReporter { private static final String title = "\nJboot-" + JbootConsts.VERSION + " action report -------- "; private static final String interceptMethodDesc = "(Lcom/jfinal/aop/Invocation;)V"; - private static boolean reportAfterInvocation = true; private static int maxOutputLengthOfParaValue = 512; private static Writer writer = new SystemOutWriter(); private static final ThreadLocal sdf = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); - public static void setReportAfterInvocation(boolean reportAfterInvocation) { - JbootActionReporter.reportAfterInvocation = reportAfterInvocation; - } public static void setMaxOutputLengthOfParaValue(int maxOutputLengthOfParaValue) { if (maxOutputLengthOfParaValue < 16) { @@ -64,24 +59,11 @@ public class JbootActionReporter { JbootActionReporter.writer = writer; } - public static boolean isReportAfterInvocation(HttpServletRequest request) { - if (reportAfterInvocation) { - return true; - } else { - String contentType = request.getContentType(); - if (contentType != null && contentType.toLowerCase().indexOf("multipart") != -1) { - return true; - } else { - return false; - } - } - } - /** * Report the action */ - public static final void report(String target, Controller controller, Action action, long time) throws NotFoundException { + public static final void report(String target, Controller controller, Action action, long time) throws Exception { CtClass ctClass = ClassPool.getDefault().get(action.getControllerClass().getName()); String desc = JbootActionReporterUtil.getMethodDescWithoutName(action.getMethod()); CtMethod ctMethod = ctClass.getMethod(action.getMethodName(), desc); @@ -174,17 +156,22 @@ public class JbootActionReporter { } } + private static class SystemOutWriter extends Writer { + @Override public void write(String str) throws IOException { System.out.print(str); } + @Override public void write(char[] cbuf, int off, int len) throws IOException { } + @Override public void flush() throws IOException { } + @Override public void close() throws IOException { } } -- Gitee From 05886b844b296929f8554886b7807759dccfc299 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 18 Aug 2020 17:31:06 +0800 Subject: [PATCH 0474/1965] remove @Package info --- src/main/java/io/jboot/codegen/GenTester.java | 1 - src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java | 1 - src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java | 1 - src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java | 1 - .../java/io/jboot/components/rpc/JbootrpcReferenceConfig.java | 1 - src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java | 1 - .../io/jboot/components/schedule/JbootDistributedRunnable.java | 1 - src/main/java/io/jboot/components/serializer/KryoSerializer.java | 1 - src/main/java/io/jboot/db/TableInfo.java | 1 - src/main/java/io/jboot/db/TableInfoManager.java | 1 - src/main/java/io/jboot/db/datasource/DruidDataSourceFactory.java | 1 - .../java/io/jboot/db/datasource/HikariDataSourceFactory.java | 1 - src/main/java/io/jboot/db/dbpro/JbootDbPro.java | 1 - src/main/java/io/jboot/db/dbpro/JbootDbProFactory.java | 1 - src/main/java/io/jboot/db/model/JbootModelConfig.java | 1 - src/main/java/io/jboot/db/model/SqlBuilder.java | 1 - .../java/io/jboot/exception/JbootIllegalConfigException.java | 1 - src/main/java/io/jboot/support/jwt/JwtConfig.java | 1 - src/main/java/io/jboot/support/jwt/JwtInterceptor.java | 1 - src/main/java/io/jboot/support/jwt/JwtManager.java | 1 - src/main/java/io/jboot/support/metric/JbootMetricReporter.java | 1 - .../support/metric/reporter/console/JbootConsoleReporter.java | 1 - .../java/io/jboot/support/metric/reporter/csv/CSVReporter.java | 1 - .../metric/reporter/elasticsearch/ElasticsearchReporter.java | 1 - .../jboot/support/metric/reporter/ganglia/GangliaReporter.java | 1 - .../support/metric/reporter/graphite/JbootGraphiteReporter.java | 1 - .../jboot/support/metric/reporter/influxdb/InfluxdbReporter.java | 1 - .../java/io/jboot/support/metric/reporter/jmx/JMXReporter.java | 1 - .../jboot/support/metric/reporter/slf4j/JbootSlf4jReporter.java | 1 - src/main/java/io/jboot/support/shiro/JbootShiroFilter.java | 1 - .../java/io/jboot/support/swagger/JbootSwaggerController.java | 1 - src/main/java/io/jboot/support/swagger/JbootSwaggerManager.java | 1 - src/main/java/io/jboot/support/swagger/ParamType.java | 1 - src/main/java/io/jboot/support/swagger/SwaggerPath.java | 1 - src/main/java/io/jboot/web/JbootWebConfig.java | 1 - .../java/io/jboot/web/controller/JbootControllerContext.java | 1 - .../java/io/jboot/web/controller/JbootControllerManager.java | 1 - src/main/java/io/jboot/web/cors/CORSInterceptor.java | 1 - .../java/io/jboot/web/fixedinterceptor/FixedInterceptor.java | 1 - .../java/io/jboot/web/fixedinterceptor/FixedInterceptors.java | 1 - src/main/java/io/jboot/web/fixedinterceptor/FixedInvocation.java | 1 - src/main/java/io/jboot/web/handler/JbootActionHandler.java | 1 - src/main/java/io/jboot/web/render/JbootHtmlRender.java | 1 - src/main/java/io/jboot/web/render/JbootJavascriptRender.java | 1 - src/main/java/io/jboot/web/render/JbootJsonRender.java | 1 - src/main/java/io/jboot/web/render/JbootReturnValueRender.java | 1 - src/main/java/io/jboot/web/render/JbootTemplateRender.java | 1 - src/main/java/io/jboot/web/render/JbootTextRender.java | 1 - src/main/java/io/jboot/web/render/JbootXmlRender.java | 1 - src/main/java/io/jboot/web/render/RenderHelpler.java | 1 - src/main/java/io/jboot/web/utils/ControllerUtil.java | 1 - src/main/java/io/jboot/web/validate/FormType.java | 1 - src/main/java/io/jboot/web/validate/ValidateRenderType.java | 1 - 53 files changed, 53 deletions(-) diff --git a/src/main/java/io/jboot/codegen/GenTester.java b/src/main/java/io/jboot/codegen/GenTester.java index 5cfbe53d..f4cee965 100644 --- a/src/main/java/io/jboot/codegen/GenTester.java +++ b/src/main/java/io/jboot/codegen/GenTester.java @@ -25,7 +25,6 @@ import io.jboot.codegen.service.JbootServiceInterfaceGenerator; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.codegen */ public class GenTester { diff --git a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java index 4e09a0c9..403385e0 100644 --- a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java +++ b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java @@ -31,7 +31,6 @@ import java.util.stream.Collectors; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.core.cache.j2cache */ public class J2cacheImpl extends JbootCacheBase { diff --git a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java index 0f9c30a1..4fcd4b74 100644 --- a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java +++ b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java @@ -32,7 +32,6 @@ import java.util.Map; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.core.http.okhttp */ public class OKHttpImpl implements JbootHttp { private static final Log LOG = Log.getLog(OKHttpImpl.class); diff --git a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java index 88aa482d..db6b8f7a 100644 --- a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java +++ b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java @@ -31,7 +31,6 @@ import javax.jms.*; /** * @author 徐海峰 (27533892@qq.com) * @version V1.0 - * @Package io.jboot.core.mq.qpid */ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java index 1394fdc3..2d7820a1 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java @@ -22,7 +22,6 @@ import java.io.Serializable; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.components.rpc */ public class JbootrpcReferenceConfig implements Serializable { diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java index b9abc38f..4284158d 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java @@ -22,7 +22,6 @@ import java.io.Serializable; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.components.rpc */ public class JbootrpcServiceConfig implements Serializable { diff --git a/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java b/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java index 1ace8886..88312bf3 100644 --- a/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java +++ b/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java @@ -30,7 +30,6 @@ import io.jboot.support.redis.JbootRedis; * 2、高可用,不存在单点故障 * 3、一致性,在集群环境中,只有一个任务在执行。 * 4、Failover,支持故障转移 - * @Package io.jboot.schedule */ public class JbootDistributedRunnable implements Runnable { diff --git a/src/main/java/io/jboot/components/serializer/KryoSerializer.java b/src/main/java/io/jboot/components/serializer/KryoSerializer.java index 57776a8c..0b070fe0 100644 --- a/src/main/java/io/jboot/components/serializer/KryoSerializer.java +++ b/src/main/java/io/jboot/components/serializer/KryoSerializer.java @@ -28,7 +28,6 @@ import java.io.ByteArrayOutputStream; * @version V1.0 * @Title: Kryo 序列化 * @Description: 性能和 fst一样 - * @Package io.jboot.core.serializer */ public class KryoSerializer implements JbootSerializer { diff --git a/src/main/java/io/jboot/db/TableInfo.java b/src/main/java/io/jboot/db/TableInfo.java index 66eee8a7..7e9d8dd5 100644 --- a/src/main/java/io/jboot/db/TableInfo.java +++ b/src/main/java/io/jboot/db/TableInfo.java @@ -20,7 +20,6 @@ import com.jfinal.plugin.activerecord.Model; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.db */ public class TableInfo { diff --git a/src/main/java/io/jboot/db/TableInfoManager.java b/src/main/java/io/jboot/db/TableInfoManager.java index 65c4b118..61637498 100644 --- a/src/main/java/io/jboot/db/TableInfoManager.java +++ b/src/main/java/io/jboot/db/TableInfoManager.java @@ -31,7 +31,6 @@ import java.util.Set; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.db */ public class TableInfoManager { diff --git a/src/main/java/io/jboot/db/datasource/DruidDataSourceFactory.java b/src/main/java/io/jboot/db/datasource/DruidDataSourceFactory.java index 63ec8343..c142fa2c 100644 --- a/src/main/java/io/jboot/db/datasource/DruidDataSourceFactory.java +++ b/src/main/java/io/jboot/db/datasource/DruidDataSourceFactory.java @@ -25,7 +25,6 @@ import java.sql.SQLException; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.db.datasource */ public class DruidDataSourceFactory implements DataSourceFactory { diff --git a/src/main/java/io/jboot/db/datasource/HikariDataSourceFactory.java b/src/main/java/io/jboot/db/datasource/HikariDataSourceFactory.java index f25a2ff6..d9fa2330 100644 --- a/src/main/java/io/jboot/db/datasource/HikariDataSourceFactory.java +++ b/src/main/java/io/jboot/db/datasource/HikariDataSourceFactory.java @@ -24,7 +24,6 @@ import javax.sql.DataSource; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.db.datasource */ public class HikariDataSourceFactory implements DataSourceFactory { diff --git a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java index 11c85d56..81402978 100644 --- a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java +++ b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java @@ -28,7 +28,6 @@ import java.util.List; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.db.dbpro */ public class JbootDbPro extends DbPro { diff --git a/src/main/java/io/jboot/db/dbpro/JbootDbProFactory.java b/src/main/java/io/jboot/db/dbpro/JbootDbProFactory.java index 5a182e3f..2191c9e9 100644 --- a/src/main/java/io/jboot/db/dbpro/JbootDbProFactory.java +++ b/src/main/java/io/jboot/db/dbpro/JbootDbProFactory.java @@ -21,7 +21,6 @@ import com.jfinal.plugin.activerecord.IDbProFactory; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.db */ public class JbootDbProFactory implements IDbProFactory { diff --git a/src/main/java/io/jboot/db/model/JbootModelConfig.java b/src/main/java/io/jboot/db/model/JbootModelConfig.java index c353c291..e47af898 100644 --- a/src/main/java/io/jboot/db/model/JbootModelConfig.java +++ b/src/main/java/io/jboot/db/model/JbootModelConfig.java @@ -26,7 +26,6 @@ import io.jboot.utils.StrUtil; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.db.model */ @ConfigModel(prefix = "jboot.model") public class JbootModelConfig { diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index 439d0a98..131e2158 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -23,7 +23,6 @@ import java.util.List; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.db.dialect */ public class SqlBuilder { diff --git a/src/main/java/io/jboot/exception/JbootIllegalConfigException.java b/src/main/java/io/jboot/exception/JbootIllegalConfigException.java index baead5f5..2bb1dfa6 100644 --- a/src/main/java/io/jboot/exception/JbootIllegalConfigException.java +++ b/src/main/java/io/jboot/exception/JbootIllegalConfigException.java @@ -20,7 +20,6 @@ package io.jboot.exception; * @version V1.0 * @Title: 配置错误 * @Description: 在某些情况,必须要指定的某个配置,但是用户没有配置时,抛出该错误 - * @Package io.jboot.exception */ public class JbootIllegalConfigException extends JbootException { diff --git a/src/main/java/io/jboot/support/jwt/JwtConfig.java b/src/main/java/io/jboot/support/jwt/JwtConfig.java index f454abc1..1dc67458 100644 --- a/src/main/java/io/jboot/support/jwt/JwtConfig.java +++ b/src/main/java/io/jboot/support/jwt/JwtConfig.java @@ -21,7 +21,6 @@ import io.jboot.utils.StrUtil; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.jwt */ @ConfigModel(prefix = "jboot.web.jwt") public class JwtConfig { diff --git a/src/main/java/io/jboot/support/jwt/JwtInterceptor.java b/src/main/java/io/jboot/support/jwt/JwtInterceptor.java index 611d8125..1aef6bef 100644 --- a/src/main/java/io/jboot/support/jwt/JwtInterceptor.java +++ b/src/main/java/io/jboot/support/jwt/JwtInterceptor.java @@ -28,7 +28,6 @@ import java.util.Map; * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 * @Title: 用于对Jwt的设置 - * @Package io.jboot.web.jwt */ public class JwtInterceptor implements FixedInterceptor { diff --git a/src/main/java/io/jboot/support/jwt/JwtManager.java b/src/main/java/io/jboot/support/jwt/JwtManager.java index 2edbdce5..334d8dda 100644 --- a/src/main/java/io/jboot/support/jwt/JwtManager.java +++ b/src/main/java/io/jboot/support/jwt/JwtManager.java @@ -32,7 +32,6 @@ import java.util.Map; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.jwt */ public class JwtManager { diff --git a/src/main/java/io/jboot/support/metric/JbootMetricReporter.java b/src/main/java/io/jboot/support/metric/JbootMetricReporter.java index 37eab3e6..9003db94 100644 --- a/src/main/java/io/jboot/support/metric/JbootMetricReporter.java +++ b/src/main/java/io/jboot/support/metric/JbootMetricReporter.java @@ -20,7 +20,6 @@ import com.codahale.metrics.MetricRegistry; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.component.metrics */ public interface JbootMetricReporter { diff --git a/src/main/java/io/jboot/support/metric/reporter/console/JbootConsoleReporter.java b/src/main/java/io/jboot/support/metric/reporter/console/JbootConsoleReporter.java index 51831979..fcb9cfb1 100644 --- a/src/main/java/io/jboot/support/metric/reporter/console/JbootConsoleReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/console/JbootConsoleReporter.java @@ -24,7 +24,6 @@ import java.util.concurrent.TimeUnit; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.component.metric.reporter.console */ public class JbootConsoleReporter implements JbootMetricReporter { @Override diff --git a/src/main/java/io/jboot/support/metric/reporter/csv/CSVReporter.java b/src/main/java/io/jboot/support/metric/reporter/csv/CSVReporter.java index 4c448633..72cc1b0d 100644 --- a/src/main/java/io/jboot/support/metric/reporter/csv/CSVReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/csv/CSVReporter.java @@ -28,7 +28,6 @@ import java.util.concurrent.TimeUnit; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.component.metric.reporter.csv */ public class CSVReporter implements JbootMetricReporter { diff --git a/src/main/java/io/jboot/support/metric/reporter/elasticsearch/ElasticsearchReporter.java b/src/main/java/io/jboot/support/metric/reporter/elasticsearch/ElasticsearchReporter.java index 6e02784c..81b11755 100644 --- a/src/main/java/io/jboot/support/metric/reporter/elasticsearch/ElasticsearchReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/elasticsearch/ElasticsearchReporter.java @@ -21,7 +21,6 @@ import io.jboot.support.metric.JbootMetricReporter; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.component.metric.reporter.elasticsearch */ public class ElasticsearchReporter implements JbootMetricReporter { @Override diff --git a/src/main/java/io/jboot/support/metric/reporter/ganglia/GangliaReporter.java b/src/main/java/io/jboot/support/metric/reporter/ganglia/GangliaReporter.java index 62bb44c1..57046870 100644 --- a/src/main/java/io/jboot/support/metric/reporter/ganglia/GangliaReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/ganglia/GangliaReporter.java @@ -21,7 +21,6 @@ import io.jboot.support.metric.JbootMetricReporter; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.component.metric.reporter.ganglia */ public class GangliaReporter implements JbootMetricReporter { @Override diff --git a/src/main/java/io/jboot/support/metric/reporter/graphite/JbootGraphiteReporter.java b/src/main/java/io/jboot/support/metric/reporter/graphite/JbootGraphiteReporter.java index 1d096ec8..09a642f8 100644 --- a/src/main/java/io/jboot/support/metric/reporter/graphite/JbootGraphiteReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/graphite/JbootGraphiteReporter.java @@ -29,7 +29,6 @@ import java.util.concurrent.TimeUnit; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.component.metric.reporter.graphite */ public class JbootGraphiteReporter implements JbootMetricReporter { @Override diff --git a/src/main/java/io/jboot/support/metric/reporter/influxdb/InfluxdbReporter.java b/src/main/java/io/jboot/support/metric/reporter/influxdb/InfluxdbReporter.java index adf8b82a..e80861fc 100644 --- a/src/main/java/io/jboot/support/metric/reporter/influxdb/InfluxdbReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/influxdb/InfluxdbReporter.java @@ -27,7 +27,6 @@ import java.util.concurrent.TimeUnit; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.component.metric.reporter.jmx * url : https://github.com/davidB/metrics-influxdb */ public class InfluxdbReporter implements JbootMetricReporter { diff --git a/src/main/java/io/jboot/support/metric/reporter/jmx/JMXReporter.java b/src/main/java/io/jboot/support/metric/reporter/jmx/JMXReporter.java index d1897518..b4c1c387 100644 --- a/src/main/java/io/jboot/support/metric/reporter/jmx/JMXReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/jmx/JMXReporter.java @@ -22,7 +22,6 @@ import io.jboot.support.metric.JbootMetricReporter; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.component.metric.reporter.jmx */ public class JMXReporter implements JbootMetricReporter { @Override diff --git a/src/main/java/io/jboot/support/metric/reporter/slf4j/JbootSlf4jReporter.java b/src/main/java/io/jboot/support/metric/reporter/slf4j/JbootSlf4jReporter.java index 8e33593d..e1fcc4fd 100644 --- a/src/main/java/io/jboot/support/metric/reporter/slf4j/JbootSlf4jReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/slf4j/JbootSlf4jReporter.java @@ -25,7 +25,6 @@ import java.util.concurrent.TimeUnit; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.component.metric.reporter.slf4j */ public class JbootSlf4jReporter implements JbootMetricReporter { @Override diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroFilter.java b/src/main/java/io/jboot/support/shiro/JbootShiroFilter.java index 5bb7d74f..e6cd82ec 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroFilter.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroFilter.java @@ -31,7 +31,6 @@ import java.io.IOException; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.component.shiro */ public class JbootShiroFilter extends ShiroFilter { diff --git a/src/main/java/io/jboot/support/swagger/JbootSwaggerController.java b/src/main/java/io/jboot/support/swagger/JbootSwaggerController.java index 373a1600..a491f51e 100644 --- a/src/main/java/io/jboot/support/swagger/JbootSwaggerController.java +++ b/src/main/java/io/jboot/support/swagger/JbootSwaggerController.java @@ -27,7 +27,6 @@ import io.swagger.models.properties.RefProperty; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.component.swagger */ public class JbootSwaggerController extends JbootController { diff --git a/src/main/java/io/jboot/support/swagger/JbootSwaggerManager.java b/src/main/java/io/jboot/support/swagger/JbootSwaggerManager.java index c26641ce..a9ddd3a9 100644 --- a/src/main/java/io/jboot/support/swagger/JbootSwaggerManager.java +++ b/src/main/java/io/jboot/support/swagger/JbootSwaggerManager.java @@ -31,7 +31,6 @@ import static io.swagger.models.Scheme.HTTPS; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.component.swagger *

* 相关文档: https://www.gitbook.com/book/huangwenchao/swagger/details */ diff --git a/src/main/java/io/jboot/support/swagger/ParamType.java b/src/main/java/io/jboot/support/swagger/ParamType.java index a26a385b..9969d2b2 100644 --- a/src/main/java/io/jboot/support/swagger/ParamType.java +++ b/src/main/java/io/jboot/support/swagger/ParamType.java @@ -3,7 +3,6 @@ package io.jboot.support.swagger; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.component.swagger */ public class ParamType { diff --git a/src/main/java/io/jboot/support/swagger/SwaggerPath.java b/src/main/java/io/jboot/support/swagger/SwaggerPath.java index 1b380aa1..6a21c034 100644 --- a/src/main/java/io/jboot/support/swagger/SwaggerPath.java +++ b/src/main/java/io/jboot/support/swagger/SwaggerPath.java @@ -28,7 +28,6 @@ import java.util.Map; * @version V1.0 * @Title: 自定义 Swagger Path * @Description: 目的是为了 防止 fastjson 生成 opreations 和 operationMap 的json生成 - * @Package io.jboot.component.swagger */ public class SwaggerPath extends Path { diff --git a/src/main/java/io/jboot/web/JbootWebConfig.java b/src/main/java/io/jboot/web/JbootWebConfig.java index f880abf9..ee9c1eb5 100644 --- a/src/main/java/io/jboot/web/JbootWebConfig.java +++ b/src/main/java/io/jboot/web/JbootWebConfig.java @@ -20,7 +20,6 @@ import io.jboot.app.config.annotation.ConfigModel; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web */ @ConfigModel(prefix = "jboot.web") public class JbootWebConfig { diff --git a/src/main/java/io/jboot/web/controller/JbootControllerContext.java b/src/main/java/io/jboot/web/controller/JbootControllerContext.java index 85802fdd..1eb88eea 100644 --- a/src/main/java/io/jboot/web/controller/JbootControllerContext.java +++ b/src/main/java/io/jboot/web/controller/JbootControllerContext.java @@ -20,7 +20,6 @@ import com.jfinal.core.Controller; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web */ public class JbootControllerContext { diff --git a/src/main/java/io/jboot/web/controller/JbootControllerManager.java b/src/main/java/io/jboot/web/controller/JbootControllerManager.java index 4a9c363b..ace09193 100644 --- a/src/main/java/io/jboot/web/controller/JbootControllerManager.java +++ b/src/main/java/io/jboot/web/controller/JbootControllerManager.java @@ -23,7 +23,6 @@ import com.jfinal.core.ControllerFactory; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web */ public class JbootControllerManager extends ControllerFactory { diff --git a/src/main/java/io/jboot/web/cors/CORSInterceptor.java b/src/main/java/io/jboot/web/cors/CORSInterceptor.java index 42903df6..3a697073 100644 --- a/src/main/java/io/jboot/web/cors/CORSInterceptor.java +++ b/src/main/java/io/jboot/web/cors/CORSInterceptor.java @@ -27,7 +27,6 @@ import javax.servlet.http.HttpServletResponse; * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 * @Title: CORS 处理相关 拦截器 - * @Package io.jboot.web.cors */ public class CORSInterceptor implements FixedInterceptor { diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptor.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptor.java index 7e6c887d..e7e1a9eb 100644 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptor.java +++ b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptor.java @@ -21,7 +21,6 @@ import com.jfinal.aop.Invocation; * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @title 不会被 @Clear 清除掉的 拦截器 * @version V1.0 - * @Package io.jboot.web.handler */ public interface FixedInterceptor { void intercept(Invocation inv); diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java index c6b2adc0..52b19329 100644 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java +++ b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java @@ -34,7 +34,6 @@ import java.util.List; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.fixedinterceptor */ public class FixedInterceptors { diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInvocation.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInvocation.java index fe85f12e..c4d3bf0f 100644 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInvocation.java +++ b/src/main/java/io/jboot/web/fixedinterceptor/FixedInvocation.java @@ -23,7 +23,6 @@ import java.lang.reflect.Method; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.handler */ public class FixedInvocation extends Invocation { diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 59029ac6..7d0db94c 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -31,7 +31,6 @@ import javax.servlet.http.HttpServletResponse; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web */ public class JbootActionHandler extends ActionHandler { diff --git a/src/main/java/io/jboot/web/render/JbootHtmlRender.java b/src/main/java/io/jboot/web/render/JbootHtmlRender.java index caf3e503..d2804d2a 100644 --- a/src/main/java/io/jboot/web/render/JbootHtmlRender.java +++ b/src/main/java/io/jboot/web/render/JbootHtmlRender.java @@ -20,7 +20,6 @@ import com.jfinal.render.ContentType; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.render */ public class JbootHtmlRender extends JbootTextRender { public JbootHtmlRender(String text) { diff --git a/src/main/java/io/jboot/web/render/JbootJavascriptRender.java b/src/main/java/io/jboot/web/render/JbootJavascriptRender.java index 12e2e059..cd4d5302 100644 --- a/src/main/java/io/jboot/web/render/JbootJavascriptRender.java +++ b/src/main/java/io/jboot/web/render/JbootJavascriptRender.java @@ -20,7 +20,6 @@ import com.jfinal.render.ContentType; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.render */ public class JbootJavascriptRender extends JbootTextRender { public JbootJavascriptRender(String text) { diff --git a/src/main/java/io/jboot/web/render/JbootJsonRender.java b/src/main/java/io/jboot/web/render/JbootJsonRender.java index 8f3adc0b..63e5a99f 100644 --- a/src/main/java/io/jboot/web/render/JbootJsonRender.java +++ b/src/main/java/io/jboot/web/render/JbootJsonRender.java @@ -21,7 +21,6 @@ import io.jboot.JbootConsts; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.render */ public class JbootJsonRender extends JsonRender { diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index 3e923a00..1152e102 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -31,7 +31,6 @@ import java.util.Date; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.render */ public class JbootReturnValueRender extends Render { diff --git a/src/main/java/io/jboot/web/render/JbootTemplateRender.java b/src/main/java/io/jboot/web/render/JbootTemplateRender.java index 7c9302f8..47fab42a 100644 --- a/src/main/java/io/jboot/web/render/JbootTemplateRender.java +++ b/src/main/java/io/jboot/web/render/JbootTemplateRender.java @@ -18,7 +18,6 @@ package io.jboot.web.render; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.render */ public class JbootTemplateRender extends JbootRender { diff --git a/src/main/java/io/jboot/web/render/JbootTextRender.java b/src/main/java/io/jboot/web/render/JbootTextRender.java index e39289d6..8a31d9ab 100644 --- a/src/main/java/io/jboot/web/render/JbootTextRender.java +++ b/src/main/java/io/jboot/web/render/JbootTextRender.java @@ -21,7 +21,6 @@ import com.jfinal.render.TextRender; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.render */ public class JbootTextRender extends TextRender { public JbootTextRender(String text) { diff --git a/src/main/java/io/jboot/web/render/JbootXmlRender.java b/src/main/java/io/jboot/web/render/JbootXmlRender.java index a3d9d9c8..699a44f5 100644 --- a/src/main/java/io/jboot/web/render/JbootXmlRender.java +++ b/src/main/java/io/jboot/web/render/JbootXmlRender.java @@ -18,7 +18,6 @@ package io.jboot.web.render; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.render */ public class JbootXmlRender extends JbootRender { diff --git a/src/main/java/io/jboot/web/render/RenderHelpler.java b/src/main/java/io/jboot/web/render/RenderHelpler.java index 5131ddfd..cc5b0160 100644 --- a/src/main/java/io/jboot/web/render/RenderHelpler.java +++ b/src/main/java/io/jboot/web/render/RenderHelpler.java @@ -29,7 +29,6 @@ import java.util.Iterator; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.render */ public class RenderHelpler { diff --git a/src/main/java/io/jboot/web/utils/ControllerUtil.java b/src/main/java/io/jboot/web/utils/ControllerUtil.java index 4551cc43..6f5aa96e 100644 --- a/src/main/java/io/jboot/web/utils/ControllerUtil.java +++ b/src/main/java/io/jboot/web/utils/ControllerUtil.java @@ -10,7 +10,6 @@ import java.util.Set; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.utils */ public class ControllerUtil { diff --git a/src/main/java/io/jboot/web/validate/FormType.java b/src/main/java/io/jboot/web/validate/FormType.java index 4926d9af..5e5b6184 100644 --- a/src/main/java/io/jboot/web/validate/FormType.java +++ b/src/main/java/io/jboot/web/validate/FormType.java @@ -18,7 +18,6 @@ package io.jboot.web.validate; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.controller.validate */ public class FormType { diff --git a/src/main/java/io/jboot/web/validate/ValidateRenderType.java b/src/main/java/io/jboot/web/validate/ValidateRenderType.java index 8b02f419..e405997d 100644 --- a/src/main/java/io/jboot/web/validate/ValidateRenderType.java +++ b/src/main/java/io/jboot/web/validate/ValidateRenderType.java @@ -18,7 +18,6 @@ package io.jboot.web.validate; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 - * @Package io.jboot.web.controller.validate */ public class ValidateRenderType { -- Gitee From 674378a8f07d701ab3e8d173280df139ce13e549 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 19 Aug 2020 16:15:01 +0800 Subject: [PATCH 0475/1965] optimize JbootPaginateDirective --- .../web/directive/JbootPaginateDirective.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/directive/JbootPaginateDirective.java b/src/main/java/io/jboot/web/directive/JbootPaginateDirective.java index ec637d9b..8a301a94 100644 --- a/src/main/java/io/jboot/web/directive/JbootPaginateDirective.java +++ b/src/main/java/io/jboot/web/directive/JbootPaginateDirective.java @@ -15,6 +15,7 @@ */ package io.jboot.web.directive; +import com.jfinal.core.Controller; import com.jfinal.plugin.activerecord.Page; import com.jfinal.template.Env; import com.jfinal.template.io.Writer; @@ -24,6 +25,7 @@ import io.jboot.web.controller.JbootControllerContext; import io.jboot.web.directive.base.PaginateDirectiveBase; import javax.servlet.http.HttpServletRequest; +import java.util.Enumeration; public class JbootPaginateDirective extends PaginateDirectiveBase { @@ -94,8 +96,27 @@ public class JbootPaginateDirective extends PaginateDirectiveBase { @Override protected Page getPage(Env env, Scope scope, Writer writer) { + Controller controller = JbootControllerContext.get(); + if (controller == null) { + return null; + } + String pageAttr = getPara(KEY_PAGE_ATTR, scope, DEFAULT_PAGE_ATTR); - return JbootControllerContext.get().getAttr(pageAttr); + Page page = controller.getAttr(pageAttr); + if (page != null) { + return page; + } + + Enumeration attrNames = controller.getAttrNames(); + if (attrNames != null) { + while (attrNames.hasMoreElements()) { + Object attrValue = controller.get(attrNames.nextElement()); + if (attrValue instanceof Page) { + return (Page) attrValue; + } + } + } + return null; } } \ No newline at end of file -- Gitee From 7bacd30d689c87654bb3e83202d69cc986a074e3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 20 Aug 2020 17:34:21 +0800 Subject: [PATCH 0476/1965] update docs and vuepress configs --- .gitignore | 6 +- README.md | 2 +- doc/.vuepress/config.js | 112 +++ doc/.vuepress/styles/index.styl | 27 + doc/CNAME | 1 + doc/deploy.sh | 31 + doc/docs/benchmark.md | 109 +++ doc/docs/communication.md | 2 +- doc/docs/hotload.md | 2 +- doc/docs/metrics.md | 2 +- doc/docs/readme.md | 34 + doc/docs/start.md | 837 ++++++++++++++++++ doc/docs/static/images/0008.png | Bin 0 -> 26372 bytes doc/docs/static/images/0009.png | Bin 0 -> 32786 bytes doc/docs/static/images/0010.png | Bin 0 -> 55428 bytes doc/docs/static/images/0011.png | Bin 0 -> 33128 bytes doc/docs/static/images/0012.png | Bin 0 -> 33591 bytes doc/docs/static/images/0013.png | Bin 0 -> 54600 bytes doc/docs/static/images/0014.png | Bin 0 -> 42917 bytes doc/docs/static/images/0015.png | Bin 0 -> 66500 bytes doc/docs/static/images/0016.png | Bin 0 -> 77898 bytes doc/docs/static/images/1_eclipse.png | Bin 0 -> 61556 bytes doc/docs/static/images/1_ieda.png | Bin 0 -> 57680 bytes doc/docs/{imgs => static/images}/grafana.png | Bin .../images}/idea-auto-build.jpg | Bin doc/docs/static/images/idea_001.jpg | Bin 0 -> 467954 bytes doc/docs/static/images/idea_002.png | Bin 0 -> 128049 bytes doc/docs/static/images/idea_003.png | Bin 0 -> 128688 bytes doc/docs/static/images/idea_004.png | Bin 0 -> 243711 bytes doc/docs/static/images/idea_005.png | Bin 0 -> 131264 bytes doc/docs/static/images/idea_006.png | Bin 0 -> 322796 bytes doc/docs/static/images/idea_007.png | Bin 0 -> 133783 bytes .../images}/jboot-wechat-group.png | Bin doc/readme.md | 67 +- 34 files changed, 1192 insertions(+), 40 deletions(-) create mode 100644 doc/.vuepress/config.js create mode 100644 doc/.vuepress/styles/index.styl create mode 100644 doc/CNAME create mode 100755 doc/deploy.sh create mode 100644 doc/docs/benchmark.md create mode 100644 doc/docs/readme.md create mode 100644 doc/docs/start.md create mode 100644 doc/docs/static/images/0008.png create mode 100644 doc/docs/static/images/0009.png create mode 100644 doc/docs/static/images/0010.png create mode 100644 doc/docs/static/images/0011.png create mode 100644 doc/docs/static/images/0012.png create mode 100644 doc/docs/static/images/0013.png create mode 100644 doc/docs/static/images/0014.png create mode 100644 doc/docs/static/images/0015.png create mode 100644 doc/docs/static/images/0016.png create mode 100755 doc/docs/static/images/1_eclipse.png create mode 100755 doc/docs/static/images/1_ieda.png rename doc/docs/{imgs => static/images}/grafana.png (100%) rename doc/docs/{imgs => static/images}/idea-auto-build.jpg (100%) create mode 100644 doc/docs/static/images/idea_001.jpg create mode 100644 doc/docs/static/images/idea_002.png create mode 100644 doc/docs/static/images/idea_003.png create mode 100644 doc/docs/static/images/idea_004.png create mode 100644 doc/docs/static/images/idea_005.png create mode 100644 doc/docs/static/images/idea_006.png create mode 100644 doc/docs/static/images/idea_007.png rename doc/docs/{imgs => static/images}/jboot-wechat-group.png (100%) diff --git a/.gitignore b/.gitignore index be3e6483..ae405371 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,8 @@ logs *.classpath .DS_Store *.patch -~$* \ No newline at end of file +~$* +node_modules +package.json +yarn.lock +dist \ No newline at end of file diff --git a/README.md b/README.md index 091aec3e..81fa5d72 100644 --- a/README.md +++ b/README.md @@ -68,5 +68,5 @@ public class HelloworldController extends JbootController { ## 微信交流群 -![](./doc/docs/imgs/jboot-wechat-group.png) +![](./doc/docs/static/images/jboot-wechat-group.png) diff --git a/doc/.vuepress/config.js b/doc/.vuepress/config.js new file mode 100644 index 00000000..7bb9793c --- /dev/null +++ b/doc/.vuepress/config.js @@ -0,0 +1,112 @@ +//参考: +// https://github.com/vuejs/vuepress/blob/master/packages/docs/docs/.vuepress/config.js +// https://vuepress-theme-reco.recoluan.com/views/1.x/ +module.exports = { + theme: 'reco', + themeConfig: { + //腾讯 404 公益配置 + noFoundPageByTencent: false, + + mode: 'light', // 默认 auto,auto 跟随系统,dark 暗色模式,light 亮色模式 + modePicker: false, // 默认 true,false 不显示模式调节按钮,true 则显示 + + // author + author: 'jboot', + + // if your docs are in a different repo from your main project: + docsRepo: 'yangfuhai/jboot', + // if your docs are in a specific branch (defaults to 'master'): + docsBranch: 'master', + // if your docs are not at the root of the repo: + docsDir: 'doc', + // defaults to false, set to true to enable + editLinks: true, + // custom text for edit link. Defaults to "Edit this page" + editLinkText: '编辑此页面', + + + lastUpdated: '更新时间', // string | boolean + + nav: [ + {text: '首页', link: '/'}, + {text: '提问', link: 'https://gitee.com/JPressProjects/jpress/issues'}, + {text: 'JPress', link: 'http://www.jpress.io'}, + { + text: '源码下载', items: [ + {text: 'Gitee', link: 'https://gitee.com/JbootProjects/jboot'}, + {text: 'Github', link: 'https://github.com/yangfuhai/jboot'} + ] + }, + ], + + sidebar: { + '/': [{ + title: '认识 Jboot', + collapsable: false, + children: [ + {title: 'Jboot 简介', path: '/'}, + {title: '快速开始', path: '/docs/start'} + ], + }, + + { + title: '开发文档', + collapsable: false, + children: [ + {title: '安装', path: '/docs/install'}, + {title: '配置', path: '/docs/config'}, + {title: 'MVC', path: '/docs/mvc'}, + {title: 'WebSocket', path: '/docs/websocket'}, + {title: 'Jwt', path: '/docs/jwt'}, + {title: 'Swagger', path: '/docs/swagger'}, + {title: 'Aop', path: '/docs/aop'}, + {title: '数据库', path: '/docs/db'}, + {title: '缓存', path: '/docs/cache'}, + {title: 'Redis', path: '/docs/redis'}, + {title: 'RPC 调用', path: '/docs/rpc'}, + {title: 'MQ 消息队列', path: '/docs/mq'}, + {title: 'Gateway 网关', path: '/docs/gateway'}, + {title: '任务调度', path: '/docs/schedule'}, + {title: '限流', path: '/docs/limit'}, + {title: '监控', path: '/docs/metrics'}, + {title: '事件机制', path: '/docs/event'}, + {title: '序列化', path: '/docs/serialize'}, + {title: 'SPI扩展', path: '/docs/spi'}, + {title: '代码生成器', path: '/docs/codegen'}, + {title: '项目构建', path: '/docs/build'}, + {title: '项目部署', path: '/docs/deploy'}, + {title: 'Docker', path: '/docs/docker'}, + ], + }, + + { + title: '性能', + collapsable: false, + children: [ + {title: '性能测试', path: '/docs/benchmark'}, + ], + } + ], + }, + sidebarDepth: 3 + + + }, + // "@vuepress-reco/vuepress-plugin-screenfull": "^1.0.1", + // "@vuepress-reco/vuepress-plugin-back-to-top": "^1.5.5", + plugins: ['@vuepress-reco/vuepress-plugin-back-to-top','@vuepress-reco/vuepress-plugin-screenfull'], + title: 'Jboot 官方网站', + description: 'Jboot 一个开源的分布式、商业级微服务框架。', + head: [ + ['link', {rel: 'icon', href: '/logo.png'}], + ['script', {}, ` + var _hmt = _hmt || []; + (function() { + var hm = document.createElement("script"); + hm.src = "https://hm.baidu.com/hm.js?d6b9b94a6fafaa41c63920e1af80bcaf"; + var s = document.getElementsByTagName("script")[0]; + s.parentNode.insertBefore(hm, s); + })(); + `] + ] +} diff --git a/doc/.vuepress/styles/index.styl b/doc/.vuepress/styles/index.styl new file mode 100644 index 00000000..16ddd550 --- /dev/null +++ b/doc/.vuepress/styles/index.styl @@ -0,0 +1,27 @@ +#app .page-title, #app .theme-reco-content.content__default, #app footer, #app .page-nav, #app .comments-wrapper { + max-width: 960px !important; + // padding: 1.5rem !important; +} + +.page { + /* 处理文本溢出 */ + word-wrap: break-word !important; + /* 允许再单词内换行 */ + word-break: break-all; + padding-top: 4rem !important; +} + +.page .page-title { + // 调整标题容器边距 + padding: 0rem 1.5rem !important; +} + +#app .theme-reco-content.content__default > h1, #app .theme-reco-content.content__default > p:nth-child(2) { + // 处理文档的第一行标题 + display: none !important; +} + +.home-center.content__default{ + padding-left: 0 !important; + padding-right: 0 !important; +} \ No newline at end of file diff --git a/doc/CNAME b/doc/CNAME new file mode 100644 index 00000000..cf681f25 --- /dev/null +++ b/doc/CNAME @@ -0,0 +1 @@ +jboot.io \ No newline at end of file diff --git a/doc/deploy.sh b/doc/deploy.sh new file mode 100755 index 00000000..894ae8da --- /dev/null +++ b/doc/deploy.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env sh + +# abort on errors +set -e + +# build +vuepress build . + + +cp CNAME .vuepress/dist + +# navigate into the build output directory +cd .vuepress/dist + + +# if you are deploying to a custom domain +# echo 'www.example.com' > CNAME + +git init +git add -A +git commit -m 'deploy' + +# if you are deploying to https://.github.io +git push -f git@github.com:yangfuhai/yangfuhai.github.io.git master + +git push -f git@github.com:yangfuhai/yangfuhai.github.io.git master:gh-pages + +# if you are deploying to https://.github.io/ +# git push -f git@github.com:/.git master:gh-pages + +cd - \ No newline at end of file diff --git a/doc/docs/benchmark.md b/doc/docs/benchmark.md new file mode 100644 index 00000000..66a61e26 --- /dev/null +++ b/doc/docs/benchmark.md @@ -0,0 +1,109 @@ +--- +sidebar: auto +--- +# Jboot 性能测试 + +## 测试方式 + +通过 apache benchmark 工具进行压力测试 + +## 测试环境 + +* JDK信息: + * java version "1.8.0_25" + * Java(TM) SE Runtime Environment (build 1.8.0_25-b17) + * Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode) + +* 硬件信息 + * 处理器:2.3 GHz Intel Core i7 + * 内存:16 GB 1600 MHz DDR3 + * 系统:macOS 10.13.4 (17E202) + * 硬件:MacBook Pro (Retina, 15-inch, Late 2013 + +## 测试代码 + +测试代码: + +```java +@RequestMapping("/") +public class HelloDemo extends JbootController { + + public static void main(String[] args) { + Jboot.setBootArg("jboot.mode","product"); + Jboot.run(args); + } + + public void index() { + renderText("hello jboot ..."); + } +} +``` + +代码含义: + +* 默认使用 undertow 服务器 +* 把配置设置为生产模式,生产模式不会打印调试日志 + + +## 测试结果 + +模拟10个并发,10000次访问: + +`ab -c10 -n10000 http://localhost:8080/` + + +``` +This is ApacheBench, Version 2.3 <$Revision: 1807734 $> +Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ +Licensed to The Apache Software Foundation, http://www.apache.org/ + +Benchmarking localhost (be patient) +Completed 1000 requests +Completed 2000 requests +Completed 3000 requests +Completed 4000 requests +Completed 5000 requests +Completed 6000 requests +Completed 7000 requests +Completed 8000 requests +Completed 9000 requests +Completed 10000 requests +Finished 10000 requests + + +Server Software: +Server Hostname: localhost +Server Port: 8080 + +Document Path: / +Document Length: 15 bytes + +Concurrency Level: 10 +Time taken for tests: 0.716 seconds +Complete requests: 10000 +Failed requests: 0 +Total transferred: 2330000 bytes +HTML transferred: 150000 bytes +Requests per second: 13970.07 [#/sec] (mean) +Time per request: 0.716 [ms] (mean) +Time per request: 0.072 [ms] (mean, across all concurrent requests) +Transfer rate: 3178.74 [Kbytes/sec] received + +Connection Times (ms) + min mean[+/-sd] median max +Connect: 0 0 0.1 0 1 +Processing: 0 0 0.1 0 3 +Waiting: 0 0 0.1 0 3 +Total: 0 1 0.1 1 4 + +Percentage of the requests served within a certain time (ms) + 50% 1 + 66% 1 + 75% 1 + 80% 1 + 90% 1 + 95% 1 + 98% 1 + 99% 1 + 100% 4 (longest request) + ``` \ No newline at end of file diff --git a/doc/docs/communication.md b/doc/docs/communication.md index 6242f8b1..bb1d13fe 100644 --- a/doc/docs/communication.md +++ b/doc/docs/communication.md @@ -8,5 +8,5 @@ - 微信群 - ![](./imgs/jboot-wechat-group.png) + ![](./static/images/jboot-wechat-group.png) \ No newline at end of file diff --git a/doc/docs/hotload.md b/doc/docs/hotload.md index adea7722..2d3c7962 100644 --- a/doc/docs/hotload.md +++ b/doc/docs/hotload.md @@ -36,4 +36,4 @@ undertow.hotSwapClassPrefix = xxx1.com, xxx2.com - **错误2:没有热加载** 在 idea 开发工具中,可能会出现未正确进行热加载的情况,一般是没有配置 idea 的自动编译功能。 -![](./imgs/idea-auto-build.jpg) \ No newline at end of file +![](./static/images/idea-auto-build.jpg) \ No newline at end of file diff --git a/doc/docs/metrics.md b/doc/docs/metrics.md index 5ef5a117..7b150157 100644 --- a/doc/docs/metrics.md +++ b/doc/docs/metrics.md @@ -10,7 +10,7 @@ Jboot 内置了一套监控机制,可以用来监控 Controller、Service 等 这些监控的数据,我们可以输出到 slf4j 日志,可以输入到网页的json,也可以通过配置直接把数据输出到 grafana,使用 grafana 面板来进行可视化的数据监控,如下图。 -![grafana](./imgs/grafana.png) +![grafana](./static/images/grafana.png) ## Metrics 输出到日志 diff --git a/doc/docs/readme.md b/doc/docs/readme.md new file mode 100644 index 00000000..fc672652 --- /dev/null +++ b/doc/docs/readme.md @@ -0,0 +1,34 @@ +## 文档目录 + +- [安装](install.md) +- [2分钟快速开始](quickstart.md) +- [热加载](hotload.md) +- [Undertow](undertow.md) +- [配置](config.md) +- [JFinalConfig](jfinalConfig.md) +- [WebSocket](websocket.md) +- [MVC](mvc.md) +- [AOP](aop.md) +- [数据库操作](db.md) +- [缓存](cache.md) +- [RPC远程调用](rpc.md) +- [MQ消息队列](mq.md) +- [Gateway 网关](gateway.md) +- [任务调度](schedule.md) +- [限流](limit.md) +- [监控](metrics.md) +- [序列化](serialize.md) +- [事件机制](event.md) +- [SPI扩展机制](spi.md) +- [代码生成器](codegen.md) +- [项目构建](build.md) +- [项目部署](deploy.md) +- [Jboot与Docker](docker.md) +- [1.x 升级到 2.x 教程](upgrade.md) +- [交流社区、QQ群和微信群](communication.md) +- 第三方组件的支持 + - [sentinel 限流降级](sentinel.md) + - [redis](redis.md) + - [shiro](shiro.md) + - [jwt](jwt.md) + - [swagger](swagger.md) diff --git a/doc/docs/start.md b/doc/docs/start.md new file mode 100644 index 00000000..6226f942 --- /dev/null +++ b/doc/docs/start.md @@ -0,0 +1,837 @@ +--- +sidebar: false +--- + +# 开始使用Jboot + +Jboot 使用到了如下的技术,了解 Jboot 之前,请先保证您已经了掌握如下技术: + + * 熟悉 Java 编程语言 + * 熟悉 maven 的基本原理 + * 熟悉 IntelliJ IDEA 或者 Eclipse 等编辑器的使用 + +交流 QQ 群: +* 群1:601440615 (已满) +* 群2:719614554 (开放中) + +## 创建项目 + +### 通过 IntelliJ IDEA 创建项目 + +IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后完成后开始安装,安装过程略。 + +第一步:打开 IntelliJ IDEA 创建 maven 项目,如下图: +![](./static/images/idea_001.jpg "") + +第二步:填写 maven 项目的 GroupId、ArtifactId 和 Version + +* GroupId 一般是包名,用来做项目的唯一标识 +* ArtifactId 一般是项目名 +* Version 是项目的版本 +![](./static/images/idea_002.png "") + +第三步:填写 项目存储路径 +![](./static/images/idea_003.png "") + +创建完毕后,我们会看到如下图所示,注意点击 Enable Auto-Import. +![](./static/images/idea_004.png "") + +### 通过 Eclipse 创建项目 +略,和 通过 IntelliJ IDEA 创建项目 基本相同。 + +## Maven 依赖 + +通过 以上步骤建立项目后,我们会在项目目录下找到 pom.xml 文件,这个文件是 maven 的核心文件,maven 是通过 pom.xml 对项目进行依赖配置和管理的。 + +我们需要在 pom.xml 里添加对 Jboot 的依赖配置,如下代码: + +```xml + + io.jboot + jboot + 3.3.6 + +``` + +如下图所示: +![](./static/images/idea_005.png "") + +## Hello World + +一般情况下,对一个新项目的了解是从 Hello World 开始的,因此,我们需要通过 Jboot 来写一个 Hello World 程序。 + +这个 Hello World 的需求是: + +> **通过编写代码,我们在浏览器访问后输出 “Hello World Jboot” 的文字内容。** + +通过以上步骤,我们创建好了项目、添加好了 jboot 的maven依赖,接下来我们需要来创建一个叫 IndexController 的java文件, +![](./static/images/idea_006.png "") +![](./static/images/idea_007.png "") + +IndexController 的代码如下: + +```java +import io.jboot.app.JbootApplication; +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; + +@RequestMapping("/") +public class IndexController extends JbootController { + + public void index() { + renderText("Hello World Jboot"); + } + + + public static void main(String[] args) { + JbootApplication.run(args); + } +} +``` +以上代码需要注意以下几点: +1. IndexController 需要继承 JbootController 或者 Controller +1. 需要添加 @RequestMapping("/") 请求映射配置 +1. 通过编写 `index()` 方法来接收请求,并向浏览器输出 "Hello World Jboot" , 此处注意:必须叫 `index()` 不能修改名字,后续会讲到其原理。 + +通过运行 IndexController 的 main() 方法,我们可以看到如下的日志输出: + +``` + + ____ ____ ___ ___ ______ + | || \ / \ / \ | | + |__ || o )| || || | + __| || || O || O ||_| |_| +/ | || O || || | | | +\ ` || || || | | | + \____||_____| \___/ \___/ |__| + + + +JbootApplication { mode='dev', version='3.3.6', jfinalConfig='io.jboot.core.JbootCoreConfig' } +Classpath : /Users/michael/git/jboot/target/test-classes/ +Starting JFinal 4.2 -> http://127.0.0.1:8080 +Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 +ClassScanner scan classpath : /Users/michael/git/jboot/target/test-classes +ClassScanner scan classpath : /Users/michael/git/jboot/target/classes +Starting Complete in 1.3 seconds. Welcome To The JFinal World (^_^) + +JbootResourceLoader started, Watched resource path name : webapp +``` + +我们看到最后一行日志的内容是: + +``` +server started success , url : http://127.0.0.1:8080/ +``` +此时我们通过浏览器访问:http://127.0.0.1:8080 , 就可以看到如下内容: +![](./static/images/0008.png "") + + + +## 链接数据库 + +在 Java Web 开发中,几乎 99% 以上的项目都需要和数据库打交道,因此,了解 Jboot 如何连接数据成为了必须。 + +通过 Jboot 连接数据库只需要做两步: +1. 创建 jboot.properties 配置文件 +1. 在 jboot.properties 添加数据库连接信息 + +第一步:在项目的resource目录下创建 jboot.properties 文件,此时,项目的目录结构应该如下: + +``` +├── pom.xml +├── src +│   ├── main +│   │   ├── java +│   │   │   └── IndexController.java +│   │   └── resources +│   │   └── jboot.properties #注意文件目录不要错了 +│   └── test +│   └── java +``` + +第二步:在 jboot.properties 文件添加如下数据库信息: + +``` +jboot.datasource.type = mysql +jboot.datasource.url = jdbc:mysql://127.0.0.1:3306/jbootdemo +jboot.datasource.user = root +jboot.datasource.password = +``` + +* jboot.datasource.type:配置的是数据库的类型,目前 Jboot 支持的数据库类型有:Mysql 、Oracle 、SqlServer 、Postgresql 、Sqlite 和 其他标准的数据库。 +* jboot.datasource.url: 配置的是数据库的连接信息 +* jboot.datasource.user: 配置的是数据库的连接账号 +* jboot.datasource.password: 配置的是数据库的连接密码,没有密码可以留空 + +通过配置完毕后,Jboot就已经有了访问数据库的能力,我们可以在 IndexController 写一个 `dbtest()` 方法,来测试下 Jboot 的数据库访问能力,代码如下: + +``` +@RequestMapping("/") +public class IndexController extends JbootController { + + public void index() { + renderText("Hello World Jboot"); + } + + public void dbtest(){ + List records = Db.find("select * from user"); + renderText(Arrays.toString(records.toArray())); + } + + + public static void main(String[] args) { + JbootApplication.run(args); + } +} +``` +注意:以上代码能够正式运行的前提是: +1. 你本地安装好mysql数据库,并创建好库 `jbootdemo` ,因为数据库的连接url是:`jdbc:mysql://127.0.0.1:3306/jbootdemo` +2. jbootdemo 下要有数据表 user,因为 sql 查询内容是:`select * from user` + +例如:作者本地数据库的内容如下: +![](./static/images/0010.png "") + +运行 IndexController 的 `main()` 方法,并访问 `http://127.0.0.1:8080/dbtest`,会看到如下内容所示: +![](./static/images/0009.png "") + +此时,证明 Jboot 已经能够准确访问数据库。 + + + +## 使用代码生成器 +Jboot 内置了一个简易的代码生成器,通过代码生成器运行,Jboot帮开发者生成每个数据库表对应 java 的 model 实体类,同时可以生成带有增、删、改、查基本数据库操作能力的 service 层代码。 + +在使用 Jboot 代码生成器之前,我们需要在 jboot.properties 配置好数据库的连接信息(以上内容已经提到)。并编写任意名字带有`main()`方法的执行类,例如我们叫 CodeGenerator, 代码如下: + +```java +public class CodeGenerator { + + public static void main(String args[]){ + + // 配置数据库的数据源 + JbootApplication.setBootArg("jboot.datasource.url", "jdbc:mysql://127.0.0.1:3306/jbootdemo"); + JbootApplication.setBootArg("jboot.datasource.user", "root"); + JbootApplication.setBootArg("jboot.datasource.password", "123456"); + + String modelPackage = "io.jboot.test.codegen.model"; + String baseModelPackage = modelPackage + ".base"; + + String modelDir = PathKit.getWebRootPath() + "/src/main/java/" + modelPackage.replace(".", "/"); + String baseModelDir = PathKit.getWebRootPath() + "/src/main/java/" + baseModelPackage.replace(".", "/"); + + System.out.println("start generate..."); + System.out.println("generate dir:" + modelDir); + + // 生成 Model + new JbootBaseModelGenerator(baseModelPackage, baseModelDir).setGenerateRemarks(true).generate(); + new JbootModelGenerator(modelPackage, baseModelPackage, modelDir).generate(); + + + String servicePackage = "io.jboot.test.codegen.service"; + String serviceImplPackage = "io.jboot.test.codegen.service.impl"; + + String serviceOutputDir = PathKit.getWebRootPath() + "/src/main/java/" + servicePackage.replace(".", "/"); + String serviceImplOutputDir = PathKit.getWebRootPath() + "/src/main/java/" + serviceImplPackage.replace(".", "/"); + + // 生成 Service 接口 及其 实现类 + new JbootServiceInterfaceGenerator(servicePackage, serviceOutputDir, modelPackage).generate(); + new JbootServiceImplGenerator(servicePackage, serviceImplOutputDir, modelPackage).setImplName("impl").generate(); + + + } +} +``` + +运行 CodeGenerator 的 `main()` 方法之后,我们能看到 Jboot 已经帮我们创建好对应的包名和类名,此时,项目的目录如下: + +``` +├── pom.xml +├── src +│   ├── main +│   │   ├── java +│   │   │   ├── CodeGenerator.java +│   │   │   ├── IndexController.java +│   │   │   └── com +│   │   │   └── xxx +│   │   │   ├── model +│   │   │   │   ├── Article.java +│   │   │   │   ├── User.java +│   │   │   │   └── base +│   │   │   │   ├── BaseArticle.java +│   │   │   │   └── BaseUser.java +│   │   │   └── service +│   │   │   ├── ArticleService.java +│   │   │   ├── UserService.java +│   │   │   └── impl +│   │   │   ├── ArticleServiceImpl.java +│   │   │   └── UserServiceImpl.java +│   │   └── resources +│   │   └── jboot.properties +│   └── test +│   └── java +``` + +通过 Jboot 代码生成器的运行,项目对应的 model 类和 service 会自动生成,同时 Service 层的代码以及带有了对数据库增、删、改、查的基本能力. + +需要注意的是: +**再次运行该代码生成器的时候,BaseUser、BaseArticle会被重新覆盖,其他代码不会被覆盖。** 若需要重新生成 service 层 和 User、Article 等代码,需要手动删除后,再次运行代码生成器 CodeGenerator 。 + +## 自动注入 +Jboot 通过 Google Guice 提供了强健稳定的代码注入功能,使用注入功能只需要了解一下三个注解: +1. @Bean : 声明此类可以被自动注入 +2. @Inject : 对属性进行赋值注入 + +通过代码生成器生成的Service层代码就已经默认添加上了 @Bean 和 @Singleton 这两个配置,生成的代码如下: + +```java +package com.xxx.service.impl; + +import io.jboot.aop.annotation.Bean; +import com.xxx.service.UserService; +import com.xxx.model.User; +import io.jboot.service.JbootServiceBase; + +import javax.inject.Singleton; + +@Bean +public class UserServiceImpl extends JbootServiceBase implements UserService { + +} +``` + +我们使用到 UserService 接口的时候,只需要添加 @Inject 注解即可,例如:在 IndexController 需要用到 UserService,代码如下: + +```java +@RequestMapping("/") +public class IndexController extends JbootController { + + @Inject + private UserService userService; + + public void index() { + renderText("Hello World Jboot"); + } + + public void dbtest() { + List records = Db.find("select * from user"); + renderText(Arrays.toString(records.toArray())); + } + + + public void users() { + // 这里用到了 userService 的查询方法 + List users = userService.findAll(); + renderText(Arrays.toString(users.toArray())); + } + + + public static void main(String[] args) { + Jboot.run(args); + } +} +``` + +运行 `main()` 方法后,我们通过浏览访问 `http://127.0.0.1:8080/users` ,此时,页面显示的内容和 访问 `http://127.0.0.1:8080/dbtest` 的效果是一样的: +![](./static/images/0009.png "") + + + +## 数据库的增删改查 +在本章节,我们要完成一个小型的项目,这个项目是一个用户管理系统,他具有以下功能: + +* 显示用户列表,带有分页的功能 +* 可以对单个用户删除 +* 可以对用户进行修改 +* 可以添加新的用户 + +### 分页查询 + +我们可以继续来改造 IndexController,通过修改代码生成器的生成的 UserService,来达到上述要求的功能。 + +在上述的章节里,我们知道,通过如下的代码可以获得所有的用户信息: + +```java + public void users() { + List users = userService.findAll(); + renderText(Arrays.toString(users.toArray())); +} +``` + +如果要分页,我们需要在UserService添加一个分页的方法,并在 UserServiceImpl 来实现这个分页的方法,代码如下: + +UserService.java + +```java +public interface UserService { + + // 代码生成器生成的其他方法略 + //... + + public Page paginate(int page, int size); +} +``` + +UserServiceImpl.java + +```java +@Bean +public class UserServiceImpl extends JbootServiceBase implements UserService { + + public Page paginate(int page, int size) { + return DAO.paginate(page, size); + } +} +``` + +为了代码更加简洁直观,我们新建一个 UserController 来做用户相关的增删改查功能,代码如下: + +```java +@RequestMapping("/user") +public class UserController extends JbootController { + + @Inject + private UserService userService; + + public void index() { + int page = getParaToInt("page", 1); + Page userPage = userService.paginate(page, 10); + setAttr("pageData", userPage); + render("/user.html"); + } + +} +``` + +* `getParaToInt()` 可以获得request提交过来的page数据,例如:http://127.0.0.1:8080/user?page=100 ,此时,代码里 page 的值是 100,当不传值得时候,默认值是 1 。 +* 通过 userService.paginate 查询数据库,返回一个 Page 对象,里面包含了 当前页码、总页码 和 数据列表等信息。 +* 通过 `setAttr()` 把数据结果传递到页面 + +把数据传递到 user.html 后,需要 user.html 把具体的数据和分页相关在网页上列出来。 + +第一步:完善数据的显示,user.html内容如下: + +```html + + + + + user index + + + + + + + + + #for(user : pageData.list ) + + + + + + #end +
ID登录名密码
#(user.id)#(user.login_name)#(user.password)
+ + +``` +此时,运行 `main()` 方法,访问 `http://127.0.0.1:8080/user` ,页面显示内容如下: + +![](./static/images/0011.png "") + +第二步:完善分页功能。 + +Jboot应用的分页功能需要自定义一个分页标签,自定义分页标签非常简单,代码内容如下: + +```java +@JFinalDirective("myPaginate") +public class MyPaginateDirective extends JbootPaginateDirective { + + protected String getPageAttrName() { + return "pageData"; //这个值要和Controller里setAttr的第一个参数值相同 + } +} + +``` +然后再修改 user.html 内容如下: + +```html + + + + + user index + + + + + + + + + #for(user : pageData.list ) + + + + + + #end +
ID登录名密码
#(user.id)#(user.login_name)#(user.password)
+#myPaginate() + #for(page : pages) + #(page.text??) + #end +#end + + +``` + +此时,运行 `main()` 方法,访问 `http://127.0.0.1:8080/user` ,页面显示内容如下: + +![](./static/images/0012.png "") + +由于数据量太小,同时在我们的代码里,要求每页显示10条数据,所以页面才显示了第一页,当我们在数据库添加数据量超过10条的时候,页面显示内容如下: + +![](./static/images/0013.png "") + +同时,上一页、下一页等功能正常使用,如下图: + +![](./static/images/0014.png "") + +实际上,#myPaginate() 自定义分页标签还可以做更多的配置,包括功能和样式等,但是这不是本章节要讨论的内容了。 + +### 新增功能 +为了实现新增功能,我们需要写一个叫 add.html 的页面,并写对应的 Controller,保证可以访问。 + +add.html 的代码如下: + +```html + + + + + user add + + +

+ 登录名:
+ 密码:
+ +
+ + +``` + +通过 add.html 内容我们能看到,当用户点击 `提交数据` 按钮的时候,页面会把数据提交到 `/user/doSave` 这个路径上去,所以,需要我们在 UserController 编写一个叫做 `doSave()` 的方法来接收数据,并保存到数据库。 + +doSave() 方法内容如下: + +```java +public void doSave() { + String loginName = getPara("login_name"); + String password = getPara("password"); + + User user = new User(); + user.setLoginName(loginName); + user.setPassword(password); + + user.save(); + + redirect("/user"); +} +``` + +`doSave()` 方法的主要作用是接收数据、把数据保存到数据库、然后跳转到 `/user` 这个页面去。 + +### 修改功能 + +为了减少代码量,我们直接把 add.html 改造成为可用做新增,也可以用作修改的功能(通常在商业项目中也会这么做),因此,我们需要简单修改下 add.html 代码和 add() 这个方法的代码。 + +add.html + +```html + + + + + user add + + +
+ + 登录名:
+ 密码:
+ +
+ + +``` + +和新增功能的html对比,增加了 `` 这行代码。 + +add() 方法内容如下: + +```java +public void add() { + int id = getParaToInt("id", 0); + if (id > 0) { //有id ,说明有数据提交过来,用来做修改的标识。 + setAttr("id", id); + } + render("/add.html"); +} +``` + +同时, doSave()方法也需要修改下,用来区分是新增还是修改,代码如下: + +```java +public void doSave() { + String loginName = getPara("login_name"); + String password = getPara("password"); + + long id = getParaToLong("id",0l); + + + User user = new User(); + user.setLoginName(loginName); + user.setPassword(password); + + if (id > 0){ //说明是更新 + user.setId(id); + user.update(); + }else { //说明是新增 + user.save(); + } + + redirect("/user"); +} +``` + +最后,我们在改造下 user.html ,在表格的后面添加一个 `修改` 的连接, user.html 代码如下: + +```html + + + + + user index + + + + + + + + + + #for(user : pageData.list ) + + + + + + + #end +
ID登录名密码操作
#(user.id)#(user.login_name)#(user.password)修改
+#myPaginate() + #for(page : pages) + #(page.text??) + #end +#end + + +``` + +此时,页面内容如下,修改功能正常使用。 +![](./static/images/0015.png "") + +### 删除功能 + +删除功能更加简单,只需要在Controller接收ID,然后调用 userService.delete() 方法就可以了,改造 user.html 代码如下: + +```html + + + + + user index + + + + + + + + + + #for(user : pageData.list ) + + + + + + + #end +
ID登录名密码操作
#(user.id)#(user.login_name)#(user.password) + 修改 + 删除 +
+#myPaginate() + #for(page : pages) + #(page.text??) + #end +#end + + +``` + +页面显示如下: +![](./static/images/0016.png "") + +我们只需要在 UserController 编写一个 del() 方法,接收id、删除数据库数据,并跳转回 /user 即可完成任务,代码如下: + +```java + public void del() { + long id = getParaToLong("id",0l); + userService.deleteById(id); + redirect("/user"); +} +``` + +到目前为止,增删改查所有功能完成。 + + +## 使用缓存提高性能 +通过以上内容,我们可以使用Jboot开发一个具有增、删、改、查基本功能的Java Web 应用,但是,在互联网的应用里,高并发的要求可以说是必不可少的,缓存在提高应用性能和并发上有绝对的话语权。 + +在 Jboot 里,我们如何来使用缓存呢? + +Jboot 提供了两种方案: + +1. 注解 +1. 手写代码 + +在注解中,Jboot提供了4个注解,方便的对缓存进行操作,他们分别是: + +* @Cacheable +* @CachePut +* @CacheEvict +* @CachesEvict + +如何来使用呢? + +在以上的章节里,我们知道,如下的代码是一个分页查询的功能: + +```java +public class UserServiceImpl extends JbootServiceBase implements UserService { + + public Page paginate(int page, int size) { + return DAO.paginate(page, size); + } +} +``` + +如何来让 `paginate(int page, int size)` 方法具有缓存的功能呢? + +非常简单: + +```java +public class UserServiceImpl extends JbootServiceBase implements UserService { + + @Cacheable(name = "myCache",key = "page:#(page)-#(size)") + public Page paginate(int page, int size) { + return DAO.paginate(page, size); + } +} +``` +只需要添加 `@Cacheable(name = "myCache",key = "page:#(page)-#(size)")` 这个注解。 + +在Jboot中,默认的缓存为 `EhCache` , 这个注解的含义是: + +* 在EhCache中创建一个缓存为myCache的缓存区 +* 当查询第 1 页的时候,缓存的key为:`page:1-10`,因为 `paginate(int page, int size)` 方法在执行的时候,传递过来的值分别是:page=1,size=10 +* 当查询第 2 页的时候,缓存的key为:`page:2-10`,原因同上。 + +当 `paginate(int page, int size)` 方法使用 @Cacheable 缓存之后,只有第一次访问的时候去查询数据库,之后的访问会直接从缓存中获取数据,大大提高了性能。 + +但是... + +使用缓存也会带来一些问题,因为 `paginate(int page, int size)` 方法不再访问数据库,从而导致我们在数据库对数据进行增、删、改,这个页面数据都不会再发生变化。 + +要让 `paginate(int page, int size)` 方法与数据库同步,怎么办呢? + +需要我们在对数据库进行 增、删、改 的时候,清除这个方法里的缓存数据。 + +代码如下: + +```java +public class UserServiceImpl extends JbootServiceBase implements UserService { + + @Cacheable(name = "myCache",key = "page:#(page)-#(size)") + public Page paginate(int page, int size) { + return DAO.paginate(page, size); + } + + @Override + @CacheEvict(name = "myCache",key = "*") + public boolean save(User model) { + return super.save(model); + } + + @Override + @CacheEvict(name = "myCache",key = "*") + public boolean update(User model) { + return super.update(model); + } + + @Override + @CacheEvict(name = "myCache",key = "*") + public boolean delete(User model) { + return super.delete(model); + } +} +``` +重写父类 `JbootServiceBase` 的增、删、改的方法,在这些方法添加 `@CacheEvict(name = "myCache",key = "*")` 注解。 + +**被添加@CacheEvict的方法,在执行之后,会清除 name 为 myCache 的所有key。** 也就是清除 `paginate(int page, int size)` 方法所有的 key 。 + +这样,就做到了 `paginate(int page, int size)` 方法与数据库同步的功能了。是不是非常简单呢 ?! + +不过 ... + +以上,只是 Jboot 缓存功能的冰山一角,Jboot 的缓存非常强大,比如: + + * @CachePut 和 @CachesEvict 又如何使用?什么场景下使用? + * 如何使用 Redis 或者其他的缓存方案代替默认的 EhCache ,甚至是公司自己内部的缓存方案 ? + * 分布式缓存如何做 ? + * 如何设置缓存的失效时间 ? + * 如何做到整个网页缓存,类似页面静态化? + * 等等等等 + +Jboot 都给与类非常完美的支持。 + +## 探索Jboot的更多功能... + +恭喜你,到目前为止,你已经掌握了使用 Jboot 来开发一个 java web 的基本技能,包含了 + +* MVC +* ORM +* AOP +* 代码生成器 +* 使用缓存提高性能 +* 等等 + +这是非常重要的一步。 + +但是,Jboot 的功能远远不止这些,以上只是 Jboot 的冰山一角。 + +Jboot 真正的核心是做微服务的开发,微服务的底层代码是分布式调用RPC,RPC 的框架和实现的方案非常繁杂,不过 Jboot 已经支持了主流 RPC 的实现,其中包含了 Dubbo 、motan、Zbus等, 未来还会添加 gRPC 等更多的支持。 + +* 在 RPC 下,Jboot 支持了 RPC 下的 熔断、降级、监控、Opentracing等等功能 +* 在分布式下,Jboot 支持了分布式缓存、分布式Session、分布式锁、分布式任务、统一配置中心 +* 在数据库下,Jboot 支持分库分表、支持 Reids 等nosql数据库的极简调用 +* 在 MQ 下,Jboot 支持 rabbitmq、redismq、zbus 甚至还支持了 阿里云的商业MQ +* 另外,Jboot还支持了Swagger、Event事件机制、高并发下的限流方案等等更多的惊喜 + + +希望你用的顺手、开心,如果有什么问题,可以通过 顶部菜单的 提问 链接进行提问,我回在第一时间回复您。 diff --git a/doc/docs/static/images/0008.png b/doc/docs/static/images/0008.png new file mode 100644 index 0000000000000000000000000000000000000000..da156921beb4cc2082eb7449343e59d13b02fb91 GIT binary patch literal 26372 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 LMe&ly#)1C>p2ZsA literal 0 HcmV?d00001 diff --git a/doc/docs/static/images/0009.png b/doc/docs/static/images/0009.png new file mode 100644 index 0000000000000000000000000000000000000000..62b152099aa347dc1bd474855e0a419fd00a320f GIT binary patch literal 32786 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 aLMO?6u~ItqyjL!NT95S~l|Qh3_kREik9GP0 literal 0 HcmV?d00001 diff --git a/doc/docs/static/images/0010.png b/doc/docs/static/images/0010.png new file mode 100644 index 0000000000000000000000000000000000000000..fd48654ada1c755a25f49ff154534bac9c6a0dfd GIT binary patch literal 55428 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

Ypoc&vNGozy-QeuvMVVe|=2`!~s5p#cRcF_{ zkIq)sozs@|VyIr2nsZ?800l1?6_Wj0wVL6Q6_WA;VwcU|{X!xpic2!;!=;C!K{E$G zb>kH*w#(W=7MN?iHvL-jHX5xkX1$kB7>`8M!#Yr{c}T2}L;9g^&o$a!=a;!+t!2g> zhS-8IbvpNl{Zd7_;-N8!K!&Jv3&Iu{yay?4Y&w;km#)A$gST$5_0*s(w~yC2Z+^Jh z^?6ovurpn$n7TWgekv9#-H`Aju6C@!qc$10PLpPP?nmm{rk^`}FEz@T?a<5e`?uAZ zf{70;M*_0Yo5?tR7&8kTbB~L+q!yX9Jox)Naj^S7AHrEbD-^33A1u4^89W#p9Kl`= zmT7ma!SG!1fn>770>GV05TS{%fA6?FiEQ}F#lgNwQ+PYoV zg}(i(5g3#Fg9DSLo8ucv)=qR@?PBNVCsq;s1_j4P2gl2C};~}B? z7C9dKUCR9#1FH&oM6ScFrFHyx&JU%t*c;cs;5?X#9rpQZT{cI@-%;cKEGxNxrmWZ1ODXfGSaZL%+MC;P97io!nh=b6q|uSi{93yV*epjiCuS z{@2S^St>^}S&kziA>PW)!)5Lia{=0Ob+5nI)kSb#&Q#%gQytflQJu=a zga6DPls0?(S@zcqM@VJ=cXicf4YkI%LlzX<@zg!F5I0#GMXliFGyY7wy#&?UQOo%H z!7qf;WXu*_DovC5jhGt>j)KGd%eLao|0{+8dHxwUMH{n;ZlGV)#5GWO#3kK4n^_Ud#t+iuEb%gf9E+`{}zLXVlEcw$oqV%rEH(DVXz z9lW$qw#Bl2Zu)G9fB*srAmCCUe7p~j=aQ4IU*9%zy@xoLd>VC~a+%npM~}zyO*L=o z;mV2Y)~#D@{spL$&G=yR@aCT4Mr|}3L*N@I5H6I%1Y>{Z(E@jg6m z0kptPHQmk+KWDm}sUO3uc{{H64C`$YdS$rBUOgVuTKN9~*P5%5Ens>W00000NkvXX Hu0mjf{k;x= literal 0 HcmV?d00001 diff --git a/doc/docs/static/images/0011.png b/doc/docs/static/images/0011.png new file mode 100644 index 0000000000000000000000000000000000000000..248297e3dbe3bcdc4726ce06c8638d4b6b250ec6 GIT binary patch literal 33128 zcmY(qV{|4#*S4LR*tVUCZC|m?iEZ1Mm=oK!ZQJ(5zG5eD?g!uco?o@PtGa5{Ms=_A z*d4AQCk_vT0|NpA0xu~cq67l+GZO^lM;H{u_nnIa#-{HJZwmlGK@tEUQgF00wXik; z0ilXF($}Y!q@@})FwoZ@ouc^#O`4+`Id|(rzz;VmO9y69P*TXatW2fMOjzS%kTvcx za8Yu>M>rzwmd-+uLH-}ck!U~gp^SHa)GZKSAQHKvq)rX~R6|8MfHRf`S^Fu8)C>`p zg5 zF$_v1${vE*`>=4BUZ$xvn86(<6Jzc6+t*hE4k+jw8AM1A>ets-*U8scPh`L&=>CNu z0tlI##cq@p3L*%IAc&-hkc#_{3mr&(71ia>dCF+AM6%#y1%ND4kwgovP-1QeRhZN> z9V~mV3;I^REwa6;{z_oGR4s^Wek+(Lbl^`IltS=ADM?{zi7c{&1A*f%j|Jw0Nf}dN zeeP>30!${>9UFwTUuB&_1ke_TzSOGv=5rYvaYEl*3D~j$xWLak!eN|{24mR_;NU= z7%U@b_>C3CSPM3fNh(IkWpXi8kEygJK1+fLX^nOf8yJl_dxG2LwOjZJ9_+*1QfB1E zys*)aQ}sEPijypgUQ4Si_1WuZm>fszP-_?KOSUtt9f8>rl{L(`MpGR}8a@=Oe57<| z4PnbAA-A+-m*Q39D|OX!fk~|kJf>_%sr&uqc^F6B7%ogB&R@=TzZ1e&K;`)~;nUR_ zlG)RabetxA!A&D4nP4S5!ZBX5!`O}UW$_<}=#-)rBhd;V+DXnSmVKI67e_h}3~I+~ z*O@HpaxBMP>6FAZv%q*>OXtqM+C9`xAA8HR2ba(i)RYG+`>t*n&9nzAVKcO3k^NwQ z@hGjYF8f3&gBOZj+b)GL<@Y129{F->T!Qg6xOV^DKiwi{@nwV+vG z5lUN}w~nDH2j4}br-|UNoe*s`w@Sw1)aBSvCjDJhamu#Qwt1?mU{0y<^5v|}isiy= zNYKe)KlhySN;X*>PE}4!wOBzb3+mR)u!)LhO=*~VIPoQ|DC=nztrktZY*~9SPx!5- zM!3lylI}KD7joXfbxQjmGZu)^*i|ls3%gWSBq+$cJQy#jDsv3!I!t&~FXMPQP8XLo zajUm9aP!ft$9g;%du+m~8URW&=$J=nW$H4_<9Aj|QI8u<(ErzlMh+Aik8Po^hUT8s z+G^^QwwMUY`M(TRr+v$l!tQun5|<=v3nJ%g2`4$zj2_9iWFHi-USeo#y|-87`P;IN zFd_I&wI(i6rnTlPKc4FbZeBstN;OV3rrVLvvYWK5pqu!;ERvHstK}2wA;gx<7-SP# zw$kNn%Ey0aFqtNqYR|X%D8oi0KLqC2yFSX+M_8VBy;`zt*w7pjjV#hTb-!qoSseLj zXEoNOnp#3%Y7cTLe_WU9Z{0(rTV~iaua}M|>No0C2(7W1XyNI}_I5T^4LJKxl1#Ci z+UD`0(mB%Q(^@oG)zrGCuTR}+80rA0%59=puhWf84c?e08kFN}OJ_(ddunZd$c`81 zPTA^MXK|=k^zAiQm$&~yfX!f@#K)0A7fMqpWdiUD#qJ;52P^#pt3q&}@Qn}~Zkd6QK1eGLHgge}x`I;5t zio52A7kh@l+M;!G6%i2>)XzZw|84}G0Q#G)by)n~RF+n$di^q-9-(}ho6+<_NJwB{ zKZFGTw=swNsSbBi!^z^&n>ja7_E^BUcfgc^@_&Y41xPmmTKAG8MFyapFX#W17LPMk zwJ)}r-(?al+85B1hl}ZTpVf6G617dPlRyHQd3L8t+M>H(SaXWQ$YlfkGZ|$AZijx0 zYiOj-d&S@sN$U{UPMzwrJGLu0Sw?6#k6ua#Rki7)I2t#SI*r*KD(KyYw%))WB#6i* z4)43anD0`i|Jysb$*^P;Z3J6mNB-&fk{*z}ks1Q77waf%X+GKvjT{$Q3*E=Awlqoa z@^&O6q#^+3q z%*Di5UobQWF+2Nm!?{zlXU;5j#)<_a^ zXOVZ{;HB&;$}x{|jhpLeaXL>(NLPCVwwRTPuJEmv{vC(EtHci&u)#7rIl@90q{Vp! z1Y(z2W&Pz!?v&v3)xPJ!`q4ei?G__WpyTwPi0H-mLAmCyG>NXc;c2XgWFG}mb0=rS zNynFw>@Wg;dH9T;2>`~}KuxcKei@0QBLhO^sN^sH+!p>Qjj^<-NgZI_8CQn-^&3&= zM(sJ}Qu*P}y+8lERHA5Z`L9JZX1&lrU@-NbNrL{Nl8Y%YB(GZ{ANKnM|c zZgiKSZPC*5B9VXqA;-e1EcAD<@A`oKO?cbem*)rk-rzj!=-RHiVXg{z344nTpFm@! z%(!H$ri7dvBefSA>~vktsAuzz;F?3KkOW+&;B^mk8-grfG4@REV?*HL>^(9sLt zyw=!X^ztYE&IpVMu9j0p)@aAwdzqNq_G9QT2;9Rh`MZ>Ui%Lb$<-bqPqk!kcLk;la zDY9$fhn#@r@7vz}m~Da<$T@&g2Smb+AE3 zMxKi+J%IgASx6-FxqR^TmdJs!g%TuGBQ7C%f@T8$6|FyUa#p;&b;pAP>kpO(n{N0G z#WZ_KfNkfa4%LJu?#!!6C9@fdged~EySaQ>+~?qs*|Til@!0*$VGr7YdU)_hYv#2J z3|}PVx~g4V6q(7i`?>$>{NB&5cKl8pcV;V zA9G@o;0$e)uS4({VxP#~7@7$-b9a&UXGe}rpLb9fvUjX#^J zN{hWH^!T{LiWUbNON;5)&EP5HxD_;*u`uSND*!w-(0x!Op=Dx!(J_+XT~0jZ|9kgL z6xp$yZ0yn0YnQyS787k`KVv>Kb{L*qfy^tktMTobKjde!?DP?pE5?`=$tx|DXjBB2 z_RL0Mvs}Pzy6;D0aoA>V@k*k7AD?efKsHQt^gNkmtF`(or0fFm*xYAtVs!THoQn#F z)OCel1O`J5`Qzl>X-CmmLeW#s`NJSz6_rskL35>uEBI^wx`09_j@ z)xe@kgNxB?G zZ4`d|G_Y3x1g4o?g7d!~pqasRT&SEAX}9tbXj>qjTNQbiKaIB?ds`ZFf^=#qL{n%ij#L^6QUg&KVhIjnGO0?dzM}{UhU&ik5aT4jzuLOo2>SeZ{F_X-3IUONvju!33bHbSA5B@?5hcS-Ezjwi^W-G`*W?R~{LY=3 z=+W5*!N`S|Z=`D@tpirli{nGbYW?Z=%TkZ+>gAYO27gU-PQ}F(arPg&uy8e zw5X8{oiB#M74+E<#hx}Amazpdk8}%1Yh7Vc@D!AOrt_`V*h0Q5-}k$eV6G#Qx0U7d3Ug?Bj{Av2h`{ z5kEu9J$L)1XQPy&D-$tMN$|s*Aup}pa$Cyjwg&IRfsXt2RJ3IZmrKbPaL#ZOc#^}o z93~O{NtXP6tX?ZHZEwhDOH#jfe#!ydt5-+5`SjlGuxue2tL}|HtuZ($u-kv|+m8JD zJax6^GV+g&CD4;e4SDMKjXccpjZwcoi}0IthX|$i*;Q|cWbyT4kV8z1J)}t{xdFna z-tNMnaX4S$yd?24kw>~?BFjbp!PX|hZ9(20DoJ>KaOvn&t5vN~bHASx&tN-+lWvFf z7`bEY;^+aip}ZsW<6#k??Me~HcSRwk^0?8f)#&P!4CVNJa%Fp6wQ-*|L-YE+KHnHM zkA+#T!Ia47lqFo9f>C7g^RrRW9i}jOwdl#OxI>?!w$kSXY04`maOj4cK6ZriyF71BziPmU`9WJ*u*?G8HEN>6eGeT&G8}oRH z%JjehtI%bKjF4?&m$RR|=p_+uTAwpBJRte{8?N(Sv8ub@Jai80lfCuyZ5OX(Tq48- z7Y0J>wX`_%M_%u8+JC|;Qu9AIzXy|_8!bGK^v&}daZl7%b_nU|#*~*!IiDYG%Dlgx z?b$vEe7(%HPXrqCgM7r81Sjl z!$DXC$$|VsM)X*%4e3bTs^?M)>@Q0^;TLg$R%{)6 zal%**VRqxXS*RNI5WU$_DnW=&{#i$SZ_o3ns%vZ8nnK9RTH)4$Xv=i7u! zoC}4-PlW(g_$mdH3#A~8v~swUQ4BT12ykV7UWR6UmFK}$gHjBl@rU~vU`%p)m3 za1<{nW+k77)c?L1mP5ma#$n>Z+w?*5j=+lX3CL5<846qyAR>RH{ao;) zy*lAU+SxMby?*;b5S6DNLcDezinZGet@HT`$@(*s51J&O>M_|nM`De{8cID0|s9d!NK?3F1kAho%@xKN%1X$eY;#Ypy=L>)91bnEjib(d& z(I7TjUu*bodTE~a8?Jo?M+1^tuiQZAUOV4*dWFY)&UnAT-yw+72 zF0_|~PNDy4G!mmKL7X6iFto@weQ@`BUITf|hW3neu~CAFfd7e})sd=4^1|1kdd|-2 za>goqVX;}85@Nl}R5%ioC1_#-{u4AQp(~H%Zw+y8i`OMVRgunxwykml!Xx~_fWAP9 zCdUy8M>QDRXCD^p3&Ok4jncEY@our1fRMX_Dt@T6O%+4_+j%Wpi~stjq(#f;3Y|uS z03L@fAX5f<^Trn6+x&9^Kei`qMDGJ_dkjdJoM3-aH471s7ZY%+^{oS_Cg}Vz@n%(9A9_wL!%>x5$$f3s$8)w@hf#d zbA*|8LxCz1Pg42HEB}`F?Qb-9s`G8^k`m5w9UKG5jsg}oHyGbUM?vBP_5_WmW{aZ0 ze|22T>D!Haq{sE{ICt9%`IN4mm$E0)*vc`}fha-)fYy81-ahh(iFjMV4E8ifTLK7K z8;`vcAme)o!S+n>8N4As0ZkNNDZrj;zXBnuOFzOx@GupuKf({BJ%f1luC+ ztC0w^5~4CMI~ngsknBROh|you*>mc;Ke9F7pv3M^qG*(_2PXDM1Dhbj2VF-5mKl0O z)GFzb7H^0+DfP%`#H2wru*!D^OmbR zvCQe!3<8mUdOFox$idytCYLkcnYmqgiLy8qdaeYGSxI-sW#{p^5qckpZN7siNluB2 zv}Q);QsEc%Wd3jZL6SACWPF-$f)a#JZ2M3C%IBX;kD}5l&hRH&KdhSLWhw$WX(}ta z2zB++F5bF2y1YBOe#C3O3K`DwMfE({Cz>+Y59Nz#8UP~~4`P)UK~ImsR>TGM;G$BS za!DL>NWxZy*z4)A6}g{K7^ch43N?giiF`pa@1Ig)FhZc*;1 z{G;d0al>FU0h6ISEg&4>ioSE-b2IB^AJjui$8&*gM{^by`ovC5kX;Jq{o3D0(`+aNsc##I>_w_27c6ehk-dg>}N$Yp3Hc7BSShv{3=R6*{>ay;iuqF_XO{j1F zwrq|s-IKkq+8~3tpA6;DE{RdE(hY7gPB*`QQVq|l-+((AIe0Q_+uHVE^XqldMF)AU zoz10UWdBDx<&x6jG?a8Z>coOzwl>L-g?c)yi2bf3|@u z?!vylPYqP}TZvRVxB)bhlzo;xAO^43>_hgnlD*%o3r==>kcC9I5)h&x47L)iwLXQQ zmtL|HFbJ%J`gJ~V!{KtneLY>&q;|YBm|Ho@hY%}7T9qKHD(-mXAI{ZFXf5_LV-~!F z6A#$gLDn>A9xo&>Mqizk(7nJ4gI^Mgf%~oh{oV6P`S=UtCRNsr-)2k?$+LB;>wAR7 zDX9&6`>VwSS(LR?(}rd94D7gHk@l~8ISHR8$jPcv6(2g+AS0K^~E}ej1p6zX=l)WwN=RkGM(4->P)02>Z1;eq+`EglI z`c_?#(6IFEr=2p>9=*yn|dlEiALordcW&XuJj*}6s zW46$I+xgB*HaL=So(8$D+nRg6Dt8+5Z6E@$OzqZ9mm?%Qj| zqc;$Sa3gUd9L}mtSE=K7#!48Ax!pk^|JBSOXVMiO(DMdT5$hdFOrS-Qa1OF`bMRNu z-PTax;sa$z#Mg+GE0lRi+%RkKBq&TaWac6K}SFhHuTl z`~kXWm#9*}In+rQR{Nv#y{JTM+colm4x1}Ib<$e`e}U1GwG62|+Z2dP9-v;L#| zU`y=CYLkNoaMXU9ESyos!-J3DS9(x@&IalC1VZV*?=J?y@TQ+3v-9h6q^~4}jh{AZ z-+lGZGGnWT<5yJ}I#Qs1YByW=r7{`mbD1XWXEuA>nWkv1Cy)=}bIYzMG8k``PrY}NV;@yTF!c$w&dv^-~M03+f z6ku}@@Y``_NO4#Mw+1FsdRAnjo7g6XXf{M0Hr6jL@9HU4Sz%o+R+%2_ab@-oW3A1K zHZUB;hU=q*^n|xoxPwp6xNG1Sb*h|g767x7i)QVo%M77*^(qLc^2`O`qBB<~oCAJUEbv75iNuvq1K*Kxn7(DS70zSU7 zWl^0JXr0mp8HUa0s<@W2*^Q;NGu!$I88%Dg*u@KE@^sNDKQBqvl`*kZ)C}HJIpDJg z4Pr?ZX6_5&Fsv1^yo5Q%wk#~xyNiMA7)9hW>Kt4Hd4?rKf2SmSv6DjCchTO+)Db ziRJW@SmhXXxzYQdinf?vn5uzOR%4_vZ*fXG_;tZwIQU$>B-evU&dVIcnh=_=okLj)r4~voJm=50^ zvyr=F|M2Ccp5gdh7^O4`D#j@bZmF=CMYx9f{cq}y-W{{URUY5FUFG7*j@`@uw@9z3 zgIW3vK|)0+oTJ!co(y2?6aiK$)6{qPRgE2JxXTQ5k?0lM;>wP&+ zUPKSYlE-HctgRP8aie4N+0Dp}$7Az3WM^C7j_cA-DQnZ6wOPQaanh=IwXSOzzaN)d zSUE{D4O3D_1_A@j7)r#GMID>)DLkr#WbNe{mresDEG&LiVP6M5Kg$u!Qc(X#LfwD@ zHnksyR#E->RlG-f#KxzMglw(7fXwxVFx&3J*4XT5#J_XYitgJ;tK9L3pX4ADQ zY!KP>2%)m}y(VrPv?&gX0kXDGDPu^}Onaoh89+cJWI&;>Ppy+1Lc5!MBXfGk8Xyp$ zP|Y9?A1`MI_IayNd(}^$Rm{JK+dk&G7jnv)``Zoh$W?WLf@;0KKi}A-V;Fj7XdBal z(CN_S2HQVI#nv~Zs~5ys69fH#ZoWIpo?l8jh0eN=;kVW9;zCAp`mNW_!XfWkxt=#) zjz8cUvv|Zj*({_Rpz`YfbyaTcoU^nbP}ng?w~j2biB@xur!ikQTKHb)AoNIyr3V_J z+acN0sjC+^K#(+I*^S_4RrXVk#Vua)zjyPa+tlmnEF9I49<-QFDU<-L0Z*`T06)U@@5wR;$ zt4cz;O+v;2Sgs_%Tc0lcs91JKuY9C^?($y~^++^sq(8_VhYL=X3fecQ5lkTNUAcn4 z&kI?y>K3+;v`AQ@LFbAUZ1ADt*$L+UD|^dZqkJx!tpDS$=wdnY<21Z@y&AYIPqk!5 zgk)hn)NFjg7yspk8#~>ArjCuE-1JPe3hfa1{}1i_2lOb^06}m*YjOhP-W6ISv|#$C$q!{hlhFf^|$kunT=xK>mXra zg{`h?IA3jIzbBFYN6sgL`fUzY33dF3UF^&b2Q1ZEO+{rl-Ktv19{A^TQ+J_J(-Iks zo)T<)VxrFH&Gm4o-b($2di~J(qNplRumRxTnYI~g>-m6wZQ8&iqRSG-m+|rTgjs*e z7A#svWN(1Hiz(SX{GyfeU#v{fNzfmvgxyzpgB`MA5WozBW=T@LS`h z2bj{oJ0QXShlYs?-q@#iWe9U04*rj6{-*)L{lsZVWXjC$Td2@dtI{fhd+*r`!R^$F zrNaD9v!ig3*s+6IG(J=|wnp!828K-aNad0lgNbGxPIn*?49pD8PA(z>jIgk3cU`aK|ffd z`CR`7MVRk53}qzA-2$!ddeo{h%tua&7^i-h5OJDV&WX#(i40^hQ&g~rSaQyRhld~7 z8-TnSNG4=51}3#~J&2L81W0996ei@L3W@0NcpT#&y1m|86#3w2&f`qDI_!GAD6dBj`2PT%EmNi}g75i!tzhnp%=r~1zR_%> zD4D?hLa@=G&LBFxBoL(s9)G*}uB;*VbYn#6h>U|06%Po}U(F4;&%6g{m{WPPi5*#j zi@nTyQREdLv~JA@wfJ4c*&k(=5BDr58|78RvT!3Jba#P$()G`s9)$x1I*|KBa|Srt z1?SteFFwJ53cOKeBMv4qzvjaOFYE&!l>bzeZ8BZJGdfsngpIa;zC}h%E!)^OR(Us1i`^Rjw>I}hs-*3c4#LDf8 zWZcu7x4)H&WK(zE&jCb%-ES$9LrPkj$sMSV8gPV5nlQ`I^NxbZ|D5~x*!mD$WHIum zR<82zL{Vfu$7b)?kCAEOS`#xk90&0HC^O@mvII_bIYh7x1#w7NwEYsh<-OT9sbH6r z*mDS%vZq1z%<;bUsSX7t?67I|=wbA%ejBU0bq=)5!8s?v4tj@K%|nfAm2AEcK1jL1 zoi%MyH&H`j8mk}cJYMdYj&j*0Gz>q(KfdLY+-M0GV&63nq+=;~GL+~mK2#*#kvgRq zy?VdlUw`MCqy!xe@w!$vLJYlIE|m43Mc^)pXpmR)otQ4~y8LUn5Vk2Y-xW&py(Z6^ zaO)V%2p_LL)2FLR? zZIEZV5!i@788hF}eZigOVPF6*3a~0!V0EL)`|C{uZBECDszM}T zg7qI4czbW{Vw^9&+N!EWyo*NpMrdm|p4m;&kxu0Z+Qi@``G%d%_xSg67jh4nR3t@3 zCK?|P92zY*Ua!?nHNt`&e^K|2>V82HGoQ)Xi?C~|&uq>h{Zrqni?Y=J<*VU#m=9`^1tJ;!#Fx|7irfC;Sze(c=FUAMnuVeT1 z9oW#+>9W40fWJm?1#$o@P=35jjeNcvG{gIa31dfr;IN5jrZd^mg@t4(>-sd64?0+c z*VTmX+JBObSx88JBJ$nGRfVlQZZuke`m&fZA6d+|xty!fYB$TAvKz`DjmDG8g2io2 zk^wjDdWQazOY<)CcN^GeeV-8k6%{lB-)DoO&lFuxXj~3k6zg%f10s zVuL$nHr_I#(^3kRQ5jDymmf5QDCYiBs%z~vAz}2C=#GBeF!BTB%@c0>znXJe>g)&N z2#j}x^e|Bb7*~40gs(Df0%JmUfWNfbbO!I;Nw-7R!0trq%X6oDAFgt0X!6PaZgxYRm`cWpVnhyl z#{LQvu+xwIGg}^f4qVj%7(9$pZN@hB;89$A=}=o1>AI%#{e18DI-0~Dl8bq|+S;&U zZ-8X6+DF{=Y_|Sgf>anbiv$K4oL@eVXf~51tfwi=@Yu(x=N=Pg2JNb*M)Z;OWIxkb zHeBs~JZ0oK7Mw6<3g}X%t>9wb)_x?oO7~2;L&qlf@j2PEDJ_g~Jfr7ODTv~2G3lxi z?j2<+jkc8BI{t*BrWQGTtfKOG>VcvqCpjwtz{gK(k;qp=JSvHe4DPD<^8XC`edjAA z6mVPlFPqdBQJ`hjp#SMr@D@!y-mIRDZ-G@jh{ol%A;^LI{hZ19d>z}N5rc+MnSi2K z-s37_S^XsGIDCFd_jg1!^bMA?sNWETfWXWa!%wSl?A>+N-hJwlwA zW+ad5S~-xfhD41IA2xa z-1A{$;@$3(yJWOj&DCa(zVq)>l6JP7i+Kd%XSNFzgEgt0fXChkv^c?be`q}ZytnrY zCjk{y?^Z>u{?4fqFIRm7cjX`ObPN6N3uHvfp1zM`fiFcnIT3cV>CDsRp?~yp-UYW1 zr^bjokc8g3sV;X@97cm-$Z8a~@WgCvs>E$vTwH1%S64vz{mYh};cwPDTdqp;$2ugm zVlh1Yc!HrPicY(kilgj1F}4Z{6QEhok8SV#KJRiUwl-Z6F1Cz+j(hmM?-QbHy0+X! z&+9T~t_MOILhQScSh*Gcyv6`elWWbI_!KsFVrBN#4Y=_sP>j37_T6AX55klax@`EB zD#UTZ1Lqugi!HSQ^5xy>>?82rv;;<`!8iv0)h3ivl#w-enP6j z*qzX%84~o2n=s|;H?SS;CuI2itMH2-VqthYQt`OE&%VMmv%h|A#b~@gXU@)9;JuUn zN3omHt+4HzCCjW4BLrF8Q`;@$^7r?}g)6@4COcZZ?-J+x)|0B|eOT#_HA06EMfD{{xRG}9Ue#pNv%R$XCL7i8;`Y1G8rpRTze6*rnQl+z09jQX+^w58>3eC-$H0Jz612Jm{Cl|)c4*KVtYAa+bzRK84a8$fqZxOzm|ympPYTGYsC|~ zGVxaT#|B`uT5PL4xyyWby~nb8jmdI=@1_F~ho4<^UXaL>Qh)RB4cOh{wb89p$lospFENSUtuiZwF4 zKJK{guE>l_r;Fb5eOxn(=oWb2L!_v;kf+=!)d;am{6_L^e8}Wg??6wI40(OK7X)6u z0IXxp2J^ia`zT*S3|pf5F=FZ)h)n>=Y9}FhBXkM-@Q^5tO>}Y9*Nw|FjR)X(rVG^) z8in~_S4ZmNsl^VxUhd4A@^5HysUO){eTY^G80i&hGfz zAL)eD7(#w{U*z>|sbLq%9MAnMJ9ra_HiOh89~ElWA$%PaPo4i@NHQu15TVs| zzzLSD^qXaj9cM!58fANJMA*}QLJX7bH>2GWV7~kFnY|z=iEVi{hD)l9QVb#CEnxD6 zgQ;ipSydimHD8p8=-2E}8z-1>!?j}%NE`u^b;{u=^HI1qVnMrJ^HdL+I{BW!D+P*U zz7!qheM2#ix}yh%3;!8y03Umf|D~B_=k361JT?{4@2)nhx(kgJykl+;CL5U=$HtpI zO(mV*Jkeg#N zM^-+f2CRGqHDs*tHfNDN#6g8ZBSy0kRLP7=x#hFBq!QKD^Z!!7En&Uem!9}fOnDto zLKMNFj<y<80=szQPjPP9efU>8pWZEsBoGvr`4nemx?R!)W6OpU!sh z?zZRP{z|-?&yK^jh`zPAG)|yQ{Vh>n)}&3Em3^kW)8ycmWR|9m@K)q%gvQw4c=yrP~ z(Pyz8r{Gtz1nL&p&g#1p)Sfkkw?jNMv`$EyHe~Tb?1PW5Wm~I#baU|<2Qdt4#7t+N z&M;-Y@@W-g+Sp;ubkA67^Kppl&gbIz2S>Bq-LRKn9~G&p88O-kGr9;sImn817r!;*m$jOpCFhO z(>uMAkV^`ng$`LE*z6DNkg(<;4hehUY5gLrBc-q+W(v-Bbx$}d0q{)=ab8et_q}G_ zV-M}P|IB$!uf`I_pJiO}fHw;iyf-4mzP^s?+=O?otXB<8dEm08W%&jOya%`1P=c(6rj*9wc#J%4J{;ugb`+Va&9q4@ny)cbJd34+4;R`2YIeK9ba9z zWFL-zO|12D7!$k=f=y@jB7z|B;%@2gV((|Cvt?&z+ukNR@?6HtPC-obNOCYBTpaG! zyY2Y2Bws!zyWFlwS_he=w3`sPy#Xt|L;Li|GZ5}%vBBMKlmu==Vun}T;24W6;{)kV zQ`-}4 zH5+&Sp`#cBlfhdPTC3o7lDheHWv^hB{9&e`0zIg>F&=wG(MGkon1-8UMqD7egqxpV ztVCYdRo(Uj@FwzIzI&3~3R?uDNIzK2;72@aJ4kq?6g!bCv;{n3u_HPk(PQ%GYZNKQ zUhpF9fOT-aoIgvn0YEVtyx-+~MIGn(5sbv(4}42*_8T@Wn&@u}IiH1bkXM>};Ub+< z!v(SYmRjTkQmfHf!j27?^H_i#5I-%`;-4NXJHnwYkhboRq9UE%Z~<&Kv;8#?X`wJk zvLEx^bRW-L!rRGODsk$=-yY)VR%gjO>IDn;TX&b0GsFJFs@RUK!X1pV%%lgI@riNr@mWX=2>IM%J&oF?{6xHWW#fFqI4{?= z8?|b_g`eIC7$I{hz(twybR)ONb{k zQ`@HmIP4QkvpvN_*Oj4CR&4_35~`dVTEHUYqskS;?E0vQX3;2rWzGHxFh(_iZnd4X zClgVbO`Tz=GZ}f)ST^9cCDiYp(^`|{z*wS?qZ2#o!QAKc5KRz^9`d)XvbVf8^=@-O z)Ya-Fh?OM_-vvSy@#x~OS{9929_v5K;S|`jtvl@S-Sxc+>r<^OeP+jE8eDN)&NXQqUbnp%SKpiV{nVlO^Sk-mpg;MwREyt3>Gw z{;PNLVxrW6$;7k|9nr}Q@x*9v2O0Zyr&3y-56yN^bx&R+rHLJ#KwF%08C`<(d?YTy z4nRU92uqDn5P24vkFOE7r`lo(8;mCn!J|J4t)h9f&v4h7qN(XbQAcdUc`3R5uIJTD z?fyIXM6UZn%1(i?W0x;U#TuV5(TaMncz^r2>}gVozXJg`bbq`?4zu>JMAJu}HQ7!^ zpMm0Qm8$?&EhWg%=R#2bn!>9AKRVQ{Xj@@^B}BqZ)TCS37(O`3^AqSa2}w>~_#v1165DPX+5^c3RmR?mz4brS5y(tOScIa+}B4+bl z-S~PDGgpfw;8vI0k31~}zGi#|oFMOoj)dO%BzqQ9@iB9P7==6g`sRWlFE;s}@ajOa zI`@XQKOkn>Tws^4??(4f2mOmLjfQ&K@#|ssZ76GB%(_vrWLbI%8QhwDe>}0>tz*Bi z;dS^Y5){<>LebAMQIqT79aPz=H@!5Ch>Z}-dmH8i9;L)$I%zN2&2Mm(zV9NI1y4I+ zvWW;`?QV;rWKns5iVX<_+Ao~QB#Z~bpSr&vw{Ode_6n%n$Q)(mmWb&!&@{-LcFWhL z{~o-pTEG3%5!89mxaaFjo;mR8f|jPg>0+rI-7w-?WTL{IkN#kgGr3mmoKv#xhWR(z zbZ~14;kAvya3q$i6pvA1PRsF~?54+-+M+N~Dm_X+-CEZhZKLdsm-z6Dv5)hm1n?WO z@xc$(&e&#X?lxZH9#Lr=!vWL#XB%^LUVktAS)c~_DXnj@L-u72%Su56SL@=(Kl`^F zGZL>m)Avp%4p(ctH2YK9*M-G-Xeu4~Wtf0-*{se(SQ1hV$ z+OqO8)@y_lQp-fF668#slQBFKBo15e|Hf^M<*QtGOlRmzn6Up5;9bcYv{`&{$ZA#} zep8m3qP2%%z%>EnS59|@`tXtrC_Le>k85i6$N;`nfw^ZdUpc0LquePreMEDkpb)*8 zL4r{pZNY8nF5%)A-tfq93TF8U;rN8yzw_`StM6kLg+|G;m~%y<&hxW;vAokwthx!5clM=p3khb~Ml$&x7e-vu>Te|dda=5FLnrHjw!i5^ zT6-n%s&;P_EO#G`01BOC$xCC`qZyiSzcD0!O`|=%_kTlxEZ9U8r-$>Cr9yS7_Oa)AL(i8)2ZQN&99^? zab*3#UIwT4eeBK7V1)15M}|3XyF1pk0|@85o7Bpdh+$3$@%c!xVc6c-4TD*o=>GZU zQu=Vw4u2YbVC+CP0{VeNc&^D`YI8XN(d)gOGc4$%K0mM*G(xH@=oO( zcX1YKPaZm`ZL2Gq=hI0{vSNHAqaTtOB7TIxrXOK7Aro8{kBhNxG18>OZM!E8mOE4s zlYL72p}e%4NycW#aQiFxY^xT@)S{1$=+>s{c=Eq{a^g=M713x0>M(mXWnJv9e8{=M z()kE2=aMn8>X5xXzB9DEmk)ER_x!l|iDR%eWAe{X#HBftjM=}usk|3KnD4T^;stA@A8+!Mnv>|xxsaMth{lE6UDlE#b{Z|nb3F(&Z z?ruanBqXJCXz2kdY3a@(q@<;hHt6o|9FQ2g+2i**==(p|dF^YTY!07UbI0%QXVzN$ z5NUOHcO8lD2`VIr$Z78nboNNKIbVV*g_ z>qf$R@(r0axdVHeGnJJCdRQpUuAw_a#vee-c^CO`RG;>X;1HJQR|ECStsJv+x+7XF zpimx=He9Vj%1)+t)Vvq^#s@8(t~@L)Z6 zQMQR5zkHV|$fv-%!kw$%P?YtBuLikXJJG3EarTa`)l66(_9q0zxYd+Al)@)_ICO8yw!sfxM zYqT{k8Gck{NioQ?HY#ys(0Y?7!S*D4OS+D(rzd;QnJOFEg`3{5p>O)hjeG~vyCf&A z;fu>oLAsaJmE#tb0JDI0t$G{bNO~8$&WDwPOsQZGlpvHa@%4--$VL&fqym~rZxJtK zPCl%{iua2NwL8v368-OSNA4-rzg{xCL*Jt%a5M7nw)s8MFT9!=rnHQG$@cBS1nanM zZ%hO?rV(!#ec8rCHDJUa{k-8F@h$c(l7x;F-B~CuGews?Zb*fR&uXIL&(DUHc@cva zti*T8Hk=r%SE&0=h6JY%oCxZ^F*}bP>;I;?-hz-_I5lJ8S(N(d_5tJh{(u z5sJz%MNeKXsHK{`@Mo*8iA)qj%7(=LoL476slq2Y`jm^VjYMXyW_2Kr4g@z2^R2oQ zR?Vt*V)5xAtId3l`_|5OOtO?x0DLWw^rD<6yP65lE8e%PVD_q=;{DT4#Di6SGS1e7 z1CF|xmSc|}s}P7DL#t#4(of$t9QO0gNY0TMVv_71gy6A6=@nZwmZ%@lh|jm4e;RRO zV`T_&C(<_rHTD4Wd03~4nXd;j=>)y6>m9}1^93gRGvBOo;8`z0Ui<_su>ud)0ZqaK={k!ta{J z7W&89>yMBQpIP~_NUnshd8r9M*RwMSD33~7o=Gu*>?yq73Ptnj_};cvx4c9YTX5&?FY@0@+>OC35Ne@E^ODz zs6ZucZ8udy-HlUq*r_2N19d&Dma-t%_wu1!@G*&s#hvoT-%CrjiI4nhi?$GSHoDl^ z5pFhRsqj*rb&ki3X$w*wP#@z)QM1HsI;8qCECSuNagRl3CyumjeX5b0)bEj0%9^;| z#;QGO5O0$0kD0SY4d-J*9>i9=x$@}XwrAfVvql%^?vyc829@#Gp8|~PcL{5}o=z&g zw7TFZV#Ts2*pbJN{~-H<8_nVYhH+l_&pu59uRNg#d=X|s z%Q#hBjcNs__(EF@rlpHACkKaHTsqW+qywP-*EH!{mK`9{{-Q_}zzb{D(aV@>Qv6RZ z992nmESrk+7z$RQt;t&IYTRhg*CZD^TGo=wR{!8CR#fYPjYTK{tFw z(FVBXUp5G(r(H(D{B4U%-riq*wnh`L0K(squyKZSL(mVK@bfMnY5_iFolxlx*~JU!`V?EcdG@O}XbYM%y>VoWmic_FR~^=vPD`gpH$nSV3Ov&(><~9B zc5d4u;Ixq2j>($xt}5*tl9+);uoOGa)E%J%FY1Q(i1?*|YPX{(%g3)Lx&CL& z-!66oaXrhVc`>p#*w(g-^;myOJoPT)%)Z&{EIoHvXPEQcG)?`gAEbPjB)hgiIw*Ad zX*Qs)HuIFhW-$x&w$NQ<*Y+wU{_-?eCm49jaei%V+;n@ceJy-NgQfB1{A>qzX|bYZD+HBALDm z&>x%Z7hAj%>h?=Yn9NjBvEG!$h2od%cFR8nHZ&7vA3xSd1+VQFoNbRIGisKp3#AB| z#6R*ROa7#k%qFfK&_XV(m_kaT_+uE1bc3O|`bmfs4r6*qC&X{rr8U#eM25}9Vx}O) z{OeMehKp&>(X&?tUY^H0mgQU+vYy?kax|$YYn&>jw7R1dx@hDMi4)T4B3>RK_K~^v z>nW2c`WNoigL)pHEwWc!eJ^5saq?$qF=)B}ty@T2GWE)}57B+kV7}I(}V?4;>wA%%`x`Nd@qe z7VOL_R_)mnD(QhWF^1p8e^420=c7P~96`I4G*MsaR(qMtb;v8$3Fomx!?Bz_lpypi zys++(Nr%=c1;=883%0(GMM@>6I6knz$8(hrLG3iCVb$y;Vk;o^NSc_3ygb3A+->KT z0Yky7m%;0{oY0>%r@Kx|(WNqG-A*&VDO+@nW=$D{8K+W>O}D~l-;N(wE(6e+F-tD(!5PXnx9odelH{r^gsq`U$p+ z#p73`7Mn_v0-`jBK<3X8ANb`AWary;Ca>Hn@_+EMp0=?f+X8@8%7G&fHQ^TbIZ*39 zj|+k@^BK8@c!CSFJx$}d0oUyDO}l8+kYw4;uWF`o)=TW5J3LOg3;r?{$LHjRqj`HW z3L~&R#eQM~bKk!Isdg1`2#Q!h}HBF7X43rQHb6qIuQ-b4vu_L-O>9po0~) zwt3BkuaYleC+|9roq=SAGFvO^FF*-Sa?>vvsN#ZHMjlP#R{?!ReQE-d zw3fe~{W^!RG$P^q>xTm%HL9(`M&7stAmZ01WF|x{MZy&V156fD`16N9P1$O1--4?Z~lxpe2(&SCO&$7lES{4e{bJTMo0e!crQWmZxRb)Vu%I2 zuW&&BeoO-&;E^7UyltX zp#1(XiOllwn|4S#Wnlhe?$^0&7Mztl7OOJZErmrkpEH`$ZA{I~v%$lbLtGetHhcT^ z;y=6#%iB$V`Q2Nj@ zzCgLizc?c6=ihcTl*-bG;7N5bXU_0X;BO1yS2j^EdDfH(4Csmo?Z^K3nGG}4{|P0v z8Me%i(pSkZ6s)!t6sV0~C)Z&U^qm^h8Vwvvn$8K43g;v;c*IG8L#|_@l<@#BBb}>7 zxm_42R++UEr{gKL!$RsmK9l8?$2S3{#Y|{G1DeCf8E#`1iJDm)=$iWarj{aNdUCTw zayDc1c~P=(1FiXNx0j12e?Bc1w{Rug(I%&Fq7e!%lgtl%3W!4D$t9(vl}V^|8F_;& z`9xxiPGUm+-(D1nVM(<-MrptPj)`I~R_F#07=PeR%wnQ)SG5_WRl!e>eUBh}gfmE@nq+d6F3EHp`VC9AoPyU=AcUikTm%6b zkM^5BT74wGhSD{LY2Uz5?yk*Amp+8+Qdt9Rw%zwd@^e3Yf{UreK!@taTZmnym0-<` zvg*m;n@Sx>$9AF0HLK@9zdIcAXFH=xRVIsdxh+*1vO^gGyNrslH5WyNUP+)F?&CV* z2x88p9K!|pJo1K> zp-;CO5eNZvDW$=qFIR7$vi7M7C2>keNKiF$C9LL4ZyetyH_<$u?X40&kno|B2DmZw z%KV8j=m{xQ_$D5J3|N&m_FG14*Oa6}Y87U%rf69*AEj-eHI}Y3MBV#Yi8QWxmd`Am z==2H{YIHcNd_p9mpk^z><9=sX0KPsCTKHRXS+SwAg|l7ki?`XM|e`P@s=9%zt0x zGxVlvBrl`iYq@Pr7V9$*TcwXOG`Y_B8D^oFO^?tTNk-t5dqTPy9+e5CL^he-lk5Fx z=;QrYp?h=V!R*0T)6K*bSTP*ih#RkjWt}FCG*;x8^cn~n&cVsI=lF9a;Q*O$1)iK3 zX$e8((1U2U}y8tHZ{#AvzMO$g_$4UnNd`JqL+3LAyrZ8)WJPvyQI{DFCgmg&07Ymmb9RU04O{@mO{EAZK@;frkntV ziPASV>*Cd~cZa>UTQbE+`iR%Q&T&~|8*AprU7{SLM5-1OaN>Dt4)}S$MY<#kUH{lE z9FzR%nHgPQzU&Jr|5#P9FS^JW($-PrC%#gxP#j2j1xeZ{Cw6!Lmi;CqV+e9`He5-&!6<=w4VcKeezgk=P1 z!V#>BX(b*$uTM-qr_*SFdCS{I6?o!sU!}#d(;IP_A1im}&d4TD`qU1`7HY|wa(li< zvy04qYS^*o^iwOKI}T4xm5@G=kLnY;(baXDUzF< z=!%qvGg|Un8JfpLli={Eae;XH%tUk~rR2WY$nPVx?n=;GLe$N7b*X`t!9ul+8iv%& z2N~jOE3fZz^0v8?g>-gVHpg;h{bTS&@l$5&;(}vo@xU=c2^`_4hd*7Z(W@)EM^*GQ z^7nLa^IW89hr(S!3mMaogYErY>bD2EZ9Euf`M43nC(x&TqA|k>g zN|RPHWW`Xc@q$Y6pu1y1xtkI3l5p@tpiC&CZMsWKh|DK6W~6dg71n8`svYJ|qYmY^ z?M^lJgpNqNnMPvdj?V+?6s7B*m|1Dxo8OMd(z;v~kG{E|H`*{C(#gvc}B#kTVQQtW2D?SQBIC#pNaWU$y6AqRLt72&Q(^!{ZJvY$#{JLnxKH6q20VscEth;9d}3h4Ue=9H-)r&40_Phlwx@fSJY3 zQx)Q1PBlCk9;;Zm5kkVR&Bz8g<{E#d^Hb;yK=_1U94jo}`@{P{hzunfcm!~)%a50a ztJJyV?)gK-URAq`u?tI$vtWn?!$6o7Wd`1Fa%s)*by})v`}=8{XF7?MIoHdl%^`(SlY1_LJIV=A@wUT5$a{|AA?%e+ubLTH%k*)ri}Y_}ym^ro6A(8s6|Ve|aJsnT zYttaWdcu+`F3~l2ZxO6K`0?bq36AXM-B5D-F;17!ykv$tq0Iaemb2D3EYA>9dc6~c z)R2%3)3Pk~6v_Bw0fl6drxilCY*rzFlGXR)v+N+5!pM$yB+C@XijI1w92$w66kZ;> z3OSJE7aSR6t}hK3bO4Y%ibU=}6qHaTo?X3=5GiNQkxe6gl2tL0-X?{1Y)?R>%^QJ5 zJ_ArsXJ^L?3n8IE=}0ly`C$_hX*Uj)EV?eH zubM0Pz*pt>vjJrkbvQOmQYAPP>9Mm}2^1$V zX-P4LWo=Vb5toml9+xW;_0eQ2OoB1GjtvzV`87lN#!iK!1YEv0w<*J8S_YXEbs5RO zPq$j2AB2X5ZGK_Fw)$tNdTY_cU8q3LJefngpMT^med5reGr&{#nJ>W5A}b&BTciIw1#VlpNjmtaUrQLK-9MdhXAJVuwX{O=1|H!(u2 zlOFioJ$Qp1FnVDgn)`Q9yccs47o$v9Y+zD#0yIsL|4GcyUSmiS@s)A|`T)&0oua?% z)RLp5ue2S~a?|surfTiK;PA^A6HB3`3aFybN)-5Y;1%{+eF+9S+NPP;c^>5N9>$&Y z*W}o#W%YOSwp1x4hWLMH?NTL8`Y}C(205dscaL|}R#$vcbaOwK*UYdnwZ11GI(||A z0U$!G?L|NpI-_|W8Bk**kevAPIg>(~uQoUh6huKQOz>3%tY}<*J0xGVMQQcooA zJFKogD(mNdJQXgk)SMWglqI>1IaGy>XM6n32Cwh3kw2sH=-Ec?&?~;sbQ!-IKU0^# z?br0dFYG*a7RU!D+zc!h%!Cm|+`_%BZ;Y9+-Jj$IyLp-O>pu_4K%nA;M?@zR%UXdm z$gI12(kNIV?rNT;N2_l92u{$v?=?sOIp^s+A-?h$N@S#J7|}CbAH=7Rv=i>!hvNH zd{yCily-kXU3jDfz*i7dheah^V>;(_i>$2$XW4u{DX&ZP+dNSw0(iIsH(ul zE1Ody-*jA&3q7I>LrEy-VJGz*?9AcUzf*5?D7$T$WRKw4FmgIEvl*B?axK#bgTnQ`aT53^T0n4fDV3OgBIB}$jx3RKp_qekP@;5Z4{cZ2{6CT#;j`C ztv>bb=PL@rCthJA15Hy}kskV#ply&+L6X~Lo$OA&Y3<^a>{mK#T3rkFT0(7I=~1K0 zcV-o{c31I`0q=^OV1TLp+lGbdIuEAO-FN8iSI>!9Cq@R|L^f8nxAA$q2r&ELUI8=F zKE@p0f}A~sT)>t^5uK(yn#IJ-y^DDdJRs&jl$BR5ED}49`_8w(pC<-7cjr-eW0&<{Tk9LC zYGbSBlhakC>N=0c$p(xEJ zU`lth|MccmAZb&lct=^M_)NgXzND_f>~x|)=j{a(4=wRx)11;(>wNL}bk)R>XOdbY zua4*bL!X;>+>3RB%7RK49eQbkrJ8`-@yiG-m(xQtH`T_o8L!?ep>du0y~|o1``W6( z4INK={kn{W)=@c=Il!&s&cF>V?P*Qppf}bpARfYvV|H*Bs>yC9*->b~8jp&O$YAD(;_mKWo=@y2kk`=xWfnxJ5W5MG?uB*6sDHb7PYxX5oJ z!rqEgI-$`J2_XY{*h^CS5FcCd5V5X^YnXXZSb?R|zJEVu?3i)kS@aBcFYkNOB$uDz&I`>)yFYRk}&3scp+%Ldv8l@{IzTIiyBRFhIvLPT(y4ZjTra6oo-SgJrdFy!5$s{|eFko;l zV!zpOQ*gmuRv_ImoUCw_u2{txS8?8-S6@?&!q7&6Px(fD~9lLY3wYUO; zuHHIKg@+E0eKbZ5W?kK=p@kUD5EzAi0 zCs&e1KYf=-nf{2MYw%D;o5^J=1mxU#s+}`5F_MSPLpf1*w$eYVLAcwe?-`?%gJ(4d zY`}y502j(@AE{q%(EMT2O%PmLk4GvrX=5WLjF!f0?76RKlTTE1`Q4nyF=hU^dYEsb zYDTi}dd#jY#m>9u1*A$BV(UuzdJ~wTX;<(ppBCj!W3=$%k;?X@(N7zj9|IL>1&0h? zQIdq^bypLuq@YA-&uQk3`A_`3#yzKaw7In<2`y{GLY;6^1BZF_r#Fre^38prOL`CP zkzh9sV^>G$JDTE@y`Vv*91HPc9-hQK(kSPm=9I_C(k9N;M^<|sfnj(iMvROO3xzRK zH1_#2e0Z#jb{%WI?N=cFAQBT;~P=L@mzceX253mY>Pf_G_P#~|l#cWV)l-T8yK z-{mQ*&B9I23%%2j2{ZwcuQ)i(E5?`wvo7aee`&m+6J8T~SsWa8QFzKJ{q+f$u2==${7!mSdMSEiC)0iUZI4fneNfT-{c zJ!wjM56O88M&tCw&%O8srM|w-ap%Iek>=jFyPMYRUY`lw@X9(YJr1sN0(XFXEtw=q z@dzs(UL}oY`)kUv9s?dP5aaSi<{+l(MA33hYv0@_LE8f6EVJR`}&5T{i;BE1A&L))R!6q zWt2ic`;~vpq$RQQcv9OLG>*+?esSJi?U88a%+wG`X+0dAN#Zc)B@m7v!Yl9WXPgFX z+BBp`!Sf`IejgM3vcJ6&HdV&lUPEfvQ9k{^kSDM8w5mBFf~!}- zUgiyEo$8XHNU735ew%h!cOJ-HM>l*&bRp7W8r?=jv3>hACpq(HYft5s_LUoFE8NlN z`Bw$=q0=?)DaPvwx}ra7^hrJ1lkI-FS0}v)6bINzL89v?}8Rl-8 z1xVod3r|&bLZDEeH0fQ3hlS12s;2za@E0Rm!FHb(aCl9n;PKIe+rlgFmW7$rf7}PZ zq|I$nFTC~Ya(^`pE#2QtE#h}5i!GJFK`X?|1uka|_#KRY0tOy2o|ViY(Y^%XpX}u; zmWKd8AV=yA`rV5BKlt36*YD_a1y-71gd8c;y&Tsh2rVRAO1x>q;W}ehq?$F;Qz7;& z^B;PXST_+mMg~LkBD7LKA^vz_Z;wumn`{+xaU=6PCZxE^2c*2J8%qeDTwPswJ0~^m zyH#n2K|(7e`p-)DIe)ADzz+=zzYB5G$b@lYhm!rUpBK{qs|kj-Nx`JGw1C~2AEqUu z@9>es!YbZd~>YZ;i`s`05-tiEJ~5&tuZ=PZmUnew0Z5Ahuh z+$PC#pA>bgVoofEQ$6=#KNmk(y+VX{tK6iGimexfR_Quacx@OL+oAtA4 zj4j>YHtIC(q!on2K^LJpXx1n0N0%p|ZLZbdAZ|7ISJ8SxEf>BUXs&L?z*`-mb7q|M zRMDatp%75SdcEtFc+1bvNs?(5ja3*&lXlXz-4Quxk+mn0zy0Tc05M(A!H&7^`$lh_ zwaTFX?07P?>jgu*-W7E9^I9Jm>%3(tGoYRF zgv9YczS{&bv7l|S8j=@HSZYK~eRSaFwd#9pQqPC=1lF3YKX$9>uINzDPxCuw#_MB! zqH)c7J!g+KAVDxOv=P&f;yoxQND92?41NamD`i@(vd6)VRH3zW7Z9Y4;;pI*5PL6w zOE_moDD-#Mv$Q9Svb15@FgiMf_NM)ND}{d5b+`L#inh)|8k3owv-9)r97(PXt-WEz zkn5A#sd*38t^TcZ)e=!3;?j9dfDOOxNS&h5%YOzTXuqn=9SP@(n4AgVnSV$3_@ z5*Vebm9CRc$0GXujd$V3wQG}BeX4}IoJvg#r1vJ|vd{77?tDaOXv@0Arz`Dp4jzJ| zs%et^v@)cmmI-4=Z;{25jS6JNP8ti!MJhs-DJp|mwBMfDWCMC5)WiOwTN66Lr<>cFa0&8Hf6@;kM$*Ch9+ZqL|?l z+#}?4)`^YPtJ$d0XA2<(o=)VTS+1B^p87Z9@VuQl)~tA zBwR_xKqDSuH%`^1x$xjXnnN$&D@TSB+NRkB1PXCd}8HQMC+71FY6RtllYiwr|K;$NWZnRa`bAB=srmd zsx8g=RaI1=^ZHycZ%i0;Y^zCc!{en*5ELAA=Qcki}c>0ccWuI2-cpVnZK#~4Zc`P_dEK8h2~w4gBEE% zyPvON!hyrLK{-Wp-P(o7JG80Ls1cJK8ddAYP3z$a80!&u zsg^6drg1B?6J1DB>$UXt#o(QWj)?{RwK0IIMHyHrC=48z6F(rsm1a4H+f^=}t>6<( zxs&a=fp)C!M@jWZ$1WvfL>2Yb1T|>ax(WTq1rhoCES=8FN@sf{=ZgOnO?0vYcDTix z(f0Gpoz|#976%?QLdeIr(E}I-F^!=f;SsfsZl4+#4?s2{AqIkU;*C||iJ1y`^SW9=(e2nT0T*!1D| z=m(DoW%}rT<^Kv(=*Uqzc6N-($9PRViS?l2YB@g|oK0eW}`)4kH2=^Vsl0Q%6xn(I#-6BTAfU1U0{Xy7@b!2xY)r$@t z7;@^`ZF<|UMJ_L4XwhktA*W5R#e2+tRlX0SSSa6E!vty=P?pG0-JkyN>AnJ&@m(9j z>bnQSF^J4CzzMM{^v#EVOfU_#V;lY zW59^30QEkOwx8lJmn^VPn!uEJ1@*XWN9%AHcEo;rH{x&>E|Fg{U^WP1VdGdK9LUbG zj)tw`en^@AT5#o_7k(wwxlJAVVTLTCA#>2!4cTG+t)XET9NcE~S8nOwvVDpbb1i6S z)6SzTD~G9&jr_b(zM1=&c!=yC7d*yjlF)d?3q%WCuA|TNZ#U??VHQC|QOfd?6IM=d zp5m|HW`J=T68 z{e~#(4)*m}C`7S-2{6}C*Mr8)C9l{WEXb&#xrq7^qcTjl251UDvG=`&5tPG4=aYY| zkpTnG#jjikjD~8)iUrg2fuA^1xbp{s<>$FYI`>ja!%@;Z=HI~&P-5=iR~k8VC^50u zvA>ClEy+OD#d9Ee>kh+~jLi~i9e>={QLr^xWMmGwe0_hA(}8A*>Q5f`oDiUCrp*Y} zFN+xtL9LbbavU~XpwmGs4Z<^6H~SFIAF9z4<|0@Q@D&pmTVirZCWrYsel^q`ELBeK zrAC4#OTX_ZFn!yyppc4(72S*c3aSh$B<9=y$}0)BDGJpxNhHxGMR*H;h81 z993t!_h%@HHhwm_!254}fGh3F2v?f+o(Kh?#lO?|UXCNA>bpb_eQJ47>?Tm$7M3xf zh-{^+;&FvBZQM)rm^vEH!eHvBr{Jf@f3W*hE9M!XHdJz2C$HpsrNPVMvC9wlpR2#g68=W-tZ{9f zbp^|Vhw!RPKjqWB?0cjW&`3!e`N#2biR8a5K$DeI8&#erB8=1_xnvVNWyY} z0~OQ|*aA_eP51V~4-sQ`cn1BS4fpN`9|<)*CGvkSe~K*$RUX`ruvzfXTnMjD4MX?; zoB)U4X=W8RpBK@+N^%%mw}OAQ-#W{s)cj!^;^((QtxT& z{dy=3gn^itm@a)9m?<8@J;H)U*1DhC&~yAkiF*t>p?2dc0`~o2g4w(60fI-t*b^-^ z`aj?O3gd?0b3r}FYfC0{0+~!|OnYnVJ`6t*|gNLdIWyUR!?( zql1G5mDRP)CF!>j*lhfimYl1GJ}=k6%I~KSVnzX@FP4fOr%rzVrT|sRPh#o0e$`(R ze}LBD1j^F;w+PBqWDedG?)@+aD58KiPU|u|w7;cWibD){Ke(cOJ4yHZNtOhh6d$AW zXBSweIG~3=Yy-cQ$$YP$22+4lFYBh{%mg}6K=$u$?^=#Cc1Z8_?S=#9_Z^yhFO*-kHH23=eoY+;anZwT(xq z^v<{QqSxPj8fNXvL1Jg8gu_-fE17o}SFvs{fUv;uV^vvdy^BiFy2-pth1=8wZZW|R zoc6zQxu0ymzCI~0#V00?_;nAV!N)+6`<4VnhOE)t$@9vmv&Y{jbbQ=LQPb8u z$=ckHv-xI~^v;^mnl?@LQ5wK%oihE`&0j~0r5cwY)`5*G-s$+#QSYSY!K7-v~MX*RQ1YM#r5q#&&`HD;N_eXEA6uY!UH1857cE9`*` z3#E(N9yYc1^S5=hbvt**V`sZ}ar`Gae#}3AUVC!gnX3ib_W~{N%;j6Fa0X7>&p|Fl zK5okUO91)ByBhbqf*ao29y@I`0wY`VBEiO6*p31M>OskrO bR_>rBUt%F^;6>8|=%4IMCCM@g!}tFUH%vpO literal 0 HcmV?d00001 diff --git a/doc/docs/static/images/0012.png b/doc/docs/static/images/0012.png new file mode 100644 index 0000000000000000000000000000000000000000..c2a983f68a9dc6eeb4426402db530a72969a99bf GIT binary patch literal 33591 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 b@ZM|H5>h7tN=)t<@K0P=TBt}s=hOcJk(#@^ literal 0 HcmV?d00001 diff --git a/doc/docs/static/images/0013.png b/doc/docs/static/images/0013.png new file mode 100644 index 0000000000000000000000000000000000000000..05371b914eee623fa7024d4abe18ec2e88d54165 GIT binary patch literal 54600 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�QL3EwE=Sa^c=$|D-^l{!sBuvnK-@$; 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 literal 0 HcmV?d00001 diff --git a/doc/docs/static/images/0014.png b/doc/docs/static/images/0014.png new file mode 100644 index 0000000000000000000000000000000000000000..5a3742f8de1e503c000c315910c461a11a570e6f GIT binary patch literal 42917 zcmZ7dW0+_?uq_I=yI0$`ZQIsr+qSXVwr$(CZQHi(uiw4T`R;x0&zV#*E0v_G<`^}@ z2 z#~JGBQA$vgkLv5|>5WcNQ9wGlDujkcD(LkL^iK5-;1B8z;b*66Y3-uJ?+*APd3Fr< zLgdq3_)khce7Y|bH+g=eveu+IsE}~BKKTKEzAo}ekA8W)=;lCuK|u`2g*@U`SN5;uv9^)b<@%Zc@k_Razn z!Xlf(7x01m$(SN@#lTPKj`r%6P4@O~2Ea?D5BJu3!2t&hQUs=sbAy3-TY?2!pFko; zAnw7Mehv$U>Smf)f#^T5Gtk#=fBgJ3U;+bw5Q7ExApQLObe;bE^hEeQ10P)S!vYYy zn(szhBEkUx@B>H)3n;k(UTTBuDGe=u&$D#_n1hJ>@|Q~R0~Ub-{_zJ<0ud(QodCwl zYolJmg{_027uxE1 zJB`WIq}#*wn$2{2(m6abvP?8eKFWfrZrI=n5xhKqUGeL$*L#+pf^T10pFWO- zbm2KKOIe9Z)zMN_0WQA`3ya|-)vT(9kCD!Nyjq)*)I_sz0kF||sSb|BlS-lNr!?YI z$ve>l=&5BPA7d;tmU-;=I->OWv0{VWDWNmC zD96+YW|*3{f{=O?1XFwI-u(}z`FrsSkZct8vgqi)qMj$@yc#KMV{{!MvNsL+2W^<4 ziZJc_5W1xARr!9Mkz@edn)=JKlI#jS<-aSGU@9#4oJiF_?-xFmPZR5K*bDX0(=f0_ z*^`CxU)pBGW`xGyi|^RSPm7qEs(|m|Qft3z7ic_LAYIBbs@F(l$;evrg3#31cukd+ zZAeRZNkU`lX}r8YP{O%$v5vrdY&WpJ`U6lyyfO}`7TY(owZgn_*XDJT}6LI)07#!go@Vx8x&Xtrhq0lT}; z>puF|hAHS0I-69$RVrgH14t39Mh*w@h&N3)aUN>o?3^S_3l4i3gUjQQ@Actw`|wm% zRcOhbP3I|7Yh{s`wYaL(Jd{=)CaJNBxdDn38UU<3apmj=*gRm-lKE_DD8#b#Vpgk} zfTjIc(MnyYT1C-H&I@g+w(GgNr*fk<@ZG}j<*`De)!s5)UOAF#In`9fU$3aOnj|Pl zgxz}|;5@L{#Z^Ng{9H?n!?;$00)ZGa!JXT(ER*UOWo`*Mw~{E7W0zH3(RMUWPjmpY*NW zv@6S!bZWjw(?4C}yUK>JruV549RZIWGqS>}HFyIos?!Q)Q)kHDOvZQ5XElv(OqN zbr@Dt502xY5Gu?N53s5XK4Mo>^5v+d9w!fsM1beVJeWf_1$r3fSJw?La-(ucer^x% zEaK_x7Iop7R~@Lfu>QOJtGd(^R&q%hi%xsguMv~C7nAk$*1xD3|5p4CmUQRY9fQeHO50_cK%IO1TB}<3 zf34+UGOuyANIdQDz!7p*Vl!VZu9>?T8EQ1gjN}s~y0r3#qg$PWgTrj2nnsB-=>Q6e z1OmiH59F0?nUxZeJBqeH%AemFZYh`t!dk4R+QG7-;;M*(1E)ltA}{);LchJ?B=M4`ijGgk&pS#abS70i)=U zPZEUXYzk zlu$b!=pBAJ_K*qUrt(K_$cX3zt-qL%LZrEy9SyiEoYAo53w4AyT-eXtkjhYWApgK5 z=T9UX>^onH=V@^V1A7@x^TW%8B90``=ydqqCg@*3B9dy83rLAUftrbSPfkKYZ|$^N zVu#rAh-;cMoq0~HT{P1jwyIfK^F1#R=v#B3VJ%k*Akt2pS->g6J+CmUq{_-> zhRYr-YDp3#Oh%+QZY}44WVdyE8zLU}45JU2lkLYkv}}3S;>U=Y)?YwAg);iqCKA-R z&I;i09I;F4peL5mR)0Gmgm=wBf|*0emd1w0l;3X|M(i;sYAdJub~Q|lO$d2umraxj z3x2()9uJVc{Z?#JlDU^?oDjYp43XaLXs;<`(^Pt?o*0?dfD`Rl>yhGL`UUoqLP&PNb=ck9P@1V`9xj`oigd zcTOCdj9XtBfXam-%5xPFg}qLx`f|H>eH#<0$PqRFK+kTA{mw5g=1kVP;x zPs`qtdW9^B6_q{J0m5WlcS}S00s#Zh3ic0pW+kqcDV9)%i0%-HMGuA|5Jm0}hW#2q zo(O3VJwZJHxtW9R^*Dr(9+oS zhu|p-DUSrXI#dhCB?dq{E6k=8+yKKIG#Wg4ALUE$89j}U19hHf{><^FKN{QrRG=B< zNN3ZgP)^T@N;Z%G3xv zj~PIJrPhrT!vVP@ux5d>ZxTjSEFu|@UpmAsEnJVy*KZsz)+SGw#N{#A+s}9EWO-m? zCgtP9BV&@4Fn*nP>V^RFyFdlJI`~?*!Fxg6j2(t9=1FL)k1@2=KsB&|NkDvvx$MWS zN)1+*vPX5frW5O+rRnPcKf(LPpyT`WCw9DOJz+T5>zIp2^d!GJan3PZiI%$^h?C_0 z+(AjroQ*cflDFT(?{+(J_H4hS*W!fjzdU22uk5&VQA*DWhC7iRTIsRueJ|$W#-!Mb>#eD9lHdm|JHi&EV`G8(E}||BL!Zf@JwV1nryMqkW=G8q)b+ zp)+v_qtp1ulShY@(&F)bznvX)q10iwU4c?fV9kL?-o`v|vMEA55FiwS*%Bp?jZO9L zPQLkVURsjBKhT0%M>aSJh?#0{1che@5LMz*gzvpug{nM+8G<|np3llt9hshFn)4ZU zbc%^wKU$8B&>xJm%*^q-j}<8Uk%pf^J8M>*n)=r)COxjqV=c;S;`p_C%GF zV1jznnH*{@Hri3BK0jYCZeLF;mV+6hw~&vHC)4SoM6tySbXu)yMBCd4(yUpTou zR<=umIsCb@AU1(yPTBx!+ul+TISTQDf=P~=Jv4L;+BE{9ocCa|sB?|(ZxYlStC+6a z^CG&n5ucw%Scc2jbbIR;B+|H;5womp`s@2C^qu7(N5U6wgx_lU|dpFp?!OEdppCrw}d!4JNitaPb}yoOvXhv*UjMNT8yMSYGIvihJZX?-=sd1ee96>^K_Unkcm`1AfQ*B-qznNyYD_^>BVH8 zyCk@~5fyDjn34M1q06v$Ppm^v42r^vn+x`etpJY~gYuCAU2`v4==+F)3s}F_R`yEu z2C$yb`-p1m9q$hs@6S6irwf%I{%W2p-HN%yf<*6x;J%#g5FI70@Ogy;Hz5oUoRFa4 z-TfeJk20ixAV8=Dx!n8Ih&HA21*j(K#xxc2#tJM_ho0jlCgooSlDnw0s7M}dgirFU z;n6qv`ze9VQ`{u`W=hcsnBdEoL%v+?;)b%n@sD%YjuO=oukb{+S0Ye8|MYi9$T64t))f~j5h)a^q5en4y`FcR?9B*MDdwOx`g|YJTyARpX(@}a%cfom zjh$;_a;*qx^37^lyFRm=Vys`_Qq1rAh#WJM-3`do(ro(Mw>v?1sMEQJh~W`&I}9)S zL-VEo`Ge)vwWyH~+2OL0?SbS&c5Ll=v>5qhgiAF+ z<4TV@BwU{#hRU^XuFmM1;aHDRV@zf%)m^fiVPL6K>EJ|cFF-V_ z=BXYLvgS140s;c;9?#sA;}d(uPLEBa;bwmPLI|_G1X$(6_n%QZxkt(s+Vj~-tV>0) zkd8$e0>D@(I?!R*e8Y*V$GL+4bZ=;@tIq333*jF<39`B3cf0Hpcrf0`= zS@Ir1UKI^Q@;DjN%*y0__9$bu=4*Kx>*4LPM&NMu9M+z~6lcpPN6d-a;j89A$a*84 zHV2V^C-pRMR|#SUHnA}0!?Vp7phh63Ql}p`z(4D^%wSGd5gGBgLmot>U|6#7?pMA)R%szsOlHDM z-azj?&45T%*~KM{!mXqTtryiD#0FzViUeucrsk*fZAf3F4}bsab3>Z8Bub?`jP93V z#i^IiQ*w}Y^z}`JEkaflEBF4EBDsbcp+T(MlYfkXDtsRJlb5mIHXlGB)_)+ezr^z zTFab3UJ;s-v8X21i_Qc(MvPoW|0U~{6;^8&-*G2LX^U=-EuSMjWWHp9yzRX`#16ib z+9uwQ`sjs7Mjg~5lb8SD@T(wi=Tamn`Rj>1Xl_qRXq_n6-7%dvX7Hf#n?n3}@;1y1 zfGS=fS%Yw3jO~mmtN;rZP*+#CVtuk_NH&X;km;iHFRv<^>gyXQBH$!Txa**VIK)#J zsJqEdpUtbdgYcT*$x~FXa)%G}Xm9~Sb8(cjYQ?qENYlW(gEsMwR65V1Qo(!!&b8b< z@y~?mT%Ydp&_j4NZN2tvQNicc#+ObfOA$$S`w1&FIyYF3Yi20i^_d{o<*eRHt$npk z?1{bBE+%S(546ij!3?W*#uEng^!%EF~@GWMe!l(yJ9Mff%m#hMB1_*` z42hl3J|ffle^%Rh1ll_rrAWc}T0jGu&^qQx5Pt~Jm^h%t&I#X2F}F{jda+jRR!}qm z4q1l^%qiefGPlbZTod zO7KaDi?iZIadvhl%aMSSv(+L7LbvLRKykvbW``&e5eQj``ODGQ2sU?{XI44c zt~0`3!`@*JU-|{>Ko1tBW@jxzarZBoaD@EMnQsLrGKOdI0?QzQ70%XY%OBqnQhJFi zPAv6&49RlVFDZByEXjGxh%}n^O>byqMEi?1Zs{4XW-CIK z?2!aYmaNm8SV6VKx3zPHyax2vXt~_QIJHHvI3ZCZ>k-# z%Mz=E_ISLmuK3*_q@`gcFH5B#vbLzRJv{1^)K*3)Wd!|TwkX0ZQ{^8cX<5iBzBt?1 z$Q&dC$5)V>C&Fa`qr^M@3?^Fk%#p{qh{-sSQHF+=OPdcWUR|!SaHaSs??_ZwZ|hxm z_qsZNr61FsdT%hy5Q1pONQdBhv^QfghtmrdhtsLP6y32aMg${h);WrQicFq}xdj2L zsgrzy1PhSh19`8PltaF0$U2Aq+q*S(FN``p-(#V`WUXjya$MN+7qZhtVs}t}OC?Fu zVo@bb7)x%!?h5E z7NKG~-^Q{|_fr0WmmRs^xO|))!`0XD4RH9+Arw?r8oy zzfoqPJlhLGTL0VNUtAyGOvZ5A%2tqq2cJK}0z3(Wy;NG5_ZMnGSE>8NlCJ$3xhvT6 z^pw*wnq;&=dBN8#oVJ>GHasH2OcR;RT*T6=o>2Q2#BF_txIKT+?zs^~cmrz42t|Bt zN=(|Tit|2XpE^}qTHV!-vpj8qikk=xWqpdD3{EDxhXTP2>{TY^KOQ?)u2u>FU>VyK zmoYCikj7N~DFd9V)O{6AVfXpw-XFb@0{LCi?z#Yevj-bCyIU&i(IC1qXK3+4S7&E# zv1_iX@*EXMq`z0Y6>e&I2K{A_w__Dw6s#pW6o^-*?!ub-d zHs>7WeEV0W^h22Ae``1=?Cn{j%`zPAX+MU1tk7iIkV1BS=fFBzXO$#aCX(Tz z)yOHMI58VMW;XbXN>mLU+9a&V(Zv1(LsB}Yl>5s$I9;tj1zWV3Rr2rt6Uen(nST7f zqbl81tq#YBH_L}$hE%t6Bhd1_uLCm+QJqX5hfwbz$km1(TsZBydY!VdKoM!)X8niM zo_}`k?Xf`Uk3e+oo00L2!>-yQn(y?hXD=qmYm3Lo?MB^qiLRNxd>xB=(C^FMMS3rV z)~$@H(ACJaD=_2qz(J_nQBnV_tINx6Smq_+O3vihX?>p0iG5@@Vs6VMTpQCW1&7`tYHifcz>V z0e*&qEGLz_A@YAy6@LB@U!yr>BgK#q_WzIh%@m7rarh#U$>P)fKr*L8y8Uy*rz~fA z-&%)?tJ26%r$$zOtaF}=lPm(F({XU9zsz#e!qv1`vS-+m4(6*q5hUQ}u|fNSn- zpYOL|iUxYN4}v?%^W9y&)^^rU6Gm05KVNE!ii-B{kLE_k#(DMihzJP3IAu8sA|m3h z(2c;!?LiD*e|F&QMNQAJZO_f*NxK0_|AhvMw|jxS`6xES*L zc>TEP!Gq(z^>wnLS#u(aKfAMp%w+QfRZTcYTBDjW18=$@h+yv z)z{Z2FBwYT9C10{RFAKFhC5IdHR7fsL!&CiDpP8WL|Kjx*HBwm~45r7k%jqn?W5|nnAN3jtsgevc9E$ z#hvcEg$62cU_Vwu^Q^n+pPX%tfHb>g#X}SIN$!EPJ^fx6{Q7o3 zv8tHq=i1=Yuc$w8t9Ckz$+#?fs~00-VlG0$YJ8|aapsvhmI9tp^{I@Gj1jt#50vNN zNN`;M4P8Z3>Y3;_+p3?m&k{t_PSoWT;eYh4FpA0s{ix zlP8AJCQNl)w@=h!y6}B}z3kle{CvR;4GoL6BMayxZxr^ z0nc9>QrhKrs`uU?pyBsS{&*>EIZwn(N zNfCYZ0o6uaV1XLcnreqRk`XLurTdLD>qGtCe9tW7^yzL)!8l`75Gi?Yv5iR3T@2&w ziE^ndXuL;qh-|UDhZSm~nuoKAc553QP!LAS2TfTTqu!#A6 zm7{NI2;-;-R6Zp+PedHu`@0ChbUJh3v7|U&d0wWxfW_xOiqG-;^UcoDF@d`IHvya* zVZUt%GOjq=d+#j-uWYtxKh{l@D2}Kw`pPE*r-XVK)6fK9h7(Ilp<+%m-HjLXiVuOK zlaq5nWifi*1qgW787BuM9?(F0galvnVCP6qnLaNs0fYfUH8LP{6U7axdYRrw^ z#Eq~sG;kf!f+(%BLU9?>EjcN7abTkIe1c^8A*ypAB=+fvvb2Al|LDN)ly#?{!U7ov zf^yg4O;gF^iN8pfuQk|7_(^x6Zn%L@QgHKctShMX7ia(0+Pg<0)z4$u%$=c5`kD*6 z=r`R388#(vjOzvomZ+w4CHx2-oRQ(R?TKA}ZTxnnP>*!`PtmSYc+5|GJl4} zt1E0B*P2m0hwV0ZSt|yU2|g?9aye@%q$SLRGjEE_Ln;p&aoYFyx7+;~+0-}(VZBHM z>TVo;0=-AV)K-V9;oJSu{3E>PZ_N*ODUtW438_!d_d69TosLyYvAtNI@%35*xSyY& z+j&_@;r*|#)VA7amN`$n?t0Pbh-8$e>N1`{EhD;FEJMO|KSW3;j9^66XZ*Sw!85$x zY)j?!Y7Y#FKM*87&3^Gf!p_-sNMq;UsBlXt+h3`4Z;`tKh%gL!^y9!h(?tC3d|xoBCF zfp_axk9DF`O#Fqx`%E4;(>)F3GvZhLL2)oVHgG6D9jLcEQZ)EE5A1j(!%27paHZaAO2^vss~~n!&Mm_c{pC~i z4vQ9H3^*q`olf$wd;1hxZR#RTq|XUq_K4b^PG~9KA0RkBc6F{J8}~VB$}}^^u}WLkaAOqUVYG>!-omj2YC3VM zvrAz(YZ(ri(1pg-H8MLoQlRsEL6o>D z3qqM?ux3fHkU)GkGH@H=@;7ceI=O!;chU$gAvu~>rauM=#)>Wi_WeDIa{E<0 zMq6YR9aMXxOw*niX}0%IM;C6~Gca&fTtP%=;S3&nnrmJ`py_6M*_BO1YbrvxbzKkB zTn;6|#E!oall4p7iYkjzZs8~zKf`M7<9>p!$RY}Z2@Dtvn!cRS6+}Wp0%d|i(>d3SM~>gy2dI2bZ1KrrsbZ|SyA5oSa~ z@{_tohVK*1>jyd=PD7s7^ix(b11z`$ECqe)b8yn;MGm3EA!2i^1Q!Cw*kI z5T#9?K@Sgm_z`>&*t%OeCO;kSH*(&5n2^mi#Cwb+v~+X_^CpZx!{q@{9C;G)l_Ftm zuIV?$Qm8vpql=7u7B(NILmY#?ObetSI`GZ(Qfs9L-SJEgcaei3jaLinI*iZ+!OY{e z$7^RT;3jTJH0u62qHm8*D)E9Xq)rky>G>$uw{rrRzSf8qxnSF^(7s|Qa}BNT;t+eD zvuAD(W@{#?!{%D1A1ic`ZbQTQl&n3d5E0*cVv2@|6o^E?T2^iu!!4}QCW(`-&SjHf z(}}9JKM>7&vRo*Zfh9>tqtzM4^SK`A>v_(v-GxjC2kGxf0 zMXJfdQ5%%71)k1kORLFR=5<+YB&CNKFXU#k-mZM!^KvOgWS~xqASHs=62M@jZllcm zw(U^c+A|s{#)T7 z?Zs3co_BWz$~J>8tR*>+R8ROCk8=B(6u`W|JX?;qB>cEN`6)0yVAjULQQ-T@tSme1 z<(EF1%cd@sB~kBx&CqNmYop&aL)yVey_ONy@~jh_Axg7_r96mJaW;l=eIVH2iCIdr zeFlP7p23v#@t%`1H@<@eHyEV8)Z8BU*7jQ<1M6v3qsKI76hFqBcDXQ;JUL971_|)r z{n|R1BL%jWWK42M3tf*ex-WGZizkU^AqW)h0 zI-+uD_O4NH5S@RUWSTh@FXqtR6sUta4PL zsDHxgz^2}@uJPl?2)t7NO2~} z>p!S^B#9HA1|!phh9n$sck`#*jFsx+V4wD2Qso$-Qe?>D$L9OQy{(Cky_nG_ShM+m zJl5?Cu@_Hn|HMrO3G0!4{6Ne+=TK0P^R&TOx?TZ317(?#^fxFF$)vcR&jpB+MzrCd zA7|_}?4jEm8H@=N!dY*L?EFO+BUa4!Ca_0O5r1F#i55yhoNZA5BeN4HE*>e&cm|P8 zur*6T4_cN&>(}MTO*Lb%lIPIs+O0{LM!it}g8sPi#Myks>8n7P&KN@__P*_?)YdUq z`AN;|aUs>%_QL?ZD4UmJYBRzmCAV9BmH;n;0XcJ>ConiOI(pC|{D$AYLm!E;sccHX z(_u=}t<@pc-1shxA6I$Sr8%a0LapxLGi@0#8qaLSl^l}&!s8b@I(@oj3eX;08%g&L z6E0>kOshpvD*QHN?Pj`W{@Bq41N)g7?xGf(eC5k-}H`R<_ zgwXauOM1I$P)>LDe!ua=J~GDp7o8)YE84RKf#3jdK+guA$Ew-q1BMlw5SkoB^l5^ zTNi`Vt- zCMDNm0IsxfM?*Qo5{W`_>v(!ia+S0mMt06B76}I&6f|y! zN?p8(^DP7XV*aV%VEM2^J;!-Pi$hZDIw*oY$I%Z}I!Pj#uGyR7C0L0xX-^Q<2qtbz z(m|YNj+WUlesNLA*qCM0|I^OqL029PWq_=ZL)Plauj7X!5FW3bobZG+zN`}eg(t=# zQ6;$`6BS5#POv%wbi}-Qk~9D9ZKw*7SejVX$sIeakLPah4DT)`L3cn?ij_`9Sx^7t zk6Y;zeJ16Yz>78$#S}_}qt6hMzEG9_qo)9d4rxJ<6sRV>dmIjeV5zi&bIf49GDhOe5R+HlH7F z31@5KgfI+Ev@3*EBk%XK5=8SO)e(lV$NTcLjsA$})%v&Zj~gl2LvF@wZg~5lv-f9D$f706u^M}yw7b)$AIBWSggk{OeJhE) zxrNQ``^9#BOozJHHS^wFP`APszqP-L{@C#uZXJ0EEp-22<$#K@MQH80bpyCQR5YS^nN(0^#zer&M%E7&h@whaVpk5kE@_5}u;fUi`{nd0_(<0r$@>uFd z8u6A1N$tqj-IDfZo-RUD+lVG(YVk!6Q*;F7Q|JP?SLJf#EJF} zlGbXm(#|TG#-{(N!KR$JALV=UKjiyESffj=5rIwUAr#Lkss97f&#m+vr8yp#5R zEr`u~Pb9y88cUNQ>sFJ2@{;_m!t>g_5|-DFTe3Y$b+P+S&d$% z0?FPH4%v1MWF}YM340sq=q+gU^NIexrBM*d%Gbm4d}HfOU9lTBTY zlUemGt-RvJ(_`$~kKy@bIB=L8Z9KJur=q+s+!soDXdxU9#8yI$yi6{yd$dO;$V`LH8Gsn1dO}2Xc zM&+)y+7g*u=AO3L?^b9kG~9&|ra9f)1n+>pF*)0uQ-ZX9d!koilGq`k+E8^=#t5nD zV+4?f;LY){6LiOt-Z9oslqdx6-JwABu)&HUO8?&R$3XX|n>SyrfH98bO?Kkb9nv~o zy02g#uP^}yhX^>20m4cAGh=Z05?l=y#krXzx@o&S#10mGydoF*rgKdRT`jCYqxy+v zWCU9!x+G_8MlK>)<=f$cKdl)x5HL!l2!p1HO7yH|;Lq53+d+>#I{`CZj`ikc0MWf) z*g>2_=V%kp|J?Zd5E%d1BO}gD?zCh{LJziV9Tu)(mAHdDQY4(zlLtrFaKxYh7XHk!5?B7Yd)x*r>xW;u7823YoXc!%lc) zVM@|O>*c@pL(%O{vF!}5HJHZ5#`@YH3}YPqobdfn#DQf??12h)UJNzN@K$U*^^30t zCwMYeoFu^IHDpj(qbUYo=DE%tU4?S#cwv3tWCi{Hc<|b$r2jR>4YKgr;1~7Eebjpg*`_3pl(T_YE>1V4Ewn(5J^c-6bVnrEY|ith zJ1=RB$!s3#RQFWTP7a51PI#t2<2bHhZlrA?Zia^w6|ku5=;5yka&Xz!5YL&-sJgyA z4O;_cEsLrsmjoUzCE4_pEWv$;xDGp-21dt=?sh@bDs1!;VXG>2m_jvQyF50UDw~Ja zw8ZD3_+3r*uQ`7Dv%BSBg(>LP3IV9b9{~ACrH{{^opOA$_MOePu}H*ndK+gH@-(`C zBIK^2n2+*))83Esfp_ZQP*UTLt>QCWHht`Pl;@}KT63r0hm+ekruk}6sw&t}bc113 zL05EV=LfEunf$LHtGaR(%tf@WFVa-(-A&jQCbKZH=@w>ibsz7%t)8%x53l46~)-sxHj#i_{FH4^@ce#L3 zm+{rv@=c-bgeP;0*(S?lEcS0A^XV+-x`>&^QD*6NyY3zpUjApSbp$Y z7aNolfttgkihwTyz39c(%@Zdc%;2ROiR#Te!bvDT{sY8wy1-A6j&_6#BczBWiJ?ZI zB``xe_QT0gIqyc#hyoJaXa;E%Zm6$JRNlWWrn8sO zzd*T6;0I1%?7r<5&wK(39v{Y~(0K_l0WCB+#OqBdV@6rAjd#i2rTsWr4(O1T1BP`Q zHdwAypRb0KCSD^61CNu`9Sn>_!@ZY9<1XlJz`EQqt&vw3fHEHCJd;*Qj7Pi=pf03p zph3IN@s=T8h)~(?AS#^1`j!QBIx?4&u{~!hl_MK~!ccpJQ@1=Hu-^A#ux&ph-TXIE zH99uc2gmoB>rsL5BGs87W+qPVy&(`GW#&hEQb$q>Y(Cp;7I7#w;u=sK``y{DMyUSD zTyYULd=K--s_768%UCJAI^dh_Ftg-_>`pDo?*S)ew;yR$SEonUzK5^29FiD-aJXBi0XxEs9lV)< zo(ynVoxB~uw@A*p$FDmnm@c6sPWJs8X88h3{oB>1&eRcHkIKJaOf+@UlvFcqMEhq( zxHRjSt0}=%5hpV_H7gt$TyQ!R+X0`C|FG0MsHw8>XtA@s-_0i&&5DX;vn-N4wfnh2 zM?L~IFx8SDR&kkE-{0Mc7VJ^gjb+h$`5<&a~8N4^B-u@nrBKgh*(!c!W0c8bz7XE2gDQe?(k~ zc{u;ruUiDlS^Rm*Y>9go;~e*Rj%P;8njkJmrb!S9rzjBtt4AZuLe3u6*_Zzt<}+ZH zUVl7G*@@Dx#Wyrk)?wf7gm0k5Q`ia0y)@k&x-Ku8VU}=(ny0Wx@+nf>8cOi)U{t5! zy-&N>dJ|;6+Tuv|z3u~V-oYx4H@3g3t_dJh5Yh-8k7{fWJ#*O>CsMqKDC_3nh)+hPR7B zM4EURQbnDtFF0)9d7t^UceTf8xC+mv@pLItQ@uDlekph_H6W%tUzCedg<|t~``{b0 zjy9*9MbAo(I}zdS-x4CN z>z1TOk9H@DElV~D=fCm(f%`*>D7V2i=UbM!&mz32HKtaG9*J&g%x`GPBV95L6N1~U z!2okHKRtobH^hujVjx0(4alxN>@DZ8`&<~SzsDH2;;xMw2j;(h(*>-$uKHWwA7&A;UnaLqF}!eO@u(@U*GAbO= zQ?qPbAZI`=bUP z4ACjv8SHy$T)SaXaL;_78sb|JgF5G>4=S5sY@|$|7#4^V3`=wyy$Clve9*dd!OoG_+GTCc=?(!E>`&-MGwPM_`@ ztqUMR2#-MMu7g*rG4kGPszU$&I6KSeIF_VMTNYW27FtY}g%&e2TFjD#EoNr2n3jU8x#}`Re zyMr!a>W?|!cDz)RV`6&bzhu8OVN8p2N)ib8EDUDX{-zNP6a0P zY?rdb*&J+8W&uc36r^m9@v)MN?)_-m;{4tw@<|j^HG%T0W~R~$oJ|9WKX7S@Dcq{SWAl*p!W#?&kS#CFGEud9+hGf@v$I{o}J;LxG4_*#u5ShzM+@U!hu;eDD6Bi;^P zE{zw{fq>A=lus}i($+%{>C8nw443m>``8+3?DA~H3(Z6tM=ikV6ZzD z&RqH6#3}+EfS~T?94y=n3$gwa37jO{(peEakAcPc(l>Wwi+ASL?k|@EWN_qG#^a|U zAt5nO800c1IXzVMk4e>E!J*Gbbxo>RhjwhW$h@p1&9V|>Sqv7Q%s9~4_d7!!z6a&N zliy6w6Hwmwp4NeTQ#^X&bK9Wq7gz2fJV4)`h9)q*;f(S`4~)d`yHqG7)x+kSpcaJ; z?}9JboMlf0z+Hx!@wb=JVr~csg;6B24>HBNP?YBZd_|_xwq$tv$qaJuit^N=YfSC4 zgoWkdwyq`eg|N^744mxfJ$CbR^d294L_miQd6zl6<8dtF~JVZLe2{^vF1q*T;cA zh7&=D{dA`kat-?o;E(l##|=?ZH(riS`t~1BNE`))9kTGzDfp-+qpKzLMrTvcMy4{A zKDg{U`tuwnxOvYRUg&f&yW7#JeixZ;#MO`CHmEk57x!JLQ<6GYZ#cM{;NgCL>RfL@ z=nGolgIT7P~`F31^X?lfuW5mlzRC zyg??VTjmiR_cN?k&&wBUOS*aU!7TjR!g=*Ycn_=aPNHTC%<|hVtdeUVf&}s2ntksx z)S+RiLKC~*96V`Cai4y#=vIET+7ci{CE#VAZa_`d2Gv*VPcZ47)V}k0r!DH{c zUZ2G?ZHbe=2@78o3JZAQalj)jss1b$#qZW2aQ8P?&;Q5OwO3Iah7PZ*n#C?$2ei%o`(uk zsLQklBWJXI%9kB=;DzFAlZ|bADp)`}=|gVSHh^A(%IpNGUA=0D7*>&eN*isLG2GKn zxDD5X&nrN?>3I?mMU3pJNG+ZeUuATZEY4F1Ix%&^T3tpk1+4CyY59ktN-lC0&+Q z8{gng#DBf0+f-$dR%Sdd?YM|ziA+UW-g&bqgB8?K`0{N=eD!j}Tc(B{W!0e&W3rA3 z1siQu9V|tCYXvG-cinj9J}$$$sSzhgGzPYs*bu2*t(Q~=5v4jh^qEgq;QISfwpGDiR}{l|OBt zd9ptk;YKg$7t0s7;!QR!@7xI^WQ65S?<%gjQcovf&T+zQJCM$V1At=?nB}jt%ToJW z5#!YsRe^U!aY|5ngL%-q+$^~!T{8QzLPiVQj52w17u9loVk3nJZ9sI_@D;`xu8X;0 zX0|#2orTsc0!d(;-YFEe0&3?01$nmKXPO714?|~CK4K?L!5ocHo=(i=*j1mtc&!)t zJYza8ZX#)5eiw~?PSWvw6i$)ZV@s$Gd_P*HYq2DhC3~J4-E4>(-?N+&t0H0Uq}d)P ztgt`+WlDkrvnCGGxju`=!1$*qhm(^E5p4sN1@hY3m-xN`sjOth>mO|^9V0#^}hfkB|CMn?gU3x8u^#dCDE4I3pR?7`? z&0PZMcy?)R;Ar;kvfuNwu@PVq@z_IzsgyS(mZ8MkZ~4J~4CYxRsGF$(Ys<>J?1wd{ z6gJ*l#_q}wSv{Q|9)<0UZ?cIkzn~i*@MHrzq!#Kiq=!4(!}41nNmtugUEX&F8hsP7 zKNhPrt84ebXTtpHE`++5A^Euk_B{S^DESua*eg_y3*QC1shGJhT4@^U6MehcNBIdr z)C7PXe^w@yssbzk5@>v$DC*kKi@Kzj7v?HDZdSQb)W;{It7}n0Ma9hK8drF24i~VD zcL^&&P8E*gjFUK2z%5>&+vbv*t5m8y=M0%o+{2@!KM)cB!Whq{(ea*O4Af#RGVc?H zlNGvCd8nv{#`4ceL!r*+hA^NUC-;k~5MN*=Wb{LQ>npRER3f;yAL8S068KVNkVL9B zGenHwvRTCp_om$pOrmBPR*d0DYqu~&6G(o_6FIokT2oMkSF*DxXa`A>tuQxdslAEq zNy&~=_PY{dHFhQUrncS_FyU#N@}F&Tp7IPCjuY4ASaWrI-z?=Cp{$a+m&?5?QEYT3 z1V0*0y<}Ap$HHfXx@RJiRnv-YYfJMurW6zPv67S;ewb=ZHFANMGf13{%kN{2zHKtC z5WW`Hn(fe(gIZ>jQce0mJi z#8&nIN|@5oSyJc5ueW+E=Lm=9HQPJ`J2pKN#K~I#;cWnFathgmBcm1X6u^(mCEX>u z9R~SF$mp@aqTD3-53k71L231X1=NmY`|1?}xIvc;`wJj{Qw{6-#^_TIo=_h9itTav zJfMLX8tF^Ypyf|})^_zaj7TneALYEnjTV9hv|^{ejtsYXqa2M@E$p|cm6Ja%NL!O% zk{^XaUd`Pd%1!oA(Rrt7_GvfQpBYRN{02!LQzZ;y3&)aG(O}95YZk{VaV)f8>Fe~7 zR;JZi*pg;U#8-G_ynN2-&Y4m3+mNl9e$YJdvI-LX93APjOi6D6pjX|T+n331+6iB@~i|$jI~uCt(}23%U`+oB$l{Kr;U+z7fiEKf##HC|Nw5QdvGG2P0AYKK0w!(o3sFO6eyee<5qTUT$ykO6GlgTh6U z(5+F0C?-Mgt1P=`hs*wWdbR7V>5%OZ6(4(5pAXE@!BlR(bk3=ok)XpP6q;_GYA}`W zknHe}E4_Y?8Zz7t*e=>5vVmBdP!7*$`i7qc8fKJ@|ivUCRcewVxIJBjWm&cyLfp@H%@oZ&8P)P+>(pN1$q$CK1*A& zFX9#0(FV!H3^>-DlN^yE%Ri1ZsHyr7Wz)4cN0;%gW3C8*| zcl2e#!j5#p1=L?==Ievjwv@#EZj`YkTa4{Z`IlA2?<1Pxr7OqNJZNQX0vG3#D z0v}S!5|>-R2hSmi7pCaJIQ1<8;!s0OzsHGbaQiiB6-_~!6b|8V=g!pH{aqcREhXs| zxF|RwaILmhlTMW@1BNz}!?ZyZ&aK8+0Wnktqh*qjiszD<0fUi_xWIk zo|)K*=H15xnzi9d5{u7sHvO^nV#8Cz_D1Of4x|jab#YAd*#$rH5{;Lp!}l1L8;EKi z5u(St>VhI`<}f-^3ckft-pL7x1p~B?eV}1<>&Bwn1Z*Z!z1IPK zRPX0qR1C@h5%u=ke$l|>5AB3eH(3kjsf&FZK^relPBDiuVWPx4qs)x=AO=d;F?^?J z0hy5(`HmU+Yo#WNB{GCFe-|iMf~>~Hk4cS5fzVozsFO6-YdfJ;`qUL#WG1)L28|Ub zN~$MrX$!%yZh+Z7B^pJJ73ytDks}84tUP;YoK~)Y-seeX*G=_4^&06`fbe}3sQ>Xp+GeBuH+3ybSrdA$CL;^8LeQ%8Kecwr>Fq6%}P=LY8Mh+|u2g z`v1;t;m_{5V}AuGI{ zJB3w_X84b+A?tF&4cBr-#p3;^LmRAiLTUE+jzBUZN=`^hlUN(JbLNu|1ObmW$5q$$ zLPcR@*)lpX6EbL0dS)3FY?>jLp9_G}J6BG6?u8l`Xu`Z5?BJDT{sI=gtV&s^m`;{6D!aqQw$)J$7>*aRw-8=e^%I|~4=47Oj zn%`7cS3|+LCk50sz5l4i&<<5W?z^S<&yk1iV+y=PAWtcV1L!$$(7W!9h`m$tnOMjq zX2{j2dmNlV;*-kc5tEX76U_8+rx49Ag$p4z_{%PUP8hbfb@DQaoeYXne=kR2Ltfm= z+&4swVDUx~Oe9ZCct19loP=;XO6#!H{*wb3it;QWz!m0H?oNg@aELTke-AOFTT>zX zd*8>^M03UVVllz-xx-SF`NJ-CrBSR|7{<{&&#Fdrg{`2N4E1239X4MNJ^b}v-&d?? zC-NcFTT0^E?B35_#3|xLvvCbLd+!JSquX!ZF+~}dM7O!pVj!yor(V?v2|TnyIZY)7 zA`M=#k`YZ4_FoB-D_6>?_3V-AZH@}ns2ZV7y}-=$Q)J2N5%2Yl$5XqAsr#fe>eH`N zBG>Bi^3AtX5|)sHbmLJSEO0x<)lYiLhtY*m3?#zQEZ61z?eTOZp1x94u?1s9zU34J zW%}z+*s8-XG&MeDOHzmZay36pLKnp6RhXGWex9bXL)FqFrS1=iZylDZ2*$JFfE6Zu z9i7eRpfZ!Xa*ZP|$dUa{m?y2_)4Cuci2(EN3z5qW&&h~#wrnOyC@Q@tnqtflLAOC9 z8Sl411N@`zn#X@tUH)1c7Tw^HvimDl?VMz@g z#B@54X98v83x?URclLStuHpzRf4|H_!&4epW06M^3~F zd}E-$Zap3ctCV?o5Zi%YNu%n0GH7rjiI0Rl#gwu(F_AzWKZA;j+(|BV_*7i&DZxel zf*#tI|IBwG;PFYz)CqOuKPQ6Dw-6s1u*6b0DCD~_+Px>S!ZLw4*vA=(B}~jN`4(_W ziqUxh@-S{_1!Mw@pw3P{q0d8czDz@sqIMVRHRf|OJ%CyOm(10PHByl_Cjosc!oKRg=@i*+g6&dV71s4FKQ4IxtR@jdj=wtV1@bKR1MI~9oG8U(OXHWgiZN!{F zy9%SUgD&ObiPbO017H1ccH!z@K)#LP$S9V9q)w2Bu&Bx|XnB4?Pr*)m$Qf4=!T*Vw z%+Z}xO>8$xmNdmLq^$5ivoNb5d_>4nguqY|h2|ta=R4o3DCHC943aXkz*L)18rYAB zn0`uK9rd@LCSZjoAU)|-OsSX{Kt&xEHH9sgO6*-E{}d~5IC~Qrmqx5c&QGXg zJG4V>no}BW6Wu94-ABsk$iPymZEWzFNy%ctE~S7#8e@d1M)fQy!+Ua0Qp^LE!v$)% z4?fkerxYO2Mvro-E&%*0K+)SfSONF=Z2UQ@L7-aPUiTe=Ddg;8p+TB*MgOo^ziN28 zq&(?R`YcZ`*;jXgFqXmx)PHyDU)i5v+Rkf(LJGw~4Xs!M-kp?`^`6i-UE{6PEGooa z4h-G>qmwcH>bCG2K1a8VLxC{|Sq9(WI**P_Pc&k}9^!3;ItwI~C6>yevIPwtK3Y*IKB-S7sDoSMGm)E8T!8WgO=&lLUzh2kI65*9Jx zxuxRjcOeC7jU#JjVvD6cF=5hJK^`j&e^PaYIvnPC^xfEaI(mFcA-zRQQ?9wxNQLJ+ zFkTw6(siDbK@DG?!2&OIdBXAH49xNn+@xl5Mh2`E{x#U#_r@DvK+GhIb zdUboNt-93dQa5P~X0F!I2LPVFDGf@U z7JJ0FyiYW$9_;}8i&V&4Z^|68N`3bTDS#T@|#SR<5hlh%Y zoFetBIds?w1EnyowM6^dJ5#bN30$slj3&CiF0 z&o0bAA5N}sW|FYuVLVvu8B?Rbp(yMZ1PMWG6gW|F1YEMby=H~XKFsXtMS;wlZGhRV76Ml@3t+AFU)tx7e zwlWe5v#qgYwtP`ro$s@9{9!q!!JVxGh3^d*1TyGi^UQR7XMgW{u-NfCD6npF#jxhYH<(RP|b^tSTdK2!Ns9@S6GUg=9nsW zg-w}eBYM(Zel&TkMx$h<)oz+PeE;geKUbSByNSk4f5cW>D^?!`2I&y{w$(EozP@52 z%#{h_=|;nQRByK~t$oGAfRd^>XnoPBLR|-b*PUf1H;u*lHh6xJ47mA%#9yp>xS+>z z3NhyXYBc|s>3`Yplkk=sE+rN-(7>=4f!!tjRCAmLoY9Pd;LOV9^Qq;{VIzF? zrzmaA#k@o8i(#@uU%W~?jg?BMQ&>C91WGNZln`1IT&nlERYy6Q#^jw?oD{U4m=_EZ zu4n;$j{|k*38P15xi=bI%q`CbpYrMESWl<>*k$}$^Vf@Z>REPLDYs5G3I5dqpv#7} z4;qxThp5p{?~JaSjO6@g%ZBpG#O?g{7`Z^F!is?#?l7Lhoy(t70MtYkQowk2_E5MN z#|qxvU`%pVE^rV#g;iG^+?cjLXF}7IuZ9aX0|zbBrTt{i3&piO@3G9uYnX;Lz6g&{HTUK>TJp=LRfZwH_somQErOLRl!%ya6FwEu>JCTCEPw} z=3@aUuWPqQZj`0MEH}%}XNoFrKgB87v^1>Pq(d!szp7K3?A~q%d4f-d>};b1W3IkC zKWik#ZYPX&KuC@v>L5`n_C@;JU_x&`Dxdc2eXsBBZuc~g^Nrdj^*`H}lxG*tRF_g0 zJx6Wam^|uh2h7y#%42=xQ);Dt8h^>OF+VeBwyW02w9?Rlu9KVnQnKE!fIBukhuIig z(<&E7RNhz^TX(I&Kp-p8(H`b0C9CM7UGGk`*O;OPL;$)lQnp`|3HjDfEn+1Tmn((3 zYMN@S)<>yyyk){-Vw_j|Gt&Ja*RF3M+3of|zXo(ee>BEl`Ed8IhW^)Oh3@TcmW!;8 zm3;?O#_{j38v^;D+Pi%-C~Hw)zeM!cCl4W$>+KRt&VS9c=CTy_ySr}WJ3Vwb`D+vV zXTyKrOEUseDwD1(P~HV62z>?DBOHdsAo_~+hDD8~VNc_C8W9GbK~W?wZqi9fMZ%7qlDz1c#JW9?GgUgBc z_H0p|NxTf_g(0n>Qq|H$dyDoCkMs|>!pp8%%P5@KoK-nC8a&NgL~9TwN5ThdcTI&3 z6j~UU?o2u0DdmLQ?KZOBECufNOQ^lH__KB-IOfWoHxJj(s;H+$7J zc+`DEL!J|O+*FH8gNg!m$*X0Jn2`nZ>8^d6QzZm8hl40fvA}y))5xisrJ&W704QQR zdhx%1nz_=4&e82XCvNvcB`8PoEyw9M=Kc{^~p-T1}K>wfHe1Mb?Ez7nQ&sVVpCY z+pqTu+HTReGh0`RtLwf_AITS`MEYOW`bqxOeujACx^2Sqz9qAvrTWBzHhI=mbm8u( zWO;%-?NoB{wfPjf;skIi#|pO0z@Sfo8an3yc=orH{62+3LAK0zIQ@9jgtE$86Gx-e zW<^21!ay?B*Q_ssW|?fR*9~N`TSbF|;jCs-P$#IG`6kcoR+?`?a_5`j#9Y0RChDh? zFGbY0k$|04m55q;H?3K`Hs!BXL7s|LZAhp~P@c8GU9!L}PopuIcl$IG-#qM?Y6tGR zCihIgG=HCOj#lqfSCigrtiY`X&`_Mr|PL1!i3m7>Kj@rjh z-gsK77R~cl%-6Voa|{H&rO);L^C@8MIkzE=%h>1?hC;K+M+Pb(#do&auRsen1#K?N z5R*l6({j9`|2wyvZWc=va3J2#h^V$GTNK=YQn9(Q4t>j@zqs%N6DH#>oAL7QF%3bY z*0gsK7?naQ^8M;Dwu8WFMER>v!@;ex*%Weujh_?nEEZx+*(+kPUj9v3Mg4b=2M@|d zH-`a>INVRi;AXD<{;GC5q9=X5De2mh-_iOt8iiGy((DqBZAQXPf&OjC%-RK-r){P- z!R7+Lm?ls@>jRT4)3IicKP^u%DQZqzL{3}O6TOjU=13`lFLNrVT8@6#5u+bRnK@K2 zG=^!QZQIr{Z`0n3qlA;p;7L+PL6m4WD`QOq2JM_hg@b@E^8||P3)pIo-%WnDnHOpP zIga$9U5E>w-h}x0qfLKFx@-AL!U}!8|I5}C3{zjuGYL;)hCyiZ!?+LL&)nLH+e@F& z#7YyWSpbOx@t14c3y;sSPP9khnN#(6FvqoV(6mra_HuW1rwaN+FaiArE^e|+DGkZZZqi1vE%o^izgM0@v8aG^h4$SJCdcKM{8QqaV{>XboD;#OhQ`+VS`SZ6RAH&Zvcl*9^BkrrXcY4QNgYV z%B+^!=G(9RI6DxAMbt&mw;Mn7F z;j$VWbBifIIutM-X4Ulc#K3G;ycmkZAdGrghJI&py2`|>Hnn-x%#mZJwzy6wmi^^* zad|Pi?KodiLjgcRP_8NeR%Y-+ynO?qxW|H##i03qyDCTV5KPbUw(Yo7)Tg#cJ8OB| zBF5FgiPU?$wOPmaw{wbAHBYi#wYiQ@GU4@ zr&|JkydElt3T-uSo2)vt(7bhhb#?UUlMy6bOX~R8lel-RBjdT@V7fDWIBu-j@9Rkk zpq2zQrf%AzRdMlTjzeSO$v4*7O;TxF9S$rdTw~G>Dh?}JPm>jLN2@Tw%7_{9Qg&g~ z0+)ew_+F|E9JsA_6scokdgCioDB|Wuq%HccxisPtkt5cjiTxCS!um2E@~rUthrqOs zZ@U4>F9|#iS>VsRn8;~$mmea3txiZ;d;r0Hd=H0-6>oSQ+=MTl;uW$8!74i6J?)Na z(AqI>>P5-D+g3wm1Fa}-LdfY5gY#}v&6u1lDXfl0hM47P`qPMRLH}`p;Bm*4Q)zW{ ze{h_##lWR4F)MZ>%<-x`Q;EODyQf(;8AS8$@Y!=9b6}+TkpmXv4gIao~mz z+5xq(DWhL4Zp~is(C(qQ8mpGuI0LcdVJa%jJWsjG+Wy@Sr3J*FJN+3wBZ_zP=qvzQ z<@wwE%Wq*1{ZNLs4|@3z{yfWAnXqy5_11Q0ws*kDPbou!W(=;IHJaE=JtIcg)~-Lw zTs+u4D9;i48c0u*<2OrHb{Y;Z7(+eH)sFUmA0y7q06j2{|pHOG)&pWqlO_$zJH zU?WT>Z|v=Lq_h&Q{SBwMa zw8FBQWHe<3Gh8+r(oukLhWJDH*E~2wJPFXYvcEsma=D#T;V`d7q)8-qPa@iWcBr-| zZyNG9GH$uydDPb_NtU+Us%hl%Tq}V(HKFEc3m1(ub1+wFbhvJ>Q%g*?IfpoJY&q5X zmdEhxBV{Y-bX&9I$LGFIvL`aezaB;ZL&wO@MUcb%wYfulNcHgHf`0(@Amj~yEmfG} zZ!CI8IQfxp(i~kE@7D?hLRhM|5Mvee@-V+#Adp9q3o0b%|9fr#Ijj}$keOP##mIgW z^_#chph6-j^M71&h-PBG$rD(T_gl>HYe%WJ9C+oDGLlQp!?IXVD$z0*!L+9@`RUhUo+l3~Q~ zQO{XxovJlB>6ivni;o55(V-_cw=|~4e9_?(AZo*S{oIj0;fkbB1zdv7Yii3eTy=9- z_JZ!du8F6xohPdPyQMoR(b#58+(I>M*u6~D4G2F4JFB|)X$9*f$6a=5=o#LTp(>*8?mKoB!9k)N|IkyFI%NaZgqcy)?mX zc6lFISMX1?27u67%`M?{H8I8XcH7ccnYN>Z8Zj zwKA(J@I5;#Tg3t=#o=R=DlkqHzk@u{a^G+uLelH?Q1#c)@%aanMK+?J^nq0ye(hQ- z<+kS4j|}-v5{m6~JYG^Bad#kZ(x%8#%T}^1dNS&H+O_(awkoxp+$OLbZ-_>+6yd+> zCu!0gkUL(Vsb$-0@U`=mTis5}Lao6?$F#Ccu@WdtjON$?rN^sZ0q7l{Pq2E@til|v zM(~3BHU?RU0GE7sF03$T{#!YLmfv`J^n6af|53EMIq9NpyR?llx2b%JR7RWO36sJc zxx5^|tUezP2IRq@*PxR8`9+Mka>TN|rg&3O+p^z?tiJ6Oo+x$muDv}9aU>P&zO`&L zFrCP`mL{VNue+YKqm2yM>`Jq!o}B2d5QumfVDIdedxupnk6WF(9HrWsHV_vq{z-+m zNoV%;QuVR*Z1SSNLdj`W_;e`r+uM9IxWSVvk)BB>t;>S*xSCP44fnaSPf8uXRmaO81Q$cp>5}vC*G9 z8ZY1d{>C=y-r3LRE^=s|TQqaqPBAxT)<%gh2MaA(p3E$zF>*WRIA zQPfodDrtmH!S;W^f1mm`C(*KceM$Fg7D~hLxjsK^TrcHLvAxPluBS+|-h|@7R^_nA z;%HHZrJ1T1ER*E7%fYET={BNV8JmM&0G+C7^sWPSaAg}b6Go}oznvBW;V}jEH@0M( zBTDG8z7(RhSM!{+rjzNQ3Rd{r^Ogn1BmX5ljc0I+sbiWz3A+kk#v5wKzu{OlPy^pn zXZTcmbcJKER%X&&XFzZthvZYJEOD^Eab<2;iiOiwjD0{86{G}#eSaOIi%zS% zwB3mB$)QkIZo+|T6-TycVEh;gI9$CTQ2X+hKvU9_GoO>fz;RSZVUT1q6at9*PT;AN9s3YF@1sPzvZbVDlDdHBlR5}q#5kWEG+P}TY85W3ytvdy z#EMMjYem7c+KUI&uJ-r`efAj0J(V$jb8@`&E|N?WZTSigf&G%|n#yBaTPeY7{2ZEx z@$!}Ta}508tT3Y>7tw;?>(hs#2A#}e2OXPcs>d9%AJcW$U#Te_%PvisJNx$yTvb6Q ziU*ojv5dt0kQF*zJwlQm@`W0Un;-bSUKT23f!5Rl(NU=swIOEzcab{XjJHwBw3J{J z1`!e*8c})e1Xo2-YC1hI3-jq8)pW!^Q{ZXKT~#pJ`}N7V~PFXeP!c~zQxER9Hzl1!%u97wsnS__kG()k|F@V zg&zjB*y5wfoE*f#V_^nPQdV_jLzgz#mY35HJ12Dsa{~}{m-s)Ut{baX=wsKX2QY4k zg0JA-$t*cX{1!r>zhu>B+eNlF11$LhDiI_i=JZCf z|3${eBKQ9rEY{J9OWZlB($rX3X~VG_Ib83){e}XxS6bGVVzXeSGY6#< zm()uSx??V#X=45S#Ymmt6q9O`vqn$=R)aCL*pZpo8q4OpG((|;X;v~+`^@>;;nW44 zC>sQd@DX`yDZc_-Yn=P~wI$V(I&FpTX+TxA`w?3rlJ;u;V0EoL?W7_akr_7dxx zYC!x~>rUVbIL?pq4=Rj8UjCr2&EKRRFWKeNRkN2>0(?`Yi~|}Z&trM-@>3Rf)cp{{31Twp78(w2R`BJ{z5{sAz*bx7 zEanZ3Ryez!Pc5Hq%0K@0rB>0MCuT5rMw18f;P`7(6aP>lXjVdM=3_T@WWqk+x8hp( zT>!+uZeYGf=i%(&zmeDa6G$O0ylR*+{$(#;);$Pt*KqBie&5(o1mUdgu0`S0ZwTf) z2Nh~#-Gl#qqq7*D&^TjwStj>47W=G$gl0FZQS3j0xB?~LBv*W6y2!6d^Ud2fOhV(k zwIs$r;9My0o#pb$iZfi~H^#ftO{Qm=sO9{`1FD{)bJQf9VH$5ww>VB)rkB!;u$)US z;1s@3K0Z>$ZE?|>{r1bryw~0k;%DrrC*Ex5IplOx9?zIt_X#R9-^W#tPK;lyx-kLP zs*k@!j8=;27YF!pX#BzAyUYI>i~nNj+XPkxd2?e6xBL7%LP_~rqC>d8}l< zm~S`gHv=z@JjdK&ujK{C9O_~uT;F^+?M5hc>s|BaUUTP5MnzD25#A?dZRB9(^ z0GSsf1JmyQ0{xP?$d(~B;7fP2=atx6NN{DZu9~n~A}7Pqg14IF&VBHN#_HEy<(-v{ zChhaIynR8vU3puVmlVzB^h7*P+Sl7ceT3hNo+FqBuCb?k`~Bj%=l#sxWrA-TTUH?>==rh2HT)Csc=WTNu1B~<;>bzr##pHasRUSRSof*>E`2LB}()h{yr3&g)tSLv* z6MRjH8RicT6|W~TZW>TfE9<$;3-z@}=D1q679;Th3^SVL7P?*i^!0q#Ro+05)(Bpm z`FVj+4{(fVtV#JDZcny&(<-|PxS@G2Q1I$DrONp4oe0=8-gT>v`!-%vCpPm%EY5nCXsXdZe>kqrlm1!UrU;rZGUP*g;GJR zh1;2dUYCwc`7|FW-?l`hRi<%o@4W52piU0DO=s#8 zDg)Pl%~u!Ja7p-MfI87yIDSSmS+>n?1P_L)SzD}TmdWHVjf$Ki%Le~cpex9;&i;wm zT=u+ju<>Um5=1gaX|BTy^{R}$Et%46T_x_Cj^cp}%wOja{?Cw2PP`hWMFrV;pdPv! zxSnX+qT+OyuD|+4jX-sVk-RtMnM03`eE#G{y*q<`t(m5|S%IShXI6ScON#X5ThS%M zuIaf_sH6efZ`O_jZ6_{nP6GRbhPPtHh>4btKjs=Or#F$N6P-zb?fXp-OfkTyrP)Y` zzZli%W;L|8s)a^CmZG`Zt1iN$B2mvG?z;9ZSV=T!UoBIvekQ8b|B><*A7*`Ya7n#M zu@Rd#BkF4wNAK|^w0$VzbYeioYjoUqM~N2>^N!L;@SQXQK-p69!mz~!$G_L zO5#h;H?{t5{^0bskK47BFoj2eI5%*98+^Linl3&mzwsv0!fw>dMnn$sZOEt6O<5tq z{D{znaYsiP*YCXe<%XoQOvj74U|-f}x2avO33vGHu+ey_LR~CkwC_P;^l5rx_sq58Aal{WJ>x*F4&V%~?kS2w6d|kNr1!3MuZF)n)t?@}vp(cx;G5@yu zH!>Zf9~6ns+yo_yGg1KqkT?I5so{$&w_%QVQZ6J;(=Y&qyjZlDN3Z7&6cY29g-NmD z<(e}NbC@L+`9^f6gXrRUwesH^>z!s^49H4^O(FjW*}A+YSN1;;->jC!putXuI(>G> zfAiLc9Y8J?zh=R~R*W#Kf{l>Ldd|^SeEMm)e>lXPNh-vawsmh#-EcZ^@fhp&G zTenZQOfxa&?|N~sF+qRs*^21jSZlm9Yy$jG89Vav^3k5B=^&~(KiyCJ(p8doz9Fr{ zn!iB4z&Dnb{WQ2WN6uP+;aMD}A!%%N+~e}0CuVAKqn>xsTm;o_FCs!rkC9{%ZwA*w zT;44`P@|QJhn=(B@b)v9Me6|X&I=7S>*!A7O*JImce)PmjlA%E=U+_Z+zB!2A3h#1 z&i!FpHFW1LqGg+&HSbi}_|?a@q`k_~qfO zUmEDHM6%TB>yL=Va-NX;521VRR%_tR8?sSxA$~=W=>0;xEY95#uo>N##F)3{2+^87 zks)$(EB%VUIG;F<6q$%xZ&Jkik@dE#Pjbc&H9;wvVCMSNa`1!Mb~ZTN~zpJV6SD;aliWk z`Zot)fuS*^=htF3q{7HUdZbIAhUq&wUdg)-oRm?2J5e_*sL7a2Q_*3rGe{)oR2iQ0 zvIayhOh_#(Zas12UWM5qdUsp48ZkzTl=k~gbY45l(8Ps-W0KYql*+{mNym{#9` zyKER?QRhfAvR|TJ4`QDQBc7Jqg$WMUWk!ce7KZ%~RhVYlRtCf0pE7)I^m=Z?M;DpV z9#{2VLVxe93v?D%vbsJc15SC+n9kSQs4Cl^LW~k*F!0oAr61@8`=MzP#(N!~Hby6F zmR%gbOl6qUWv;_pq!O*mU@)exz$dsNnR(waVWfjLfrlMz!3H=A+McJi2-f@HdR8rOSzvr-O&3}GLVn2 zLwJfBVk~e&dm7^Rt2aDBsJ|BJ^v&$o{wEj&{#!8LJwavg zR>IM$IkvW9%Qt>UvmR?b`vVwKg9;~3m|)sek(0NDa;R`!Yij8!YFUt{I)i^*ZFXzT0@i0WZ!ep5XhOfue;I4ySKP-)_>vZeIhHc??4$Y1<_iXG9Jf#o6oC1f3<9MBt^ zMlQ#}B*WD-mituiyk9{QdpOA|Gv8%0hB|uh^mn)ii%Vx;n zXo)#_PV*YuceCMH+JAC;(BIr1F)}jj{vDfzWqE5zbId1Ur?HVVv2robZ$g9W8rAw} zl4vyw@!V<_Ki9Axr3Hw9AoLsC&8YuGjOZ_va7UxpQ_x|+##PV|){fvJ2(CNyr7Cl+ zd8$d654ShCIS4o{C}x5rc?57<59%*Ynjl4fVZm_0#Lnq_qyjN8lxbzVx%OD%uMm-i z|A!C}aS=y*i^_s#56;g-&D^Hv2lMH@MV|Qewa?7LIJ{?|Xp!=PSuzqvN8)N#EvE+A zga6gqmB&N9{`;~dIZ8R@Af!afa!kesi7 zQi<7t(@i=P_(@tG>eMqiKUf-^I(fYj{#i3`VwQiYLffGCwH;Sdu{FBF*euD_#~t1h z^xh$R<>*x3>zuTJ{Em=ejq9?E1|4jD{DpzL%>bx=yIRHD9^W@amr*4~L)*tftqf@2Qpki_|yvT<=E~{a(Wfg4VnDoG;zlrvF=_NHFR_lJ~*lnZL2{ zxE*J?uO#){eowM)$>DVlaGl96Iag42^#OPzvS4MSU%HrM>6%Ydegexi-#0b0@dV6b z6z989Z1ndB_Y~Et65mQhmAOC2pH&`{S}!M_FR}b=eOEyCa{jV)$@{aXeipuuT9Oxc zCkyRPb&1N6+QJTTv;pi8pU70aSnPwfYc{@a+Mm~&2D~r;0bhIaeD{fz(?el*dY)Mp ztgh|JSHz`$P34tS8!eC=iJiko<-NJ1=a=G=`sf3{AmY z-mqkhOdsDZwTfdwJ&DXQ=fO>EX zx9@H;UzZP{#N`0m?vvJbM{$hb)Tr^6Q3xXZUDo$^Um76fyUgTPiFu;JYUjWkTI*ve z$5#Cbls$tD*e=<6Ddy!wu0p)MsW-Ri=V8oT6qnP)2)~5yo0vuw`MueT1GS&(d#?^g z{{lMTuu5|^!V9H`Bsy_{7q{wMPNJ3zia^^eR)9?dL@c0m_*M~RI# z#U6?K-gLp=Ay4L*qKW^|9|c_ne&Z+n1z6_4Y@FlADiB8XhNV*t^1q!OmOHShr($ca z`l0_9OiC#6uO7SkP1nLr^?%W0i(;SeMKOEq{`KoR-ocg;KU?XCZT|)M_PvIqu(L_n z4KiD4IGgK)>v&&g!EBP?qaQBS`x<2HmvTktC%Jk(46Xb>cZFk@>)7()AG_FoOenSK zN7h8L^b6>_t7;=}ZRwqvBoVz~#Yvfel0?2v5e#eWZdN|cS#tfC9FfJg?GJ6bsso33 zWj?Ik7tfBo`DbMg>Rc)Qy&tkIhUR%TVlg(+hE$y*id&!v8 zk;2DV$=GW%?SVwptVbg<&M19KUZo!YbctQpC34zX2JtVzZ}&6lZczaIb|C-Z{|)6S zBB~f4*?D8}vt7Ad&hS{X+~8u})rB?hHRA!n{~7#^qlHJgw7OU(hT94#XjoOr{$DsD zhps)hPe)d@fsfS?znLK>-r`k@EtBVLBBDrLHD8kk4NJeJb-rAwFGIH3pDNd>9U!kF zUk?{&zZ+jXC44FW+dHfNPW<{;dn@}dRlJpi+0uahaScEF#la^mnuI-#nGV8J=UdfB zlO&mBcF+iO{oNl6JM#-fGW+A!{Q&+p`z@E*_QEr)KOP9TuiELtfU>u-^z*cK!`ble zaAOWeKN|{94NdYhY|T4j!_H`5;e%N$vla@R$r64ej9>ef_a$x`{+Yf)Q}adR-@utt$TCAp869E)DfZx|&SHRvr1t> zu{<9-Mjeil8(T_qrtByVML3B9>Q`;MH3wGdWfAN|i5c#AI=xAxfj+PHvTvCDa>80o z>`H;G?#MP`H1@twS}I29Y%QD zmWMTYgiWtJ(Txuf;?J`<*iU=;YP*{Su{95>Lc|LJRZ;^njAf`TjBTc_y^C`qJc|S+$LyyssPS=HnWISd*E2q+`w?b;&vY%QWCGN-Pkm!9EkKR!28lJC@nTX^Q6yfyrKSf)p5To~Y z5hE7sM-l3WF`C~G_6fu&JEJ^T3+!gliu&aVLE4#3i`5S3EojN(u?|z|h~Vm^*RJ8CF65HO{w|Xh zoEhSUmG-9KeA5vT?wD^~uGAM&mefM|X_`;9yW<<#R%bU0+J}|C#8h(r7)_p%UEPw{ zb(MBXtA+2#;kMh_pN&%YPQ6I1iLN@iJ`-?%qkCm!C;^x7Nfs-fDSM@GL$ct!b3iw> zOm)JsPhVWs`dff+;icu?wTCAN8^L|6wt@S=+&^9*hr+I{768ce2{NMgtK(_2DPOwI~!Nf zD}pf!ang;PZ$yQ1pR>qMPHP)W)u10*#CXqW)0E1WQqYGnFj(hvZotzUXw}|+Ru1Q! zmKkid?k=+bb%%>fm3vydl#zq?EA>uTLgg6J_)EyJ4yPvK5fM#yv=gr&X^q{WY1O_e z#DAU@xwgt`FK~bX)T;-xpuC4Xb_A+joLp0SgCXh(9yX$0J1V!d8h77&yboT# zc;_qZ9iQZ&pD#mcmwr%~onHT_l7ymh^z$wc63BK}Wvh;k3T+=K6+gW+cqV7x&q5d| zGY8*c!22!j>$96hm8%}uarLRy4UQ@@-eWS)qklPzS2d{JJVlQdz45x`zU68k`iK(U z>q4`Nnb#uBNOK`?p>KGmBVFm&kaMNQSXRCkwHrj)0nwzMbUuP;N~3$s;DwE-8;HdV zgVw}pR@vfIA=HZ>i?}vSNlMyx?Hl8{N;g(4RQYxI3Xp7P((|t|k||Pa@K2{{p=R4O zk9v)un3rrL2>KuJr2yixgXsu@I%?dlcOWWw1cR=qd0Va$ue~AS*v4aYdZj<1E;5aW9@9^+Zb=hSlPIOYZhN52Ml(I? zN??1P1yM+ta z?U0eXgRtJc+}7#zezY{~`MQ40_5SClr>W1zh{nCVc9YTWSKejKO*R%L4LoWwpqCW2 z{DPo&wnQjoao5Yk=En**dSxfsQe>x2+`>x1o^4+gi3u@bbx=e0JU%HuzQ^L#I_}hZ z952t5F2}i$*^G?bu#IZmh9q)k-j32&fU?r0sv%^B)^awevg)$wtAp}>e+ZQfRXSR6 zA)^-@$1f!;yV9rP&=Y<-)B!;`3amoH{K`^Y2l`pn*9lzgGgy#SKBD}@F#0+z5G$?c zLVEOW&tByqf3=+7{)(=!phK-);J}+EVSV~T0IA`10oHq55-%TND-x1x$G^~UctPO# ztwdZwfyCOM@5CF6Uri`dZq*YKEv9BQNwON!>8p3%2EVUadBA5)^Px;<(@jeCy9a&9 zIPg8)&Z?+4hCW86`-v8Wll?)$Dl5$aRnlFiw3lg7017Xwo+ZT3#TTfx*b4NFbHy}t z)TaN6D|^ScY*3FPDkf27Nvd|*7!l0K0?gBBc83Hb@Md?d2E(Lc(vW%73@7nI^}0qr z`eawK3_YDbL~1lW-c?MM2ybcd2&N4i^cGmI`Ys9HO@%=&P1e;X3WN<_N0H+B8haLF46Qm<%izJ9SO0u+#wr>{9UaU)V^K4w*#q55-K1zK-mz|{^4 z)lFcu-XER#ewT0IOwOhjgf25#CmMTi!g2gL5(l>6=B+~8Q6;hF<89NsB}$ldl${Dd zpdkFpPl7w}=y){XhpR<*Fi3KMP;&^7%D36<|5;1`say@vXJ(a+8TAg3%5Q<2w%`5( zQgn8cR6cw`nxW`67H)9U4jxlzK~Ql7kjkTv{QoPXlTomkl2M<|2CZ!Z*w+-CC(!~5 zjY&lxmM$|0oPgIjaUer`!Iyx(%485N>W5}@%U?atWat={eLT|4qnbIEyc8wPtpsE<2xPaD8|#ZVwkn?|_V=u@~@zZYStMR@s@n z!40`*@hs4V*Yr5#pF@shCB}vW@C`3m?HOIk{gvsQ(2RNlQ=w3}5!mz4uz6w3^vtFL ztXmbhnN?6=)U*e3CRiT+3PzTa%p@D_JaiH0X?kpR%YzNTgOwC>xtI^m0A~d9F3v&Y z2ghf@64Tk34|2n~&tC|n8bkMwtB$uQLm+dR{7<`W0>gFDz~`H-u_I1T_* z*dM0tN_7mm!q`*UjM|smH2xQYz!>g8hpjgUe#p`Pa(o3O`8XKEE_M;AM|hk#=P6)5ssPC4HL8WWt# z*!6fUiC5u6gRULqEjysFc{qdP0Zd`FA1Is<&wvYn{00^Q1^lrZ2<6qpZ$pV&TA3jpA78z>#%V=ON|d57DN-989~wdY;1uf=oFe@k_e0jb*;eFO^I z-5@}O!>hMAvKhEGwbx6G`Mi&!V8~jY-x(A(5WVHVNIq)OP<}W{UQ zZeE{pcTLE&MO4oPYunUi4{eEtJ^_$^iS>S5R~~9ysK9D&;K% zv}AAr+sGU;W@lOpUIf(dzP+V@TJZp1Z;w3=#bPqB0$$wp=d8Aw2I1f)uDd2-OuMv7 zf#QA22Fh+YAE*dc@T^^sdF??|6>(ZXat#k(TpQ8>_T*7Hz_jNe99TF^VHz5RGKld7 zvA~0j&EuViJTt&AZ~=eFicdiSL+;avh6~naEe>7R%-;7z zk`3R?slsTG!5c4r2MN5V-9`j*KrL`d^n@}GWP#s-1#DEOx17Mb0?PAAA&#w@XUK=| zOy-z;%h=8Xvc-U;_;D{OrtFVl;1^b>?~tE7_ie`SqVj)d?{Uk}f{g!^cKgBXby3(K zQo#2(NKKRIVaU9@L5&b@s5XQQV7Qr@4PFN!eVze*w2~Qz5~rK(JcEurf3TEii^i@>^b};0*aNO;JaG@!Ct!ZQ39rVzympY`K9(#R2Hk z+^4*YdE4MlpwC|TG;_A+8Q+}$lLWQ5=pzdBVY%B62~Gth1T~-GV~{>aKw4>Ud&fBZ z4T=gc_=uUK|K~3!q1&`ULx3#adlYhx>Sj`hei7Yz4bb`2^DWH_2he$Ut1^V>4mtvz z+s;3RQgIv(blxry2_@b^X`pk@-F=YY+F#$6sGnYV;<)8xp|a)2+xkU33RWD9GmgM8 zQOI!rw&h|C z(*$o-=6JQG@ylRYbR;`$>5l$$Tc(U>ndOSsJzZpEjaq`G*>m?mKz=KGnC{Dtyj<|l zwZ(@s2ucIJ{m!dg7AQm&xPpl%5PpR(PTu@8_{1a5Wb~qTZoBK5UMLqU4$W!j`bk(g zo4r(DV}%V?xlcCP7hUfy-RQ>!<#J_bY^nF{;&Za-6Eb@AMv`-D@SmJ1FES=H-En??TIJJ#C~Giw(aDJH^2YxUhlpi8|R#^>guYy z>h2S%C@+BshX)4&0)i+dDXI(t0+tB^0-6B>_1y!D|6=vs@UavTQIrx9AyIU)|7~ew z1_DBzXkutcD@9K|Vq|1!I5J5`1Lx$f91#(tY}no3GuhKmGGI7Jl9!>YyMu$W(;tBD z(=pToQ^a&0G$DQe?m1uD#1u}^nTE^r6m^wPz0fMOv$SRl=N;6bM zE|QT>z{)6YB#lrr^ax`Feow%BzgX`^kMeS2K!+4m8+QpauTbG&auzlof)g~#II{F571{)5uChG|+SK1%-lmHB#V{BC{vr_XEYA!Gl4z*rx!IpwX@+L`%UN$Bz1&Q8~ zo*9roNEqfQ#e&d*a^@Jk@hIa4BRz%{6Fof}!6-6WLp}d|k->upXhJf^_@JQttf9he zkKxdw(RPu{--muh807r6fiSw~VqyNb_4f7EfCmo#Mh+F$jsErZ)phdq)g2x91ipVE zgaktFZn+a7;NKLI&&JhdTwKgaispBtt5_cU8F~ z2IR+b!F13AeO7!%ac~XPLtan|g5GID8>FC?PS6n!3W7BcB9P)rN(h!sk~;FQ_42Bj zAZJU?Oc279E$uj4tkN)ecJco>Q)|+&vFRkjl0xoMixaC`u~aW`Om$1%KNOc?GSAt7 zQR&b)+~M9ogp`q5YndP+$1__ImEdlmHyg}j8E*v*pZ*mNIVsEf(K{b%tgSO*6Ots*O)8E3_2{fxRG{XK@~{6~Rf1XXu#dbYt3DOc8iXXHB}R*O8^ITc=#%K1K1R*pF*>aI7#W##ooG6hSwy zHKF|5vMlJCKHM!oSh5H7jwnX?(s z8sYnGiTPZ+N#*IsmY?3UZtGSIK7{-o7wab711v6tO+75OS1ETXW(;C~DSRJP^CxVb zO8qPOOLs7a=r4}CGV|crxDhT_IrUBI2&&D zI)psaKUWqMUgtos+CKH$1)56Ibl?pnrWxO!R+tP=7}vN` zra-(qdSG416U|HGqSLdw0pHr}TIEaYDPjhDbIe%zos4SPf1(v(-G7%swL_ z*yWpUw@WMcVm-tvP3s>AUh#lw*GKU)Y0kAf`x5T;oJ)7-SDXD~ZHD!BMps6%rK7j~ z^1!oQd*v;^!jB{w-xQk`rgjg`6c%mOoSvZ{W7>}nx>_qp zM0^-VySu+();r=niPF;=dt5KI?3TFOR;^%kql#yf*hZ4Dn($<+S5Sx((|#P3*DY0R zUhCxI7p-Uy%{JE{aElLL=1tIn^SpKat*Gej?d1DYvK5t)f%6trFZyj74zPhmMWlb< z_~2n-HF^a}dJ6XEbT2k%Y0v+)`?iHd|8+V1u@iTHJti`#9#1Gdhl;Dyu9N3}s%;l| z2~}(fTZ~0rSGmR*`0q?a#$6#_Ahb{fS|(^!nZ(RDGJp^QxNAF-(97j{_J^%tPF;jJ!q+;WbEapJU{Ks>iW_3ktZc3B_Z5Q)B8HA(SUTO z6jf3dCkoz-qP%Q7$9c4&h3?oUan2SV)YFdLn~(8_XrATTCPZ@eLrlpeQ8ouv!48RJ zsWh72i=Fd_)f~w*(fi(>@xv)PbgcONAIy5iZ>cSC!QS!D39;dcc-$G=-b(j$&z0L5 z$aW^$c4KUGt{Re?_4@2Ox=*nzZ0fC&L*>D?{01S{iwf_s22Ed6(u9&5paVSxd!Gj! z1H)=0m{f<6gE$-^&d7MVLKEC4aUs%d!%B@geVB+;A0QC}I3BE2C1iy}gi=&qrxtQW z+z#C?0IkT-)^Sno;9##iX9W=Cp}Nu3-Ba?X*3zwk>Mcx+jD5D2Dp_dBX5XhaAR9$e zmLM!%@C8G!!zDD`JsR39r&)SSIwwq4-GL0O+V8W;E12@%#Qe@lOR7&{>ndU}M-4!6P8 z26Gh5&GzIAZNI^P5CM;#ZaIVX{B7pQpb~K*9q|Buor~j^cF5|jg1Kjz1sju{^_U;A za@ObEjQpPs(8;hz0r!J2=Nq{Cy}}rqr(^=1lb*Umtn}-;#>kZD6Mlsfs6_LqTf%_@ zZ<55m9@+P;F=m?0Z39HIlo=9a@no#+=wPFi#7qXayS7vN^=cGfkmK$9cYd!AmgJO_ z?B-eY>EAAMBSOe{PZ0wv&#ic`bZod9zjA`XEDR9`6!& zy;YQ|^f=2^xgmK^e1l+P2HU#a+>i z(xmHD@0smWs2w1nW~+B_=XY7bRIyp+IJbJ^-fz+%(|^b(Iz=RFK$;v>Kofi|P4(a+ z+G#-`z%DzokYKYokeziwf}Gd@JulcvHX}5qN!+EQM_$^Sa!gDVb+y=5{$sWBWF0ZI zx!WTmhd(26Hy5EDfk!Jx0mq&RA{n52`GY0LPl6oT(Yd%LSB`?>7!?;ca0(1rd0sT@ zr*+!ooQ_=^WHOaPvlb;A#dr8vhqYWlT|G`~qy|1DiU1 zM$a(U@e@(UETm+#cj*$$>1`v`>aR*fdzw0uvgrX3vEI#6R871zS8!+T?a@Wru1CBh z{#=l^KccYgFCG6*tx&n0x$po*^fTG z2pbhS*t3#*q}H8F8R27MBq^%+QG0aN@K%r@n;g-gb1_K3X&2MKsPFrz2Cqe#$Ln^1 z;%S>@VFiCXS=xWXdCTKsc_-U)lDER0?hmt=$UjTgHyRbIHcAi~00|b^nAOjz)4Wce zDa%~jP75j^a0NKYlBK4j>#utUn-$05*KJ{M+XoD37-f=!G5qQwq$)y(@&Is8x9o_z z%N2h?xCw`_zmU2=0KDR?T{Y%=6rd|8=y+d%SxK z?H^k63F5rK^!beBb_qll5Y&u^{siEiO8JJAYQv%XTyIbBPu4o^vKf-*WwBu=htU*@ zFN(e0tnFz5Pjm=}k^sX*vLY=)^RDyL7G=HVzX1!@3IAE#=&+1PGzmNdl$tD@n(+wa zL%!Jptgzf_f3i;}I4q&OEKIBy!a7va29oG|`>iYcZT!N1CLf3gpimsqlaX6H=hByF&?<4%}W-D8#*H4a!_Uoztu=OQUkYunxRpDMm z{r%1SA9Br=Du*3K5cf$Y=gn-T2FmBBG2hK9=#SB>?rPjxqYZTX{l{*>kgs*~PE*JV6@#9!aKRwPJo=cgKRhFzwYyEEQ}> z79fbX-iVQ_m>DrH04^%I;sr5p0ZU@l6%`1$YFQ6z{EQ5PxXWG4W z8303w9D{u0+!}__S&yRG>hNQ!)?mAQKHv8VH_!j`Mf_z6mv8?4U=)36wXehT>7q^i z{xM#yQvZz+=IzI0-Xu+!j#bPiyJlm<0+u8E|@+iQmGxUhvv%Yis zCpxP84KbH^I?8pHiN|wQUaM=&s;D{f;peHyCe8a5I55r!Z{9yKAVXinmb7jp5^QXtB048VUjq}Bm0DI^q-Bh zl*Bm3PgORUl$92%1(OvHppI6US+-24tP;byiE|8Pom?2vo;j`J2?2!i zI9H)duurI(*p_sX_8X{HFdBvx0bTUBx~TRzs|Xj(HGAVqo20iyHvatc!qu}!?|r^0 z3X+FbkBVwiXY=u+*kLtLcEi@4$wnO5Tl(z*YRl*JBR7NacJ}`(q3;zg`;mN-pdwG)&2QS!w{?C^wAL}C? z5Vd-L-9Mq5hgH|U-T%y2$-omqK>;t?aXEJQ4=)kfC`x62WFO`&o?V+8x!fIYBy0;G zt<$u65_DteXa%!;6N5fy43s|V`ha?D{aC3r1XOMZF=XDEL*Dj=N_7g8Trf(R;rX3%v8xI_jA8z(Mx+Ap- zJ<%W9WSYJp`ul;z8HlT-{=A|YsdXsiK6-D~{$rG2q^J+tU=#1hf- zx<&P6vYF3TX^j8U&T1$W2}j~`+{3W4w%)DPKgg~kAEy9wLt)+ky-e{U>A=Ky`AakW z2ZKB((G`T3v&tGUD9sLpGFoW#zG>0W=;O@!HJ|s!^y~dxmVCBAwDwti!2!_?J55>s z??|-(0N#KAZjfOaoeDh?da+vh)V=k1)ZcikDu1U`coXG7EkL4HhaMONcp@0I_$12j za>Rc>$J<#~j1vi;PS8lUTMJe9BG|;tk-1*8rtmC$MBnuKrO{g_@4`L+x+_>}%V_c) zq#192om07jD|rVRS0X?tqR3HZ|4qC5hG&)iRP1H)^o;`l+65~$^9unpb~vU zXQvyE>v|=f@gnYH+YW5XBTuT}vo^^<_`Qr9z$uT4s!(L)99!V=d5|#9h~~HRjF;IX ziE$e!OXtb&&u0|V<|dgG;bSgF!r0cSJn~UYqzh!OQGEmY3++1LH#x`1mtpYXDP3Qc zbs~#)tyFeaLT(o@#7zH~XN$W@xkwfaAIo5RuNRY^kT=E8HNhKSBZ`NgWj9-T@q0~u zUc000_7e7PqgAy^;?BnHE&)ST1lxdTcNWWXb)E33kJ{4RTLp)L6H>6z3QwDPc#o4^ zOssK=Q&ovvPjDm~@9gP1M`N{{y(=Iv>Ng4^*V! ztBGq)g5kxEXB`rIW|9S7Z#a$}amTstmh!2))lAF$8Ht~?_mGNN2vp0!O*xF%6F@5qAyA2UZR2?2~=l&Z_&D3h0Sz-SMz3TIKHiZ zSA0a%A0-iE$Hzcl2L&sVK^=j>&s}LfB87)XHDf4WDC+IP{;Qs7wvOF81B3Y631U@$M z;3%#O>7FAV(YG68uKP2#Ce7d@Vqy71JH3iRDz|^#dPLH295#wTDNV&N7qil6f!urB z;Ar=@7Y~oGOhr)!ei9FBXD%0r=0CoJM{Kxyw(6m`GXBP@cLkH-&D>UdHp7ja$(t31 zNGRJtCMf<)#7`YF=%N-A6*W{>42_D8KEHR9R%&BZv>v!L!NBdG*O@gd7uhV0yG^R= zBGee=TaStk=JQwkXOmNK+N9Q%XXkeHMh@G@ZJbnJnypk3Ty2o&esZ>Bv9NHk0{y4F zk(sn~?@^0|%zHsF%h{H{;Xc#2a4$DTdpSMgicrcOvbg6_&gKnoKR~}$_{;#%8{5q! z(xpJ%7etNM@F~D2UzY{yMNIyOKUS_gBU+lYqqo;clooU5y<;<^dYzyf3O@$Z;9}qp zZ|8YB$&>OOUdCi~u;rG8%DOD+w--Ia(y^Bg_ry(>)CX@UIXBEYKe*g0A{`hSO47*; zHe=G8dmtbUD;L~$0qGiY@6q_mkt6ESWbx5&_Qo}~f5*!I1me}4qeiz1m>v&mhq&O& zgYT)_?0IwUW*Q(4W?pcz+$|Fu`u1|fC{4{{xks#nG~F&lk@NY#F(3E=5sv21;Mi&9 z|280(&JB@GcST#LZ3dU05#LVCVCedoFzI*mV=y7-Lea0q1f4&VxgBWA0_tEY_%}>7 zqrE9HJ>Wp|wCt=SHnfUHyGL7F^{I=tEtYFGV~$>xVoZe$nWtD@oOeoKhoTs524)}% zHahzsoWiKHtUXSz?J6PTbWgRGP`R-U6VYETJkuzqrjf9VkX31py>5-|r$LWKCjco$F;kbd5 z^16P##@-A(E{!b#*$njfH2jNt7%^aAEae4Sif}34Kj(Y9unDqbHHW=~ll{>C;XO;ez54)KFP}7eNL~j&(o3d)|2)Qc+g>1q=tDPp6){xl z^jE9WPQzN!JHQ8JrO6tbQZ5VP9!GZTs56(>oo8`&w=qY_5%p!f+Bk5fK~q)pAcavQ z0tP%t|+jnd|GjfJB>At3PQ^R9Ys*KFJN9G#3w)&AxfVpbNLZPOVlKy;F4eb~*o zw0~2XSiP&=<+Q~A>q8Amqw^gyTVTO_I#1YE4^cUZstp2?NzIf$dqX>sjP=OXWlb6{ zYl%d(pL~k-)PhZgGXd zJswqgJT;={^)J)zaV_eW=vDV?m4Md+bGsrv>oml(=^o?`d|bZh*C#mtYRs@p6(>ym zPZCUgRK;mIlQ1ey?IxD_>nWe{YKIIN?Y}}0-bY7EgywbxAbc4II8~GLMkE>e_xF|AYH*By;c{-e|REzJ5B$0oI&K9V@>EThQ0FDG;*+D(&wKvRBM zCz-bYVwPjj)z^8KH_oHR!(TtYJIlC;72RPK3-GV%0Bpw6MtvO4p!~e8qCD?-65Z-0 zW9KX{oZY*u)RpP>jnH(v-jYF^NCWfCTz)QBDNWUqrQRupPE{-b4+=< zw*IF$N}biS?q`?f50gm>{i6vi#pWF7BmQPJJDKwTCE;su%CR&=Z4oDvN)@{OeD%O}mXf|pfsC+VAFq$SSNz^eBHh~oAZDSE zkL`a7eLr=jMz>ry^*>WXCe0Q#9?84^@|cyeUH+~8))sF8XQJ(*@FAg?+{ifT4!b7{ zG%}uKgP56aDjzX9*d;9J=Ohvl29sZZyb}i^d=Pb$6_R~&)j7qS#(Gof`e&PJ$tn30S0&ZWeNs82e z6>fVN1YFjLEh;}y9>3U1S-WqtrFO)Q^XpFeS-)9Cb@F^VR%gtW?1+t#QCvt48 z@@W~=vQD%I6T)We!S?ss;#E$g-p_g=u^y0<)Id5^{?}_v!c7tXo1J)Ch&!GCmyG(4 zY}#Q33l5JdKoNNvB2EWQ!Thhx82Nu$C`kw#5<{H2G<1I(L$ZedA+Y|xA?>gL@=S() zg{8FrOK$z|F^iA@qDu!i&tm-%g#YJF|1F&(D1fJX{mS^lc0k@TEE~nb!lI|nWLQyE zHDql~o6|DY>c3gdK>^*}g2u+iGac^s9^T`^mCa7Ne+bb6>2N`%SZfoCry&|jOQDtx z{Q1GwcdD*x68cFxXi#r(z!n!5S32BlJu)uVH2}F2CPFfU|49Q5LoI_dI;;m>&`uz_ z63p!bbdX|Z3J<1vBSHNa2qMULXP@JL&iX$TTwrs+w^+8j)Qv%ie4>o}8Py<-Hor(6 zU{QCrTM@$BLZF2>7``0}4(d*;ti9iUyzKu^U;pW-g~V_tjg@kq!J^$@wB7Yw^yYJt zbNj;YP`UV9;WJ05J2*1()huAMGeR=49{1x{4_wa85FJk!!?VQ_O4>u$KD8o-LLhs4 zUd9_uWT@-K_zwrU76tKD=rQbgh#o~r>FFj+EBj9?B2|l4kd#xOx68}D&wbohwSn6% z&@-Zvw}bv%!4KX_`oUZ9`UR}S{_HMM!qr>h6l-F}F{*r3jQXOhp-vWH>?bQf`uhAF zHwXqci;9e>4$3}Sfya>Lf{i6gx7NwqgHItk_=nEoJ}Jnh@kl}~B+L4jmDs~EuP2w_ z*JJZHRX{X>rn1wU7a{Bg==OivGdslMIMkENoF_Z`n@&;Ab%j`T(cQgIViN641mRUGB`*w{atRo0tOldVqA zE5+5nNO8P-xbsR%N;i+LGMso#o{4_ttE;PAe(zjx@bF6CNd9WGqgW!INK#t*m!xDw z`F;1-2l5qpFtP7L;;ru;PcD*5sjQ`GHWe!^E$z)HRenS^yaLAF0Gry|!)b71BYQ|_ zsArK@h06Eo_!rpoefq?%5IHvtix+&gXt^sYs{7qV8Qm{Gi3SHlsrlWBN=l9u6_!_b zc2EwEi3eLc0R0)W8vD%)IuSjkbmk+oj4)eu}i{eA}Chz8xc@ZYzlY8QFH(LU<@DnRtOlt$q=*;MaRY#-G7wjGakA9Bw#^_CCQ@r1oTg3D58fF5)?S!MybtyTC3~VwQo@+_>KW{fD2QTEK4cy)rAA ze`U19OqjMkCVG^+yBXPdqg>g&$X>F{Rp)27u6{He#7zCuW@Ev>;~fwwhs5_@TO}>t zeInkCfdY%*>G2mKI-rB9ZN-xCPY?deOekPvzXg%GO`WIGK!hIVVN78Qq4(sjvUNqCq`u3-K3ip}^ zP2#;%H}k_8o~622bf@0BbVfZmK|#UYiYCg8N`axkYX}I>NXGw4Pxz%(6=#~<4vIv-Rx>2HDQ$28aK{3NgF;!@YRaHHwSx$|6nY0@WtpdWCH&yWhWVnCO=Lf!Qc8 zm~{HwFH%pn{a9k@8O{ytZ#Xnu3nkW(Yr)#9TW=a^VRyCBCMhZDP>H~Lg*h&mP9Dt@n)^La3zGxI0ku}BKj%THC* z2DFsVRye}2P20TC?!SUTf)vknBP#B3ejiK^T}B+2NX2f?nQLm02-`2&+e7QCFIa7# zj#9u&Uvvbw1(UCK&YRmj{9+_CU)$jEOBzrWSOL%i3!fw=RC{TFfB z!y!ph-z@l%c%i1ctA1z88UF_7hrlpCtE89rETWEv43aD!-16c+siy@K5&+5%ol=;1 zH85<*7gbR|Y^|$9>E+6Q$3X00ULqKZG|P)rc8c;zTwF|IU)GW3Jwox`&(gA@RpS;0 z^*Q`n=Yo9Kx=YYA+s9x8!|tMYiQG@#?yp@+{EUEXEy!R zb`(VIKfzQx!N-nP(m=%K00LXDy>@GBhj)tC>%3=Tr(6o9R*PZ{dN{c-2#q_6U)LFc zr#2W+8vH`2h_@CnnL@6o1f$u3VLh6nL%PwsWz9w+JCj`6&)~FY$}=nlD#@?5n4e`H z$s!Oc)g5&{g8DSCa}@EVXl{F<-M@+ZqrFF}6t>(9`Vt8Pd~jB8Cx+AG_Y;)`H@X2J z(9`O>6|h~Y37A+G$&u3nch#*{_<|K5-~5a^qogSFcW!(+hdi+O^s;vVTQr*2i7WsV z%5Yt`ZP{%0Gtj%?ZX!+(3a6d8^qgzQB?R69XSo=i9J z!|iapM8huaxN0A-7EP97J3sz$`wbed#Wy$H$k^|keaL|=_Ok2#XxxU z2_t{ut0lo70uKnRe(15dG{~eC2i7qyCquY@v%Jx-^k2cl*YytOKV*wPGE>R#SJT^YoH|$+8U&rcm%V?L z6^s8Wwlh$p=0RsoCa|%H{Tv&`@zeq|M6!MEPpDa{Q7(?yu%LqRd0f;GuU-B|>6c1L zo=>{4fHwhXXup-{s5|%*4AjV-xLq#JD-6n5!SM4SnScJpSkd{&!pGFFawv{-R#kPj z>qxqSyF3pR#``wL(1C1qrLq$%nLE;R=cn?MoYt=vrvSBd4#Hjr)(F18sK*xe=(OZFl*`=^GUa!Ga22RgWI$~rA$6b zBKdW=P^yNUl4LOCcv+`8;`});$5)ir8nQz5(NPdbB<)Z?Rp~DSmB!n!D_Pun%?A}@ zW3sXV8Q8Tc?dltU>>BWgv4z6I^3wiD**XfI2cZ|zyfASBhh&e#Ix&@q0^qNJH^oO=>SBs39~0oz;`|)$E+5tRy4Z(UB(ss(M(>a5%0=8 zvIL>BI8MHx2a$Z~cV68B(Hm1*X7Gej36Z?MUuO7(;;f72&AsjWtmlYU+1&W>Hm@Lv z`Re!Fv5gB`%8={hXp9Ts@_Sk}Zx$8Rcm4cLngO4n(}g_!2X&m!9*v;`B`OmBXy>~- zhjib52%J19H(XKLpm1ThMz2`Zp!j6{`GE30vokLmdkek<3ikBwhS3b^ZJaa6P#I~j zjJ^kMuHm6tjy=LIv#|}oE~P%Ln>QSWGF|M4@2rMsAm{F8El}Jzt&!@Mm^5QNvh&I- z*x#lU-uSVuT2n&Qe={o=MTzy1hecH2rm`x+upWtcK+hSGOIl+XArt06LC0;@o#2W* zXtZ%!kO$SBGC95VLFe5iuNDv6awHB=v=7<)hXFzyc~FQH&~!YFzs4P?C%D+>KT`|Q zYnn+^Y}e0~G`ihnkhm+mKx%(2WjMp^@|Qob1sf1CNtNP3q~zs=udfT_xI@&!q+8s#NY|(De97WP^5BRo`r5M4G;Q6f)^wE4ow zAZw0z`3Zq4UIrwT_huhdPNvx8L{B5QtXm*z{;)b{S5@! zlLg>zjE+<@CqJT0Azz*<_b@q?j>FLg1v%Myr_IwCxzyD-6A1Kh$bGx&m~Pj+2#@nX zSbQih+ZOQq%aX{>Nbq&Q3)Ggx{j_kd<4(@Y!irZ8o1YwafTGLGK$O)DgO)%oR!+Vm zjk0{ZGRS~-2{#sntDDFScL?)4FZ^x(xXZ62#e=y#kk$5!EPUV3OVK-dpL{a;h}K6* zVme;6dv6%;9u49t`#UnQv2+@Zp|~1CKad}_Uf;V$#CO7W1zFwrE_vNazZT2j5cg}t z9T(L4^;_Jmr|=9u73TV2q|Gqv4l5$y5J(-*%bOlP46F#Ca=BZuHK(Ip%bry?s?@5D zT+HZGQmD>#_>(tZ(TdaPJ%hclm-C1ELdgC?8^_$GN3?gFp~V)c(KJwzzAZQZ;icve zcHPy4G?@6|U~)!s0_Qg8a5}7sIfwh4Y~JXB>?yrWC@%R7L5>_b&|AGa;&n~|{Ctv% zIgw=Ju439d0NCU_#EbVuIG(Kc`Zmz5F4584K0XkC0mETFcKw_?Nd--jaF{_%Ec&3f z6*F|5Jdn_30KXmfln98l44JfK=6gjXEV!Qenl7VJvf4V{O8sVB@k6%XVh6VkS|Qhv z8)2y8Cm&9DHqaz@e;p{rP=S?@T~8_q61r4)rdn`&v8f#DS;u$~$emS`W+;Ebb_e)- zn5+7~bNqW{c-33@rQrzbD&btXSr3c2prE(Q!agIH$4%LCZgnPon;KfC5bYu3hIMnR zLd9@vN(}J7W9BBlx5F4-O~MdQHa?>c3Eb!5W~s61!1iPklduqUoDtII-XVZH1yz?x zjuG9v!z!)UPeX<;3qofBmUaEAqRW%9c5XLt#;oM`v&cpoOVlr%F@_p-6R{mo2eca%4&PMEMrj6%3$NZS3!ioJ>EW;!-qp>XsSTr4J zp13Z}H@~je(8b2e8bhRXRmKohvDRZ&b{aGo*@dh7{P}jR5v}+6JNG>e<=j+2*G_*( zgoiJC%kEAMrgYQEM3_cDnBO#HM4VPXq{e%-Sc5_B<-2WqdRb&D&m zdTHl6-yx3A)pr6&ZUxkh1?Og*KpQli?aKV*lr}C0C^4Fckoql9HzdVL=JLP^c6GsD zGkuL_d_P4cE|xvwFRA&jE{4;Gv_t!Wit$C>WxI)>gLS))vDlQrE`LVkVlj`Gth2LA zjy6`ghdX5%Os~RzaTqy4m)!1hGM9-pS&1_>zg~gfw74^dWjXo>n++ z{NH|bKg22naMO3p5%*^$@s`gMqN)EgoA^JfG#GZ1*PZ0&!Ya9r1&%{^XBcLw_kH58 z2yA^1P%8NxA!TK>tIm7*Xe)nUKQV;S4k|_-!a$YIf4Av|78&S6^j2ud+`VuI5xm}B z=8~9yQn#O`yLdqF936i~-lQw^bQiMq`QRnE{>4yYKf2iL4S%N@&^s|e^fb_w@)6m$ z{+VncFB=6c9x`we7>6kmeK~8Uv;C>)i&q;~L*_}-Pc||xVR~^+AJy^)cl4p>4_V)$ zkPPN2os3dhH3p~G8Qxc!;92!tYFZ%R<8>EAueME4DYw|+J6|Y_2p?SpJUjN&3GpVD zu`8knR7NjCb)<61?U4srgoT9}_BEL} z)}T$**LC-B3Tlx2b7l`3BUAJ&1>GH`DX+TiU%Tu*kwahT41SxRu<=(-IoN~W^jJuJ z-KW=^kZ_k)jd9ttH>w_e=@Gq8@egA1 zCPb`==&YD3eUzB$ixwXOJ`Vt-2@^*km)&|3%26D*6t0$~2CT7`-&8!HoqyJ zH|(Bxwj|2BE4=R}M^7`ANl`*u^JWwHYP+h}QXQ!2yc8Xs4zP%SoRAJQEapn-4le@9 z&)tADYngz%(8R7`1{rN19!^-cNCw8Y--tEWR@K)CTOJm9H^y7mSA&1Ve-aj1mS>}F zq7X`r?T>QnnwI5hrDDBpO&#xSi!!64h2vAjO6YnZ4m$V+>b-3ZR}!%~F2l?ZF80Z{ zXHauE+CkQTHW5&bzGWt)1fhQL?8#oq`_w&BbvF92<>hdL1{Urbm+Qf*HqDsUIkO*2 zL))d`Fo^PB?3d7{yUwJ-}q}APVfTf#f%|=Li4d6eJD84GB>67hQ%R+n@`IU7|8$iwc}XLGQojo)WX%#XJEdG2mIr?_u3ui$_vv7} zgh~cXiLzT;MMHWnqNF9s5pRxO(FyB;x@5`ccIB5y%w`T0r1_M8WiN z0altwa?2TpMFasM3}&7g=xm_=5gp*zv||rt>8Y<=JB{zt_s-z9ByK6s;C+5Q$o|n z>y}=I`c7|$iQWGO30p1w)si3Oq?qWp>qIIqjx=kFJC_P)m{t&@9TZ?3;R_lb!Y^is zrIl$hHQYju?aKJo!>Q>(5-mTQYg|5R=Bb1*Nu?u;V*C=P2OMsX@7#SDGy#&P?#xiG zNwQt=JgCLrQ~8xrD0mf-#VX`at&@cfsSvj#M?$f+Z!IIPg?pK#7TUzijzI-M8&ghvhwnlcj^+mTgy&k$fgN=AbL~$MW^x%)v zO$$>Mfj3Q_#$q8o=kqIQoxI}AT2mUhc<=OW->VUv5}%sN1?~=mue4Yf0}MJ86)h{BSwOi#4(E1#g7S;bOf2#Uw%%!=l75+?2zPPXfDfjV)8Z-j4;3esi`F`h1xJ{kp5AaLwUe zG4PZh^IfOZ!M$m_%RIaQ3M`gKf2Ifx70hLlqqcj1pwv7D()!ZXBr`sFF0pZ>UjivPZIO$KI&(!&ryxwE2$m2%+36a=NtEm>OO$w z&UwIYItV;wpqwHY*NlaK+=6*tG*&c(d<~lo9Uom-6a*W#=30`H+W4;C)25&}>ahZa zZ&7gG@49zo&WAYCT^zfVexA=#4*vHm#?%7n$*AX@e?O-B-(tT?_=at6a=h=Nz}`S8 z9^a?j51$curlST&e$EmOw%c$i9D@@eY>X6^0S5Qdfii`4(CXaaYB++D@kf0_L-yrd z7$SZMcG7mttp5B+B3YtLH1xgk{Uk-2(W>>x`JvUTz&@%jCsT473T=?ph43ilCr`0XDbNH)$N}6igW+DlDR$S>=E;WU%qXOWhmf|mV1jf^m zKM2>|1DZ)$3cRT&Q~sj018r%KMu#wQ|<#YyZh%ArUyuzj6Yop0>JqCd*UrV z`D^9Q=tIfQ)4Jz)Mnp%W7fSj}vGg9`uI5aWXXoPU4$mHZf8z2r3TP+6z&si` zxI`n6OttTU&T@=w-vBVJzsa(1QF!-)cVSdQGj2sC@;ulu{%!JFU2ELq1Oy`~g^ zmtnlq`l%D4JZYp;5^P)^?`5Mi#Y5o`d2}B2sD~3GR8j$FX&-aZv!?PgRERkID-?_2 zM8nMp@VEDWyPuJQ?3Uw!&G?axm_?`msg+PhP?`wos8s@~xg|YA^Uew59Xy}Uv!1_> zBYzs(B`(Izl-XYEi0KntDW;&{(}*?waFX&I1#!x*|7qz7MKEK zuUnEh)8+aT*s^sQ?fdz2a~MW9-n7wYN!WHa?d0o0@c{5h#b@kvUdYK~t2+>?!;wo=h&duZakHr6DD3)SX+UNZ>yb;Hn1gt!`(!nt`iiB$xI@qD{aKVkwb21 zHP~4D{o{Yp_ErIPG)vTIAV6^Upc{90ciXs2aCdi?;4Z-l4#7RRySuv++&w=z=S%XP zd;f>~a$jerd%CNqt7`SCswrt$N6xeFaq2#(78Y@1R*C|<&ekK2HDyot8VzQe*VWW+ zn|OHaek#$~?~BDU0~x8=kv4?xU*28C>r zxI4lN2zftDzQY#$x!M14?}MjuE0hnU7q|d!SFaO*nmWBj?^RslAxMg~D&8L|3ERdL zFP-~$Er9Vg_E-8&2Y0bj28WRb)8Db ze@oV)WNL3klFD1k2$i^p+wRv>dGxx!I>YOK7Q^qH4ehqQ^@IX7oqX=cYsreTLa>87 zo00dzaVZ23E0@II-CX^YYT*B#zCQkQ5*2jLz1XcjWq0Lq&o-uLtPt2>^a3z(3c(+4?ReAt5T(k>*o=2w#F&vqv4LBq^yB&kOVqR*?TQdqyX`tFgO zfD}*ik>A!mmiBdeaHrd67P_1g+P<-h4_t1SdF+tf-RaAF^BHy-x2f!SAw1B}qHvkI z=MzE*P|^Mm$M)VOlRqljX|>q~rKmaZgoI^dais@Pan}tN+%OsnTP!Fyz?b7Iawd=()hp##92)J@4P+G0j}S=;`2m zz5hUTkv4*F%k6||ti>jWL>kxJri%q9GF_RNXdUowmA|omQPI|vZ{;TW5PECf02B{c z)^Rk#bAL}mYAbn^856-}eUN=D9PyQfdHDS3N%P|MBVQ=@&(rPmiIY%>L(`RqWv&bF zzNdxMMrcjA#t@u9t}_{s)k~2h&Q0ySq_!ZShKgw;NS8O;nwW*%rE~*!V{`Z>A7GFi z0UJKcexALh(VU94?PvW2gXs$SbIVN(0qiSJ-?r*@*u{kkpgg!n#LaMpeZ{SHs{Q-N z0-`#1w7M@r4X`J@k&?YLRo}>_qs1$WNPm7u0%>crO@{YbDCuAb^^APD9H#3P$+}YO z!(F3usPVWtiki$V7r5Okn}MfOmx(2Z>Xx%_lLKN;PLy)sr^4YP&7{nQ7Tn;Pt*`3@ z^aK*t-n-~HD8t)abyK34YiE6V>gjHH?r8IRau%THAXO&GejWwWyjkE_frR0e>S0V7 z`!S#SzK=&{J2QB|>5Z&!Qyjv@*n7P_#WnGi3(2(_b8ykOAKQltW^g=449$Ya>(VS) z?;o9)MIDQWN7`hUMo8j;t65Z^n>FMCmLE|d96`Il#+N*TdbPE!?;^9I@DW{^3a zsp9duUZ;CSNFM96NGIzpxjD&k2%0jRUQU2qLyPV|VeoICT{RWzUkn0g!7fHE^j`P( zsaNoL`vCX|yzjN7we_QvUHqVdxX1y=iVvvU>fupAX|cti@gO9Ga4@0wzv<-^(sDP_z1NFt!LO;rsSC zDP>wn@xOpibJ=8gmF1N=ucY)>WS34%#t4&_m6^3!$Bv2~jLH>`Pk8A0!LK%HP&*hZ zu56BIBxF<|3gN!)m_g;IL`5oCfsg6z4Cf$liqbP!rhlW@5r(y%jsXf6QQS^xRX0Vf zDG8c;`U6Y{rY2-W@U)-zpD;bwt|AQKriL(UJDfc-H*M{YLY*!YfGsoN`Wf2j z56SuCW@FcLP5Xg$?dOKqq)z65nh;ZqTPUkGQ!>h3Rt1}9hLQgue8jSk7gs@{EJi)n zNc9sM@5*_|Er}cbk9DFPH@;8g1@D8k-=7w492Xw%D9%L2JXvFb568o=u}NXvCZ(Kk zv^NHbGZ!+xA~QelhKgUXT0iLvwwT}S^eCJhxeCAE@lh8(@VYpTm;Da3zv-Uo z*I9;mSztk<%WJo4j%8s$@{yfFfy>scPD11ld zBeYxqSZg|eMjnPO;fZQ(d92I8oS?uwTW5&Ct2PZuAp=v-YdS$t`l(sPmD;U<~oG4?2 zl3topX(|uyREOG=7D}U*XzVnkA+pQXCuyTKrlJR~O9)MK6HEL&cGRuI}hfGOu9 zg$)XW9q;15WNyjo4-$z8GjCz=k1M!Ok%Dd(%zWW)r)k~i)Q2|cQ-cS9t`{p3Yx|fa zstFJTwoJ4)8C7>Av}TsODz}%fbrC7t~dEUbtX$6tRWHZ zWP9yTV0rpPwZL?F^H7@R$l9z+f<4VmzjsGikts2PJqAiChdRt_q-{U_JaA%z0Kcg{ zH>LDeq{5}+6{=9gmL{I2T<`p(jd4|0OSpA!i+01NThgS#>Z3(ncDixG>OW}J=&=T~ySDR~txJ3fI?>e?LAB#UM<)xr|3zw{w21~y+RA2+! z{F=tWA#a_&9%DElz`84#S1xgY_p{S26Y(s4?79ygFn9TO5r6z~`tw*4+VV*|GK zfc`yxYd=cs!j~yQlDaHu^L}CR1bNQkvl%2tvW4~gOw*Bj z9XkjOqCv1uDR!z|0C~RS*XnTx{TAlzEblA@uoWTd0E_q8e%5TCia(_+se^B4Bud`f zSQuve!eGfZWhNwQ{0R&4VC#{6MqX?eM`2DTXs0L3*?l9OB`x6k;e@aS2l~Ur@UQTH zEp5okx1trK6~5VhYTDX739YejLCZ@pphL)_-u7Fv9vr$fb@4WwEuIiQhQGqi|HL|P zNPxL|x8u;>5qBvx{3E0P-1ehF{S}Qo@t&^vS3K~aCLQsR;&F!(j``OA6a1`74jN~u zo&7=E`F}&O#Sk}qfs*%S$1${oR) zoX+5M@cB5JpC$Q_41fMlb5~FhtoTDHrF@zH9SZxep=^@*9{*nS{|;W3;O$c&lS}0M zpH8$k-{#{Ml=ZIq>3<@fx&7Yaq%--Ij}rdQogZKu^!8=!p?QN2oc_vt_|xJX?CqWI zpvwOVvHc^h4z#GZPE_xq2%3K>iN7*L`6a{u-v-XlMVewXKt+B9Mk;+oJ-i%xoWVg( zKFvxopBg0M#xXE9B>E$F)03{w2z+30P-u99iU?PvuW$IWIyNyQAm{d-@1%Jn;qL+a z4Woe22{m{wyWdss5d(}Cd8L@h@&R?^C@D_`L=BEe&N3(d`Jq3m9ON09lj0`!$^Vflfoo3jPe(vgMarBeWfu+P2prj9 z%>i+v8o?&xq#MPfqfLc30Q&l1v?7aGYN^VBA&@o3EL9=1$33;`!!a>2x+)!MPnC3b zEZ>+4Si5E=CpK68>&!5D-!Eup1PJ>n<4v_f=;y=1)u`?p9L_9=k{B%xGk9)+>Tbdx z>_>~vJ%=VIL6v?rNXFZod_OUOp7Lk9Z}Ie2U|K{eB8$oGrZk9IpG}NO1&D5I5RnY@ z>5PnwYVo5d2M5K&qN`S$-ZvV#^<)-|_c;O-jYOzo2Vv=qDfh-B#P+Ph8R!+V$)Mgc zBRzM9ri9fH0dOa>vKdh!YVlTkqv(coMAc!UJtJD8mKZ0(?ifM3m#u- z(mIXwhGHNBFupGWDosSS|uuVjV0x+#emZ1f9ctd>0g37!Db1CTw8J!h6JI3 zb~e^~?J*k|Ne}wY3@2GL!`O^X4b5B?n#|{cTtFi(@O7&t=Ta%JMzuWRetb%m!DWk@ zzyS%gwIH-%o#T_NE}USYMefBZB2#o z(gzbQl*vJdj){#GYt<_+7dXz7z_i-KQU#I=i8gHwda)RgGVk=JH~M-og|^I!s7cTs z9!jYY(IkDZ0ZlAIxAAU46}hbVmkGiX?Pw*A|6GvroxD~!;6ATdtpq>vL7Ta@MGsC-I`Kk$Fhz zE|R*0dZnMGHLw5>a$h+OTf9S(juOBjoJ>ZWDbk!)X)F^5i6Tx^p-UP{y`m((Q&0t; zi)Fnq(G$`4I7?M|T_3vBNIb>mg{-nitY>rTZioQv9H}+L-Zc(KsU#*vxGFT(-8_<| z7Pr$@`CLitR=dT5%Ehevspr^w3MZFIg^F<@7ONzr_Pm1BnbO`NUary^ep5Qo^US_S zG2%y#2>1kXtXRBPH-m(DI9*pN^^gJi6=P*l$XM1j@ud4&Dn~L2{EZ{=bbzP|r<-B1 z;p0)hE2(}XLCTeNzMKX5P) zllb$FsF#kSbnkNbi|Q}UFdqW}8tRDAA{Ki@nUp=mbb@-XI6Xa-r6h&SNW=TU-qJ=C zq-243!&|7nfS?~&3|ajXJQ;wk*mNb|hZ&i!F6m^Hc62J$(?C*ZQsc3-z-8eUBT(MK zQ9+R9z@x&17J(1|luh-}MiPULgzg4KsUlL0)rmyUytETWIy(y0WH>bRyCyP^ zZ%;fS_Io5*V1x+u=!{OKE{I#mem1oQ1EX1-JfND~hGciNZ>V=Tf!-5S61=6D^#8%S9t0Pp`gwN;?pK<3zhr(Q#PsxmE#7KBI!cx)+ zIg^g^W5jV=xMwX?KZv24_>;qhO7{bpwBJ%XXs3{sBc&t2RpMw#lcA=o%ueiKWC@4e zs3#{pHNK!8Nq{=Xkg*tBL9ir-`uW95#>b-rTuwYqzmOSx_XEqrM;jFNTQ7fEPVywDF zI2Q{q{*NwwWuto${8kvi?$ zK9O>=rfDtJyqwMB5$Ad;z` z8rzzL{%Jg~_&6+UQtJrP=lRem5l(+z&muf!>OWB0_<3<_IK=hR zmZlJ_lwc}86EpYta(#ihH0w(k3zDLlpftDhST^`LTOP9fr+~>GkK}j764VB@>F8Kl zv&oOej5I$+a_Q(|RkZLf)6lo~G2F-`+ z#x(+L%tcwY;!URPfxxBNo*S1q>?=HZtdlJ7m?qgCHg#*2w#|d0`LgKx`X)%=wxx0X$dOh$IRB0a>4B&4>*wYILvG_nQ+A9r4d(xfbu z6b89dm93*)dkXUnM2@gW2>i{cq;+ETBqUxEn@_JHDpRC+rxmCVeKwaN{JQ zo6U_ej&Q+ZdT&7y6*~{upTFxt$zI`S$PuOq)uP0-Ff(SQTj4bmp&jjtVoUCGI6(6s zRLmB}2e_(#Wl*cHsJf}JUdoFZ=@S{-H{D`LBt1YaRi{0=M2e7J@{>**s&v*5Lq@R3 zznQ&BGX+%%wCihDt{PqSN};03u|Y9aFXUh)G5Q|`BK&6b9lDt{ho>4(70s4EsM+OQ+8Os)$~NC)N{_Wu=7BjHZmuVU3M<4GKErDUOgr=aC!3CyaAp z!b99oAG=TDZ@|8idNTv;_h9@}Ds6cf_2*;0psBS}Kv6i3B$=R1P+vMSpbbpU>8xs! zlX4V~6Ao#JN6jVK|ACM(kl$fIzK9nvoKAM=Mm!m!RZJHtND%|OT2gi-BxRj9>%36| zlB*!xgyUULqzgz1I2q}s1JHJm4C1`(6 zm84$ytGU@jG3fD#emfI*usKX6IlWBj9h627U)tJ%lJU8HA89_3j}!b>!6^S1Bc_9l zVN5lLd5f%OM3w)Ahs!ITyf4#TXD|~4S>+Wc!Uv2 zMOkrAe?%7{U1aFl=XUHvQ(4K_mCnBG)rxV*NI~Sv=VI7Tlv&8sHv7@5H7Bu)^hx7^ zL=Q3D;)>{lsKz)$IK$61uk)>GEWfTpJIyeFjOn9oxQwkZ=W=_d^pN`ts!=a3}OG+IRMFp zJ)z8i$fhwuSzH~Nm>z3dR$)UsGcw8C4mHQ?&R-qPJtT7z9b(z}LJn_i@{U3wEb=|X z60gLaF!Xy3cG~K}P$+s3(hjiXcPIZVdfz0#vqfIV7tYua(Qc@ewfF>$IYbi-kL4yc|?c*EI2 z$K`_b&XaFxMXi~W_>hWPaTqe@MYX)jI&sN^hnS*b-SnKE=LaI#k#Zs5w;Y?=QrXAl zTgk0b>5j2YeH2TSg$pWVB>QuAtk5TqRrK4>i=jVfv$q-*EmwV?D74P&28rl{JAdRs z8>XwiKzz=vo%Z$dU88BAxIK#SE&Dw!UC=K?w$p132Fr6N3p0Q#`+$unpzohq>w>0* z^UE7rFKVrBt^IBD_JkH3n3|zBoNR3Wf5f8;`m2@SsNh$``fh+j4L9QH2bBL_{3XD0 z04OzMMNaUbklueD{EtvOj0rn_Jqq=|kAi~g*K1|}eCr(?AwKwjdKco?Oc(?>TyZ0v zLjAjU>|ed7iVeR1ckg?@dcWd^`M*B;0D&FiiG87yUVc;=v2eGK=tHWL*giANtskA0 zGp~}VV-8N+|7+daq0FuSYji za^YlCnHIWhLZUlvu3Y<*FY#9g-gjx2CZB}6#Qyu`y{&~93e-@h9# zzT4fW-C)naa-l%%QvF$cz^?Q6!}%eoD?*!P$q3P22-m%(bK^YTx&y>%MN#Ys zjC`D~W3sbvig*s9}!asUk=s5iyBzb8{)|BTP}o~EG`yCZTnN zloCmgJ%nY?8Q5fu!Sf^le6os}mUsOAILVpV8-eUAF+b-&y?=SY2^}F^PjKBaOpm+p z`TIVOX>vz@z5Z2$7QW;4j0nMRZl;4X9)E$M4;WJwVD9S|Vwl#(eOQYO`D;cYMl&bU58LKgR#Bdt> zTUJ{dveC}94)5v2k+`8L`*9@kFBr&M)zzx$F>p+HlKV1;a)k@yO;{7s2h*n!SE-p! zrZ#lQFGt96O9K>3G>?F!5ue94yYjQ+KDxb=;ON3X@7DSz=M~p&N(2R*G_IW>tz$5W zlM~ZtOULn@*Doqp`kpCvPbCOz?RRz8&@s(`&|(X(n80)o_4Q{b3}m|vo=UnZxbZYu zefQIc`6Mf4;BZ!uUHgFzoYxp45oe6J^h%8~uHs{r!$Au|vUhb7IBE7~&p$gzcNH?G zBk$*zcC2PYdOd1a*at)8cou4+kBB5``q0`yg!!2T(;-zLtO?R3m!zAY zt;T#*ge7B?qskW%+_2#1$|6=%?0Heeeg-dGN((T?L(EXZ$tdz|5oVet%0iq~1lRBr z^Xbki;I6L2PUqDUbx@o7#>Z$HnErgVg6-{@6jE4rDv_MxUeO1!82SEmkw^9kA;op( zJo`8x@eFw^c?po1^CPEUn*n7769MCNjBK&5mwgAs{3|ggV^LXvzDXyYG56V5e@fqZ~|+Ckn=Lbyo6|)SpW# zrvi=##?wXk(6Bkh@rE_mL7BX$*%bTQ$|~2~SGQ(4zWh>=uNRJfT$rFz_yQ*>gzW;;g@P*h3|W z)_i%CRUB95WWdEG32>f(WhG$(4fFS%)|wP&#Zw9Tx>$ta=`T$*dC&

P*Cl<}?GN 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

^8mk*XA-1(TL5}` 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 O6Bm{dDinC<@qYjzhQ6-= literal 0 HcmV?d00001 diff --git a/doc/docs/static/images/0016.png b/doc/docs/static/images/0016.png new file mode 100644 index 0000000000000000000000000000000000000000..7b1a43edba866ffe3802238631ebebd2aaadd1cc GIT binary patch literal 77898 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�E7*1*O7L!&+@D}i2AmM7d4`n#N^B7@> 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 a&j}17a7iJ>d4d02Joi*C2p*IzL;nW|RyjHV literal 0 HcmV?d00001 diff --git a/doc/docs/static/images/1_eclipse.png b/doc/docs/static/images/1_eclipse.png new file mode 100755 index 0000000000000000000000000000000000000000..10b75a2810fdd99ed9b75cfc082ff10d599da530 GIT binary patch literal 61556 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 XwfO56jFn#$7VtsHt3E7&8wdUeC*ftL literal 0 HcmV?d00001 diff --git a/doc/docs/static/images/1_ieda.png b/doc/docs/static/images/1_ieda.png new file mode 100755 index 0000000000000000000000000000000000000000..1e87b58a892477335f901308fda853df23ee6b9b GIT binary patch literal 57680 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 literal 0 HcmV?d00001 diff --git a/doc/docs/imgs/grafana.png b/doc/docs/static/images/grafana.png similarity index 100% rename from doc/docs/imgs/grafana.png rename to doc/docs/static/images/grafana.png diff --git a/doc/docs/imgs/idea-auto-build.jpg b/doc/docs/static/images/idea-auto-build.jpg similarity index 100% rename from doc/docs/imgs/idea-auto-build.jpg rename to doc/docs/static/images/idea-auto-build.jpg diff --git a/doc/docs/static/images/idea_001.jpg b/doc/docs/static/images/idea_001.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2516433b99504cdd8779bdb2fca4cb4a79b6d09c GIT binary patch literal 467954 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

p4iP_GrD`Cob^-$B>IZY@T$ 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

(#dWz+XR9GSO=h!^vlTepT$& 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 literal 0 HcmV?d00001 diff --git a/doc/docs/static/images/idea_002.png b/doc/docs/static/images/idea_002.png new file mode 100644 index 0000000000000000000000000000000000000000..9c4ec19d6280e71cb4dd6fe90a3faf862351de03 GIT binary patch literal 128049 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 literal 0 HcmV?d00001 diff --git a/doc/docs/static/images/idea_004.png b/doc/docs/static/images/idea_004.png new file mode 100644 index 0000000000000000000000000000000000000000..1bb8fa47ab869d2f359554e0c95b51a790be7e74 GIT binary patch literal 243711 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ƀo<(97HUFFVD2bflIEzAL&lk*}O89 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 pXUQpH(`Fo|LU>k2Z4g|FeR$d8Tx5kC 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 k8zEUlRmM$;kzk6+fD5|2oYs|s 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 vMrns4mnV`8Z*_*?=q|!!~e`M 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 literal 0 HcmV?d00001 diff --git a/doc/docs/static/images/idea_006.png b/doc/docs/static/images/idea_006.png new file mode 100644 index 0000000000000000000000000000000000000000..fe50b6c16bb42aef8be483fbb6db3db19221f067 GIT binary patch literal 322796 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�fsLcG&#OYz_k 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

  • O{XCA`!1P0 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

    kGv{WG-sgOyd(|nIS?VwqiQp-{jsJ+9cB)6x<^3 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⪻7} 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

    S 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 U*@j?UksGhFo1^7(jen2-FD-O*8~^|S literal 0 HcmV?d00001 diff --git a/doc/docs/static/images/idea_007.png b/doc/docs/static/images/idea_007.png new file mode 100644 index 0000000000000000000000000000000000000000..a8756ad37b9a6067ed6963544d555abc2a2b3224 GIT binary patch literal 133783 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

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.gateway; + +import javax.servlet.http.HttpServletRequest; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @author michael yang (fuhai999@gmail.com) + * 负载均衡策略 + */ +public interface GatewayLoadBalanceStrategy { + + /** + * 默认的负载均衡策略,随机返回一个 url + */ + public static GatewayLoadBalanceStrategy DEFAULT_STRATEGY = (config, request) -> { + String[] urls = config.getUri(); + if (urls == null || urls.length == 0) { + return null; + } else if (urls.length == 1) { + return urls[0]; + } else { + return urls[ThreadLocalRandom.current().nextInt(urls.length)]; + } + }; + + public String getUrl(JbootGatewayConfig config, HttpServletRequest request); +} diff --git a/src/main/java/io/jboot/components/gateway/GatewayUtil.java b/src/main/java/io/jboot/components/gateway/GatewayUtil.java index 10819f3a..5f6e0854 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayUtil.java +++ b/src/main/java/io/jboot/components/gateway/GatewayUtil.java @@ -28,8 +28,8 @@ public class GatewayUtil { private static final String PATH_SPLIT = "/"; - public static String buildProxyUrl(JbootGatewayConfig config,HttpServletRequest request){ - StringBuilder url = new StringBuilder(config.getRandomUri()); + public static String buildProxyUrl(JbootGatewayConfig config, HttpServletRequest request) { + StringBuilder url = new StringBuilder(config.buildLoadBalanceStrategy().getUrl(config, request)); if (StrUtil.isNotBlank(request.getRequestURI())) { url.append(request.getRequestURI()); } @@ -64,7 +64,6 @@ public class GatewayUtil { } - private static String getResourcePath(HttpServletRequest request) { String pathInfo = normalizeAbsolutePath(request.getPathInfo(), false); String servletPath = normalizeAbsolutePath(request.getServletPath(), pathInfo.length() != 0); @@ -134,7 +133,7 @@ public class GatewayUtil { continue; } - pathChars[level++] = (char)buf.length(); + pathChars[level++] = (char) buf.length(); buf.append(element).append(PATH_SPLIT); } diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index eb26e8e0..8fc3178d 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -24,7 +24,6 @@ import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Objects; -import java.util.concurrent.ThreadLocalRandom; /** * @author michael yang (fuhai999@gmail.com) @@ -69,6 +68,7 @@ public class JbootGatewayConfig implements Serializable { //拦截器配置,一般可以用于对请求进行 鉴权 等处理 private String[] interceptors; + private String loadBalanceStrategy; // 暂时不支持 cookie // private Map cookieEquals; @@ -92,16 +92,6 @@ public class JbootGatewayConfig implements Serializable { this.uri = uri; } - public String getRandomUri() { - if (uri == null || uri.length == 0) { - return null; - } else if (uri.length == 1) { - return uri[0]; - } else { - return uri[ThreadLocalRandom.current().nextInt(uri.length)]; - } - } - public boolean isEnable() { return enable; } @@ -279,6 +269,38 @@ public class JbootGatewayConfig implements Serializable { } + public String getLoadBalanceStrategy() { + return loadBalanceStrategy; + } + + public void setLoadBalanceStrategy(String loadBalanceStrategy) { + this.loadBalanceStrategy = loadBalanceStrategy; + } + + private GatewayLoadBalanceStrategy gatewayLoadBalanceStrategy; + + public GatewayLoadBalanceStrategy buildLoadBalanceStrategy() { + if (gatewayLoadBalanceStrategy != null) { + return gatewayLoadBalanceStrategy; + } + + if (gatewayLoadBalanceStrategy == null) { + synchronized (this) { + if (StrUtil.isBlank(loadBalanceStrategy)) { + this.gatewayLoadBalanceStrategy = GatewayLoadBalanceStrategy.DEFAULT_STRATEGY; + } else { + GatewayLoadBalanceStrategy glbs = ClassUtil.newInstance(loadBalanceStrategy); + if (glbs == null) { + throw new NullPointerException("can not new instance by class:" + loadBalanceStrategy); + } + this.gatewayLoadBalanceStrategy = glbs; + } + } + } + return gatewayLoadBalanceStrategy; + } + + private Boolean configOk = null; public boolean isConfigOk() { -- Gitee From 7d03608e54792239f3029443c45b74ba1688883d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 25 Aug 2020 16:45:26 +0800 Subject: [PATCH 0484/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20gateway=20?= =?UTF-8?q?=E9=97=A8=E6=88=B7=E7=BD=91=E5=85=B3=E7=9A=84=E8=B4=9F=E8=BD=BD?= =?UTF-8?q?=E5=9D=87=E8=A1=A1=E7=AD=96=E7=95=A5=E9=85=8D=E7=BD=AE=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/docs/gateway.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/docs/gateway.md b/doc/docs/gateway.md index 2cb5f467..05a7b958 100644 --- a/doc/docs/gateway.md +++ b/doc/docs/gateway.md @@ -30,6 +30,7 @@ jboot.gateway.proxyConnectTimeout = 5000 jboot.gateway.proxyContentType = text/html;charset=utf-8 jboot.gateway.interceptors = com.xxx.Interceptor1,com.xxx.Interceptor2 +jboot.gateway.loadBalanceStrategy = com.xxx.loadBalanceStrategy1 jboot.gateway.pathEquals = /path jboot.gateway.pathContains = /path @@ -55,6 +56,7 @@ jboot.gateway.queryContains = aa,bb - proxyConnectTimeout 发生路由后,默认的连接超时时间,默认为 5 秒 - proxyContentType 发生路由后,返回给浏览器的 http-content-type,默认为:text/html;charset=utf-8 - interceptors 网关拦截器,一般用于进行鉴权等功能,配置类名,多个拦截器用英文逗号隔开,拦截器必须实现 GatewayInterceptor 接口 +- loadBalanceStrategy 负载均衡策略,当配置了多个 uri 的时候,可以通过此策略对 uri 进行获取 ## Path 路由 -- Gitee From 4922322697192849c94c8b925b9bac957734e2c4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 26 Aug 2020 15:03:46 +0800 Subject: [PATCH 0485/1965] upgrade doc theme --- doc/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/package.json b/doc/package.json index 1d4e1db5..6bc3f8c2 100644 --- a/doc/package.json +++ b/doc/package.json @@ -5,6 +5,6 @@ "chalk": "^2.4.2", "commander": "^2.20.3", "esm": "^3.2.25", - "vuepress-theme-reco": "1.5.4" + "vuepress-theme-reco": "1.5.5" } } -- Gitee From 69983f9c16d7da3e2001580338ebb91d2c188ab5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 26 Aug 2020 16:46:45 +0800 Subject: [PATCH 0486/1965] add jfinalConfig menu --- doc/.vuepress/config.js | 1 + doc/docs/jfinalConfig.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/doc/.vuepress/config.js b/doc/.vuepress/config.js index 1af646cc..123371ac 100644 --- a/doc/.vuepress/config.js +++ b/doc/.vuepress/config.js @@ -55,6 +55,7 @@ module.exports = { children: [ {title: '安装', path: '/docs/install'}, {title: '配置', path: '/docs/config'}, + {title: 'JFianlConfig', path: '/docs/jfinalConfig'}, {title: 'MVC', path: '/docs/mvc'}, {title: 'WebSocket', path: '/docs/websocket'}, {title: 'Jwt', path: '/docs/jwt'}, diff --git a/doc/docs/jfinalConfig.md b/doc/docs/jfinalConfig.md index ff666251..8bd25f0a 100644 --- a/doc/docs/jfinalConfig.md +++ b/doc/docs/jfinalConfig.md @@ -1,5 +1,7 @@ # 如何在Jboot中添加自己的 JFianlConfig +[[toc]] + 凡是开发过 JFinal 的同学,都知道 JFinalConfig 是 JFinal 的核心配置,详情: https://www.jfinal.com/doc/2-1 ,其内容如下: ```java -- Gitee From 7f941f8abbf06e148d07bf501c717e0912ed93cf Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 26 Aug 2020 18:02:22 +0800 Subject: [PATCH 0487/1965] add gitee pages deploy --- doc/.vuepress/config.js | 9 +++++---- doc/deploy_to_gitee.sh | 31 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) create mode 100755 doc/deploy_to_gitee.sh diff --git a/doc/.vuepress/config.js b/doc/.vuepress/config.js index 123371ac..85a5cf3a 100644 --- a/doc/.vuepress/config.js +++ b/doc/.vuepress/config.js @@ -2,6 +2,10 @@ // https://github.com/vuejs/vuepress/blob/master/packages/docs/docs/.vuepress/config.js // https://vuepress-theme-reco.recoluan.com/views/1.x/ module.exports = { + title: 'Jboot 官方网站', + description: 'Jboot 一个开源的分布式、商业级微服务框架。', + // base:'/docs/', + theme: 'vuepress-theme-reco', themeConfig: { //腾讯 404 公益配置 @@ -90,11 +94,8 @@ module.exports = { ], }, sidebarDepth: 1 - - }, - title: 'Jboot 官方网站', - description: 'Jboot 一个开源的分布式、商业级微服务框架。', + head: [ ['link', {rel: 'icon', href: '/logo.png'}], ['script', {}, ` diff --git a/doc/deploy_to_gitee.sh b/doc/deploy_to_gitee.sh new file mode 100755 index 00000000..cb65a35d --- /dev/null +++ b/doc/deploy_to_gitee.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env sh + +# abort on errors +set -e + +# build +vuepress build . + + + +cp CNAME .vuepress/dist + +# navigate into the build output directory +cd .vuepress/dist + + +# if you are deploying to a custom domain +# echo 'www.example.com' > CNAME + +git init +git add -A +git commit -m 'deploy' + +# need add config base:'/docs/', +# if you are deploying to https://.github.io +git push -f https://gitee.com/JbootProjects/docs.git master + +# if you are deploying to https://.github.io/ +# git push -f git@github.com:/.git master:gh-pages + +cd - \ No newline at end of file -- Gitee From 60d6a6bc70420b5051f8b03a099e56ec4d69f5a3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 30 Aug 2020 15:12:38 +0800 Subject: [PATCH 0488/1965] v3.4.0 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5f690fd9..f46fae75 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.3.7-SNAPSHOT + 3.4.0-SNAPSHOT jar jboot -- Gitee From c5c7fec7da238e912bf7d6dca96aaadf147423ae Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 30 Aug 2020 15:13:24 +0800 Subject: [PATCH 0489/1965] add AttachmentContainer component --- .../java/io/jboot/core/JbootCoreConfig.java | 5 + .../web/attachment/AttachmentContainer.java | 38 +++++ .../web/attachment/AttachmentHandler.java | 41 +++++ .../web/attachment/AttachmentManager.java | 82 +++++++++ .../attachment/LocalAttachmentContainer.java | 155 ++++++++++++++++++ .../jboot/web/controller/JbootController.java | 11 ++ 6 files changed, 332 insertions(+) create mode 100644 src/main/java/io/jboot/web/attachment/AttachmentContainer.java create mode 100644 src/main/java/io/jboot/web/attachment/AttachmentHandler.java create mode 100644 src/main/java/io/jboot/web/attachment/AttachmentManager.java create mode 100644 src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 838c6a27..7851c77c 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -47,6 +47,8 @@ import io.jboot.support.swagger.JbootSwaggerController; import io.jboot.support.swagger.JbootSwaggerManager; import io.jboot.utils.*; import io.jboot.web.JbootJson; +import io.jboot.web.attachment.AttachmentHandler; +import io.jboot.web.attachment.LocalAttachmentContainer; import io.jboot.web.controller.JbootControllerManager; import io.jboot.web.controller.annotation.RequestMapping; import io.jboot.web.directive.annotation.JFinalDirective; @@ -143,6 +145,8 @@ public class JbootCoreConfig extends JFinalConfig { constants.setJsonFactory(JbootJson::new); constants.setInjectDependency(true); + constants.setBaseUploadPath(LocalAttachmentContainer.DEFAULT_ATTACHEMENT_PATH); + JbootAppListenerManager.me().onConstantConfig(constants); @@ -261,6 +265,7 @@ public class JbootCoreConfig extends JFinalConfig { JbootAppListenerManager.me().onHandlerConfig(new JfinalHandlers(handlers)); handlers.add(new JbootGatewayHandler()); + handlers.add(new AttachmentHandler()); handlers.add(new JbootFilterHandler()); handlers.add(new JbootHandler()); diff --git a/src/main/java/io/jboot/web/attachment/AttachmentContainer.java b/src/main/java/io/jboot/web/attachment/AttachmentContainer.java new file mode 100644 index 00000000..621354b0 --- /dev/null +++ b/src/main/java/io/jboot/web/attachment/AttachmentContainer.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.jboot.web.attachment; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public interface AttachmentContainer { + + public String saveFile(File file); + public boolean deleteFile(String relativePath); + public File newRandomFile(String suffix); + public File getFile(String relativePath); + public String getRelativePath(File file); + public boolean isSafeFile(File file); + public boolean isRemoteContainer(); + + public boolean matchFile(String target, HttpServletRequest request); + public void renderFile(String target, HttpServletRequest request, HttpServletResponse response); +} diff --git a/src/main/java/io/jboot/web/attachment/AttachmentHandler.java b/src/main/java/io/jboot/web/attachment/AttachmentHandler.java new file mode 100644 index 00000000..d4fbadd8 --- /dev/null +++ b/src/main/java/io/jboot/web/attachment/AttachmentHandler.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.jboot.web.attachment; + +import com.jfinal.handler.Handler; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public class AttachmentHandler extends Handler { + + private AttachmentManager manager = AttachmentManager.me(); + + @Override + public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { + AttachmentContainer container = manager.matchContainer(target, request); + if (container != null) { + container.renderFile(target, request, response); + isHandled[0] = true; + } else { + next.handle(target, request, response, isHandled); + } + } +} diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java new file mode 100644 index 00000000..aed92128 --- /dev/null +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.jboot.web.attachment; + +import javax.servlet.http.HttpServletRequest; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public class AttachmentManager { + + + private static final AttachmentManager ME = new AttachmentManager(); + + public static AttachmentManager me() { + return ME; + } + + private AttachmentManager() { + containerMap.put("default",new LocalAttachmentContainer()); + } + + + private Map containerMap = new ConcurrentHashMap<>(); + + + public AttachmentContainer matchContainer(String target, HttpServletRequest request){ + if (containerMap.size() == 0){ + return null; + } + + for (AttachmentContainer container : containerMap.values()){ + if (container.matchFile(target,request)){ + return container; + } + } + + return null; + } + + + public void addContainer(String name,AttachmentContainer container){ + containerMap.put(name,container); + } + + public AttachmentContainer getContainer(String name){ + return getContainer(name); + } + + public Map getContainerMap() { + return containerMap; + } + + public AttachmentContainer getDefaultContainer(){ + AttachmentContainer defaultContainer = containerMap.get("default"); + if (defaultContainer != null){ + return defaultContainer; + } + + if (containerMap.size() > 0){ + return containerMap.values().stream().findFirst().get(); + } + + return null; + } +} diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java new file mode 100644 index 00000000..54309e05 --- /dev/null +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -0,0 +1,155 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.attachment; + +import com.jfinal.ext.kit.DateKit; +import com.jfinal.kit.LogKit; +import com.jfinal.kit.PathKit; +import com.jfinal.render.FileRender; +import io.jboot.utils.FileUtil; +import io.jboot.utils.StrUtil; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.util.Date; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public class LocalAttachmentContainer implements AttachmentContainer { + + public static final String DEFAULT_ATTACHEMENT_PATH = "attachment"; + + private String rootPath = PathKit.getWebRootPath(); + private String targetPrefix = DEFAULT_ATTACHEMENT_PATH; + + public LocalAttachmentContainer() { + } + + public LocalAttachmentContainer(String rootPath, String targetPrefix) { + this.rootPath = rootPath; + this.targetPrefix = targetPrefix; + } + + @Override + public String saveFile(File file) { + File newfile = newRandomFile(FileUtil.getSuffix(file.getName())); + + if (!newfile.getParentFile().exists()) { + newfile.getParentFile().mkdirs(); + } + + try { + org.apache.commons.io.FileUtils.moveFile(file, newfile); + newfile.setReadable(true, false); + } catch (IOException e) { + LogKit.error(e.toString(), e); + } + + String attachmentRoot = getRootPath(); + return FileUtil.removePrefix(newfile.getAbsolutePath(), attachmentRoot); + } + + + @Override + public boolean deleteFile(String relativePath) { + return getFile(relativePath).delete(); + } + + + @Override + public File newRandomFile(String suffix) { + String rootPath = getRootPath(); + + StringBuilder newFileName = new StringBuilder(rootPath) + .append(File.separator).append(targetPrefix) + .append(File.separator).append(DateKit.toStr(new Date(), "yyyyMMdd")) + .append(File.separator).append(StrUtil.uuid()) + .append(suffix); + + return new File(newFileName.toString()); + } + + + @Override + public File getFile(String relativePath) { + return new File(getRootPath(), relativePath); + } + + + @Override + public String getRelativePath(File file) { + String rootPath = getRootPath(); + String filePath = file.getAbsolutePath(); + return filePath.startsWith(rootPath) + ? filePath.substring(rootPath.length()) + : filePath; + } + + @Override + public boolean isSafeFile(File file) { + return true; + } + + + @Override + public boolean isRemoteContainer() { + return false; + } + + @Override + public boolean matchFile(String target, HttpServletRequest request) { + return target.startsWith(buildMatchTarget()); + } + + private String matchTarget = null; + + private String buildMatchTarget() { + if (matchTarget == null) { + synchronized (this) { + if (matchTarget == null) { + matchTarget = "/" + getTargetPrefix() + "/"; + } + } + } + return matchTarget; + } + + @Override + public void renderFile(String target, HttpServletRequest request, HttpServletResponse response) { + new FileRender(new File(target)).setContext(request, response).render(); + } + + + public String getRootPath() { + return rootPath; + } + + public void setRootPath(String rootPath) { + this.rootPath = rootPath; + } + + public String getTargetPrefix() { + return targetPrefix; + } + + public void setTargetPrefix(String targetPrefix) { + this.targetPrefix = targetPrefix; + this.matchTarget = null; + } +} diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 1f547951..2adb3b22 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -23,6 +23,7 @@ import com.jfinal.core.NotAction; import com.jfinal.kit.JsonKit; import com.jfinal.kit.StrKit; import com.jfinal.render.RenderManager; +import com.jfinal.upload.UploadFile; import io.jboot.support.jwt.JwtManager; import io.jboot.utils.RequestUtil; import io.jboot.utils.StrUtil; @@ -31,6 +32,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.util.Enumeration; import java.util.HashMap; +import java.util.List; import java.util.Map; @@ -395,4 +397,13 @@ public class JbootController extends Controller { return toBigDecimal(getTrimPara(name), defaultValue); } + @Override + public UploadFile getFile() { + return super.getFile(); + } + + @Override + public List getFiles() { + return super.getFiles(); + } } -- Gitee From 370b093943da60d0bd5d1a442c63cd5344f8d762 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 30 Aug 2020 15:42:35 +0800 Subject: [PATCH 0490/1965] add AttachmentContainer component --- .../web/attachment/LocalAttachmentContainer.java | 2 +- .../io/jboot/web/controller/JbootController.java | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java index 54309e05..4cec01a2 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -132,7 +132,7 @@ public class LocalAttachmentContainer implements AttachmentContainer { @Override public void renderFile(String target, HttpServletRequest request, HttpServletResponse response) { - new FileRender(new File(target)).setContext(request, response).render(); + new FileRender(getFile(target)).setContext(request, response).render(); } diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 2adb3b22..b974aa9e 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -23,7 +23,6 @@ import com.jfinal.core.NotAction; import com.jfinal.kit.JsonKit; import com.jfinal.kit.StrKit; import com.jfinal.render.RenderManager; -import com.jfinal.upload.UploadFile; import io.jboot.support.jwt.JwtManager; import io.jboot.utils.RequestUtil; import io.jboot.utils.StrUtil; @@ -32,7 +31,6 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.util.Enumeration; import java.util.HashMap; -import java.util.List; import java.util.Map; @@ -396,14 +394,4 @@ public class JbootController extends Controller { public BigDecimal getBigDecimal(String name, BigDecimal defaultValue) { return toBigDecimal(getTrimPara(name), defaultValue); } - - @Override - public UploadFile getFile() { - return super.getFile(); - } - - @Override - public List getFiles() { - return super.getFiles(); - } } -- Gitee From b70aa8422bb8b00d82a5d0f97f31d082a89e6e1a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 30 Aug 2020 15:50:39 +0800 Subject: [PATCH 0491/1965] optimize JbootActionReporter --- .../jboot/web/handler/JbootActionReporter.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index c5dec2af..511a69ad 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -17,7 +17,9 @@ package io.jboot.web.handler; import com.jfinal.aop.Interceptor; import com.jfinal.core.Action; +import com.jfinal.core.ActionReporter; import com.jfinal.core.Controller; +import com.jfinal.kit.LogKit; import io.jboot.JbootConsts; import javassist.ClassPool; import javassist.CtClass; @@ -63,7 +65,19 @@ public class JbootActionReporter { /** * Report the action */ - public static final void report(String target, Controller controller, Action action, long time) throws Exception { + public static final void report(String target, Controller controller, Action action, long time) { + try { + doReport(target, controller, action, time); + } catch (Exception ex) { + // 出错的情况,一般情况下是:用户自定义了自己的 classloader, 此 classloader 加载的 class 没有被添加到 ClassPool + // 如何添加可以参考 jpress 的 插件加载 + LogKit.error("JbootActionReport.doReport error, use Jfinal Reporter"); + ActionReporter.report(target, controller, action); + } + } + + + private static final void doReport(String target, Controller controller, Action action, long time) throws Exception { CtClass ctClass = ClassPool.getDefault().get(action.getControllerClass().getName()); String desc = JbootActionReporterUtil.getMethodDescWithoutName(action.getMethod()); CtMethod ctMethod = ctClass.getMethod(action.getMethodName(), desc); -- Gitee From eb4d5d7b5214c8a57de284fa434a6922eeb18024 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 30 Aug 2020 16:01:37 +0800 Subject: [PATCH 0492/1965] add AttachmentContainer component --- .../web/attachment/AttachmentContainer.java | 46 ++++++++++++++++++- .../attachment/LocalAttachmentContainer.java | 6 --- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/web/attachment/AttachmentContainer.java b/src/main/java/io/jboot/web/attachment/AttachmentContainer.java index 621354b0..6dfc6ec1 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentContainer.java @@ -25,14 +25,56 @@ import java.io.File; */ public interface AttachmentContainer { + /** + * 保存文件 + * @param file + * @return 返回文件的相对路径 + */ public String saveFile(File file); + + + /** + * 删除文件 + * @param relativePath + * @return + */ public boolean deleteFile(String relativePath); - public File newRandomFile(String suffix); + + /** + * 通过相对路径获取文件 + * @param relativePath + * @return + */ public File getFile(String relativePath); + + /** + * 通过一个文件,获取其相对路径 + * @param file + * @return + */ public String getRelativePath(File file); - public boolean isSafeFile(File file); + + + /** + * 是否是 远程 文件容器 + * @return + */ public boolean isRemoteContainer(); + + /** + * 通过路径判断是匹配这个文件 + * @param target + * @param request + * @return + */ public boolean matchFile(String target, HttpServletRequest request); + + /** + * 渲染这个文件 + * @param target + * @param request + * @param response + */ public void renderFile(String target, HttpServletRequest request, HttpServletResponse response); } diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java index 4cec01a2..ed7f0b0f 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -72,7 +72,6 @@ public class LocalAttachmentContainer implements AttachmentContainer { } - @Override public File newRandomFile(String suffix) { String rootPath = getRootPath(); @@ -101,11 +100,6 @@ public class LocalAttachmentContainer implements AttachmentContainer { : filePath; } - @Override - public boolean isSafeFile(File file) { - return true; - } - @Override public boolean isRemoteContainer() { -- Gitee From fbf11a09b700f1cac0dff8e21ffe719c5d10197c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 30 Aug 2020 19:33:45 +0800 Subject: [PATCH 0493/1965] add AttachmentContainer component --- .../web/attachment/AttachmentContainer.java | 3 ++- .../web/attachment/AttachmentHandler.java | 6 ++--- .../attachment/LocalAttachmentContainer.java | 25 +++++++++++++++++-- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/web/attachment/AttachmentContainer.java b/src/main/java/io/jboot/web/attachment/AttachmentContainer.java index 6dfc6ec1..04c1a354 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentContainer.java @@ -75,6 +75,7 @@ public interface AttachmentContainer { * @param target * @param request * @param response + * @return return true 渲染成功,不需要服务器再进行渲染,return false 需要服务器自行渲染 */ - public void renderFile(String target, HttpServletRequest request, HttpServletResponse response); + public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response); } diff --git a/src/main/java/io/jboot/web/attachment/AttachmentHandler.java b/src/main/java/io/jboot/web/attachment/AttachmentHandler.java index d4fbadd8..77af81d4 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentHandler.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentHandler.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package io.jboot.web.attachment; import com.jfinal.handler.Handler; @@ -32,8 +31,9 @@ public class AttachmentHandler extends Handler { public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { AttachmentContainer container = manager.matchContainer(target, request); if (container != null) { - container.renderFile(target, request, response); - isHandled[0] = true; + if (container.renderFile(target, request, response)){ + isHandled[0] = true; + } } else { next.handle(target, request, response, isHandled); } diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java index ed7f0b0f..42b0856e 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -38,6 +38,7 @@ public class LocalAttachmentContainer implements AttachmentContainer { private String rootPath = PathKit.getWebRootPath(); private String targetPrefix = DEFAULT_ATTACHEMENT_PATH; + public LocalAttachmentContainer() { } @@ -106,6 +107,7 @@ public class LocalAttachmentContainer implements AttachmentContainer { return false; } + @Override public boolean matchFile(String target, HttpServletRequest request) { return target.startsWith(buildMatchTarget()); @@ -124,9 +126,28 @@ public class LocalAttachmentContainer implements AttachmentContainer { return matchTarget; } + + private Boolean runInFatjar; + + private boolean isRunInfatjar() { + if (runInFatjar == null) { + runInFatjar = LocalAttachmentContainer.class.getResource("/") == null; + } + return runInFatjar; + } + + @Override - public void renderFile(String target, HttpServletRequest request, HttpServletResponse response) { - new FileRender(getFile(target)).setContext(request, response).render(); + public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { + //是否在 fatjar 运行,如果不在 fatjar 运行,由 tomcat 或者 undertow 自行渲染 + if (!isRunInfatjar()) { + return false; + } + // 如果在 fatjar 运行,需要自己来渲染该文件 + else { + new FileRender(getFile(target)).setContext(request, response).render(); + return true; + } } -- Gitee From 74b6347c827693d2cdf3d1fd74eb5ca8716ded6a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 30 Aug 2020 19:46:38 +0800 Subject: [PATCH 0494/1965] add AttachmentContainer component --- .../web/attachment/AttachmentManager.java | 98 ++++++++++++++++--- 1 file changed, 85 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index aed92128..f2872d8d 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -16,7 +16,10 @@ package io.jboot.web.attachment; +import com.jfinal.log.Log; + import javax.servlet.http.HttpServletRequest; +import java.io.File; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -25,6 +28,9 @@ import java.util.concurrent.ConcurrentHashMap; */ public class AttachmentManager { + private static final Log LOG = Log.getLog(AttachmentManager.class); + private static final String DEFAULT_KEY = "default"; + private static final AttachmentManager ME = new AttachmentManager(); @@ -33,20 +39,20 @@ public class AttachmentManager { } private AttachmentManager() { - containerMap.put("default",new LocalAttachmentContainer()); + containerMap.put(DEFAULT_KEY, new LocalAttachmentContainer()); } - private Map containerMap = new ConcurrentHashMap<>(); + private Map containerMap = new ConcurrentHashMap<>(); - public AttachmentContainer matchContainer(String target, HttpServletRequest request){ - if (containerMap.size() == 0){ + public AttachmentContainer matchContainer(String target, HttpServletRequest request) { + if (containerMap.size() == 0) { return null; } - for (AttachmentContainer container : containerMap.values()){ - if (container.matchFile(target,request)){ + for (AttachmentContainer container : containerMap.values()) { + if (container.matchFile(target, request)) { return container; } } @@ -55,11 +61,11 @@ public class AttachmentManager { } - public void addContainer(String name,AttachmentContainer container){ - containerMap.put(name,container); + public void addContainer(String name, AttachmentContainer container) { + containerMap.put(name, container); } - public AttachmentContainer getContainer(String name){ + public AttachmentContainer getContainer(String name) { return getContainer(name); } @@ -67,16 +73,82 @@ public class AttachmentManager { return containerMap; } - public AttachmentContainer getDefaultContainer(){ - AttachmentContainer defaultContainer = containerMap.get("default"); - if (defaultContainer != null){ + public AttachmentContainer getDefaultContainer() { + AttachmentContainer defaultContainer = containerMap.get(DEFAULT_KEY); + if (defaultContainer != null) { return defaultContainer; } - if (containerMap.size() > 0){ + if (containerMap.size() > 0) { return containerMap.values().stream().findFirst().get(); } return null; } + + + /** + * 保存文件 + * + * @param file + * @return 返回文件的相对路径 + */ + public String saveFile(File file) { + String retString = null; + for (Map.Entry entry : containerMap.entrySet()) { + AttachmentContainer container = entry.getValue(); + try { + String result = container.saveFile(file); + if (DEFAULT_KEY.equals(entry.getKey())) { + retString = result; + } + } catch (Exception ex) { + LOG.error("save file error in container :" + container, ex); + } + } + return retString; + } + + + /** + * 删除文件 + * + * @param relativePath + * @return + */ + public boolean deleteFile(String relativePath) { + boolean ret = false; + for (Map.Entry entry : containerMap.entrySet()) { + AttachmentContainer container = entry.getValue(); + try { + boolean result = container.deleteFile(relativePath); + if (DEFAULT_KEY.equals(entry.getKey())) { + ret = result; + } + } catch (Exception ex) { + LOG.error("delete file error in container :" + container, ex); + } + } + return ret; + } + + /** + * 通过相对路径获取文件 + * + * @param relativePath + * @return + */ + public File getFile(String relativePath) { + return getDefaultContainer().getFile(relativePath); + } + + /** + * 通过一个文件,获取其相对路径 + * + * @param file + * @return + */ + public String getRelativePath(File file) { + return getDefaultContainer().getRelativePath(file); + } } -- Gitee From 6f884719652aae468251cda88b9263fe7e0cc110 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 31 Aug 2020 10:46:02 +0800 Subject: [PATCH 0495/1965] v3.4.0 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 13 +++++++++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 81fa5d72..4d3a3f5b 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.3.6 + 3.4.0 ``` diff --git a/changes.txt b/changes.txt index 7b1f80b1..252b5e01 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,16 @@ +jboot v3.4.0: +新增:门户网关 Gateway 新增自定义负载均衡策略的支持 +新增:AttachmentContainer 组件,方便自定义把附件上传到其他第三方任何平台 +新增:全新的文档地址 和 Jboot 官网 +修复:数据源 Datasource 的 validationQuery 属性配置不生效的问题 +优化:升级 JFinal-Weixin 版本 +优化:删除 JbootActionReporter 一些不必要的方法 +优化:删除一些不必要的 注释信息 +优化:修改 JbootPaginateDirective 自动去获取当前的 page 信息,而不需配置 +优化:移除 JbootHttpImpl 默认的 content-type 配置 + + + jboot v3.3.5: 修复:通过门户网关下载文件 或者 渲染图片可能出现乱码的问题 优化:重构 Http 工具类里的 HttpRequest 里的某些方法 diff --git a/doc/docs/install.md b/doc/docs/install.md index 421f3edc..e3ad7170 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.3.6 + 3.4.0 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 34629af3..d9eb8c0e 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.3.6 + 3.4.0 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 6226f942..37f94e46 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -49,7 +49,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.3.6 + 3.4.0 ``` @@ -107,7 +107,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.3.6', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.4.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 3e4c7061..fc55fb61 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.3.6"; + public static String VERSION = "3.4.0"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 48c1037021317f92eb5c569d2d61cb8b1153ae57 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 31 Aug 2020 10:53:21 +0800 Subject: [PATCH 0496/1965] v3.4.0 release (^.^)YYa!! --- changes.txt | 1 + src/main/java/io/jboot/db/SqlDebugger.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 252b5e01..f6f677d9 100644 --- a/changes.txt +++ b/changes.txt @@ -3,6 +3,7 @@ jboot v3.4.0: 新增:AttachmentContainer 组件,方便自定义把附件上传到其他第三方任何平台 新增:全新的文档地址 和 Jboot 官网 修复:数据源 Datasource 的 validationQuery 属性配置不生效的问题 +修复:SqlDebugPrinter 对参数为 Boolean 数据输出的格式不正确的问题 优化:升级 JFinal-Weixin 版本 优化:删除 JbootActionReporter 一些不必要的方法 优化:删除一些不必要的 注释信息 diff --git a/src/main/java/io/jboot/db/SqlDebugger.java b/src/main/java/io/jboot/db/SqlDebugger.java index 66b98c37..102980df 100644 --- a/src/main/java/io/jboot/db/SqlDebugger.java +++ b/src/main/java/io/jboot/db/SqlDebugger.java @@ -66,7 +66,7 @@ public class SqlDebugger { sql = sql.replaceFirst("\\?", "null"); } // number - else if (value instanceof Number) { + else if (value instanceof Number || value instanceof Boolean) { sql = sql.replaceFirst("\\?", value.toString()); } // numeric -- Gitee From b217d38d131e2f876abdc14c1bb3cdaab776e991 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 31 Aug 2020 11:09:26 +0800 Subject: [PATCH 0497/1965] v3.4.0 release (^.^)YYa!! --- .../java/io/jboot/web/attachment/LocalAttachmentContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java index 42b0856e..24b56bbe 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -110,7 +110,7 @@ public class LocalAttachmentContainer implements AttachmentContainer { @Override public boolean matchFile(String target, HttpServletRequest request) { - return target.startsWith(buildMatchTarget()); + return target.startsWith(buildMatchTarget()) && target.indexOf('.') != -1; } private String matchTarget = null; -- Gitee From 2bb5848433459596bdb2390bf7c1343828285ca1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 31 Aug 2020 11:24:31 +0800 Subject: [PATCH 0498/1965] v3.4.0 release (^.^)YYa!! --- .../java/io/jboot/web/attachment/LocalAttachmentContainer.java | 2 +- src/main/java/io/jboot/web/handler/JbootActionHandler.java | 2 +- src/main/java/io/jboot/web/handler/JbootFilterHandler.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java index 24b56bbe..42986b8e 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -110,7 +110,7 @@ public class LocalAttachmentContainer implements AttachmentContainer { @Override public boolean matchFile(String target, HttpServletRequest request) { - return target.startsWith(buildMatchTarget()) && target.indexOf('.') != -1; + return target.startsWith(buildMatchTarget()) && target.lastIndexOf('.') != -1; } private String matchTarget = null; diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 7d0db94c..87815bfc 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -86,7 +86,7 @@ public class JbootActionHandler extends ActionHandler { */ @Override public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { - if (target.indexOf('.') != -1) { + if (target.lastIndexOf('.') != -1) { return; } diff --git a/src/main/java/io/jboot/web/handler/JbootFilterHandler.java b/src/main/java/io/jboot/web/handler/JbootFilterHandler.java index 82a22420..796a4585 100644 --- a/src/main/java/io/jboot/web/handler/JbootFilterHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootFilterHandler.java @@ -32,7 +32,7 @@ public class JbootFilterHandler extends Handler { public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { //static files - if (target.indexOf('.') != -1) { + if (target.lastIndexOf('.') != -1) { return; } -- Gitee From ef5801cd241607acc9e27b9b9b35b29743dcd5bb Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 31 Aug 2020 11:32:48 +0800 Subject: [PATCH 0499/1965] v3.4.0 release (^.^)YYa!! --- changes.txt | 1 + src/main/java/io/jboot/core/JbootCoreConfig.java | 12 ++++++++++-- ...lterHandler.java => JbootMetricsHandler.java} | 16 ++++++++-------- 3 files changed, 19 insertions(+), 10 deletions(-) rename src/main/java/io/jboot/web/handler/{JbootFilterHandler.java => JbootMetricsHandler.java} (76%) diff --git a/changes.txt b/changes.txt index f6f677d9..6d631016 100644 --- a/changes.txt +++ b/changes.txt @@ -9,6 +9,7 @@ jboot v3.4.0: 优化:删除一些不必要的 注释信息 优化:修改 JbootPaginateDirective 自动去获取当前的 page 信息,而不需配置 优化:移除 JbootHttpImpl 默认的 content-type 配置 +优化:重构 Metrics 读取的相关处理,在 Metrics 未配置的时候,没必要添加相关 Handler,提示性能 diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 7851c77c..7d1aae53 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -39,6 +39,7 @@ import io.jboot.components.schedule.JbootScheduleManager; import io.jboot.core.listener.JbootAppListenerManager; import io.jboot.core.log.Slf4jLogFactory; import io.jboot.db.ArpManager; +import io.jboot.support.metric.JbootMetricConfig; import io.jboot.support.seata.JbootSeataManager; import io.jboot.support.sentinel.SentinelManager; import io.jboot.support.shiro.JbootShiroManager; @@ -57,8 +58,8 @@ import io.jboot.web.directive.annotation.JFinalSharedObject; import io.jboot.web.directive.annotation.JFinalSharedStaticMethod; import io.jboot.web.fixedinterceptor.FixedInterceptors; import io.jboot.web.handler.JbootActionHandler; -import io.jboot.web.handler.JbootFilterHandler; import io.jboot.web.handler.JbootHandler; +import io.jboot.web.handler.JbootMetricsHandler; import io.jboot.web.render.JbootRenderFactory; import java.io.File; @@ -266,7 +267,14 @@ public class JbootCoreConfig extends JFinalConfig { handlers.add(new JbootGatewayHandler()); handlers.add(new AttachmentHandler()); - handlers.add(new JbootFilterHandler()); + + //metrics 处理 + JbootMetricConfig metricsConfig = Jboot.config(JbootMetricConfig.class); + if (metricsConfig.isConfigOk()){ + handlers.add(new JbootMetricsHandler(metricsConfig.getUrl())); + } + + handlers.add(new JbootHandler()); //若用户自己没配置 ActionHandler,默认使用 JbootActionHandler diff --git a/src/main/java/io/jboot/web/handler/JbootFilterHandler.java b/src/main/java/io/jboot/web/handler/JbootMetricsHandler.java similarity index 76% rename from src/main/java/io/jboot/web/handler/JbootFilterHandler.java rename to src/main/java/io/jboot/web/handler/JbootMetricsHandler.java index 796a4585..f7678973 100644 --- a/src/main/java/io/jboot/web/handler/JbootFilterHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootMetricsHandler.java @@ -16,17 +16,19 @@ package io.jboot.web.handler; import com.jfinal.handler.Handler; -import io.jboot.Jboot; -import io.jboot.support.metric.JbootMetricConfig; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -public class JbootFilterHandler extends Handler { +public class JbootMetricsHandler extends Handler { - private static JbootMetricConfig metricsConfig = Jboot.config(JbootMetricConfig.class); + private String metricsReadUrl; + + public JbootMetricsHandler(String metricsReadUrl) { + this.metricsReadUrl = metricsReadUrl; + } @Override public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { @@ -36,14 +38,12 @@ public class JbootFilterHandler extends Handler { return; } - // metrics - if (metricsConfig.isConfigOk() && target.startsWith(metricsConfig.getUrl())) { + // metrics servlet 处理,此处如果是 tomcat,需要在 web.xml 配置 metrics 的相关 servlet + if (target.startsWith(metricsReadUrl)) { return; } - next.handle(target, request, response, isHandled); - } -- Gitee From bc77d1bf106f32dae5606b25181827763ec92788 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 31 Aug 2020 11:39:18 +0800 Subject: [PATCH 0500/1965] v3.4.0 release (^.^)YYa!! --- changes.txt | 2 +- src/main/java/io/jboot/utils/ClassScanner.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 6d631016..4d3d5e2c 100644 --- a/changes.txt +++ b/changes.txt @@ -9,7 +9,7 @@ jboot v3.4.0: 优化:删除一些不必要的 注释信息 优化:修改 JbootPaginateDirective 自动去获取当前的 page 信息,而不需配置 优化:移除 JbootHttpImpl 默认的 content-type 配置 -优化:重构 Metrics 读取的相关处理,在 Metrics 未配置的时候,没必要添加相关 Handler,提示性能 +优化:重构 Metrics 读取的相关处理,在 Metrics 未配置的时候,没必要添加相关 Handler,提升性能 diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 7e4bfb59..ae282d4a 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -308,6 +308,7 @@ public class ClassScanner { excludeClasses.add("io.jboot.service."); excludeClasses.add("io.jboot.Jboot"); excludeClasses.add("freemarker."); + excludeClasses.add("com.twelvemonkeys."); } -- Gitee From 082d08a3ac6aa04b537f0c8ff390eaa61994bc08 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 31 Aug 2020 11:52:17 +0800 Subject: [PATCH 0501/1965] v3.4.0 release (^.^)YYa!! --- changes.txt | 2 +- pom.xml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/changes.txt b/changes.txt index 4d3d5e2c..ee6f3115 100644 --- a/changes.txt +++ b/changes.txt @@ -4,7 +4,7 @@ jboot v3.4.0: 新增:全新的文档地址 和 Jboot 官网 修复:数据源 Datasource 的 validationQuery 属性配置不生效的问题 修复:SqlDebugPrinter 对参数为 Boolean 数据输出的格式不正确的问题 -优化:升级 JFinal-Weixin 版本 +优化:升级 Sentinal、Metrics、JFinal-Weixin 等到最新版本 优化:删除 JbootActionReporter 一些不必要的方法 优化:删除一些不必要的 注释信息 优化:修改 JbootPaginateDirective 自动去获取当前的 page 信息,而不需配置 diff --git a/pom.xml b/pom.xml index f46fae75..eb4150a7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.4.0-SNAPSHOT + 3.4.0 jar jboot @@ -65,12 +65,12 @@ 4.5.12 4.4.13 4.1.50.Final - 1.7.2 + 1.8.0 2.7.8 3.12.1.Final 1.3.0 1.1.8 - 4.1.11 + 4.1.12.1 1.8 1.8 -- Gitee From 5de1778e5774695e36a358ea15dd24ed1d4bff8b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 1 Sep 2020 16:47:27 +0800 Subject: [PATCH 0502/1965] v3.4.1 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eb4150a7..b1184ba3 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.4.0 + 3.4.1-SNAPSHOT jar jboot -- Gitee From 44aa4342056497c46a6b6f2af9a82c224ad7ae32 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 1 Sep 2020 16:54:02 +0800 Subject: [PATCH 0503/1965] fixed: cant not config jboot.app.mode with start args --- src/main/java/io/jboot/app/ApplicationUtil.java | 2 +- src/main/java/io/jboot/app/JbootApplication.java | 2 +- src/main/java/io/jboot/app/JbootSimpleApplication.java | 2 +- src/main/java/io/jboot/app/config/JbootConfigManager.java | 4 ++-- .../java/io/jboot/app/undertow/JbootUndertowConfig.java | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/app/ApplicationUtil.java b/src/main/java/io/jboot/app/ApplicationUtil.java index a269cb39..4edbde9f 100644 --- a/src/main/java/io/jboot/app/ApplicationUtil.java +++ b/src/main/java/io/jboot/app/ApplicationUtil.java @@ -23,7 +23,7 @@ import java.net.URL; class ApplicationUtil { static JbootApplicationConfig getAppConfig(String[] args) { - JbootConfigManager.me().parseArgs(args); + JbootConfigManager.parseArgs(args); return getConfig(JbootApplicationConfig.class); } diff --git a/src/main/java/io/jboot/app/JbootApplication.java b/src/main/java/io/jboot/app/JbootApplication.java index b7b4ebea..a30cca0d 100644 --- a/src/main/java/io/jboot/app/JbootApplication.java +++ b/src/main/java/io/jboot/app/JbootApplication.java @@ -46,7 +46,7 @@ public class JbootApplication { } public static void setBootArg(String key, Object value) { - JbootConfigManager.me().setBootArg(key, value); + JbootConfigManager.setBootArg(key, value); } /** diff --git a/src/main/java/io/jboot/app/JbootSimpleApplication.java b/src/main/java/io/jboot/app/JbootSimpleApplication.java index def02497..0b20b5cb 100644 --- a/src/main/java/io/jboot/app/JbootSimpleApplication.java +++ b/src/main/java/io/jboot/app/JbootSimpleApplication.java @@ -39,7 +39,7 @@ public class JbootSimpleApplication { } public static void setBootArg(String key, Object value) { - JbootConfigManager.me().setBootArg(key, value); + JbootConfigManager.setBootArg(key, value); } public static void run(String[] args) { diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 5a02c693..10f9c712 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -436,7 +436,7 @@ public class JbootConfigManager { * * @param args */ - public void parseArgs(String[] args) { + public static void parseArgs(String[] args) { if (args == null || args.length == 0) { return; } @@ -451,7 +451,7 @@ public class JbootConfigManager { } } - public void setBootArg(String key, Object value) { + public static void setBootArg(String key, Object value) { if (argMap == null) { argMap = new HashMap<>(); } diff --git a/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java b/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java index 4cf583fb..ce9aa253 100644 --- a/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java +++ b/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java @@ -58,7 +58,7 @@ public class JbootUndertowConfig extends UndertowConfig { //当不配置端口号时,默认为 8080 if (port == null || port.trim().length() == 0) { propExt.getProperties().put(UNDERTOW_PORT, "8080"); - JbootConfigManager.me().setBootArg(UNDERTOW_PORT, "8080"); + JbootConfigManager.setBootArg(UNDERTOW_PORT, "8080"); } //当配置为 -1 或者 * 时,默认为随机端口号 @@ -66,7 +66,7 @@ public class JbootUndertowConfig extends UndertowConfig { Integer availablePort = getAvailablePort(); if (availablePort != null) { propExt.getProperties().put(UNDERTOW_PORT, availablePort.toString()); - JbootConfigManager.me().setBootArg(UNDERTOW_PORT, availablePort.toString()); + JbootConfigManager.setBootArg(UNDERTOW_PORT, availablePort.toString()); } } @@ -80,7 +80,7 @@ public class JbootUndertowConfig extends UndertowConfig { String host = propExt.get(UNDERTOW_HOST); if (host == null || host.trim().length() == 0) { propExt.getProperties().put(UNDERTOW_HOST, "0.0.0.0"); - JbootConfigManager.me().setBootArg(UNDERTOW_HOST, "0.0.0.0"); + JbootConfigManager.setBootArg(UNDERTOW_HOST, "0.0.0.0"); } //当不配置资源路径时,默认为 classpath 下的 webapp -- Gitee From e6c5519276cfb7601767160425e2e40844153347 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 1 Sep 2020 18:56:55 +0800 Subject: [PATCH 0504/1965] fixed: paginate methods in model can not print sql --- .../java/io/jboot/db/dbpro/JbootDbPro.java | 4 +- .../java/io/jboot/db/model/JbootModel.java | 134 ++++++++++++++++++ .../io/jboot/test/db/simple/DbController.java | 17 +++ 3 files changed, 153 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java index 81402978..15948d7c 100644 --- a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java +++ b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java @@ -41,14 +41,14 @@ public class JbootDbPro extends DbPro { @Override - protected List find(Config config, Connection conn, String sql, Object... paras) throws SQLException { + public List find(Config config, Connection conn, String sql, Object... paras) throws SQLException { SqlDebugger.debug(config, sql, paras); return super.find(config, conn, sql, paras); } @Override - protected List query(Config config, Connection conn, String sql, Object... paras) throws SQLException { + public List query(Config config, Connection conn, String sql, Object... paras) throws SQLException { SqlDebugger.debug(config, sql, paras); return super.query(config, conn, sql, paras); } diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index c970259c..80461580 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -19,6 +19,7 @@ import com.jfinal.log.Log; import com.jfinal.plugin.activerecord.*; import com.jfinal.plugin.activerecord.dialect.Dialect; import io.jboot.db.SqlDebugger; +import io.jboot.db.dbpro.JbootDbPro; import io.jboot.db.dialect.JbootDialect; import io.jboot.exception.JbootException; import io.jboot.exception.JbootIllegalConfigException; @@ -27,6 +28,7 @@ import io.jboot.utils.StrUtil; import java.lang.reflect.Array; import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.Statement; import java.util.*; @@ -38,6 +40,7 @@ import java.util.*; public class JbootModel> extends Model { private static final Log LOG = Log.getLog(JbootModel.class); + private static final Object[] NULL_PARA_ARRAY = new Object[0]; private static final String DATASOURCE_CACHE_PREFIX = "__ds__"; private static JbootModelConfig config = JbootModelConfig.getConfig(); @@ -779,4 +782,135 @@ public class JbootModel> extends Model { return (M) this; } + + + //////copy from jfinal model ///// + /** + * Paginate. + * @param pageNumber the page number + * @param pageSize the page size + * @param select the select part of the sql statement + * @param sqlExceptSelect the sql statement excluded select part + * @param paras the parameters of sql + * @return the Page object + */ + @Override + public Page paginate(int pageNumber, int pageSize, String select, String sqlExceptSelect, Object... paras) { + return doPaginate(pageNumber, pageSize, null, select, sqlExceptSelect, paras); + } + + /** + * @see #paginate(int, int, String, String, Object...) + */ + @Override + public Page paginate(int pageNumber, int pageSize, String select, String sqlExceptSelect) { + return doPaginate(pageNumber, pageSize, null, select, sqlExceptSelect, NULL_PARA_ARRAY); + } + + /** + * 指定分页 sql 最外层以是否含有 group by 语句 + *

    +     * 举例:
    +     * paginate(1, 10, true, "select *", "from user where id>? group by age", 123);
    +     * 
    + */ + @Override + public Page paginate(int pageNumber, int pageSize, boolean isGroupBySql, String select, String sqlExceptSelect, Object... paras) { + return doPaginate(pageNumber, pageSize, isGroupBySql, select, sqlExceptSelect, paras); + } + + private Page doPaginate(int pageNumber, int pageSize, Boolean isGroupBySql, String select, String sqlExceptSelect, Object... paras) { + Config config = _getConfig(); + Connection conn = null; + try { + conn = config.getConnection(); + String totalRowSql = "select count(*) " + config.getDialect().replaceOrderBy(sqlExceptSelect); + StringBuilder findSql = new StringBuilder(); + findSql.append(select).append(' ').append(sqlExceptSelect); + return doPaginateByFullSql(config, conn, pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras); + } catch (Exception e) { + throw new ActiveRecordException(e); + } finally { + config.close(conn); + } + } + + private Page doPaginateByFullSql(Config config, Connection conn, int pageNumber, int pageSize, Boolean isGroupBySql, String totalRowSql, StringBuilder findSql, Object... paras) throws Exception { + if (pageNumber < 1 || pageSize < 1) { + throw new ActiveRecordException("pageNumber and pageSize must more than 0"); + } + if (config.getDialect().isTakeOverModelPaginate()) { + return config.getDialect().takeOverModelPaginate(conn, _getUsefulClass(), pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras); + } + +// List result = Db.query(config, conn, totalRowSql, paras); + + JbootDbPro dbPro = (JbootDbPro) Db.use(); + List result = dbPro.query(config,conn,totalRowSql,paras); + + int size = result.size(); + if (isGroupBySql == null) { + isGroupBySql = size > 1; + } + + long totalRow; + if (isGroupBySql) { + totalRow = size; + } else { + totalRow = (size > 0) ? ((Number)result.get(0)).longValue() : 0; + } + if (totalRow == 0) { + return new Page(new ArrayList(0), pageNumber, pageSize, 0, 0); // totalRow = 0; + } + + int totalPage = (int) (totalRow / pageSize); + if (totalRow % pageSize != 0) { + totalPage++; + } + + if (pageNumber > totalPage) { + return new Page(new ArrayList(0), pageNumber, pageSize, totalPage, (int)totalRow); + } + + // -------- + String sql = config.getDialect().forPaginate(pageNumber, pageSize, findSql); + List list = find(config, conn, sql, paras); + return new Page(list, pageNumber, pageSize, totalPage, (int)totalRow); + } + + private Page doPaginateByFullSql(int pageNumber, int pageSize, Boolean isGroupBySql, String totalRowSql, String findSql, Object... paras) { + Config config = _getConfig(); + Connection conn = null; + try { + conn = config.getConnection(); + StringBuilder findSqlBuf = new StringBuilder().append(findSql); + return doPaginateByFullSql(config, conn, pageNumber, pageSize, isGroupBySql, totalRowSql, findSqlBuf, paras); + } catch (Exception e) { + throw new ActiveRecordException(e); + } finally { + config.close(conn); + } + } + + @Override + public Page paginateByFullSql(int pageNumber, int pageSize, String totalRowSql, String findSql, Object... paras) { + return doPaginateByFullSql(pageNumber, pageSize, null, totalRowSql, findSql, paras); + } + + @Override + public Page paginateByFullSql(int pageNumber, int pageSize, boolean isGroupBySql, String totalRowSql, String findSql, Object... paras) { + return doPaginateByFullSql(pageNumber, pageSize, isGroupBySql, totalRowSql, findSql, paras); + } + + + protected List find(Config config, Connection conn, String sql, Object... paras) throws Exception { + SqlDebugger.debug(config,sql,paras); + + try (PreparedStatement pst = conn.prepareStatement(sql)) { + config.getDialect().fillStatement(pst, paras); + try(ResultSet rs = pst.executeQuery()){ + return config.getDialect().buildModelList(rs, _getUsefulClass()); // ModelBuilder.build(rs, getUsefulClass()); + } + } + } } diff --git a/src/test/java/io/jboot/test/db/simple/DbController.java b/src/test/java/io/jboot/test/db/simple/DbController.java index 537201e3..f1316378 100644 --- a/src/test/java/io/jboot/test/db/simple/DbController.java +++ b/src/test/java/io/jboot/test/db/simple/DbController.java @@ -1,6 +1,7 @@ package io.jboot.test.db.simple; import com.jfinal.kit.Ret; +import com.jfinal.plugin.activerecord.Page; import com.jfinal.plugin.activerecord.Record; import io.jboot.app.JbootApplication; import io.jboot.db.JbootDb; @@ -104,6 +105,22 @@ public class DbController extends SuperDbController { } + + + public void find8(){ + List users = JbootDb.use().find("user",Columns.create("login_name",true)); + renderJson(users); + } + + + public void find9(){ + User dao = new User(); + Page page = dao.paginateByColumns(getInt("page",1),10,Columns.create()); + renderJson(page); + } + + + public void del1(){ User dao = new User(); dao.batchDeleteByIds("1",2,3); -- Gitee From e9125d21734af15b410404937a6952c15aaef479 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 2 Sep 2020 11:52:54 +0800 Subject: [PATCH 0505/1965] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E9=94=99=E5=88=AB=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/.vuepress/config.js | 2 +- doc/docs/jfinalConfig.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/.vuepress/config.js b/doc/.vuepress/config.js index 85a5cf3a..2aae683b 100644 --- a/doc/.vuepress/config.js +++ b/doc/.vuepress/config.js @@ -59,7 +59,7 @@ module.exports = { children: [ {title: '安装', path: '/docs/install'}, {title: '配置', path: '/docs/config'}, - {title: 'JFianlConfig', path: '/docs/jfinalConfig'}, + {title: 'JFinalConfig', path: '/docs/jfinalConfig'}, {title: 'MVC', path: '/docs/mvc'}, {title: 'WebSocket', path: '/docs/websocket'}, {title: 'Jwt', path: '/docs/jwt'}, diff --git a/doc/docs/jfinalConfig.md b/doc/docs/jfinalConfig.md index 8bd25f0a..095e72e9 100644 --- a/doc/docs/jfinalConfig.md +++ b/doc/docs/jfinalConfig.md @@ -1,4 +1,4 @@ -# 如何在Jboot中添加自己的 JFianlConfig +# 如何在Jboot中添加自己的 JFinalConfig [[toc]] -- Gitee From 8fdd6948d64716ba22b6492cdaca5e934c4d3c46 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 2 Sep 2020 19:45:29 +0800 Subject: [PATCH 0506/1965] optimize ClassScanner --- .../java/io/jboot/utils/ClassScanner.java | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index ae282d4a..040aa3cd 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -39,6 +39,18 @@ public class ClassScanner { public static final Set scanClasses = new HashSet<>(); public static final Set excludeClasses = new HashSet<>(); +// public static final Set excludeFolderInjar = new HashSet<>(); +// +// static { +// excludeFolderInjar.add("webapp"); +// excludeFolderInjar.add("template"); +// excludeFolderInjar.add("assets"); +// excludeFolderInjar.add("META-INF"); +// excludeFolderInjar.add("schemaorg_apache_xmlbeans"); +// excludeFolderInjar.add("rest-management-private-classpath"); +// } + + public static void addScanJarPrefix(String prefix) { scanJars.add(prefix.trim()); } @@ -511,10 +523,12 @@ public class ClassScanner { Enumeration entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry jarEntry = entries.nextElement(); - String entryName = jarEntry.getName(); - if (!jarEntry.isDirectory() && entryName.endsWith(".class")) { - String className = entryName.replace("/", ".").substring(0, entryName.length() - 6); - addClass(classForName(className)); + if (!jarEntry.isDirectory()) { + String entryName = jarEntry.getName(); + if (entryName.endsWith(".class")) { + String className = entryName.replace("/", ".").substring(0, entryName.length() - 6); + addClass(classForName(className)); + } } } } catch (IOException e1) { @@ -588,7 +602,7 @@ public class ClassScanner { if (!path.toLowerCase().endsWith(".jar")) { classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); } else { - jarPaths.add(path.replace('\\', '/')); + jarPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); } } } @@ -621,14 +635,13 @@ public class ClassScanner { if (path.startsWith("/") && path.indexOf(":") == 2) { path = path.substring(1); } - - if (!path.toLowerCase().endsWith(".jar") && !jarPaths.contains(path)) { - try { + try { + if (!path.toLowerCase().endsWith(".jar") && !jarPaths.contains(path)) { classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); - } catch (IOException e) { + } else { + jarPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); } - } else { - jarPaths.add(path.replace('\\', '/')); + } catch (IOException e) { } } } -- Gitee From add868cb536889f581b0b452b5519036c6a54441 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 3 Sep 2020 11:17:10 +0800 Subject: [PATCH 0507/1965] v3.4.1 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 13 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4d3a3f5b..b829cd8a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.4.0 + 3.4.1 ``` diff --git a/changes.txt b/changes.txt index ee6f3115..8861b048 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.4.1: +修复:在 fatjar 模式下,通过 --jboot.app.mode 启动参数配置应用模式无效的问题 +修复:在 fatjar 模式下,不同位置的 java -jar 启动可能会导致 ClassScanner 扫描 jar 两次拖慢速度的问题 +修复:paginate 分页方法无法正确输出 sql 的问题 + + + jboot v3.4.0: 新增:门户网关 Gateway 新增自定义负载均衡策略的支持 新增:AttachmentContainer 组件,方便自定义把附件上传到其他第三方任何平台 diff --git a/doc/docs/install.md b/doc/docs/install.md index e3ad7170..f8328756 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.4.0 + 3.4.1 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index d9eb8c0e..26b987d0 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.4.0 + 3.4.1 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 37f94e46..afb628a8 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -49,7 +49,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.4.0 + 3.4.1 ``` @@ -107,7 +107,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.4.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.4.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index fc55fb61..a225cbe7 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.4.0"; + public static String VERSION = "3.4.1"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 8285d8a1456c553302b7aee6c500db6e2bf6a28d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 3 Sep 2020 11:17:39 +0800 Subject: [PATCH 0508/1965] v3.4.1 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b1184ba3..d312f94d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.4.1-SNAPSHOT + 3.4.1 jar jboot -- Gitee From 38d6498f20f360cd8e5e5a46da73102b8e639152 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 4 Sep 2020 11:41:08 +0800 Subject: [PATCH 0509/1965] v3.4.2 snapshot --- changes.txt | 2 +- pom.xml | 2 +- src/test/java/io/jboot/test/JavaTester.java | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/changes.txt b/changes.txt index 8861b048..e79d1075 100644 --- a/changes.txt +++ b/changes.txt @@ -1,6 +1,6 @@ jboot v3.4.1: 修复:在 fatjar 模式下,通过 --jboot.app.mode 启动参数配置应用模式无效的问题 -修复:在 fatjar 模式下,不同位置的 java -jar 启动可能会导致 ClassScanner 扫描 jar 两次拖慢速度的问题 +修复:在 fatjar 模式下,不同位置的 java -jar 启动可能会导致 ClassScanner 两次扫描 jar 拖慢启动速度的问题 修复:paginate 分页方法无法正确输出 sql 的问题 diff --git a/pom.xml b/pom.xml index d312f94d..59ceb9d8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.4.1 + 3.4.2-SNAPSHOT jar jboot diff --git a/src/test/java/io/jboot/test/JavaTester.java b/src/test/java/io/jboot/test/JavaTester.java index 7a8fe241..6b2b5217 100644 --- a/src/test/java/io/jboot/test/JavaTester.java +++ b/src/test/java/io/jboot/test/JavaTester.java @@ -6,6 +6,7 @@ public class JavaTester { public static void main(String[] args) { + } -- Gitee From 3083c794c16223567ca439072123da369dca13b7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 7 Sep 2020 12:35:02 +0800 Subject: [PATCH 0510/1965] optimize Configs --- .../java/io/jboot/app/config/ConfigUtil.java | 38 +++++++++++++---- .../jboot/app/config/JbootConfigManager.java | 5 +-- .../io/jboot/app/config/JbootConfigUtil.java | 4 +- .../support/apollo/ApolloConfigManager.java | 12 +++--- .../support/apollo/ApolloServerConfig.java | 4 +- .../support/nacos/NacosConfigManager.java | 4 +- .../support/nacos/NacosServerConfig.java | 42 +++++++++---------- 7 files changed, 66 insertions(+), 43 deletions(-) diff --git a/src/main/java/io/jboot/app/config/ConfigUtil.java b/src/main/java/io/jboot/app/config/ConfigUtil.java index 3e9ddbeb..8ab2b57e 100644 --- a/src/main/java/io/jboot/app/config/ConfigUtil.java +++ b/src/main/java/io/jboot/app/config/ConfigUtil.java @@ -27,13 +27,6 @@ import java.util.concurrent.ConcurrentHashMap; public class ConfigUtil { - public static boolean isBlank(String string) { - return string == null || string.trim().equals(""); - } - - public static boolean isNotBlank(String string) { - return !isBlank(string); - } public static T newInstance(Class clazz) { try { @@ -102,6 +95,37 @@ public class ConfigUtil { return str; } + public static boolean isBlank(String str) { + if (str == null) { + return true; + } + + for (int i = 0, len = str.length(); i < len; i++) { + if (str.charAt(i) > ' ') { + return false; + } + } + return true; + } + + public static boolean isNotBlank(Object str) { + return str == null ? false : !isBlank(str.toString()); + } + + public static boolean areNotBlank(String... strs) { + if (strs == null || strs.length == 0) { + return false; + } + + for (String string : strs) { + if (isBlank(string)) { + return false; + } + } + return true; + } + + private static String rootClassPath; diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 10f9c712..655065e1 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -21,7 +21,6 @@ import com.jfinal.kit.LogKit; import io.jboot.app.config.annotation.ConfigModel; import io.jboot.app.config.support.apollo.ApolloConfigManager; import io.jboot.app.config.support.nacos.NacosConfigManager; -import io.jboot.utils.StrUtil; import java.lang.reflect.Method; import java.util.*; @@ -236,7 +235,7 @@ public class JbootConfigManager { public String getConfigValue(Properties properties, String key) { - if (StrUtil.isBlank(key)) { + if (ConfigUtil.isBlank(key)) { return ""; } String originalValue = getOriginalConfigValue(properties, key); @@ -249,7 +248,7 @@ public class JbootConfigManager { for (ConfigPart cp : configParts) { String value = getConfigValue(properties, cp.getKey()); - value = StrUtil.isNotBlank(value) ? value : cp.getDefaultValue(); + value = ConfigUtil.isNotBlank(value) ? value : cp.getDefaultValue(); stringValue = stringValue.replace(cp.getPartString(), value); } return stringValue; diff --git a/src/main/java/io/jboot/app/config/JbootConfigUtil.java b/src/main/java/io/jboot/app/config/JbootConfigUtil.java index 4def73fa..7fa84bed 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigUtil.java +++ b/src/main/java/io/jboot/app/config/JbootConfigUtil.java @@ -16,11 +16,11 @@ package io.jboot.app.config; import io.jboot.Jboot; -import io.jboot.utils.StrUtil; import org.apache.commons.lang3.StringUtils; import java.util.*; + /** * @author michael yang (fuhai999@gmail.com) * @Date: 2020/3/19 @@ -43,7 +43,7 @@ public class JbootConfigUtil { Properties prop = JbootConfigManager.me().getProperties(); for (Map.Entry entry : prop.entrySet()) { - if (entry.getKey() == null || StrUtil.isBlank(entry.getKey().toString())) { + if (entry.getKey() == null || ConfigUtil.isBlank(entry.getKey().toString())) { continue; } diff --git a/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java b/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java index 891a25b4..3c1c11c5 100644 --- a/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java @@ -18,12 +18,12 @@ package io.jboot.app.config.support.apollo; import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.ConfigService; import com.ctrip.framework.apollo.model.ConfigChange; -import io.jboot.Jboot; +import io.jboot.app.config.ConfigUtil; import io.jboot.app.config.JbootConfigManager; -import io.jboot.utils.StrUtil; import java.util.Set; + /** * @author michael yang (fuhai999@gmail.com) * @Date: 2020/2/8 @@ -43,7 +43,7 @@ public class ApolloConfigManager { return; } - Config config = getDefaultConfig(); + Config config = getDefaultConfig(configManager); Set propNames = config.getPropertyNames(); if (propNames != null && !propNames.isEmpty()) { @@ -64,9 +64,9 @@ public class ApolloConfigManager { } - private Config getDefaultConfig() { - ApolloServerConfig apolloServerConfig = Jboot.config(ApolloServerConfig.class); - if (StrUtil.isNotBlank(apolloServerConfig.getDefaultNamespace())) { + private Config getDefaultConfig(JbootConfigManager configManager) { + ApolloServerConfig apolloServerConfig = configManager.get(ApolloServerConfig.class); + if (ConfigUtil.isNotBlank(apolloServerConfig.getDefaultNamespace())) { return ConfigService.getConfig(apolloServerConfig.getDefaultNamespace()); } else { return ConfigService.getAppConfig(); diff --git a/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java b/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java index 3bbdb7f3..ca86ce32 100644 --- a/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java +++ b/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java @@ -15,8 +15,8 @@ */ package io.jboot.app.config.support.apollo; +import io.jboot.app.config.ConfigUtil; import io.jboot.app.config.annotation.ConfigModel; -import io.jboot.utils.StrUtil; @ConfigModel(prefix = "jboot.config.apollo") public class ApolloServerConfig { @@ -60,6 +60,6 @@ public class ApolloServerConfig { } public boolean isConfigOk() { - return StrUtil.areNotEmpty(appId, meta); + return ConfigUtil.areNotBlank(appId, meta); } } diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java index 4e3f3675..def88225 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java @@ -17,8 +17,8 @@ package io.jboot.app.config.support.nacos; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; +import io.jboot.app.config.ConfigUtil; import io.jboot.app.config.JbootConfigManager; -import io.jboot.utils.StrUtil; import java.io.IOException; import java.io.StringReader; @@ -56,7 +56,7 @@ public class NacosConfigManager { String content = configService.getConfig(nacosServerConfig.getDataId() , nacosServerConfig.getGroup(), 3000); - if (StrUtil.isNotBlank(content)) { + if (ConfigUtil.isNotBlank(content)) { contentProperties = str2Properties(content); if (contentProperties != null) { configManager.setRemoteProperties(contentProperties); diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java b/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java index 720d1f06..e90fb297 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java @@ -16,8 +16,8 @@ package io.jboot.app.config.support.nacos; +import io.jboot.app.config.ConfigUtil; import io.jboot.app.config.annotation.ConfigModel; -import io.jboot.utils.StrUtil; import java.util.Properties; @@ -221,35 +221,35 @@ public class NacosServerConfig { public Properties toProperties() { Properties properties = new Properties(); - putProperties(properties,"isUseCloudNamespaceParsing",isUseCloudNamespaceParsing); - putProperties(properties,"isUseEndpointParsingRule",isUseEndpointParsingRule); - putProperties(properties,"endpoint",endpoint); - putProperties(properties,"endpointPort",endpointPort); - putProperties(properties,"namespace",namespace); - putProperties(properties,"username",username); - putProperties(properties,"password",password); - putProperties(properties,"accessKey",accessKey); - putProperties(properties,"secretKey",secretKey); - putProperties(properties,"ramRoleName",ramRoleName); - putProperties(properties,"serverAddr",serverAddr); - putProperties(properties,"contextPath",contextPath); - putProperties(properties,"clusterName",clusterName); - putProperties(properties,"encode",encode); - putProperties(properties,"configLongPollTimeout",configLongPollTimeout); - putProperties(properties,"configRetryTime",configRetryTime); - putProperties(properties,"maxRetry",maxRetry); - putProperties(properties,"enableRemoteSyncConfig",enableRemoteSyncConfig); + putProperties(properties, "isUseCloudNamespaceParsing", isUseCloudNamespaceParsing); + putProperties(properties, "isUseEndpointParsingRule", isUseEndpointParsingRule); + putProperties(properties, "endpoint", endpoint); + putProperties(properties, "endpointPort", endpointPort); + putProperties(properties, "namespace", namespace); + putProperties(properties, "username", username); + putProperties(properties, "password", password); + putProperties(properties, "accessKey", accessKey); + putProperties(properties, "secretKey", secretKey); + putProperties(properties, "ramRoleName", ramRoleName); + putProperties(properties, "serverAddr", serverAddr); + putProperties(properties, "contextPath", contextPath); + putProperties(properties, "clusterName", clusterName); + putProperties(properties, "encode", encode); + putProperties(properties, "configLongPollTimeout", configLongPollTimeout); + putProperties(properties, "configRetryTime", configRetryTime); + putProperties(properties, "maxRetry", maxRetry); + putProperties(properties, "enableRemoteSyncConfig", enableRemoteSyncConfig); return properties; } private void putProperties(Properties p, String key, String value) { - if (StrUtil.isNotBlank(value)) { + if (value != null && value.trim().length() > 0) { p.put(key, value); } } public boolean isConfigOk() { - return StrUtil.areNotEmpty(serverAddr, dataId, group); + return ConfigUtil.areNotBlank(serverAddr, dataId, group); } } -- Gitee From 049bc45e67164c183b0568a6454f1b150908c4be Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 7 Sep 2020 19:19:46 +0800 Subject: [PATCH 0511/1965] restructure PaginateDirectiveBase --- .../directive/base/PaginateDirectiveBase.java | 83 ++++++++++++++----- 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/jboot/web/directive/base/PaginateDirectiveBase.java b/src/main/java/io/jboot/web/directive/base/PaginateDirectiveBase.java index 82f43caa..28a1d9d6 100644 --- a/src/main/java/io/jboot/web/directive/base/PaginateDirectiveBase.java +++ b/src/main/java/io/jboot/web/directive/base/PaginateDirectiveBase.java @@ -36,6 +36,10 @@ public abstract class PaginateDirectiveBase extends JbootDirectiveBase { private static final String PREVIOUS_TEXT_KEY = "previousText"; private static final String NEXT_TEXT_KEY = "nextText"; private static final String PAGE_ITEMS_NAME_KEY = "pageItemsName"; + private static final String PAGE_DATA_KEY = "pageData"; + private static final String SIBLINGS_ITEM_COUNT_KEY = "siblingsItemCount"; + private static final String START_ITEM_COUNT_KEY = "startItemCount"; + private static final String END_ITEM_COUNT_KEY = "endItemCount"; private static final String DEFAULT_PREVIOUS_CLASS = "previous"; @@ -46,10 +50,15 @@ public abstract class PaginateDirectiveBase extends JbootDirectiveBase { private static final String DEFAULT_PREVIOUS_TEXT = "上一页"; private static final String DEFAULT_NEXT_TEXT = "下一页"; private static final String DEFAULT_PAGE_ITEMS_NAME = "pages"; + private static final String DEFAULT_PAGE_DATA_KEY = "pageData"; private static final String JAVASCRIPT_TEXT = "javascript:;"; private static final String ELLIPSIS_TEXT = "..."; + private static final int SIBLINGS_ITEM_COUNT = 2; + private static final int START_ITEM_COUNT = 1; + private static final int END_ITEM_COUNT = 1; + @Override public void onRender(Env env, Scope scope, Writer writer) { @@ -64,6 +73,25 @@ public abstract class PaginateDirectiveBase extends JbootDirectiveBase { String nextText = getPara(NEXT_TEXT_KEY, scope, DEFAULT_NEXT_TEXT); String pageItemsName = getPara(PAGE_ITEMS_NAME_KEY, scope, DEFAULT_PAGE_ITEMS_NAME); + String pageDataKey = getPara(PAGE_DATA_KEY, scope, DEFAULT_PAGE_DATA_KEY); + + int siblingsItemCount = getParaToInt(SIBLINGS_ITEM_COUNT_KEY, scope, SIBLINGS_ITEM_COUNT); + if (siblingsItemCount < 1) { + siblingsItemCount = SIBLINGS_ITEM_COUNT; + } + + + int startItemCount = getParaToInt(START_ITEM_COUNT_KEY, scope, START_ITEM_COUNT); + if (startItemCount < 1) { + startItemCount = START_ITEM_COUNT; + } + + + int endItemCount = getParaToInt(END_ITEM_COUNT_KEY, scope, END_ITEM_COUNT); + if (endItemCount < 1) { + endItemCount = END_ITEM_COUNT; + } + Page page = getPage(env, scope, writer); @@ -74,20 +102,13 @@ public abstract class PaginateDirectiveBase extends JbootDirectiveBase { return; } - int startPage = currentPage - 4; + int startPage = currentPage - siblingsItemCount; if (startPage < 1) { startPage = 1; } - int endPage = currentPage + 4; - if (endPage > totalPage) { - endPage = totalPage; - } - if (currentPage <= 8) { - startPage = 1; - } - - if ((totalPage - currentPage) < 8) { + int endPage = currentPage + siblingsItemCount; + if (endPage > totalPage) { endPage = totalPage; } @@ -98,13 +119,22 @@ public abstract class PaginateDirectiveBase extends JbootDirectiveBase { pages.add(new PaginateDirectiveBase.PaginateItem(previousClass, getUrl(currentPage - 1, env, scope, writer), previousText)); } - if (currentPage > 8 && !onlyShowPreviousAndNext) { - pages.add(new PaginateDirectiveBase.PaginateItem(StrUtil.EMPTY, getUrl(1, env, scope, writer), 1)); - pages.add(new PaginateDirectiveBase.PaginateItem(StrUtil.EMPTY, getUrl(2, env, scope, writer), 2)); - pages.add(new PaginateDirectiveBase.PaginateItem(disabledClass, JAVASCRIPT_TEXT, ELLIPSIS_TEXT)); - } - if (!onlyShowPreviousAndNext) { + + //开始页码 + for (int i = 1; i <= startItemCount; i++) { + if (i < currentPage - siblingsItemCount) { + pages.add(new PaginateDirectiveBase.PaginateItem(StrUtil.EMPTY, getUrl(i, env, scope, writer), i)); + } + } + + //省略号 + if (currentPage > startItemCount + siblingsItemCount + 1) { + pages.add(new PaginateDirectiveBase.PaginateItem(disabledClass, JAVASCRIPT_TEXT, ELLIPSIS_TEXT)); + } + + + //中间页码 for (int i = startPage; i <= endPage; i++) { if (currentPage == i) { pages.add(new PaginateDirectiveBase.PaginateItem(activeClass, JAVASCRIPT_TEXT, i)); @@ -112,14 +142,23 @@ public abstract class PaginateDirectiveBase extends JbootDirectiveBase { pages.add(new PaginateDirectiveBase.PaginateItem(StrUtil.EMPTY, getUrl(i, env, scope, writer), i)); } } - } - if ((totalPage - currentPage) >= 8 && !onlyShowPreviousAndNext) { - pages.add(new PaginateDirectiveBase.PaginateItem(disabledClass, JAVASCRIPT_TEXT, ELLIPSIS_TEXT)); - pages.add(new PaginateDirectiveBase.PaginateItem(StrUtil.EMPTY, getUrl(totalPage - 1, env, scope, writer), totalPage - 1)); - pages.add(new PaginateDirectiveBase.PaginateItem(StrUtil.EMPTY, getUrl(totalPage, env, scope, writer), totalPage)); + + //省略号 + if (currentPage < totalPage - siblingsItemCount - endItemCount) { + pages.add(new PaginateDirectiveBase.PaginateItem(disabledClass, JAVASCRIPT_TEXT, ELLIPSIS_TEXT)); + } + + //后边页码 + for (int i = (endItemCount - 1); i >= 0; i--) { + if (i < totalPage - (currentPage + siblingsItemCount)) { + pages.add(new PaginateDirectiveBase.PaginateItem(StrUtil.EMPTY, getUrl(totalPage - i, env, scope, writer), totalPage - i)); + } + + } } + if (currentPage == totalPage) { pages.add(new PaginateDirectiveBase.PaginateItem(nextClass + StrUtil.SPACE + disabledClass, JAVASCRIPT_TEXT, nextText)); } else { @@ -127,6 +166,8 @@ public abstract class PaginateDirectiveBase extends JbootDirectiveBase { } scope.setLocal(pageItemsName, pages); + scope.setLocal(pageDataKey, page); + renderBody(env, scope, writer); } -- Gitee From 90411110d1aa1570064839235cd12a1274354f24 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 7 Sep 2020 19:24:23 +0800 Subject: [PATCH 0512/1965] v3.4.2 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b829cd8a..f72aa41e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.4.1 + 3.4.2 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index f8328756..92a9d7dc 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.4.1 + 3.4.2 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 26b987d0..77ab7c11 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.4.1 + 3.4.2 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index afb628a8..3d141f57 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -49,7 +49,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.4.1 + 3.4.2 ``` @@ -107,7 +107,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.4.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.4.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index a225cbe7..3edcab0e 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.4.1"; + public static String VERSION = "3.4.2"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 4bc620b931cfb7dbe7ddd091cc45a9f3354d3e2d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 7 Sep 2020 19:24:50 +0800 Subject: [PATCH 0513/1965] v3.4.2 release (^.^)YYa!! --- changes.txt | 6 ++++++ pom.xml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index e79d1075..ddbc3b2f 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.4.2: +修复:当使用 Apollo 配置中心时,在某些场景下会导致 devMode 判断不正确的问题 +优化:重构 PaginateDirectiveBase,使之显示更加 "人性化" 和支持更多的功能配置 + + + jboot v3.4.1: 修复:在 fatjar 模式下,通过 --jboot.app.mode 启动参数配置应用模式无效的问题 修复:在 fatjar 模式下,不同位置的 java -jar 启动可能会导致 ClassScanner 两次扫描 jar 拖慢启动速度的问题 diff --git a/pom.xml b/pom.xml index 59ceb9d8..e367f64e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.4.2-SNAPSHOT + 3.4.2 jar jboot -- Gitee From 80c2df550ee4df0d31089fece9984957b366a7d3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 8 Sep 2020 12:31:22 +0800 Subject: [PATCH 0514/1965] update docs --- doc/.vuepress/config.js | 37 +++++++++++++++++++++++++--- doc/.vuepress/styles/index.styl | 3 ++- doc/docs/readme.md | 33 +++++++++++++++++++++++++ doc/jbootadmin/buy.md | 3 +++ doc/jbootadmin/db.md | 3 +++ doc/jbootadmin/feature.md | 32 ++++++++++++++++++++++++ doc/jbootadmin/front.md | 3 +++ doc/jbootadmin/menu.md | 3 +++ doc/jbootadmin/permission.md | 3 +++ doc/jbootadmin/readme.md | 7 ++++++ doc/jbootadmin/safety_precautions.md | 3 +++ doc/jbootadmin/start.md | 3 +++ doc/readme.md | 2 +- 13 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 doc/jbootadmin/buy.md create mode 100644 doc/jbootadmin/db.md create mode 100644 doc/jbootadmin/feature.md create mode 100644 doc/jbootadmin/front.md create mode 100644 doc/jbootadmin/menu.md create mode 100644 doc/jbootadmin/permission.md create mode 100644 doc/jbootadmin/readme.md create mode 100644 doc/jbootadmin/safety_precautions.md create mode 100644 doc/jbootadmin/start.md diff --git a/doc/.vuepress/config.js b/doc/.vuepress/config.js index 2aae683b..5f324faa 100644 --- a/doc/.vuepress/config.js +++ b/doc/.vuepress/config.js @@ -33,7 +33,9 @@ module.exports = { nav: [ {text: '首页', link: '/'}, - {text: '提问', link: 'https://gitee.com/JPressProjects/jpress/issues'}, + {text: 'Jboot文档', link: '/docs/'}, + {text: 'JbootAdmin', link: '/jbootadmin/'}, + {text: '提问', link: 'https://gitee.com/JbootProjects/jboot/issues'}, {text: 'JPress', link: 'http://www.jpress.io'}, { text: '源码下载', items: [ @@ -44,11 +46,11 @@ module.exports = { ], sidebar: { - '/': [{ + '/docs/': [{ title: '认识 Jboot', collapsable: false, children: [ - {title: 'Jboot 简介', path: '/'}, + {title: 'Jboot 简介', path: '/docs/'}, {title: '快速开始', path: '/docs/start'} ], }, @@ -72,7 +74,8 @@ module.exports = { {title: 'MQ 消息队列', path: '/docs/mq'}, {title: 'Gateway 网关', path: '/docs/gateway'}, {title: '任务调度', path: '/docs/schedule'}, - {title: '限流', path: '/docs/limit'}, + {title: 'Jboot限流', path: '/docs/limit'}, + {title: 'Sentinel限流', path: '/docs/sentinel'}, {title: '监控', path: '/docs/metrics'}, {title: '事件机制', path: '/docs/event'}, {title: '序列化', path: '/docs/serialize'}, @@ -81,6 +84,8 @@ module.exports = { {title: '项目构建', path: '/docs/build'}, {title: '项目部署', path: '/docs/deploy'}, {title: 'Docker', path: '/docs/docker'}, + {title: '热加载', path: '/docs/hotload'}, + {title: 'Swagger', path: '/docs/swagger'}, ], }, @@ -92,6 +97,30 @@ module.exports = { ], } ], + + + '/jbootadmin/': [{ + title: '认识 JbootAdmin', + collapsable: false, + children: [ + {title: '简介', path: '/jbootadmin/'}, + {title: '功能介绍', path: '/jbootadmin/feature'}, + {title: '我要购买', path: '/jbootadmin/buy'} + ], + }, + { + title: '开发文档', + collapsable: false, + children: [ + {title: '开始', path: '/jbootadmin/start'}, + {title: '数据库设计', path: '/jbootadmin/db'}, + {title: '后台菜单', path: '/jbootadmin/menu'}, + {title: '权限设计', path: '/jbootadmin/permission'}, + {title: '前端组件', path: '/jbootadmin/front'}, + {title: '安全防护', path: '/jbootadmin/safety_precautions'}, + ], + }, + ] }, sidebarDepth: 1 }, diff --git a/doc/.vuepress/styles/index.styl b/doc/.vuepress/styles/index.styl index 16ddd550..0dd1d912 100644 --- a/doc/.vuepress/styles/index.styl +++ b/doc/.vuepress/styles/index.styl @@ -16,7 +16,8 @@ padding: 0rem 1.5rem !important; } -#app .theme-reco-content.content__default > h1, #app .theme-reco-content.content__default > p:nth-child(2) { + //, #app .theme-reco-content.content__default > p:nth-child(2) +#app .theme-reco-content.content__default > h1{ // 处理文档的第一行标题 display: none !important; } diff --git a/doc/docs/readme.md b/doc/docs/readme.md index fc672652..90522646 100644 --- a/doc/docs/readme.md +++ b/doc/docs/readme.md @@ -1,3 +1,36 @@ +# Jboot 简介 + +[[toc]] + +## 简介 + +Jboot 是一个基于 JFinal、JFinal-Undertow、Dubbo、Seata、Sentinel、ShardingSphere、Nacos 等开发的微服务框架, +帮助开发者降低微服务开发门槛。同时完美支持在 idea、eclipse 下多 maven 模块,对 java 代码、html、css、js 等资源文件进行热加载。爽爽开发,快乐生活。 + + + +## 特点 + +目前已经开源超过了 3 年的时间,迭代了 100+ 个版本,已经被超过 1000+ 公司在使用。 + +Jboot 主要有以下特征: + +- 1、基于 JFinal 的 MVC + ORM 快速开发。 +- 2、基于 ShardingSphere + Seata 分布式事务 和 分库分表。 +- 3、基于 Dubbo 或 Motan 的 RPC 实现 +- 4、基于 Sentinel 的分布式限流和降级 +- 5、基于 Apollo 和 Nacos 的分布式配置中心 +- 6、基于 EhCache 和 Redis 的分布式二级缓存 + +## 微信群 + +![](./static/images/jboot-wechat-group.png) + +## QQ群 + +群1: 601440615(已满) +群2: 719614554 + ## 文档目录 - [安装](install.md) diff --git a/doc/jbootadmin/buy.md b/doc/jbootadmin/buy.md new file mode 100644 index 00000000..edc01cc2 --- /dev/null +++ b/doc/jbootadmin/buy.md @@ -0,0 +1,3 @@ +# 购买 JbootAdmin + +购买 JbootAdmin 请联系海哥微信:wx198819880 \ No newline at end of file diff --git a/doc/jbootadmin/db.md b/doc/jbootadmin/db.md new file mode 100644 index 00000000..4164a9c1 --- /dev/null +++ b/doc/jbootadmin/db.md @@ -0,0 +1,3 @@ +# JbootAdmin 数据库设计 + +购买后获得阅读权限。 \ No newline at end of file diff --git a/doc/jbootadmin/feature.md b/doc/jbootadmin/feature.md new file mode 100644 index 00000000..93243a77 --- /dev/null +++ b/doc/jbootadmin/feature.md @@ -0,0 +1,32 @@ +# JbootAdmin 功能介绍 + + +- 1、服务器管理(方便我们查看分布式服务器的健康情况) + +- 2、分布式缓存管理(方便我们对分布式服务进行管理和同步) + +- 3、配置中心进行管理,RPC服务管理 + +- 4、门户网关 Gateway 路由管理 + +- 5、CDN管理,通过配置可以对网站静态资源进行加速 + +- 6、在线执行服务方法 + +- 7、精确到方法级别的性能监控 + +- 8、插件化热修复 + +方便在线紧急情况下,在线对 Bug 进行修复。或者对逻辑进行在线修改。 + +- 9、分布式文件同步 + +当你把你的应用部署在多个机器的时候,JbootAdmin 自带了分布式的文件同步功能,用户上传文件或者进行文件删除时,可以通过 JbootAdmin 进行配置让所有需要的机器(或者机器的指定目录)进行文件同步。 + +- 10、自动扩缩容 + +基于阿里云服务器 ECS、或者腾讯云服务器 CVS、或者 K8S 进行自动扩缩容管理配置,方便当我们的系统在高并发的时候,自动去帮我们开通机器、自动帮我们进行部署以应对高并发的流量。同时在流量减少的时候,又会自动帮我们去删除服务器而减少资金的浪费。 + +- 11、异地多活 + +方便我们把我们的程序部署在多个不同的机房里,就算因为运营商的问题导致机房出现问题,而不会影响的产品的正常服务。 \ No newline at end of file diff --git a/doc/jbootadmin/front.md b/doc/jbootadmin/front.md new file mode 100644 index 00000000..a55d4611 --- /dev/null +++ b/doc/jbootadmin/front.md @@ -0,0 +1,3 @@ +# 前端组件 + +购买后获得阅读权限。 \ No newline at end of file diff --git a/doc/jbootadmin/menu.md b/doc/jbootadmin/menu.md new file mode 100644 index 00000000..086f891f --- /dev/null +++ b/doc/jbootadmin/menu.md @@ -0,0 +1,3 @@ +# 后台菜单 + +购买后获得阅读权限。 \ No newline at end of file diff --git a/doc/jbootadmin/permission.md b/doc/jbootadmin/permission.md new file mode 100644 index 00000000..eded1673 --- /dev/null +++ b/doc/jbootadmin/permission.md @@ -0,0 +1,3 @@ +# 权限设计 + +购买后获得阅读权限。 \ No newline at end of file diff --git a/doc/jbootadmin/readme.md b/doc/jbootadmin/readme.md new file mode 100644 index 00000000..95bd0fa1 --- /dev/null +++ b/doc/jbootadmin/readme.md @@ -0,0 +1,7 @@ +# 关于 JbootAdmin + +JbootAdmin 是 Jboot 作者海哥基于 Jboot 开发的一个功能强大的快速开发框架,JbootAdmin 是一个真正的微服务框架平台, +但是你却不会为此付出更多的开发时间和开发成本,它开发起来就跟开发单体项目是一模一样的,而且你还可以打包成为war、fatjar、undertow、微服务等不同的部署项目。 + + +不过,JbootAdmin 是商业付费的,关于更多的 JbootAdmin,你可以通过 [功能介绍](./feature) 来进一步了解。 \ No newline at end of file diff --git a/doc/jbootadmin/safety_precautions.md b/doc/jbootadmin/safety_precautions.md new file mode 100644 index 00000000..dce02043 --- /dev/null +++ b/doc/jbootadmin/safety_precautions.md @@ -0,0 +1,3 @@ +# 安全防护 + +购买后获得阅读权限。 \ No newline at end of file diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md new file mode 100644 index 00000000..7ef3f73a --- /dev/null +++ b/doc/jbootadmin/start.md @@ -0,0 +1,3 @@ +# 快速开始 + +购买后获得阅读权限。 \ No newline at end of file diff --git a/doc/readme.md b/doc/readme.md index 5a286745..aa7fd379 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -29,4 +29,4 @@ Jboot 主要有以下特征: ## QQ群 群1: 601440615(已满) -群2: 719614554 \ No newline at end of file +群2: 719614554 -- Gitee From 41888b42c0c2be15f6ab46cfb43f2dd012526150 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 10 Sep 2020 11:34:35 +0800 Subject: [PATCH 0515/1965] v3.4.3 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e367f64e..45b5195f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.4.2 + 3.4.3-SNAPSHOT jar jboot -- Gitee From 16cc8452c05a9d94d9c8aa9290e7f5fe89378a5c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 10 Sep 2020 11:35:17 +0800 Subject: [PATCH 0516/1965] add override feature for annotation @JFinalDirective --- src/main/java/io/jboot/core/JbootCoreConfig.java | 4 ++++ .../io/jboot/web/directive/annotation/JFinalDirective.java | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 7d1aae53..91374bfb 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -210,6 +210,10 @@ public class JbootCoreConfig extends JFinalConfig { for (Class clazz : directiveClasses) { JFinalDirective directive = (JFinalDirective) clazz.getAnnotation(JFinalDirective.class); if (directive != null) { + if (directive.override()){ + //remove old directive + engine.removeDirective(directive.value()); + } engine.addDirective(AnnotationUtil.get(directive.value()), clazz); } diff --git a/src/main/java/io/jboot/web/directive/annotation/JFinalDirective.java b/src/main/java/io/jboot/web/directive/annotation/JFinalDirective.java index ce8d25a0..2da38649 100644 --- a/src/main/java/io/jboot/web/directive/annotation/JFinalDirective.java +++ b/src/main/java/io/jboot/web/directive/annotation/JFinalDirective.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -20,9 +20,11 @@ import java.lang.annotation.*; @Inherited @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE }) +@Target({ElementType.TYPE}) public @interface JFinalDirective { String value(); + boolean override() default false; + } -- Gitee From e367c05bd9a6ff1c07478e19f59d60d5210801cb Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 10 Sep 2020 11:42:01 +0800 Subject: [PATCH 0517/1965] optimize AttachmentManager --- .../web/attachment/AttachmentManager.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index f2872d8d..a5ac2654 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -139,7 +139,27 @@ public class AttachmentManager { * @return */ public File getFile(String relativePath) { - return getDefaultContainer().getFile(relativePath); + + //优先从 默认的 container 去获取 + File file = getDefaultContainer().getFile(relativePath); + if (file != null && file.exists()) { + return file; + } + + for (Map.Entry entry : containerMap.entrySet()) { + if (!DEFAULT_KEY.equals(entry.getKey())) { + AttachmentContainer container = entry.getValue(); + try { + file = container.getFile(relativePath); + if (file != null && file.exists()) { + return file; + } + } catch (Exception ex) { + LOG.error("get file error in container :" + container, ex); + } + } + } + return null; } /** -- Gitee From d5c27c876c3222f366b9a5c2594d0aa3a3cc36fd Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 10 Sep 2020 12:39:03 +0800 Subject: [PATCH 0518/1965] optimize AttachmentManager --- .../web/attachment/AttachmentManager.java | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index a5ac2654..0793f4e3 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -94,19 +94,23 @@ public class AttachmentManager { * @return 返回文件的相对路径 */ public String saveFile(File file) { - String retString = null; + AttachmentContainer defaultContainer = getDefaultContainer(); + + //优先从 默认的 container 去保存文件 + String relativePath = defaultContainer.saveFile(file); + File defaultContainerFile = defaultContainer.getFile(relativePath); + for (Map.Entry entry : containerMap.entrySet()) { AttachmentContainer container = entry.getValue(); try { - String result = container.saveFile(file); - if (DEFAULT_KEY.equals(entry.getKey())) { - retString = result; + if (container != defaultContainer) { + container.saveFile(defaultContainerFile); } } catch (Exception ex) { - LOG.error("save file error in container :" + container, ex); + LOG.error("get file error in container :" + container, ex); } } - return retString; + return relativePath; } @@ -140,23 +144,25 @@ public class AttachmentManager { */ public File getFile(String relativePath) { + AttachmentContainer defaultContainer = getDefaultContainer(); + //优先从 默认的 container 去获取 - File file = getDefaultContainer().getFile(relativePath); + File file = defaultContainer.getFile(relativePath); if (file != null && file.exists()) { return file; } for (Map.Entry entry : containerMap.entrySet()) { - if (!DEFAULT_KEY.equals(entry.getKey())) { - AttachmentContainer container = entry.getValue(); - try { + AttachmentContainer container = entry.getValue(); + try { + if (container != defaultContainer) { file = container.getFile(relativePath); if (file != null && file.exists()) { return file; } - } catch (Exception ex) { - LOG.error("get file error in container :" + container, ex); } + } catch (Exception ex) { + LOG.error("get file error in container :" + container, ex); } } return null; -- Gitee From 4070dae2fea1bfaecc971c4041a433b9f0d360fa Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 10 Sep 2020 12:43:46 +0800 Subject: [PATCH 0519/1965] v3.4.3 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f72aa41e..19407874 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.4.2 + 3.4.3 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 92a9d7dc..75c48c95 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.4.2 + 3.4.3 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 77ab7c11..50f138cc 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.4.2 + 3.4.3 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 3d141f57..4aa64564 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -49,7 +49,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.4.2 + 3.4.3 ``` @@ -107,7 +107,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.4.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.4.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 -- Gitee From 8c11768f722c7c907e01c641faabdbd3767fc40f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 10 Sep 2020 12:44:25 +0800 Subject: [PATCH 0520/1965] v3.4.3 release (^.^)YYa!! --- changes.txt | 6 ++++++ pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/changes.txt b/changes.txt index ddbc3b2f..3f2dffc6 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.4.3: +新增:指令 @JFinalDirective 新增 override 配置,用于覆盖系统已经内置的指令 +优化:优化 AttachmentManager,使之更加方便的上传 获取文件 + + + jboot v3.4.2: 修复:当使用 Apollo 配置中心时,在某些场景下会导致 devMode 判断不正确的问题 优化:重构 PaginateDirectiveBase,使之显示更加 "人性化" 和支持更多的功能配置 diff --git a/pom.xml b/pom.xml index 45b5195f..dc3cc344 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.4.3-SNAPSHOT + 3.4.3 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 3edcab0e..87f32df6 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.4.2"; + public static String VERSION = "3.4.3"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 2d82a1f11ce2a2445da06024df685bc30d7176e4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 12 Sep 2020 17:40:49 +0800 Subject: [PATCH 0521/1965] add attachment docs --- doc/.vuepress/config.js | 1 + doc/docs/attachment.md | 323 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 324 insertions(+) create mode 100644 doc/docs/attachment.md diff --git a/doc/.vuepress/config.js b/doc/.vuepress/config.js index 5f324faa..52c0f10c 100644 --- a/doc/.vuepress/config.js +++ b/doc/.vuepress/config.js @@ -76,6 +76,7 @@ module.exports = { {title: '任务调度', path: '/docs/schedule'}, {title: 'Jboot限流', path: '/docs/limit'}, {title: 'Sentinel限流', path: '/docs/sentinel'}, + {title: '分布式附件管理', path: '/docs/attachment'}, {title: '监控', path: '/docs/metrics'}, {title: '事件机制', path: '/docs/event'}, {title: '序列化', path: '/docs/serialize'}, diff --git a/doc/docs/attachment.md b/doc/docs/attachment.md new file mode 100644 index 00000000..e08d848c --- /dev/null +++ b/doc/docs/attachment.md @@ -0,0 +1,323 @@ +# Attachment 附件管理 + +Jboot 定位是分布式的开发系统,在项目进行分布式部署的时候,用户在上传文件时,我们需要对文件进行分布式同步也是必须的。 + +在分布式部署的需求下,我们假设我们把我们的应用部署在 A/B/C 三台服务器上,这三台服务器通过 nginx 或者 SLB 等做负载均衡。 + +此时,也就意味着当用户每次访问我们的应用的时候,可能访问到了 A 服务器,也有可能访问到 B 服务器 或者 C 服务器。 + +当我们的应用假设有一个图片上传的功能,用户在上传图片的时候,假设上传到了 A 服务器,但是第二次去访问附件的时候,可能访问到了 B 服务器,但是 B 服务器却不存在 A 用户刚刚上传的图片,Jboot 的 AttachmentContainer 就是为了解决这一系列问题而存在的。 + +在使用 AttachmentContainer 之前,我们先来了解下以下的几个概念。 + +- AttachmentContainer : 附件容器,就是专门用来存放图片、读取图片和渲染http请求图片的。 +- AttachmentManager : 用来管理 AttachmentContainer 的,一个应用里可以有多个 AttachmentContainer 。比如,阿里云OSS存储的,我们可以来定义一个 AliyunOSSAttachmentContainer; fastDFS 存储我们一样可以来定义一个 FastDFSAttachmentContainer;同时,Attachment 内置了一个默认的 AttachmentContainer,用来存在 ”本地“ 附件的。 + +在使用 AttachmentContainer 之前,我们需要需要编写自己的一个类,来实现 AttachmentContainer 接口,并添加到 AttachmentManager 里去。 + +``` +AliyunOssAttachmentContainer aliyunOss = new AliyunOssAttachmentContainer(); + +AttachmentManager.me().addContainer("containerName",aliyunOss); +``` + +当我们的 Controller 有文件上传的时候,我们需要调用 AttachmentManager 进行保存,AttachmentManager 最终会保持到其所有的容器里去。 + +例如: + +``` +public void upload() { + if (!isMultipartRequest()) { + renderError(404); + return; + } + + UploadFile uploadFile = getFile(); + if (uploadFile == null) { + renderJson(Ret.fail().set("message", "请选择要上传的文件")); + return; + } + + //通过 AttachmentManager 去保存文件 + String relativePath = AttachmentManager.me().saveFile(file); + file.delete(); + + renderJson(Ret.ok().set("success", true).set("src", relativePath)); +} +``` + +通过 `AttachmentManager.me().saveFile(file);` 保存文件,AttachmentManager 会保持到所有的容器里。 + +当需要读取文件的时候,我们也可以通过 AttachmentManger 去读文件。 + +``` +File attachment = AttachmentManager.me().getFile(relativePath); +``` + +`AttachmentManager.me().getFile(relativePath);` 读取文件的时候,会优先从 默认 的 ”容器“ 去读取,当默认 ”容器“ 不存在该文件的时候,AttachmentManager 会遍历所有的 Container ,直到读到为此。 + + +一下是 `AttachmentManager.me().getFile()` 代码的实现逻辑: + +``` +public File getFile(String relativePath) { + + AttachmentContainer defaultContainer = getDefaultContainer(); + + //优先从 默认的 container 去获取 + File file = defaultContainer.getFile(relativePath); + if (file != null && file.exists()) { + return file; + } + + for (Map.Entry entry : containerMap.entrySet()) { + AttachmentContainer container = entry.getValue(); + try { + if (container != defaultContainer) { + file = container.getFile(relativePath); + if (file != null && file.exists()) { + return file; + } + } + } catch (Exception ex) { + LOG.error("get file error in container :" + container, ex); + } + } + return null; +} +``` + +一下是阿里云 Oss 的代码实现逻辑,可供参考: + +``` +public class AliyunOssAttachmenetContainer implements AttachmentContainer { + + private String basePath = PathKit.getWebRootPath(); + private AliyunOssAttachmentConfig config = JbootConfigManager.me().get(AliyunOssAttachmentConfig.class); + private static ExecutorService fixedThreadPool = NamedThreadPools.newFixedThreadPool(3, "aliyun-oss-upload"); + + + public AliyunOssAttachmenetContainer() { + } + + @Override + public String saveFile(File file) { + if (!config.isEnable()) { + return null; + } + String relativePath = getRelativePath(file); + fixedThreadPool.execute(() -> { + upload(relativePath, file); + }); + return relativePath; + } + + + @Override + public boolean deleteFile(String relativePath) { + if (!config.isEnable()) { + return false; + } + relativePath = removeFirstFileSeparator(relativePath); + OSSClient ossClient = createOSSClient(); + try { + ossClient.deleteObject(config.getBucketname(), relativePath); + } catch (Exception ex) { + LogKit.error(ex.toString(), ex); + } finally { + ossClient.shutdown(); + } + return true; + } + + + @Override + public File getFile(String relativePath) { + if (!config.isEnable()){ + return null; + } + File localFile = new File(basePath, relativePath); + if (localFile.exists()) { + return localFile; + } + if (download(relativePath, localFile)) { + return localFile; + } + return null; + } + + + @Override + public String getRelativePath(File file) { + return FileUtil.removePrefix(file.getAbsolutePath(), basePath); + } + + + @Override + public boolean isRemoteContainer() { + return true; + } + + + @Override + public boolean matchFile(String target, HttpServletRequest request) { + return false; + } + + + @Override + public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { + return false; + } + + + /** + * 同步本地文件到阿里云OSS + * + * @param path + * @param file + * @return + */ + public boolean upload(String path, File file) { + if (StrUtil.isBlank(path)) { + return false; + } + + path = removeFirstFileSeparator(path); + path = path.replace('\\', '/'); + + String ossBucketName = config.getBucketname(); + OSSClient ossClient = createOSSClient(); + + try { + ossClient.putObject(ossBucketName, path, file); + boolean success = ossClient.doesObjectExist(ossBucketName, path); + if (!success) { + LogKit.error("aliyun oss upload error! path:" + path + "\nfile:" + file); + } + return success; + + } catch (Throwable e) { + LogKit.error("aliyun oss upload error!!!", e); + return false; + } finally { + ossClient.shutdown(); + } + } + + /** + * 如果文件以 / 或者 \ 开头,去除 / 或 \ 符号 + */ + private static String removeFirstFileSeparator(String path) { + while (path.startsWith("/") || path.startsWith("\\")) { + path = path.substring(1); + } + return path; + } + + /** + * 下载 阿里云 OSS 到本地 + * + * @param path + * @param toFile + * @return + */ + public boolean download(String path, File toFile) { + if (StrUtil.isBlank(path)) { + return false; + } + path = removeFirstFileSeparator(path); + OSSClient ossClient = createOSSClient(); + try { + if (!toFile.getParentFile().exists()) { + toFile.getParentFile().mkdirs(); + } + + if (!toFile.exists()) { + toFile.createNewFile(); + } + ossClient.getObject(new GetObjectRequest(config.getBucketname(), path), toFile); + return true; + } catch (Throwable e) { + LogKit.error("aliyun oss download error!!! path:" + path + " toFile:" + toFile, e); + if (toFile.exists()) { + toFile.delete(); + } + return false; + } finally { + ossClient.shutdown(); + } + } + + + private OSSClient createOSSClient() { + String endpoint = config.getEndpoint(); + String accessId = config.getAccessKeyId(); + String accessKey = config.getAccessKeySecret(); + return new OSSClient(endpoint, new DefaultCredentialProvider(accessId, accessKey), null); + } + + +} +``` + +``` +@ConfigModel(prefix = "aliyunoss") +public class AliyunOssAttachmentConfig { + + private boolean enable = false; + private String endpoint; + private String accessKeyId; + private String accessKeySecret; + private String bucketname; + private boolean delSync; + + public boolean isEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public String getAccessKeyId() { + return accessKeyId; + } + + public void setAccessKeyId(String accessKeyId) { + this.accessKeyId = accessKeyId; + } + + public String getAccessKeySecret() { + return accessKeySecret; + } + + public void setAccessKeySecret(String accessKeySecret) { + this.accessKeySecret = accessKeySecret; + } + + public String getBucketname() { + return bucketname; + } + + public void setBucketname(String bucketname) { + this.bucketname = bucketname; + } + + public boolean isDelSync() { + return delSync; + } + + public void setDelSync(boolean delSync) { + this.delSync = delSync; + } +} +``` \ No newline at end of file -- Gitee From 265d552456148e77b7a97b3ba51624ac6527b0db Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 13 Sep 2020 15:31:08 +0800 Subject: [PATCH 0522/1965] update docs --- doc/docs/config.md | 14 +++-- doc/docs/jwt.md | 8 +-- doc/docs/metrics.md | 50 ++++++++++++------ doc/docs/static/images/jboot-wechat-group.png | Bin 16106 -> 13278 bytes 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/doc/docs/config.md b/doc/docs/config.md index ebeb333e..ff01250c 100644 --- a/doc/docs/config.md +++ b/doc/docs/config.md @@ -13,7 +13,6 @@ - 开启 Nacos 分布式配置中心 - 开启 Apollo 分布式配置中心 - 配置内容加密解密 -- 设计原因 - 常见问题 - Jboot所有配置参考 @@ -22,13 +21,17 @@ 在 Jboot 应用中,可以通过几下几种方式给 Jboot 应用进行配置。 - jboot.properties 配置文件 +- jboot-xxx.properties 配置文件 - 环境变量 - Jvm 系统属性 - 启动参数 - 分布式配置中心(目前支持 Apollo 和 Nacos) -> 注意:如果同一个属性被多处配置,那么 Jboot 读取配置的优先顺序是: -> `分布式配置中心` > `启动参数` > `Jvm 系统属性` > `环境变量` > `jboot.properties 配置` +> 注意: +> 1、如果同一个属性被多处配置,那么 Jboot 读取配置的优先顺序是: +> `分布式配置中心` > `启动参数` > `Jvm 系统属性` > `环境变量` > `jboot-xxx.properties` > `jboot.properties`。 +> +> 2、jboot-xxx.properties 的含义是:当配置 jboot.app.mode=dev 时,默认去读取 jboot-dev.properties,同理当配置 jboot.app.mode=product 时,默认去读取 jboot-product.properties,jboot-xxx.properties 的文件名称是来源于 jboot.app.mode 的配置。jboot-xxx.properties 这个文件并不是必须的,但当该配置文件存在时,其优读取顺序先于 jboot.properties。 @@ -315,11 +318,6 @@ public MyConfigDecriptor implements JbootConfigDecryptor { } ``` -## 设计原因 - -由于 Jboot 定位是微服务框架,同时 Jboot 假设:基于 Jboot 开发的应用部署在 Docker 之上。 - -因此,在做 Devops 的时候,编排工具(例如:k8s、mesos)会去修改应用的相关配置,而通过环境变量和启动配置,无疑是最方便快捷的。 ## 常见问题 diff --git a/doc/docs/jwt.md b/doc/docs/jwt.md index aca24a39..12934f0a 100644 --- a/doc/docs/jwt.md +++ b/doc/docs/jwt.md @@ -12,8 +12,10 @@ JWT 是 Json web token 的简称, 是为了在网络应用环境间传递声明 ## JWT 配置 -- jboot.web.jwt.httpHeaderName:配置JWT的http头的key,默认为 `Jwt` -- jboot.web.jwt.secret:配置JWT的密钥 +在使用 JWT 之前,我们需要对 JWT 进行一些必要的配置。 + +- jboot.web.jwt.httpHeaderName:配置JWT的http头的key,默认为 `Jwt`,可以不配置 +- jboot.web.jwt.secret:配置JWT的密钥,不行配置,否则 jboot 会给出警告 - jboot.web.jwt.validityPeriod:配置JWT的过期时间,默认不过期 @@ -31,7 +33,7 @@ JWT 是 Json web token 的简称, 是为了在网络应用环境间传递声明 在服务端通过 `setJwtAttr()` 方法设置 JWT 后,Http 的响应头会添加一个名称为 `Jwt` 的属性(可以通过 `jboot.web.jwt.httpHeaderName` 进行配置)。 -此时,客户端(浏览器、小程序、APP等)发现 Http 头有该属性后,需要客户端主动把该值存储起来。APP存储到数据库、浏览和小程序可以存储到localStorage等,当客户端进行 Http 请求的时候,需要在 Http 头添加下属性为 `Jwt`、值为之前存储数据 的请求头。 +此时,客户端(浏览器、小程序、APP等)发现 Http 头有该属性后,需要客户端主动把该值存储起来。APP存储到数据库、浏览和小程序可以存储到 `localStorage` 等,当客户端进行 Http 请求的时候,需要在 Http 头添加下属性为 `Jwt`、值为之前存储数据 的请求头。 当客户端正确添加 `Jwt` 的 Http 请求头的时候,服务端可以通过 `getJwtPara()` 方法获取到客户端传入的内容。 diff --git a/doc/docs/metrics.md b/doc/docs/metrics.md index 7b150157..e2876964 100644 --- a/doc/docs/metrics.md +++ b/doc/docs/metrics.md @@ -41,11 +41,13 @@ public class MetricsController extends JbootController { 同时,由于我们配置了 `jboot.metric.url=-/metrics_admin` ,我们可以通过 `http://127.0.0.1:8888/metrics_admin` 来查看 `index()` 这个方法的访问次数和并发量。 -## Metrics 输出到 grafana +## Metrics 输出到 Grafana -grafana 并没有接收数据的能力,因此,jboot 的方案是先数据输出到 influxdb,在配置 grafana 来读取 influxdb 的数据。 +Grafana 是一个开源的度量分析与可视化套件。经常被用作基础设施的时间序列数据和应用程序分析的可视化,它在其他领域也被广泛的使用包括工业传感器、家庭自动化、天气和过程控制等。 -因此,在 grafana 正常显示 jboot 数据之前,先把 grafana 和 influxdb 启动起来。 +Grafana支持许多不同的数据源,比如: Graphite,InfluxDB,OpenTSDB,Prometheus,Elasticsearch,CloudWatch 和 KairosDB 等。每个数据源都有一个特定的查询编辑器,该编辑器定制的特性和功能是公开的特定数据来源。但是,Grafana 并没有接收数据的能力,因此,jboot 的方案是先把数据输出到 influxdb,再配置 Grafana 来读取 influxdb 的数据。 + +因此,在 Grafana 正常显示 jboot 数据之前,先把 Grafana 和 influxdb 启动起来。 **启动 influxdb :** @@ -60,13 +62,13 @@ docker run -d -p 8086:8086 -p 8083:8083 \ influxdb ``` -**启动 grafana :** +**启动 Grafana :** ``` docker run -d -p 3000:3000 grafana/grafana ``` -同时,需要在 jboot 应用添加如下依赖: +最后,需要在 jboot 应用添加如下依赖: ```xml @@ -105,23 +107,26 @@ public class MetricsController extends JbootController { } ``` -启动 jboot,当访问 `http://127.0.0.1:8080/` 之后, jboot 就会把Metrics的数据输出到 influxdb,此时我们就可以配置 grafana 读取 influxdb 的数据了。 +启动 jboot,当访问 `http://127.0.0.1:8080/` 之后, jboot 就会把 Metrics 的数据输出到 influxdb,此时我们就可以配置 grafana 读取 influxdb 的数据了。 更多关于 grafana 读取 influxdb 的文档请参考 https://grafana.com/docs/features/datasources/influxdb/ 。 -## Metrics 输出到 graphite +## Metrics 输出到 Graphite -在开始之前,需要添加如下的 Maven 依赖。 +Graphite 是一个开源实时的、显示时间序列度量数据的图形系统。Graphite 并不收集度量数据本身,而是像一个数据库,通过其后端接收度量数据,然后以实时方式查询、转换、组合这些度量数据。Graphite支持内建的Web界面,它允许用户浏览度量数据和图。 -```xml - - io.dropwizard.metrics - metrics-graphite - 4.1.0 - -``` +Graphite 有三个主要组件组成: + +- 1)Graphite-Web +这是一个基于Django的Web应用,可以呈现图形和仪表板 + +- 2)Carbon +这是一个度量处理守护进程 + +- 3)Whisper +这是一个基于时序数据库的库 -**启动 graphite** +在开始之前,我们需要启动 Graphite ``` docker run -d\ @@ -135,7 +140,18 @@ docker run -d\ graphiteapp/graphite-statsd ``` -在 jboot.properties 添加如下配置: + +然后,在我们自己的项目添加如下的 Maven 依赖。 + +```xml + + io.dropwizard.metrics + metrics-graphite + 4.1.0 + +``` + +最后在 jboot.properties 添加如下配置: ``` jboot.metric.url=/metrics_admin diff --git a/doc/docs/static/images/jboot-wechat-group.png b/doc/docs/static/images/jboot-wechat-group.png index 3d46050d5d6276b97877067ed4e33d62ab57c764..05495fb7aeb228d7337f324cd3c9ba004d8e8d4c 100644 GIT binary patch literal 13278 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

    M@$_OsS){3AMn?7v-SwpJ*v(pW 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 Date: Sun, 13 Sep 2020 15:52:45 +0800 Subject: [PATCH 0523/1965] update docs --- doc/docs/rpc.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/doc/docs/rpc.md b/doc/docs/rpc.md index ba4dcb86..eaae0886 100644 --- a/doc/docs/rpc.md +++ b/doc/docs/rpc.md @@ -97,6 +97,39 @@ jboot.rpc.autoExportEnable = true - jboot.rpc.groups : 每个服务对应的 group - jboot.rpc.autoExportEnable : 当 Jboot 启动的时候,是否自动暴露 @RPCBean 注解的接口。 +在 以上 示例中,`jboot.rpc.providers` 配置中,可以对每个 Service 进行配置,但是,在绝大多数的情况下,我们可能只需要一个配置,这个配置应用于所有的 Service 服务,此时我们需要做如下配置: + +``` +# 名称为 default 的 provider 配置(当不配置其名称的时候,名称默认为 default) +jboot.rpc.dubbo.provider.timeout = xx.xx.xx +jboot.rpc.dubbo.provider.loadbalance = xx.xx.xx +jboot.rpc.dubbo.provider.group = xx.xx.xx +jboot.rpc.dubbo.provider.host = xx.xx.xx +jboot.rpc.dubbo.provider.default = true #设置当前 provider 为默认配置,既所有未指定 provider 的 service 服务都使用此配置。 + +# 名称为 name1 的 provider 配置 +jboot.rpc.dubbo.provider.name1.timeout = xx.xx.xx +jboot.rpc.dubbo.provider.name1.loadbalance = xx.xx.xx +jboot.rpc.dubbo.provider.name1.group = xx.xx.xx +jboot.rpc.dubbo.provider.name1.host = xx.xx.xx + +# 名称为 name2 的 provider 配置 +jboot.rpc.dubbo.provider.name2.timeout = xx.xx.xx +jboot.rpc.dubbo.provider.name2.loadbalance = xx.xx.xx +jboot.rpc.dubbo.provider.name2.group = xx.xx.xx +jboot.rpc.dubbo.provider.name2.host = xx.xx.xx + + +# 配置 com.yourdomain.AAAService 使用的 provider 配置为 name1 +# 配置 com.yourdomain.AAAService 使用的 provider 配置为 name2 +# 其他所有服务的 provider 配置使为 default,原因是名称为 default 的 provider 其属性 default 为 true 了 +# 此处要注意,如果我们给 name2 的 provider 添加配置 jboot.rpc.dubbo.provider.name2.default = true, +# 那么所有的未配置 providers 的服务都使用 name2 作为其默认位置。 +jboot.rpc.providers = com.yourdomain.AAAService:name1,com.yourdomain.XXXService:name2 +``` + +provider 的更多配置情况参考:http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-provider.html + ## 开始使用 -- Gitee From ac009e6aace5a77c47a50f2dc8a86a2c474bea15 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 13 Sep 2020 16:49:37 +0800 Subject: [PATCH 0524/1965] add JbootInterceptorBuilder to build Interceptor dynamic --- .../java/io/jboot/aop/JbootAopFactory.java | 33 +++++- .../io/jboot/aop/JbootInterceptorBuilder.java | 28 +++++ src/main/java/io/jboot/aop/cglib/CPI.java | 26 +++++ .../jboot/aop/cglib/JbootCglibCallback.java | 96 +++++++++++++++ .../aop/cglib/JbootCglibProxyFactory.java | 110 ++++++++++++++++++ 5 files changed, 291 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/jboot/aop/JbootInterceptorBuilder.java create mode 100644 src/main/java/io/jboot/aop/cglib/CPI.java create mode 100644 src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java create mode 100644 src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 739a6652..6d9a1ab2 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -18,7 +18,6 @@ package io.jboot.aop; import com.jfinal.aop.AopFactory; import com.jfinal.aop.Inject; import com.jfinal.core.Controller; -import com.jfinal.ext.proxy.CglibProxyFactory; import com.jfinal.kit.LogKit; import com.jfinal.log.Log; import com.jfinal.plugin.activerecord.Model; @@ -26,6 +25,8 @@ import com.jfinal.proxy.Proxy; import com.jfinal.proxy.ProxyManager; import io.jboot.Jboot; import io.jboot.aop.annotation.*; +import io.jboot.aop.cglib.CPI; +import io.jboot.aop.cglib.JbootCglibProxyFactory; import io.jboot.app.config.ConfigUtil; import io.jboot.app.config.JbootConfigManager; import io.jboot.app.config.annotation.ConfigModel; @@ -44,10 +45,12 @@ import io.jboot.web.controller.JbootController; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; public class JbootAopFactory extends AopFactory { @@ -69,10 +72,11 @@ public class JbootAopFactory extends AopFactory { private Map beansMap = new ConcurrentHashMap<>(); + private List interceptorBuilders = new CopyOnWriteArrayList(); private JbootAopFactory() { - ProxyManager.me().setProxyFactory(new CglibProxyFactory()); + ProxyManager.me().setProxyFactory(new JbootCglibProxyFactory()); setInjectSuperClass(true); initBeanMapping(); } @@ -370,4 +374,29 @@ public class JbootAopFactory extends AopFactory { public void setBean(String name, Object obj) { beansMap.put(name, obj); } + + public List getInterceptorBuilders() { + return interceptorBuilders; + } + + public void setInterceptorBuilders(List interceptorBuilders) { + this.interceptorBuilders = interceptorBuilders; + } + + + public void addInterceptorBuilder(JbootInterceptorBuilder interceptorBuilder){ + if(interceptorBuilder == null){ + throw new NullPointerException("interceptorBuilder must not be null."); + } + this.interceptorBuilders.add(interceptorBuilder); + CPI.clearIntersCache(); + } + + public void addInterceptorBuilders(Collection interceptorBuilders){ + if(interceptorBuilders == null){ + throw new NullPointerException("interceptorBuilder must not be null."); + } + this.interceptorBuilders.addAll(interceptorBuilders); + CPI.clearIntersCache(); + } } diff --git a/src/main/java/io/jboot/aop/JbootInterceptorBuilder.java b/src/main/java/io/jboot/aop/JbootInterceptorBuilder.java new file mode 100644 index 00000000..0b9e45a9 --- /dev/null +++ b/src/main/java/io/jboot/aop/JbootInterceptorBuilder.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop; + +import com.jfinal.aop.Interceptor; + +import java.lang.reflect.Method; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public interface JbootInterceptorBuilder { + + public Interceptor[] build(Class serviceClass, Method method); +} diff --git a/src/main/java/io/jboot/aop/cglib/CPI.java b/src/main/java/io/jboot/aop/cglib/CPI.java new file mode 100644 index 00000000..5b3ba16a --- /dev/null +++ b/src/main/java/io/jboot/aop/cglib/CPI.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop.cglib; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public class CPI { + + public static void clearIntersCache(){ + JbootCglibProxyFactory.IntersCache.clear(); + } +} diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java new file mode 100644 index 00000000..8e467bbd --- /dev/null +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop.cglib; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.InterceptorManager; +import com.jfinal.aop.Invocation; +import io.jboot.aop.JbootAopFactory; +import io.jboot.aop.JbootInterceptorBuilder; +import io.jboot.utils.ArrayUtil; +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; + +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * CglibCallback. + */ +class JbootCglibCallback implements MethodInterceptor { + + private static final Set excludedMethodName = buildExcludedMethodName(); + private static final InterceptorManager interMan = InterceptorManager.me(); + + @Override + public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { + if (excludedMethodName.contains(method.getName())) { + return methodProxy.invokeSuper(target, args); + } + + Class targetClass = target.getClass(); + if (targetClass.getName().indexOf("$$EnhancerBy") != -1) { + targetClass = targetClass.getSuperclass(); + } + + + JbootCglibProxyFactory.MethodKey key = JbootCglibProxyFactory.IntersCache.getMethodKey(targetClass, method); + Interceptor[] inters = JbootCglibProxyFactory.IntersCache.get(key); + if (inters == null) { + + // jfinal 原生的构建 + inters = interMan.buildServiceMethodInterceptor(targetClass, method); + + List interceptorBuilders = JbootAopFactory.me().getInterceptorBuilders(); + if (interceptorBuilders != null && interceptorBuilders.size() > 0) { + for (JbootInterceptorBuilder builder : interceptorBuilders) { + Interceptor[] buildInters = builder.build(targetClass, method); + if (buildInters != null || buildInters.length > 0) { + inters = ArrayUtil.concat(inters, buildInters); + } + } + } + + JbootCglibProxyFactory.IntersCache.put(key, inters); + } + + Invocation invocation = new Invocation(target, method, inters, + x -> { + return methodProxy.invokeSuper(target, x); + } + , args); + + + invocation.invoke(); + return invocation.getReturnValue(); + } + + private static final Set buildExcludedMethodName() { + Set excludedMethodName = new HashSet(64, 0.25F); + Method[] methods = Object.class.getDeclaredMethods(); + for (Method m : methods) { + excludedMethodName.add(m.getName()); + } + // getClass() registerNatives() can not be enhanced + // excludedMethodName.remove("getClass"); + // excludedMethodName.remove("registerNatives"); + return excludedMethodName; + } +} + + diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java b/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java new file mode 100644 index 00000000..e2931881 --- /dev/null +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop.cglib; + +import com.jfinal.aop.Interceptor; +import com.jfinal.kit.HashKit; +import com.jfinal.kit.SyncWriteMap; +import com.jfinal.proxy.ProxyFactory; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Objects; + +/** + * CglibProxyFactory 用于扩展 cglib 的代理模式,默认不使用 + * + *

    + * 配置方法:
    + * public void configConstant(Constants me) {
    + *     ProxyManager.me().setProxyFactory(new JbootCglibProxyFactory());
    + * }
    + * 
    + */ +public class JbootCglibProxyFactory extends ProxyFactory { + + @Override + public T get(Class target) { + return (T) net.sf.cglib.proxy.Enhancer.create(target, new JbootCglibCallback()); + } + + static class IntersCache { + private static final Map cache = new SyncWriteMap<>(2048, 0.25F); + + public static void put(MethodKey methodKey, Interceptor[] inters) { + Objects.requireNonNull(methodKey, "methodKey can not be null"); + Objects.requireNonNull(inters, "inters can not be null"); + + cache.putIfAbsent(methodKey, inters); + } + + public static Interceptor[] get(MethodKey methodKey) { + return cache.get(methodKey); + } + + public static MethodKey getMethodKey(Class target, Method method) { + long paraHash = HashKit.FNV_OFFSET_BASIS_64; + Class[] paraTypes = method.getParameterTypes(); + for (Class pt : paraTypes) { + paraHash ^= pt.getName().hashCode(); + paraHash *= HashKit.FNV_PRIME_64; + } + + return new MethodKey(target.getName().hashCode(), method.getName().hashCode(), paraHash); + } + + public static void clear() { + cache.clear(); + } + } + + static class MethodKey { + final int classHash; + final int methodHash; + final long paraHash; + + MethodKey(int classHash, int methodHash, long paraHash) { + this.classHash = classHash; + this.methodHash = methodHash; + this.paraHash = paraHash; + } + + @Override + public int hashCode() { + return classHash ^ methodHash ^ ((int) paraHash); + } + + /** + * 通过比较三部分 hash 值,避免超大规模场景下可能的 key 值碰撞 + *

    + * 不必判断 if (methodKey instanceof MethodKey),因为所有 key 类型必须要相同 + * 不必判断 if (this == methodKey),因为每次用于取值的 methodKey 都是新建的 + */ + @Override + public boolean equals(Object methodKey) { + MethodKey mk = (MethodKey) methodKey; + return classHash == mk.classHash && methodHash == mk.methodHash && paraHash == mk.paraHash; + } + + @Override + public String toString() { + return "classHash = " + classHash + "\nmethodHash = " + methodHash + "\nparaHash = " + paraHash; + } + } +} + + + -- Gitee From ce00150c02eddc8edff54578335b01dcda1f0803 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 13 Sep 2020 17:00:44 +0800 Subject: [PATCH 0525/1965] add JbootInterceptorBuilder to build Interceptor dynamic --- .../java/io/jboot/aop/JbootInterceptorBuilder.java | 12 ++++++++++-- .../java/io/jboot/aop/cglib/JbootCglibCallback.java | 4 +--- .../io/jboot/aop/cglib/JbootCglibProxyFactory.java | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/aop/JbootInterceptorBuilder.java b/src/main/java/io/jboot/aop/JbootInterceptorBuilder.java index 0b9e45a9..12098b4d 100644 --- a/src/main/java/io/jboot/aop/JbootInterceptorBuilder.java +++ b/src/main/java/io/jboot/aop/JbootInterceptorBuilder.java @@ -21,8 +21,16 @@ import java.lang.reflect.Method; /** * @author michael yang (fuhai999@gmail.com) + * + * JbootInterceptorBuilder 用于自定义每个服务方法的拦截器构建 + * + *

    + * 配置方法:
    + * public void onInit() {
    + *     JbootAopFactory.me().addInterceptorBuilder(new MyInterceptorBuilder());
    + * }
    + * 
    */ public interface JbootInterceptorBuilder { - - public Interceptor[] build(Class serviceClass, Method method); + Interceptor[] build(Class serviceClass, Method method); } diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java index 8e467bbd..bcfef569 100644 --- a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java @@ -29,9 +29,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -/** - * CglibCallback. - */ + class JbootCglibCallback implements MethodInterceptor { private static final Set excludedMethodName = buildExcludedMethodName(); diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java b/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java index e2931881..edb57f86 100644 --- a/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java @@ -25,7 +25,7 @@ import java.util.Map; import java.util.Objects; /** - * CglibProxyFactory 用于扩展 cglib 的代理模式,默认不使用 + * JbootCglibProxyFactory 用于扩展 cglib 的代理模式 * *
      * 配置方法:
    -- 
    Gitee
    
    
    From 0f64860ff2300ac83910ec744d60a8792dde66da Mon Sep 17 00:00:00 2001
    From: yangfuhai 
    Date: Mon, 14 Sep 2020 12:20:19 +0800
    Subject: [PATCH 0526/1965] add InterceptorBuilder to build Interceptor dynamic
    
    ---
     ...orBuilder.java => InterceptorBuilder.java} |  7 +++--
     .../java/io/jboot/aop/JbootAopFactory.java    | 11 +++----
     .../jboot/aop/cglib/JbootCglibCallback.java   | 29 +++++++++++++------
     3 files changed, 30 insertions(+), 17 deletions(-)
     rename src/main/java/io/jboot/aop/{JbootInterceptorBuilder.java => InterceptorBuilder.java} (78%)
    
    diff --git a/src/main/java/io/jboot/aop/JbootInterceptorBuilder.java b/src/main/java/io/jboot/aop/InterceptorBuilder.java
    similarity index 78%
    rename from src/main/java/io/jboot/aop/JbootInterceptorBuilder.java
    rename to src/main/java/io/jboot/aop/InterceptorBuilder.java
    index 12098b4d..57c1de95 100644
    --- a/src/main/java/io/jboot/aop/JbootInterceptorBuilder.java
    +++ b/src/main/java/io/jboot/aop/InterceptorBuilder.java
    @@ -18,11 +18,12 @@ package io.jboot.aop;
     import com.jfinal.aop.Interceptor;
     
     import java.lang.reflect.Method;
    +import java.util.LinkedList;
     
     /**
      * @author michael yang (fuhai999@gmail.com)
      * 
    - * JbootInterceptorBuilder 用于自定义每个服务方法的拦截器构建
    + * InterceptorHandler 用于控制某个方法已经添加好的拦截器,可以对其删除或者添加
      *
      * 
      * 配置方法:
    @@ -31,6 +32,6 @@ import java.lang.reflect.Method;
      * }
      * 
    */ -public interface JbootInterceptorBuilder { - Interceptor[] build(Class serviceClass, Method method); +public interface InterceptorBuilder { + void build(Class serviceClass, Method method, LinkedList interceptors); } diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 6d9a1ab2..c831da6c 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -72,7 +72,7 @@ public class JbootAopFactory extends AopFactory { private Map beansMap = new ConcurrentHashMap<>(); - private List interceptorBuilders = new CopyOnWriteArrayList(); + private List interceptorBuilders = new CopyOnWriteArrayList(); private JbootAopFactory() { @@ -375,16 +375,16 @@ public class JbootAopFactory extends AopFactory { beansMap.put(name, obj); } - public List getInterceptorBuilders() { + public List getInterceptorBuilders() { return interceptorBuilders; } - public void setInterceptorBuilders(List interceptorBuilders) { + public void setInterceptorBuilders(List interceptorBuilders) { this.interceptorBuilders = interceptorBuilders; } - public void addInterceptorBuilder(JbootInterceptorBuilder interceptorBuilder){ + public void addInterceptorBuilder(InterceptorBuilder interceptorBuilder){ if(interceptorBuilder == null){ throw new NullPointerException("interceptorBuilder must not be null."); } @@ -392,11 +392,12 @@ public class JbootAopFactory extends AopFactory { CPI.clearIntersCache(); } - public void addInterceptorBuilders(Collection interceptorBuilders){ + public void addInterceptorBuilders(Collection interceptorBuilders){ if(interceptorBuilders == null){ throw new NullPointerException("interceptorBuilder must not be null."); } this.interceptorBuilders.addAll(interceptorBuilders); CPI.clearIntersCache(); } + } diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java index bcfef569..ae173ba2 100644 --- a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java @@ -18,14 +18,14 @@ package io.jboot.aop.cglib; import com.jfinal.aop.Interceptor; import com.jfinal.aop.InterceptorManager; import com.jfinal.aop.Invocation; +import io.jboot.aop.InterceptorBuilder; import io.jboot.aop.JbootAopFactory; -import io.jboot.aop.JbootInterceptorBuilder; -import io.jboot.utils.ArrayUtil; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -54,16 +54,17 @@ class JbootCglibCallback implements MethodInterceptor { // jfinal 原生的构建 inters = interMan.buildServiceMethodInterceptor(targetClass, method); - List interceptorBuilders = JbootAopFactory.me().getInterceptorBuilders(); - if (interceptorBuilders != null && interceptorBuilders.size() > 0) { - for (JbootInterceptorBuilder builder : interceptorBuilders) { - Interceptor[] buildInters = builder.build(targetClass, method); - if (buildInters != null || buildInters.length > 0) { - inters = ArrayUtil.concat(inters, buildInters); - } + // 通过 InterceptorBuilder 去构建 + List interceptorBuilder = JbootAopFactory.me().getInterceptorBuilders(); + if (interceptorBuilder != null && interceptorBuilder.size() > 0) { + LinkedList list = toLinkedList(inters); + for (InterceptorBuilder builder : interceptorBuilder) { + builder.build(targetClass, method, list); } + inters = list.toArray(new Interceptor[0]); } + JbootCglibProxyFactory.IntersCache.put(key, inters); } @@ -89,6 +90,16 @@ class JbootCglibCallback implements MethodInterceptor { // excludedMethodName.remove("registerNatives"); return excludedMethodName; } + + private LinkedList toLinkedList(Interceptor[] interceptors) { + LinkedList linkedList = new LinkedList(); + if (interceptors != null) { + for (Interceptor interceptor : interceptors) { + linkedList.add(interceptor); + } + } + return linkedList; + } } -- Gitee From 49cf890e2d200776bb7321c5fdee57b076b111ac Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Sep 2020 12:27:15 +0800 Subject: [PATCH 0527/1965] add InterceptorBuilder to build Interceptor dynamic --- .../jboot/aop/cglib/JbootCglibCallback.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java index ae173ba2..02f502ed 100644 --- a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java @@ -20,6 +20,7 @@ import com.jfinal.aop.InterceptorManager; import com.jfinal.aop.Invocation; import io.jboot.aop.InterceptorBuilder; import io.jboot.aop.JbootAopFactory; +import io.jboot.utils.ClassUtil; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; @@ -41,11 +42,7 @@ class JbootCglibCallback implements MethodInterceptor { return methodProxy.invokeSuper(target, args); } - Class targetClass = target.getClass(); - if (targetClass.getName().indexOf("$$EnhancerBy") != -1) { - targetClass = targetClass.getSuperclass(); - } - + Class targetClass = ClassUtil.getUsefulClass(target.getClass()); JbootCglibProxyFactory.MethodKey key = JbootCglibProxyFactory.IntersCache.getMethodKey(targetClass, method); Interceptor[] inters = JbootCglibProxyFactory.IntersCache.get(key); @@ -61,10 +58,13 @@ class JbootCglibCallback implements MethodInterceptor { for (InterceptorBuilder builder : interceptorBuilder) { builder.build(targetClass, method, list); } - inters = list.toArray(new Interceptor[0]); + if (list.isEmpty()) { + inters = InterceptorManager.NULL_INTERS; + } else { + inters = list.toArray(new Interceptor[0]); + } } - JbootCglibProxyFactory.IntersCache.put(key, inters); } @@ -79,6 +79,8 @@ class JbootCglibCallback implements MethodInterceptor { return invocation.getReturnValue(); } + + private static final Set buildExcludedMethodName() { Set excludedMethodName = new HashSet(64, 0.25F); Method[] methods = Object.class.getDeclaredMethods(); @@ -91,8 +93,10 @@ class JbootCglibCallback implements MethodInterceptor { return excludedMethodName; } - private LinkedList toLinkedList(Interceptor[] interceptors) { - LinkedList linkedList = new LinkedList(); + + + private static LinkedList toLinkedList(Interceptor[] interceptors) { + LinkedList linkedList = new LinkedList<>(); if (interceptors != null) { for (Interceptor interceptor : interceptors) { linkedList.add(interceptor); -- Gitee From 780511994ba453e0afc5c0e7f7a2415eb8757076 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Sep 2020 12:31:53 +0800 Subject: [PATCH 0528/1965] add InterceptorBuilder to build Interceptor dynamic --- src/main/java/io/jboot/aop/JbootAopFactory.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index c831da6c..43301bf7 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -36,6 +36,7 @@ import io.jboot.components.rpc.Jbootrpc; import io.jboot.components.rpc.JbootrpcManager; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.annotation.RPCInject; +import io.jboot.core.weight.WeightUtil; import io.jboot.db.model.JbootModel; import io.jboot.exception.JbootException; import io.jboot.service.JbootServiceBase; @@ -389,14 +390,19 @@ public class JbootAopFactory extends AopFactory { throw new NullPointerException("interceptorBuilder must not be null."); } this.interceptorBuilders.add(interceptorBuilder); + WeightUtil.sort(this.interceptorBuilders); + CPI.clearIntersCache(); } + public void addInterceptorBuilders(Collection interceptorBuilders){ if(interceptorBuilders == null){ throw new NullPointerException("interceptorBuilder must not be null."); } this.interceptorBuilders.addAll(interceptorBuilders); + WeightUtil.sort(this.interceptorBuilders); + CPI.clearIntersCache(); } -- Gitee From 8a7eb02e8b5a05f161728f0458cc91e0494e42f8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Sep 2020 15:25:25 +0800 Subject: [PATCH 0529/1965] v3.5.0 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dc3cc344..0fdf90d0 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.4.3 + 3.5.0-SNAPSHOT jar jboot -- Gitee From d03dcbb1652c6293b1c5b4708a2eda7a2f8a6214 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Sep 2020 15:31:37 +0800 Subject: [PATCH 0530/1965] add InterceptorBuilder to build Interceptor dynamic --- .../jboot/aop/InterceptorBuilderManager.java | 101 ++++++++++++++ .../java/io/jboot/aop/JbootAopFactory.java | 35 ----- src/main/java/io/jboot/aop/cglib/CPI.java | 26 ---- .../jboot/aop/cglib/JbootCglibCallback.java | 19 +-- .../aop/cglib/JbootCglibProxyFactory.java | 4 +- .../support/shiro/JbootShiroInterceptor.java | 2 - .../fixedinterceptor/FixedInterceptor.java | 5 +- .../fixedinterceptor/FixedInterceptors.java | 2 +- .../web/fixedinterceptor/FixedInvocation.java | 120 ---------------- .../jboot/web/handler/JbootActionHandler.java | 11 +- .../web/handler/JbootActionReporter.java | 7 +- .../io/jboot/web/handler/JbootInvocation.java | 129 ++++++++++++++++++ .../web/handler/JbootInvocationWarpper.java | 44 ------ 13 files changed, 245 insertions(+), 260 deletions(-) create mode 100644 src/main/java/io/jboot/aop/InterceptorBuilderManager.java delete mode 100644 src/main/java/io/jboot/aop/cglib/CPI.java delete mode 100644 src/main/java/io/jboot/web/fixedinterceptor/FixedInvocation.java create mode 100644 src/main/java/io/jboot/web/handler/JbootInvocation.java delete mode 100644 src/main/java/io/jboot/web/handler/JbootInvocationWarpper.java diff --git a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java new file mode 100644 index 00000000..982f4e72 --- /dev/null +++ b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop; + +import com.jfinal.aop.AopFactory; +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.InterceptorManager; +import io.jboot.aop.cglib.JbootCglibProxyFactory; +import io.jboot.core.weight.WeightUtil; + +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public class InterceptorBuilderManager extends AopFactory { + + + private static InterceptorBuilderManager me = new InterceptorBuilderManager(); + + + public static InterceptorBuilderManager me() { + return me; + } + + + private List interceptorBuilders = new CopyOnWriteArrayList(); + + + public List getInterceptorBuilders() { + return interceptorBuilders; + } + + + + public void addInterceptorBuilder(InterceptorBuilder interceptorBuilder) { + if (interceptorBuilder == null) { + throw new NullPointerException("interceptorBuilder must not be null."); + } + this.interceptorBuilders.add(interceptorBuilder); + WeightUtil.sort(this.interceptorBuilders); + + JbootCglibProxyFactory.IntersCache.clear(); + } + + + + + public void addInterceptorBuilders(Collection interceptorBuilders) { + if (interceptorBuilders == null) { + throw new NullPointerException("interceptorBuilder must not be null."); + } + this.interceptorBuilders.addAll(interceptorBuilders); + WeightUtil.sort(this.interceptorBuilders); + + JbootCglibProxyFactory.IntersCache.clear(); + } + + + + + public Interceptor[] build(Class targetClass, Method method, Interceptor[] interceptors) { + if (interceptorBuilders != null && interceptorBuilders.size() > 0) { + LinkedList list = toLinkedList(interceptors); + for (InterceptorBuilder builder : interceptorBuilders) { + builder.build(targetClass, method, list); + } + return list.isEmpty() ? InterceptorManager.NULL_INTERS : list.toArray(new Interceptor[0]); + } + + return interceptors; + } + + + + + private static LinkedList toLinkedList(Interceptor[] interceptors) { + LinkedList linkedList = new LinkedList<>(); + if (interceptors != null) { + for (Interceptor interceptor : interceptors) { + linkedList.add(interceptor); + } + } + return linkedList; + } + +} diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 43301bf7..8898aca9 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -25,7 +25,6 @@ import com.jfinal.proxy.Proxy; import com.jfinal.proxy.ProxyManager; import io.jboot.Jboot; import io.jboot.aop.annotation.*; -import io.jboot.aop.cglib.CPI; import io.jboot.aop.cglib.JbootCglibProxyFactory; import io.jboot.app.config.ConfigUtil; import io.jboot.app.config.JbootConfigManager; @@ -36,7 +35,6 @@ import io.jboot.components.rpc.Jbootrpc; import io.jboot.components.rpc.JbootrpcManager; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.annotation.RPCInject; -import io.jboot.core.weight.WeightUtil; import io.jboot.db.model.JbootModel; import io.jboot.exception.JbootException; import io.jboot.service.JbootServiceBase; @@ -46,12 +44,10 @@ import io.jboot.web.controller.JbootController; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; public class JbootAopFactory extends AopFactory { @@ -73,7 +69,6 @@ public class JbootAopFactory extends AopFactory { private Map beansMap = new ConcurrentHashMap<>(); - private List interceptorBuilders = new CopyOnWriteArrayList(); private JbootAopFactory() { @@ -376,34 +371,4 @@ public class JbootAopFactory extends AopFactory { beansMap.put(name, obj); } - public List getInterceptorBuilders() { - return interceptorBuilders; - } - - public void setInterceptorBuilders(List interceptorBuilders) { - this.interceptorBuilders = interceptorBuilders; - } - - - public void addInterceptorBuilder(InterceptorBuilder interceptorBuilder){ - if(interceptorBuilder == null){ - throw new NullPointerException("interceptorBuilder must not be null."); - } - this.interceptorBuilders.add(interceptorBuilder); - WeightUtil.sort(this.interceptorBuilders); - - CPI.clearIntersCache(); - } - - - public void addInterceptorBuilders(Collection interceptorBuilders){ - if(interceptorBuilders == null){ - throw new NullPointerException("interceptorBuilder must not be null."); - } - this.interceptorBuilders.addAll(interceptorBuilders); - WeightUtil.sort(this.interceptorBuilders); - - CPI.clearIntersCache(); - } - } diff --git a/src/main/java/io/jboot/aop/cglib/CPI.java b/src/main/java/io/jboot/aop/cglib/CPI.java deleted file mode 100644 index 5b3ba16a..00000000 --- a/src/main/java/io/jboot/aop/cglib/CPI.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.aop.cglib; - -/** - * @author michael yang (fuhai999@gmail.com) - */ -public class CPI { - - public static void clearIntersCache(){ - JbootCglibProxyFactory.IntersCache.clear(); - } -} diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java index 02f502ed..2c0d0409 100644 --- a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java @@ -18,8 +18,7 @@ package io.jboot.aop.cglib; import com.jfinal.aop.Interceptor; import com.jfinal.aop.InterceptorManager; import com.jfinal.aop.Invocation; -import io.jboot.aop.InterceptorBuilder; -import io.jboot.aop.JbootAopFactory; +import io.jboot.aop.InterceptorBuilderManager; import io.jboot.utils.ClassUtil; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; @@ -27,7 +26,6 @@ import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; import java.util.HashSet; import java.util.LinkedList; -import java.util.List; import java.util.Set; @@ -51,19 +49,8 @@ class JbootCglibCallback implements MethodInterceptor { // jfinal 原生的构建 inters = interMan.buildServiceMethodInterceptor(targetClass, method); - // 通过 InterceptorBuilder 去构建 - List interceptorBuilder = JbootAopFactory.me().getInterceptorBuilders(); - if (interceptorBuilder != null && interceptorBuilder.size() > 0) { - LinkedList list = toLinkedList(inters); - for (InterceptorBuilder builder : interceptorBuilder) { - builder.build(targetClass, method, list); - } - if (list.isEmpty()) { - inters = InterceptorManager.NULL_INTERS; - } else { - inters = list.toArray(new Interceptor[0]); - } - } + // builder 再次构建 + inters = InterceptorBuilderManager.me().build(targetClass,method,inters); JbootCglibProxyFactory.IntersCache.put(key, inters); } diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java b/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java index edb57f86..bb9a50d2 100644 --- a/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java @@ -41,7 +41,7 @@ public class JbootCglibProxyFactory extends ProxyFactory { return (T) net.sf.cglib.proxy.Enhancer.create(target, new JbootCglibCallback()); } - static class IntersCache { + public static class IntersCache { private static final Map cache = new SyncWriteMap<>(2048, 0.25F); public static void put(MethodKey methodKey, Interceptor[] inters) { @@ -71,7 +71,7 @@ public class JbootCglibProxyFactory extends ProxyFactory { } } - static class MethodKey { + public static class MethodKey { final int classHash; final int methodHash; final long paraHash; diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java b/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java index 8bd51b60..2503c6de 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java @@ -19,8 +19,6 @@ import com.jfinal.aop.Invocation; import io.jboot.Jboot; import io.jboot.support.shiro.processer.AuthorizeResult; import io.jboot.web.fixedinterceptor.FixedInterceptor; -import io.jboot.web.fixedinterceptor.FixedInvocation; - /** * Shiro 拦截器 */ diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptor.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptor.java index e7e1a9eb..4f360959 100644 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptor.java +++ b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptor.java @@ -15,13 +15,12 @@ */ package io.jboot.web.fixedinterceptor; -import com.jfinal.aop.Invocation; +import com.jfinal.aop.Interceptor; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @title 不会被 @Clear 清除掉的 拦截器 * @version V1.0 */ -public interface FixedInterceptor { - void intercept(Invocation inv); +public interface FixedInterceptor extends Interceptor { } diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java index 52b19329..394abd76 100644 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java +++ b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java @@ -65,7 +65,7 @@ public class FixedInterceptors { private List inters; - FixedInterceptor[] all() { + public FixedInterceptor[] all() { if (allInters == null) { synchronized (this) { if (allInters == null) { diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInvocation.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInvocation.java deleted file mode 100644 index c4d3bf0f..00000000 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInvocation.java +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.web.fixedinterceptor; - -import com.jfinal.aop.Invocation; -import com.jfinal.core.Controller; - -import java.lang.reflect.Method; - -/** - * @author Michael Yang 杨福海 (fuhai999@gmail.com) - * @version V1.0 - */ -public class FixedInvocation extends Invocation { - - private Invocation origin; - - - private FixedInterceptor[] inters = FixedInterceptors.me().all(); - private int index = 0; - - - public FixedInvocation(Invocation invocation) { - this.origin = invocation; - } - - - @Override - public void invoke() { - if (index < inters.length) { - inters[index++].intercept(this); - } else if (index++ == inters.length) { // index++ ensure invoke action only one time - origin.invoke(); - } - } - - - @Override - public Method getMethod() { - return origin.getMethod(); - } - - @Override - public Controller getController() { - return origin.getController(); - } - - @Override - public String getActionKey() { - return origin.getActionKey(); - } - - @Override - public String getControllerKey() { - return origin.getControllerKey(); - } - - @Override - public String getMethodName() { - return origin.getMethodName(); - } - - @Override - public boolean isActionInvocation() { - return true; - } - - @Override - public Object getArg(int index) { - return origin.getArg(index); - } - - @Override - public void setArg(int index, Object value) { - origin.setArg(index, value); - } - - @Override - public Object[] getArgs() { - return origin.getArgs(); - } - - @Override - public T getTarget() { - return origin.getTarget(); - } - - @Override - public T getReturnValue() { - return origin.getReturnValue(); - } - - @Override - public void setReturnValue(Object returnValue) { - origin.setReturnValue(returnValue); - } - - @Override - public String getViewPath() { - return origin.getViewPath(); - } - - public Invocation getOrigin() { - return origin; - } - -} diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 87815bfc..7cc966e9 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -22,7 +22,6 @@ import com.jfinal.render.Render; import com.jfinal.render.RenderException; import io.jboot.utils.ClassUtil; import io.jboot.web.controller.JbootControllerContext; -import io.jboot.web.fixedinterceptor.FixedInvocation; import io.jboot.web.render.JbootRenderFactory; import javax.servlet.http.HttpServletRequest; @@ -70,7 +69,7 @@ public class JbootActionHandler extends ActionHandler { * @return */ public Invocation getInvocation(Action action, Controller controller) { - return devMode ? new JbootInvocationWarpper(action, controller) : new Invocation(action, controller); + return new JbootInvocation(action, controller); } @@ -123,12 +122,12 @@ public class JbootActionHandler extends ActionHandler { // } long time = System.currentTimeMillis(); try { - invokeInvocation(invocation); + invocation.invoke(); } finally { JbootActionReporter.report(target, controller, action, time); } } else { - invokeInvocation(invocation); + invocation.invoke(); } Render render = controller.getRender(); @@ -213,8 +212,4 @@ public class JbootActionHandler extends ActionHandler { } - private void invokeInvocation(Invocation inv) { - new FixedInvocation(inv).invoke(); - } - } diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index 511a69ad..0c14a046 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -96,7 +96,7 @@ public class JbootActionReporter { } Interceptor[] inters = action.getInterceptors(); - List invokedInterceptors = JbootInvocationWarpper.getInvokedInterceptor(); + List invokedInterceptors = JbootInvocation.getInvokedInterceptor(); if (inters.length > 0) { sb.append("Interceptor : "); for (int i = 0; i < inters.length; i++) { @@ -138,8 +138,9 @@ public class JbootActionReporter { } else { sb.append(name).append("[]={"); for (int i = 0; i < values.length; i++) { - if (i > 0) + if (i > 0) { sb.append(","); + } sb.append(values[i]); } sb.append("}"); @@ -155,7 +156,7 @@ public class JbootActionReporter { } catch (IOException ex) { throw new RuntimeException(ex); } finally { - JbootInvocationWarpper.clear(); + JbootInvocation.clear(); } } diff --git a/src/main/java/io/jboot/web/handler/JbootInvocation.java b/src/main/java/io/jboot/web/handler/JbootInvocation.java new file mode 100644 index 00000000..0af1dfd0 --- /dev/null +++ b/src/main/java/io/jboot/web/handler/JbootInvocation.java @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.handler; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import com.jfinal.core.Action; +import com.jfinal.core.Controller; +import com.jfinal.core.JFinal; +import io.jboot.aop.InterceptorBuilderManager; +import io.jboot.aop.cglib.JbootCglibProxyFactory; +import io.jboot.utils.ArrayUtil; +import io.jboot.web.fixedinterceptor.FixedInterceptors; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public class JbootInvocation extends Invocation { + + private Action action; + private Object target; + private Object[] args; + + private Interceptor[] inters; + private int index = 0; + + private Object returnValue; + + private static ThreadLocal> invokedInterceptors = ThreadLocal.withInitial(ArrayList::new); + private static boolean devMode = JFinal.me().getConstants().getDevMode(); + + public JbootInvocation(Action action, Controller controller) { + super(action, controller); + + this.action = action; + this.inters = buildInterceptors(action); + this.target = controller; + + // this.args = NULL_ARGS; + this.args = action.getParameterGetter().get(action, controller); + } + + + private Interceptor[] buildInterceptors(Action action) { + + JbootCglibProxyFactory.MethodKey key = JbootCglibProxyFactory.IntersCache.getMethodKey(action.getControllerClass(), action.getMethod()); + Interceptor[] inters = JbootCglibProxyFactory.IntersCache.get(key); + if (inters == null) { + + // jfinal 原生的构建 + inters = ArrayUtil.concat(FixedInterceptors.me().all(), action.getInterceptors()); + + // builder 再次构建 + inters = InterceptorBuilderManager.me().build(action.getControllerClass(), action.getMethod(), inters); + + JbootCglibProxyFactory.IntersCache.put(key, inters); + } + + return inters; + } + + + @Override + public void invoke() { + if (index < inters.length) { + Interceptor interceptor = inters[index++]; + if (devMode) { + invokedInterceptors.get().add(interceptor); + } + interceptor.intercept(this); + } else if (index++ == inters.length) { // index++ ensure invoke action only one time + try { + // Invoke the action + returnValue = action.getMethod().invoke(target, args); + } catch (InvocationTargetException e) { + Throwable t = e.getTargetException(); + if (t == null) { + t = e; + } + throw t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t); + } catch (RuntimeException e) { + throw e; + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + } + + + @Override + public Object getReturnValue() { + return returnValue; + } + + @Override + public void setReturnValue(Object returnValue) { + this.returnValue = returnValue; + } + + + public static List getInvokedInterceptor() { + return invokedInterceptors.get(); + } + + + public static void clear() { + invokedInterceptors.get().clear(); + invokedInterceptors.remove(); + } + + +} diff --git a/src/main/java/io/jboot/web/handler/JbootInvocationWarpper.java b/src/main/java/io/jboot/web/handler/JbootInvocationWarpper.java deleted file mode 100644 index d13277fb..00000000 --- a/src/main/java/io/jboot/web/handler/JbootInvocationWarpper.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.jboot.web.handler; - -import com.jfinal.aop.Interceptor; -import com.jfinal.aop.Invocation; -import com.jfinal.core.Action; -import com.jfinal.core.Controller; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author michael yang (fuhai999@gmail.com) - */ -public class JbootInvocationWarpper extends Invocation { - - private Interceptor[] inters; - private int index = 0; - - private static ThreadLocal> invokedInterceptors = ThreadLocal.withInitial(ArrayList::new); - - public JbootInvocationWarpper(Action action, Controller controller) { - super(action, controller); - this.inters = action.getInterceptors(); - } - - @Override - public void invoke() { - if (index < inters.length) { - invokedInterceptors.get().add(inters[index++]); - } - super.invoke(); - } - - - public static List getInvokedInterceptor() { - return invokedInterceptors.get(); - } - - - public static void clear(){ - invokedInterceptors.get().clear(); - invokedInterceptors.remove(); - } -} -- Gitee From c8d565044efa50ca76b44f4ad0e9fd6fd6d731c3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Sep 2020 15:37:33 +0800 Subject: [PATCH 0531/1965] add InterceptorBuilder to build Interceptor dynamic --- src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java index 2c0d0409..55f83a24 100644 --- a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java @@ -50,7 +50,7 @@ class JbootCglibCallback implements MethodInterceptor { inters = interMan.buildServiceMethodInterceptor(targetClass, method); // builder 再次构建 - inters = InterceptorBuilderManager.me().build(targetClass,method,inters); + inters = InterceptorBuilderManager.me().build(targetClass, method, inters); JbootCglibProxyFactory.IntersCache.put(key, inters); } @@ -67,7 +67,6 @@ class JbootCglibCallback implements MethodInterceptor { } - private static final Set buildExcludedMethodName() { Set excludedMethodName = new HashSet(64, 0.25F); Method[] methods = Object.class.getDeclaredMethods(); @@ -81,7 +80,6 @@ class JbootCglibCallback implements MethodInterceptor { } - private static LinkedList toLinkedList(Interceptor[] interceptors) { LinkedList linkedList = new LinkedList<>(); if (interceptors != null) { -- Gitee From 5c5d0b76f68e677f19d595584fd1502749d0395b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Sep 2020 16:07:54 +0800 Subject: [PATCH 0532/1965] add InterceptorBuilder to build Interceptor dynamic --- .../fixedinterceptor/FixedInterceptors.java | 7 ++++--- .../jboot/web/handler/JbootActionHandler.java | 18 +----------------- .../io/jboot/web/handler/JbootInvocation.java | 1 + 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java index 394abd76..bbc13cb4 100644 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java +++ b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java @@ -16,6 +16,7 @@ package io.jboot.web.fixedinterceptor; import com.jfinal.aop.Aop; +import com.jfinal.aop.Interceptor; import io.jboot.components.limiter.LimiterInterceptor; import io.jboot.support.jwt.JwtInterceptor; import io.jboot.support.metric.JbootMetricInterceptor; @@ -61,11 +62,11 @@ public class FixedInterceptors { private List userInters = new ArrayList<>(); - private FixedInterceptor[] allInters = null; + private Interceptor[] allInters = null; private List inters; - public FixedInterceptor[] all() { + public Interceptor[] all() { if (allInters == null) { synchronized (this) { if (allInters == null) { @@ -79,7 +80,7 @@ public class FixedInterceptors { private void initInters() { - FixedInterceptor[] interceptors = new FixedInterceptor[defaultInters.length + userInters.size()]; + Interceptor[] interceptors = new Interceptor[defaultInters.length + userInters.size()]; inters = new ArrayList<>(); inters.addAll(Arrays.asList(defaultInters)); inters.addAll(userInters); diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 7cc966e9..71acc14b 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -73,10 +73,6 @@ public class JbootActionHandler extends ActionHandler { } - public void setResponse(HttpServletResponse response, Action action) { - } - - /** * handle * 1: Action action = actionMapping.getAction(target) @@ -113,13 +109,6 @@ public class JbootActionHandler extends ActionHandler { // Invocation invocation = new Invocation(action, controller); Invocation invocation = getInvocation(action, controller); if (devMode) { -// if (ActionReporter.isReportAfterInvocation(request)) { -// invokeInvocation(invocation); -// JbootActionReporter.report(target, controller, action); -// } else { -// JbootActionReporter.report(target, controller, action); -// invokeInvocation(invocation); -// } long time = System.currentTimeMillis(); try { invocation.invoke(); @@ -141,9 +130,7 @@ public class JbootActionHandler extends ActionHandler { return; } - if (render == null - && void.class != action.getMethod().getReturnType() - && renderManager.getRenderFactory() instanceof JbootRenderFactory) { + if (render == null && void.class != action.getMethod().getReturnType() && renderManager.getRenderFactory() instanceof JbootRenderFactory) { JbootRenderFactory jrf = (JbootRenderFactory) renderManager.getRenderFactory(); render = jrf.getReturnValueRender(action, invocation.getReturnValue()); } @@ -152,9 +139,6 @@ public class JbootActionHandler extends ActionHandler { render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); } - //设置 response 的一些信息,比如 headers 等 - setResponse(response, action); - render.setContext(request, response, action.getViewPath()).render(); } catch (RenderException e) { diff --git a/src/main/java/io/jboot/web/handler/JbootInvocation.java b/src/main/java/io/jboot/web/handler/JbootInvocation.java index 0af1dfd0..251cb8b1 100644 --- a/src/main/java/io/jboot/web/handler/JbootInvocation.java +++ b/src/main/java/io/jboot/web/handler/JbootInvocation.java @@ -67,6 +67,7 @@ public class JbootInvocation extends Invocation { // jfinal 原生的构建 inters = ArrayUtil.concat(FixedInterceptors.me().all(), action.getInterceptors()); + // builder 再次构建 inters = InterceptorBuilderManager.me().build(action.getControllerClass(), action.getMethod(), inters); -- Gitee From c0671940b27755f6ff10eb1012a5fc6856ec81c3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Sep 2020 16:34:47 +0800 Subject: [PATCH 0533/1965] add LocalAttachmentContainerConfig --- .../web/attachment/AttachmentManager.java | 5 ++- .../attachment/LocalAttachmentContainer.java | 12 ++--- .../LocalAttachmentContainerConfig.java | 45 +++++++++++++++++++ 3 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index 0793f4e3..441c08b1 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -110,7 +110,7 @@ public class AttachmentManager { LOG.error("get file error in container :" + container, ex); } } - return relativePath; + return relativePath.replace("\\", "/"); } @@ -175,6 +175,7 @@ public class AttachmentManager { * @return */ public String getRelativePath(File file) { - return getDefaultContainer().getRelativePath(file); + String relativePath = getDefaultContainer().getRelativePath(file); + return relativePath != null ? relativePath.replace("\\", "/") : null; } } diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java index 42986b8e..e06d8ec1 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -17,8 +17,8 @@ package io.jboot.web.attachment; import com.jfinal.ext.kit.DateKit; import com.jfinal.kit.LogKit; -import com.jfinal.kit.PathKit; import com.jfinal.render.FileRender; +import io.jboot.Jboot; import io.jboot.utils.FileUtil; import io.jboot.utils.StrUtil; @@ -33,13 +33,13 @@ import java.util.Date; */ public class LocalAttachmentContainer implements AttachmentContainer { - public static final String DEFAULT_ATTACHEMENT_PATH = "attachment"; - - private String rootPath = PathKit.getWebRootPath(); - private String targetPrefix = DEFAULT_ATTACHEMENT_PATH; - + private String rootPath ; + private String targetPrefix ; public LocalAttachmentContainer() { + LocalAttachmentContainerConfig config = Jboot.config(LocalAttachmentContainerConfig.class); + this.rootPath = config.getRootPath(); + this.targetPrefix = config.getTargetPrefix(); } public LocalAttachmentContainer(String rootPath, String targetPrefix) { diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java new file mode 100644 index 00000000..970bb576 --- /dev/null +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.attachment; + +import com.jfinal.kit.PathKit; +import io.jboot.app.config.annotation.ConfigModel; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +@ConfigModel(prefix = "jboot.attachment") +public class LocalAttachmentContainerConfig { + + private String rootPath = PathKit.getWebRootPath(); + private String targetPrefix = "attachment"; + + public String getRootPath() { + return rootPath; + } + + public void setRootPath(String rootPath) { + this.rootPath = rootPath; + } + + public String getTargetPrefix() { + return targetPrefix; + } + + public void setTargetPrefix(String targetPrefix) { + this.targetPrefix = targetPrefix; + } +} -- Gitee From 1cdde6ecb722763cdd9d9d93b4f62f1a590c3431 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Sep 2020 16:56:52 +0800 Subject: [PATCH 0534/1965] optimize AttachmentManager --- .../web/attachment/AttachmentManager.java | 89 ++++++++----------- 1 file changed, 39 insertions(+), 50 deletions(-) diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index 441c08b1..8e88a8ac 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -20,8 +20,8 @@ import com.jfinal.log.Log; import javax.servlet.http.HttpServletRequest; import java.io.File; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; /** * @author michael yang (fuhai999@gmail.com) @@ -29,7 +29,6 @@ import java.util.concurrent.ConcurrentHashMap; public class AttachmentManager { private static final Log LOG = Log.getLog(AttachmentManager.class); - private static final String DEFAULT_KEY = "default"; private static final AttachmentManager ME = new AttachmentManager(); @@ -39,48 +38,51 @@ public class AttachmentManager { } private AttachmentManager() { - containerMap.put(DEFAULT_KEY, new LocalAttachmentContainer()); } + /** + * 默认的 附件容器 + */ + private AttachmentContainer defaultContainer = new LocalAttachmentContainer(); - private Map containerMap = new ConcurrentHashMap<>(); - - - public AttachmentContainer matchContainer(String target, HttpServletRequest request) { - if (containerMap.size() == 0) { - return null; - } + /** + * 其他附件容器 + */ + private List containers = new CopyOnWriteArrayList<>(); - for (AttachmentContainer container : containerMap.values()) { - if (container.matchFile(target, request)) { - return container; - } - } - return null; + public AttachmentContainer getDefaultContainer() { + return defaultContainer; } + public void setDefaultContainer(AttachmentContainer defaultContainer) { + this.defaultContainer = defaultContainer; + } - public void addContainer(String name, AttachmentContainer container) { - containerMap.put(name, container); + public void addContainer(AttachmentContainer container) { + containers.add(container); } - public AttachmentContainer getContainer(String name) { - return getContainer(name); + public void setContainers(List containers) { + this.containers = containers; } - public Map getContainerMap() { - return containerMap; + + public List getContainers() { + return containers; } - public AttachmentContainer getDefaultContainer() { - AttachmentContainer defaultContainer = containerMap.get(DEFAULT_KEY); - if (defaultContainer != null) { + + public AttachmentContainer matchContainer(String target, HttpServletRequest request) { + + if (defaultContainer.matchFile(target, request)) { return defaultContainer; } - if (containerMap.size() > 0) { - return containerMap.values().stream().findFirst().get(); + for (AttachmentContainer container : containers) { + if (container.matchFile(target, request)) { + return container; + } } return null; @@ -94,14 +96,11 @@ public class AttachmentManager { * @return 返回文件的相对路径 */ public String saveFile(File file) { - AttachmentContainer defaultContainer = getDefaultContainer(); - //优先从 默认的 container 去保存文件 String relativePath = defaultContainer.saveFile(file); File defaultContainerFile = defaultContainer.getFile(relativePath); - for (Map.Entry entry : containerMap.entrySet()) { - AttachmentContainer container = entry.getValue(); + for (AttachmentContainer container : containers) { try { if (container != defaultContainer) { container.saveFile(defaultContainerFile); @@ -121,19 +120,14 @@ public class AttachmentManager { * @return */ public boolean deleteFile(String relativePath) { - boolean ret = false; - for (Map.Entry entry : containerMap.entrySet()) { - AttachmentContainer container = entry.getValue(); + for (AttachmentContainer container : containers) { try { - boolean result = container.deleteFile(relativePath); - if (DEFAULT_KEY.equals(entry.getKey())) { - ret = result; - } + container.deleteFile(relativePath); } catch (Exception ex) { LOG.error("delete file error in container :" + container, ex); } } - return ret; + return defaultContainer.deleteFile(relativePath); } /** @@ -144,22 +138,17 @@ public class AttachmentManager { */ public File getFile(String relativePath) { - AttachmentContainer defaultContainer = getDefaultContainer(); - //优先从 默认的 container 去获取 File file = defaultContainer.getFile(relativePath); if (file != null && file.exists()) { return file; } - for (Map.Entry entry : containerMap.entrySet()) { - AttachmentContainer container = entry.getValue(); + for (AttachmentContainer container : containers) { try { - if (container != defaultContainer) { - file = container.getFile(relativePath); - if (file != null && file.exists()) { - return file; - } + file = container.getFile(relativePath); + if (file != null && file.exists()) { + return file; } } catch (Exception ex) { LOG.error("get file error in container :" + container, ex); @@ -175,7 +164,7 @@ public class AttachmentManager { * @return */ public String getRelativePath(File file) { - String relativePath = getDefaultContainer().getRelativePath(file); + String relativePath = defaultContainer.getRelativePath(file); return relativePath != null ? relativePath.replace("\\", "/") : null; } } -- Gitee From a03506b3bcecc9f7a688f82a7222cd29eae0c571 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Sep 2020 17:14:00 +0800 Subject: [PATCH 0535/1965] optimize AttachmentManager --- src/main/java/io/jboot/core/JbootCoreConfig.java | 10 ++++------ .../web/attachment/LocalAttachmentContainer.java | 12 ++++++++---- .../attachment/LocalAttachmentContainerConfig.java | 11 +++++++++++ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 91374bfb..bb9c1fd5 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -49,7 +49,7 @@ import io.jboot.support.swagger.JbootSwaggerManager; import io.jboot.utils.*; import io.jboot.web.JbootJson; import io.jboot.web.attachment.AttachmentHandler; -import io.jboot.web.attachment.LocalAttachmentContainer; +import io.jboot.web.attachment.LocalAttachmentContainerConfig; import io.jboot.web.controller.JbootControllerManager; import io.jboot.web.controller.annotation.RequestMapping; import io.jboot.web.directive.annotation.JFinalDirective; @@ -146,8 +146,7 @@ public class JbootCoreConfig extends JFinalConfig { constants.setJsonFactory(JbootJson::new); constants.setInjectDependency(true); - constants.setBaseUploadPath(LocalAttachmentContainer.DEFAULT_ATTACHEMENT_PATH); - + constants.setBaseUploadPath(LocalAttachmentContainerConfig.getInstance().buildUploadAbsolutePath()); JbootAppListenerManager.me().onConstantConfig(constants); @@ -159,7 +158,6 @@ public class JbootCoreConfig extends JFinalConfig { routes.setMappingSuperClass(true); - List> controllerClassList = ClassScanner.scanSubClass(Controller.class); if (ArrayUtil.isNotEmpty(controllerClassList)) { for (Class clazz : controllerClassList) { @@ -210,7 +208,7 @@ public class JbootCoreConfig extends JFinalConfig { for (Class clazz : directiveClasses) { JFinalDirective directive = (JFinalDirective) clazz.getAnnotation(JFinalDirective.class); if (directive != null) { - if (directive.override()){ + if (directive.override()) { //remove old directive engine.removeDirective(directive.value()); } @@ -274,7 +272,7 @@ public class JbootCoreConfig extends JFinalConfig { //metrics 处理 JbootMetricConfig metricsConfig = Jboot.config(JbootMetricConfig.class); - if (metricsConfig.isConfigOk()){ + if (metricsConfig.isConfigOk()) { handlers.add(new JbootMetricsHandler(metricsConfig.getUrl())); } diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java index e06d8ec1..e6290c96 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -18,7 +18,6 @@ package io.jboot.web.attachment; import com.jfinal.ext.kit.DateKit; import com.jfinal.kit.LogKit; import com.jfinal.render.FileRender; -import io.jboot.Jboot; import io.jboot.utils.FileUtil; import io.jboot.utils.StrUtil; @@ -33,15 +32,20 @@ import java.util.Date; */ public class LocalAttachmentContainer implements AttachmentContainer { - private String rootPath ; - private String targetPrefix ; + private String rootPath; + private String targetPrefix; public LocalAttachmentContainer() { - LocalAttachmentContainerConfig config = Jboot.config(LocalAttachmentContainerConfig.class); + LocalAttachmentContainerConfig config = LocalAttachmentContainerConfig.getInstance(); this.rootPath = config.getRootPath(); this.targetPrefix = config.getTargetPrefix(); } + /** + * + * @param rootPath + * @param targetPrefix 不能以 / 开头 + */ public LocalAttachmentContainer(String rootPath, String targetPrefix) { this.rootPath = rootPath; this.targetPrefix = targetPrefix; diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java index 970bb576..213943ed 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java @@ -16,8 +16,11 @@ package io.jboot.web.attachment; import com.jfinal.kit.PathKit; +import io.jboot.Jboot; import io.jboot.app.config.annotation.ConfigModel; +import java.io.File; + /** * @author michael yang (fuhai999@gmail.com) */ @@ -42,4 +45,12 @@ public class LocalAttachmentContainerConfig { public void setTargetPrefix(String targetPrefix) { this.targetPrefix = targetPrefix; } + + public static LocalAttachmentContainerConfig getInstance(){ + return Jboot.config(LocalAttachmentContainerConfig.class); + } + + public String buildUploadAbsolutePath(){ + return new File(rootPath,targetPrefix).getAbsolutePath(); + } } -- Gitee From 83793a3c9aafff3255bde79a8b17d8db6c20b331 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Sep 2020 17:39:41 +0800 Subject: [PATCH 0536/1965] add @AutoLoad annotation --- .../io/jboot/aop/annotation/AutoLoad.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/io/jboot/aop/annotation/AutoLoad.java diff --git a/src/main/java/io/jboot/aop/annotation/AutoLoad.java b/src/main/java/io/jboot/aop/annotation/AutoLoad.java new file mode 100644 index 00000000..41f6ed5f --- /dev/null +++ b/src/main/java/io/jboot/aop/annotation/AutoLoad.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop.annotation; + +import java.lang.annotation.*; + +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface AutoLoad { +} \ No newline at end of file -- Gitee From 5097b731ae30d5c5b046c3d370dc870a1aa83ea2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Sep 2020 18:08:38 +0800 Subject: [PATCH 0537/1965] add @AutoLoad annotation --- .../jboot/aop/InterceptorBuilderManager.java | 15 +++++++++++ .../java/io/jboot/utils/ClassScanner.java | 26 +++++-------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java index 982f4e72..32072f8b 100644 --- a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java +++ b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java @@ -18,8 +18,11 @@ package io.jboot.aop; import com.jfinal.aop.AopFactory; import com.jfinal.aop.Interceptor; import com.jfinal.aop.InterceptorManager; +import io.jboot.aop.annotation.AutoLoad; import io.jboot.aop.cglib.JbootCglibProxyFactory; import io.jboot.core.weight.WeightUtil; +import io.jboot.utils.ClassScanner; +import io.jboot.utils.ClassUtil; import java.lang.reflect.Method; import java.util.Collection; @@ -37,6 +40,18 @@ public class InterceptorBuilderManager extends AopFactory { return me; } + public InterceptorBuilderManager() { + List> builderClasses = ClassScanner.scanSubClass(InterceptorBuilder.class,true); + if (builderClasses != null){ + for (Class builderClass : builderClasses){ + if (builderClass.getAnnotation(AutoLoad.class) != null){ + interceptorBuilders.add(ClassUtil.newInstance(builderClass,false)); + } + } + + JbootCglibProxyFactory.IntersCache.clear(); + } + } private List interceptorBuilders = new CopyOnWriteArrayList(); diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 040aa3cd..013caedc 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -305,20 +305,6 @@ public class ClassScanner { excludeClasses.add("cn.hutool."); excludeClasses.add("com.dyuproject."); excludeClasses.add("io.protostuff."); - excludeClasses.add("io.jboot.core."); - excludeClasses.add("io.jboot.web."); - excludeClasses.add("io.jboot.objects."); - excludeClasses.add("io.jboot.utils."); - excludeClasses.add("io.jboot.codegen."); - excludeClasses.add("io.jboot.wechat."); - excludeClasses.add("io.jboot.components."); - excludeClasses.add("io.jboot.support."); - excludeClasses.add("io.jboot.exception."); - excludeClasses.add("io.jboot.db."); - excludeClasses.add("io.jboot.aop."); - excludeClasses.add("io.jboot.app."); - excludeClasses.add("io.jboot.service."); - excludeClasses.add("io.jboot.Jboot"); excludeClasses.add("freemarker."); excludeClasses.add("com.twelvemonkeys."); } @@ -380,10 +366,10 @@ public class ClassScanner { } - public static List> scanSubClass(Class pclazz, boolean isInstantiable) { + public static List> scanSubClass(Class pclazz, boolean instantiable) { initIfNecessary(); List> classes = new ArrayList<>(); - findChildClasses(classes, pclazz, isInstantiable); + findChildClasses(classes, pclazz, instantiable); return classes; } @@ -415,7 +401,7 @@ public class ClassScanner { } - public static List scanClassByAnnotation(Class annotationClass, boolean isInstantiable) { + public static List scanClassByAnnotation(Class annotationClass, boolean instantiable) { initIfNecessary(); List list = new ArrayList<>(); @@ -425,7 +411,7 @@ public class ClassScanner { continue; } - if (isInstantiable && !isInstantiable(clazz)) { + if (instantiable && !isInstantiable(clazz)) { continue; } @@ -442,14 +428,14 @@ public class ClassScanner { } - private static void findChildClasses(List> classes, Class parent, boolean isInstantiable) { + private static void findChildClasses(List> classes, Class parent, boolean instantiable) { for (Class clazz : appClassesCache) { if (!parent.isAssignableFrom(clazz)) { continue; } - if (isInstantiable && !isInstantiable(clazz)) { + if (instantiable && !isInstantiable(clazz)) { continue; } -- Gitee From a4a730713ca84fb0b2a4f75317d7e33c8a85782f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Sep 2020 19:26:44 +0800 Subject: [PATCH 0538/1965] optimize LocalAttachmentContainer --- .../web/attachment/LocalAttachmentContainer.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java index e6290c96..dced0433 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -42,7 +42,6 @@ public class LocalAttachmentContainer implements AttachmentContainer { } /** - * * @param rootPath * @param targetPrefix 不能以 / 开头 */ @@ -133,7 +132,7 @@ public class LocalAttachmentContainer implements AttachmentContainer { private Boolean runInFatjar; - private boolean isRunInfatjar() { + protected boolean isRunInFatjar() { if (runInFatjar == null) { runInFatjar = LocalAttachmentContainer.class.getResource("/") == null; } @@ -144,14 +143,12 @@ public class LocalAttachmentContainer implements AttachmentContainer { @Override public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { //是否在 fatjar 运行,如果不在 fatjar 运行,由 tomcat 或者 undertow 自行渲染 - if (!isRunInfatjar()) { + if (!isRunInFatjar()) { return false; } // 如果在 fatjar 运行,需要自己来渲染该文件 - else { - new FileRender(getFile(target)).setContext(request, response).render(); - return true; - } + new FileRender(getFile(target)).setContext(request, response).render(); + return true; } -- Gitee From 3b5199683b46164efbd8cafd26d520c02aef0beb Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 17 Sep 2020 15:51:13 +0800 Subject: [PATCH 0539/1965] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20=20InterceptorBu?= =?UTF-8?q?ilder=20=E9=87=8D=E6=9E=84=E6=8B=A6=E6=88=AA=E5=99=A8=E7=9A=84?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/aop/JbootAopInterceptor.java | 152 ---------------- ...ceptor.java => CacheEvictInterceptor.java} | 2 +- .../interceptor/CacheInterceptorBuilder.java | 60 ++++++ ...erceptor.java => CachePutInterceptor.java} | 2 +- ...rceptor.java => CacheableInterceptor.java} | 2 +- ...eptor.java => CachesEvictInterceptor.java} | 4 +- .../components/limiter/LimiterManager.java | 7 + .../BaseLimiterInterceptor.java} | 63 +------ .../interceptor/LimiterGlobalInterceptor.java | 58 ++++++ .../interceptor/LimiterInterceptor.java | 57 ++++++ .../LimiterInterceptorBuilder.java | 51 ++++++ .../java/io/jboot/core/JbootCoreConfig.java | 3 - .../java/io/jboot/support/jwt/EnableJwt.java | 30 +++ .../io/jboot/support/jwt/JwtInterceptor.java | 5 +- .../support/jwt/JwtInterceptorBuilder.java | 48 +++++ .../MetricConcurrencyInterceptor.java} | 76 +------- .../interceptor/MetricCounterInterceptor.java | 53 ++++++ .../MetricHistogramInterceptor.java | 49 +++++ .../interceptor/MetricInterceptorBuilder.java | 70 +++++++ .../interceptor/MetricMeterInterceptor.java | 48 +++++ .../interceptor/MetricTimerInterceptor.java | 58 ++++++ .../SeataGlobalInterceptorBuilder.java | 46 +++++ .../seata/tcc/TccInterceptorBuilder.java | 47 +++++ .../support/sentinel/SentinelInterceptor.java | 3 +- .../sentinel/SentinelInterceptorBuilder.java | 47 +++++ .../support/shiro/JbootShiroInterceptor.java | 7 +- .../shiro/ShiroInterceptorBuilder.java | 44 +++++ .../web/cors/CORSInterceptorBuilder.java | 48 +++++ .../java/io/jboot/web/cors/EnableCORS.java | 49 ----- .../fixedinterceptor/FixedInterceptors.java | 27 +-- .../jboot/web/handler/JbootActionHandler.java | 2 +- .../web/handler/JbootActionReporter.java | 9 +- .../io/jboot/web/handler/JbootInvocation.java | 4 +- .../CaptchaValidateInterceptor.java | 83 +++++++++ .../EmptyValidateInterceptor.java} | 172 +----------------- .../interceptor/RegexValidateInterceptor.java | 116 ++++++++++++ .../jboot/web/validate/interceptor/Util.java | 67 +++++++ .../ValidateInterceptorBuilder.java | 57 ++++++ 38 files changed, 1191 insertions(+), 535 deletions(-) delete mode 100644 src/main/java/io/jboot/aop/JbootAopInterceptor.java rename src/main/java/io/jboot/components/cache/interceptor/{JbootCacheEvictInterceptor.java => CacheEvictInterceptor.java} (95%) create mode 100644 src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java rename src/main/java/io/jboot/components/cache/interceptor/{JbootCachePutInterceptor.java => CachePutInterceptor.java} (96%) rename src/main/java/io/jboot/components/cache/interceptor/{JbootCacheInterceptor.java => CacheableInterceptor.java} (98%) rename src/main/java/io/jboot/components/cache/interceptor/{JbootCachesEvictInterceptor.java => CachesEvictInterceptor.java} (94%) rename src/main/java/io/jboot/components/limiter/{LimiterInterceptor.java => interceptor/BaseLimiterInterceptor.java} (46%) create mode 100644 src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java create mode 100644 src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java create mode 100644 src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java create mode 100644 src/main/java/io/jboot/support/jwt/EnableJwt.java create mode 100644 src/main/java/io/jboot/support/jwt/JwtInterceptorBuilder.java rename src/main/java/io/jboot/support/metric/{JbootMetricInterceptor.java => interceptor/MetricConcurrencyInterceptor.java} (38%) create mode 100644 src/main/java/io/jboot/support/metric/interceptor/MetricCounterInterceptor.java create mode 100644 src/main/java/io/jboot/support/metric/interceptor/MetricHistogramInterceptor.java create mode 100644 src/main/java/io/jboot/support/metric/interceptor/MetricInterceptorBuilder.java create mode 100644 src/main/java/io/jboot/support/metric/interceptor/MetricMeterInterceptor.java create mode 100644 src/main/java/io/jboot/support/metric/interceptor/MetricTimerInterceptor.java create mode 100644 src/main/java/io/jboot/support/seata/interceptor/SeataGlobalInterceptorBuilder.java create mode 100644 src/main/java/io/jboot/support/seata/tcc/TccInterceptorBuilder.java create mode 100644 src/main/java/io/jboot/support/sentinel/SentinelInterceptorBuilder.java create mode 100644 src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java create mode 100644 src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java delete mode 100644 src/main/java/io/jboot/web/cors/EnableCORS.java create mode 100644 src/main/java/io/jboot/web/validate/interceptor/CaptchaValidateInterceptor.java rename src/main/java/io/jboot/web/validate/{ValidateInterceptor.java => interceptor/EmptyValidateInterceptor.java} (35%) create mode 100644 src/main/java/io/jboot/web/validate/interceptor/RegexValidateInterceptor.java create mode 100644 src/main/java/io/jboot/web/validate/interceptor/Util.java create mode 100644 src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorBuilder.java diff --git a/src/main/java/io/jboot/aop/JbootAopInterceptor.java b/src/main/java/io/jboot/aop/JbootAopInterceptor.java deleted file mode 100644 index c668ad0f..00000000 --- a/src/main/java/io/jboot/aop/JbootAopInterceptor.java +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.aop; - -import com.jfinal.aop.Interceptor; -import com.jfinal.aop.Invocation; -import io.jboot.components.cache.interceptor.JbootCacheEvictInterceptor; -import io.jboot.components.cache.interceptor.JbootCacheInterceptor; -import io.jboot.components.cache.interceptor.JbootCachePutInterceptor; -import io.jboot.components.cache.interceptor.JbootCachesEvictInterceptor; -import io.jboot.components.limiter.LimiterInterceptor; -import io.jboot.support.metric.JbootMetricInterceptor; -import io.jboot.support.seata.interceptor.SeataGlobalTransactionalInterceptor; -import io.jboot.support.seata.tcc.TccActionInterceptor; -import io.jboot.support.sentinel.SentinelInterceptor; - -import java.util.LinkedList; -import java.util.List; - -public class JbootAopInterceptor implements Interceptor { - - static final Interceptor[] JBOOT_INTERS = { - new SentinelInterceptor(), - new LimiterInterceptor(), - new JbootMetricInterceptor(), - new JbootCacheEvictInterceptor(), - new JbootCachesEvictInterceptor(), - new JbootCachePutInterceptor(), - new JbootCacheInterceptor(), - new SeataGlobalTransactionalInterceptor(), - new TccActionInterceptor() - }; - - - private static Interceptor[] aopInterceptors = JBOOT_INTERS; - - - @Override - public void intercept(Invocation inv) { - JbootAopInvocation invocation = new JbootAopInvocation(inv, aopInterceptors); - invocation.invoke(); - } - - - /** - * 添加新的拦截器 - * - * @param interceptor - * @param toIndex - */ - public static void addInterceptor(Interceptor interceptor, int toIndex) { - - if (interceptor == null) { - throw new NullPointerException("interceptor is null"); - } - - synchronized (JbootAopInterceptor.class) { - - int length = aopInterceptors.length; - - if (toIndex < 0) { - toIndex = 0; - } - - if (toIndex > length) { - toIndex = length; - } - - Interceptor[] temp = new Interceptor[length + 1]; - - System.arraycopy(aopInterceptors, 0, temp, 0, toIndex); - temp[toIndex] = interceptor; - if (toIndex < length) { - System.arraycopy(aopInterceptors, toIndex, temp, toIndex + 1, length - toIndex); - } - - aopInterceptors = temp; - } - } - - - /** - * 移除拦截器 - * - * @param interceptor - */ - public static void removeInterceptor(Interceptor interceptor) { - - if (interceptor == null) { - throw new NullPointerException("interceptor is null"); - } - - synchronized (JbootAopInterceptor.class) { - - int length = aopInterceptors.length; - List tempList = new LinkedList<>(); - - for (int i = 0; i < length; i++) { - if (aopInterceptors[i] != interceptor) { - tempList.add(aopInterceptors[i]); - } - } - - if (tempList.size() != length) { - aopInterceptors = tempList.toArray(new Interceptor[]{}); - } - } - } - - - /** - * 移除拦截器 - * - * @param clazz - */ - public static void removeInterceptor(Class clazz) { - - if (clazz == null) { - throw new NullPointerException("interceptor class is null"); - } - - synchronized (JbootAopInterceptor.class) { - - int length = aopInterceptors.length; - List tempList = new LinkedList<>(); - - for (int i = 0; i < length; i++) { - if (aopInterceptors[i].getClass() != clazz) { - tempList.add(aopInterceptors[i]); - } - } - - if (tempList.size() != length) { - aopInterceptors = tempList.toArray(new Interceptor[]{}); - } - } - } - -} diff --git a/src/main/java/io/jboot/components/cache/interceptor/JbootCacheEvictInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheEvictInterceptor.java similarity index 95% rename from src/main/java/io/jboot/components/cache/interceptor/JbootCacheEvictInterceptor.java rename to src/main/java/io/jboot/components/cache/interceptor/CacheEvictInterceptor.java index 3af70ddd..83c3e523 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/JbootCacheEvictInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheEvictInterceptor.java @@ -25,7 +25,7 @@ import java.lang.reflect.Method; /** * 清除缓存操作的拦截器 */ -public class JbootCacheEvictInterceptor implements Interceptor { +public class CacheEvictInterceptor implements Interceptor { @Override public void intercept(Invocation inv) { diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java b/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java new file mode 100644 index 00000000..6b14c874 --- /dev/null +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.cache.interceptor; + +import com.jfinal.aop.Interceptor; +import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.annotation.AutoLoad; +import io.jboot.components.cache.annotation.CacheEvict; +import io.jboot.components.cache.annotation.CachePut; +import io.jboot.components.cache.annotation.Cacheable; +import io.jboot.components.cache.annotation.CachesEvict; + +import java.lang.reflect.Method; +import java.util.LinkedList; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +@AutoLoad +public class CacheInterceptorBuilder implements InterceptorBuilder { + + + @Override + public void build(Class serviceClass, Method method, LinkedList interceptors) { + CacheEvict cacheEvict = method.getAnnotation(CacheEvict.class); + if (cacheEvict != null) { + interceptors.add(new CacheEvictInterceptor()); + } + + Cacheable cacheable = method.getAnnotation(Cacheable.class); + if (cacheable != null) { + interceptors.add(new CacheableInterceptor()); + } + + CachePut cachePut = method.getAnnotation(CachePut.class); + if (cachePut != null) { + interceptors.add(new CachePutInterceptor()); + } + + CachesEvict cachesEvict = method.getAnnotation(CachesEvict.class); + if (cachesEvict != null) { + interceptors.add(new CachesEvictInterceptor()); + } + } + + +} diff --git a/src/main/java/io/jboot/components/cache/interceptor/JbootCachePutInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java similarity index 96% rename from src/main/java/io/jboot/components/cache/interceptor/JbootCachePutInterceptor.java rename to src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java index 02b7f5ed..eee0ff5c 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/JbootCachePutInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java @@ -26,7 +26,7 @@ import java.lang.reflect.Method; /** * 缓存设置拦截器 */ -public class JbootCachePutInterceptor implements Interceptor { +public class CachePutInterceptor implements Interceptor { @Override public void intercept(Invocation inv) { diff --git a/src/main/java/io/jboot/components/cache/interceptor/JbootCacheInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java similarity index 98% rename from src/main/java/io/jboot/components/cache/interceptor/JbootCacheInterceptor.java rename to src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java index 3b36a95f..9e2f8bc2 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/JbootCacheInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java @@ -36,7 +36,7 @@ import java.util.Set; * * @author michael yang */ -public class JbootCacheInterceptor implements Interceptor { +public class CacheableInterceptor implements Interceptor { private static final String NULL_VALUE = "NULL_VALUE"; diff --git a/src/main/java/io/jboot/components/cache/interceptor/JbootCachesEvictInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CachesEvictInterceptor.java similarity index 94% rename from src/main/java/io/jboot/components/cache/interceptor/JbootCachesEvictInterceptor.java rename to src/main/java/io/jboot/components/cache/interceptor/CachesEvictInterceptor.java index d5bec562..797fa784 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/JbootCachesEvictInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CachesEvictInterceptor.java @@ -29,8 +29,8 @@ import java.util.List; /** * 清除缓存操作的拦截器 */ -public class JbootCachesEvictInterceptor implements Interceptor { - private static final Log LOG = Log.getLog(JbootCachesEvictInterceptor.class); +public class CachesEvictInterceptor implements Interceptor { + private static final Log LOG = Log.getLog(CachesEvictInterceptor.class); @Override diff --git a/src/main/java/io/jboot/components/limiter/LimiterManager.java b/src/main/java/io/jboot/components/limiter/LimiterManager.java index 9e0aee5a..27f73337 100644 --- a/src/main/java/io/jboot/components/limiter/LimiterManager.java +++ b/src/main/java/io/jboot/components/limiter/LimiterManager.java @@ -154,6 +154,13 @@ public class LimiterManager { return semaphore; } + public Set getConfigPackageOrTargets() { + return configPackageOrTargets; + } + + public void setConfigPackageOrTargets(Set configPackageOrTargets) { + this.configPackageOrTargets = configPackageOrTargets; + } private static boolean match(String string, String regex) { Pattern pattern = Pattern.compile(regex); diff --git a/src/main/java/io/jboot/components/limiter/LimiterInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java similarity index 46% rename from src/main/java/io/jboot/components/limiter/LimiterInterceptor.java rename to src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java index c7d499e9..607f25f6 100644 --- a/src/main/java/io/jboot/components/limiter/LimiterInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java @@ -13,69 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.jboot.components.limiter; +package io.jboot.components.limiter.interceptor; import com.google.common.util.concurrent.RateLimiter; -import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; -import io.jboot.components.limiter.annotation.EnableLimit; -import io.jboot.utils.AnnotationUtil; +import io.jboot.components.limiter.LimiterManager; import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; -import io.jboot.web.fixedinterceptor.FixedInterceptor; import javax.servlet.http.HttpServletRequest; import java.util.concurrent.Semaphore; -public class LimiterInterceptor implements FixedInterceptor, Interceptor { +public abstract class BaseLimiterInterceptor { - @Override - public void intercept(Invocation inv) { - String packageOrTarget = getPackageOrTarget(inv); - LimiterManager.TypeAndRate typeAndRate = LimiterManager.me().matchConfig(packageOrTarget); - if (typeAndRate != null) { - doInterceptByTypeAndRate(typeAndRate, packageOrTarget, inv); - return; - } - - EnableLimit enableLimit = inv.getMethod().getAnnotation(EnableLimit.class); - if (enableLimit != null) { - String resource = StrUtil.obtainDefaultIfBlank(enableLimit.resource(), packageOrTarget); - doInterceptByLimitInfo(enableLimit, resource, inv); - return; - } - - inv.invoke(); - } - - - private void doInterceptByTypeAndRate(LimiterManager.TypeAndRate typeAndRate, String resource, Invocation inv) { - switch (typeAndRate.getType()) { - case LimitType.CONCURRENCY: - doInterceptForConcurrency(typeAndRate.getRate(), resource, null, inv); - break; - case LimitType.TOKEN_BUCKET: - doInterceptForTokenBucket(typeAndRate.getRate(), resource, null, inv); - break; - } - } - - private void doInterceptByLimitInfo(EnableLimit enableLimit, String resource, Invocation inv) { - String type = AnnotationUtil.get(enableLimit.type()); - switch (type) { - case LimitType.CONCURRENCY: - doInterceptForConcurrency(enableLimit.rate(), resource, enableLimit.fallback(), inv); - break; - case LimitType.TOKEN_BUCKET: - doInterceptForTokenBucket(enableLimit.rate(), resource, enableLimit.fallback(), inv); - break; - } - } - - - private void doInterceptForConcurrency(int rate, String resource, String fallback, Invocation inv) { + protected void doInterceptForConcurrency(int rate, String resource, String fallback, Invocation inv) { Semaphore semaphore = LimiterManager.me().getOrCreateSemaphore(resource, rate); boolean acquire = false; try { @@ -95,7 +48,7 @@ public class LimiterInterceptor implements FixedInterceptor, Interceptor { } - private void doInterceptForTokenBucket(int rate, String resource, String fallback, Invocation inv) { + protected void doInterceptForTokenBucket(int rate, String resource, String fallback, Invocation inv) { RateLimiter limiter = LimiterManager.me().getOrCreateRateLimiter(resource, rate); //允许通行 if (limiter.tryAcquire()) { @@ -107,16 +60,16 @@ public class LimiterInterceptor implements FixedInterceptor, Interceptor { } } - private void doExecFallback(String resource, String fallback, Invocation inv) { + protected void doExecFallback(String resource, String fallback, Invocation inv) { LimiterManager.me().processFallback(resource, fallback, inv); } - private String getPackageOrTarget(Invocation inv) { + protected String getPackageOrTarget(Invocation inv) { return inv.isActionInvocation() ? buildUrl(inv) : ClassUtil.buildMethodString(inv.getMethod()); } - private String buildUrl(Invocation inv) { + protected String buildUrl(Invocation inv) { HttpServletRequest request = inv.getController().getRequest(); String uri = request.getRequestURI(); String query = request.getQueryString(); diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java new file mode 100644 index 00000000..79c7c9f3 --- /dev/null +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.limiter.interceptor; + + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import io.jboot.components.limiter.LimitType; +import io.jboot.components.limiter.LimiterManager; + +public class LimiterGlobalInterceptor extends BaseLimiterInterceptor implements Interceptor { + + @Override + public void intercept(Invocation inv) { + String packageOrTarget = getPackageOrTarget(inv); + LimiterManager.TypeAndRate typeAndRate = LimiterManager.me().matchConfig(packageOrTarget); + + if (typeAndRate != null) { + doInterceptByTypeAndRate(typeAndRate, packageOrTarget, inv); + return; + } + + + inv.invoke(); + } + + + private void doInterceptByTypeAndRate(LimiterManager.TypeAndRate typeAndRate, String resource, Invocation inv) { + switch (typeAndRate.getType()) { + case LimitType.CONCURRENCY: + doInterceptForConcurrency(typeAndRate.getRate(), resource, null, inv); + break; + case LimitType.TOKEN_BUCKET: + doInterceptForTokenBucket(typeAndRate.getRate(), resource, null, inv); + break; + } + } + + + + + + + +} diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java new file mode 100644 index 00000000..2c3830ed --- /dev/null +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.limiter.interceptor; + + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import io.jboot.components.limiter.LimitType; +import io.jboot.components.limiter.annotation.EnableLimit; +import io.jboot.utils.AnnotationUtil; +import io.jboot.utils.StrUtil; + +public class LimiterInterceptor extends BaseLimiterInterceptor implements Interceptor { + + @Override + public void intercept(Invocation inv) { + + String packageOrTarget = getPackageOrTarget(inv); + + EnableLimit enableLimit = inv.getMethod().getAnnotation(EnableLimit.class); + if (enableLimit != null) { + String resource = StrUtil.obtainDefaultIfBlank(enableLimit.resource(), packageOrTarget); + doInterceptByLimitInfo(enableLimit, resource, inv); + return; + } + + inv.invoke(); + } + + + private void doInterceptByLimitInfo(EnableLimit enableLimit, String resource, Invocation inv) { + String type = AnnotationUtil.get(enableLimit.type()); + switch (type) { + case LimitType.CONCURRENCY: + doInterceptForConcurrency(enableLimit.rate(), resource, enableLimit.fallback(), inv); + break; + case LimitType.TOKEN_BUCKET: + doInterceptForTokenBucket(enableLimit.rate(), resource, enableLimit.fallback(), inv); + break; + } + } + + +} diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java new file mode 100644 index 00000000..a49cc591 --- /dev/null +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.limiter.interceptor; + +import com.jfinal.aop.Interceptor; +import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.annotation.AutoLoad; +import io.jboot.components.limiter.LimiterManager; +import io.jboot.components.limiter.annotation.EnableLimit; + +import java.lang.reflect.Method; +import java.util.LinkedList; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +@AutoLoad +public class LimiterInterceptorBuilder implements InterceptorBuilder { + + private LimiterManager manager = LimiterManager.me(); + + @Override + public void build(Class serviceClass, Method method, LinkedList interceptors) { + + if (manager.isEnable() && !manager.getConfigPackageOrTargets().isEmpty()) { + interceptors.add(new LimiterGlobalInterceptor()); + return; + } + + EnableLimit enableLimit = method.getAnnotation(EnableLimit.class); + if (enableLimit != null) { + interceptors.add(new LimiterInterceptor()); + } + + } + + +} diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index bb9c1fd5..b7ea3478 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -27,7 +27,6 @@ import com.jfinal.plugin.activerecord.ActiveRecordPlugin; import com.jfinal.template.Engine; import io.jboot.Jboot; import io.jboot.aop.JbootAopFactory; -import io.jboot.aop.JbootAopInterceptor; import io.jboot.aop.jfinal.JfinalHandlers; import io.jboot.aop.jfinal.JfinalPlugins; import io.jboot.app.config.support.apollo.ApolloServerConfig; @@ -254,8 +253,6 @@ public class JbootCoreConfig extends JFinalConfig { @Override public void configInterceptor(Interceptors interceptors) { - interceptors.addGlobalServiceInterceptor(new JbootAopInterceptor()); - JbootAppListenerManager.me().onInterceptorConfig(interceptors); JbootAppListenerManager.me().onFixedInterceptorConfig(FixedInterceptors.me()); } diff --git a/src/main/java/io/jboot/support/jwt/EnableJwt.java b/src/main/java/io/jboot/support/jwt/EnableJwt.java new file mode 100644 index 00000000..1e594f16 --- /dev/null +++ b/src/main/java/io/jboot/support/jwt/EnableJwt.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.support.jwt; + +import java.lang.annotation.*; + +/** + * @author Michael Yang 杨福海 (fuhai999@gmail.com) + * + * 每个参数意义的详情 : https://developer.mozilla.org/en-US/docs/Glossary/CORS + */ +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface EnableJwt { + +} diff --git a/src/main/java/io/jboot/support/jwt/JwtInterceptor.java b/src/main/java/io/jboot/support/jwt/JwtInterceptor.java index 1aef6bef..488d64d8 100644 --- a/src/main/java/io/jboot/support/jwt/JwtInterceptor.java +++ b/src/main/java/io/jboot/support/jwt/JwtInterceptor.java @@ -16,6 +16,7 @@ package io.jboot.support.jwt; import com.jfinal.aop.Invocation; +import io.jboot.exception.JbootException; import io.jboot.utils.StrUtil; import io.jboot.web.controller.JbootController; import io.jboot.web.fixedinterceptor.FixedInterceptor; @@ -35,9 +36,9 @@ public class JwtInterceptor implements FixedInterceptor { @Override public void intercept(Invocation inv) { + if (!JwtManager.me().getConfig().isConfigOk()) { - inv.invoke(); - return; + throw new JbootException("jwt secret can not config well, please config jboot.web.jwt.secret in jboot.properties."); } HttpServletRequest request = inv.getController().getRequest(); diff --git a/src/main/java/io/jboot/support/jwt/JwtInterceptorBuilder.java b/src/main/java/io/jboot/support/jwt/JwtInterceptorBuilder.java new file mode 100644 index 00000000..1716141c --- /dev/null +++ b/src/main/java/io/jboot/support/jwt/JwtInterceptorBuilder.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.support.jwt; + +import com.jfinal.aop.Interceptor; +import com.jfinal.core.Controller; +import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.annotation.AutoLoad; + +import java.lang.reflect.Method; +import java.util.LinkedList; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +@AutoLoad +public class JwtInterceptorBuilder implements InterceptorBuilder { + + private static JwtManager manager = JwtManager.me(); + + @Override + public void build(Class serviceClass, Method method, LinkedList interceptors) { + EnableJwt enableAnnotation = getAnnotation(serviceClass,method); + if (enableAnnotation != null && Controller.class.isAssignableFrom(serviceClass)) { + interceptors.add(new JwtInterceptor()); + } + + } + + private EnableJwt getAnnotation(Class serviceClass, Method method) { + EnableJwt enableAnnotation = serviceClass.getClass().getAnnotation(EnableJwt.class); + return enableAnnotation != null ? enableAnnotation : method.getAnnotation(EnableJwt.class); + } + +} diff --git a/src/main/java/io/jboot/support/metric/JbootMetricInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricConcurrencyInterceptor.java similarity index 38% rename from src/main/java/io/jboot/support/metric/JbootMetricInterceptor.java rename to src/main/java/io/jboot/support/metric/interceptor/MetricConcurrencyInterceptor.java index 6f64acef..5ea22124 100644 --- a/src/main/java/io/jboot/support/metric/JbootMetricInterceptor.java +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricConcurrencyInterceptor.java @@ -13,52 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.jboot.support.metric; +package io.jboot.support.metric.interceptor; import com.codahale.metrics.Counter; -import com.codahale.metrics.Histogram; -import com.codahale.metrics.Meter; -import com.codahale.metrics.Timer; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import io.jboot.Jboot; -import io.jboot.support.metric.annotation.*; +import io.jboot.support.metric.annotation.EnableMetricConcurrency; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.StrUtil; import io.jboot.web.fixedinterceptor.FixedInterceptor; -/** - * 用于对controller的Metrics 统计 - * 注意:如果 Controller通过 @Clear 来把此 拦截器给清空,那么此方法(action)注入将会失效 - */ -public class JbootMetricInterceptor implements Interceptor, FixedInterceptor { - - private static JbootMetricConfig config = Jboot.config(JbootMetricConfig.class); +public class MetricConcurrencyInterceptor implements Interceptor, FixedInterceptor { @Override public void intercept(Invocation inv) { - if (!config.isConfigOk()) { - inv.invoke(); - return; - } - - - Timer.Context timerContext = null; - EnableMetricCounter counterAnnotation = inv.getMethod().getAnnotation(EnableMetricCounter.class); - if (counterAnnotation != null) { - String value = AnnotationUtil.get(counterAnnotation.value()); - String name = StrUtil.isBlank(value) - ? inv.getController().getClass().getName() + "." + inv.getMethodName() + ".counter" - : value; - - - Counter counter = Jboot.getMetric().counter(name); - counter.inc(); - } - - Counter concurrencyRecord = null; EnableMetricConcurrency concurrencyAnnotation = inv.getMethod().getAnnotation(EnableMetricConcurrency.class); if (concurrencyAnnotation != null) { @@ -73,44 +44,6 @@ public class JbootMetricInterceptor implements Interceptor, FixedInterceptor { } - EnableMetricMeter meterAnnotation = inv.getMethod().getAnnotation(EnableMetricMeter.class); - if (meterAnnotation != null) { - String value = AnnotationUtil.get(meterAnnotation.value()); - String name = StrUtil.isBlank(value) - ? inv.getController().getClass().getName() + "." + inv.getMethodName() + ".meter" - : value; - - - Meter meter = Jboot.getMetric().meter(name); - meter.mark(); - } - - - EnableMetricHistogram histogramAnnotation = inv.getMethod().getAnnotation(EnableMetricHistogram.class); - if (histogramAnnotation != null) { - String value = AnnotationUtil.get(histogramAnnotation.value()); - String name = StrUtil.isBlank(value) - ? inv.getController().getClass().getName() + "." + inv.getMethodName() + ".histogram" - : value; - - - Histogram histogram = Jboot.getMetric().histogram(name); - histogram.update(histogramAnnotation.update()); - } - - - EnableMetricTimer timerAnnotation = inv.getMethod().getAnnotation(EnableMetricTimer.class); - if (timerAnnotation != null) { - String value = AnnotationUtil.get(timerAnnotation.value()); - String name = StrUtil.isBlank(value) - ? inv.getController().getClass().getName() + "." + inv.getMethodName() + ".timer" - : value; - - - Timer timer = Jboot.getMetric().timer(name); - timerContext = timer.time(); - } - try { inv.invoke(); @@ -118,9 +51,6 @@ public class JbootMetricInterceptor implements Interceptor, FixedInterceptor { if (concurrencyRecord != null) { concurrencyRecord.dec(); } - if (timerContext != null) { - timerContext.stop(); - } } } diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricCounterInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricCounterInterceptor.java new file mode 100644 index 00000000..2908b841 --- /dev/null +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricCounterInterceptor.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.support.metric.interceptor; + +import com.codahale.metrics.Counter; +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import io.jboot.Jboot; +import io.jboot.support.metric.JbootMetricConfig; +import io.jboot.support.metric.annotation.EnableMetricCounter; +import io.jboot.utils.AnnotationUtil; +import io.jboot.utils.StrUtil; +import io.jboot.web.fixedinterceptor.FixedInterceptor; + + +public class MetricCounterInterceptor implements Interceptor, FixedInterceptor { + + private static JbootMetricConfig config = Jboot.config(JbootMetricConfig.class); + + + @Override + public void intercept(Invocation inv) { + + EnableMetricCounter counterAnnotation = inv.getMethod().getAnnotation(EnableMetricCounter.class); + if (counterAnnotation != null) { + String value = AnnotationUtil.get(counterAnnotation.value()); + String name = StrUtil.isBlank(value) + ? inv.getController().getClass().getName() + "." + inv.getMethodName() + ".counter" + : value; + + + Counter counter = Jboot.getMetric().counter(name); + counter.inc(); + } + + inv.invoke(); + } + + +} diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricHistogramInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricHistogramInterceptor.java new file mode 100644 index 00000000..a8576b01 --- /dev/null +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricHistogramInterceptor.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.support.metric.interceptor; + +import com.codahale.metrics.Histogram; +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import io.jboot.Jboot; +import io.jboot.support.metric.annotation.EnableMetricHistogram; +import io.jboot.utils.AnnotationUtil; +import io.jboot.utils.StrUtil; +import io.jboot.web.fixedinterceptor.FixedInterceptor; + +public class MetricHistogramInterceptor implements Interceptor, FixedInterceptor { + + + @Override + public void intercept(Invocation inv) { + + EnableMetricHistogram histogramAnnotation = inv.getMethod().getAnnotation(EnableMetricHistogram.class); + if (histogramAnnotation != null) { + String value = AnnotationUtil.get(histogramAnnotation.value()); + String name = StrUtil.isBlank(value) + ? inv.getController().getClass().getName() + "." + inv.getMethodName() + ".histogram" + : value; + + + Histogram histogram = Jboot.getMetric().histogram(name); + histogram.update(histogramAnnotation.update()); + } + + inv.invoke(); + } + + +} diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricInterceptorBuilder.java b/src/main/java/io/jboot/support/metric/interceptor/MetricInterceptorBuilder.java new file mode 100644 index 00000000..180ac321 --- /dev/null +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricInterceptorBuilder.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.support.metric.interceptor; + +import com.jfinal.aop.Interceptor; +import io.jboot.Jboot; +import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.annotation.AutoLoad; +import io.jboot.support.metric.JbootMetricConfig; +import io.jboot.support.metric.annotation.*; + +import java.lang.reflect.Method; +import java.util.LinkedList; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +@AutoLoad +public class MetricInterceptorBuilder implements InterceptorBuilder { + + private static JbootMetricConfig config = Jboot.config(JbootMetricConfig.class); + + @Override + public void build(Class serviceClass, Method method, LinkedList interceptors) { + if (!config.isConfigOk()) { + return; + } + + EnableMetricConcurrency concurrencyAnnotation = method.getAnnotation(EnableMetricConcurrency.class); + if (concurrencyAnnotation != null) { + interceptors.add(new MetricConcurrencyInterceptor()); + } + + EnableMetricHistogram histogramAnnotation = method.getAnnotation(EnableMetricHistogram.class); + if (histogramAnnotation != null) { + interceptors.add(new MetricHistogramInterceptor()); + } + + EnableMetricCounter counterAnnotation = method.getAnnotation(EnableMetricCounter.class); + if (counterAnnotation != null) { + interceptors.add(new MetricCounterInterceptor()); + } + + EnableMetricMeter meterAnnotation = method.getAnnotation(EnableMetricMeter.class); + if (meterAnnotation != null) { + interceptors.add(new MetricMeterInterceptor()); + } + + EnableMetricTimer timerAnnotation = method.getAnnotation(EnableMetricTimer.class); + if (timerAnnotation != null) { + interceptors.add(new MetricTimerInterceptor()); + } + + } + + +} diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricMeterInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricMeterInterceptor.java new file mode 100644 index 00000000..b3760bd4 --- /dev/null +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricMeterInterceptor.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.support.metric.interceptor; + +import com.codahale.metrics.Meter; +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import io.jboot.Jboot; +import io.jboot.support.metric.annotation.EnableMetricMeter; +import io.jboot.utils.AnnotationUtil; +import io.jboot.utils.StrUtil; +import io.jboot.web.fixedinterceptor.FixedInterceptor; + +public class MetricMeterInterceptor implements Interceptor, FixedInterceptor { + + @Override + public void intercept(Invocation inv) { + + EnableMetricMeter meterAnnotation = inv.getMethod().getAnnotation(EnableMetricMeter.class); + if (meterAnnotation != null) { + String value = AnnotationUtil.get(meterAnnotation.value()); + String name = StrUtil.isBlank(value) + ? inv.getController().getClass().getName() + "." + inv.getMethodName() + ".meter" + : value; + + + Meter meter = Jboot.getMetric().meter(name); + meter.mark(); + } + + inv.invoke(); + } + + +} diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricTimerInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricTimerInterceptor.java new file mode 100644 index 00000000..3efdbb2e --- /dev/null +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricTimerInterceptor.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.support.metric.interceptor; + +import com.codahale.metrics.Timer; +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import io.jboot.Jboot; +import io.jboot.support.metric.annotation.EnableMetricTimer; +import io.jboot.utils.AnnotationUtil; +import io.jboot.utils.StrUtil; +import io.jboot.web.fixedinterceptor.FixedInterceptor; + +public class MetricTimerInterceptor implements Interceptor, FixedInterceptor { + + + @Override + public void intercept(Invocation inv) { + + Timer.Context timerContext = null; + + EnableMetricTimer timerAnnotation = inv.getMethod().getAnnotation(EnableMetricTimer.class); + if (timerAnnotation != null) { + String value = AnnotationUtil.get(timerAnnotation.value()); + String name = StrUtil.isBlank(value) + ? inv.getController().getClass().getName() + "." + inv.getMethodName() + ".timer" + : value; + + + Timer timer = Jboot.getMetric().timer(name); + timerContext = timer.time(); + } + + + try { + inv.invoke(); + } finally { + if (timerContext != null) { + timerContext.stop(); + } + } + } + + +} diff --git a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalInterceptorBuilder.java b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalInterceptorBuilder.java new file mode 100644 index 00000000..a1acfcf0 --- /dev/null +++ b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalInterceptorBuilder.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.support.seata.interceptor; + +import com.jfinal.aop.Interceptor; +import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.annotation.AutoLoad; +import io.jboot.support.seata.annotation.SeataGlobalLock; +import io.jboot.support.seata.annotation.SeataGlobalTransactional; + +import java.lang.reflect.Method; +import java.util.LinkedList; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +@AutoLoad +public class SeataGlobalInterceptorBuilder implements InterceptorBuilder { + + + @Override + public void build(Class serviceClass, Method method, LinkedList interceptors) { + + final SeataGlobalTransactional globalTrxAnno = method.getAnnotation(SeataGlobalTransactional.class); + final SeataGlobalLock globalLockAnno = method.getAnnotation(SeataGlobalLock.class); + + if (globalTrxAnno != null || globalLockAnno != null){ + interceptors.add(new SeataGlobalTransactionalInterceptor()); + } + } + + +} diff --git a/src/main/java/io/jboot/support/seata/tcc/TccInterceptorBuilder.java b/src/main/java/io/jboot/support/seata/tcc/TccInterceptorBuilder.java new file mode 100644 index 00000000..adc871bb --- /dev/null +++ b/src/main/java/io/jboot/support/seata/tcc/TccInterceptorBuilder.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.support.seata.tcc; + +import com.jfinal.aop.Interceptor; +import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.annotation.AutoLoad; +import io.seata.rm.tcc.api.TwoPhaseBusinessAction; + +import java.lang.reflect.Method; +import java.util.LinkedList; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +@AutoLoad +public class TccInterceptorBuilder implements InterceptorBuilder { + + + @Override + public void build(Class serviceClass, Method method, LinkedList interceptors) { + try { + Class.forName("io.seata.rm.tcc.api.TwoPhaseBusinessAction"); + + TwoPhaseBusinessAction businessAction = method.getAnnotation(TwoPhaseBusinessAction.class); + if (businessAction != null ){ + interceptors.add(new TccActionInterceptor()); + } + }catch (Exception ex){} + + } + + +} diff --git a/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java b/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java index 8f04a65b..ffc62f6d 100644 --- a/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java +++ b/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java @@ -17,13 +17,12 @@ package io.jboot.support.sentinel; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; -import io.jboot.web.fixedinterceptor.FixedInterceptor; /** * @author michael yang (fuhai999@gmail.com) * @Date: 2020/1/7 */ -public class SentinelInterceptor implements Interceptor, FixedInterceptor { +public class SentinelInterceptor implements Interceptor{ @Override diff --git a/src/main/java/io/jboot/support/sentinel/SentinelInterceptorBuilder.java b/src/main/java/io/jboot/support/sentinel/SentinelInterceptorBuilder.java new file mode 100644 index 00000000..98bb1b0e --- /dev/null +++ b/src/main/java/io/jboot/support/sentinel/SentinelInterceptorBuilder.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.support.sentinel; + +import com.alibaba.csp.sentinel.annotation.SentinelResource; +import com.jfinal.aop.Interceptor; +import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.annotation.AutoLoad; + +import java.lang.reflect.Method; +import java.util.LinkedList; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +@AutoLoad +public class SentinelInterceptorBuilder implements InterceptorBuilder { + + + @Override + public void build(Class serviceClass, Method method, LinkedList interceptors) { + try { + Class.forName("com.alibaba.csp.sentinel.Sph"); + + SentinelResource annotation = method.getAnnotation(SentinelResource.class); + if (annotation != null ){ + interceptors.add(new SentinelInterceptor()); + } + }catch (Exception ex){} + + } + + +} diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java b/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java index 2503c6de..3463e676 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java @@ -16,7 +16,6 @@ package io.jboot.support.shiro; import com.jfinal.aop.Invocation; -import io.jboot.Jboot; import io.jboot.support.shiro.processer.AuthorizeResult; import io.jboot.web.fixedinterceptor.FixedInterceptor; /** @@ -24,14 +23,10 @@ import io.jboot.web.fixedinterceptor.FixedInterceptor; */ public class JbootShiroInterceptor implements FixedInterceptor { - private static JbootShiroConfig config = Jboot.config(JbootShiroConfig.class); + @Override public void intercept(Invocation inv) { - if (!config.isConfigOK()) { - inv.invoke(); - return; - } JbootShiroManager.me().getInvokeListener().onInvokeBefore(inv); AuthorizeResult result = JbootShiroManager.me().invoke(inv.getActionKey()); diff --git a/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java b/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java new file mode 100644 index 00000000..3b1d4a18 --- /dev/null +++ b/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.support.shiro; + +import com.jfinal.aop.Interceptor; +import com.jfinal.core.Controller; +import io.jboot.Jboot; +import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.annotation.AutoLoad; + +import java.lang.reflect.Method; +import java.util.LinkedList; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +@AutoLoad +public class ShiroInterceptorBuilder implements InterceptorBuilder { + + private static JbootShiroConfig config = Jboot.config(JbootShiroConfig.class); + + @Override + public void build(Class serviceClass, Method method, LinkedList interceptors) { + + if (config.isConfigOK() && Controller.class.isAssignableFrom(serviceClass)) { + interceptors.add(new JbootShiroInterceptor()); + } + } + + +} diff --git a/src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java b/src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java new file mode 100644 index 00000000..b08d1de6 --- /dev/null +++ b/src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.cors; + +import com.jfinal.aop.Interceptor; +import com.jfinal.ext.cors.EnableCORS; +import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.annotation.AutoLoad; + +import java.lang.reflect.Method; +import java.util.LinkedList; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +@AutoLoad +public class CORSInterceptorBuilder implements InterceptorBuilder { + + + @Override + public void build(Class serviceClass, Method method, LinkedList interceptors) { + EnableCORS enableCORS = getAnnotation(serviceClass,method); + if (enableCORS != null) { + interceptors.add(new CORSInterceptor()); + } + + } + + private EnableCORS getAnnotation(Class serviceClass, Method method) { + EnableCORS enableCORS = serviceClass.getClass().getAnnotation(EnableCORS.class); + return enableCORS != null ? enableCORS : method.getAnnotation(EnableCORS.class); + } + + +} diff --git a/src/main/java/io/jboot/web/cors/EnableCORS.java b/src/main/java/io/jboot/web/cors/EnableCORS.java deleted file mode 100644 index 6b820636..00000000 --- a/src/main/java/io/jboot/web/cors/EnableCORS.java +++ /dev/null @@ -1,49 +0,0 @@ -///** -// * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). -// *

    -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// *

    -// * http://www.apache.org/licenses/LICENSE-2.0 -// *

    -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ -//package io.jboot.web.cors; -// -//import java.lang.annotation.*; -// -///** -// * @author Michael Yang 杨福海 (fuhai999@gmail.com) -// * @version V1.0 -// * @Package io.jboot.web.cors -// *

    -// * detail : https://developer.mozilla.org/en-US/docs/Glossary/CORS -// */ -//@Inherited -//@Retention(RetentionPolicy.RUNTIME) -//@Target({ElementType.METHOD, ElementType.TYPE}) -//public @interface EnableCORS { -// -// String allowOrigin() default "*"; -// -// String allowCredentials() default "true"; -// -// String allowHeaders() default "Origin,X-Requested-With,Content-Type,Accept,Authorization,Jwt"; -// -// String allowMethods() default "GET,PUT,POST,DELETE,PATCH,OPTIONS"; -// -// String exposeHeaders() default ""; -// -// String requestHeaders() default ""; -// -// String requestMethod() default ""; -// -// String origin() default ""; -// -// String maxAge() default "3600"; -//} diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java index bbc13cb4..5b7d419b 100644 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java +++ b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java @@ -17,15 +17,6 @@ package io.jboot.web.fixedinterceptor; import com.jfinal.aop.Aop; import com.jfinal.aop.Interceptor; -import io.jboot.components.limiter.LimiterInterceptor; -import io.jboot.support.jwt.JwtInterceptor; -import io.jboot.support.metric.JbootMetricInterceptor; -import io.jboot.support.seata.interceptor.SeataGlobalTransactionalInterceptor; -import io.jboot.support.seata.tcc.TccActionInterceptor; -import io.jboot.support.sentinel.SentinelInterceptor; -import io.jboot.support.shiro.JbootShiroInterceptor; -import io.jboot.web.validate.ValidateInterceptor; -import io.jboot.web.cors.CORSInterceptor; import java.util.ArrayList; import java.util.Arrays; @@ -49,15 +40,15 @@ public class FixedInterceptors { * 默认的 Jboot 系统拦截器 */ private FixedInterceptorWapper[] defaultInters = new FixedInterceptorWapper[]{ - new FixedInterceptorWapper(new SentinelInterceptor(), 9), - new FixedInterceptorWapper(new LimiterInterceptor(), 10), - new FixedInterceptorWapper(new CORSInterceptor(), 20), - new FixedInterceptorWapper(new ValidateInterceptor(), 30), - new FixedInterceptorWapper(new JwtInterceptor(), 40), - new FixedInterceptorWapper(new JbootShiroInterceptor(), 50), - new FixedInterceptorWapper(new JbootMetricInterceptor(), 60), - new FixedInterceptorWapper(new SeataGlobalTransactionalInterceptor(), 80), - new FixedInterceptorWapper(new TccActionInterceptor(), 90) +// new FixedInterceptorWapper(new SentinelInterceptor(), 9), +// new FixedInterceptorWapper(new LimiterInterceptor(), 10), +// new FixedInterceptorWapper(new CORSInterceptor(), 20), +// new FixedInterceptorWapper(new ValidateInterceptor(), 30), +// new FixedInterceptorWapper(new JwtInterceptor(), 40), +// new FixedInterceptorWapper(new JbootShiroInterceptor(), 50), +// new FixedInterceptorWapper(new JbootMetricInterceptor(), 60), +// new FixedInterceptorWapper(new SeataGlobalTransactionalInterceptor(), 80), +// new FixedInterceptorWapper(new TccActionInterceptor(), 90) }; private List userInters = new ArrayList<>(); diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 71acc14b..90722ee1 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -113,7 +113,7 @@ public class JbootActionHandler extends ActionHandler { try { invocation.invoke(); } finally { - JbootActionReporter.report(target, controller, action, time); + JbootActionReporter.report(target, controller, action,invocation,time); } } else { invocation.invoke(); diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index 0c14a046..cedf085c 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -16,6 +16,7 @@ package io.jboot.web.handler; import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; import com.jfinal.core.Action; import com.jfinal.core.ActionReporter; import com.jfinal.core.Controller; @@ -65,9 +66,9 @@ public class JbootActionReporter { /** * Report the action */ - public static final void report(String target, Controller controller, Action action, long time) { + public static final void report(String target, Controller controller, Action action, Invocation invocation, long time) { try { - doReport(target, controller, action, time); + doReport(target, controller, action,invocation, time); } catch (Exception ex) { // 出错的情况,一般情况下是:用户自定义了自己的 classloader, 此 classloader 加载的 class 没有被添加到 ClassPool // 如何添加可以参考 jpress 的 插件加载 @@ -77,7 +78,7 @@ public class JbootActionReporter { } - private static final void doReport(String target, Controller controller, Action action, long time) throws Exception { + private static final void doReport(String target, Controller controller, Action action, Invocation invocation, long time) throws Exception { CtClass ctClass = ClassPool.getDefault().get(action.getControllerClass().getName()); String desc = JbootActionReporterUtil.getMethodDescWithoutName(action.getMethod()); CtMethod ctMethod = ctClass.getMethod(action.getMethodName(), desc); @@ -95,7 +96,7 @@ public class JbootActionReporter { sb.append("UrlPara : ").append(urlParas).append("\n"); } - Interceptor[] inters = action.getInterceptors(); + Interceptor[] inters = invocation instanceof JbootInvocation ? ((JbootInvocation) invocation).getInters() : action.getInterceptors(); List invokedInterceptors = JbootInvocation.getInvokedInterceptor(); if (inters.length > 0) { sb.append("Interceptor : "); diff --git a/src/main/java/io/jboot/web/handler/JbootInvocation.java b/src/main/java/io/jboot/web/handler/JbootInvocation.java index 251cb8b1..9ee10325 100644 --- a/src/main/java/io/jboot/web/handler/JbootInvocation.java +++ b/src/main/java/io/jboot/web/handler/JbootInvocation.java @@ -126,5 +126,7 @@ public class JbootInvocation extends Invocation { invokedInterceptors.remove(); } - + public Interceptor[] getInters() { + return inters; + } } diff --git a/src/main/java/io/jboot/web/validate/interceptor/CaptchaValidateInterceptor.java b/src/main/java/io/jboot/web/validate/interceptor/CaptchaValidateInterceptor.java new file mode 100644 index 00000000..3eddd4d3 --- /dev/null +++ b/src/main/java/io/jboot/web/validate/interceptor/CaptchaValidateInterceptor.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.validate.interceptor; + +import com.jfinal.aop.Invocation; +import com.jfinal.core.Controller; +import com.jfinal.log.Log; +import io.jboot.Jboot; +import io.jboot.utils.AnnotationUtil; +import io.jboot.utils.StrUtil; +import io.jboot.web.fixedinterceptor.FixedInterceptor; +import io.jboot.web.validate.CaptchaValidate; + +/** + * 验证拦截器 + */ +public class CaptchaValidateInterceptor implements FixedInterceptor { + + private static final Log LOG = Log.getLog("Validate"); + + @Override + public void intercept(Invocation inv) { + + CaptchaValidate captchaValidate = inv.getMethod().getAnnotation(CaptchaValidate.class); + if (captchaValidate != null && !validateCaptache(inv, captchaValidate)) { + if (Jboot.isDevMode()){ + LOG.error(Util.buildErrorMessage(inv,"@CaptchaValidate")); + } + return; + } + + inv.invoke(); + } + + + /** + * 对验证码进行验证 + * + * @param inv + * @param captchaValidate + * @return + */ + private boolean validateCaptache(Invocation inv, CaptchaValidate captchaValidate) { + String formName = AnnotationUtil.get(captchaValidate.form()); + if (StrUtil.isBlank(formName)) { + throw new IllegalArgumentException("@CaptchaValidate.form must not be empty in " + inv.getController().getClass().getName() + "." + inv.getMethodName()); + } + + + Controller controller = inv.getController(); + if (controller.validateCaptcha(formName)) { + return true; + } + + Util.renderError(inv.getController() + , AnnotationUtil.get(captchaValidate.renderType()) + , formName + , AnnotationUtil.get(captchaValidate.message()) + , AnnotationUtil.get(captchaValidate.redirectUrl()) + , AnnotationUtil.get(captchaValidate.htmlPath()) + , captchaValidate.errorCode() + ); + + return false; + } + + + + +} diff --git a/src/main/java/io/jboot/web/validate/ValidateInterceptor.java b/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java similarity index 35% rename from src/main/java/io/jboot/web/validate/ValidateInterceptor.java rename to src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java index e48c7b43..5a49a471 100644 --- a/src/main/java/io/jboot/web/validate/ValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java @@ -13,56 +13,36 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.jboot.web.validate; +package io.jboot.web.validate.interceptor; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONPath; import com.jfinal.aop.Invocation; -import com.jfinal.core.Controller; -import com.jfinal.kit.Ret; import com.jfinal.log.Log; import io.jboot.Jboot; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.ArrayUtil; -import io.jboot.utils.RequestUtil; import io.jboot.utils.StrUtil; import io.jboot.web.fixedinterceptor.FixedInterceptor; - -import java.lang.reflect.Method; +import io.jboot.web.validate.EmptyValidate; +import io.jboot.web.validate.Form; +import io.jboot.web.validate.FormType; /** * 验证拦截器 */ -public class ValidateInterceptor implements FixedInterceptor { +public class EmptyValidateInterceptor implements FixedInterceptor { private static final Log LOG = Log.getLog("Validate"); @Override public void intercept(Invocation inv) { - Method method = inv.getMethod(); - - EmptyValidate emptyParaValidate = method.getAnnotation(EmptyValidate.class); + EmptyValidate emptyParaValidate = inv.getMethod().getAnnotation(EmptyValidate.class); if (emptyParaValidate != null && !validateEmpty(inv, emptyParaValidate)) { if (Jboot.isDevMode()){ - LOG.error(buildErrorMessage(inv,"@EmptyValidate")); - } - return; - } - - RegexValidate regexValidate = method.getAnnotation(RegexValidate.class); - if (regexValidate != null && !validateRegex(inv, regexValidate)) { - if (Jboot.isDevMode()){ - LOG.error(buildErrorMessage(inv,"@RegexValidate")); - } - return; - } - - CaptchaValidate captchaValidate = method.getAnnotation(CaptchaValidate.class); - if (captchaValidate != null && !validateCaptache(inv, captchaValidate)) { - if (Jboot.isDevMode()){ - LOG.error(buildErrorMessage(inv,"@CaptchaValidate")); + LOG.error(Util.buildErrorMessage(inv,"@EmptyValidate")); } return; } @@ -71,18 +51,6 @@ public class ValidateInterceptor implements FixedInterceptor { } - private static String buildErrorMessage(Invocation inv,String annotation){ - StringBuilder sb = new StringBuilder(); - sb.append("method \"").append(inv.getController().getClass().getName()) - .append(".") - .append(inv.getMethodName()) - .append("()\"") - .append(" has intercepted by annotation ") - .append(annotation); - return sb.toString(); - } - - /** * 非空判断验证 * @@ -124,7 +92,7 @@ public class ValidateInterceptor implements FixedInterceptor { } if (value == null || value.trim().length() == 0) { - renderError(inv.getController() + Util.renderError(inv.getController() , AnnotationUtil.get(emptyParaValidate.renderType()) , formName , AnnotationUtil.get(form.message()) @@ -140,131 +108,7 @@ public class ValidateInterceptor implements FixedInterceptor { } - /** - * 正则验证 - * - * @param inv - * @param regexValidate - * @return - */ - private boolean validateRegex(Invocation inv, RegexValidate regexValidate) { - RegexForm[] forms = regexValidate.value(); - if (ArrayUtil.isNullOrEmpty(forms)) { - return true; - } - - - for (RegexForm form : forms) { - String formName = AnnotationUtil.get(form.name()); - String formType = AnnotationUtil.get(form.type()); - if (StrUtil.isBlank(formName)) { - throw new IllegalArgumentException("@MatchesForm.value must not be empty in " + inv.getController().getClass().getName() + "." + inv.getMethodName()); - } - String value = null; - if (FormType.FORM_DATA.equalsIgnoreCase(formType)) { - value = inv.getController().getPara(formName); - } else if (FormType.RAW_DATA.equalsIgnoreCase(formType)) { - try { - JSONObject json = JSON.parseObject(inv.getController().getRawData()); - if (json != null) { - Object tmp = JSONPath.eval(json, "$." + formName); - if (tmp != null) { - value = tmp.toString(); - } - } - } catch (Exception e) { - value = null; - } - } else { - throw new IllegalArgumentException("@MatchesValidate not support form type : " + formType + ", " + - "see : io.jboot.web.controller.validate.FormType"); - } - - if (value == null || !value.trim().matches(form.regex())) { - renderError(inv.getController() - , AnnotationUtil.get(regexValidate.renderType()) - , formName - , AnnotationUtil.get(form.message()) - , AnnotationUtil.get(regexValidate.redirectUrl()) - , AnnotationUtil.get(regexValidate.htmlPath()) - , form.errorCode() - ); - return false; - } - } - - return true; - } - - - /** - * 对验证码进行验证 - * - * @param inv - * @param captchaValidate - * @return - */ - private boolean validateCaptache(Invocation inv, CaptchaValidate captchaValidate) { - String formName = AnnotationUtil.get(captchaValidate.form()); - if (StrUtil.isBlank(formName)) { - throw new IllegalArgumentException("@CaptchaValidate.form must not be empty in " + inv.getController().getClass().getName() + "." + inv.getMethodName()); - } - - Controller controller = inv.getController(); - if (controller.validateCaptcha(formName)) { - return true; - } - - renderError(inv.getController() - , AnnotationUtil.get(captchaValidate.renderType()) - , formName - , AnnotationUtil.get(captchaValidate.message()) - , AnnotationUtil.get(captchaValidate.redirectUrl()) - , AnnotationUtil.get(captchaValidate.htmlPath()) - , captchaValidate.errorCode() - ); - - return false; - } - - - private void renderError(Controller controller, String renderType, String formName, String message, String redirectUrl, String htmlPath, int errorCode) { - String reason = StrUtil.isNotBlank(message) ? (formName + " validate failed: " + message) : (formName + " validate failed!"); - switch (renderType) { - case ValidateRenderType.DEFAULT: - if (RequestUtil.isAjaxRequest(controller.getRequest())) { - controller.renderJson( - Ret.fail("message", message) - .set("reason", reason) - .set("errorCode", errorCode) - .setIfNotNull("formName", formName) - ); - } else { - controller.renderText(reason); - } - break; - case ValidateRenderType.JSON: - controller.renderJson( - Ret.fail("message", message) - .set("reason", reason) - .set("errorCode", errorCode) - .setIfNotNull("formName", formName) - ); - break; - case ValidateRenderType.REDIRECT: - controller.redirect(redirectUrl); - break; - case ValidateRenderType.HTML: - controller.render(htmlPath); - break; - case ValidateRenderType.TEXT: - controller.renderText(message); - break; - default: - throw new IllegalArgumentException("can not process render : " + renderType); - } - } } diff --git a/src/main/java/io/jboot/web/validate/interceptor/RegexValidateInterceptor.java b/src/main/java/io/jboot/web/validate/interceptor/RegexValidateInterceptor.java new file mode 100644 index 00000000..2fb62299 --- /dev/null +++ b/src/main/java/io/jboot/web/validate/interceptor/RegexValidateInterceptor.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.validate.interceptor; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.JSONPath; +import com.jfinal.aop.Invocation; +import com.jfinal.log.Log; +import io.jboot.Jboot; +import io.jboot.utils.AnnotationUtil; +import io.jboot.utils.ArrayUtil; +import io.jboot.utils.StrUtil; +import io.jboot.web.fixedinterceptor.FixedInterceptor; +import io.jboot.web.validate.FormType; +import io.jboot.web.validate.RegexForm; +import io.jboot.web.validate.RegexValidate; + +/** + * 验证拦截器 + */ +public class RegexValidateInterceptor implements FixedInterceptor { + + private static final Log LOG = Log.getLog("Validate"); + + @Override + public void intercept(Invocation inv) { + + RegexValidate regexValidate = inv.getMethod().getAnnotation(RegexValidate.class); + if (regexValidate != null && !validateRegex(inv, regexValidate)) { + if (Jboot.isDevMode()){ + LOG.error(Util.buildErrorMessage(inv,"@RegexValidate")); + } + return; + } + + inv.invoke(); + } + + + + + /** + * 正则验证 + * + * @param inv + * @param regexValidate + * @return + */ + private boolean validateRegex(Invocation inv, RegexValidate regexValidate) { + RegexForm[] forms = regexValidate.value(); + if (ArrayUtil.isNullOrEmpty(forms)) { + return true; + } + + + for (RegexForm form : forms) { + String formName = AnnotationUtil.get(form.name()); + String formType = AnnotationUtil.get(form.type()); + if (StrUtil.isBlank(formName)) { + throw new IllegalArgumentException("@MatchesForm.value must not be empty in " + inv.getController().getClass().getName() + "." + inv.getMethodName()); + } + String value = null; + if (FormType.FORM_DATA.equalsIgnoreCase(formType)) { + value = inv.getController().getPara(formName); + } else if (FormType.RAW_DATA.equalsIgnoreCase(formType)) { + try { + JSONObject json = JSON.parseObject(inv.getController().getRawData()); + if (json != null) { + Object tmp = JSONPath.eval(json, "$." + formName); + if (tmp != null) { + value = tmp.toString(); + } + } + } catch (Exception e) { + value = null; + } + } else { + throw new IllegalArgumentException("@MatchesValidate not support form type : " + formType + ", " + + "see : io.jboot.web.controller.validate.FormType"); + } + + if (value == null || !value.trim().matches(form.regex())) { + Util.renderError(inv.getController() + , AnnotationUtil.get(regexValidate.renderType()) + , formName + , AnnotationUtil.get(form.message()) + , AnnotationUtil.get(regexValidate.redirectUrl()) + , AnnotationUtil.get(regexValidate.htmlPath()) + , form.errorCode() + ); + return false; + } + } + + return true; + } + + + + + +} diff --git a/src/main/java/io/jboot/web/validate/interceptor/Util.java b/src/main/java/io/jboot/web/validate/interceptor/Util.java new file mode 100644 index 00000000..6d717bc1 --- /dev/null +++ b/src/main/java/io/jboot/web/validate/interceptor/Util.java @@ -0,0 +1,67 @@ +package io.jboot.web.validate.interceptor; + +import com.jfinal.aop.Invocation; +import com.jfinal.core.Controller; +import com.jfinal.kit.Ret; +import io.jboot.utils.RequestUtil; +import io.jboot.utils.StrUtil; +import io.jboot.web.validate.ValidateRenderType; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +class Util { + + + static String buildErrorMessage(Invocation inv, String annotation){ + StringBuilder sb = new StringBuilder(); + sb.append("method \"").append(inv.getController().getClass().getName()) + .append(".") + .append(inv.getMethodName()) + .append("()\"") + .append(" has intercepted by annotation ") + .append(annotation); + return sb.toString(); + } + + + + static void renderError(Controller controller, String renderType, String formName, String message, String redirectUrl, String htmlPath, int errorCode) { + String reason = StrUtil.isNotBlank(message) ? (formName + " validate failed: " + message) : (formName + " validate failed!"); + switch (renderType) { + case ValidateRenderType.DEFAULT: + if (RequestUtil.isAjaxRequest(controller.getRequest())) { + controller.renderJson( + Ret.fail("message", message) + .set("reason", reason) + .set("errorCode", errorCode) + .setIfNotNull("formName", formName) + ); + } else { + controller.renderText(reason); + } + break; + case ValidateRenderType.JSON: + controller.renderJson( + Ret.fail("message", message) + .set("reason", reason) + .set("errorCode", errorCode) + .setIfNotNull("formName", formName) + ); + break; + case ValidateRenderType.REDIRECT: + controller.redirect(redirectUrl); + break; + case ValidateRenderType.HTML: + controller.render(htmlPath); + break; + case ValidateRenderType.TEXT: + controller.renderText(message); + break; + default: + throw new IllegalArgumentException("can not process render : " + renderType); + } + } + + +} diff --git a/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorBuilder.java b/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorBuilder.java new file mode 100644 index 00000000..1be17812 --- /dev/null +++ b/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorBuilder.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.validate.interceptor; + +import com.jfinal.aop.Interceptor; +import com.jfinal.core.Controller; +import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.annotation.AutoLoad; +import io.jboot.web.validate.CaptchaValidate; +import io.jboot.web.validate.EmptyValidate; +import io.jboot.web.validate.RegexValidate; + +import java.lang.reflect.Method; +import java.util.LinkedList; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +@AutoLoad +public class ValidateInterceptorBuilder implements InterceptorBuilder { + + + @Override + public void build(Class serviceClass, Method method, LinkedList interceptors) { + + + EmptyValidate emptyParaValidate = method.getAnnotation(EmptyValidate.class); + if (emptyParaValidate != null && Controller.class.isAssignableFrom(serviceClass)) { + interceptors.add(new EmptyValidateInterceptor()); + } + + RegexValidate regexValidate = method.getAnnotation(RegexValidate.class); + if (regexValidate != null && Controller.class.isAssignableFrom(serviceClass)) { + interceptors.add(new RegexValidateInterceptor()); + } + + CaptchaValidate captchaValidate = method.getAnnotation(CaptchaValidate.class); + if (captchaValidate != null && Controller.class.isAssignableFrom(serviceClass)) { + interceptors.add(new CaptchaValidateInterceptor()); + } + } + + +} -- Gitee From ad75348cb088baea67f0cb8f17c53db364870219 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 17 Sep 2020 16:05:37 +0800 Subject: [PATCH 0540/1965] remove FixedInterceptor --- .../java/io/jboot/core/JbootCoreConfig.java | 3 - .../jboot/core/listener/JbootAppListener.java | 3 - .../core/listener/JbootAppListenerBase.java | 10 +- .../listener/JbootAppListenerManager.java | 11 -- .../io/jboot/support/jwt/JwtInterceptor.java | 4 +- .../MetricConcurrencyInterceptor.java | 3 +- .../interceptor/MetricCounterInterceptor.java | 3 +- .../MetricHistogramInterceptor.java | 3 +- .../interceptor/MetricMeterInterceptor.java | 3 +- .../interceptor/MetricTimerInterceptor.java | 3 +- .../SeataGlobalTransactionalInterceptor.java | 3 +- .../seata/tcc/TccActionInterceptor.java | 3 +- .../support/shiro/JbootShiroInterceptor.java | 4 +- .../io/jboot/web/cors/CORSInterceptor.java | 4 +- .../fixedinterceptor/FixedInterceptor.java | 26 ----- .../FixedInterceptorWapper.java | 54 --------- .../fixedinterceptor/FixedInterceptors.java | 105 ------------------ .../io/jboot/web/handler/JbootInvocation.java | 5 +- .../CaptchaValidateInterceptor.java | 4 +- .../interceptor/EmptyValidateInterceptor.java | 4 +- .../interceptor/RegexValidateInterceptor.java | 4 +- .../io/jboot/test/app/TestAppListener.java | 6 - 22 files changed, 23 insertions(+), 245 deletions(-) delete mode 100644 src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptor.java delete mode 100644 src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptorWapper.java delete mode 100644 src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index b7ea3478..f3e11f28 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -55,7 +55,6 @@ import io.jboot.web.directive.annotation.JFinalDirective; import io.jboot.web.directive.annotation.JFinalSharedMethod; import io.jboot.web.directive.annotation.JFinalSharedObject; import io.jboot.web.directive.annotation.JFinalSharedStaticMethod; -import io.jboot.web.fixedinterceptor.FixedInterceptors; import io.jboot.web.handler.JbootActionHandler; import io.jboot.web.handler.JbootHandler; import io.jboot.web.handler.JbootMetricsHandler; @@ -252,9 +251,7 @@ public class JbootCoreConfig extends JFinalConfig { @Override public void configInterceptor(Interceptors interceptors) { - JbootAppListenerManager.me().onInterceptorConfig(interceptors); - JbootAppListenerManager.me().onFixedInterceptorConfig(FixedInterceptors.me()); } @Override diff --git a/src/main/java/io/jboot/core/listener/JbootAppListener.java b/src/main/java/io/jboot/core/listener/JbootAppListener.java index 21a0ccda..e7c539fd 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListener.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListener.java @@ -21,7 +21,6 @@ import com.jfinal.config.Routes; import com.jfinal.template.Engine; import io.jboot.aop.jfinal.JfinalHandlers; import io.jboot.aop.jfinal.JfinalPlugins; -import io.jboot.web.fixedinterceptor.FixedInterceptors; public interface JbootAppListener { @@ -38,8 +37,6 @@ public interface JbootAppListener { public void onInterceptorConfig(Interceptors interceptors); - public void onFixedInterceptorConfig(FixedInterceptors fixedInterceptors); - public void onHandlerConfig(JfinalHandlers handlers); public void onStartBefore(); diff --git a/src/main/java/io/jboot/core/listener/JbootAppListenerBase.java b/src/main/java/io/jboot/core/listener/JbootAppListenerBase.java index 8649bfd1..332749dc 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListenerBase.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListenerBase.java @@ -15,11 +15,12 @@ */ package io.jboot.core.listener; -import com.jfinal.config.*; +import com.jfinal.config.Constants; +import com.jfinal.config.Interceptors; +import com.jfinal.config.Routes; import com.jfinal.template.Engine; import io.jboot.aop.jfinal.JfinalHandlers; import io.jboot.aop.jfinal.JfinalPlugins; -import io.jboot.web.fixedinterceptor.FixedInterceptors; public class JbootAppListenerBase implements JbootAppListener { @@ -55,11 +56,6 @@ public class JbootAppListenerBase implements JbootAppListener { } - @Override - public void onFixedInterceptorConfig(FixedInterceptors fixedInterceptors) { - - } - @Override public void onHandlerConfig(JfinalHandlers handlers) { diff --git a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java index e2ceacb4..f471969a 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java @@ -25,7 +25,6 @@ import io.jboot.aop.jfinal.JfinalPlugins; import io.jboot.core.weight.WeightUtil; import io.jboot.utils.ClassScanner; import io.jboot.utils.ClassUtil; -import io.jboot.web.fixedinterceptor.FixedInterceptors; import java.util.ArrayList; import java.util.List; @@ -130,16 +129,6 @@ public class JbootAppListenerManager implements JbootAppListener { } } - @Override - public void onFixedInterceptorConfig(FixedInterceptors fixedInterceptors) { - for (JbootAppListener listener : listeners) { - try { - listener.onFixedInterceptorConfig(fixedInterceptors); - } catch (Throwable ex) { - log.error(ex.toString(), ex); - } - } - } @Override public void onHandlerConfig(JfinalHandlers handlers) { diff --git a/src/main/java/io/jboot/support/jwt/JwtInterceptor.java b/src/main/java/io/jboot/support/jwt/JwtInterceptor.java index 488d64d8..7e269c67 100644 --- a/src/main/java/io/jboot/support/jwt/JwtInterceptor.java +++ b/src/main/java/io/jboot/support/jwt/JwtInterceptor.java @@ -15,11 +15,11 @@ */ package io.jboot.support.jwt; +import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import io.jboot.exception.JbootException; import io.jboot.utils.StrUtil; import io.jboot.web.controller.JbootController; -import io.jboot.web.fixedinterceptor.FixedInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -30,7 +30,7 @@ import java.util.Map; * @version V1.0 * @Title: 用于对Jwt的设置 */ -public class JwtInterceptor implements FixedInterceptor { +public class JwtInterceptor implements Interceptor { public static final String ISUUED_AT = "isuuedAt"; diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricConcurrencyInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricConcurrencyInterceptor.java index 5ea22124..9314c93a 100644 --- a/src/main/java/io/jboot/support/metric/interceptor/MetricConcurrencyInterceptor.java +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricConcurrencyInterceptor.java @@ -22,9 +22,8 @@ import io.jboot.Jboot; import io.jboot.support.metric.annotation.EnableMetricConcurrency; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.StrUtil; -import io.jboot.web.fixedinterceptor.FixedInterceptor; -public class MetricConcurrencyInterceptor implements Interceptor, FixedInterceptor { +public class MetricConcurrencyInterceptor implements Interceptor { @Override diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricCounterInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricCounterInterceptor.java index 2908b841..7c190469 100644 --- a/src/main/java/io/jboot/support/metric/interceptor/MetricCounterInterceptor.java +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricCounterInterceptor.java @@ -23,10 +23,9 @@ import io.jboot.support.metric.JbootMetricConfig; import io.jboot.support.metric.annotation.EnableMetricCounter; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.StrUtil; -import io.jboot.web.fixedinterceptor.FixedInterceptor; -public class MetricCounterInterceptor implements Interceptor, FixedInterceptor { +public class MetricCounterInterceptor implements Interceptor { private static JbootMetricConfig config = Jboot.config(JbootMetricConfig.class); diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricHistogramInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricHistogramInterceptor.java index a8576b01..ac98f243 100644 --- a/src/main/java/io/jboot/support/metric/interceptor/MetricHistogramInterceptor.java +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricHistogramInterceptor.java @@ -22,9 +22,8 @@ import io.jboot.Jboot; import io.jboot.support.metric.annotation.EnableMetricHistogram; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.StrUtil; -import io.jboot.web.fixedinterceptor.FixedInterceptor; -public class MetricHistogramInterceptor implements Interceptor, FixedInterceptor { +public class MetricHistogramInterceptor implements Interceptor { @Override diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricMeterInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricMeterInterceptor.java index b3760bd4..e7b19d4d 100644 --- a/src/main/java/io/jboot/support/metric/interceptor/MetricMeterInterceptor.java +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricMeterInterceptor.java @@ -22,9 +22,8 @@ import io.jboot.Jboot; import io.jboot.support.metric.annotation.EnableMetricMeter; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.StrUtil; -import io.jboot.web.fixedinterceptor.FixedInterceptor; -public class MetricMeterInterceptor implements Interceptor, FixedInterceptor { +public class MetricMeterInterceptor implements Interceptor { @Override public void intercept(Invocation inv) { diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricTimerInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricTimerInterceptor.java index 3efdbb2e..d5981b2d 100644 --- a/src/main/java/io/jboot/support/metric/interceptor/MetricTimerInterceptor.java +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricTimerInterceptor.java @@ -22,9 +22,8 @@ import io.jboot.Jboot; import io.jboot.support.metric.annotation.EnableMetricTimer; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.StrUtil; -import io.jboot.web.fixedinterceptor.FixedInterceptor; -public class MetricTimerInterceptor implements Interceptor, FixedInterceptor { +public class MetricTimerInterceptor implements Interceptor { @Override diff --git a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java index 513f08b3..1500763e 100644 --- a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java +++ b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java @@ -20,7 +20,6 @@ import com.jfinal.aop.Invocation; import io.jboot.support.seata.JbootSeataManager; import io.jboot.support.seata.annotation.SeataGlobalLock; import io.jboot.support.seata.annotation.SeataGlobalTransactional; -import io.jboot.web.fixedinterceptor.FixedInterceptor; import java.lang.reflect.Method; @@ -31,7 +30,7 @@ import java.lang.reflect.Method; * io/seata/spring/annotation/GlobalTransactionalInterceptor.java * */ -public class SeataGlobalTransactionalInterceptor implements Interceptor, FixedInterceptor { +public class SeataGlobalTransactionalInterceptor implements Interceptor { public SeataGlobalTransactionalInterceptor() { } diff --git a/src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java b/src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java index 9eacde50..c7012bed 100644 --- a/src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java +++ b/src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java @@ -18,7 +18,6 @@ package io.jboot.support.seata.tcc; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import io.jboot.support.seata.JbootSeataManager; -import io.jboot.web.fixedinterceptor.FixedInterceptor; /** @@ -28,7 +27,7 @@ import io.jboot.web.fixedinterceptor.FixedInterceptor; * * @author zhangsen */ -public class TccActionInterceptor implements Interceptor, FixedInterceptor { +public class TccActionInterceptor implements Interceptor { @Override diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java b/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java index 3463e676..447aac3e 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java @@ -15,13 +15,13 @@ */ package io.jboot.support.shiro; +import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import io.jboot.support.shiro.processer.AuthorizeResult; -import io.jboot.web.fixedinterceptor.FixedInterceptor; /** * Shiro 拦截器 */ -public class JbootShiroInterceptor implements FixedInterceptor { +public class JbootShiroInterceptor implements Interceptor { diff --git a/src/main/java/io/jboot/web/cors/CORSInterceptor.java b/src/main/java/io/jboot/web/cors/CORSInterceptor.java index 3a697073..d844805e 100644 --- a/src/main/java/io/jboot/web/cors/CORSInterceptor.java +++ b/src/main/java/io/jboot/web/cors/CORSInterceptor.java @@ -15,11 +15,11 @@ */ package io.jboot.web.cors; +import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import com.jfinal.ext.cors.EnableCORS; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.StrUtil; -import io.jboot.web.fixedinterceptor.FixedInterceptor; import javax.servlet.http.HttpServletResponse; @@ -28,7 +28,7 @@ import javax.servlet.http.HttpServletResponse; * @version V1.0 * @Title: CORS 处理相关 拦截器 */ -public class CORSInterceptor implements FixedInterceptor { +public class CORSInterceptor implements Interceptor { private static final String METHOD_OPTIONS = "OPTIONS"; diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptor.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptor.java deleted file mode 100644 index 4f360959..00000000 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptor.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.web.fixedinterceptor; - -import com.jfinal.aop.Interceptor; - -/** - * @author Michael Yang 杨福海 (fuhai999@gmail.com) - * @title 不会被 @Clear 清除掉的 拦截器 - * @version V1.0 - */ -public interface FixedInterceptor extends Interceptor { -} diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptorWapper.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptorWapper.java deleted file mode 100644 index 63d9f774..00000000 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptorWapper.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.web.fixedinterceptor; - -/** - * FixedInterceptorWapper 排序用 - * - * @author Rlax - */ -public class FixedInterceptorWapper { - - private FixedInterceptor fixedInterceptor; - - private int orderNo = 100; - - public FixedInterceptorWapper(FixedInterceptor fixedInterceptor) { - this.fixedInterceptor = fixedInterceptor; - } - - public FixedInterceptorWapper(FixedInterceptor fixedInterceptor, int orderNo) { - this.fixedInterceptor = fixedInterceptor; - this.orderNo = orderNo; - } - - public FixedInterceptor getFixedInterceptor() { - return fixedInterceptor; - } - - public void setFixedInterceptor(FixedInterceptor fixedInterceptor) { - this.fixedInterceptor = fixedInterceptor; - } - - public int getOrderNo() { - return orderNo; - } - - public void setOrderNo(int orderNo) { - this.orderNo = orderNo; - } - -} diff --git a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java b/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java deleted file mode 100644 index 5b7d419b..00000000 --- a/src/main/java/io/jboot/web/fixedinterceptor/FixedInterceptors.java +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

    - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

    - * http://www.apache.org/licenses/LICENSE-2.0 - *

    - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.web.fixedinterceptor; - -import com.jfinal.aop.Aop; -import com.jfinal.aop.Interceptor; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; - -/** - * @author Michael Yang 杨福海 (fuhai999@gmail.com) - * @version V1.0 - */ -public class FixedInterceptors { - - private static final FixedInterceptors me = new FixedInterceptors(); - - public static FixedInterceptors me() { - return me; - } - - - /** - * 默认的 Jboot 系统拦截器 - */ - private FixedInterceptorWapper[] defaultInters = new FixedInterceptorWapper[]{ -// new FixedInterceptorWapper(new SentinelInterceptor(), 9), -// new FixedInterceptorWapper(new LimiterInterceptor(), 10), -// new FixedInterceptorWapper(new CORSInterceptor(), 20), -// new FixedInterceptorWapper(new ValidateInterceptor(), 30), -// new FixedInterceptorWapper(new JwtInterceptor(), 40), -// new FixedInterceptorWapper(new JbootShiroInterceptor(), 50), -// new FixedInterceptorWapper(new JbootMetricInterceptor(), 60), -// new FixedInterceptorWapper(new SeataGlobalTransactionalInterceptor(), 80), -// new FixedInterceptorWapper(new TccActionInterceptor(), 90) - }; - - private List userInters = new ArrayList<>(); - - private Interceptor[] allInters = null; - - private List inters; - - public Interceptor[] all() { - if (allInters == null) { - synchronized (this) { - if (allInters == null) { - initInters(); - } - } - } - return allInters; - } - - - private void initInters() { - - Interceptor[] interceptors = new Interceptor[defaultInters.length + userInters.size()]; - inters = new ArrayList<>(); - inters.addAll(Arrays.asList(defaultInters)); - inters.addAll(userInters); - inters.sort(Comparator.comparingInt(FixedInterceptorWapper::getOrderNo)); - - int i = 0; - for (FixedInterceptorWapper interceptor : inters) { - interceptors[i++] = interceptor.getFixedInterceptor(); - } - - allInters = interceptors; - } - - - public void add(FixedInterceptor interceptor) { - Aop.inject(interceptor); - userInters.add(new FixedInterceptorWapper(interceptor)); - } - - public void add(FixedInterceptor interceptor, int orderNo) { - if (orderNo < 0) { - orderNo = 0; - } - Aop.inject(interceptor); - userInters.add(new FixedInterceptorWapper(interceptor, orderNo)); - } - - public List list() { - return inters; - } -} diff --git a/src/main/java/io/jboot/web/handler/JbootInvocation.java b/src/main/java/io/jboot/web/handler/JbootInvocation.java index 9ee10325..8536553b 100644 --- a/src/main/java/io/jboot/web/handler/JbootInvocation.java +++ b/src/main/java/io/jboot/web/handler/JbootInvocation.java @@ -22,8 +22,6 @@ import com.jfinal.core.Controller; import com.jfinal.core.JFinal; import io.jboot.aop.InterceptorBuilderManager; import io.jboot.aop.cglib.JbootCglibProxyFactory; -import io.jboot.utils.ArrayUtil; -import io.jboot.web.fixedinterceptor.FixedInterceptors; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; @@ -65,8 +63,7 @@ public class JbootInvocation extends Invocation { if (inters == null) { // jfinal 原生的构建 - inters = ArrayUtil.concat(FixedInterceptors.me().all(), action.getInterceptors()); - + inters = action.getInterceptors(); // builder 再次构建 inters = InterceptorBuilderManager.me().build(action.getControllerClass(), action.getMethod(), inters); diff --git a/src/main/java/io/jboot/web/validate/interceptor/CaptchaValidateInterceptor.java b/src/main/java/io/jboot/web/validate/interceptor/CaptchaValidateInterceptor.java index 3eddd4d3..bbb48870 100644 --- a/src/main/java/io/jboot/web/validate/interceptor/CaptchaValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/interceptor/CaptchaValidateInterceptor.java @@ -15,19 +15,19 @@ */ package io.jboot.web.validate.interceptor; +import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import com.jfinal.core.Controller; import com.jfinal.log.Log; import io.jboot.Jboot; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.StrUtil; -import io.jboot.web.fixedinterceptor.FixedInterceptor; import io.jboot.web.validate.CaptchaValidate; /** * 验证拦截器 */ -public class CaptchaValidateInterceptor implements FixedInterceptor { +public class CaptchaValidateInterceptor implements Interceptor { private static final Log LOG = Log.getLog("Validate"); diff --git a/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java b/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java index 5a49a471..dbb4d1f2 100644 --- a/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java @@ -18,13 +18,13 @@ package io.jboot.web.validate.interceptor; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONPath; +import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import com.jfinal.log.Log; import io.jboot.Jboot; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.ArrayUtil; import io.jboot.utils.StrUtil; -import io.jboot.web.fixedinterceptor.FixedInterceptor; import io.jboot.web.validate.EmptyValidate; import io.jboot.web.validate.Form; import io.jboot.web.validate.FormType; @@ -32,7 +32,7 @@ import io.jboot.web.validate.FormType; /** * 验证拦截器 */ -public class EmptyValidateInterceptor implements FixedInterceptor { +public class EmptyValidateInterceptor implements Interceptor { private static final Log LOG = Log.getLog("Validate"); diff --git a/src/main/java/io/jboot/web/validate/interceptor/RegexValidateInterceptor.java b/src/main/java/io/jboot/web/validate/interceptor/RegexValidateInterceptor.java index 2fb62299..2e4bda22 100644 --- a/src/main/java/io/jboot/web/validate/interceptor/RegexValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/interceptor/RegexValidateInterceptor.java @@ -18,13 +18,13 @@ package io.jboot.web.validate.interceptor; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONPath; +import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import com.jfinal.log.Log; import io.jboot.Jboot; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.ArrayUtil; import io.jboot.utils.StrUtil; -import io.jboot.web.fixedinterceptor.FixedInterceptor; import io.jboot.web.validate.FormType; import io.jboot.web.validate.RegexForm; import io.jboot.web.validate.RegexValidate; @@ -32,7 +32,7 @@ import io.jboot.web.validate.RegexValidate; /** * 验证拦截器 */ -public class RegexValidateInterceptor implements FixedInterceptor { +public class RegexValidateInterceptor implements Interceptor { private static final Log LOG = Log.getLog("Validate"); diff --git a/src/test/java/io/jboot/test/app/TestAppListener.java b/src/test/java/io/jboot/test/app/TestAppListener.java index ecaf72bf..96306f1d 100644 --- a/src/test/java/io/jboot/test/app/TestAppListener.java +++ b/src/test/java/io/jboot/test/app/TestAppListener.java @@ -7,7 +7,6 @@ import com.jfinal.template.Engine; import io.jboot.aop.jfinal.JfinalHandlers; import io.jboot.aop.jfinal.JfinalPlugins; import io.jboot.core.listener.JbootAppListener; -import io.jboot.web.fixedinterceptor.FixedInterceptors; public class TestAppListener implements JbootAppListener { @Override @@ -40,11 +39,6 @@ public class TestAppListener implements JbootAppListener { System.out.println("TestAppListener.onInterceptorConfig"); } - @Override - public void onFixedInterceptorConfig(FixedInterceptors fixedInterceptors) { - System.out.println("TestAppListener.onFixedInterceptorConfig"); - } - @Override public void onHandlerConfig(JfinalHandlers handlers) { System.out.println("TestAppListener.onHandlerConfig"); -- Gitee From 4dc763ff930e70f7464c8846c9c67a63fc604d55 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 17 Sep 2020 16:51:09 +0800 Subject: [PATCH 0541/1965] optimize InterceptorBuilder --- .../java/io/jboot/aop/InterceptorBuilder.java | 9 +- .../jboot/aop/InterceptorBuilderManager.java | 23 +-- src/main/java/io/jboot/aop/Interceptors.java | 160 ++++++++++++++++++ .../interceptor/CacheInterceptorBuilder.java | 5 +- .../LimiterInterceptorBuilder.java | 5 +- .../support/jwt/JwtInterceptorBuilder.java | 5 +- .../interceptor/MetricInterceptorBuilder.java | 5 +- .../SeataGlobalInterceptorBuilder.java | 5 +- .../seata/tcc/TccInterceptorBuilder.java | 5 +- .../sentinel/SentinelInterceptorBuilder.java | 5 +- .../shiro/ShiroInterceptorBuilder.java | 5 +- .../web/cors/CORSInterceptorBuilder.java | 6 +- .../ValidateInterceptorBuilder.java | 5 +- 13 files changed, 188 insertions(+), 55 deletions(-) create mode 100644 src/main/java/io/jboot/aop/Interceptors.java diff --git a/src/main/java/io/jboot/aop/InterceptorBuilder.java b/src/main/java/io/jboot/aop/InterceptorBuilder.java index 57c1de95..ce34a7af 100644 --- a/src/main/java/io/jboot/aop/InterceptorBuilder.java +++ b/src/main/java/io/jboot/aop/InterceptorBuilder.java @@ -15,15 +15,12 @@ */ package io.jboot.aop; -import com.jfinal.aop.Interceptor; - import java.lang.reflect.Method; -import java.util.LinkedList; /** * @author michael yang (fuhai999@gmail.com) - * - * InterceptorHandler 用于控制某个方法已经添加好的拦截器,可以对其删除或者添加 + *

    + * InterceptorBuilder 用于控制某个方法已经添加好的拦截器,可以对其删除或者添加 * *

      * 配置方法:
    @@ -33,5 +30,5 @@ import java.util.LinkedList;
      * 
    */ public interface InterceptorBuilder { - void build(Class serviceClass, Method method, LinkedList interceptors); + void build(Class serviceClass, Method method, Interceptors interceptors); } diff --git a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java index 32072f8b..c17e8436 100644 --- a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java +++ b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java @@ -17,7 +17,6 @@ package io.jboot.aop; import com.jfinal.aop.AopFactory; import com.jfinal.aop.Interceptor; -import com.jfinal.aop.InterceptorManager; import io.jboot.aop.annotation.AutoLoad; import io.jboot.aop.cglib.JbootCglibProxyFactory; import io.jboot.core.weight.WeightUtil; @@ -26,7 +25,6 @@ import io.jboot.utils.ClassUtil; import java.lang.reflect.Method; import java.util.Collection; -import java.util.LinkedList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -88,29 +86,18 @@ public class InterceptorBuilderManager extends AopFactory { - public Interceptor[] build(Class targetClass, Method method, Interceptor[] interceptors) { + public Interceptor[] build(Class targetClass, Method method, Interceptor[] inters) { if (interceptorBuilders != null && interceptorBuilders.size() > 0) { - LinkedList list = toLinkedList(interceptors); + Interceptors interceptors = new Interceptors(inters); for (InterceptorBuilder builder : interceptorBuilders) { - builder.build(targetClass, method, list); + builder.build(targetClass, method, interceptors); } - return list.isEmpty() ? InterceptorManager.NULL_INTERS : list.toArray(new Interceptor[0]); + return interceptors.getInterceptorArray(); } - return interceptors; + return inters; } - - private static LinkedList toLinkedList(Interceptor[] interceptors) { - LinkedList linkedList = new LinkedList<>(); - if (interceptors != null) { - for (Interceptor interceptor : interceptors) { - linkedList.add(interceptor); - } - } - return linkedList; - } - } diff --git a/src/main/java/io/jboot/aop/Interceptors.java b/src/main/java/io/jboot/aop/Interceptors.java new file mode 100644 index 00000000..add4e9b1 --- /dev/null +++ b/src/main/java/io/jboot/aop/Interceptors.java @@ -0,0 +1,160 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.InterceptorManager; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author michael yang (fuhai999@gmail.com) + */ +public class Interceptors { + + private List warppers = new ArrayList<>(); + + private int minimalWeight = 1; + private int maximumWeight = 1; + private int currentWeight = 1; + + public Interceptors() { + } + + public Interceptors(Interceptor[] inters) { + if (inters != null && inters.length > 0) { + for (Interceptor interceptor : inters) { + add(interceptor); + } + } + } + + + public void add(Interceptor interceptor) { + warppers.add(new InterceptorWarpper(interceptor, currentWeight++)); + + if (currentWeight > maximumWeight) { + maximumWeight = currentWeight; + } + } + + + public void add(Interceptor interceptor, int weight) { + warppers.add(new InterceptorWarpper(interceptor, weight)); + + if (weight >= maximumWeight) { + maximumWeight = weight + 1; + } + if (weight <= minimalWeight) { + minimalWeight = weight - 1; + } + } + + + public void addToFirst(Interceptor interceptor) { + warppers.add(new InterceptorWarpper(interceptor, --minimalWeight)); + } + + + public void addToLast(Interceptor interceptor) { + warppers.add(new InterceptorWarpper(interceptor, ++maximumWeight)); + } + + public void addBefore(Interceptor interceptor, Class interceptroClass) { + int weight = ++currentWeight; + for (InterceptorWarpper warpper : warppers) { + if (warpper.getInterceptor().getClass() == interceptroClass) { + weight = warpper.weight - 1; + } + } + warppers.add(new InterceptorWarpper(interceptor, weight)); + if (currentWeight > maximumWeight) { + maximumWeight = currentWeight; + } + } + + public void addAfter(Interceptor interceptor, Class interceptroClass) { + int weight = ++currentWeight; + for (InterceptorWarpper warpper : warppers) { + if (warpper.getInterceptor().getClass() == interceptroClass) { + weight = warpper.weight + 1; + } + } + warppers.add(new InterceptorWarpper(interceptor, weight)); + + if (currentWeight > maximumWeight) { + maximumWeight = currentWeight; + } + } + + + public void remove(Interceptor interceptor) { + warppers.removeIf(interceptorWarpper -> interceptorWarpper.interceptor == interceptor); + } + + public void removeByClass(Class clazz) { + warppers.removeIf(interceptorWarpper -> interceptorWarpper.interceptor.getClass() == clazz); + } + + public List getInterceptors() { + return warppers.stream().map(InterceptorWarpper::getInterceptor).collect(Collectors.toList()); + } + + public Interceptor[] getInterceptorArray() { + if (warppers == null || warppers.size() == 0) { + return InterceptorManager.NULL_INTERS; + } else { + warppers.sort(Comparator.comparingInt(InterceptorWarpper::getWeight)); + Interceptor[] inters = new Interceptor[warppers.size()]; + for (int i = 0; i < warppers.size(); i++) { + inters[i] = warppers.get(i).getInterceptor(); + } + return inters; + } + } + + + static class InterceptorWarpper { + + private Interceptor interceptor; + private int weight = 0; + + public InterceptorWarpper(Interceptor interceptor, int weight) { + this.interceptor = interceptor; + this.weight = weight; + } + + public Interceptor getInterceptor() { + return interceptor; + } + + public void setInterceptor(Interceptor interceptor) { + this.interceptor = interceptor; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + } + +} diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java b/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java index 6b14c874..1230665a 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java @@ -15,8 +15,8 @@ */ package io.jboot.components.cache.interceptor; -import com.jfinal.aop.Interceptor; import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.Interceptors; import io.jboot.aop.annotation.AutoLoad; import io.jboot.components.cache.annotation.CacheEvict; import io.jboot.components.cache.annotation.CachePut; @@ -24,7 +24,6 @@ import io.jboot.components.cache.annotation.Cacheable; import io.jboot.components.cache.annotation.CachesEvict; import java.lang.reflect.Method; -import java.util.LinkedList; /** * @author michael yang (fuhai999@gmail.com) @@ -34,7 +33,7 @@ public class CacheInterceptorBuilder implements InterceptorBuilder { @Override - public void build(Class serviceClass, Method method, LinkedList interceptors) { + public void build(Class serviceClass, Method method, Interceptors interceptors) { CacheEvict cacheEvict = method.getAnnotation(CacheEvict.class); if (cacheEvict != null) { interceptors.add(new CacheEvictInterceptor()); diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java index a49cc591..de62cf4b 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java @@ -15,14 +15,13 @@ */ package io.jboot.components.limiter.interceptor; -import com.jfinal.aop.Interceptor; import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.Interceptors; import io.jboot.aop.annotation.AutoLoad; import io.jboot.components.limiter.LimiterManager; import io.jboot.components.limiter.annotation.EnableLimit; import java.lang.reflect.Method; -import java.util.LinkedList; /** * @author michael yang (fuhai999@gmail.com) @@ -33,7 +32,7 @@ public class LimiterInterceptorBuilder implements InterceptorBuilder { private LimiterManager manager = LimiterManager.me(); @Override - public void build(Class serviceClass, Method method, LinkedList interceptors) { + public void build(Class serviceClass, Method method, Interceptors interceptors) { if (manager.isEnable() && !manager.getConfigPackageOrTargets().isEmpty()) { interceptors.add(new LimiterGlobalInterceptor()); diff --git a/src/main/java/io/jboot/support/jwt/JwtInterceptorBuilder.java b/src/main/java/io/jboot/support/jwt/JwtInterceptorBuilder.java index 1716141c..c31fadb3 100644 --- a/src/main/java/io/jboot/support/jwt/JwtInterceptorBuilder.java +++ b/src/main/java/io/jboot/support/jwt/JwtInterceptorBuilder.java @@ -15,13 +15,12 @@ */ package io.jboot.support.jwt; -import com.jfinal.aop.Interceptor; import com.jfinal.core.Controller; import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.Interceptors; import io.jboot.aop.annotation.AutoLoad; import java.lang.reflect.Method; -import java.util.LinkedList; /** * @author michael yang (fuhai999@gmail.com) @@ -32,7 +31,7 @@ public class JwtInterceptorBuilder implements InterceptorBuilder { private static JwtManager manager = JwtManager.me(); @Override - public void build(Class serviceClass, Method method, LinkedList interceptors) { + public void build(Class serviceClass, Method method, Interceptors interceptors) { EnableJwt enableAnnotation = getAnnotation(serviceClass,method); if (enableAnnotation != null && Controller.class.isAssignableFrom(serviceClass)) { interceptors.add(new JwtInterceptor()); diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricInterceptorBuilder.java b/src/main/java/io/jboot/support/metric/interceptor/MetricInterceptorBuilder.java index 180ac321..8e8b19f7 100644 --- a/src/main/java/io/jboot/support/metric/interceptor/MetricInterceptorBuilder.java +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricInterceptorBuilder.java @@ -15,15 +15,14 @@ */ package io.jboot.support.metric.interceptor; -import com.jfinal.aop.Interceptor; import io.jboot.Jboot; import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.Interceptors; import io.jboot.aop.annotation.AutoLoad; import io.jboot.support.metric.JbootMetricConfig; import io.jboot.support.metric.annotation.*; import java.lang.reflect.Method; -import java.util.LinkedList; /** * @author michael yang (fuhai999@gmail.com) @@ -34,7 +33,7 @@ public class MetricInterceptorBuilder implements InterceptorBuilder { private static JbootMetricConfig config = Jboot.config(JbootMetricConfig.class); @Override - public void build(Class serviceClass, Method method, LinkedList interceptors) { + public void build(Class serviceClass, Method method, Interceptors interceptors) { if (!config.isConfigOk()) { return; } diff --git a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalInterceptorBuilder.java b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalInterceptorBuilder.java index a1acfcf0..cd1e41ba 100644 --- a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalInterceptorBuilder.java +++ b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalInterceptorBuilder.java @@ -15,14 +15,13 @@ */ package io.jboot.support.seata.interceptor; -import com.jfinal.aop.Interceptor; import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.Interceptors; import io.jboot.aop.annotation.AutoLoad; import io.jboot.support.seata.annotation.SeataGlobalLock; import io.jboot.support.seata.annotation.SeataGlobalTransactional; import java.lang.reflect.Method; -import java.util.LinkedList; /** * @author michael yang (fuhai999@gmail.com) @@ -32,7 +31,7 @@ public class SeataGlobalInterceptorBuilder implements InterceptorBuilder { @Override - public void build(Class serviceClass, Method method, LinkedList interceptors) { + public void build(Class serviceClass, Method method, Interceptors interceptors) { final SeataGlobalTransactional globalTrxAnno = method.getAnnotation(SeataGlobalTransactional.class); final SeataGlobalLock globalLockAnno = method.getAnnotation(SeataGlobalLock.class); diff --git a/src/main/java/io/jboot/support/seata/tcc/TccInterceptorBuilder.java b/src/main/java/io/jboot/support/seata/tcc/TccInterceptorBuilder.java index adc871bb..fdd5680d 100644 --- a/src/main/java/io/jboot/support/seata/tcc/TccInterceptorBuilder.java +++ b/src/main/java/io/jboot/support/seata/tcc/TccInterceptorBuilder.java @@ -15,13 +15,12 @@ */ package io.jboot.support.seata.tcc; -import com.jfinal.aop.Interceptor; import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.Interceptors; import io.jboot.aop.annotation.AutoLoad; import io.seata.rm.tcc.api.TwoPhaseBusinessAction; import java.lang.reflect.Method; -import java.util.LinkedList; /** * @author michael yang (fuhai999@gmail.com) @@ -31,7 +30,7 @@ public class TccInterceptorBuilder implements InterceptorBuilder { @Override - public void build(Class serviceClass, Method method, LinkedList interceptors) { + public void build(Class serviceClass, Method method, Interceptors interceptors) { try { Class.forName("io.seata.rm.tcc.api.TwoPhaseBusinessAction"); diff --git a/src/main/java/io/jboot/support/sentinel/SentinelInterceptorBuilder.java b/src/main/java/io/jboot/support/sentinel/SentinelInterceptorBuilder.java index 98bb1b0e..590264aa 100644 --- a/src/main/java/io/jboot/support/sentinel/SentinelInterceptorBuilder.java +++ b/src/main/java/io/jboot/support/sentinel/SentinelInterceptorBuilder.java @@ -16,12 +16,11 @@ package io.jboot.support.sentinel; import com.alibaba.csp.sentinel.annotation.SentinelResource; -import com.jfinal.aop.Interceptor; import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.Interceptors; import io.jboot.aop.annotation.AutoLoad; import java.lang.reflect.Method; -import java.util.LinkedList; /** * @author michael yang (fuhai999@gmail.com) @@ -31,7 +30,7 @@ public class SentinelInterceptorBuilder implements InterceptorBuilder { @Override - public void build(Class serviceClass, Method method, LinkedList interceptors) { + public void build(Class serviceClass, Method method, Interceptors interceptors) { try { Class.forName("com.alibaba.csp.sentinel.Sph"); diff --git a/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java b/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java index 3b1d4a18..9800db42 100644 --- a/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java +++ b/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java @@ -15,14 +15,13 @@ */ package io.jboot.support.shiro; -import com.jfinal.aop.Interceptor; import com.jfinal.core.Controller; import io.jboot.Jboot; import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.Interceptors; import io.jboot.aop.annotation.AutoLoad; import java.lang.reflect.Method; -import java.util.LinkedList; /** * @author michael yang (fuhai999@gmail.com) @@ -33,7 +32,7 @@ public class ShiroInterceptorBuilder implements InterceptorBuilder { private static JbootShiroConfig config = Jboot.config(JbootShiroConfig.class); @Override - public void build(Class serviceClass, Method method, LinkedList interceptors) { + public void build(Class serviceClass, Method method, Interceptors interceptors) { if (config.isConfigOK() && Controller.class.isAssignableFrom(serviceClass)) { interceptors.add(new JbootShiroInterceptor()); diff --git a/src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java b/src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java index b08d1de6..1995b289 100644 --- a/src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java +++ b/src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java @@ -15,13 +15,12 @@ */ package io.jboot.web.cors; -import com.jfinal.aop.Interceptor; import com.jfinal.ext.cors.EnableCORS; import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.Interceptors; import io.jboot.aop.annotation.AutoLoad; import java.lang.reflect.Method; -import java.util.LinkedList; /** * @author michael yang (fuhai999@gmail.com) @@ -31,12 +30,11 @@ public class CORSInterceptorBuilder implements InterceptorBuilder { @Override - public void build(Class serviceClass, Method method, LinkedList interceptors) { + public void build(Class serviceClass, Method method, Interceptors interceptors) { EnableCORS enableCORS = getAnnotation(serviceClass,method); if (enableCORS != null) { interceptors.add(new CORSInterceptor()); } - } private EnableCORS getAnnotation(Class serviceClass, Method method) { diff --git a/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorBuilder.java b/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorBuilder.java index 1be17812..87a77fa7 100644 --- a/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorBuilder.java +++ b/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorBuilder.java @@ -15,16 +15,15 @@ */ package io.jboot.web.validate.interceptor; -import com.jfinal.aop.Interceptor; import com.jfinal.core.Controller; import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.Interceptors; import io.jboot.aop.annotation.AutoLoad; import io.jboot.web.validate.CaptchaValidate; import io.jboot.web.validate.EmptyValidate; import io.jboot.web.validate.RegexValidate; import java.lang.reflect.Method; -import java.util.LinkedList; /** * @author michael yang (fuhai999@gmail.com) @@ -34,7 +33,7 @@ public class ValidateInterceptorBuilder implements InterceptorBuilder { @Override - public void build(Class serviceClass, Method method, LinkedList interceptors) { + public void build(Class serviceClass, Method method, Interceptors interceptors) { EmptyValidate emptyParaValidate = method.getAnnotation(EmptyValidate.class); -- Gitee From b27f17e17098985ee47d35a19e0e0b4aa2fac3a9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 17 Sep 2020 17:49:35 +0800 Subject: [PATCH 0542/1965] add controller method invoke log --- .../io/jboot/web/handler/JbootActionReporter.java | 5 +++++ .../java/io/jboot/web/handler/JbootInvocation.java | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index cedf085c..5e997bad 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -88,6 +88,11 @@ public class JbootActionReporter { sb.append("Request : ").append(controller.getRequest().getMethod()).append(" ").append(target).append("\n"); Class cc = action.getMethod().getDeclaringClass(); sb.append("Controller : ").append(cc.getName()).append(".(").append(getClassFileName(cc)).append(".java:" + lineNumber + ")"); + if (JbootInvocation.isControllerInvoked()) { + sb.append(ConsoleColor.GREEN_BRIGHT + " ---> invoked √" + ConsoleColor.RESET); + } else { + sb.append(ConsoleColor.RED_BRIGHT + " ---> skipped ×" + ConsoleColor.RESET); + } sb.append("\nMethod : ").append(JbootActionReporterUtil.getMethodString(action.getMethod())).append("\n"); diff --git a/src/main/java/io/jboot/web/handler/JbootInvocation.java b/src/main/java/io/jboot/web/handler/JbootInvocation.java index 8536553b..62c4d08a 100644 --- a/src/main/java/io/jboot/web/handler/JbootInvocation.java +++ b/src/main/java/io/jboot/web/handler/JbootInvocation.java @@ -41,7 +41,9 @@ public class JbootInvocation extends Invocation { private Object returnValue; - private static ThreadLocal> invokedInterceptors = ThreadLocal.withInitial(ArrayList::new); + protected static ThreadLocal> invokedInterceptors = ThreadLocal.withInitial(ArrayList::new); + protected static ThreadLocal controllerInvokeFlag = ThreadLocal.withInitial(() -> Boolean.FALSE); + private static boolean devMode = JFinal.me().getConstants().getDevMode(); public JbootInvocation(Action action, Controller controller) { @@ -86,6 +88,9 @@ public class JbootInvocation extends Invocation { } else if (index++ == inters.length) { // index++ ensure invoke action only one time try { // Invoke the action + if (devMode){ + controllerInvokeFlag.set(true); + } returnValue = action.getMethod().invoke(target, args); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); @@ -117,10 +122,15 @@ public class JbootInvocation extends Invocation { return invokedInterceptors.get(); } + public static boolean isControllerInvoked(){ + return controllerInvokeFlag.get(); + } + public static void clear() { invokedInterceptors.get().clear(); invokedInterceptors.remove(); + controllerInvokeFlag.remove(); } public Interceptor[] getInters() { -- Gitee From ca0e9335741939b1c5e43ecb4eff3c749ecad334 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 17 Sep 2020 17:55:06 +0800 Subject: [PATCH 0543/1965] update docs --- doc/docs/jfinalConfig.md | 6 ------ doc/docs/mvc.md | 4 ---- 2 files changed, 10 deletions(-) diff --git a/doc/docs/jfinalConfig.md b/doc/docs/jfinalConfig.md index 095e72e9..068fcace 100644 --- a/doc/docs/jfinalConfig.md +++ b/doc/docs/jfinalConfig.md @@ -61,12 +61,6 @@ public class JbootAppListenerBase implements JbootAppListener { //对应 JFinalConfig 的 configInterceptor } - @Override - public void onFixedInterceptorConfig(FixedInterceptors fixedInterceptors) { - //FixedInterceptor 类似 Interceptor, - // 但是 FixedInterceptor 不会被注解 @Clear 清除 - } - @Override public void onHandlerConfig(JfinalHandlers handlers) { //对应 JFinalConfig 的 configHandler diff --git a/doc/docs/mvc.md b/doc/docs/mvc.md index 43fcf2ad..a076dd7c 100644 --- a/doc/docs/mvc.md +++ b/doc/docs/mvc.md @@ -7,7 +7,6 @@ - Controller : 控制器 - Action :请求的基本单位 - Interceptor : 拦截器 -- FixedInterceptor :永久拦截器 - Handler : 处理器 - Render :渲染器 - Session @@ -40,9 +39,6 @@ Action 的相关文档请参考: [https://www.jfinal.com/doc/3-2](https://www. Interceptor 拦截器的相关文档请参考 [https://www.jfinal.com/doc/4-2](https://www.jfinal.com/doc/4-2) -## FixedInterceptor :永久拦截器 - -FixedInterceptor 的用法和 Interceptor 一样,但是 `FixedInterceptor` 不会被 `@Clear` 清除,常用于给 Controller 或者 Service 的注解进行增强。 ## Handler : 处理器 -- Gitee From 0f08dd4e994721f35ed81a18e8f66fe36f406302 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 17 Sep 2020 18:06:52 +0800 Subject: [PATCH 0544/1965] optimize LocalAttachmentContainer --- .../jboot/web/attachment/LocalAttachmentContainer.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java index dced0433..8f5257c1 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -113,7 +113,9 @@ public class LocalAttachmentContainer implements AttachmentContainer { @Override public boolean matchFile(String target, HttpServletRequest request) { - return target.startsWith(buildMatchTarget()) && target.lastIndexOf('.') != -1; + return !isRunInFatjar() //非 fatjar 模式下,让容器(tomcat 或者 undertow)去渲染 + && target.startsWith(buildMatchTarget()) + && target.lastIndexOf('.') != -1; } private String matchTarget = null; @@ -142,11 +144,6 @@ public class LocalAttachmentContainer implements AttachmentContainer { @Override public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { - //是否在 fatjar 运行,如果不在 fatjar 运行,由 tomcat 或者 undertow 自行渲染 - if (!isRunInFatjar()) { - return false; - } - // 如果在 fatjar 运行,需要自己来渲染该文件 new FileRender(getFile(target)).setContext(request, response).render(); return true; } -- Gitee From 6dd9855ec988a7f1a2e83f4163a289ba3ec40e57 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 18 Sep 2020 11:35:57 +0800 Subject: [PATCH 0545/1965] optimize AttachmentManager --- .../web/attachment/AttachmentContainer.java | 26 +--------- .../web/attachment/AttachmentHandler.java | 12 ++--- .../web/attachment/AttachmentManager.java | 37 +++++++------- .../attachment/LocalAttachmentContainer.java | 51 +------------------ .../LocalAttachmentContainerConfig.java | 2 +- 5 files changed, 30 insertions(+), 98 deletions(-) diff --git a/src/main/java/io/jboot/web/attachment/AttachmentContainer.java b/src/main/java/io/jboot/web/attachment/AttachmentContainer.java index 04c1a354..ffca96a6 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentContainer.java @@ -16,8 +16,6 @@ package io.jboot.web.attachment; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.File; /** @@ -40,6 +38,7 @@ public interface AttachmentContainer { */ public boolean deleteFile(String relativePath); + /** * 通过相对路径获取文件 * @param relativePath @@ -47,6 +46,7 @@ public interface AttachmentContainer { */ public File getFile(String relativePath); + /** * 通过一个文件,获取其相对路径 * @param file @@ -55,27 +55,5 @@ public interface AttachmentContainer { public String getRelativePath(File file); - /** - * 是否是 远程 文件容器 - * @return - */ - public boolean isRemoteContainer(); - - - /** - * 通过路径判断是匹配这个文件 - * @param target - * @param request - * @return - */ - public boolean matchFile(String target, HttpServletRequest request); - /** - * 渲染这个文件 - * @param target - * @param request - * @param response - * @return return true 渲染成功,不需要服务器再进行渲染,return false 需要服务器自行渲染 - */ - public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response); } diff --git a/src/main/java/io/jboot/web/attachment/AttachmentHandler.java b/src/main/java/io/jboot/web/attachment/AttachmentHandler.java index 77af81d4..1485b739 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentHandler.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentHandler.java @@ -29,12 +29,12 @@ public class AttachmentHandler extends Handler { @Override public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { - AttachmentContainer container = manager.matchContainer(target, request); - if (container != null) { - if (container.renderFile(target, request, response)){ - isHandled[0] = true; - } - } else { + // manager 成功渲染文件 + if (manager.renderFile(target, request, response)) { + isHandled[0] = true; + } + // manager 不渲染,由容器本身去渲染 + else { next.handle(target, request, response, isHandled); } } diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index 8e88a8ac..c1699bfd 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -17,8 +17,10 @@ package io.jboot.web.attachment; import com.jfinal.log.Log; +import com.jfinal.render.FileRender; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.File; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -43,7 +45,7 @@ public class AttachmentManager { /** * 默认的 附件容器 */ - private AttachmentContainer defaultContainer = new LocalAttachmentContainer(); + private LocalAttachmentContainer defaultContainer = new LocalAttachmentContainer(); /** * 其他附件容器 @@ -51,11 +53,11 @@ public class AttachmentManager { private List containers = new CopyOnWriteArrayList<>(); - public AttachmentContainer getDefaultContainer() { + public LocalAttachmentContainer getDefaultContainer() { return defaultContainer; } - public void setDefaultContainer(AttachmentContainer defaultContainer) { + public void setDefaultContainer(LocalAttachmentContainer defaultContainer) { this.defaultContainer = defaultContainer; } @@ -73,21 +75,6 @@ public class AttachmentManager { } - public AttachmentContainer matchContainer(String target, HttpServletRequest request) { - - if (defaultContainer.matchFile(target, request)) { - return defaultContainer; - } - - for (AttachmentContainer container : containers) { - if (container.matchFile(target, request)) { - return container; - } - } - - return null; - } - /** * 保存文件 @@ -167,4 +154,18 @@ public class AttachmentManager { String relativePath = defaultContainer.getRelativePath(file); return relativePath != null ? relativePath.replace("\\", "/") : null; } + + + + + public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { + if (target.startsWith(defaultContainer.getTargetPrefix()) && target.lastIndexOf('.') != -1){ + new FileRender(getFile(target)).setContext(request, response).render(); + return true; + }else { + return false; + } + } + + } diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java index 8f5257c1..a15398f8 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -17,12 +17,9 @@ package io.jboot.web.attachment; import com.jfinal.ext.kit.DateKit; import com.jfinal.kit.LogKit; -import com.jfinal.render.FileRender; import io.jboot.utils.FileUtil; import io.jboot.utils.StrUtil; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.util.Date; @@ -50,6 +47,7 @@ public class LocalAttachmentContainer implements AttachmentContainer { this.targetPrefix = targetPrefix; } + @Override public String saveFile(File file) { File newfile = newRandomFile(FileUtil.getSuffix(file.getName())); @@ -79,8 +77,7 @@ public class LocalAttachmentContainer implements AttachmentContainer { public File newRandomFile(String suffix) { String rootPath = getRootPath(); - StringBuilder newFileName = new StringBuilder(rootPath) - .append(File.separator).append(targetPrefix) + StringBuilder newFileName = new StringBuilder(rootPath).append(targetPrefix) .append(File.separator).append(DateKit.toStr(new Date(), "yyyyMMdd")) .append(File.separator).append(StrUtil.uuid()) .append(suffix); @@ -105,49 +102,6 @@ public class LocalAttachmentContainer implements AttachmentContainer { } - @Override - public boolean isRemoteContainer() { - return false; - } - - - @Override - public boolean matchFile(String target, HttpServletRequest request) { - return !isRunInFatjar() //非 fatjar 模式下,让容器(tomcat 或者 undertow)去渲染 - && target.startsWith(buildMatchTarget()) - && target.lastIndexOf('.') != -1; - } - - private String matchTarget = null; - - private String buildMatchTarget() { - if (matchTarget == null) { - synchronized (this) { - if (matchTarget == null) { - matchTarget = "/" + getTargetPrefix() + "/"; - } - } - } - return matchTarget; - } - - - private Boolean runInFatjar; - - protected boolean isRunInFatjar() { - if (runInFatjar == null) { - runInFatjar = LocalAttachmentContainer.class.getResource("/") == null; - } - return runInFatjar; - } - - - @Override - public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { - new FileRender(getFile(target)).setContext(request, response).render(); - return true; - } - public String getRootPath() { return rootPath; @@ -163,6 +117,5 @@ public class LocalAttachmentContainer implements AttachmentContainer { public void setTargetPrefix(String targetPrefix) { this.targetPrefix = targetPrefix; - this.matchTarget = null; } } diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java index 213943ed..1f862416 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java @@ -28,7 +28,7 @@ import java.io.File; public class LocalAttachmentContainerConfig { private String rootPath = PathKit.getWebRootPath(); - private String targetPrefix = "attachment"; + private String targetPrefix = "/attachment"; public String getRootPath() { return rootPath; -- Gitee From e69a9f03262e311fc0ed23fdb574c6a9310a54d4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 18 Sep 2020 16:09:05 +0800 Subject: [PATCH 0546/1965] v3.5.0 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 8 ++++++++ doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- src/test/resources/jboot.properties | 2 +- 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 19407874..e01c1cf3 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.4.3 + 3.5.0 ``` diff --git a/changes.txt b/changes.txt index 3f2dffc6..052cd817 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,11 @@ +jboot v3.5.0: +新增:InterceptorBuilder 组件,方便对 Controller 或者 AOP 对象的拦截器进行构建 +优化:移除 FixedInterceptor 组件,其可以通过 InterceptorBuilder 进行替代 +优化:新增 Controller 方法是否被正常执行的 log +优化:优化 AttachmentManager 对分布式文件的渲染流程 + + + jboot v3.4.3: 新增:指令 @JFinalDirective 新增 override 配置,用于覆盖系统已经内置的指令 优化:优化 AttachmentManager,使之更加方便的上传 获取文件 diff --git a/doc/docs/install.md b/doc/docs/install.md index 75c48c95..a2543110 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.4.3 + 3.5.0 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 50f138cc..d4e3edac 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.4.3 + 3.5.0 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 4aa64564..d8e7f9ce 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -49,7 +49,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.4.3 + 3.5.0 ``` @@ -107,7 +107,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.4.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.5.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 87f32df6..f3ca3dba 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.4.3"; + public static String VERSION = "3.5.0"; public static final String ATTR_REQUEST = "REQUEST"; diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index 0ad9e61e..1f3f2217 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -10,7 +10,7 @@ jboot.redis.host=127.0.0.1 jboot.rpc.autoExportEnable=false -jboot.limit.enable = true +jboot.limit.enable = false jboot.limit.rule = /*?a=b*c:tb:1,io.jboot.test.aop*.get*(*):tb:1 jboot.model.unscanPackage = * -- Gitee From 649cbb31321d5b51fd1c1ee17e76f43754c75ec6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 18 Sep 2020 16:09:37 +0800 Subject: [PATCH 0547/1965] v3.5.0 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0fdf90d0..6673dc5e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.5.0-SNAPSHOT + 3.5.0 jar jboot -- Gitee From f7cbb48b7fd62c29706ecf967626b06df012ee5a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 18 Sep 2020 17:04:40 +0800 Subject: [PATCH 0548/1965] v3.5.0 release (^.^)YYa!! --- doc/docs/attachment.md | 20 +------------------ .../java/io/jboot/aop/InterceptorBuilder.java | 2 +- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/doc/docs/attachment.md b/doc/docs/attachment.md index e08d848c..f3549da3 100644 --- a/doc/docs/attachment.md +++ b/doc/docs/attachment.md @@ -18,7 +18,7 @@ Jboot 定位是分布式的开发系统,在项目进行分布式部署的时 ``` AliyunOssAttachmentContainer aliyunOss = new AliyunOssAttachmentContainer(); -AttachmentManager.me().addContainer("containerName",aliyunOss); +AttachmentManager.me().addContainer(aliyunOss); ``` 当我们的 Controller 有文件上传的时候,我们需要调用 AttachmentManager 进行保存,AttachmentManager 最终会保持到其所有的容器里去。 @@ -153,24 +153,6 @@ public class AliyunOssAttachmenetContainer implements AttachmentContainer { } - @Override - public boolean isRemoteContainer() { - return true; - } - - - @Override - public boolean matchFile(String target, HttpServletRequest request) { - return false; - } - - - @Override - public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { - return false; - } - - /** * 同步本地文件到阿里云OSS * diff --git a/src/main/java/io/jboot/aop/InterceptorBuilder.java b/src/main/java/io/jboot/aop/InterceptorBuilder.java index ce34a7af..f79a5575 100644 --- a/src/main/java/io/jboot/aop/InterceptorBuilder.java +++ b/src/main/java/io/jboot/aop/InterceptorBuilder.java @@ -25,7 +25,7 @@ import java.lang.reflect.Method; *

      * 配置方法:
      * public void onInit() {
    - *     JbootAopFactory.me().addInterceptorBuilder(new MyInterceptorBuilder());
    + *     InterceptorBuilderManager.me().addInterceptorBuilder(new MyInterceptorBuilder());
      * }
      * 
    */ -- Gitee From 804d456b19032e324afcf0772e0bf745cd07c79a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 21 Sep 2020 15:25:59 +0800 Subject: [PATCH 0549/1965] update docs shell --- doc/deploy.sh | 2 +- doc/deploy_to_gitee.sh | 2 +- doc/package.json | 4 ++++ doc/start.txt | 4 ++++ 4 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 doc/start.txt diff --git a/doc/deploy.sh b/doc/deploy.sh index 6dac71e1..69b97ca0 100755 --- a/doc/deploy.sh +++ b/doc/deploy.sh @@ -4,7 +4,7 @@ set -e # build -vuepress build . +yarn build diff --git a/doc/deploy_to_gitee.sh b/doc/deploy_to_gitee.sh index cb65a35d..b7edd49c 100755 --- a/doc/deploy_to_gitee.sh +++ b/doc/deploy_to_gitee.sh @@ -4,7 +4,7 @@ set -e # build -vuepress build . +yarn build diff --git a/doc/package.json b/doc/package.json index 6bc3f8c2..0c3ee62b 100644 --- a/doc/package.json +++ b/doc/package.json @@ -6,5 +6,9 @@ "commander": "^2.20.3", "esm": "^3.2.25", "vuepress-theme-reco": "1.5.5" + }, + "scripts": { + "dev": "vuepress dev .", + "build": "vuepress build ." } } diff --git a/doc/start.txt b/doc/start.txt new file mode 100644 index 00000000..13c44cbf --- /dev/null +++ b/doc/start.txt @@ -0,0 +1,4 @@ +1、安装好 yarn +2、执行 yarn install 初始化 +3、执行 yarn dev 预览 +4、执行 yarn build 构建生成 html \ No newline at end of file -- Gitee From 7b6bc1e3107f0c373d3210aa825f0e7d82bd9936 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 21 Sep 2020 15:45:33 +0800 Subject: [PATCH 0550/1965] update docs --- doc/.vuepress/config.js | 2 +- doc/docs/aop.md | 42 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/doc/.vuepress/config.js b/doc/.vuepress/config.js index 52c0f10c..54370197 100644 --- a/doc/.vuepress/config.js +++ b/doc/.vuepress/config.js @@ -66,7 +66,7 @@ module.exports = { {title: 'WebSocket', path: '/docs/websocket'}, {title: 'Jwt', path: '/docs/jwt'}, {title: 'Swagger', path: '/docs/swagger'}, - {title: 'Aop', path: '/docs/aop'}, + {title: 'AOP', path: '/docs/aop'}, {title: '数据库', path: '/docs/db'}, {title: '缓存', path: '/docs/cache'}, {title: 'Redis', path: '/docs/redis'}, diff --git a/doc/docs/aop.md b/doc/docs/aop.md index 0ec52a65..47ba082b 100644 --- a/doc/docs/aop.md +++ b/doc/docs/aop.md @@ -320,11 +320,49 @@ public class JbootManager { } public static JbootManager create(){ - return new JbootManager() + return new JbootManager(); } private JbootManager(){ //do sth } } -``` \ No newline at end of file +``` + +## InterceptorBuilder + +InterceptorBuilder 能够根据当前 Controller、Service 的方法,来对当前方法应该使用哪些拦截器进行构建,在之前 JFinal 的体系里,给某个方法添加拦截器只能通过 @Before({MyInterceptor.class}) 或者 configInterceptor() 配置方法里添加全局拦截器。 + +这样带来一个不够优雅的地方是,如果我们想给某个方法 ”增强“,只能以添加全局拦截器的方式来做,这样所有方法都会被该拦截器拦截,然后再通过其判断进行 ”放行“ 。这样就带来了性能效率的下降,因为在我们整个系统中,有非常多的方法是没有必要走拦截器的。 + +有了 InterceptorBuilder 之后,我们可以通过 InterceptBuilder 给某个方法来构建其特定的拦截器,而不是全局拦截器,这样,极大的提高了性能,同时减少了不必要的调用堆栈。 + +如何使用 InterceptorBuilder 呢?如下代码: + +```java +public void onInit() { + InterceptorBuilderManager.me().addInterceptorBuilder(new MyInterceptorBuilder()); +} +``` + +或者 `CacheInterceptorBuilder` 添加主键 `@AutoLoad` + +> 注意:使用主键 `@Autoload` 之后就不要通过 `InterceptorBuilderManager` 来添加了,`@Autoload` 的作用就是在其启动的时候自动添加过去 + +比如 @Cacheable 的拦截器构建 CacheInterceptorBuilder 代码如下: + +```java +@AutoLoad //自动把当前的 CacheInterceptorBuilder 添加到 InterceptorBuilderManager 里去。 +public class CacheInterceptorBuilder implements InterceptorBuilder { + + @Override + public void build(Class serviceClass, Method method, Interceptors interceptors) { + + Cacheable cacheable = method.getAnnotation(Cacheable.class); + if (cacheable != null) { + interceptors.add(new CacheableInterceptor()); + } + } +} +``` +在 `build()` 方法中的 Interceptors,我们不仅仅可以通过 Interceptors 来添加拦截器,我们还可以通过其来删除拦截器、或者移动拦截器位置等等操作。 \ No newline at end of file -- Gitee From 5cdfa9b9967dac452187043acbde2f0e08257ad8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 21 Sep 2020 17:05:39 +0800 Subject: [PATCH 0551/1965] v3.5.1 snapshot --- doc/.vuepress/config.js | 1 + pom.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/.vuepress/config.js b/doc/.vuepress/config.js index 54370197..d2be8117 100644 --- a/doc/.vuepress/config.js +++ b/doc/.vuepress/config.js @@ -64,6 +64,7 @@ module.exports = { {title: 'JFinalConfig', path: '/docs/jfinalConfig'}, {title: 'MVC', path: '/docs/mvc'}, {title: 'WebSocket', path: '/docs/websocket'}, + {title: 'Json', path: '/docs/json'}, {title: 'Jwt', path: '/docs/jwt'}, {title: 'Swagger', path: '/docs/swagger'}, {title: 'AOP', path: '/docs/aop'}, diff --git a/pom.xml b/pom.xml index 6673dc5e..73700a3a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.5.0 + 3.5.1-SNAPSHOT jar jboot -- Gitee From d86ea0451731dbb7e3804b955d9cf1ac11812f4d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 21 Sep 2020 17:06:34 +0800 Subject: [PATCH 0552/1965] optimize JbootJson --- src/main/java/io/jboot/web/JbootJson.java | 27 ++++---- .../java/io/jboot/web/JbootJsonConfig.java | 63 +++++++++++++++++++ .../java/io/jboot/web/JbootWebConfig.java | 27 -------- 3 files changed, 74 insertions(+), 43 deletions(-) create mode 100644 src/main/java/io/jboot/web/JbootJsonConfig.java diff --git a/src/main/java/io/jboot/web/JbootJson.java b/src/main/java/io/jboot/web/JbootJson.java index 79bf47d2..de93f508 100644 --- a/src/main/java/io/jboot/web/JbootJson.java +++ b/src/main/java/io/jboot/web/JbootJson.java @@ -35,20 +35,17 @@ import java.util.Map; public class JbootJson extends JFinalJson { - protected boolean isCamelCaseJsonStyleEnable = Jboot.config(JbootWebConfig.class).isCamelCaseJsonStyleEnable(); - protected boolean camelCaseToLowerCaseAnyway = Jboot.config(JbootWebConfig.class).isCamelCaseToLowerCaseAnyway(); - protected String jsonTimestampPattern = Jboot.config(JbootWebConfig.class).getJsonTimestampPattern(); - - protected Map methodAndFieldsCache = new HashMap<>(); + private JbootJsonConfig config = Jboot.config(JbootJsonConfig.class); + protected static Map methodAndFieldsCache = new HashMap<>(); public JbootJson() { //跳过 null 值输出到浏览器,提高传输性能 - setSkipNullValueField(true); + setSkipNullValueField(config.isSkipNullValueField()); //默认设置为 CamelCase 的属性模式 - if (isCamelCaseJsonStyleEnable) { - setModelAndRecordFieldNameConverter((fieldName) -> StrKit.toCamelCase(fieldName, camelCaseToLowerCaseAnyway)); + if (config.isCamelCaseJsonStyleEnable()) { + setModelAndRecordFieldNameConverter((fieldName) -> StrKit.toCamelCase(fieldName, config.isCamelCaseToLowerCaseAnyway())); } @@ -60,8 +57,8 @@ public class JbootJson extends JFinalJson { } }); - if (StrUtil.isNotBlank(jsonTimestampPattern)) { - setTimestampPattern(jsonTimestampPattern); + if (StrUtil.isNotBlank(config.getTimestampPattern())) { + setTimestampPattern(config.getTimestampPattern()); } } @@ -87,8 +84,8 @@ public class JbootJson extends JFinalJson { if (fillMap != null && !fillMap.isEmpty()) { for (Map.Entry entry : fillMap.entrySet()) { String fieldName = entry.getKey(); - if (isCamelCaseJsonStyleEnable) { - fieldName = StrKit.toCamelCase(fieldName, camelCaseToLowerCaseAnyway); + if (config.isCamelCaseJsonStyleEnable()) { + fieldName = StrKit.toCamelCase(fieldName, config.isCamelCaseToLowerCaseAnyway()); } toMap.put(fieldName, entry.getValue()); } @@ -115,10 +112,8 @@ public class JbootJson extends JFinalJson { try { Object value = wrapper.methods.get(index++).invoke(bean); toMap.put(field, value); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); + } catch (Exception ex) { + ex.printStackTrace(); } } } diff --git a/src/main/java/io/jboot/web/JbootJsonConfig.java b/src/main/java/io/jboot/web/JbootJsonConfig.java new file mode 100644 index 00000000..fa325257 --- /dev/null +++ b/src/main/java/io/jboot/web/JbootJsonConfig.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

    + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web; + +import io.jboot.app.config.annotation.ConfigModel; + +/** + * @author Michael Yang 杨福海 (fuhai999@gmail.com) + * @version V1.0 + */ +@ConfigModel(prefix = "jboot.json") +public class JbootJsonConfig { + + private boolean camelCaseJsonStyleEnable = true; + private boolean camelCaseToLowerCaseAnyway = false; + private boolean skipNullValueField = true; + private String timestampPattern; + + public boolean isCamelCaseJsonStyleEnable() { + return camelCaseJsonStyleEnable; + } + + public void setCamelCaseJsonStyleEnable(boolean camelCaseJsonStyleEnable) { + this.camelCaseJsonStyleEnable = camelCaseJsonStyleEnable; + } + + public boolean isCamelCaseToLowerCaseAnyway() { + return camelCaseToLowerCaseAnyway; + } + + public void setCamelCaseToLowerCaseAnyway(boolean camelCaseToLowerCaseAnyway) { + this.camelCaseToLowerCaseAnyway = camelCaseToLowerCaseAnyway; + } + + public boolean isSkipNullValueField() { + return skipNullValueField; + } + + public void setSkipNullValueField(boolean skipNullValueField) { + this.skipNullValueField = skipNullValueField; + } + + public String getTimestampPattern() { + return timestampPattern; + } + + public void setTimestampPattern(String timestampPattern) { + this.timestampPattern = timestampPattern; + } +} diff --git a/src/main/java/io/jboot/web/JbootWebConfig.java b/src/main/java/io/jboot/web/JbootWebConfig.java index ee9c1eb5..ab1216ae 100644 --- a/src/main/java/io/jboot/web/JbootWebConfig.java +++ b/src/main/java/io/jboot/web/JbootWebConfig.java @@ -29,9 +29,6 @@ public class JbootWebConfig { private String cookieEncryptKey = DEFAULT_COOKIE_ENCRYPT_KEY; private String webSocketEndpoint; - private boolean camelCaseJsonStyleEnable = true; - private boolean camelCaseToLowerCaseAnyway = false; - private String jsonTimestampPattern; public String getCookieEncryptKey() { return cookieEncryptKey; @@ -48,28 +45,4 @@ public class JbootWebConfig { public void setWebSocketEndpoint(String webSocketEndpoint) { this.webSocketEndpoint = webSocketEndpoint; } - - public boolean isCamelCaseJsonStyleEnable() { - return camelCaseJsonStyleEnable; - } - - public void setCamelCaseJsonStyleEnable(boolean camelCaseJsonStyleEnable) { - this.camelCaseJsonStyleEnable = camelCaseJsonStyleEnable; - } - - public boolean isCamelCaseToLowerCaseAnyway() { - return camelCaseToLowerCaseAnyway; - } - - public void setCamelCaseToLowerCaseAnyway(boolean camelCaseToLowerCaseAnyway) { - this.camelCaseToLowerCaseAnyway = camelCaseToLowerCaseAnyway; - } - - public String getJsonTimestampPattern() { - return jsonTimestampPattern; - } - - public void setJsonTimestampPattern(String jsonTimestampPattern) { - this.jsonTimestampPattern = jsonTimestampPattern; - } } -- Gitee From 046729cb2d1f62364cd7e9ea8b0260c4e1e5a609 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 21 Sep 2020 17:06:44 +0800 Subject: [PATCH 0553/1965] add json config docs --- doc/docs/json.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 doc/docs/json.md diff --git a/doc/docs/json.md b/doc/docs/json.md new file mode 100644 index 00000000..4499a7e0 --- /dev/null +++ b/doc/docs/json.md @@ -0,0 +1,31 @@ +# Json 配置 + +## jboot v3.5.1(包括 3.5.1) 之后的配置 + +``` +#是否启用大写转换,比如 user_age 自动转为为 UserAge +jboot.json.camelCaseJsonStyleEnable = true + +#是否转换任何地方,比如 map.put("my_field") 默认输出为 myField +jboot.json.camelCaseToLowerCaseAnyway = false + +#是否跳过 null 值输出,map.put("key",null),则 key 值不输 +jboot.json.skipNullValueField = true + +#配置输出的时间格式 +jboot.json.jsonTimestampPattern +``` + +## jboot v3.5.1 之前的配置 + +``` +#是否启用大写转换,比如 user_age 自动转为为 UserAge +jboot.web.camelCaseJsonStyleEnable = true + +#是否转换任何地方,比如 map.put("my_field") 默认输出为 myField +jboot.web.camelCaseToLowerCaseAnyway = false + +#配置输出的时间格式 +jboot.web.jsonTimestampPattern +``` + -- Gitee From 3da56e5fbd4b94020125fff8afab1774d7302ec6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 21 Sep 2020 17:06:58 +0800 Subject: [PATCH 0554/1965] v3.5.1 snapshot --- src/main/java/io/jboot/web/session/JbootHttpSession.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/jboot/web/session/JbootHttpSession.java b/src/main/java/io/jboot/web/session/JbootHttpSession.java index cca82051..ea904fc4 100644 --- a/src/main/java/io/jboot/web/session/JbootHttpSession.java +++ b/src/main/java/io/jboot/web/session/JbootHttpSession.java @@ -85,6 +85,7 @@ public class JbootHttpSession implements HttpSession { return maxInactiveInterval; } + @Override @Deprecated public HttpSessionContext getSessionContext() { return null; -- Gitee From d25ae6809830c2b53562307f02010396b85b03b5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 22 Sep 2020 17:38:18 +0800 Subject: [PATCH 0555/1965] optimize JbootRedisCacheImpl.buildKey --- .../cache/redis/JbootRedisCacheImpl.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java index 19a8054a..1a6d3e9b 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java @@ -25,6 +25,7 @@ import io.jboot.exception.JbootIllegalConfigException; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.StringJoiner; public class JbootRedisCacheImpl extends JbootCacheBase { @@ -107,17 +108,18 @@ public class JbootRedisCacheImpl extends JbootCacheBase { private String buildKey(String cacheName, Object key) { - if (key instanceof Number) { - return String.format("%s:I:%s", cacheName, key); + StringBuilder keyBuilder = new StringBuilder(cacheName).append(":"); + if (key instanceof String) { + keyBuilder.append("S"); + } else if (key instanceof Number) { + keyBuilder.append("I"); + } else if (key == null) { + keyBuilder.append("S"); + key = "null"; } else { - Class keyClass = key.getClass(); - if (String.class.equals(keyClass) || - StringBuffer.class.equals(keyClass) || - StringBuilder.class.equals(keyClass)) { - return String.format("%s:S:%s", cacheName, key); - } + keyBuilder.append("O"); } - return String.format("%s:O:%s", cacheName, key); + return keyBuilder.append(":").append(key).toString(); } @Override -- Gitee From b33ced6086757eac8fd38048ef711fe35a5c9b88 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 22 Sep 2020 17:49:07 +0800 Subject: [PATCH 0556/1965] update docs --- doc/docs/json.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/docs/json.md b/doc/docs/json.md index 4499a7e0..9f45a1ea 100644 --- a/doc/docs/json.md +++ b/doc/docs/json.md @@ -29,3 +29,7 @@ jboot.web.camelCaseToLowerCaseAnyway = false jboot.web.jsonTimestampPattern ``` +## 通过 Java 代码配置 + +更多的配置可以通过 JFinalJsonKit 工具类来进行配置。 + -- Gitee From cc6a5c9f8ba42690eb79676bb7fa5edb9b3a0bfb Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 22 Sep 2020 18:05:26 +0800 Subject: [PATCH 0557/1965] optimize --- .../io/jboot/support/sentinel/ResourceMetadataRegistry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/support/sentinel/ResourceMetadataRegistry.java b/src/main/java/io/jboot/support/sentinel/ResourceMetadataRegistry.java index 6c7d6bcd..7452bafa 100644 --- a/src/main/java/io/jboot/support/sentinel/ResourceMetadataRegistry.java +++ b/src/main/java/io/jboot/support/sentinel/ResourceMetadataRegistry.java @@ -66,7 +66,7 @@ final class ResourceMetadataRegistry { } private static String getKey(Class clazz, String name) { - return String.format("%s:%s", clazz.getCanonicalName(), name); + return clazz.getCanonicalName() + ":" + name; } /** -- Gitee From 93359060e4e960a7771ac95b853478977581deee Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 22 Sep 2020 18:34:11 +0800 Subject: [PATCH 0558/1965] optimize --- .../java/io/jboot/aop/InterceptorBuilder.java | 2 + .../jboot/aop/InterceptorBuilderManager.java | 2 +- src/main/java/io/jboot/aop/Interceptors.java | 44 +++++++++++++++++-- .../web/attachment/AttachmentManager.java | 9 +++- 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/aop/InterceptorBuilder.java b/src/main/java/io/jboot/aop/InterceptorBuilder.java index f79a5575..d4ffc62d 100644 --- a/src/main/java/io/jboot/aop/InterceptorBuilder.java +++ b/src/main/java/io/jboot/aop/InterceptorBuilder.java @@ -27,6 +27,8 @@ import java.lang.reflect.Method; * public void onInit() { * InterceptorBuilderManager.me().addInterceptorBuilder(new MyInterceptorBuilder()); * } + * + * 或者给 MyInterceptorBuilder 类添加 @AutoLoad 注解 *

    */ public interface InterceptorBuilder { diff --git a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java index c17e8436..735b06b7 100644 --- a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java +++ b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java @@ -92,7 +92,7 @@ public class InterceptorBuilderManager extends AopFactory { for (InterceptorBuilder builder : interceptorBuilders) { builder.build(targetClass, method, interceptors); } - return interceptors.getInterceptorArray(); + return interceptors.toArray(); } return inters; diff --git a/src/main/java/io/jboot/aop/Interceptors.java b/src/main/java/io/jboot/aop/Interceptors.java index add4e9b1..a0384819 100644 --- a/src/main/java/io/jboot/aop/Interceptors.java +++ b/src/main/java/io/jboot/aop/Interceptors.java @@ -76,6 +76,7 @@ public class Interceptors { warppers.add(new InterceptorWarpper(interceptor, ++maximumWeight)); } + public void addBefore(Interceptor interceptor, Class interceptroClass) { int weight = ++currentWeight; for (InterceptorWarpper warpper : warppers) { @@ -89,6 +90,7 @@ public class Interceptors { } } + public void addAfter(Interceptor interceptor, Class interceptroClass) { int weight = ++currentWeight; for (InterceptorWarpper warpper : warppers) { @@ -108,15 +110,39 @@ public class Interceptors { warppers.removeIf(interceptorWarpper -> interceptorWarpper.interceptor == interceptor); } + public void removeByClass(Class clazz) { warppers.removeIf(interceptorWarpper -> interceptorWarpper.interceptor.getClass() == clazz); } - public List getInterceptors() { + + public Integer getWeight(Interceptor interceptor) { + for (InterceptorWarpper warpper : warppers) { + if (warpper.interceptor == interceptor) { + return warpper.weight; + } + } + return null; + } + + + public Integer getWeightByClass(Class clazz) { + for (InterceptorWarpper warpper : warppers) { + if (warpper.interceptor.getClass() == clazz) { + return warpper.weight; + } + } + return null; + } + + + public List toList() { + warppers.sort(Comparator.comparingInt(InterceptorWarpper::getWeight)); return warppers.stream().map(InterceptorWarpper::getInterceptor).collect(Collectors.toList()); } - public Interceptor[] getInterceptorArray() { + + public Interceptor[] toArray() { if (warppers == null || warppers.size() == 0) { return InterceptorManager.NULL_INTERS; } else { @@ -129,11 +155,23 @@ public class Interceptors { } } + public int getMinimalWeight() { + return minimalWeight; + } + + public int getMaximumWeight() { + return maximumWeight; + } + + public int getCurrentWeight() { + return currentWeight; + } + static class InterceptorWarpper { private Interceptor interceptor; - private int weight = 0; + private int weight; public InterceptorWarpper(Interceptor interceptor, int weight) { this.interceptor = interceptor; diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index c1699bfd..04606dd3 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -156,8 +156,13 @@ public class AttachmentManager { } - - + /** + * 渲染文件到浏览器 + * @param target + * @param request + * @param response + * @return true 渲染成功,false 不进行渲染 + */ public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { if (target.startsWith(defaultContainer.getTargetPrefix()) && target.lastIndexOf('.') != -1){ new FileRender(getFile(target)).setContext(request, response).render(); -- Gitee From 05dcd50b8fee7122a9ed9e7775ffa05f34a5f9fb Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 22 Sep 2020 19:05:34 +0800 Subject: [PATCH 0559/1965] v3.5.1 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e01c1cf3..e6fac5d9 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.5.0 + 3.5.1 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index a2543110..45cfcfc8 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.5.0 + 3.5.1 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index d4e3edac..473eb44d 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.5.0 + 3.5.1 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index d8e7f9ce..00854afb 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -49,7 +49,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.5.0 + 3.5.1 ``` @@ -107,7 +107,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.5.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.5.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index f3ca3dba..838e97c2 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.5.0"; + public static String VERSION = "3.5.1"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From c2f7bfa296883878c8a5e9be9aa30ad03061b17d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 22 Sep 2020 19:05:44 +0800 Subject: [PATCH 0560/1965] v3.5.1 release (^.^)YYa!! --- changes.txt | 7 +++++++ pom.xml | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/changes.txt b/changes.txt index 052cd817..4102f7b0 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.5.1: +优化:JbootRedisCacheImpl buildKey() 方法 +优化:JbootJson 并新增更多的配置 +优化:InterceptorBuilderManager 的方法名并添加更多可配置的方法 + + + jboot v3.5.0: 新增:InterceptorBuilder 组件,方便对 Controller 或者 AOP 对象的拦截器进行构建 优化:移除 FixedInterceptor 组件,其可以通过 InterceptorBuilder 进行替代 diff --git a/pom.xml b/pom.xml index 73700a3a..5e4a38b3 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.5.1-SNAPSHOT + 3.5.1 jar jboot @@ -60,7 +60,7 @@ 2.10.6 2.8.5 3.11 - 2.7 + 2.8.0 1.14 4.5.12 4.4.13 -- Gitee From 199e23d1da14ccd287b6bc2889db06ad92433cb6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 23 Sep 2020 13:31:00 +0800 Subject: [PATCH 0561/1965] v3.5.2 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5e4a38b3..1d0cdc7a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.5.1 + 3.5.2-SNAPSHOT jar jboot -- Gitee From 0ddbe2fe858f518a69e257e71ebe0acfed971bc1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 23 Sep 2020 13:32:59 +0800 Subject: [PATCH 0562/1965] optimize InterceptorBuilder and add test --- .../jboot/aop/InterceptorBuilderManager.java | 16 ++- src/main/java/io/jboot/aop/Interceptors.java | 124 ++++++++++++------ .../io/jboot/test/aop/InterceptorsTest.java | 46 +++++++ .../io/jboot/test/aop/Name1Interceptor.java | 24 ++++ .../io/jboot/test/aop/NameInterceptor.java | 28 ++++ 5 files changed, 197 insertions(+), 41 deletions(-) create mode 100644 src/test/java/io/jboot/test/aop/InterceptorsTest.java create mode 100644 src/test/java/io/jboot/test/aop/Name1Interceptor.java create mode 100644 src/test/java/io/jboot/test/aop/NameInterceptor.java diff --git a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java index 735b06b7..779fa1bc 100644 --- a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java +++ b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java @@ -15,7 +15,6 @@ */ package io.jboot.aop; -import com.jfinal.aop.AopFactory; import com.jfinal.aop.Interceptor; import io.jboot.aop.annotation.AutoLoad; import io.jboot.aop.cglib.JbootCglibProxyFactory; @@ -27,8 +26,9 @@ import java.lang.reflect.Method; import java.util.Collection; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Predicate; -public class InterceptorBuilderManager extends AopFactory { +public class InterceptorBuilderManager{ private static InterceptorBuilderManager me = new InterceptorBuilderManager(); @@ -73,7 +73,7 @@ public class InterceptorBuilderManager extends AopFactory { - public void addInterceptorBuilders(Collection interceptorBuilders) { + public void addInterceptorBuilder(Collection interceptorBuilders) { if (interceptorBuilders == null) { throw new NullPointerException("interceptorBuilder must not be null."); } @@ -85,6 +85,15 @@ public class InterceptorBuilderManager extends AopFactory { + public void removeInterceptorBuilder(Predicate filter){ + if (interceptorBuilders != null && !interceptorBuilders.isEmpty()){ + if (interceptorBuilders.removeIf(filter)) { + JbootCglibProxyFactory.IntersCache.clear(); + } + } + } + + public Interceptor[] build(Class targetClass, Method method, Interceptor[] inters) { if (interceptorBuilders != null && interceptorBuilders.size() > 0) { @@ -94,7 +103,6 @@ public class InterceptorBuilderManager extends AopFactory { } return interceptors.toArray(); } - return inters; } diff --git a/src/main/java/io/jboot/aop/Interceptors.java b/src/main/java/io/jboot/aop/Interceptors.java index a0384819..defd0f8c 100644 --- a/src/main/java/io/jboot/aop/Interceptors.java +++ b/src/main/java/io/jboot/aop/Interceptors.java @@ -20,7 +20,9 @@ import com.jfinal.aop.InterceptorManager; import java.util.ArrayList; import java.util.Comparator; +import java.util.Iterator; import java.util.List; +import java.util.function.Predicate; import java.util.stream.Collectors; /** @@ -31,7 +33,6 @@ public class Interceptors { private List warppers = new ArrayList<>(); private int minimalWeight = 1; - private int maximumWeight = 1; private int currentWeight = 1; public Interceptors() { @@ -48,71 +49,124 @@ public class Interceptors { public void add(Interceptor interceptor) { warppers.add(new InterceptorWarpper(interceptor, currentWeight++)); - - if (currentWeight > maximumWeight) { - maximumWeight = currentWeight; - } } - public void add(Interceptor interceptor, int weight) { warppers.add(new InterceptorWarpper(interceptor, weight)); - - if (weight >= maximumWeight) { - maximumWeight = weight + 1; - } - if (weight <= minimalWeight) { - minimalWeight = weight - 1; - } } - public void addToFirst(Interceptor interceptor) { warppers.add(new InterceptorWarpper(interceptor, --minimalWeight)); } + public boolean addBefore(Interceptor interceptor, Predicate filter) { + Integer weight = null; + for (InterceptorWarpper warpper : warppers) { + if (filter.test(warpper.interceptor)) { + weight = warpper.weight; + break; + } + } - public void addToLast(Interceptor interceptor) { - warppers.add(new InterceptorWarpper(interceptor, ++maximumWeight)); + //所有在 新加 的拦截器往前推 1 + if (weight != null) { + for (InterceptorWarpper warpper : warppers) { + if (warpper.weight < weight) { + warpper.weight--; + } + } + minimalWeight--; + warppers.add(new InterceptorWarpper(interceptor, --weight)); + return true; + }else { + return false; + } } - - public void addBefore(Interceptor interceptor, Class interceptroClass) { - int weight = ++currentWeight; + public boolean addBefore(Interceptor interceptor, Class interceptroClass) { + Integer weight = null; for (InterceptorWarpper warpper : warppers) { if (warpper.getInterceptor().getClass() == interceptroClass) { - weight = warpper.weight - 1; + weight = warpper.weight; + break; } } - warppers.add(new InterceptorWarpper(interceptor, weight)); - if (currentWeight > maximumWeight) { - maximumWeight = currentWeight; + + //所有在 新加 的拦截器往前推 1 + if (weight != null) { + for (InterceptorWarpper warpper : warppers) { + if (warpper.weight < weight) { + warpper.weight--; + } + } + minimalWeight--; + warppers.add(new InterceptorWarpper(interceptor, --weight)); + return true; + }else { + return false; } } + public boolean addAfter(Interceptor interceptor, Predicate filter) { + Integer weight = null; + for (InterceptorWarpper warpper : warppers) { + if (filter.test(warpper.interceptor)) { + weight = warpper.weight; + break; + } + } + + //所有在 新加 的拦截器往后推 1 + if (weight != null) { + for (InterceptorWarpper warpper : warppers) { + if (warpper.weight > weight) { + warpper.weight++; + } + } + currentWeight++; + warppers.add(new InterceptorWarpper(interceptor, ++weight)); + return true; + }else { + return false; + } + } - public void addAfter(Interceptor interceptor, Class interceptroClass) { - int weight = ++currentWeight; + + public boolean addAfter(Interceptor interceptor, Class interceptroClass) { + Integer weight = null; for (InterceptorWarpper warpper : warppers) { if (warpper.getInterceptor().getClass() == interceptroClass) { - weight = warpper.weight + 1; + weight = warpper.weight; + break; } } - warppers.add(new InterceptorWarpper(interceptor, weight)); - if (currentWeight > maximumWeight) { - maximumWeight = currentWeight; + if (weight != null) { + for (InterceptorWarpper warpper : warppers) { + if (warpper.weight > weight) { + warpper.weight++; + } + } + currentWeight++; + warppers.add(new InterceptorWarpper(interceptor, ++weight)); + return true; + }else { + return false; } } - public void remove(Interceptor interceptor) { - warppers.removeIf(interceptorWarpper -> interceptorWarpper.interceptor == interceptor); + public boolean remove(Interceptor interceptor) { + return warppers.removeIf(interceptorWarpper -> interceptorWarpper.interceptor == interceptor); } - public void removeByClass(Class clazz) { - warppers.removeIf(interceptorWarpper -> interceptorWarpper.interceptor.getClass() == clazz); + public boolean remove(Predicate predicate) { + return warppers.removeIf(warpper -> predicate.test(warpper.interceptor)); + } + + public boolean removeByClass(Class clazz) { + return warppers.removeIf(interceptorWarpper -> interceptorWarpper.interceptor.getClass() == clazz); } @@ -159,10 +213,6 @@ public class Interceptors { return minimalWeight; } - public int getMaximumWeight() { - return maximumWeight; - } - public int getCurrentWeight() { return currentWeight; } diff --git a/src/test/java/io/jboot/test/aop/InterceptorsTest.java b/src/test/java/io/jboot/test/aop/InterceptorsTest.java new file mode 100644 index 00000000..0e6b0649 --- /dev/null +++ b/src/test/java/io/jboot/test/aop/InterceptorsTest.java @@ -0,0 +1,46 @@ +package io.jboot.test.aop; + +import com.jfinal.aop.Interceptor; +import io.jboot.aop.Interceptors; +import io.jboot.test.HelloInterceptor; + +import java.util.Arrays; +import java.util.function.Predicate; + +public class InterceptorsTest { + + public static void main(String[] args) { + + Interceptors interceptors = new Interceptors(); + + interceptors.add(new NameInterceptor("name1")); + interceptors.add(new NameInterceptor("name2")); + interceptors.add(new NameInterceptor("name3")); + interceptors.addToFirst(new NameInterceptor("name0")); + interceptors.addToFirst(new NameInterceptor("name-1")); + interceptors.add(new Name1Interceptor("Name1Interceptor")); + interceptors.add(new NameInterceptor("name5")); + interceptors.add(new NameInterceptor("name6")); + interceptors.addBefore(new NameInterceptor("name1Before"),Name1Interceptor.class); + System.out.println(interceptors.addAfter(new NameInterceptor("name1Afater"),Name1Interceptor.class)); + System.out.println(interceptors.addAfter(new NameInterceptor("HelloInterceptorAfater"), HelloInterceptor.class)); + interceptors.addAfter(new NameInterceptor("name1.5"), interceptor -> { + if (interceptor instanceof NameInterceptor){ + NameInterceptor i = (NameInterceptor) interceptor; + return i.getName().equals("name1"); + } + return false; + }); + + + interceptors.addBefore(new NameInterceptor("name2.5"), interceptor -> { + if (interceptor instanceof NameInterceptor){ + NameInterceptor i = (NameInterceptor) interceptor; + return i.getName().equals("name3"); + } + return false; + }); + + System.out.println(Arrays.toString(interceptors.toArray())); + } +} diff --git a/src/test/java/io/jboot/test/aop/Name1Interceptor.java b/src/test/java/io/jboot/test/aop/Name1Interceptor.java new file mode 100644 index 00000000..f4d6f2fa --- /dev/null +++ b/src/test/java/io/jboot/test/aop/Name1Interceptor.java @@ -0,0 +1,24 @@ +package io.jboot.test.aop; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; + +public class Name1Interceptor implements Interceptor { + private String name; + + public Name1Interceptor(String name) { + this.name = name; + } + + @Override + public void intercept(Invocation inv) { + + } + + @Override + public String toString() { + return "NameInterceptor{" + + "name='" + name + '\'' + + '}'; + } +} diff --git a/src/test/java/io/jboot/test/aop/NameInterceptor.java b/src/test/java/io/jboot/test/aop/NameInterceptor.java new file mode 100644 index 00000000..319aa1db --- /dev/null +++ b/src/test/java/io/jboot/test/aop/NameInterceptor.java @@ -0,0 +1,28 @@ +package io.jboot.test.aop; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; + +public class NameInterceptor implements Interceptor { + private String name; + + public NameInterceptor(String name) { + this.name = name; + } + + @Override + public void intercept(Invocation inv) { + + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return "NameInterceptor{" + + "name='" + name + '\'' + + '}'; + } +} -- Gitee From 907ba9347faf550961e36e337392105577ab9d23 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 23 Sep 2020 13:44:34 +0800 Subject: [PATCH 0563/1965] optimize ClassUtil --- src/main/java/io/jboot/utils/ClassUtil.java | 43 +++++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassUtil.java b/src/main/java/io/jboot/utils/ClassUtil.java index c9e53d0d..d46eca96 100644 --- a/src/main/java/io/jboot/utils/ClassUtil.java +++ b/src/main/java/io/jboot/utils/ClassUtil.java @@ -30,7 +30,7 @@ import java.util.concurrent.ConcurrentHashMap; */ public class ClassUtil { - public static Log log = Log.getLog(ClassUtil.class); + private static Log LOG = Log.getLog(ClassUtil.class); private static final Map singletons = new ConcurrentHashMap<>(); @@ -42,23 +42,33 @@ public class ClassUtil { * @return */ public static T singleton(Class clazz) { - Object object = singletons.get(clazz); - if (object == null) { + return singleton(clazz,true); + } + + + /** + * 获取单利 + * @param clazz + * @param createByAop + * @param + * @return + */ + public static T singleton(Class clazz, boolean createByAop) { + Object ret = singletons.get(clazz); + if (ret == null) { synchronized (clazz) { - object = singletons.get(clazz); - if (object == null) { - object = newInstance(clazz); - if (object != null) { - singletons.put(clazz, object); + ret = singletons.get(clazz); + if (ret == null) { + ret = newInstance(clazz, createByAop); + if (ret != null) { + singletons.put(clazz, ret); } else { - Log.getLog(clazz).error("cannot new newInstance!!!!"); + LOG.error("cannot new newInstance!!!!"); } - } } } - - return (T) object; + return (T) ret; } /** @@ -103,7 +113,7 @@ public class ClassUtil { constructor.setAccessible(true); return (T) constructor.newInstance(); } catch (Exception e) { - log.error("can not newInstance class:" + clazz + "\n" + e.toString(), e); + LOG.error("can not newInstance class:" + clazz + "\n" + e.toString(), e); } return null; @@ -129,7 +139,7 @@ public class ClassUtil { } return (T) ret; } catch (Exception e) { - log.error("can not newInstance class:" + clazz + "\n" + e.toString(), e); + LOG.error("can not newInstance class:" + clazz + "\n" + e.toString(), e); } return null; @@ -158,7 +168,7 @@ public class ClassUtil { return (T) method.invoke(null, null); } catch (Exception e) { - log.error("can not invoke method:" + method.getName() + LOG.error("can not invoke method:" + method.getName() + " in class : " + clazz + "\n" + e.toString(), e); @@ -225,7 +235,7 @@ public class ClassUtil { Class clazz = (Class) Class.forName(clazzName, false, classLoader); return newInstance(clazz, createByAop); } catch (Exception e) { - log.error("can not newInstance class:" + clazzName + "\n" + e.toString(), e); + LOG.error("can not newInstance class:" + clazzName + "\n" + e.toString(), e); } return null; @@ -270,7 +280,6 @@ public class ClassUtil { } - public static String buildMethodString(Method method) { StringBuilder sb = new StringBuilder() -- Gitee From 3557b26aa98c0ff177d482c8df455022e744ce8b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 23 Sep 2020 13:54:06 +0800 Subject: [PATCH 0564/1965] change defautl cache type from ehcache to caffeine --- pom.xml | 13 +++++++------ .../io/jboot/components/cache/JbootCacheConfig.java | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 1d0cdc7a..32769533 100644 --- a/pom.xml +++ b/pom.xml @@ -218,12 +218,6 @@ ${jsoup.version} - - net.sf.ehcache - ehcache - ${ehcache.version} - - com.github.ben-manes.caffeine caffeine @@ -243,6 +237,13 @@ + + net.sf.ehcache + ehcache + ${ehcache.version} + provided + + com.alibaba druid diff --git a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java index 7efcf2a8..8ae09cae 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java @@ -32,7 +32,7 @@ public class JbootCacheConfig { public static final String TYPE_NONE = "none"; - private String type = TYPE_EHCACHE; + private String type = TYPE_CAFFEINE; // AOP 缓存的默认有效时间,0为永久有效,单位秒, // 当 @Cacheable 和 @CachePut 注解不配置的时候默认用这个配置 -- Gitee From f184200db604bff4007293d37027fb256a3b53ee Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 23 Sep 2020 14:03:47 +0800 Subject: [PATCH 0565/1965] change defautl cache type from ehcache to caffeine --- .../jboot/components/cache/caffeine/CaffeineCacheObject.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheObject.java b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheObject.java index 7125649f..909ac837 100644 --- a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheObject.java +++ b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheObject.java @@ -53,10 +53,7 @@ public class CaffeineCacheObject implements Serializable { return -1; } - long timeMillis = cachetime - DateUtils.addSeconds(new Date(), -liveSeconds) - .getTime(); - - System.out.println("timeMillis:" + timeMillis); + long timeMillis = cachetime - DateUtils.addSeconds(new Date(), -liveSeconds).getTime(); if (timeMillis > 0) { return (int) (timeMillis / 1000); -- Gitee From f88006cd6bd2b45ec9d7b0715416139974d7ce0f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 23 Sep 2020 16:54:22 +0800 Subject: [PATCH 0566/1965] update docs --- doc/.vuepress/config.js | 12 ++++++ doc/jbootadmin/attachment.md | 3 ++ doc/jbootadmin/cdn.md | 3 ++ doc/jbootadmin/config.md | 3 ++ doc/jbootadmin/deploy.md | 3 ++ doc/jbootadmin/feature.md | 79 ++++++++++++++++++++++++++---------- doc/jbootadmin/gateway.md | 3 ++ doc/jbootadmin/readme.md | 28 +++++++++++-- doc/jbootadmin/server.md | 3 ++ 9 files changed, 112 insertions(+), 25 deletions(-) create mode 100644 doc/jbootadmin/attachment.md create mode 100644 doc/jbootadmin/cdn.md create mode 100644 doc/jbootadmin/config.md create mode 100644 doc/jbootadmin/deploy.md create mode 100644 doc/jbootadmin/gateway.md create mode 100644 doc/jbootadmin/server.md diff --git a/doc/.vuepress/config.js b/doc/.vuepress/config.js index d2be8117..f0e00727 100644 --- a/doc/.vuepress/config.js +++ b/doc/.vuepress/config.js @@ -122,6 +122,18 @@ module.exports = { {title: '安全防护', path: '/jbootadmin/safety_precautions'}, ], }, + { + title: '运维和部署', + collapsable: false, + children: [ + {title: '部署', path: '/jbootadmin/deploy'}, + {title: 'CDN配置', path: '/jbootadmin/cdn'}, + {title: '文件同步', path: '/jbootadmin/attachment'}, + {title: '配置中心', path: '/jbootadmin/config'}, + {title: '门户网关', path: '/jbootadmin/gateway'}, + {title: '服务器管理', path: '/jbootadmin/server'}, + ], + }, ] }, sidebarDepth: 1 diff --git a/doc/jbootadmin/attachment.md b/doc/jbootadmin/attachment.md new file mode 100644 index 00000000..7ef3f73a --- /dev/null +++ b/doc/jbootadmin/attachment.md @@ -0,0 +1,3 @@ +# 快速开始 + +购买后获得阅读权限。 \ No newline at end of file diff --git a/doc/jbootadmin/cdn.md b/doc/jbootadmin/cdn.md new file mode 100644 index 00000000..7ef3f73a --- /dev/null +++ b/doc/jbootadmin/cdn.md @@ -0,0 +1,3 @@ +# 快速开始 + +购买后获得阅读权限。 \ No newline at end of file diff --git a/doc/jbootadmin/config.md b/doc/jbootadmin/config.md new file mode 100644 index 00000000..7ef3f73a --- /dev/null +++ b/doc/jbootadmin/config.md @@ -0,0 +1,3 @@ +# 快速开始 + +购买后获得阅读权限。 \ No newline at end of file diff --git a/doc/jbootadmin/deploy.md b/doc/jbootadmin/deploy.md new file mode 100644 index 00000000..7ef3f73a --- /dev/null +++ b/doc/jbootadmin/deploy.md @@ -0,0 +1,3 @@ +# 快速开始 + +购买后获得阅读权限。 \ No newline at end of file diff --git a/doc/jbootadmin/feature.md b/doc/jbootadmin/feature.md index 93243a77..a0d80e5d 100644 --- a/doc/jbootadmin/feature.md +++ b/doc/jbootadmin/feature.md @@ -1,32 +1,67 @@ # JbootAdmin 功能介绍 - -- 1、服务器管理(方便我们查看分布式服务器的健康情况) - -- 2、分布式缓存管理(方便我们对分布式服务进行管理和同步) - -- 3、配置中心进行管理,RPC服务管理 - -- 4、门户网关 Gateway 路由管理 - -- 5、CDN管理,通过配置可以对网站静态资源进行加速 - -- 6、在线执行服务方法 - -- 7、精确到方法级别的性能监控 - -- 8、插件化热修复 - -方便在线紧急情况下,在线对 Bug 进行修复。或者对逻辑进行在线修改。 - -- 9、分布式文件同步 +**基础功能模块** +- 账户管理 + - 账户管理 + - 部门管理 + - 职位管理 +- 角色管理 + - 角色管理 + - 权限分配 + - 权限自动化维护 +- 日志管理 + - 登录日志 + - 操作日志 +- 消息管理 + - 我的消息 + - 发布消息 + - 消息类型 +- 微信管理 + - 微信账户配置(多账户支持) + - 微信菜单 + - 默认回复 + - 自动回复 + +**开发模块** + +- 代码生成管理 + - 项目管理 + - 在线代码生成 + - `main()` 方法代码生成 +- 权限自动维护构建 +- 菜单自动维护构建 + + +**运维模块** + + - 服务器管理(查看分布式服务器健康情况) + - 分布式缓存管理 + - 分布式配置中心 + - 门户网格配置 + - CDN 配置 + - 性能监控 + - 插件化热修复 + - 分布式文件同步 + - 自动扩缩绒 + - 异地多活 + - 蓝绿发布部署 + - 插件化 + - 在线热修复 + + +**备注** + +- 1、运维模块有部分功能正在完善中 + + +- 2、分布式文件同步 当你把你的应用部署在多个机器的时候,JbootAdmin 自带了分布式的文件同步功能,用户上传文件或者进行文件删除时,可以通过 JbootAdmin 进行配置让所有需要的机器(或者机器的指定目录)进行文件同步。 -- 10、自动扩缩容 +- 3、自动扩缩容 基于阿里云服务器 ECS、或者腾讯云服务器 CVS、或者 K8S 进行自动扩缩容管理配置,方便当我们的系统在高并发的时候,自动去帮我们开通机器、自动帮我们进行部署以应对高并发的流量。同时在流量减少的时候,又会自动帮我们去删除服务器而减少资金的浪费。 -- 11、异地多活 +- 4、异地多活 方便我们把我们的程序部署在多个不同的机房里,就算因为运营商的问题导致机房出现问题,而不会影响的产品的正常服务。 \ No newline at end of file diff --git a/doc/jbootadmin/gateway.md b/doc/jbootadmin/gateway.md new file mode 100644 index 00000000..7ef3f73a --- /dev/null +++ b/doc/jbootadmin/gateway.md @@ -0,0 +1,3 @@ +# 快速开始 + +购买后获得阅读权限。 \ No newline at end of file diff --git a/doc/jbootadmin/readme.md b/doc/jbootadmin/readme.md index 95bd0fa1..a1ec7f92 100644 --- a/doc/jbootadmin/readme.md +++ b/doc/jbootadmin/readme.md @@ -1,7 +1,29 @@ # 关于 JbootAdmin -JbootAdmin 是 Jboot 作者海哥基于 Jboot 开发的一个功能强大的快速开发框架,JbootAdmin 是一个真正的微服务框架平台, -但是你却不会为此付出更多的开发时间和开发成本,它开发起来就跟开发单体项目是一模一样的,而且你还可以打包成为war、fatjar、undertow、微服务等不同的部署项目。 +JbootAdmin 是 Jboot 作者海哥基于 Jboot 开发的一个功能强大、简单易用的快速开发框架,是一个真正的微服务框架平台。在各种 ***Admin 开源盛行的今天,我们为什么还要推出 JbootAdmin 呢? 主要是有以下几点原因: +**第一:JbootAdmin 提供长久的商业技术支持** -不过,JbootAdmin 是商业付费的,关于更多的 JbootAdmin,你可以通过 [功能介绍](./feature) 来进一步了解。 \ No newline at end of file +虽然市场上有各种 Admin,但是几乎都是新手用来练手的,或者只是炫技产品,而 JbootAdmin 是真正的商业级开发框架,我们提供了前段到后端的完整开发框架,长久的商业技术支持。因此,您不用担心购买后遇到 BUG 没人维护,遇到问题无人解答的情况。 + +**第二:JbootAdmin 更像一套标准和规范** + +Jboot 已经开源超过 4 年的时间,非常稳定。JbootAdmin 又建立在 Jboot 之上,其稳定性是不言而喻的,更重要的是,JbootAdmin 更像是一套标准和规范,您的项目建立在这套规范之上,会大大的减轻团队协作带来的各种问题。 + +**第三:同一套源码不同的部署形式** + +JbootAdmin 功能非常强大,同一套源码可以编译成 jar包、war包、微服务 等不同的方式,不需要修改任何代码的情况下,支持单机部署和分布式部署,不用再担心分布式情况下带来的问题。这样,我们在用户量少的时候,可以一个 jar 一个命令进行启动,当用户量大的时候随时可以切换到分部署部署 或者 微服务部署。 + +**第四:入门非常简单** + +JbootAdmin 入门非常简单,几乎任何有 1 年以上开发经验的同学都能迅速上手。JbootAdmin 是一个真正的微服务框架平台,但是你却不会为此付出更多的开发时间和开发成本,它开发起来就跟开发单体项目是一模一样的。同时,我们也在加快对 JbootAdmin 文档的完善。 + +**第五:商业的产品带来商业的服务** + +JbootAdmin 定位在商业级开发,购买商业版本的毕竟是少数,因此我们也有足够的精力来对商业客户的维护。所以,商业是相对的,商业的产品自然带来商业的服务。 + + +**更多关于 JbootAdmin** + +- 1) [功能介绍](./feature) +- 2) [我要购买](./buy) diff --git a/doc/jbootadmin/server.md b/doc/jbootadmin/server.md new file mode 100644 index 00000000..7ef3f73a --- /dev/null +++ b/doc/jbootadmin/server.md @@ -0,0 +1,3 @@ +# 快速开始 + +购买后获得阅读权限。 \ No newline at end of file -- Gitee From f5c7b45f86897eb8cf1de2ad211a08646ee1bbeb Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 23 Sep 2020 17:47:46 +0800 Subject: [PATCH 0567/1965] optimize --- src/main/java/io/jboot/app/ApplicationUtil.java | 4 ++-- src/main/java/io/jboot/db/datasource/DataSourceBuilder.java | 2 +- src/main/java/io/jboot/utils/ClassScanner.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/app/ApplicationUtil.java b/src/main/java/io/jboot/app/ApplicationUtil.java index 4edbde9f..090e6f4e 100644 --- a/src/main/java/io/jboot/app/ApplicationUtil.java +++ b/src/main/java/io/jboot/app/ApplicationUtil.java @@ -44,9 +44,9 @@ class ApplicationUtil { try { URL resourceURL = ApplicationUtil.class.getResource("/"); if (resourceURL != null) { - System.out.println("Classpath : " + resourceURL.toURI().getPath()); + System.out.println("JbootApplication ClassPath: " + resourceURL.toURI().getPath()); } else { - System.out.println("Classpath : application in one jar."); + System.out.println("JbootApplication ClassPath in fat jar."); } } catch (URISyntaxException e) { e.printStackTrace(); diff --git a/src/main/java/io/jboot/db/datasource/DataSourceBuilder.java b/src/main/java/io/jboot/db/datasource/DataSourceBuilder.java index 2f9d4a52..49fddfd1 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceBuilder.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceBuilder.java @@ -74,7 +74,7 @@ public class DataSourceBuilder { default: DataSourceFactory dataSourceFactory = JbootSpiLoader.load(DataSourceFactory.class, factory); if (dataSourceFactory == null) { - throw new NullPointerException("can not load DataSourceFactory spi for name : " + factory); + throw new NullPointerException("can not load DataSourceFactory spi for name: " + factory); } return dataSourceFactory.createDataSource(dsc); } diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 013caedc..4104e280 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -473,7 +473,7 @@ public class ClassScanner { } if (JbootConfigManager.me().isDevMode()) { - System.out.println("ClassScanner scan classpath : " + classPath); + System.out.println("Jboot Scan ClassPath: " + classPath); } addClassesFromClassPath(classPath); @@ -493,7 +493,7 @@ public class ClassScanner { } if (JbootConfigManager.me().isDevMode()) { - System.out.println("ClassScanner scan jar : " + jarPath); + System.out.println("Jboot Scan Jar: " + jarPath); } addClassesFromJar(jarPath); -- Gitee From 2a68b16d4619a6a24e19dbe7c5a5b41a0da72505 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 23 Sep 2020 18:06:48 +0800 Subject: [PATCH 0568/1965] optimize --- src/main/java/io/jboot/db/SqlDebugger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/SqlDebugger.java b/src/main/java/io/jboot/db/SqlDebugger.java index 102980df..7b91d2c2 100644 --- a/src/main/java/io/jboot/db/SqlDebugger.java +++ b/src/main/java/io/jboot/db/SqlDebugger.java @@ -38,7 +38,7 @@ public class SqlDebugger { @Override public void print(String sql) { - System.out.println("\r\njboot exec sql >>> " + sql); + System.out.println("Jboot exec sql >>> " + sql); } }; -- Gitee From 7fd648b89344b1fb1be93683a0ef704c159f8578 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 24 Sep 2020 12:46:34 +0800 Subject: [PATCH 0569/1965] update docs --- doc/jbootadmin/db.md | 512 +++++++++++++++++++++++++++++++++++++++- doc/jbootadmin/start.md | 228 +++++++++++++++++- 2 files changed, 738 insertions(+), 2 deletions(-) diff --git a/doc/jbootadmin/db.md b/doc/jbootadmin/db.md index 4164a9c1..13c6de98 100644 --- a/doc/jbootadmin/db.md +++ b/doc/jbootadmin/db.md @@ -1,3 +1,513 @@ # JbootAdmin 数据库设计 -购买后获得阅读权限。 \ No newline at end of file +JbootAdmin 目前的表结构如下: + + +**账户信息表** 用来存储每个账户的基本信息、账号密码 +```sql +CREATE TABLE `account` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `tenant_id` bigint(20) unsigned DEFAULT NULL COMMENT '租户用户ID,租户ID是自己的时候,自己是租户的管理员', + `superior_tenant_id` bigint(20) unsigned DEFAULT NULL COMMENT '上级租户ID', + `loginname` varchar(128) DEFAULT NULL COMMENT '登录名', + `nickname` varchar(128) DEFAULT NULL COMMENT '昵称', + `password` varchar(128) DEFAULT NULL COMMENT '密码', + `salt` varchar(32) DEFAULT NULL COMMENT '盐', + `email` varchar(64) DEFAULT NULL COMMENT '邮件', + `mobile` varchar(32) DEFAULT NULL COMMENT '手机电话', + `domain` varchar(32) DEFAULT NULL COMMENT 'SaaS系统给用户分配的域名', + `avatar` varchar(256) DEFAULT NULL COMMENT '账户头像', + `type` tinyint(2) DEFAULT NULL COMMENT '账户类型', + `status` tinyint(2) DEFAULT NULL COMMENT '状态', + `created` datetime DEFAULT NULL COMMENT '创建日期', + `activated` datetime DEFAULT NULL COMMENT '激活时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `loginname` (`loginname`) USING BTREE, + UNIQUE KEY `email` (`email`) USING BTREE, + UNIQUE KEY `mobile` (`mobile`) USING BTREE, + KEY `created` (`created`) USING BTREE, + KEY `tenant_id` (`tenant_id`) USING BTREE, + KEY `superior_tenant_id` (`superior_tenant_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='账号信息表,保存用户账号信息。'; +``` + +**账户地址** 用户的收货地址表 +```sql +CREATE TABLE `account_address` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `account_id` bigint(20) unsigned NOT NULL COMMENT '账户 id', + `username` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '姓名', + `mobile` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '手机号', + `province` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '省', + `city` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '市', + `district` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '区(县)', + `detail` varchar(256) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '详细地址到门牌号', + `zipcode` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '邮政编码', + `default_enable` tinyint(1) DEFAULT '0' COMMENT '是否默认,1是,0否', + `options` text COLLATE utf8mb4_unicode_ci COMMENT '扩展字段', + `modified` datetime DEFAULT NULL COMMENT '修改时间', + `created` datetime DEFAULT NULL COMMENT '新增时间', + PRIMARY KEY (`id`) USING BTREE, + KEY `account_id` (`account_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='收货地址'; +``` + +**用户属性表** 用来保持用户的扩展属性 +```sql +CREATE TABLE `account_attr` ( + `account_id` bigint(20) unsigned NOT NULL COMMENT '账户ID', + `name` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '属性名称', + `content` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '属性内容', + PRIMARY KEY (`account_id`,`name`) USING BTREE, + KEY `content` (`content`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='账户属性表'; +``` + +**账户部门关系表** 账户和部门的 多对多 映射关系 +```sql +CREATE TABLE `account_dept` ( + `account_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '账户ID', + `dept_id` bigint(20) unsigned NOT NULL COMMENT '部门ID', + PRIMARY KEY (`dept_id`,`account_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='部门和账户的多对多关系表'; +``` + +**账户第三方关系表** +```sql +CREATE TABLE `account_openid` ( + `account_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '用户ID', + `type` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '第三方类型:wechat,dingding,qq...', + `openid` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '第三方的openId的值', + `access_token` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '可能用不到', + `expired_time` datetime DEFAULT NULL COMMENT 'access_token的过期时间', + `nickname` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '昵称', + `avatar` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '头像', + `options` text COLLATE utf8mb4_unicode_ci COMMENT '其他扩展属性', + `created` datetime DEFAULT NULL COMMENT '创建日期', + `modified` datetime DEFAULT NULL COMMENT '修改日期', + PRIMARY KEY (`type`,`account_id`) USING BTREE, + KEY `type_openid` (`type`,`openid`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='账号第三方账号绑定表'; +``` + +```sql +CREATE TABLE `account_option` ( + `account_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '账户ID', + `name` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '扩展属性名称', + `content` text COLLATE utf8mb4_unicode_ci COMMENT '扩展属性内容', + PRIMARY KEY (`account_id`,`name`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='账户配置'; +``` + +```sql +CREATE TABLE `account_permission` ( + `account_id` bigint(20) unsigned NOT NULL COMMENT '账户ID', + `permission_id` varchar(128) NOT NULL DEFAULT '' COMMENT '权限ID', + `own` tinyint(1) DEFAULT NULL COMMENT '1 拥有, 0 排除', + PRIMARY KEY (`account_id`,`permission_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='账户和权限的多对多映射表,这个很少用到,只有在极特殊情况下,可以通过这个对某些权限进行添加或者排除'; +``` + +```sql +CREATE TABLE `account_receive_msg` ( + `account_id` bigint(20) unsigned DEFAULT NULL COMMENT '账户id', + `send_msg_type` tinyint(2) unsigned DEFAULT NULL COMMENT '可接收消息类型', + KEY `account_id` (`account_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='通知功能'; +``` + +```sql +CREATE TABLE `account_role` ( + `account_id` bigint(20) unsigned NOT NULL COMMENT '账户ID', + `role_id` bigint(20) unsigned NOT NULL COMMENT '权限ID', + PRIMARY KEY (`account_id`,`role_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='账户和角色的多对多映射表'; +``` + +```sql +CREATE TABLE `account_scope` ( + `account_id` bigint(20) unsigned NOT NULL COMMENT '账户ID', + `scope_account_id` bigint(20) unsigned NOT NULL COMMENT '可以操作其他账户数据的账户ID', + PRIMARY KEY (`account_id`,`scope_account_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='账户的操作范围(只可以操作哪些用户的数据)'; +``` + +```sql +CREATE TABLE `account_session` ( + `id` varchar(32) NOT NULL DEFAULT '' COMMENT 'session id', + `client` varchar(32) NOT NULL DEFAULT '' COMMENT '客户端', + `platform` varchar(32) DEFAULT NULL COMMENT '登录平台', + `account_id` bigint(20) unsigned NOT NULL COMMENT '账户id', + `expire_at` datetime NOT NULL COMMENT '到期时间', + `options` text COMMENT '扩展属性', + `created` datetime DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='账户登录session'; +``` + +```sql +CREATE TABLE `account_station` ( + `account_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '账户ID', + `station_id` bigint(20) unsigned NOT NULL COMMENT '岗位ID', + PRIMARY KEY (`station_id`,`account_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='岗位和账户的多对多关系'; +``` + +```sql +CREATE TABLE `dev_project` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `name` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '项目名称', + `module_name` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '模块名称', + `old_module_name` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '旧模块名称,用于在模块名称更新的时候,需要记录之前的模块名称', + `package_name` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '模块报名', + `description` text COLLATE utf8mb4_unicode_ci COMMENT '模块描述', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='项目表,对应的是 idea(或者eclipse)的代码模块'; +``` + +```sql +CREATE TABLE `dev_tablefield` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', + `name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '字段名称', + `table_name` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '字段所在的表名', + `title` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '字段的标题', + `render_type` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '渲染类型', + `valid_required` tinyint(1) DEFAULT NULL COMMENT '是否必填', + `valid_mobile` tinyint(1) DEFAULT NULL COMMENT '是否是手机', + `valid_email` tinyint(1) DEFAULT NULL COMMENT '是否是邮件', + `show_in_list` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否在列表里显示', + `show_in_edit` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否在编辑页面编辑', + `search_type` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '搜索类型 like、相等', + PRIMARY KEY (`id`) USING BTREE, + KEY `table_name` (`table_name`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='表字段配置'; +``` + +```sql +CREATE TABLE `dev_tableinfo` ( + `name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '表名', + `project_id` int(11) DEFAULT NULL COMMENT '所在项目', + `controller_mapping` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Controller 的 url 映射', + `title` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '标题', + `alias` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '别名', + `help_text` text COLLATE utf8mb4_unicode_ci COMMENT '帮助内容', + `crumbs_text` text COLLATE utf8mb4_unicode_ci COMMENT '面包屑内容', + `description` text COLLATE utf8mb4_unicode_ci COMMENT '描述或备注', + `created` datetime DEFAULT NULL COMMENT '创建时间', + `modified` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`name`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='表生成内容配置'; +``` + +```sql +CREATE TABLE `log_action` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `account_id` bigint(20) unsigned DEFAULT NULL COMMENT '用户ID', + `tenant_id` bigint(20) unsigned DEFAULT NULL, + `action` varchar(512) DEFAULT NULL COMMENT '访问路径', + `name` varchar(128) DEFAULT NULL, + `query` varchar(512) DEFAULT NULL COMMENT '访问参数', + `ip` varchar(64) DEFAULT NULL COMMENT 'IP', + `ua` varchar(1024) DEFAULT NULL COMMENT '浏览器', + `created` datetime DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE, + KEY `account_id` (`account_id`) USING BTREE, + KEY `tenant_id` (`tenant_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='用户行为日志'; +``` + +```sql +CREATE TABLE `log_login` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `account_id` bigint(20) unsigned NOT NULL COMMENT '登录账户', + `tenant_id` bigint(20) unsigned DEFAULT NULL, + `client` varchar(100) DEFAULT NULL COMMENT '登录客户端', + `ip` varchar(100) DEFAULT NULL COMMENT '登录的I IP 地址', + `ua` varchar(512) DEFAULT NULL COMMENT '登录的浏览器 ua', + `platform` varchar(32) DEFAULT NULL COMMENT '登录平台', + `created` datetime NOT NULL COMMENT '登录时间', + PRIMARY KEY (`id`) USING BTREE, + KEY `account_id` (`account_id`) USING BTREE, + KEY `tenant_id` (`tenant_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='用户登录日志'; +``` + +```sql +CREATE TABLE `sys_cnarea` ( + `id` mediumint(7) unsigned NOT NULL AUTO_INCREMENT, + `level` tinyint(2) unsigned NOT NULL COMMENT '层级', + `parent_code` bigint(14) unsigned NOT NULL DEFAULT '0' COMMENT '父级行政代码', + `area_code` bigint(14) unsigned NOT NULL DEFAULT '0' COMMENT '行政代码', + `zip_code` mediumint(6) unsigned zerofill NOT NULL DEFAULT '000000' COMMENT '邮政编码', + `city_code` char(6) NOT NULL DEFAULT '' COMMENT '区号', + `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名称', + `short_name` varchar(50) NOT NULL DEFAULT '' COMMENT '简称', + `merger_name` varchar(50) NOT NULL DEFAULT '' COMMENT '组合名', + `pinyin` varchar(30) NOT NULL DEFAULT '' COMMENT '拼音', + `lng` decimal(10,6) NOT NULL DEFAULT '0.000000' COMMENT '经度', + `lat` decimal(10,6) NOT NULL DEFAULT '0.000000' COMMENT '纬度', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `uk_code` (`area_code`) USING BTREE, + KEY `idx_parent_code` (`parent_code`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='中国行政地区表'; +``` + +```sql +CREATE TABLE `sys_dept` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '部门ID', + `type` tinyint(2) DEFAULT NULL COMMENT '部门类型', + `pid` bigint(20) unsigned DEFAULT NULL COMMENT '上级部门id', + `code` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '部门编码', + `name` varchar(256) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '部门名称', + `description` text COLLATE utf8mb4_unicode_ci COMMENT '部门描述', + `sort_no` int(11) DEFAULT NULL COMMENT '排序编号', + `tenant_id` bigint(20) unsigned DEFAULT NULL COMMENT '租户ID', + PRIMARY KEY (`id`) USING BTREE, + KEY `tenant_id` (`tenant_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='部门表'; +``` + +```sql +CREATE TABLE `sys_dept_account` ( + `dept_id` bigint(20) unsigned NOT NULL COMMENT '部门ID', + `account_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '账户ID', + PRIMARY KEY (`dept_id`,`account_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='部门和账户的多对多关系表'; +``` + +```sql +CREATE TABLE `sys_menu` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `menu_id` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '菜单对应html的id属性的值', + `menu_pid` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '通过 PID 来设置上下级关系', + `text` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '菜单内容', + `icon` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '菜单ICON', + `url` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '菜单URL地址', + `target` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '菜单的打开类型', + `type` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '菜单类型', + `platform` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '菜单所在的平台,一个系统下可能存在多个平台', + `sort_no` int(11) DEFAULT NULL COMMENT '排序值', + `options` text COLLATE utf8mb4_unicode_ci COMMENT '其他扩展字段', + PRIMARY KEY (`id`) USING BTREE, + KEY `platform` (`platform`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='系统菜单'; +``` + +```sql +CREATE TABLE `sys_message` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `title` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '消息标题', + `level` tinyint(2) NOT NULL COMMENT '内容级别(1普通 2一般 3紧急 4弹窗)', + `type` tinyint(2) DEFAULT NULL COMMENT '内容类型(1系统 2业务 3公告 4其它)', + `content` text COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '消息内容', + `receive_type` tinyint(2) NOT NULL COMMENT '接受者类型(0全部 1用户 2部门 3角色 4岗位)', + `send_account_id` bigint(20) unsigned DEFAULT NULL COMMENT '发送者用户编码', + `send_account_name` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发送者用户姓名', + `send_date` datetime DEFAULT NULL COMMENT '发送时间', + `notify_type` tinyint(2) DEFAULT NULL COMMENT '通知类型(PC APP 短信 邮件 微信)多选', + `status` tinyint(2) NOT NULL COMMENT '状态(0正常 1删除 4审核 5驳回 9草稿)', + `reply_status` tinyint(2) DEFAULT NULL COMMENT '是否支持回复等', + `reply_id` bigint(20) unsigned DEFAULT NULL COMMENT '回复的消息ID', + `created_by` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '创建者', + `created` datetime NOT NULL COMMENT '创建时间', + `modified_by` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '更新者', + `modified` datetime NOT NULL COMMENT '更新时间', + `remarks` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注信息', + PRIMARY KEY (`id`) USING BTREE, + KEY `send_account_id` (`send_account_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='系统消息'; +``` + +```sql +CREATE TABLE `sys_message_send_record` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `message_id` bigint(20) unsigned NOT NULL COMMENT '所属消息', + `send_account_id` bigint(20) unsigned DEFAULT NULL COMMENT '发送消息用户', + `receive_account_id` bigint(20) unsigned DEFAULT NULL COMMENT '接受者用户', + `read_status` tinyint(2) NOT NULL COMMENT '读取状态(0未送达 1已读 2未读)', + `read_date` datetime DEFAULT NULL COMMENT '阅读时间', + `reply_status` tinyint(2) DEFAULT NULL COMMENT '回复状态', + `reply_date` datetime DEFAULT NULL COMMENT '回复时间', + `is_star` tinyint(1) DEFAULT NULL COMMENT '是否标星', + PRIMARY KEY (`id`) USING BTREE, + KEY `send_account_id` (`send_account_id`) USING BTREE, + KEY `receive_account_id` (`receive_account_id`) USING BTREE, + KEY `read_status` (`read_status`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='消息发送记录表'; +``` + +```sql +CREATE TABLE `sys_option` ( + `name` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '配置key', + `content` text COLLATE utf8mb4_unicode_ci COMMENT '配置内容', + PRIMARY KEY (`name`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='系统配置表'; +``` + +```sql +CREATE TABLE `sys_permission` ( + `id` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '权限ID', + `group_id` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '所属的权限组ID', + `name` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '权限名称', + `description` varchar(256) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '权限描述', + `type` tinyint(2) DEFAULT NULL COMMENT '权限类型,1 菜单,2 Action,3 逻辑权限, 4 敏感数据权限, 5 页面元素,6 其他类型', + `platform` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `sort_no` int(11) DEFAULT NULL COMMENT '权限排序,只用于展示', + `created` datetime DEFAULT NULL COMMENT '创建修改', + `modified` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + KEY `group_id` (`group_id`) USING BTREE, + KEY `platform` (`platform`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='权限表'; +``` + +```sql +CREATE TABLE `sys_permission_group` ( + `id` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '权限组ID', + `name` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '权限组名称', + `description` text COLLATE utf8mb4_unicode_ci COMMENT '权限组描述', + `sort_no` int(11) DEFAULT NULL COMMENT '权限组排序', + `platform` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '权限组所在的平台', + `type` tinyint(2) DEFAULT NULL COMMENT '权限组类型,1 菜单,2 Action,3 逻辑权限, 4 敏感数据权限, 5 页面元素,6 其他类型', + PRIMARY KEY (`id`) USING BTREE, + KEY `platform` (`platform`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='对权限进行分组,只用于显示的作用,不起逻辑控制作用'; +``` + +```sql +CREATE TABLE `sys_role` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '角色ID', + `pid` bigint(20) unsigned DEFAULT NULL COMMENT '父级 id', + `tenant_id` bigint(20) unsigned DEFAULT NULL COMMENT '住户 ID', + `name` varchar(128) NOT NULL DEFAULT '' COMMENT '角色名称', + `desc` text COMMENT '角色的描述', + `flag` varchar(64) DEFAULT '' COMMENT '角色标识,全局唯一,sa 为超级管理员', + `sort_no` int(11) DEFAULT NULL COMMENT '排序编码', + `created` datetime NOT NULL COMMENT '创建时间', + `modified` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + KEY `tenant_id` (`tenant_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='角色表'; +``` + +```sql +CREATE TABLE `sys_role_permission` ( + `role_id` bigint(20) unsigned NOT NULL COMMENT '角色ID', + `permission_id` varchar(128) NOT NULL DEFAULT '' COMMENT '权限ID', + PRIMARY KEY (`role_id`,`permission_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='角色和权限的多对多映射表'; +``` + +```sql +CREATE TABLE `sys_station` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '岗位名称', + `type` tinyint(2) DEFAULT NULL COMMENT '岗位分类(高管、中层、基层)', + `sort_no` int(10) DEFAULT NULL COMMENT '岗位排序(升序)', + `status` tinyint(2) DEFAULT '0' COMMENT '状态(0正常 1删除 2停用)', + `create_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者', + `created` datetime DEFAULT NULL COMMENT '创建时间', + `modifiy_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者', + `modified` datetime DEFAULT NULL COMMENT '更新时间', + `remarks` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注信息', + `tenant_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '租户ID', + PRIMARY KEY (`id`) USING BTREE, + KEY `tenant_id` (`tenant_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='岗位表'; +``` + +```sql +CREATE TABLE `wechat_account` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `name` varchar(256) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '公众号名称', + `type` tinyint(4) DEFAULT NULL COMMENT '公众号类型', + `tenant_id` bigint(20) unsigned DEFAULT NULL COMMENT '租户ID', + `app_id` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '公众号的 APP ID', + `app_secret` varchar(256) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '公众号的 APP Secret', + `app_token` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '公众号配置的 token', + `encoding_aes_key` varchar(256) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Aes 加密 key', + `message_encrypt` tinyint(1) DEFAULT NULL COMMENT '是否进行加密', + `description` text COLLATE utf8mb4_unicode_ci COMMENT '公众号描述', + `sort_no` int(11) DEFAULT NULL COMMENT '排序编号', + `status` tinyint(2) DEFAULT NULL COMMENT '状态', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `app_id` (`app_id`) USING BTREE, + KEY `tenant_id` (`tenant_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='微信公众号表'; +``` + +```sql +CREATE TABLE `wechat_account_keyword` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `wechat_account_id` bigint(20) unsigned DEFAULT NULL COMMENT '归属公众号', + `wechat_account_appid` varchar(64) DEFAULT NULL, + `keyword` varchar(128) DEFAULT NULL COMMENT '关键字', + `reply_content_type` tinyint(2) DEFAULT NULL COMMENT '回复类型', + `reply_content` text COMMENT '回复内容,保存内容是一个 json', + `options` text, + `status` tinyint(2) DEFAULT NULL COMMENT '是否启用', + `created` datetime DEFAULT NULL COMMENT '创建时间', + `modified` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + KEY `ak` (`wechat_account_appid`,`keyword`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='用户自定义关键字回复表'; +``` + +```sql +CREATE TABLE `wechat_account_menu` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键 ID', + `pid` bigint(20) unsigned DEFAULT NULL COMMENT '父级ID', + `wechat_account_id` bigint(20) unsigned DEFAULT NULL COMMENT '归属公众号', + `text` varchar(512) DEFAULT NULL COMMENT '文本内容', + `keyword` varchar(128) DEFAULT NULL COMMENT '关键字', + `type` varchar(32) DEFAULT '' COMMENT '菜单类型', + `sort_no` int(11) DEFAULT '0' COMMENT '排序字段', + `created` datetime DEFAULT NULL COMMENT '创建时间', + `modified` datetime DEFAULT NULL COMMENT '修改时间', + `status` tinyint(2) DEFAULT NULL COMMENT '状态', + PRIMARY KEY (`id`) USING BTREE, + KEY `wechat_account_id` (`wechat_account_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='微信公众号菜单表'; +``` + +```sql +CREATE TABLE `wechat_account_msg` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `wechat_account_id` bigint(20) unsigned DEFAULT NULL COMMENT '微信公众号ID', + `wechat_account_appid` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '微信公众号的 AppID', + `user_open_id` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '微信消息发送用户', + `user_nickname` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发送消息的用户昵称', + `user_avatar` varchar(256) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发送消息的用户头像', + `content_type` tinyint(2) DEFAULT NULL COMMENT '消息类型', + `content` text COLLATE utf8mb4_unicode_ci COMMENT '消息内容', + `created` datetime DEFAULT NULL COMMENT '消息的接收时间', + `is_replied` tinyint(1) DEFAULT NULL COMMENT '是否已经回复', + `reply_date` datetime DEFAULT NULL COMMENT '回复时间', + PRIMARY KEY (`id`) USING BTREE, + KEY `wechat_account_id` (`wechat_account_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='微信公众号消息表'; +``` + +```sql +CREATE TABLE `wechat_account_msg_reply` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `wechat_account_id` bigint(20) unsigned DEFAULT NULL COMMENT '回复的微信公众号ID', + `reply_msg_id` bigint(20) unsigned DEFAULT NULL COMMENT '回复的消息ID', + `reply_to_open_id` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '回复的用户ID', + `reply_content_type` tinyint(2) DEFAULT NULL COMMENT '回复的消息类型', + `reply_content` text COLLATE utf8mb4_unicode_ci COMMENT '回复内容', + `created` datetime DEFAULT NULL COMMENT '回复时间', + PRIMARY KEY (`id`) USING BTREE, + KEY `wechat_account_id` (`wechat_account_id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='微信公众号消息回复表'; +``` + +```sql +CREATE TABLE `wechat_account_option` ( + `wechat_account_appid` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `name` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '微信公众号配置key', + `content` text COLLATE utf8mb4_unicode_ci COMMENT '微信公众号配置Neri', + PRIMARY KEY (`wechat_account_appid`,`name`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='微信公众号配置'; + +``` \ No newline at end of file diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 7ef3f73a..0d338a83 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -1,3 +1,229 @@ # 快速开始 -购买后获得阅读权限。 \ No newline at end of file +购买 JbootAdmin 拿到源码后,需要进入如下步骤: + +- 1)创建数据库 +- 2)将源码导入 idea 或者 eclipse 等开发工具 +- 3)编译 JbootAdmin +- 4)运行 JbootAdmin +- 5)通过 JbootAdmin 创建自己的业务模块 + +## 创建数据库 + +JbootAdmin 源码内置了一个叫 JbootAdmin.sql 的脚本,创建好数据库后,将该脚本导入即可。但需要注意的是,数据库一定要选择编码 utf8mb4 。 + +## 将源码导入开发工具 + +代码目录结构如下 + +``` +. +├── pom.xml +├── core-codegen (代码生成器) +│   ├── pom.xml +│   ├── src +│   +├── core-commons +│   ├── pom.xml +│   ├── readme.md +│   ├── src +│   +├── core-devops (DevOps相关) +│   ├── core-devops-model +│   ├── core-devops-provider +│   ├── core-devops-service +│   ├── core-devops-web-admin +│   ├── core-devops-web-api +│   ├── pom.xml +│   └── readme.md +│   +├── core-framework (JbootAdmin Framework 核心代码) +│   ├── core-framework-model +│   ├── core-framework-provider +│   ├── core-framework-service +│   ├── core-framework-web +│   ├── pom.xml +│   └── readme.md +│   +├── core-web (JbootAdmin Web Controller 相关代码) +│   ├── pom.xml +│   ├── readme.md +│   ├── src +│   +├── core-wechat (微信相关代码) +│   ├── core-wechat-model +│   ├── core-wechat-provider +│   ├── core-wechat-service +│   ├── core-wechat-web +│   └── pom.xml +│   +├── module-cms ( CMS 模块示例) +│   ├── module-cms-commons +│   ├── module-cms-model +│   ├── module-cms-provider +│   ├── module-cms-service +│   ├── module-cms-web +│   └── pom.xml +│   +├── starter-cms (CMS 启动模块) +│   ├── jboot.bat +│   ├── jboot.sh +│   ├── package.xml +│   ├── pom.xml +│   ├── src +│   +├── starter-tomcat (Tomcat War 包的打包示例) +│   ├── pom.xml +│   ├── src +│   +``` + +## 编译 JbootAdmin + +编译 JbootAdmin 的主要目的,是为了把 Framework 模块的资源文件,包括 html、js 等全部复制打包到"自己" 的模块里来,我们假设 `module-cms` 是自己的模块,那么打包的目的就是为了在 `starter-cms` 里生成可以执行的程序。 + +我们需要在 JbootAdmin 的根目录执行如下的 Maven 命令进行编译: + +``` +mvn clean package +``` + +命令执行的过程中,Maven 可能需要去下载相关依赖的 jar 包,命令执行完毕后,会在 `starter-cms` 的 `target` 目录下生成 `starter-cms-1.0.0.zip` 文件,复制该文件到任何地方解压缩,通过其内置的 jboot.sh 脚本,执行 `./jboot.sh start` 即可启动项目。 + + +## 运行 JbootAdmin + +编译完成 JbootAdmin 后,我们以下的 Java 类,然后执行其 `main()` 方法即可。 + +``` +starter-cms/src/main/io.jboot.cms.admin.CmsStarter +``` + +控制台输入如下内容,我们通过浏览器访问 `http://0.0.0.0:8003` 既可以显示系统登录信息,登录账号和密码请联系 海哥 获取。(注意:Windows 系统可能需要访问 `http://127.0.0.1:8003` ) +``` + + ____ ____ ___ ___ ______ + | || \ / \ / \ | | + |__ || o )| || || | + __| || || O || O ||_| |_| +/ | || O || || | | | +\ ` || || || | | | + \____||_____| \___/ \___/ |__| + + +JbootApplication { name='jboot', mode='dev', version='3.5.1', config='io.jboot.core.JbootCoreConfig' } +Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ +Starting JFinal 4.9.01 -> http://0.0.0.0:8003 +Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 +ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes +ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-model/target/classes +ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-codegen/target/classes +ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-web/target/classes +ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-web/target/classes +ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-provider/target/classes +ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes +ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes +ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.5.1/jboot-3.5.1.jar +ClassScanner scan jar : /Users/michael/.m2/repository/eu/bitwalker/UserAgentUtils/1.21/UserAgentUtils-1.21.jar +ClassScanner scan jar : /Users/michael/.m2/repository/cn/smallbun/screw/screw-core/1.0.0/screw-core-1.0.0.jar + +Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) + +JbootResourceLoader started, Watched resource path name : webapp +``` + +## 创建自己的业务模块 + +在 JbootAdmin 中,创建自己的业务模块有 2 中方式: + +- 1)在后台创建项目,然后对项目进行表配置 +- 2)在 `core-codegen` 模块下,创建自己的代码生成器,代码生成器带有可以执行的 `main()` 方法 + +我们建议使用第 2 种方式,当每次表结构发生变化的时候,我们只需要执行以下 `main()` 方法就可以了,第一种方式还需要登录到 JbootAdmin 后台去执行相关操作。 + +例如:CMS 代码生成器的 代码如下: + +```java +public class CmsModuleCodeGenerator { + + public static void main(String[] args) { + + String dbTables = "module_cms_article," + + "module_cms_article_category," + + "module_cms_article_comment" ; + + String optionsTables = ""; + String layerTables = ""; + String layerOptionsTables = ""; + + JbootApplication.setBootArg("jboot.datasource.url", "jdbc:mysql://127.0.0.1:3306/jbootadmin"); + JbootApplication.setBootArg("jboot.datasource.user", "root"); + JbootApplication.setBootArg("jboot.datasource.password", "123456"); + + String modelPackage = "io.jboot.cms.model"; + String baseModelPackage = modelPackage + ".base"; + String moduleName = "module-cms"; + + String modelDir = PathKit.getWebRootPath() + "/../"+moduleName+"/"+moduleName+"-model/src/main/java/" + modelPackage.replace(".", "/"); + String baseModelDir = PathKit.getWebRootPath() + "/../"+moduleName+"/"+moduleName+"-model/src/main/java/" + baseModelPackage.replace(".", "/"); + + System.out.println("start generate...dir:" + modelDir); + + Set genTableNames = StrUtil.splitToSet(dbTables, ","); + MetaBuilder metaBuilder = CodeGenHelpler.createMetaBuilder(); + metaBuilder.setGenerateRemarks(true); + List tableMetas = metaBuilder.build(); + tableMetas.removeIf(tableMeta -> genTableNames != null && !genTableNames.contains(tableMeta.name.toLowerCase())); + + + new BaseModelGenerator(baseModelPackage, baseModelDir).generate(tableMetas); + new ModelGenerator(modelPackage, baseModelPackage, modelDir).generate(tableMetas); + + + String servicePackage = "io.jboot.cms.service"; + String providerPackage = "io.jboot.cms.provider"; + String servicePath = PathKit.getWebRootPath() + "/../"+moduleName+"/"+moduleName+"-service/src/main/java/" + servicePackage.replace(".", "/"); + String providerPath = PathKit.getWebRootPath() + "/../"+moduleName+"/"+moduleName+"-provider/src/main/java/" + providerPackage.replace(".", "/"); + + + new ServiceGenerator(servicePackage, modelPackage, servicePath).generate(tableMetas); + new ProviderGenerator(providerPackage,servicePackage, modelPackage, providerPath).generate(tableMetas); + + //optionsTables + Set optionsTableNames = StrUtil.splitToSet(optionsTables, ","); + if (optionsTableNames != null && optionsTableNames.size() > 0) { + new BaseOptionsModelGenerator(baseModelPackage, baseModelDir).generate(copyTableMetasByNames(tableMetas,optionsTableNames)); + } + + //layerTables + Set layerTableNames = StrUtil.splitToSet(layerTables, ","); + if (layerTableNames != null && layerTableNames.size() > 0) { + new BaseLayerModelGenerator(baseModelPackage, baseModelDir).generate(copyTableMetasByNames(tableMetas,layerTableNames)); + } + + //layerOptionsTables + Set layerOptionsTableNames = StrUtil.splitToSet(layerOptionsTables, ","); + if (layerOptionsTableNames != null && layerOptionsTableNames.size() > 0) { + new BaseLayerOptionsModelGenerator(baseModelPackage, baseModelDir).generate(copyTableMetasByNames(tableMetas,layerOptionsTableNames)); + } + } + + + private static List copyTableMetasByNames(List tableMetas,Set names){ + List retList = new ArrayList<>(); + tableMetas.forEach(tableMeta -> { + if (names.contains(tableMeta.name.toLowerCase())){ + retList.add(tableMeta); + } + }); + return retList; + } + +} + +``` + +其他 Module 需要复制一份这个代码,然后修改掉 表明、包名、模块名称即可。 + + -- Gitee From 80b11c71546274b0adca53fa2918e230e495258e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 24 Sep 2020 17:44:49 +0800 Subject: [PATCH 0570/1965] optimize --- .../io/jboot/web/render/JbootErrorRender.java | 2 +- .../io/jboot/web/render/JbootHtmlRender.java | 2 +- .../web/render/JbootJavascriptRender.java | 2 +- .../java/io/jboot/web/render/JbootRender.java | 40 +++++++++++++++---- .../web/render/JbootResponseEntityRender.java | 2 +- .../web/render/JbootReturnValueRender.java | 2 +- .../io/jboot/web/render/JbootTextRender.java | 2 +- .../io/jboot/web/render/JbootXmlRender.java | 2 +- .../WechatApiConfigInterceptor.java | 2 +- .../interceptor/WechatUserInterceptor.java | 2 +- 10 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/jboot/web/render/JbootErrorRender.java b/src/main/java/io/jboot/web/render/JbootErrorRender.java index 988225a9..3db12dca 100644 --- a/src/main/java/io/jboot/web/render/JbootErrorRender.java +++ b/src/main/java/io/jboot/web/render/JbootErrorRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

    * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootHtmlRender.java b/src/main/java/io/jboot/web/render/JbootHtmlRender.java index d2804d2a..a5004dde 100644 --- a/src/main/java/io/jboot/web/render/JbootHtmlRender.java +++ b/src/main/java/io/jboot/web/render/JbootHtmlRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

    * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootJavascriptRender.java b/src/main/java/io/jboot/web/render/JbootJavascriptRender.java index cd4d5302..a841b350 100644 --- a/src/main/java/io/jboot/web/render/JbootJavascriptRender.java +++ b/src/main/java/io/jboot/web/render/JbootJavascriptRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

    * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootRender.java b/src/main/java/io/jboot/web/render/JbootRender.java index cf8d9e18..620349a9 100644 --- a/src/main/java/io/jboot/web/render/JbootRender.java +++ b/src/main/java/io/jboot/web/render/JbootRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

    * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,20 +16,22 @@ package io.jboot.web.render; import com.jfinal.render.Render; +import com.jfinal.render.RenderException; import com.jfinal.render.RenderManager; import com.jfinal.template.Engine; import io.jboot.Jboot; +import java.io.IOException; +import java.io.OutputStream; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; public class JbootRender extends Render { - private static Engine engine; - private static final String contentType = "text/html; charset=" + getEncoding(); + private static JbootWebCdnConfig cdnConfig = Jboot.config(JbootWebCdnConfig.class); private Engine getEngine() { if (engine == null) { @@ -38,11 +40,8 @@ public class JbootRender extends Render { return engine; } - private JbootWebCdnConfig config; - public JbootRender(String view) { this.view = view; - this.config = Jboot.config(JbootWebCdnConfig.class); } public String getContentType() { @@ -59,10 +58,35 @@ public class JbootRender extends Render { data.put(attrName, request.getAttribute(attrName)); } + if (cdnConfig.isEnable()){ + renderWithCdnConfig(data); + }else { + renderNormal(data); + } + } + + private void renderWithCdnConfig(Map data){ String html = getEngine().getTemplate(view).renderToString(data); - html = config.isEnable() ? RenderHelpler.processCDN(html, config.getDomain()) : html; + RenderHelpler.renderHtml(response, RenderHelpler.processCDN(html, cdnConfig.getDomain()) , contentType); + } - RenderHelpler.renderHtml(response, html, contentType); + private void renderNormal(Map data){ + try { + OutputStream os = response.getOutputStream(); + engine.getTemplate(view).render(data, os); + os.flush(); + } catch (RuntimeException e) { // 捕获 ByteWriter.close() 抛出的 RuntimeException + Throwable cause = e.getCause(); + if (cause instanceof IOException) { // ClientAbortException、EofException 直接或间接继承自 IOException + String name = cause.getClass().getSimpleName(); + if ("ClientAbortException".equals(name) || "EofException".equals(name)) { + return ; + } + } + throw e; + } catch (IOException e) { + throw new RenderException(e); + } } diff --git a/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java b/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java index 69efd942..1fc3e797 100644 --- a/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java +++ b/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

    * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index 1152e102..9c5fe016 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

    * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootTextRender.java b/src/main/java/io/jboot/web/render/JbootTextRender.java index 8a31d9ab..f23dc405 100644 --- a/src/main/java/io/jboot/web/render/JbootTextRender.java +++ b/src/main/java/io/jboot/web/render/JbootTextRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

    * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootXmlRender.java b/src/main/java/io/jboot/web/render/JbootXmlRender.java index 699a44f5..1820284d 100644 --- a/src/main/java/io/jboot/web/render/JbootXmlRender.java +++ b/src/main/java/io/jboot/web/render/JbootXmlRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

    * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/wechat/interceptor/WechatApiConfigInterceptor.java b/src/main/java/io/jboot/wechat/interceptor/WechatApiConfigInterceptor.java index 4dcc4976..6b816e2a 100644 --- a/src/main/java/io/jboot/wechat/interceptor/WechatApiConfigInterceptor.java +++ b/src/main/java/io/jboot/wechat/interceptor/WechatApiConfigInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

    * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/wechat/interceptor/WechatUserInterceptor.java b/src/main/java/io/jboot/wechat/interceptor/WechatUserInterceptor.java index 0d122d4b..53dc4fbd 100644 --- a/src/main/java/io/jboot/wechat/interceptor/WechatUserInterceptor.java +++ b/src/main/java/io/jboot/wechat/interceptor/WechatUserInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2016, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). *

    * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. -- Gitee From 2be38b882faec9d024001cbde4ac480a2e3994ea Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 24 Sep 2020 17:45:06 +0800 Subject: [PATCH 0571/1965] update docs --- doc/jbootadmin/menu.md | 55 +++++++++++++++++++++++++++++++++++- doc/jbootadmin/start.md | 62 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/doc/jbootadmin/menu.md b/doc/jbootadmin/menu.md index 086f891f..ac953a27 100644 --- a/doc/jbootadmin/menu.md +++ b/doc/jbootadmin/menu.md @@ -1,3 +1,56 @@ # 后台菜单 -购买后获得阅读权限。 \ No newline at end of file +JbootAdmin 的菜单,不需要进行手动维护,不需要在数据库进行新增、删除或者修改等操作,JbootAdmin 全部是是自动维护的,我们只需要专注于编码即可。 + +JbootAdmin 的菜单定义有两种方式 + +- 1)通过实现 MenuBuilder 来定义菜单 +- 2)通过注解 @MenuDef 来定义菜单 + + +## 通过实现 MenuBuilder 来定义菜单 + +```java +public class AdminMenus implements MenuBuilder { + + public static final String ACCOUNT = "account"; + public static final String WECHAT = "wechat"; + + static List frameworkMenus = new ArrayList<>(); + static { + frameworkMenus.add(new MenuBean(ACCOUNT, "账户管理", "fa-user", MenuTypes.SYSTEM, 111)); + frameworkMenus.add(new MenuBean(WECHAT, "微信平台", "fab fa-weixin", MenuTypes.SYSTEM, 222)); + } + + @Override + public List buildMenus() { + return frameworkMenus; + } +} +``` +- 1) 编写任意类名,实现 `MenuBuilder` 接口 +- 2) 复写 `buildMenus()` 方法,返回 `List`,每个 MenuBean 可以理解为一个菜单,但当这个菜单没有 pid (父级ID)的时候,可以理解其为一个 “菜单组”,给子菜单使用的。 + + +所以,以上的代码,是定义两个菜单组,这个菜单组的 ID 分别是 account 和 wechat 。 + +## 通过注解 @MenuDef 来定义菜单 + +```java +@RequestMapping("/wechat") +public class WechatController extends BaseAdminController { + + @MenuDef(text = "微信账户", pid = "wechat", sortNo = 1) + public void list(){ + render(""); + } + +} +``` + +以上的 `@MenuDef` 定义了一个菜单; + +- 菜单名称:微信账户 +- 归属菜单组:微信平台 (因为其 pid 值 wechat 是 ”微信平台“ 这个菜单组的 id ) +- 排序序号:1 +- URL地址: /wechat/list diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 0d338a83..d21eb49f 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -6,7 +6,9 @@ - 2)将源码导入 idea 或者 eclipse 等开发工具 - 3)编译 JbootAdmin - 4)运行 JbootAdmin -- 5)通过 JbootAdmin 创建自己的业务模块 +- 5)通过代码生成器,生成自己的业务模块 +- 6)定义后台菜单组 +- 7)编写 Controller 代码,并生成菜单 ## 创建数据库 @@ -227,3 +229,61 @@ public class CmsModuleCodeGenerator { 其他 Module 需要复制一份这个代码,然后修改掉 表明、包名、模块名称即可。 +## 定义后台菜单组 + +我们通过 CmsModuleCodeGenerator 只是生成了 Model/Service/Provider 的代码,并不会生成 Controller 和 html,以及后台菜单,因此,我们需要来定义一个菜单组,我们登陆的时候,才能看到 CMS 模块的菜单,其他模块通用需要定义一个模块自己的菜单组。 + +如果定义后台菜单呢?代码如下: + +```java +public class CmsMenus implements MenuBuilder { + + public static final String ARTICLES = "articles"; + public static final String ARTICLE_CATEGORY = "article-category"; + + + static List cmsMenus = new ArrayList<>(); + + static { + cmsMenus.add(new MenuBean(ARTICLES, "文章管理", "fa-cart-plus", MenuTypes.MODULE, 11)); + cmsMenus.add(new MenuBean(ARTICLE_CATEGORY, "文章分类", "fa-crown", MenuTypes.MODULE, 22)); + } + + + @Override + public List buildMenus() { + return cmsMenus; + } + +} +``` + +- 1、编写一个类,实现 MenuBuilder 接口 +- 2、复写 buildMenus 方法,并返回一个 `List` + +需要注意的是,如果 MenuBean 有配置了 pid 属性,那说明其实一个 “菜单”,如果没有 pid,说明其实一个 “菜单组”。 + + +## 编写 Controller 代码,并生成菜单 + +Controller 代码如下 + +```java +@RequestMapping("/cms/article") +public class ArticleController extends BaseAdminController { + + private CmsArticleService articleService; + + @MenuDef(text = "文章列表", pid = CmsMenus.ARTICLES, sortNo = 1) + public void list(){ + render(""); + } + +} +``` + +- 1)通过 `@RequestMapping("/cms/article")` 来配置 Controller 的映射路径 +- 2)通过 `@MenuDef(text = "文章列表", pid = CmsMenus.ARTICLES, sortNo = 1)` 来定义菜单名称,及其归属在哪个 “菜单组” 下。 + +完成 Controller 代码后,我们需要进入后台,并点击 ` “开发管理” > “菜单构建”` 就可以显示出我们在 `ArticleController.list()` 里定义的菜单了。 + -- Gitee From 98409d8a2fbf36e53730b10d3eec36ce6422e346 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 24 Sep 2020 18:06:53 +0800 Subject: [PATCH 0572/1965] optimize JbootRender --- src/main/java/io/jboot/web/render/JbootRender.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/render/JbootRender.java b/src/main/java/io/jboot/web/render/JbootRender.java index 620349a9..f1b095fc 100644 --- a/src/main/java/io/jboot/web/render/JbootRender.java +++ b/src/main/java/io/jboot/web/render/JbootRender.java @@ -73,7 +73,7 @@ public class JbootRender extends Render { private void renderNormal(Map data){ try { OutputStream os = response.getOutputStream(); - engine.getTemplate(view).render(data, os); + getEngine().getTemplate(view).render(data, os); os.flush(); } catch (RuntimeException e) { // 捕获 ByteWriter.close() 抛出的 RuntimeException Throwable cause = e.getCause(); -- Gitee From 5deb67a41ff868e8bfab5105196830bced54ed2b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 24 Sep 2020 18:07:04 +0800 Subject: [PATCH 0573/1965] optimize JbootActionReporter --- src/main/java/io/jboot/web/handler/JbootActionReporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index 5e997bad..93dd9116 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -155,7 +155,7 @@ public class JbootActionReporter { } sb.append("\n"); } - sb.append("----------------------------------- taked " + (System.currentTimeMillis() - time) + " ms --------------------------------\n"); + sb.append("----------------------------------- taked " + (System.currentTimeMillis() - time) + " ms --------------------------------\n\n\n"); try { writer.write(sb.toString()); -- Gitee From 9d6d1ac8fac841aa84c3c725e18b67cbf6635eb7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 24 Sep 2020 18:07:45 +0800 Subject: [PATCH 0574/1965] optimize AttachmentManager --- .../io/jboot/web/attachment/AttachmentManager.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index 04606dd3..5e799bee 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -16,8 +16,9 @@ package io.jboot.web.attachment; +import com.jfinal.kit.HandlerKit; import com.jfinal.log.Log; -import com.jfinal.render.FileRender; +import com.jfinal.render.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -52,6 +53,8 @@ public class AttachmentManager { */ private List containers = new CopyOnWriteArrayList<>(); + private IRenderFactory renderFactory = RenderManager.me().getRenderFactory(); + public LocalAttachmentContainer getDefaultContainer() { return defaultContainer; @@ -165,12 +168,19 @@ public class AttachmentManager { */ public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { if (target.startsWith(defaultContainer.getTargetPrefix()) && target.lastIndexOf('.') != -1){ - new FileRender(getFile(target)).setContext(request, response).render(); + Render render = getFileRender(getFile(target)); + render.setContext(request, response).render(); return true; }else { return false; } } + private Render getFileRender(File file){ + return file == null || !file.isFile() + ? renderFactory.getErrorRender(404) + : renderFactory.getFileRender(file); + } + } -- Gitee From 78170a7a9ff79ad1f2a2228789f13c95720dc016 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 24 Sep 2020 18:09:42 +0800 Subject: [PATCH 0575/1965] v3.5.2 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e6fac5d9..08b5c613 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ io.jboot jboot - 3.5.1 + 3.5.2 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 45cfcfc8..b0839dbb 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.5.1 + 3.5.2 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 473eb44d..d8074bd4 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.5.1 + 3.5.2 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 00854afb..bc0fefee 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -49,7 +49,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.5.1 + 3.5.2 ``` @@ -107,7 +107,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.5.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.5.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index d21eb49f..1c5a4270 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.5.1', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.5.2', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.5.1/jboot-3.5.1.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.5.2/jboot-3.5.2.jar ClassScanner scan jar : /Users/michael/.m2/repository/eu/bitwalker/UserAgentUtils/1.21/UserAgentUtils-1.21.jar ClassScanner scan jar : /Users/michael/.m2/repository/cn/smallbun/screw/screw-core/1.0.0/screw-core-1.0.0.jar diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 838e97c2..7f12d0c1 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.5.1"; + public static String VERSION = "3.5.2"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 0fc81784c4830a9299b4dccd9a2fa1253ec1b076 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 24 Sep 2020 18:16:19 +0800 Subject: [PATCH 0576/1965] v3.5.2 release (^.^)YYa!! --- changes.txt | 10 ++++++++++ pom.xml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 4102f7b0..c25f8626 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,13 @@ +jboot v3.5.2: +优化:InterceptorBuilderManager,添加移除 Builder 等方法及其相关测试 +优化:ClassUtil,完善 singleton 等方法 +优化:Jboot 缓存默认类型 由 ehcache 修改为 caffeine +优化:优化启动输出内容 和 sql 打印内容 +优化:JbootRender,当不启用 CDN 的时候进一步提升性能 +修复:AttachmentManager,当分布式文件不存在时,访问文件出现空指针的问题 + + + jboot v3.5.1: 优化:JbootRedisCacheImpl buildKey() 方法 优化:JbootJson 并新增更多的配置 diff --git a/pom.xml b/pom.xml index 32769533..1b315269 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.5.2-SNAPSHOT + 3.5.2 jar jboot -- Gitee From ca68a4543e7d5ee3b18768b4ac8de779e25e2298 Mon Sep 17 00:00:00 2001 From: JFinal Date: Fri, 25 Sep 2020 15:00:07 +0800 Subject: [PATCH 0577/1965] =?UTF-8?q?update=20src/main/java/io/jboot/compo?= =?UTF-8?q?nents/cache/caffeine/CaffeineCacheImpl.java.=20getCache=20?= =?UTF-8?q?=E7=BC=BA=E5=B0=91=E4=B8=80=E8=A1=8C=E4=BB=A3=E7=A0=81=EF=BC=9A?= =?UTF-8?q?cache=20=3D=20cacheMap.get(cacheName);?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/components/cache/caffeine/CaffeineCacheImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java index 4ddabbb9..5076e042 100644 --- a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java @@ -37,6 +37,7 @@ public class CaffeineCacheImpl extends JbootCacheBase { Cache cache = cacheMap.get(cacheName); if (cache == null) { synchronized (CaffeineCacheImpl.class) { + cache = cacheMap.get(cacheName); if (cache == null) { cache = createCacheBuilder().build(); cacheMap.put(cacheName,cache); -- Gitee From d212794fc33f43643fde5b83136859eafc320fb0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 25 Sep 2020 15:17:33 +0800 Subject: [PATCH 0578/1965] update docs --- doc/jbootadmin/images/permission-assign1.png | Bin 0 -> 161540 bytes doc/jbootadmin/images/permission-build.png | Bin 0 -> 143820 bytes doc/jbootadmin/images/permission-roles.png | Bin 0 -> 109762 bytes doc/jbootadmin/permission.md | 87 ++++++++++++++++++- 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 doc/jbootadmin/images/permission-assign1.png create mode 100644 doc/jbootadmin/images/permission-build.png create mode 100644 doc/jbootadmin/images/permission-roles.png diff --git a/doc/jbootadmin/images/permission-assign1.png b/doc/jbootadmin/images/permission-assign1.png new file mode 100644 index 0000000000000000000000000000000000000000..c75021c4bf77fa6bef29ae7e71e408b6598986df GIT binary patch literal 161540 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 a=Uw^RE=i-?AD#pM$v;q*$(MTa^8W$csKeC& literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/images/permission-build.png b/doc/jbootadmin/images/permission-build.png new file mode 100644 index 0000000000000000000000000000000000000000..810d55dd7f0fe181e048619595d31a8eb732659a GIT binary patch literal 143820 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/images/permission-roles.png b/doc/jbootadmin/images/permission-roles.png new file mode 100644 index 0000000000000000000000000000000000000000..e40b3b3a55a72abbf2fde93a2410988f23685997 GIT binary patch literal 109762 zcmZU4by!v3x-}r(jdZtc5D96JZrGH-21G)dO*cqMNynycEt<l6OpXe|Ni1Lq7?8Gt3V@;c59r63 zz=ZtA&r%-fF2|0e4SpXDV!^|0P59!dEVQv%18Npix5D!u!wdWqtO`7EfhmB1X6lZh z;g^D`s_M4RrKIlcGB}9`Q6G638WlEpY~70%_pueNq3Lzk;6(Rgm!t5%&9Iyf0;vL& zk>Llus3?`3TK7jy1DZ)hg{iwh=3zR-^MHYcXH=Q0R!}pOP9h@8i0$O$7u8KInD}x6 z@Cp?Puh#JX|6Y)$iGRX{prN6KMnp(#cgeaxrwH0kW@%_@$aW6{Lx*DtzJC20Bl;&A zzu|pZ_WY^rn-ml*JBu7A8!Jvs7sDPROWZ_ei2nc0h`1|r3@xILjWg!q5>7{Jvs+9I zZlO{9w}aVSXzcq2=ngkkh^*;V-RedB$3aAvhR-9xqMO46Y4%K1$!4f(P_vZ$_29pA zWa@~ZCmwD2E;?zX=NI1|@oOI>oi_;YP2OkqmCwZxO4~}9!QY+l1TJ#6p|%IgR$YCh zrV_)Jh0JTfn=dd{lK>4vB0(ZCLSlHQ@G&6b3h`ep&7+2Qp#Jn`06dwRJs;Pu;i_v` z<4gp>ZzOu|PA=gO9~#Y1tW$0AaCg;PW!$yAyBmHWbhVsiD|Ea)LSMjMuw)(cVJHvi z0ZRk-PWgakAuSL^JV685@_(0B2Lin0S2MOoO=ZVGCYSy?bC!CRre9sk{GCLUl)-oK zZg|d|YWa+*7~NxtI*0R(D@RAsXawz&rkXF(8}A7G-o}WB14E(?&ENiHyk#Q+gpdA< zSDbPGEM1ysc)x>^E(=jPZoJ&35Oe70@ra1>hxMEs9Rt$R$mfBjv$K*-S6>@>&C}U! z0b1);Ch261)rW_#$D<%D(ieV(w2npJaJt?fXgl|9Wq`A{t9y`A6e&W@(d-KeYhm08 z+MlnR-8Uz^s!jUXf`^XYgyS(5&#Y>g)-}%BP{ugq`>$|{@kG#bPGYM65gZf}Qu?B9 z+En4CfRI4vzyL;OX6CNQ7h^=2ae~h7hm=NH_ssxBy81(;d1>fxpC7&5ZQsu()>=Lz(U~I5$OvGUZkSH_XQVzN+Aw{Qwh99#I%3|FkSx5aiYxb} zw6^7Uh1-;Z7n{FQ2n-t?S^LeN4@&pvcU|uzPQ?eD`oq_s(6Hd1IE0PiW9dw4Ldy-B ziPxBw#Fd9DZp5BscE>m@uQ-!kFVMT!im`R>UeOdMSnz+^Q+Za zX+=Cta8@#jl#jbk={C!AD!f9FT#; zak@0>(ppGRXdeTL!!b1E!`N-#t0K<%pGn^ig3P11m1GmH?txT*iiPniPM@ym&o%^d zIzCp*?Yp}T?US2WQgaSl|C#uT`G=Xso6o$B&dXUT%bt2_qPQn$o50{5At?s13$iJo zamb=Dub-XxM@QT4gfm8v$c`~#O(+vkoZ@yz5NVB=-aTNie(xTzVmoSmXu7>>zq|jn zIM3Z1X5{lp*J*QQmC-MOb1S0S>?cba85Na3q|vd+e&%W6C?o`jdgcJ>yT2Sw{-3N8 z7AzW7g}$;e0IRY;GQ@oT8nv^tbGpTYz;3Z2+tuBm&Wb3N*FGxH*OyJoeYu9E?d??DZ}yNslYD6En^+AEa-DfsFMEL zA7<#OyV1{?EQVA0nIfz&R8A>{o0Qt`wD^%>Z>pU4b|#0ep~6UZ!fN6(6Sx@6n82Z9 z_%yvD83JAI@Wmt(e*W_1S5zFFuFp&6%ZWNN-#hW%o=lLT|sB)bC5<&l(ZfSWFf5Np0FPq!dABR7j|@ z0&tSH@q6Zfc3*!bh2^1M0^G-8P)&^+)Gau$hJ95Tj951$Fv}~OT`8(`kw{*^q;MR8 zTJZ%1=RA0Q196oPo%jVRj+>j0XO8Q80mA^tLu5oC#pG$XwRj{hJ32UwOs!Y*YMty( zlumy&LHUG98nh~yoc{ms0{|ve0ChvoyQzsEQdE}kHzk*$NZ?CRTd3E2z?M2t| zN*66}LoJ~+QK#8&3d#)UxPe}jp`pnMYEr%;vB&iK-(?o@G<0CDK1YE zxI>u(ujAOVGfXW z)|m~6vFWkX(7>4uBuRHGBz`ry<4a)I!RED^{2tz(lM5Os&%QL8^2zPhKO}2+{3uUc&$W#^K@C1rZtf9ng{O@#1(vyWzuy zBEwuJ$F2Uz+l#Dvca-}Yk5@S+IU~gyTZHqB`>E<^_pQbvm-{n;o)^2>UeTH6g*RJe zDGlx?bWP4X;mBAntmXZ@=oRazCmw(2?3avq+i^T6teX~MIf-;)K8oLZcOmsg=>>jH z?h2$2g|>#6c17Mt`NB32NuA8ArA?FHcMUxPQ3K~m=ux7YhA?jFIu-GlNMWPe2<(=2M9%U5- zDCX3?p%U&GwAP76NHMt{Dqqbd z4IMP9CG;`c&41*v$yZ4FXumURRsT#G8rEkuR!#c2P*2UQk|ASb<6XV=4h7$x_)p4X z)u(S*N9`h%4qHBL_a@C~c9xct3k_fPJh zKuOD+xbkbQaLBxMp=y@Q-6f@_URmQe&k2R=o11L)wI1yVW%P<#2yc|~KcG2=3Xvhr zc&#V$IgbVI8=(tT9;9J&EL>_l|GvQ9aVM3VGrA9V+U^bS8Xb&eQLd?`(@PY8=hWAZ zOUXaOgf;2msSf0aX5mUOvW9~pT69&yA}+iQdHb<^GVa$VQqr<}(n-RcS)_S}FsU2z z0=A#ew}vtTt8h<%Fa`10&A;_gudHL&sVL%9+jtciTFh zKO%$4Y)KR#CQC%QeKQ1I&M$(G7F#1EL$I@*J}kiY0C*u&u2*-w33H<&BRJWKt%3e=&>I ze~Q=1`{9w!d1smm*zHypA7~z4J{TTGZm?_TcAi~a1Sn=Eq}8aDrxt;W1Gvi^=LKGk z81-kSQ_6KjA&%-n`*8$s{B4!vr>p~!vBv8OcP4D`Ag37b=O#IhFD|sW6?`qn%ZlUQ zhG0|vATf&4@mo3_HeVlv;kfLMYqjI@l^KO#b2@9PzXSJL?@e+ji1%?u+P9~K)-OJ0 z-3IgBY-IrmS5hiSmL;1}XtlfN$Gg=Zl_&j$S;b$=@gVhf3*UR%`B^$$A8vWi_a}r} zxz1W#>8d=&nD*xzpVe7U$sCawaSQE!UMi4m^Vplu#~|agF`Y;O;NtfmDu3Z(%EynP zH2r0-PnYlz7jWJbgY(ty?!@p1g@^n6Q8SGpqAbIq@TiX_Z4O34Xdv5OU{S^i>VYAK zL9j`lPR5J++JKW)!aSgPbRbcc3mjBb%`6E9@Ib$AePGVLLHOXn2ed90(9N%}rv3Pd zNObwiAfvX=e_(Fil%WQlQqcJ6#=uCMkC)c$>TI30f?EHU03IZb&o&^BlFxRQkWn#7 zlTNAU{PYwYE#iI*m&R`|^Yr+j<@2^*c&5pP{S!82$>zh|QG01)nC1YClNy`i^_S}R z>mK^nLAqWT1?9`(k=+{+A8<*j>=+u>bHZUP{#IJ0CDMy^*8J~HOz_zDdR%jT6-B7g`uZFXQB%)Xlm zpD2OV*OTbgTYMraoyyjY?~w@@fIe^R*Q>YrM3I?!ZLPKYoxI#Y?UEke=}*x3JUv{o zB5+F2Mi8+l<(TZx2rvd%cimp}ZEOT+vXS5I#|lfc!tkSBEH%k_-CutV z$Bs98ejwy54y+b}$ePv4cD3yH(g~bK*h_v_8Tb}EtON&*b~7$zH6MoGEzUPuEf{${ zxDc{wPNN3*O*T52m-a5YpD9PA;U=HvF8{1>BdkpOKOA38438&{&&y!;W*x&qUX?IR z<8X|cz@SJVCDH4xSkN!-iu_1TbWqp1RIEN-^bemtFR!ui_0?B-QelOKg`Vvnrq^fR zNOsW}y|mK57RzM{D370$F5=SRS%`{<0z-{s0&rwPghBVRk$LP zzYDX+bI3;nrF^I~Y#rBRh_hb#9_)Uz0aI^jsKBmDh+%^?aAkTO3e*7U{(CB~b;0e= z55OjT{VZ1|a@4OvzEp_I76}%N3hHO|xms{b7>SqmeYoJ)Y)CVX1nI8h(aXGB?(mPX z<${3>!sX+At5_P!BA4-FB=eD{=8=d~_$RIAh__nqO|)WM?k_}t?C`Rzi=u!b#xcZ90_9dB7}(Wlt7iELcD_3I zqk(P;%%0kOUOTRHQH2C&TVEY2kj-OYYK|pRU(eOsYg|O-2?z>4CyLPVc_WEOMjXDU zQ)#^P=%cV5Hs3@|7(^d}qx-oGzw_eqX;B?t+0_uU$&Z>tzYe^m+wcy4w$>Jdnwr|( zbMtpFH#v`h`RsV{E-+v$jw-jcc4JH;TxaR7bYJ*{QeNr%#52YE(eeBE4A6;+5`R$1 z3LcM$=YKgbJ)+Vffca0UB3?iTy*@VHPs|ub!Q`4nI(BUR7FIz&yxobK$=ADmBHVRH z>t67H1WPhx$%lnOX6{v70L8xNw_nuUN|f`9^>f*q?B1WL(XyRlLpEv}*us`)9Y=&AlFCm; z@(4V_jC?!O?AwZmXqDX+OU(8r%Lmax@|NSK$>&cOQ${1rZuW_X$w?f_NU(e|2wj8k z?T##u)wmo-2Y~dx`t)M|=j*`F^BU1;+mp%ki}ko6!1 z={;;&(Q|X7_o`;94AiYpGrWrx*Tbmz^+{BqlX0rX?tpB)FGWMz01K#!U?9VeQ?r5P zje|&Gk4(EPcFW*S9IAKovebJoh23%Ud{ge5KSbXmo!y^d(Zqf^b)w;B!=L+RC*36z zNrSYhoQ7KAw&O`it~HQc3&xQ|kTMaEW%ZcYJq} z2U_1yhLibIaa-)GDT*&Y`HJ{KaXl7;*n*#zf3{pb`=Ig9(z#TA9`xxfR*uR zV`1JIUtUzG&qPlX_8DMC$`JxJiPM_XZcjBHVVre!f{-}J>#HTdQhe4^#%5Hm*pH|7 zc#u~ghBQaCKJhl1*5vp-J?L;YNPU~|tCmR8?X0t}WGjC!50&>cjir-5S1MFa`$Q$; zndd=S!9xUb5BS@DPV%clX5@>#h(-(&+7~Y%I12!88i;(}T_%7rE&y;edGS)d z@wicFJ%@sdu56Jm#3WQ#_tS?){|@hU5NBIRMOy z4J67ePrKgI#VB>zmkUf9`jp{896?=lXQxNaZb$EcV0!-AqUt9zjLR@q&33j{dQh+K zd`!q;^|cCx7`Bd1e~6oQd$J~#SB?2G^y*`CNb@Y7I+$YM`j*TfiK&5sL)meIv0H6G zOH~1CaB|hVDvfc{lV(r56`vqO>V9=-(BQejhKxy(yeV*WsYe}UlKF97796p+9=6tS z#X$FVw^g2}wgrB+#Uoy@$}^J8eJ39lp9ld+>ao8RDz^}PnDY~WOisRIa5Btx52r8x zyz7Am{W1u@4#~b%92U7Y3;OkPCJ2Ou2qSJ~%u!fR3T3{**UPD27pxUD`03hT4^y@B zR%|4=5vDQASa*UEX2G_799SzVKx}m*YETZtmgo5c2{O6NF1q=ZavZFfoB`I|;Y58| zV(p^AYC|<9z1Tz>z(P}p>bjftz3Ki}?TkOcSnj;T%@(d0-X5kD_uT|Y1(IrNG3aJ{ zqxl$C<3-t`6o+$ea7S%-73f=Iqt9Jp^`ltSicn9tI8m8Nwg`RP2M!J*GZ&hRQu(e9 zk!aiZGh<#AvWZfkFZ-wF7B80=)CkVj+WFfvi}1F63K$q*CqceC%C)PAFC3yVYHRtK zLnV4jt&$~@HRIp; zO0-Inz4Zg{T@G>p=J4xNhp$$kBDkLz^fr`#$x~n2Wui>$nS%pbUkZH)I>%&c-gA}P zN3RK0Q>|gcR<$o5qaQsK0~AM}Zw;nJi2B?yl?*e0r5MsfdGH+cgq&_`@tq^HyvGXPBr{z(Bd4bVC9ttg5bw65oJ5G0=Ku6jlE!`ek@6K0sw# z@k^Z`(xotTb1^D|iI3(RODV#yF-{nPIt(#se63u!Ch0b3;phuB_AR9*o*iAodW0J* z1!q?O`dbG&aDrhTIT9?f6px%Tu%lxR6&+LR&6{*jjm08k$u>7v<`3N!CQZFIVo4&^HNt9x`of-h$67dDfc*K`-Xdro_`FeZK z%g4J)Hie~HU5)jh8~Hs+F&jH$1tn^RGiG_R!-i~m)6l}$wchCLGJD-b!D8N&;gUr5 z-}OfAQjUsy^cNikDi1_1d!mKA9{tJSEFhrM+|AZmCTxGMXKDQ?8{dn26Qkk#C?Bc& z4RUj?$CSw*jEKnyD?9NzYPU~Nu@5&FA#f1;-mx|VM}d}-3N-5S6r?*tS% zXP(9E6c9)(O*1}mU-a@$)1c>PJu*~~M?0s4&?!_H1Q%HpKW-;M{IvXb5ZRm44fQ%b zUdAktVMC}%Zxr3~p+T+flA*K{hkg?w71g1|Voq?Jj_lJN&el+tM)z(=w7YcnC`0m+ z|1$+odH1jTzAU4vi1B@|Vzu!DUbKgNQsxALy)*CnLvbzjp)%acMw`yOeG$m#A znnSMv4vUOODl(OKV(fH4nSAQ_s9@Oa+r{9}S>m0^SwSR=VNM>KV3sBTpoiShD>ME9 z(8&G(yS%#66=D@4Hs+eIOY%cjDz#M&1;_1mQzIQ{J$7b`65tv=xAf9z`@O6cr8hTP zTNAir#C&w{KAT=4_Grj^z$iHv`{!65-4x@{ zxO!HQvpri^ii(D<9=w__-nzp{R8E|dhw+n>q-)lcTl;#gx8!;Lw}~p(mOTEZ#^%x@ z?k=9*yhG%2x%_-1$NnYx{n*4m%2WQtdfopiPvV#KG{GY}m4=Z?9Qrv@VfbNiL4J4a z@7<2-(UhWG2i3Pracl*(u}2r1Bi7?)_2>%L745>75GT>?O! zoBZJ+`VaL5?}a}Nl&MivB32H4!nD~IO>4O6?_GvEXPHLyrOlN;rB&K7JL#wOad6zd zVW;6_{RE`KCj=yP8ri6HsHei%v(_;P= zlrbs|MPm+IZ=!~hNL)GdHYWV2{_TuBigts3O(RBM&%I*X!&=DiUrP)D3L%s5-NB2a zC8LQ-g-V&uM8kED#m`cJphaJfy!GZE&=&(npo?bc1{D+U`C+X@Xw%Fdq2klURWx!~ zMr11Q(GP!VgR-XyHZnSmf(aRk)VNt|SwOM+_=Ajoo*o{oFZ1`TZxp2T`T+$d`!-cy z?|=hf4IEYVbP3Qa5*FoMcb{sHy+8W8NjXUh2`Wh31Qdf<>ibJ7X=7V0%r-}b~0aEh=KCGYh0Ayk0 zPrvCk3RcoCE@gF5c6maWB?BxJglD8zjGuuy=2H>Z2a44`O&%vDh-Dnnqx*(QUyqqv zU+v_X!=(r$y}I|R=)+6x)E~Xv>`PZqKgB|tx*eqa-$=)Z`AHBbsP>nJ`N(&C{2xS* zoEE@dQ~rD_?GFJs8o<{3S3ZzaMc5k73TbZ_E77Tp%8`yJ*wP$ttYZP4P|($5(l^$I zf^IatR7{gLrGA8jF2`-Zh<%&7%zY31jX-}!z?1y}X)1nBnXb_}ZZAqfXFN5Jk(fpE zEpz=_C-w!S^mOq}MqqKaxDrBAt_(MS9RSk*fbRi#L6 z>|YHq#X|U09F9N2_-3FA1Y>Hb%{l*s2hkgGS2ib|oWozq7g24@Z`K**mmXR7O(c}o z)s%DKMG>A^|1nH#ZzLJd z0^C@{F?WcZX%ca{+4H*Y+8;k^SsJeC=>2WPf#pd4f}1n;U#jUJ1WwaM@OwQ;Y5e%; z)Sly~Vl!&j_6x~u=|(eOfYal?KNv=f6W;}d;`~fn+5fVIc;Rs+>KE*U&3a%CD5%*m zDrW@@7`id{()wd60iS!uiCrl3&+7kQheb>|nuX&41PcV;HCsHnKt%|dba>B+-!~3~ zIQPY=!|7iN@$W@HutHZk-XP3;ghSHdduwV)_n%!iH9;VR%($O;?97A!_gv~)8~)Wm z|1p!8%Ko{>VZvi@CJMOco4%tV*MGf-9RZut>q50pTjy5dzP~y7Ul)C*h|fC{`-wk~F~aMkDy5!_2#W}h zKLJ1iO47N(7BE1n5`_(QhH7>>x?$^YpFaQBqyIO?{^x)EGK=gTXYn1c0IB8zlje-@ z?~wex*pUH-dExij4~FA?V3QLOniMAZ&t3sB25hw#`zdFi8IFGeQz_!KE=>F1cfDfx z^BLnaO@vXmSijv-(MSNSIZu?xuJmyCeE z2z)voL{a!xH2G^edPfAby_D-K0h+1ZpI3Q3y!QIDv|7BUS7(o|#`|4Wx$ z&032^ii6p@8lgcUpxU3U-LaT&csW~Vo3?vW326-n{GKesiE#ci=f_ z98GP~7n2RhiNd~FC*)+pPu?ZHPuKCPRptkRF95Zdw?wD@^yO2fsFLTZRxNNvl{9nOGT|1`{ ze1kZN<5Saf_jczwCaFY;&&8o#=eSU?-MxKOG8?o6=+x{Vq6{(pXnDcQ;SteY`u44n z8O^Tr_4WJ!U7N~~uUBW4{UMr4J3irL$@k80`l}NOn|9GhW62N(kA0_$U0S2X7WvCY z&jYq5=Yy!AAjAX~!+D#fDI@a0%Y!+SW1zREOMb_!YH){#hvs*Wd_H91B%DzqPmd8J zCWbZcheicTJ>3x`FE?OD>P2c2uLT53+W;?!`}KCLU`Rx1spr1K+vjhnM1PDjMyNM} z8B0CB(*OG!6z^fCaV+i38k#Y*NvTMvA*k}Rz+}5&m~IIrgz)9A`7^|LpaT6%J@3Lv z7P1#bzrF65o13HiTsl=jD=0`tZ$#=p)${o)*{s`=U-S(IvEG8;V)_hjn0awO#i!`! z{<*zTx%yL}2Dc~C%fp2bHZ8VXg^j}EaL$(B@c#br?oWXZMH5U}(5s;uOtI}nVMz9Fe6#JT}QM;(O&p=VH=lQn3L9;7sXPw8G3?VDz zt%#`C5XJrV*zntGcf1nKiZZG@>uz-KlMNj}bFUN@iu)%g|AAcMo3y{ZVlns(0US%< z`{~tznjrkZUC*?)3_2)noj2p%~}L?XL3O-Ny(Am4W9Gv~cY${`Tw zZH*OUOG`_YD_~+eKViOx&ZBIfIc70b8lPzh9v>j2u`T*3Y&A0o1 z*ZBWj=%#(V;;#se7?_>1*qe|i)ajeH(8obRf(>rF)1aABAzb1^t_~NvrX8qPm$M}; zCTr1rlX=kW?CiqCj5X?E7q^HHo3$nzHw8 zfA97}DT(?0qtG||C4XS0X&?<$)XJ8Lr_C1a=^h^OI(}=P#a=6joPH9o>P-V zcX(ZKLB;)At`3`Tw>S-(f-+k@+5I)kt%w>L8@pbHIrm_Ya4vTSzSBx?RU$RWVDGx@ z@1@QL%21i;$h%|7vZdqtg$B3XYXm0W2bY`^p!K$T^>3mm1jZdmK#(A*9C;o{rm)Aj z3DAVj69VP~fTz`R9@`vz?REKLC|w|TNFDIfXjVW-59YoFmZm+lNyHiOn!JA>bJbsG z0bPHLm21zls++MjRzf~)cSO25+X?|*RV*s)tn^uT$5V_&&LEALv?ybH`ee- z)Re()snJCHccz#h6%m`J+(o)AJhj#}p%RzW_-DfUWICvvVJ zVN3~4Q$oNZlbqu;SL&p2#M`(D&R0l@3{bXHaV)c+sU{w)SB@U;yV7%<`nmOgxu2#K zFcB$uO0zap&r%NW(ZUN6$dxb0?Z^pc?LU7`LGhVZGGyg!Sd_P!{XNiS^)`sduluzt zDIHpBZ*qRyBsr*jZ+ErzyA8JEtE=9^ET1SkX>@v>p&#HkrpXZz7z&9mQY`Mhu8vqw z>bz(0HOtgSwmp=gK0J11>I~Wn>WU0gL19ZB+MO3KB7S8F`C(K0-Lu|TUgugRvMJce z6U?~mo?9lc8Xz0_Tp+P&)ehT5uHdb_ZE`04gh^!+`f{1EG?P;>IlW7$Dc^PbGo!SKU!LP&#=1As-f2E>EeT!jk&M!8-GG)z5rZWLj2R;%g<3i z0kQDnIzz}!?YbWsH#<99W~*v*D~R_hl`krE&};y|U3>i|z0r|Tw^9w<0hka{w_TFP zDPHZinq%o)(*7^_@-O=Xc6W&Kmw#Hh30H<7?kjar1t_MCuF%VlNq{e@w&dq}tCSfR1S)N;_$ULq<=i)0i`iP%Bu=9!5+iSQ z5yLz=yk9A=Wb^wJeqFsQI}wmhbD`Pa!vp&|+aGT+T0Jko-)q#e1K!r# zi5TuL=wD=9Y-K&?bGFE72Nd1R`b)$3Ho5@74g}kNj0%tH=>ah3gWD3V&QQ9!>fLK$ zig>1n(ADmU@BQ!2u<%mo07{)7G~z&dV_5TvJDN#(YlZ0fTX+;`<2jTE?8 z*94TxkC~S+CFgC2i4)*4#hQ=0+AV$ZINM_HtaI*vPc%qpd$QVnZQFB{d@|>(SymMB zTEE_olIh{tqy$VT-Q;{Uf-yW*8FI4LTWWzDD?_Kb9$3{!9_Ad2(+8xYm)O^oKXwz@ zZ4_u*0I+(vh$Z&}j41W>efRm4)wINqfIbq%gDbJejw)lxm30f>DTl9Zyecowe5N+sNGsAdJxI(q*D3EcqyHc1H8JkH2HEA%Vd}bx!h{Q++yy{^PzF zIDR&@Xm23i!fiL3`kyK9+L~nKtvL*ucz#~ru3n$4H8`vxH@O^?@bTkX3Ynj8ON|i~ zvsB*%V+%UG{%{BZ4yQDVnU?9ue69+eQG?pf*Q)ow0`vhxly;Sj@R3|2<=U}b&i2Rf z#a1uI#so*Bg+?cR&aH2@OW1(1lE-FR%^~BZ39=#iD`a5rQ^T)RoC56%gE@Z>P7u{u zO(^iRW&enCR`5atSs(Ehe`UTvmL4TtTspnJzd3tp+J_slQ+wL9SnEIlpj3Drowf1z zVR8El&2BkfBJ@kvlWkEm(f_ykSCjqO_H_~h*h?2qTBz`=UisJJLW)yzEU`dcUx89c zzRB?S1I-_iU%DZ5nT+?8h{xjCyBg*Qn0x-9UEI!zZ_4Bs@VrKk-eIFOW?$B=pp0JC z%#>0=DW$JW2TzQ=1*m|C`u>y1kJXabuM@%h>Vcv43m)E7qlT=vh;cv(Y&`b^ClMM$ zv;1sn7{kt7eP#E#aM#&=t3$3rnzO5;HV#Ti(DVEhX=&MP!&Xmo`rEkF%041Inn@l! zrc&*4aL!lHI;&k=G{&1**v(?--sZf`%(Rez@v(^ePygdj77mxXKZIt94j7{Od0R19qE&$c+}{2smg zV4R#(qTWHkPOeAU$%rqXv-cCmX^)qdy*&CO36L{2nxmSIka4KW=zb}s?YN{0rR1GZ ziuo#uxk;LjK8@tc9-p3mk9~U7P=}-@D21mLFW+9Tsj3yK*t*>M++F4xcOmZ{F{`Bi z;3r_#bJ{Bsot(FhV`2r5a@A=>MMgwuRO=%qeq%4s^A1;IZWxH99G3=gF(Up z<{lm$j`-|Zsfz{srB4s7s-tXDvX>RW}k`CC11Fd50!_am_ zi*@e+%B1a_+q>)E*(_j9*iaE~m@qfTq}@T}?-Cbs01>Df<;v!D1BSEXn-5vVMPKYh zJWDdju8!tP!OiBwDS^QlB*uG5eq|!kIlg<7l{zL?%3x|nk)MOV+?xP`-#r3L z4rsDi#N!szuPXB)<4RohkXO?4F%ieDGP8QjR-{$$6-w2%o-<&nM?HC*nqFTM$~lVw z+5^;KBuuo6__;fded|rZBP|eU*3(<^dW#i+OD(b~j#05>TsS4oSh*~ZMvvYKur(|$ zHlEBo4eKhM)Jj7Q8r<;!$ERj-*e4EJ3Fa_UCyXw@vxr4>p!p~tUMT#5twFm2lCMh6 z`#B&!bRaKh8gtlWu$2h`u>El0o-kX64yK7Iw;R@3@iw_0pv(3aHrIlT9!VB`wkTc5 z@H9&&%d{(*<+COZ)jbYp>-L~fC;-rVSo=P?9CHJ(rk92vU<+=+xh)7~Zf)TdcF18@0Q)Eo=k9%G2OTC>onPZ)X z=E3So7H^ikl5cu3wPcx!CkvGIa|;2hRf+vvG$sFJf=gfY8sZk||j1 zx8F`5qJ@>4y!|{~pmc6RX?BABvB71JX|~SZ-nl}9SvA}3h-Bq(CDtMwZ)N4$chrp! zB7)Q3hqyac8Vwx9!jwfpQOdlJFFA-$HCOGd1I$h>HB(_d7y9X%+ zIn(L5?g&j31iK=INhNZ_E2(PWa*D5cYt+Oh9vvqn`F8+A0!1VJ7I2Xz9Vx(YIr0t~?&pK6U z6p~k{YXua;GX40B7p$S&nSD6LCoTuGyo@Pnyw)XFe)->!r!tz|pvxbro80!2*5d%v zb{v5fIL4qf-fYLz?P#$ZK=kL1Ds*3{L_LF2C#;u$HP*-veFb-izxG{41pMaXb_vX? z8Q=ijylN?ja0aXShFAkIp@w5+O1OL@YOEz)-bP&!uTv!vp5^zpMW^ANyM7Uti#bf| z?i8vtydz#~--0Tf|09-6wGb|eAz3`gZQeP!WXjJj7rrSS4w#FZyh?n74MFjCgu8J< z7<>3A!tZ~aT)0L!h&FxE}AU_Zhq`JKWe6yxG|^Ee!a`4>N6^ zDPU-S+CBfGxBk=0T4j5Z$Y(-B3j3DiRngk|%&Pxl4#J(Y;}S)dJ@NWPvU0WXVtVca8_f{*4T+S1c@;(STZl z&2MXs0Ah4aYD~ep(+n#FZLODC_X~soGMLKeUs+knD_J56K9UoC!94Lo8k!=+#DNIg ze|xp6=u>62fu6wI`0YyU8V;yGwc|FLkJ_|^*cF#n2!!#a@$RYm)J7BgFfDn=ufqrl-5eaW zq^}rD4jP+WCbH>_81hxX7->B6_kyE6FocI{jJ!!BDwwlOtS@)wzVr6VN|e}BdL-a8 zEa@_pm1bS%Y)1Jx6b`l!5m;g94YP=C@=QWcw|1&#XkbWHw(1K@j+^QqOsiKbd#mp9 zwytF2v+I}p^)XfTcHak6SbOt)a8UdjMk{yJIlP%Ao} zDdLsj9Die_ym`7%zeJX?qz&-;M|Gy1)zAJ3U1NkD)&|5qqgH=i*^iNr;?bw#S!Ciz z8y&w?IyO)`q%hE=@>~23y8Km~{O>cxjSTwr2rm(=NS^Vfdz@b7bnpv(WDT*^|DnX% zP^*~8pG?4{6x5vNRjtEGP2qcOjDNV;8gFCdM7cia6js1yQ1AjcSt|Li!(h!08{56Y zPJ}|p6_ChBUb`RDR6tq-m?G0k%IETlI1M30=53xACAalb)8my(aK0h;xYUJ;r89Nb zg}2|@BtxPappWUdu7+(hFlSOrfJklGrB^q>M`O}OBjGf7W3f$NN7-820)nO*c2uJ5TJG;m>x03^QpXhZWq}v>IO3d_Rcul zG+n~Rez?&#l%kcLKLjXCh_su2@ zC?JdaGJxh#7#kbQNXlzYC$j6P7rNTIdu(ufRu~5&_scXSr@_3J?S5C``Q97j>(yK3 zAJN5%UF`CUdwWxSyFZIo&zl5b$Z@;dq84j}Ug|rA3f)`Zn+f+iCnK?J^X$*t9j7b>gf~yMLq$oGtI{z~iyuQ!+M-2?`0&N<1pcbsWsO z{myfUmjX+Nk1BRP&NsGdQ44Gxig;=IKXB(OO7ig>b7;ss9CA%Prizx@x?c4 z3XI9o;E7dZWYq#rP7>f$m)etJd?@!6Gda~;!RS0og%Q_S%YI&Kz+Z_B+wZZMHLa*# z%ieFYP>i32&|UG6nhAdDqm_JVe%+ECn&^VX>#nR2djA4RNM(Epqe zV#I@#nt&X>g?tHtPrX6V3H(U;C|*1-?m@jA@qwj$@X`Hec}muAKD*#_%@rJiwZ}5mUC?aV4}a&4@kR%!38gK15lNJPHwPh;r1qcOcr*933gHWb4%B(+iG3gd2h>35DG#kCX02neHs*JF5UibkPV@oaoRF z+V*FFdzC9d^0USW1BZSdEKff5Fnn?+ILiHOkl&`squKQ+ig+-Me8MKq-;`MzZ>`|; zq&LeqVBA)(#wt;HD?V_aw6o5=|GiecoYil6#Xr#C*3j>Ox3p54E->#)3o1yUFr<)~ zm_sjt1wkpNoS|IBRVm@yQwKc21A+6K`(Ze7VAfd0ICFO1r1dM{C|Dy7?~JJ+Yq1C8 zRdzm8JGs?lQS7#*Q?Z>Mb7Q+SA5)!1{?127j%vy5B;r&;VP&YiaxIqMQ@PdKlsV~> z-z!}JX)K$wx}{TL7@TLWfaj~9iVBKVpD5kqd&V`D%4ch?J;~&frRRi`mdT!fl`5st z4>M3E6N{Wxln>7t-{UTqFc)NQoYJeaNkLVVX`Hgrb)u5|Kb=-LOxy|)=1t9)O{zw- zr9r2K4u7Gz``CKRa7=T*;}l{KKlbExdqg|oAE4US6<^zfrtc!XZZCq6nuz?91TJa; zo-6@w75}5iV~5w=$3>!#(mpP0eH5v~aE9fT1f~^3;vjySZTarvF8+_82wS7CND<;$vQ?|Bt||_+eIw**ImQzTMur_ zyZZsEqK`oV14(H#ZmH=|yf7)pN4>Ba?Ao}07HqNbOZ7J%-8uRK}ifsw$z7vbTw zm70;-3MTYR;TGs_-n1kjyV)&$z3qmJf^(a~;aHAVaSNPLtVKdI_jHhuxcX|g$M>21 zPW1ND<8%3D$ zwKN#i2PSPb^(Mb@Jh8yDD5HP5@;#Ae48p|FC_Hbw+nlpk&+UnSZH9p|V3%Mbc1<%< zQ-B8n6V6qA@l-&B<&oar-IXqYG-jpX`0N)U3$OGR=wk8c{7gB_3=9?^7j zN*fb^B=vr}D!U{LIHxZqpF&(lVmUecu3p&5DQR4CtWOXRf`UW&1~~UJVHf6$a-X1- zSU}V_6&_2%3YHoaD`w*T%HXNU{y9t2NdAg|$#DV94+bYb%~VUjggFlZd~s2NT;rnH z(>($GCmen7cl?*|d!q{HlhYgb4-e1yC-PAPi ze)Faz(hWn!oxQk+e<=rYk;_&(WYavqJn6nVd>r_&c^Be`O!$cG{yg7nkxTky>zm-o zMd#&@-b}A&7Gfj~w`y1EQno(h_!j4y!EePRUWvSFT`tit1l?XviB9m2-2D-{{(Qi=Or0c#d((=khC<{8^s3-;ZKw#H$%d`{A^kk4wlZDcXJ%6?6vd?# z3q>HFXqFfulk~*Pe^$>bbz0>C%q+O3(l>L0w>rDJGS#zsKu#xAF+CDb=b>6fFLx9@ zT2vT{T(fxRc66^12vG(zk!-a&(54Oa`t%7X*|jj2m=lQAIxe?QjTU_AABrQEjSBPm zaU{Z7Q^BycUuQF3Tolc;+{422(oXz6fK-<`INX$UFBPAOVb>V34UNt?oitA^=xRD$ zaAG!Qa$K(*H`d8>cUtLF^P8IbIOwXL=PFP<(h)s_s+{!G`|xmAmA0E1TI?P~nD6>E zGZ0U$K$~0WWMZPmZ1}sGL6p)fG;B?Vo+*~~9qy~J!6;D5kWz>LX6UrSs$IN7%Hcqr~K134n%-WK)%s)V4V@A@)59I%7GB3 zrb~G`i%k8bVNABlkrCiJ2J+4wu!b~v(R6OwIX zLC8w<{c3|^nbJE;y7N2!r|#dh`G&E0CdDto22&a~))IGs3hf1$@xmI1eyIZS_*zuv zsK|wWm2KzMtS`ELwT%|){1+ht1H0xU++w3fljE~wF((eOXE$52Tw;j(KCSl8cYbM? zmAh@dH!r0l_k;{#kEXRdtd_DqJ|Kjo1 zN4K+w+Z$W-H4F@lyLY?#^P8sQ{Djj%%I~>KQVb$G_no%Q6wCVzHlc>wXNQ7X1$k~I zMxPB6hYlF-nu*kc)b+jUVnZ&{pfQwj^quA$hQdNi22rz<2Tq_mD_x`bi#mm#I_=%9 z#*hrxVgP6`87VNuCm;yN>k89&(^nZIO`z!Iy3_f(gq1F!_Y9sg43TrXA6zsV4tGFnHEZ%Z|tIE*KQ$IK4$3Y!$y7)0FhP~DQu%&@haQkV1ZdFrkiMJxYmjF zNbn9w-hQjnxU10Aeg^XPyUkY@Dh^l}7_brD&8e&qAzq13KKHdhhrbOFT6rjuIBUhg zt`y7h{=o}!8uKqwg-6wYNg=>d5AO8mg~fszzsh;+%s#F=F&5mzQ#R!DMxQ)}Jm~)R zn&_c<1O_i#lwU_b!pS}EKY&lLX1PgR-k`dbn|?&sqkHSj9eO6|96{|xWO1Bii+vRc z-zW3``~xsl5MlA;wYcnYBih2?t2Qvlcd03<1P0G-`}Y=`AMJca zzG&K$-_FTmW07QkVJAXI#ny(8|M_SmcZg%^p8C<9gXZD9}5z_4IX2q&C z!gubqFry}C$wl2?e6W=RpzS6@3(Th$)4Cx;3}ie;_d%tK)_oOFc210J^{v)v)#0)g zm1IWLYK1n}D*8AxEE8k}x`e))KeA^Q0YNz%0xH7j+$8IV^h4VFEptN;4@VM)N=!hu z_A`#%o=233^u?iI9Jg_CMrH-WuKIUFl&>L;UW<_|#_mTmGGd)Om&Y^69ZOJe$1D#;+{^~p~; zmksKy%E4(WAH=z%=x{uDktl_&;cy5takIeU0i} zi-NHw?BRH{-n&g|OJ70Sw{NUq7-Uho)T|Xo3!R}CWrdUe+1auGB|+tjsztSTv?YMFBLV_%YV8_d{2_`YCFv7i0r zBVmEaw+!sgv(0wR#Ih6bA3a1Fudp0eEi;h~aU!Kr*m}uUt`J;LDSY-=t7vMt&M10e zGRw*Q(`qRY(l275h^dHSA3KSq#w5cuhKodvm*7qIdV{0gqxS+NMf#89_RHB@!>Q{B zn|vfL!aqy_WUSGrb2L-=|6)nH7f{qWYaZ@-f4v#$srU6dUPWH;=Kd{}P>7Ksjx{+h z_G3)!Vk~D}Bm+;ZHE~)|>KI-7&vY6RNJYJnq<%=CEhIB#9A|5*m{ZTM=LudjIXdht z$40;VAuTXcFE(=dyFajsXczzN<$42yBfUmpy@Xj%3e2eO)92Zx`sQln@Msqq1xb0i z>QnXie#4@ckh~aVSXW^Vj&ENVRMAn!e3e9bkD=ygi9zQF{S`OA(4er+yy4CQ-j@Xq zi=8#DTltE4Yh#rKzP2eTV)!(dA74_!jI$rzfzQB3(hz0jk+A91NB#Y8ztGjQ`ARu3 z2;0ntR$6H@R=e4Tv`H9uNV|q~xZ_4ajpi3eeuZ=btY1{9U zPS={24_gXsGG#o#R5{riM!!b?P3RLD@HU<@0PDhXbfY)$9QcNUlr6E-NDO{V(HvRI zF9hFnl79H=VAiF51}{;-R~RA`dvdcZ<}1JQ52e?kaoNu;xrT2?Q@H;avk8&FP7G%h z%K3<>v%KS5lu9h*#}iZ`WL2zYHq;i04`3x?9RwbPjb^s1azS;`IHMtp>Paiu`j~z#K*VaQI?9ngxe<*I#w#8_ZcXf=)jW*2ML88CPR?l|MXS7ai_9E4=em{u=cDUcw%qw7_E-rKcav;Yy980zn zz$d(6{*HSLi}dhLk)BJb<#6ZyoLYwq3Mdr$C?W(A{T9jN;Kh`Ip@EiEZxa&Ok^vzRIxZF8xy;Fsd@2Iq4%Dv;#im8&oF=zsyMvJ@8G(_6$zxcT zoPpQcs=k4%P()`56rgsV));Yl+$DpI`d2f9JnmS7dXUKP3a7aTD0ccVZ#Z=+ePCb@ z=U!;54qZ0fiIxyaW&K}dYj+dMR}Fja9URF}q<|9i3Wfu_QDhD>sAO{Lwy%ZWE6za9 zQd2&}cE&e;hp{_PCM^*r7^Wf!^I$=}Hy~gW_ z%$0x56&=Pq79aB}ByYtu1t#sr)fD;6&amCNs5aeZ-$q5Ds9PcNM%x9hywhb)5>u^^EYonvbd@Mo98fv_^jNP|v z>Tv2G0}rCfQa@~2(gg^yF-G@`W7hjbr&Onm4K zMSRB)6F3AZ%fLy9 z?>F3S!+RI~Ap(PEN74h|l#rYSU0h7Gb!PY;Z%rSLu;-!G3whZhF>z5kBdj^7{q}eA zz7(5UU)^#dvp}wEhQeN6e%{Fc!Mi=6BfhKX&dPGW?XBJay%n;JH$p?4r3)Z zYI`U0w~|eo17`>b4CLw_n7>;!#3r=NSb#mi@_CnJmrZ8i=+7KX7T8_Lct!n)I7_Qh z4~L1VeSRrV<8#@A{WHvEPQ=tv!Q0SVkM7)ZQyl`XHwk&VYrnv_qlZVH9)4e&w*f_ak@;4OEliQb03JO{`wOD{-ye~eC;~4Lihh`J=8?=O zI&x@zwNWz90n$BoQYZ4@PqD@XAo6G`}hNj2Xr{s0Cjo;+<&35wLyJF2BLgl z`>6=Hmz==Axf}l~4f-1|at`XTG7ySa^^t;f{tvH99f){oa(-2}{|EIMc};i-`1Rf= zy(ha1UBBk{lgd3v{&OBrz`uvQ3@Nz*NHR6A;o_*0G>U)9t1E*{i)_oY9Q~lSNSWhn z7`)W@2c16#A6bUs?zb3RQxhtb6DmMy&Pr!yUGZhuA@^IU$ICDB2QuIb53h%z!9~RS z$B~HaxN&H-^fF9`s{B-I%GHBfk@bnk+|6&eXrM$!P@Lug^!;^d#mZyO{+K_cXOmEr z2=NxpPn?nG_NGxLw+<*l`w`fg79Fw0uS;xD6}id}klA1@o|U66I{yLjF6V+o8Kl}e zwZ>WDP8vCji9v0YSP&s&4!PgoiEqlc#6B|?Xy7Aszvg#z`#U-Cdp~5fugfH2b>he6 zpeo=Kd)oi1Q6cQ^0h?I9zp+2{;F=`zpWx&F{FV_01s1V5r0?S&KtmA=QvVCuz22|L z*z1~t<|(`*IHE_^{7%GGK`?eBjitOQ>7-M(v=;*%6aA0coFaT>9M1l{iL6vD8mgiQD_xr@#$;# z`?9i6CyaS?-K5YZ4Pn#8(Uc$Nd7_B^T`>Y`bUyAYKe&ZYgY3++ck`2H2mH$yoN(N5 z_D5PD5j|!h1MAT+ZWIvK2QiYj7h?%7#V(bGFxH0m+5;F&L zGKUcuu%~x4F-0oenBb~0F(EiUQ!o$TI^5?_l_~2l0RU|YJ z6F$HK0&aOH*7Sc$0REcE#5+(=BW3BxMcDoF`n)1C_M?BDt^fW2P##dMX@b?$OAqk^ zD!5!kEnNOz6MA9?K61>`v-jYi&;PG9`f~wYz=_4f!5f6~9Wed&rrZ)E{kQb%UF&14 ze5!XS|Cq!d64{+`J>0d{{qhI!>$<7$veAqGKH48ajZbj}L$8AZT?lw-4+GZc-alSM z=g)P0wy}75c){5Qdb*c}SPbp|8Ps(Jpi&)G5=uGJ^~#pMZ9w_o*+B?_F9im53O(5W z4>75_Qe@Yk5(B7_g{}w1(zmBT_YN#)&k|Pm`kznzQ#s~cmqSr_O2+-U#h^kNOv;lq z#sinIyB&O5U~j=;9ePV#D_TkX_q zac;NSvBRRl*XJBD@?KEX_h*fkmFh=(dH}#r+M=`OWOR}Y2GRh`lVpF{%Iy7fUNlo;6*hNgq)^T9ixdL`v?C-| z#=aZ*^_w^8plm)t&>T0MMxwJm3`O92M;|tNCrkb*H)dGZ)1jiGQmrcF1r*Cy1G7Fd zvM#q&k(!9t>12sj@!JEQ>@=Rt)(_>fv#?NHF~zm#1}TKHB$_DP%AWMvX$(Pa$INBp zpu>C+WppX{s*vjgs=wj31nVlw@fR<_xdxIN2~-7z&w%iev9GVMbm3$UfH$xRVDI&H z;a%e*8iBX0rm!#04_iWcpSKhilCw1aXp-#eAH$7Oi+URxsu^2>HGyQ zC`G{~(l8w<(jdF~dB7VT{f-YnlwSwR!!N$dHVEz+{w&*fyDxWt-eXO$!JC}%UcrV| zLB_rMW8JbB%z*KPf4%Xf)TYaIx^^*)98>|k3dEhy&q2@j*}oA^Ezt=p&}<01RUd_2 zbF1EOQXEqi1|?2ILS>VIT>ng@Q~eInp~y%OW`nYrGMW}%{-FK%k?AvHb0%o#$z0Qg zsPF!LmL1;b{jGq1)M0r;E7Zs8R$S33@|ZSh!fhY%mA((G1GN0sY}&;}-14u2+Q#~E zaW+QVW_^Cp$-%-XCD8P%tUCtjUq!_vhvICCbX&eD(y#1f^z)P4^~-gh^K{wBu$ya2 z_~Ei~1Sqq@DMjoU*!VDfJWm2vE3H6fgM37a6v!7mLNiF*>q!p+yo-{pT!* z(_j=BF>hhLP3p`IC;bw9w*;cCjLFF z>9pGaHbMoS=wh|Ew-m8tQF4KP+$*s2Faeg9qf%ydVteKSEc(6qk9Y!x1AP<3dg6K* z5pTN>r>f_CSmoaZF&4l$#aYk@2ng(*Tsq#tkgkPPo13iAus$3TPtkBgw<8(#rv2WH z^k1v}pN032=22yU@|ZFbjpQu`ti=yKP<(WMU|T9ZrzG%MZ+zRt1d3L^W=jw@W6f>% zD<4rE2f2}6wuJafc;BSpP@WH~^VSq9C}Z(|^9GfRi|eVi3AT;JVh*!(%T5cjW}z;@ zJL%-mou;ctMiM#q3bT$bst@*>RixipIab4oK7L9joygAiHztj>T*ts<7Q;AAy1FF8%?joi+?Io^-T4 z%KqO8{4M%v2q-fHf^gHY(sG7^gmY?sWPSK;P*6ZrI)6{%`__s=a%+x>w9sOj6<jKu zH5J32AaRw%MY8I5A(TCk?4R2muUKN9oH3FAOi4FEzuO7apT$zxqu!1+t~YLaoui_0 zm5Po!q6LG**Bw)BD*tspe;w7d!)pOo;_A0Gs^T&+F5{$ng#Z8arKG)(c2x&v<)fXlQ6D;4U>l=HY+2+42f~^7t44 z(mBYIRs77bYrZze`I2>fJ-{f-O5}mVl!m<$wle=$bki>XTC~u(6;kkJA=jNrED1`= zhaUt`Q@zjG>s+^Ary0sC;Vh!zjg?=gO zy;Q0W&&tt}>Y>+Pbwi~WL1ah6XTX?3V zgw+u08&osle%1&m7ft30I<`lz)ShA~79b%Pf1Nt87WrGN{dd)l2-pZ9I-~jh?mIJX z4*N?A!!L^mZ=gaUYRSel~XT%Fw>x>Kn2gRWq2kxcW#7ICfu>UQKe{1Ax#qb>x zKk?pv5OGJh-j#aV_>J;$EXUK0F4|rKg9Sc8p|&MF>DN1kwAQobB<$?$pehYbyuuDI zo}$wJbf2a#}%VCPr2}g+UN7r>lI>Z+{TRsXy5Uk;K_x7-x=9&5QcFblHQI zU@J>Y%XFQHl-R z_%QV6`~2|0(^_jlD(t3AO6$7!kD0U0KszYxgN9Uwvmdo(e@SxKxs*ysxcRg@jlQ(9 z^3`d_QIg#%r)O99Ad<%I6b3ix2A@@25P?UQl9DoTZhqU{{Q#R%00pG_I#fdZS`T!v zhD2;9Ff!#L?sgyGC&UVMB-c7E<#IRxF*EiNqa1kk>XSG05c`PW=Pg|V`)4j+zb$sA zdkjAhjtA3=N>0Wua%NRIGovt)laJlb2`$pG@{cC953=uFZDG_bXGaeye?B-grfp@c zZRFzramfrcl0Y~NI%&F9@Tzt*w-$N!j)+Y3EYW%vbzdw#Xe#4GA?&GghMnSJy2+`4gX<}0p6JrL@N%t(f?mH9%YW?Ww z=mjP;+Ae!in8O!* zm*I8qmg=}lr;`<#F|QPqnH#ZxfCtOVBj|aY5TEksiINksJIygPDr(5yu~g%6{)sU> z#fyWCHwx8D47tARS+Sm~D*oZ&(Nk+{b%xCkbBc}LfU%@TbYfzO&y2g7Z29b$f`UO` zu&ORVc`Av_-ms_5|Mjf~AF@g-&G~L_9KY@TVM+ez&AJnx5vRPwR1?M3xc5b3nblt@ zSmYd-`+6TPtgfO{>Pu8o*T$B~nlS08K0!)OPS)J^BmKQR#HxPaj@pGPpp&JU6iLN%ytsR|?Z;`s_UhnqH`tMf(^97km*@p^?5D%b_)r z-Bif-TTjT$WAPz4yfuuXpcKqN>1Ebwm{PXEI5&%*!L!ODBq`;7tKrByFbEm#4NO%! zc`yoZnUU+ik$J)q>Va+?n-6|HIGYUgk%)OOr-lq8%1wLA`ywyA8E@M2BTV2~dv;F3 zk-torB{n!L1P=q1Eix1I<+)9_K?nZ6ey59jPI>sRrA2(X!H4j4s%j8Gy>%B{4|2|# zogy!~EFfp#LYdhS6vkwtKQL;D5*w4tXjJ8} z+zC$3lWiZ#coW+9HSe~Em_(hOp1Ok$4Bc0)ZN~5~MZ!G*B;g*`-4_M!aA`2+%6Wk@|;gVpvU=< z`+cTXqt|Ui>FUKxyqQ3qL_>*87Yzo!Y@iIqNi>}xER1i+$pxG>1=Lhm@A!ynmVYVd z_GnC-dnL~&BzQ+mDWri^Et!mx3?13W=j`YmJco~OFi;EkxOOJ323nk51=9}bxtOAy zaK;h+K%a~G7W$J>L*=rDfUk^v@W>9Kpt*5ktBmF=iTcxnyLxQp;;0q7CY)9xfKeWo zk%fgcDCkBxAut_sH9ym=tUP3eYr2*56SH|klyB77 zdi+@EsZ3fM&5{D&XuVRPgQlW!!nbGqFf=rXVydguy-R{$Nfq`!=4iUK8^(3E9VZlC zHon^9F~atxdD$5F2tG1_aUA=7u;S?qmBd2$fK-i_d%E4pBQ~q&i93%Z9QUbcZu`E( zy%{bjN|eN z@_%;rd@M+0VVNZ-o&J;;ak5Krw~cX^fy)5R{~QhNrcg~p!&TxJiJ=AuDNzaS{;jIX zCL;sL_Mqdz!R&Oj*w8Ck)PV~0!?-D6#s(5oZ@MCAo_?=!;1G&|#4B2J z-T!qWZz*1%8W&c|d;YCtgE26rZg+#`H@!4QYtv0a_wf%IBn==d>n>QNq@=mlb_J&? zm9Zp!50!V<6zGgz%OjS=+$t=GP{hTj_`Zt3-epSBprS0mqu#BJWHVEJ-zwedR*2y> zkG1M{x$>8Olv4jX{lZJ;wW5bqf35MGor}QIf;6h~j`oh0D+eS!A*0 zrfZgQz%`cT6#ecUJ`p7=GCWx>J!r^Sr`RxlKCY>0U#J~bc8!266?W1Yu8Q)^|BlMM z&j21fJ8Z74>*h#KlCv^Sb8D&XCe1A?O)}4CM#d%KR$m&TyAOgn5rijId`Pp!$cMQ% zxFm5N+F=#eU{n-hYx1G`-bPRMiFR?C?tzTS!B^Da68bQk+dZpc1<2Tr$ezW$WPuAy zU|$*<8yz-Rl$Xs}n8~kxUWjytc}Zw2zo@Ot0C0KyOdPw}8b~ub9`Wj1^R1H60)F40 zCEloge+_r5h$BtvwJV%%|ms}O5IuwlTy!23Z*elc!KNI$qJQ-+;n z@FPsgdA<=HFFi90r>p0<#=e8-#$KLA5sjoL)YOzww>i<)+{Enlxr^blm5!7hylViD zpMNiyz-Z-pcCKm@6>h5NtK*`?%;%LH`_sUOr4n_eIQR6ln9H}!CQx6e&0I9+ktumd zUSU1OH=e`E&whio-ykNlAMJ1hy68O2lzJPhrNuNyb-a>g<2jLS(E5AUexv&`gBpiB zld@>umVu`^KM_M34-^&dL_D$~VJs2QRz%Ut={J_MHjwmmwTxdLX_rEe_)K8Nop`Hz zBs(oC)Tmz!Rd^oddv(Jarj6p%LpxP z>l%QsULud!yWY1MPjo+)B(~)ljcqOEOc{TsJ6ht!nzgUSn@K=K84?-EN6vBl{VPpf zNw%ErR1?`f^uqv;$#D&#j*Mk;`GnERW32QSA-03(u#3V^c@kZJSC` zvUuSY-t8C>qMZ*AYzv#jN5j?W@k#Tx)q!)MzOQAJ289+iZR*+ zzJ}yIPJcSjW*&P1{qD$JBp)%a-uNPpG39G9``C9+Vtmk1?dICXMr;3y3w5LIY?N{Q zC(lOhgw!ij&C>Gwz0bpzm_d1)f13FK0S!%1WT|t=-R0c|v*djD#VR~tZ?<)2H#Bsn zo93kJ$dMUJRK?Cudz=*PHk}}~ouBko>|RWU=$OISI)7r4@plY=ZQv^GjeUhd*kMru zOQTD6I)SI%o}tgS({}I{YG;oaaCSg3u+;j|Kt9;WvO6%^7fZ6~criuGm;COm(ghhN zh-Zv$aPb1-Bl0oj#L@KyoOx`0Lmg+WnLh%b{1NAO7@*sodtzn9x)c49c)Heyl82|{ zRUQn+db+mA{vKKku+i{z$7d6b97#=y5P!(-!9m+}?SZBh6EaWijP7q6&LSF^Zps_V zRd1knGBQNa;?3#?0&1d=E*0ogjizL5-*!FCT56La_cGF$gfX}8hp3?x(`1`f+i{S1 zWmGHoxkg_3+ga3KC`R8sO-Q&r83@?AdAl^;*pV)s@l^RbNF4`)2s)S zIXPQ}^po}#t*mV(D`{P3UZm^Aj$>7X$S8bNu!QL7ofXyNrUhq8AO3 z&9pbmn{>j;oLskvZv+Kl*Eo80pNapvE7JaH&;lus9fKdlOD-|2S++zZJZB@-9@xOw zSmDr(f5&UMzx(pUP+C;8!hc`}J=a19a!j(dv<sfwHRHz(DAM=mc$yUTd*!jzy1ggbE}$`$-`06JF?kn%dk6nqklvv5GN;GrNX2xE|nw((zDI zWy&<|uoq%uhA5tXMc)`2zg1bhoGO{(7{epVtTDQOzX!Hi^c4A{%T(fX4<9d5H~5jQ z6I?Ll%~FTwx}ntyH)3N4^Uy}L)L3K~y`G$oH7K&B>P#@!TpCmoY9(u=#>Om} z8PwQAzUWt|PLGUtk?p7?#yo?M7P!%l6u$R3fJwM*KUwXk#;(!aG>Oajt>WE*!xIt# zz4ks}Ea(O%_uo~83qMp=cRN&)NC^ypSKalfu!*V*kJPo>Xu4uNyD~w)Th{x8SwdeD z$CrgnlME7t5j<7>B5oCCwRkM$_#)#bz3cc6{e4v-N7YnoGIz2QQb>FPO@gogDFU&| z$1kL&*QWISRCjbJeGr+aEG~XR?d!=bG76gXI~SLt$0W}?dwesBO{P93^e4EUN_oGw z5@{r$A%FnWcqD&?>y_}l>i8&XfLuT@fq|9qPE6+Vz7w$Dk!^T%hpTj}7!mz^yj(Cq`O;Gaj&q&<)9O%rgpSn zvv!A!OZ7YjqsG8+2B9i;F!kaR?_%C%CIk}y!mv;Mmrn$PmH?+{PYr~C9Ek@HVVsfz z9K*L-ClYiu3pNsL;93Q8T4E*~YnGSt_8sl*Cd2(c7V6Eo1}`+VCPLUlOyoP%?wz*| z(rr?cTuqiLho3vZLjU_!#Bi`@%`p86N_e=(u!_R$i=I!N1vl?EFtkXKd|7cT@D5bI^ysU#LXPn zqNjTKI#$Ger7%!QWmm54>(#FiMJ{d_;Vp0q4~1-KLFJ0k!zEGr4r2x|Lr`6@k&7kK znr>%K8Da_|FtM;S@?T>-pQ8~W^}9N-%kFt><}Q36uJtJ!>pQgox;r@?OlCCj~}tgTmBM45}M^_L%4% zgbNUApx|I1s(s5s0}XiyQX?^b1Pj-=FH?BB+Qu(p@DN~ppY|hWXaBr5_em*x{jC=+ z%`4rv`k~f@!&hHvO^8RpT6|s)T1c*7+*npxTVH4AR^i@Jd_}60e&pZTiE>OFIM@w`!;pd;-;p zjlMGn{DUhcMH&AEa!m?38ClilLNzDK?6V{H> zUHi4QHI7NpjW$!Rk|P+lS)N0-ut=oV-$Q7UeqOw#|CvC}BzWPVl&rLhFTVc9@#gG} zz(>g)h}$!Q+ycCas=7;k=4( z4%>4Bv)lE7&*$ce9J@ZFxy%TBh@OLSZak}?!eNnX$U-$RG5qwU{&=zI>BUoPvya5~ zdHZz07R^$WI?&4wVN{hv7yXI!R;9-(uWyw^8y*{z@b=@<_%M-EK~#8^u(PuZ{n!>^ zi>(nJ!7K$i`SOu-hX;1ezH;a9rI1;?j!78Am){{~Gk_>#*0hg^NngpYO%;bxRWi$w zj=C1*RdA*c)-QM<@7>t*07%G1h6C-myy4@^Q!lB`@CP%p{GewqdPJ%}#`yWkTFlnS zZMAI=i-f}X1hmO=%X=LbMTKK?gsbKx_8-Lj6at<+R#q$29%Ub&p~y5fDf8Xrakd_2 z=>0$}3$EC=&kP<&Zha;SSz6s!KHsU3VDD z6N_IC*qb3bPe)gk4Ff@DCwkfCSMUpVj=_gePi-<$W>@8cCKB5R9p%3mwGU7}8xhLA z9~t8tY@+LqvVA7iexyGufbv3@_h4a?oa{XrMCa~ZD34KN0RO#n`cn@!8tEJDHh^n_ z&odW05tmk)k?i@-Y;6%$QOq~`>^s2(nhZZJr%HKVzI<7BvU{UJ$b`<>#W^N82i0J? z_i>L`@Q<0gMb4ay+A&R|QX>>&k)6|?C=1mT$k}-!JN(lievA+&r+mix2FILAG8>eK zHBiwp|G9SH)uK#|rdvNhLXneI&h3U>S#L%HE_=3Zz9S!^M=uN#&!Ixp`;YW(?sd@6 z<-5?>d#Eg1ngT>eP8hEwskFo_(6L0$FbEZ4H@z3N>7-S#5M#OLp>`P^7>}KDo!mOP zM7~K$z98%VI$4{URLzeV|1Ag(O?;b#_h-LsSngf3xVg63OHFj(2~t|U*ouWEu?aD+ zyC8WX4d#;2S1+GFfBsUSu5P!+SHH>nsV$3}exJKEzZTc-x*({MSkc8%(;f$BO!(NnDl(vHgGzZ=)DGwl8sJFxr>Z7lh6;r@+p&1yMH7zyrK4$!tw@z%xFV zeL%?CmFie=SGz0i(r2*P#>!iCd6;VRbn)d`DW6E!=i!o*z&SqzA1Ti%nnRg`sMqDo zjGsa!n>CADA#SW3JGWi-N67n5ttlRch6&Da3UFYy$u%41K5Eb%!UFk^drC@_YHDiu zSnqSFP0XT-ZOPxh>FppE*TMtWMNgT58n`7);2p36wmF9evR?)>n+KGF$euevnv8m? zQVk3Secfq4(b!Lj$ZE`1M$@@M9WG>d{IHhoFy3jEvo+x9 zEA%v*&GGq!@NnUcvAnPXn(>&8YOfvN?-K61z9q(~DehJy_Eh5D#EgM>9V3RebF&OgUH1fNjR(E$B0+OnCp0p3%$A*>i)yRc&$y_TdI@GswHA1Erm z%&HaBtG0#Y^u#VdXjamZjAp>a*fHR^z>@HM>U#KgcWmiB8u0(<{{rcUvhf9GSM=l`^h%b`yvx>w#}~h z3RYZPjNNwAJ~mV&Bfm0Nt&w7u{EBpAyomEvp|XC}yn+lqfYyZhU;JooZKb*@nt>f9 zRdGnYJFBHdF8*n;H@yI&0UaQ9pySZ^!`=9|XbYg+Vn9$xyWW~jO7O^^363q;Tb;Wi z33k?lkCxq&0Ij#E83WkS4J2H*qOcUqD@o%qYF@rdH9cy!`c%{#s*(sMY5r_C8Wq=cz(QnW7ARlf&Lq%*ZKg~@+N0onTdrZ z-{2X@7`+b%-O1C^AUZqEFsYN^(pFjf0;7Jo?ea77Wy4o5U#8`h;F+4fW614DYVPQb zR|AblUWJ8yNopqF`3I0e)dUsnFJYZ*8CrZoM#61Go#QBx28&8hxzlh2%@4=UoLr+K z4jL)n=*3s|OKUf%B;MDVE)3qen4fo{PJQ$~bocE8Ngs;jyh4oQri*#j5yw3LMs!>T zzFl_lD!Y*EFv?Z{m>IK~4m}{_d3tc}XAcO1nV4DAWj|#*awLUU0kd4&9IjLWj+L-T?d#ezSYdmc_}pg*Ow8ZnU^GVktY1jBCG}CpWv_ z07Sa|ny&jFPfDFTILLLgqy-+SS&wG0_pEJv*6=;GrMaDY7F(!)*F>^ES%W8O2eVuz zhyb5n2lORf8fiZun|9dpY>~6g5-R+NYLHUBli?c1F*p>tuEo3pQC|!O%d_~OET>+Y z_S;CSb6*53^&HKRVPfMtH7>Pj&x!NkwE^HJaXg4RtS=$OD1D0sGVv|IGX?nv?CRi& zl$NRt#4}f?+A^|kc<2b{M z9g5Raj)8*DH|?`{6NF{?FBneGiixpebRnjUj24?~bmHQA%d*RY!o^_5zLyX_A)&is zSO=|JC6ZohmLNGFHemvSa{AO6fZk0FlhCoXbegj)W< z=QgjQ%5kLj&W`2!MOparx(js^`Q5?_td5Q@^C@<96oY$c$6aAb)%Kg=BH7tVa+c4Z z2LLZ#R*L1(`OT9Hnx%RpE>%wyMLiG(AVN^8e{Imk0i2`ptIde4iBiiD6LFIRmN34fMLa$;Gg8irmGrv|2v2hASLOpL z&&0ohoR>0%(EagQ42qYMkXiM z5uXhs5KwNJlt7kUVj(g%;JG4*>Boo7=`sAs!(wy>$`b`YopWGVSX!kQmJx91>hn*l zw?xSP!=?fTBlHEe{YBZK`ACTl>-Z(Wakom)dM9ssh<#Aaen`$`SdNj?pZw+~s#ez* z655AnwD~$taggzn-pj~M+O64oT4|nBQq#`nkerr*-EYxv^86>lXE@UrC7(ms2}0ak4YEA^%~;m`0>jdt&Dd_BQkz4iv?SCnh)C_ zPoow-ZYD@E65(5)xiL7*`N~GZ)%1*4I(;rO>cN8ts_VxLh zBl0cG1C6BWWKdKmsTp*qKJDPApr8=vrf!V6#@2t$qBbGc@iw5k22dD{VP8hja2gGd zj=nK^FYq=zD2lN`fCrkI8eCyFOi}IAKtN2KoR^T0F;V1%{(Yj6S2}*Fmbg0}UDel#Mib-4q-ji{( zGV@OnxIUqDSKyg_yG}x4HZAZ=QOoU9**{`6f0^IvAbYCkO3A70pK<*q=dlK&-?#05 zKAFn`z!O3uaTzjs1mKYub%{9t(BOX!rs{eyRhzmFn8ab%gK=0f{BtleE+FZY|E-J( zRq+jYM1AOsHPatosw4$@kFlAq9u3470NwCB=xq4M)&HNt5I+R|Ld#FxL?{I>7}jEa z0?t1$B!3O&_34>FxYSkmlME z8^pLyk`0AfqoDoXaqkESzL=a1Es@oyz!Q-zN1*pNDJiL4EX9~Hfqn~siVlRv*@M2AeE8R@zq*bHsG66v6T1_Ek<7m4 zDEUL$VbC}1;2iJ?d2nM|qyaw)=ewqG&~MdvJ!e<|u))8U;y!a?Q=I^>!PJi#|6`(H z7GNc{CNdgmP;Y@viVtvMOS<>R!9oV0S<5Fm!oa!;C~fu#y`yy zcPD{U+aR{b^{=np2Fu%FJ|LkX!wp`TCo57G{fCxOth_$)GZ)s9sE%Y$;IZ`t1J@_@ zAJ(pn{{jh+o`%Z?2~@=f@HF9^Y$4qrkEnwaUoavKMM;zC2je<@{f~gjf6k)p2HcxOE^!+P%aMVj4{$aSMKetzAm@8fq}7CLUJfFrtc#IFAv zN%Pl!$P8b*l`jU<2*lksFn#X%0O%OJuMgXS-vPAY#s@5dnruG8PvB3coBi-+OIox2 zU!g7bO%4mRE7IB@(ehzT=}k>k*h9r@@vIj_wfCq%4PI120xYgbukB(%7Bs;i6tQ)umz0Epe8cXk z0#c%g6)vT|{=~!te7FtyWc!E7cYUH#_s@6RyI2tgB`Z&7$Bpl(=-|;RzYj*88{j3T zHM;L~t#fSW%Afrql(%Go^<8}DRX|4YJ0j3ZCPdJrDs7eZ zq%Pu;5E6g#;hQ7zc$N~`V7aVSvV@;DfxXRK6V6QiiOhX7lqwq^YTF>nleHl%1qB6s zh_j#==7kG6W*&K?!3m7Ll@Be7N)-?4$4F#=j=vu`TJ9(UZufhG>gAuaNBT8 z?jGDT)BH%K%o1Mbve8!b$fnTnpya1#=OM4d%6E1qW|nlT>TOZ?H9DXOhBBTh0YtyA zz^&^${#J9uU0< zvOwRa##k}}u*b2Z8tCvC%NZCL#9286!8jQh2+jMG`;I-KSzZ1h6R#6r`&J?if|!2jWOpt?#1Wpy z-^pFKrr!KGiIo?9K03mom7fNaLp#6RcFz{o;9a${i zKHjm=VnDM@M^8`M+4&qq09IE+h8X8s!wn_O;LIEB*{q3)3G{qyNP)OpT{UGydy6Vq zAe0aRxCl)9r+L$JG|)7%FCrj13O?p>X`Uo5Af1uwm*IPH4!T7s6k~#BO1rzeaKIz) zBvs9)0ub*N7KnwVz1rM4Gx*tEhZdU|^MAG;!G8JU=f0pW9$@y_fgvPQ2X%{rNf z53#Xva0Cr+3G?8_^BUhNh~u^&nxPK+Ki1wlstRs<7nV{=VADuRcZYzmK}i7t>DaUg z(p}O>mqAKOcSv`Gf^p>@NSkkdWQt)70IVQnBg#nINWJ?r zO_1H?G1|o!&E?syek`g1ar^CF(r3j>{mD9qNr0ZT^==61czuX;Xov$;nxy0vYUX;a zMUn%^|4s4O3H3D!IGih#b1~cGfE7SXun$!J=eZs)Lp~*in~zzvOmveDj!OYeY!chX zUoLThu=-r2VjvG;*y@MkHhu*}-nqQJS`oK}=jE@5>Rc$Z6;lKDny((l#uCa(Ch}T` zbDQ+Mb*mNmaAtg(RC~Twq82*%Dv6OF6roYALCmhO$vP?&bBtH^K+ZthVU{T1PLq`-!oczhf2 zV47XpZTx`x_|07IMswwsQdv4V$T#LV7H7p|T_>xM<;UWgcxxZ>R9Q}=k?)L_3fzEh zF25H@my2JSC<`oG{eWTa?|&Di_r6AR-kGEpEOyiFX1Ke;Un$bR!@8v9d@#*#BO%Db zvf@p&JJC^zHRAmCNA^U^C_tM`joKQQ;Q0Xe?ZGZ^+t% zqB<^5ELQHq_-fIO(z)p|@&VdkJTXfLE&Ik}1HC-nL@3h8U{O|9w%tBQ(~M7CDj8!j zOe;yv^TsBJhLY(24wQ@7lr0fCK8vHN8^10W>NTb2=O>)5+UrP9CCqR?F;D2P-E~5e z^VanPw_MKz=$_(eXM2S7tr&O*Zw%?7nFSP?enOSAqnmDTO_-FF zk%tl4y}i9XpvLr|oSJ6Sgm;@|s)yNqwkANxx)Q#=9!%MEr|z{kyuuQHc2a%Vo~6ys zNAUU;rEQ3T(ZN9TVgaa8jn4i-Vb1so3dg;pyxt}O+`vIa(=Gsn07R zw6`3exZri03%+bR)l1Vht4!#RDTEMv<8HsauedWEG3~)N&Uu~e2b#G8$?KhsuWPEg zXA56mpaUT>ntETvPUf^KkiP~Mb79RTx>uBdH@LXs;l$xe^Y_<@r-Q@8UBA9c#|WLY zV~cjJvL){GGIGu*vmvq{JURyXQLR`Jw?%=JhoWlkY{o4yx1AM$73EjnbK%1@F>CPp zJ-CyO+HY@5wLQHSl)pKrnVg!GsrvX4*>dLC+_U~o`}d3tBpx0WUQfP8YhsjnZj`vIw-m9><%&YkBh4dZb-8~zCL7Y>ndWte5O{w zWwegtxH(BxE$veRF4hfX^}8GF-TAZjY_@4mGci$23f>2N7Nb7#b=xaWH+IN z*&_WZLhSAP$E_|v9qsrr=KlOQC;K&1J6Mwkfg&Q(Q1#gsQ^d*U_%GhNK=K#94XE3* z)s#63sjaQz(u4IH*vFfH$i2L<_i3L#aB1+wN6?rIBf=R-h>0N(a*@5sP(@ZV(l(wI zs$AhondM6J)}K_(O>@;6vtL968Rxx#?AK3B>t-d-AHv)=+)~7S3sG)$%L&1i zEZ}e|45U)R-_QiLPyUp72y@?n#z#a%v_8k{YVYWfu%qJQzQ|x2v9+DImXMataJg$e zw_ja5cf6Em)vLgH_^?ED63{poURV1+LJ3v5e>^V4&&#zY%S{=q=juKe85$O~j^(MC zo{JuLgO227h7uq=6u8Ex_>_(Wc#I!WYb4hBjYf6OcP2VzPZGE+_zBt0vCu-$E_N*- zwjv(uL1|(uI4nCajTT&OQm17n2oN-Y#@7D&5Dl`hmmg5zQOw}w=6&fXU$J;SR&d)= zzBX{E%R$H}0?v{TpT$HkCETIumhfoRXkvbSw2X#~Yc(lvOVtJyO zUH2iI*+N`auY;!nefS5AR8OJTDRI%!;gwtCzeEK(7`8>(E>NOUgUoc%cnZ3nki6Fnim9z%84yZK88K*Rj4?@tQKKkC+v3XKEk?z7xApWWNA8-> zHu0S9OwmsmEP%*#S^wz`erJxN|H1Wz1At>rRuDvDQd05opW%u!HzF-R z2bGGAsUurj#4ssO2!W??aH64~Znq?LQGzL_^wi%cUj=`}FW=c}x(8bWD5|Xth#nw> zpZ}rtDt>SA-mvRxZKC#xdw)3zCS!uWiTX=*@UctlKr z-T(y-WmV{z?G)G2k>bLpHinVoflq6@637l#+58E9Md?L5vG9%4i)@qE%n!p=SXRUF=nh1ZcCSfEpC9i`U_Y~&>4@o#1J(17 zb#!ziFV|qc4;zxtw+%r6ryU3ckv!D^uD4 zxrdmk0i(FlcSTbzOT&_q-HNVfp7OT_UlvE0%h>0`we*=0+3nKL+n+hj`nAih%q4T` z9RzI$MkE5@LI(7loxDvJu){51^JF8sAm_734#a#Czd}7~RJIvwC1h~hP%>C#K@>b> zK#hsF<#KshZvj5FYWp|(T(Tx-bB(}P>!R%S77ttPdmGSnar$9WBmd(`B7b{rC?4qB z42Cw=-l{L0s&CY5cQJ)7&U+lZ_@#!e*ok@%u<%+c=@(znXQPJFu;p$}es8>X->G{n z;BicxEr0vKyYLbZbl-J-*pvT_oG)7ogj|?oPkzc_YiVl!j39OeQTGLr3?+z!YUOVA zSiC67xGj;J?yR9+cg8VEYWux5aw}%CT%?Ah@pkq!p}>td91S+qthU4BFTPPMe#U~x z&b6tjxOXyDPO0z>83V)1_S@Z@VMrVP{pk?He8P(l#1AK%U9fl0XFg-}hXt%ysJpxX zyt`Dl@R=Vb#=dnyr%1)(L0D~_ohTY5dYwN% zXOtRs+Dk|U8XvEh)zJB67sRRN`=i#d>)OZd{B(`~7H@F8&IKyyO1=yvQVw5d#9@?#C}nc4vo54Uu3?_L94Y7u-VH5Zl?)Q(p%HX+e}Gldw^^+3<1{ zm-8_r@2h9JO4|*mD@nCL@ww$NtNEQ4hguJt&TTymQH)HZ#Jwj~@${6S+{$I82g~(t z)&+gJtqq^C2)ronTRZol$9J;6J%=+2n6A0e)~T-M(Ln0CSr3ERd~p$s#eTEo&?SW3rV-W$eLBOFL^&%Vz`&MD*ukWRuyM9jB%pS9q zDDYhH*IvkHQlo`F;JGY*FhbU7_Vq8Lo~1-NYS#C;^}I@p4GUv+S^xPa*y#Xxf{-t| zHSkDaYw(Ud9*`}FUQ@HP$G0)cdQ@U~;~sx^0=p17m`gd`SmSQMP5ogrTZhCFdl$Mu z(+wYp0nB!w>*JAx$)iUXcVXThv$4YmyyY?Yj5R*-oAoNzFb7IGZ!Ok#R>)fu0^7c8 zqbk?{qi1{)XoD4{=xR3Ds<`-{T7aVz%)ZXMoSpQV^&C3;x}~#uFO~hCL-Z_VvBKs` zr)g(0L1)eW12sJ$lerHx)vd+g2mhsgo? zvB(~*7=jmLJ&+ehi?|4``r@Z_|IagPdQFX1(jvMn(1T z3*IlxVXUhg4-8t1xrTjNQ+}!sd;;a#j-21dU6BL4xGWqh_|F|T0s*td>iS%x4pHrT zjz9+%pGL8JnnU-g{R{ZvO0PKUdg1|&j_}@0C24_|qVAI1b#uJ)_SRAu;OzM6?cZ^5 za=mA}c@C*1s7I~oIPR(`2Am}G#3 zO_y496K+p?KL9>VEZL$k>KkvV?b<8hZFO-f#THaNW3e0={GngE+vve<NMTx7EE=rs zew2j2$j6u`qvJ-lJExu-;oG*;_t|ycG#I(eReY%48YM`aC<&-^67&$c(-FiK*jf7m z?4p>Q@bu;G%Wq>;P}t!~W7vG$T{b$>eMMl&{iirBuX71X&b1$XyEYg-nsO2lRF}#K ze5^FouO68xpO~Gb(h@_M{Xhv7f?om-Y@mqa z$@_koR)g5Z#l_Xt)!_R4pf*Kk5byTJ=5b2UM}yH|NT}TFD@&oSC~|-8N@Zy+;p4Mz z5aAZ!1JkP|g+ieT;|o2CElyXF&tT@`pDeC}eF~fkV$G(%W87+%;a9@UnNeViUK%38 zoHqU2e#520D6&~%T|W1w)5-3*r*Qee|KYLpnu3ualGXb-FJz6pj<*>Yx=%d;@bC6I za&G_VgAbq`VM1gVrxCw!c*#n{u-28UjeTV!kY8I) z753;gSbOtGG&JI!x}AYi@9)@4c^H*o;7o?frJQB?d_%=|Y9Rm#T=1aYi!9fotK})9 zG=4aXRXLwqrjpV)*?5L(ad##6eqk-Vo-x$teSbp<4+^aD604=G{mRhX{F@11CuO6) z7vCd33)KN$WjSORw5bV@n7q&a0qQ(@9y1PSGiE1$0ynQ@arMq*Ui{PnU?4Fxwm{eM3{$joT*{f~~&~ABoV*IidZ*g`_CEdU!N` z1`L`ARf$Y@JEX-?f!->y%W?o$aeAX)_XDSW2WP~wQAViVQ;-ZqdWfy|O+9rQzcUG{ z^nRiYgWpDzLE6?tSd}e~C`PT|4pG?$h)1ogIHmvu?=8(!#NDWzTSbL8!nXT3UOH7| z^L=+u)RK}08(s91gq$TJGp_;1_qq8g>jjTISyK8>Y1!F(C`c!&tO=jd$TuCJ*)?u% zb+g5@MinD`wVbAxG;DBmZ0w+`tX=`nOV*hgb0$JhqP5Wde4T+g9B-U3U5#=f5b{gQ z+T-#U>4zPz6vZe&yaim{-M)Lx{s7z5u=O2w z7jr{|zXGV`OEK2bdZ#PrU`fDEJkQ$9kH79 zf%x@D)TAgf(Gc{NBEz?4s8;0Uf^72J581}g3-yYgG$rIT3_b(-3^#}-;8GHB-bR-1 zlP_~$*$btvMS*3^xszfv-8>{<$=)6Puy^a3B1xB%$F6pV<7czyg0x~(1P{$;E@O4nS}@M1ri9* zpL$b(67uwQAUI-sJc-u%C=DQBGI;=%V|Uu(8jnMiWNqGL=X+J6MRuiH==tZx9mceAM5AM4P!eie zyqI}ztS^sHURl|9Y|~L`p;uZ~_IaKNdTwrX(4zS%DLOpb@lGoEv1P}U zj4pg}Crmw_?(l z#Iy0@oxmq!1*TdAYP}{C_i3~HmL(Tt!VEc9*55~REk{gZzK)1SBf%g5uFaHEZ_Y>& zbl{)H7S(TX_3hOt?jiRdp)@kiND-;)oqyw|ltv4{ZRS^vt#wLi$N*Vp_DybM{wFN$ zbKeRG#raov(Ep-g_#0v&_WvXFC`axd;*=@Ibw3v=kGXGuN53DS9MXcFR+JYGLCn$s z)esvyAF7i6{x?2#_vP|&DMlhpO7izt8d^&Ae^ea^g9oCAhd?n?Yy%&_rb1WAMgJ@^ zQ2_sUG_P7y10CNVxVJ`PYi$1kr{23rhn=0kZMg(CAJYUi4g1I6#>q@dAMZCCC$ak9S^@t;+GFnT<9q&!f2Fx(74^%r{_%DG4?C4FH7Ge& zlNfAx{P&3?py%G-F{zF#MOFq@R-hoSc&#?mi^OxZSOYtbL-)4>iJg%ki-o36T=&lN zXE8m0CMR1bCyCE?FIKnQg%c3-?@y&w!yf9?UMBjdIX7h51< zc@Ja;K7P_Z*2kuD(?-(=fxN8STM*OWwbcxqV3}!xtoPRh;m64VA)6(L}vWVg1k*>!gKiJ|B{k1ny zGLK_5RS^&u#{ksu{KJR_sQCFQ0LO{hpM1U<4n2u5LN>dlK%%yG8u;BHc2p{Inp^p5 zX<21JM8soZ5nOZOCryhPrP{h_9N>Eh~<++K0JH@ z+QVRfAlg&fuJE;>pSd!CWu(Ue^fq8u3ssgm_cb4Gk&HBVB$Vg8Egi4aaUm0S#1WdB zG8_6qhgAI1jhpw1`v1n92(a_G_|1v`D|F6C1EegseDO2kaNiqShXVh)lg(c^d2ib3 zL5ErUFO@QvGxl?lnm8tS&xS83zKwVy_>hn=HaH}#??kZR{{=YLP^jL%$IXjjPHu?c z#XFfflmk% zJ_!CR#}X=dJjf4G;Dg_9h@UxAdtEa*^(=#)c`no4xWN%7f2NV!Zl8k8W^p;O8*uOX zC>Rj@!&vT_l`0?Cbod=odhwzhtqpt~!7YC-Apoe=EJ56g8$*;rGy)+VwTWM0>j-AI zVzUAX%Fi@X?bF?)aUAtTjBxYe8j@>D5wR$WMhH2ZU&X%snM&hZa#cYm3G&uuO`!} z5!qXOKkTuEmhaY`nN?P)7ZiQ4DH_)n)tGgPs;gX_!+Qg%mWN2527X%p(Ek_>Of$#To=Ywgr5qYK$4 z7O>?!-J9>d@s9ncggFA? zk`a1TRMeKBGvD>mtgy?5kTSG;^>0y|`8t$TaVXKDl}w*^A2PbOZ9qW4!u7=wXf*wW z8|LqiB;>puyx19Lsuhd=_uWFEHaJKAlBpalnO@?YrvKrtN!#|%db}P6dsUc`4iS;% zWGR+iS>CV|pSA98`cLYbAGok4XcMDOF*D>OOtY|wVA8%%bfS1Qy=@XgLV!{Wi;d6; z2*p=JL}9A|`x)g96#IbYjTUvY_Z^f%#Qh_Xt9|z$Q}<1mZQ1(zfExw7Qr0x3Sx9t} zwikg)v~o3#5o*a6P5NVHV#uZCspWJ7BsMlSf#0rM@#CdQCJqbFg>82ZF%-~$0dz$< zdm`C`UFys{A$6cX{>Mo3_Gr7NSLBkZg~Gh_Ve1-#^TluI^PBzwZg;jC;`k=ds|-JX zr2b^Vh`17cd$ctzBe%64wV_H24cDu4_|MP&Zts2~Yn-Kc?!#|IoOCNKCR#zm-zWrj zA9W=sv&}WRA&Gj67Q>w57-Ly9C}uw#cU?B$4sNpOq@TlEJ*~;!V>|HhV3G^Tv(;#F zU#kOvW-Ec=gUEe9 zdteHihi+x0kHMU4eCgY!b=6A(_ zkZBK}_bMxmW;CExg$I_Px*&awpJ2yYIPh>h@0Op1xnT%KVku<{x|6Vq?a^c#vt4P3e+>d zc%ast4_5CkIEyRU_pqF%28ctWNJ}#yBcCs=e-_uiG4bnd8^#^(=x|(uAEA6AmK*hB z$?%ON<_he^&K7bNP7!hs^Xzg~6}B}VYGPa?~Cm zo|Ykb^bW_fb791df>G_??okBleKnjxd>E_p`%yF%-by*C$D~=?!CwzKm+~RkUXhDl zP3P+2Gy{^F<5Uy_Q1sG^&yY(Exi(1~s?};arKk;ylU$-D z8z}t(QQ=F3^{1Z9Lw5{e5+W31!!AJd0e|B38`ELB!*~ga=D38t^GBl8{1RvI38#1xuDE^jEr^6T9egd@9 z(@60@MuAMVm@jSftVv^CnXj=_~o?X{AvSC>XNbeFw}G5En0?@`l5{ZQ~JtP;!6NsmymqdAW`Oi@@?OB zyLhBb*+fDN5U#TWs{f2GqwAWz!J0Si!t+moV{0~^Pj0Q>jA^`dY}0x;^Wg9MD2DuC z`}7lhLmEoqiR;siv>E%wP==hz`U;~a%YU9vH8dd+O1iFJ)Ww>4^dXe!I8?wZ5D!ms-U}3i26Zz~l_gL#Zdd542R3Cq!1) zRn$Vu9^=*IQaihP;4EB|QdJ2<3=PKKCY=zbH<#m!6wtN)(bnlwoi}Ke)LH>A;d+tY zqt|xtlvvrD@Y;rDe7Q`+7ewALo$^L?YJ|TTQ0PJ{&Y=v5clbk`%p&_{PPt$5O<7j# zoT10e2~pe2FYYE{wAga6rKhr#pSlQoI9kWtN=PWYhyqd-64NYJ-U^!-9fqK?Ow*;= zk*p-?o)Q`#uyJ*be_70VcaW6Cli8kz97pvUGa>-hgAF;f4)j8-vZ)tpM-Xx7x6aKG z60Q=S8}~7n(zOBl+Ah$m3tuDh`No%T*pr#Mu)f~%dbVJq#BP5O0>o$@#Uzmp4OIdS z$h(I*14*6D-I47}Aq<=2#UVdTdW~8e1oe9zRYd={pK||7V}25g@?6a|WNZI|)nmpS z>u=t@r24&bIh2bmte2~_%6{dOtZkp=SD)LU*LLq+dHWmimwrUygiTpU*rK494vFyj zg|WpXI`$*;)7!H;MU3<`XMu_a50_!&&TxFq5kZPoDf{assuvQ7*ile)&f52gk?tXa z7h{>0+(K&O{cbTtt(~zi>3dvDxxOx)lTuknhQAnly;{nnK-qdC=o`eAyd>D#Nj>w; znrAH*HB#8nZ;K|wjzjFDh$6SbI~&vNmT>+a)Upa`FZANxcB2{@_G&6F%@5PTc#es) z5pc>rb$f&rHvISzW`whO46Y_SVdMlE0nZ!sPw?T1gbXh{Uqou9Q4|drjLgHsv&>)! z_jqFrgU?*m-yw$C_3)S}yDup$1g|UOP5Z+d=Nln@>$%X3pwsV~Wk&0RwJd?yo0KTQ1*am~tfH%GcVgfCO^>ReMXPXN$21Y_=gw$QnN|UH4!3Y!u;^ z-i5@62^6Sl2f{U>bAj34;@MKD8z?A~3sBjsgg;BFCws9-nC862EQ#~^mdGlAMM5rx zh{E8jX@4=j7jRx?hxqWuq6e)w%?w%v<@NKXrS!L8dejTOI`V~#JB3SU{OHLF7lgnN zAc)tl&TJYT0fT z*D|_+@=(-*opbr zV6(n{h1m!N>8*Bw-thV82%aTPFt82(SjN?nKG`wce|JMOX{t=4R-#ndda*5kWDy2%D9G3d zI0+CQTYlxrDN;@tRBaJHP#CRjdhfO)+NF zi!kzcHA2D|i4DP4Q+}TuoPz%yMMWmygU|;U=`bye6P-%^E0|_GCar5d$wc z?vHG^(W^zB3g;K|b0UWN^n4o)LEXmIctI<2L`yBL?UNNIEKm3gqu7&$%;};TTGbvr zW9NJEf1)DcaN~ ze~S@MG~7&K0fM-Nz-Anl=Us;FB52jWOB7FN8!lOWPWdIu20pIiAA(5M6q zOzCuSLRdyGnkRX=T2J4O3cX5}-4h_BQ6CH}t8RF=uQKF?=7mp5p_$dVZQOM+;T zr(r8y*Yg&TDtF@VR)Qvx1%t>GDIU1XAcPWfr{rv(pIs_F%>F3HEqeD2`RcN3agAr{ z)vAd-#ct+y`p+-F3+!#VNHpZr#Vm2Lp%B^D6IxABqh6vSn=nx5n@r}vl_|AI(xVXo zM23FTM);#H<00U}Hc;YV?9xl`itcL)HFb0>J_7c>&ET+m_(gT zCmf_Ll#n%`vjwVb|0^*4mp%CK`|e)-zV~Zu8VT(D!X#h6r_i_Wl>47rfE(-tD&CZ! zu=YxtNM4kQuTQB6VP6g2?$B1k(L)!Fr%>>&hgl&sicgmIiVsN88R@%U8oR~E;4XAd z={a^1W2vKK4&kuM+lt(DbT7X{D=k+F{Sh+}F$RIv;R3D!V&e-DuAX=%xT<{Qu3Dx6 z?ykGf1BORkMm^qL#4sMTxdlY)_vtceaOAj6SIS>&v5b18BoVpDQ1(VV`(MZ2*6Gob zUJyjq2KrxL;pUa?WEz3e#;cNe3br?k^sO=0<|9w2*+3Xj{sbZng?O-c zCzyvU)^r`n#2yLOng%~YV;4_Sdx8fUr25yQ5?y5~Hv19_mx{WdZRTa)EHmi@qDHgm zV7ctgs)Vtt!%I#`;TPK^RhCm?esV!)3~G}9exg4K`uZzI3^uD%^UrnPLGS^aX`op~G=R`|O#=_#ww}R@OKCX}debk8n{p7? zt}nHWiI~U-rs|G2F<&hXthlD4W0qQ_q1DISLTED%H!de%1X@qHHCrSbL~a>d?oxsc z*_91oR!rcDFNFsp#}q}k1+$@;$RRk?OMD(1GD;M*j*MK5{gjVP6y`&k{J7Q0SQQ^j zBnIyFl(ImmGo#blfozK?43DE@23NkyVmZjnYQaoqo)Q-p$eYMZNc{=>J_6sFZ>Bui z923uwiugg+2UxrhKw%~(nE+?+4>vYWw@sUM_%Ie|TUg9DlEWFeKBH7;*G1Hs9zH)m zzzd8d^JgQyeR*-XGFUzTlvRtDQmsJ9VP)dZhUQD=t8QBa(1Ze%3;VT~_vf+mz_)i&QgI0DHYw2wA=|T&C z6WBlq_2S_0>M}-?Z%}Hp1p?!93DnXI4RdP_XWfa$Rm~ZG1g9P4I5O_m6{1%0y=a(v z;Y^jZ)H*ym$vPx`O`3{O_bXhfqaVeu33WVS>P$*@Kc@I3qIf#kS{3R%$UN>TXqA);=v17>p>S>H@nf1*>1;;X7oEs#>{i6=F^@(ci ziBV8&Qns>ZC8H2a#mtOeH&F8DPS6V}W^sPPmEi#_#>8qk;8dt`in(o%$@pnHU`87g}+d0Bothz!u*od2U?B#ee@ zAeHE=>eurOIySJx*Q zS|O`AW*Wr5S-~H=G+dZ5DScZ1I|unfj!x{?i>(@~PuH+wIkskWAPShF{YtiMwMOB0 z9v|1YlEV9AzADvbDpznGRBn8R^kYV@ekpVLWBf432)%F_fX~9^os7r)p?j6ESV_g# zX=|2onkxCfKb8+5j0T7x=1w9r8WLpygJePQsfdk~Vb-|S=otnfgxhwGKcz8iXcFgS=tHR`f2WmqZSNuhu2I;>^P zAtcDuw(6umu$z_6jS-bEhjcB(rYezJ#lCseG%8A;s%dmZ)cVr8$@~vJjgT*PGb)q; z)nfXGF6V{ok=6r;5Wl70WF{}UZ5JYl=T*n_2!F*xQE}{EtrSfCtoae>0TyKWPgWTO zfgA@c2vV>r%HJ2{Upek&7VvCdgvK#5AYLj0ukdz&D&n7MaFAGCM+Uz9CFaO^uo%UC zzMMv^F6kf1@_V1;F!=#mv4^z$V;qDVdhotBuSK~l{`>p-ucQzoIv9;0Kr5RI*H;+K zE#{G{Bh!DG8$K$S8;W1T6Dq_@x%;{Glb`(Orz)wUeu6VI^SoZ7MXaS2^J9sFuXBUB z{4GcWzc6xwbUHdFAD;yB@qO;&Ch6HG-+z2+8YARst6KEC#4cH6`xhV?&Ym_z_cu%Y z>laXW0KNmGwRt$|IwhFqYe`T4Z=*5ZkB07S9*w$=eLotH#;D4F z8IA6KZvDa$iKuVEf)rc&r5pXX(ai2gd*W^$kGlTIw*j3!@r(K;m(nKPf2^Gk>N=aR zBGm%tGrj;o`Ip)~FeKn=rCZNz?}$P`k0>Fw2X<<>o+$$h58BQ9Fx{>A7fi=KX? zpIq4W0eS%J#%*hyol*b0u=Czj@vY@d4O)%+NoefxaG@F8rxj>mv>y?1j8cApx`l&I zYH{Y6#j&l4&4HU6&jTh#RIBOAkxh*HD(SNn&Mz{-EFJ~b&xW{9TTSAR zWHYD{|GCh_xCs6@u#-(bv7l3O!?%_nb8{IrH8r1dK_NsOb*(o;{=x%9Q3F8^EBR(q z4JcS-=Z_SyVw9@AJh$hsu;~?UQ8YMorCz@Dt4oG@Hg_&|VmyuOtr_T`=AwU{5;~Hj zNDZX@bc+rBrEtPJLEUclW%+)oQgJbeEi%*!#$7LuxxN8XtPf|_G*nb*zkVt6+6>{I z?ahjpzw0~L{-YuxCDl4V@MsT7H0}M@Y(^`Ku;DDZrLv$$b8gM8!3)Ow-BW5ZvR{nm zKz6gnjH2Tu7rnx_aDv{rPl zQ9imZ;*Z0s@x~WP^VSsr)ts92EDW}KnoSj65M3>jD{~pJ-d!UZS%a?@ zZGEEa9V}BZO>%jTjgT7kt}pQ`yyqy{@SFhLw%3B$kiVrWaPE)!ak7sdM)Bpzll#Vo zyXhK)vL}A8_?WWa5yo@gK7!RbG7Wr&fpq=$EYU!!+xK`iF4TZ@OSIxp1qEVmM_LNe z)0TrJ={Ga^$oT@#@4Ifb&lCnyJZ-mDojkBEetSaS?sIBt5lDL&nZMP1_bd_A4OH38 zho)isCZHD^cSlhR3X*`fitZF|XRxSg(gS(`RlFgEJ-E3K=|AP6{nHe|9{t2aXe5M< zTwH^w{&5GgyZ& z09iu;h8p*?+gP2HpND}cp4ShaRXY$18nLREX!!#52v*Hf)q0DP`N|-Li)UUue zr?|#(gWA$+=S`I*NhQ3_X^9?}&)(K^r14RlznqcfN3dyjXbTCBMiAKc`AId&WUXg` zkaj_|(|b%ZVPx;(8(op`dx{6q79!m`5C57PO<*S3q((sW-gfca4p4YH)Yef`(guzL7>A`dK#t56itE>3%~iDr{a2as#g30>`cj>fyebZq^k=TH1j$5 z6=>`=aegMwDR7y;ZQRtw2MiCzbTyNQ?Ev&!Sn&TaH@g)2SAEC`$ zgOov<{l)jyh0Q4l5qpgtuH2DKqJLPKksR(HE?c<4_Iz{P)g3E?hF<*g6MEfT4Pf6J zV~%f3{~ik5#|4sKeCE)L+k)Xe!0E2H*xv53?L{E47bFrqfwt&xy_03B(XMuW!6oOA zB;c0BGSMi_@e$d(Yv*}hJySljfuG!WrKkM-^zEd_Q)4XW_}3d@esV{d<{{KuEaVbT zpMwuJ3Brw``i~10$G43ZfcIf95>KaDlc1i6dIPB@J)ENUwrY7W0&$8F_pY;?n0s_MMJ`^Xp&hbs^GcNuYumQzie(a@-av`>Ech<)fj*lmEf?~G5qUt~jmn7SmH8}Z zP!|j#rPb9-v_pSXatsaMBQQuymxAg6TRS_+5L~*7Ski0)n3NHX{DqTHESt_pSg3t@G^}10D=i{safhruxgV;sUAzv;t+^@&5{Jk9&``##-FgI`a_`bVdHmW&F9q zeF??!l?Sfh@J-+6Pw^iI3_k~l$SHXfInASwc$_I0w8lL=S8gZ^ftDv9za zY@%v8aPjDP<)9-sRM!vuF2^s%vT`K(aoMPg>3mnb+V@wFk0In7hfIAP$$rLCC)acT z7MiOxt-)ulk?$INleV_Y3L!8<&=rl#*=Yb!KL7t0*!ml9h=d=sv01X^>I`-N3=n8= zZ2~?Fz+%1F9U{84{@s87Y332u>>a2Xs<)p>v<7jw=(J7$;E#Jdm zr_d68)nRi9-H$84Y&55Br`>BRl=-a*!?oQ`z!9xQvo-!0O{?)a-suVLz0=jgiN@Kw zqEuyzf@kBr@I#H2edbha3ySdKr>yOegUiBz$`nOzYOlXI+#t1j?J!&_2LQ&8LnZAi zZT8SxvNAw@*ISG}ldl2TMu6jryK&EAe27m!7l84|Kly!5|8<51~vV*ej2J^6}U#`&Rh zMwCCu39!i~a~z~pzc1VeA=9;(sF#=+4m{D!Z{({9RIIdcQL!Qb^DSo2l(y-8IZXyu z7<@6a1@4}(H9UQxt`DC*$@a;}#0(du}Xd1-ke zOl@uAwF6dGR)$7KvDVe?+sD0sYM(%nQK*@@m*_&r3MM{w8=q~fN}2?V6$9V3hoDNH z!~KB$0bepXlf7KEX1N;a2jk_7VN$hZddn>+tp6b+-*`8*_pzSsN-*QS!~9g| z{(8euslpKP-ioRlV=<{0CKzQyz%F@~t6l~xW{F|te%VI$g{Sr*st@sM0EMpHUr%Ac z>-%sCmEC=pecPrtZu)HBJx7tZwb&(XP;HT06z|Yzqg{Tv6q!UTX|)> zk-{`4^VdhCNc$iYp%wepPh-~8fjpu}Gp`VU|L?!=^n0q-ES(g=q6lOSg^-OVVK59& z)|0>9j5Ih{VCq&iTbD$`Y6-vI(zQ4#Y2+rLR}ZX+4gBck$*KGd?_uJajbmud`jD+k z^H%_Ze;+x3$Zpq~XFK+P+{_k@`yvJF9FTLjc3Z9B$At;fWnUeFVFsejY5(IoU=fh=I`lEfNy~jyGz2uf_QDRBMApMeiU*J;| z{mROxU%od{0qgqDNvK~anE$F_MJsOL$$avcOPsd<*VlJ-Nj0pL2VZ~ntCTqMvERSe z`)e_BoIJg_kr6IX@8sErkC+Yse~`Fma@xcd%EW|bRHnHFv$SNz{rW{|rU#uvujygb zBLR)PBqpD@53oN34gZ_EA;U5&q9o* zjS)ggn8JSH`}jP95odXlKd(Oejw79VWM{64+uwj=@XLwH+B>xgX1yAUtYMuy#P?U^ zvtJ4B14%y?jNvt;e)9}8y_)eRhy7i>;H}Z); zDn1orAahNMI~3^(MWtT7Pu`RV3QfTi_!iXc{lL=F5(oE?WUrxdDBq-)Z1yn#DTvs0 zI~R;|IBix{fiJdrFk+e%txOKskpo`meaVI8rC*kJJ>CLRh{E@pcT3CfiA6j(0bx}- zwApJ!otB}ObEA8&%<~Mo?No4Lc|0?B zjnrI`HuNNbeeFjZ>SS7yDf}tXvP2^)*IId+jg$4-7s2VPUVkAuYexv(}Yos9@}!z z+R|%a(DHfajS6f3tW#Y>55rSb=d4igGFe>_7t@^ig@pysc>ekfQE;cydv7x0h^W|L zX7V#mn51?Nig3z!x!b#)A3_q;TvC`MKHxAdHDhS}7?CXEP8O<=tY#;7@JMFo00t}f zO_c~3*&k|yU_XmJpgw-MXSd%X9<{5>Wx0;CuUVt~&G@#hvy@#6S2X+%u&2bx4poVp zl8IDc0}=7gyb;qfkN5pv?OTULJ0U7-rDyOr@`;CdvayVj*e_B?Y0l3&-m5Zd5CUEd zb??<>C5kNNIsC?>)7B)(-l1|GqT{U*q}Eh%PWNkC)jU8fkeUX9e#_4vQ)@-LoTHMBgAzQX+jYKOr8Km~;^G!)vc4j$!M~1{D<>gt_ z0NgY^2sfZ}VIhashZ3TAg1)MtgRjNF69ELNgFmPSJyW1MTDX3YF-B<7U$3KjTH#B0 z|HlsmY$1Gku)goH%zdWE`0?tTzdLBfysxo$o`;m>CuG?k4~*k-80XzQ*E^c?Gh|_B zxA^^gORwV9y_T3#MF>QSjEuaee_`Q*&Y6<0sVv-C9m`m^Rw`mvUmnQ(>Ah6#Yn+x&cNcue?x zEYA$xx1mlpHa3I3yW%CUj+EBXpU>}3g0iuGaaE@Lkdd;+h0b*M3q}$b+rer0QKq5p zcZZl#WOoM2d~KrQ0PTJ{3DL@6Lv4^E2|ZQk-a^WWMKq7`ZMExjh}8U0D#Z;tqtZsI zLq)w> z175-SdQ@^Q$lHhpdqkq8XkN|ce!kX&D8#A3U=8*nLj7$v(Ole^0JUoQ+bwlRP8KH9 zZGBR{@QxD!1u}?!J5%ys zYMkAT-2PyBCb?0O7VEUU>qZ3dPR4ZiqhnqJ5!)o~5`zVgmZqWG^sFqh&oMUZI+U8V zHAo>FGFSn;xw(e958(~wBlVO|u?BMo9bfJqa#ru{H#Di&>(yCR7yxSs7X_P~2lOik zb$Rjr+agDvemVYqbb7*t4h=W)dyMES=jGT16V)@ug`i|&sv&Frx{s^LdCd%cHSx6{ zt1qH@&%9XH5W^)GQ-11Z?V)cBvW8m72O^dcJ_qO>Nmc%=gjO~&g8U+n*(aU6nU@wZ z$RH9JkNZi`nLhYjB5i>u;vMj0eZEB6x^9Ei&&%R&Z%LVJ^jwbZ)NtAZ)$zK(oH7s6&4nDNo?$$Vwc}2aH|W@EA_@#0&u#MFNz5FC4tN{ zi5PBB5FFjvd57mD&$d2en1NjVv8LsX&St@dC-6Ml<*z*TLV|*GEP4-d$-O5p#IVWz zZoW4V&UIF({Vu=Nm@f(TnTQS{6A+)xnIoYq-?YD`0k@-GH`sSw1|1+SyJdp3=fdaf zui_FDEt@kL2)tDTS0YC-+8h$Y$k+(-D*5DjX|NkbRp);(N!}nprTBGB@QoUBXbdP( zKgr-kuqBa9Wo0%OmY@aGQKtL3h*Q-IBaBDaquc++0ti6d-c~>ic^G|m>KdLK8egZ; zLTT}6{J5z%pdXqg=j~mh{`y>73&uq~ z_RNo%75A-U+Dez_@8?xZeAoL|Ar9EEoQTU{<unl_|`yfe#BTS={7B*v)pbH z+mYU{?bL;AD8Kylzy(0ve=d>+6(y;@mKu&fB=0FH464lNoWnxB($Jg*maTZiQ76)}yL+_fJO;(>fS z!+Ee{9&5XjmCF7z!^lw2z~ILik45*7ZOw*Xji_JEy+f6j7v*K=+Cp+H+0EH+0gKn;dFylbKra$v%l4S*WoIekI}bdbXMa3tX^)H?{Su{-skiJT$A;LKnR&pY zk@lS$xqv=zj$9FpB!?)PY-;4*C_JI2?__6V(#J{iW0ij{g>?0{TjNmd-4=)ot;dI> zhsekE@CbL;#N#cRD0(%UyUs^q>AG?}q5jLNGb2Mlw*(xls z$E!s6MW+5L_wjU+DStk38%`U|N!&xn6OI*y&a?5P$h@JJ1Cdha9+2H@WxALqhZl+0 z6ok8eQ_$eZ=~e5bWLJR+{j0Sx&QatJ6GD%r_egmVwMK&m8)ygZlQC~nzi>Q`N{(1e zL}`XMHx1G+6hMB4q;*2=M*|Hx8I<0kT00QJtv8}k=1-n9lHt1*5Yg=g#iEvFUR`7J zmLbSDcrIg7QvH3E8dUn6b22Oo`_NuuBhF{2z;Ch6^!j!1&z_#!C0VLdE!NQj3eIhL z#5@_lWkyAyN3}x4YzT}AKjiC|Uj!8@oc1IxtG`cT>2{>HPJT8}7j zn8c#+%8tiQ$zTpI5{^yfB}sNTg$c9sIf*bz`ehzcbz-qzw+9-`oZDL21Yed1>zt(D zcXf3Kafn(P8T7|J&%T33JlwE54IieME6RB<-T$v+C_c?KzYTwzzfTFZt7e!`O_{A=thUmZ#4);{^-8Mogm&!bmN zc0lLr@faC6T=zmD-~|C+Y_IJE*_=~H2Q37k3n&gy9`i{we2|Jp-X**KYWz&~Gx1hm zt8%mx)|N)2iqGV+d~w;a5EX~^r1EIPhs~cHi*H-xe7OrurR%FsRMWqm?~Tc9-7UID zuIRMdKF=50+xd=R8Z*3=^GlX7)(&(!2CdgHvn6Aa4{u6Dg%j~r02P^de|7JMEDzd% z+sDt}-(XB_wUqZyy_Aw?*+gkb3E#>s%ZJd4?;rRuvLh&6RKh#V1K-DTSRHhHbpjvc z@-(8w@OXzZ@3hFwWCN`R1k3B&VSwPidV=bo2cM(x13{I9se%jp>B_^I!wvpto3iT= z*T^wEIOzOwTf&tfK(2nVD^ol{C@0mn=;v{EL7;Mj7{gsW2Ux+6(F5t)rP)9>PEP(} zN!&b$96q0RDgegpR}Z^wKIfgv;HvT%<-2tGRv?Dzw;lPj8$@1LI;~L}*Z2lq=dRN?I1^tDO^ev0sk8Jv~jRZ}wbk|c_ffs%@C94>$e9`~XGHD~RJMyA&TdpdK zubVSnkd~rzCcdu%&5n?J5bWGM=i}`I+Uwc3CGhi(egz`$Y4{l^(Eqi$ISDm=eEciw z#4@4V%&=03OZ(NVT9HXt(aSA&)H-^>!X19E;~OCP^FAAFs@>4>wA74yAYVUp#{q^~ zq4(ct;+S+2c$ek5^UK#NFjxNFG>3!swK=U=_5ro#eCWx#Pqp_R&X-`~dAKF1H4pBs z*4~WVAZfj}xGF5i36ES>s$4_XaxQUk^Wh)*2Hi$)7!injXYA9I59C=eE>;-lK9z4Y z2cU|DFcP#i2(lOGJ;V+n{@81p*n_VH@1Kea2?yGyB;xhl@GJcub;EDs#{JlBrDatP zVs)9V?kocvA)QDY0kI60_rPh9D;d4~4^MT#9n#F z(_&XGEqm$Ed$HKQ^Dz%mA^R~rka%-ndx7hlR@HU(P60_LW<*@qk?qd)zMLl%$v~M+ z#GIS!a;tCn`sKtzJxR(9T{uUl-v8bR4Yg&6su>}iSdN~5KBRf5cB%7K?u~sqF^mGL zg2GqzJfIj58E)k51*h|YZ}VX|dn-RORFw;>9oaiiLn78j?{nOkng|MUUp~6snNs)K z1nITAJV9-tc)j;NzwdOxp+ldBgQffkb3Q6GEbIr8bkgFYqaDIU;X0dzY8iJH$A_YNn2-&QwDC0 zdxmb%vqpP0Ks@hZ1p&?36@q>ZOU@3PTYEp%XPRt`nr;?~Y8aMn%#%3`I#_=9`}ZSv zQ24MZG8Of)iTt$IhFVGA(3(J6H1NH4qcPh=^$D7;CNB?v@c+JVaa}`5Q1TnqL z%Zov^87j?kxKXdYYO+Y+N_|j>UWqGh`R%QdJsO*Om!)kAA#iTA%I_Re*Ee8MDT~GL z=Xbo*vIgaPqIv804kE@$@D;{7_!0u&ztPE!P47r5$0{(c3D9GDcrw7t?>Vax!RjU1 z;A&^4DF2jY+G57c)-d5D{;_B~R+2|GZzbt215iBkJ_I z^AzHWhxh%n=#PdDp8C(hNAM?F^|%BmAbBZ-1=vF;uOri4X2q5p6eSh09gcPiGjpHT zNPUcHRe`EX@&0H|e{8VI%Oc+S6o6h}ZZiFUfl9mRG34avU#|YbcL^C5sK=kF#gt*i zg+bVwE=26>)yW&PTX&bcADr=g?0!^24uDbviRF%^d@={AH0+3DpHamX=LIh9;;{z4 z@<8Q&l2PHvj5i4PHMW+K(ey9Mw$Vi;?cMUri74|#hVjZ1G+4o;oMjdPI6xF6IHQ@- zOd7}<+;?fE2>kN(ao9r$48|wFyRgFsp0#w!KvPSoep`2%^l*LA_+4Q{F zKej#5X7K1hjjvcEW{{h=;CtZvn&(y?wbwVs;l9D0j`a=4(V#{NaHrO6WJ>|xkOzw? zN8tOpspC^|dDWSrmvfjp1u5A_@k%kp!O8jin85&K?r8Hc?3~WoT2-;cN)_aQkhPao z6Ei;yZzyIZG)tU6{(iV)2n8|UUvtX6QhINlMn!mwZ$Ru;X7Hmj&6-cCA)kCy>l@Df z%s=wDS{13(wGhJxT=Urh>6<)k3^Em%u$roxn@ZDtF9oK5ZssE0aj>KBAeETf| zEoibABQk!_J8y%rVHjrj?lam0+tv>ZGxck{#B0en7PSPaqtxGb6@9*)oD`#s=XUQ# z4veNaRsZ!MHvPR?#}AdiKKM+03%gyqrnzIY=E3r7$CPf~MT75&)P;`!1I(z+SxkDx zl&zxltQ&-UZ#QSjHwQ(V3(HTPu&CxxEfY15GXCM;Ww5|_fvxK*wsap_fqD{8nG@ExwaCRHW2< z3V{Ek+WO6&lD=tXx z7}qp>OULX>jW>+@$;bLxIi&nsC09Qj#>N)E(9XeAUwRt)p*2zXTky5IGPOp81rua@ z?n^<*?a6^p_0k_AD+aN?X=|dVKBA+GP!6p4h!I4bIjPUdLg!B1_<-*Vvkveuz=(fxry|0Ogj24t< zkjhK{YzDy(sV$&qS69S^4J?j-Nj$!_?}#0X+fnLMRjn>dDg11sA_+^kRvk-N7KCR- z&>xUMoGOw=Yvgwhy+&Qvn~J_DW+XakGl9BB4(8oGmftip2tp0@)73YOziF2Q>v+)9 zo4kJgI?YDg8r0UX^f*}rVQs9H)y`^aE#OV}#i}NL{fMV$SUJJB7`fWa!k>lAoOE#DyZMA$1eXOntI1nyA7j=|D|4qU9ApYSthE|eNMu4eYn6u zl%MhAXh6@Hlndd^Jn#~n4t*R8LRL3$^Skzu^yO+vDqB_ZXy^s5JYk^U(o8ec7l-X| z@kE4}Hxg{d(9k%An21GkYh{1M;LH^!N&fbPfrjDNeQX%b$`Lyq$P%>~X(=5s)%FgZ zpYmZL-fR8((CdZHnLu1f91Gp8=SjrjGgARFCbZhI;qYe$uR*rb-yyiW*D!coe!c6p zE_7($7<1@bef#$9uif~P4{>p?wYg(~SCmZux=2+Z_u-E9slcA-tM$du`XC$|d^kvn z@`t1&e|R|8#X*ewsV`$&w`rrFfJSctmp<-mG-JfcMn(v#7TW>)*B^)Q2w`DGp3`;o?z<|hN~Q0O-_%H~qnaS8Z!TU%SciZA|m{2_3Em6WhwuUXns zglS!DvL^fGOU#T2_}Jp~?ghoyowq627eN;tD-oaNY9tISq@7N0fJjU!*5I+Kp?JmI ztR7te{XO&9Lb-4HH8z#yuaLy!D;g^0iZ32N>L}W0w=~j)a@fqvss8iJOGy!?mQDCVCm5sYR## zr$XmWIX}u@e{b6{@Vs7HPqITlp3nY1i|xlvO-D0p~~DXk{#i+ z#s-Jfy?LSKi($wVzyF&H7iQISJl3Y_P{3FpW9U>_6@dj?*V9x5@{|PKhY1DqzmOR%cCL!_Ju-t(|f4kLjq&V*z8sVZQG-GXlOUwF6mKD*{ZyFJj z6ciNU7krS1{K|yipAX2_WTw{OACv6jm)bnl(yeEq^opV;BMTlvh!rLqiTU9Y1?aZi zvTPj+dM6kwo1);-UtlQtQ;Hf$6o}E@3dJhfue{7pSh_l|w=bmd;iuLryug(qi&d+G zK_3{{W4{=sZ=&zO)9=vcK^-}-EL>*xVL|YYp1yv%LvmQES+ULg%Yc90`p54J`nA0M zN_b@gfi>Qg1X#~C3Pjjzdg$!p8NH_3BK*{orc#EvCB&bNtb+LN)a>Gi7Z{%Hg!&Ru zx&@O?OUeW%0LD8cD7!`;!1M^jlVIu09qRR#i>t5r?~y?&)8fh{9zo~A>5WwXiE;p- za4_g-J`28OAU;|ycxUSb3dR^Ib0qs}5KWN?aq28+)b>6Yx?-ci##8w`L$A1p2Mk6@9?7%h4H_lI_WF|`Na{1f4SiP5J5xcVBe zI(Q0z#Xh^o)`v^^3X3P)pZJM?GB!y(UYi@{fK1;wOY!e~)#QKyHKb~4pC>C*nxQZ% zj{WYc`|kTKEwF%;o9jK z*8oF`hZerjrttwJ30ARWk=L3J0V;xAbAYh2}SbMdyn(qK$Sah5Ag4q9S5xjkatY zKtJxsk~sZxD(+3Wxj@94$eJ7=eu2((Z_`2XzPH`& zl!fP4(bARsP(Z=S(iuZu}Ynz9pdhYy=A0c)gQvtQI8Z2EIT!m}CHj1$>=vGOpXB8Qk zBsZ|bq=^cX3>_Z$qx4K=$MYPk`9lKi)uZP+h_P>nV%kxg=_u~?$cE%2*NEc@;Tw?{ z@0yWYq_PD@{vXkXxoEl+j1Ph(tVHdBft--$mV=)PI_3KbmsLwL_dLF5u5nV zE4I5!Seuc$^Y+B~*H61KIVyxU&Q=z#g*jgPJ|1HLvm<4Qi#pzEoA(cj(FA@idKxr% zjQ{#cOzK@THj}RquHT#UQ`a zk9QI=PQA&*&e>LqO}O~exbEiCZZ;8Fa#FtAb2IVh<$X#Ej|o_Oy&GsNYi~N--{+L2 zh#~EU(ah;lK3|2FxSsJ4nyoZ(>&p#IVvODcyBCV45j0SN_Kxru&QN|GmEEEl6zEVv z=P%s)xoNwF#Zi9A#)knA@IlEh-(lbD@8wk>7&qR~;N_mw`@9j}c9)8ae`IPj_GxxN zS4^gl^*=t;EEQNv$DQJeT+zC}RzdL)xKLI7=PAB37Zq2@zkH&IlsRn`t*<6rdP7bQ z0U+&q&d2k;1BJ7(q;lKgV_3n{(;RH}^=zp(bs(;~q{Qml^tJrS7d{W=v4>T;}|= zCK~*a>v*a21tgJo+159FD(`efx4FRf4po+MH(LArE$8T%1C^?XjETMt@uu6{rb;Vr z6lmU9!E6X~W=E)9$5N9TG|mKNt5ldsZpQn1H!`IeDIl%hY(w~&O)b6Bj98(PUbKEV zcHVe#p_&Cgf)r3=qTiaa50B;11xE6v7bcUKUSZ?InS_Rgp8og7q>x?CDvth$^7#~= ziZsz}&+InthHce7Hyj;5Oo$)uZvXF(F{2(f4#ajVm|_LJa7Iw`Lc!j@$7%_C@I$3+ zZ+^>MFg2|X8EOm5w2c%MXkvrUhA11y?eNN~%Lx;QI_c%0rgs7q@&nFNkR>0&M$=hC zD{3%9*MYtex<1zX`@bRf$M-=B4HafPQ^kB0M(jgm2nVtQf%{jwET1Tk9J z^)Z6Ku>kOD>~j^ZR3#{{!xy`96I#=H57YjdK3AG(9EV8Vhh`Z@f#0~aI|w$9@q{LE zehV|@#G$w>@PDyo(uZiE=_zSq>po@ZQvXx_UCz><5#SLm@k$<9Fkl=#0(pP; zeq`kQ_g2fxvR|r~Sj^h@v97@YIP+(+;VtUFHH3dH7pVczoC4ADUtf|0cr$^V#8jka z>HjdfG{H+%EBF%^6UJXvwahxW!$U6viC3m)`Us3k-DB6I2P&z6X~qz1WGh|0<*)m@ zIKlEH8Xm`h%>mff*3J3ohyOh1{$uRBLYJS&{^1KLdD*7R#oXl3R{W3C;O`epumdTz z@F@E}ld0b2_ygWO&;Na9{C!t2*ee)g2UqwzyhV`U$VdC*YtDb)CC^}T37@9Qd&)_A z_x9!Z)_gBmF040df9B?F3rEx~#Y{wXeLdh}P>{VLCx4r?@G}#GJv_pm!& zZH)$&LdGHolS0m`NB$%XvUsm4A8k~0P9Q&iV3_*U*NNwSR}G&=>Pf?aG#t3#U*5b^ALFp+%T}n zto@(QqhaV!GN6i`+_H(#sqjdk6=)wBuRM>AMWBt$_RqCFFs^tbA&H}i7QjU1zJ9bo zag)UKoSgw_a_;N9y|?!fR$g8$B|W<|^`fKu=g*(~gGpnzEiIpM@r&!nw7@vGZ^iw4 z)_?u-FTI@lxJk|fxtQujMT*7dRO1KAM;?UPU>ToZqFz8oQ5D!hnyBK`bq; zxp{e2-({(B@_ZMlLX3S*RKd71dSR@8kr($o$}EOMTj~7#e0zWUQ%FdYB}FrLD6FBO z0q=%-05=iB$;ml7{$uuQt*q#jkMN!hrJQH)$hNnqT^gn<$l`?mMK6~-kGh93JS?vC zNTsh?3x9iOPperoV|Hf6Vts-M<&=7lpB!uKAxa4pew}MM^b-S^!Inqrx(_{r1&WKK z6Qh{tZcT2>UEN+;)HYUIjXoXS_U3MML3z3NnfKhv0y}V+IzM<5vgNmms+4*Pe~7$} zp-Tp(=4-SKwSE9!&EwgLX&76zn@c*XqFwz)dS%_R$UTgQZ|7dTh8P>qbH6j5n9#lL z5br@<_j&P{jh_i)oei3SK3KsBBXymdolQuG6?wIu64U>S?*I8RxIA(w-4Y7|rnW0} zHmO_eyt4WQ9|8Z0KNcj*PYz{6Nf_o(+fi3ly7L#sxuh;J{ReZQVXSkTzBXuFa58x; zRR`*`9<1>TK0sPxzyT46P~+VHV;7JO7-htW7FcdtSJz$p;Y$6A3UR+y!=n3^+TS$f zrbrEwZ60Wtey|yY=e8S)7;lRa(TEIWzmAf)N$%Wy0A<9m1_ZB1_&0C=lV5Zwp;i95 zcJaladuw0>y-W`I7CS+It;6{7^7x5JvXqPF3(o9>oi)3RtgJaND}NLJ>gt-OaESQ$ zu%CZs?L`XcW#RNR*NWC<&DA2sYrZWdeP~E;Z*^Fi=$1&!&bVla6^Epxa{1(h?nd23 z#8EN~R$e(d_Tasr;ZxL3xUE6C%@-UZn&s9zGTcweamq6R(+Qs_Y5y_JEbK8hl+AE) zsYB(K)e-g~CnKXAt8(m9Pn$|g-eZLo(bJ_KUk`Wp9E+xv{_pyNK7zKHpbbq9pHbyE zvoCD(GW627U<)1x6QUaF+V{8HLAK?SmGe_Akki`tBXypWqwqBjF?EjUq`N9;by_(S z;X6`dUGC~I=`b;R(BXS*(cmQbOF|TN+(hfv()&|u(nqwZ=zX4Q;>y!HIm{+OX=!Qg zF9t+w?1agr20?4thBrV!hhgAD*qRMM%lh74dtm zrK3f_kFMBv90w#`!S+|jbH$yl#K%R6)zyXV$5dzw>Wo8(>3dG;{WLruR$7cnl&)m! zZcIo(P3vNLjS|=wkDFwUZMr`zD5*%XGBZ@2z`b^V5u5VK_~u5?!gVuR zV)oX`C*v$`p$~S@j?{LU*3Xh+<7TC}-uqYsW%OlDhm8b*JJO9SwA?lFP&nz>lR z7*M;E8Clu2aNJ?!GW(m9*Koh8SLB+hJrJ(82e9R+Fi62J~_G?R)p@&h`_vBrC$43+U(so@QrfOBDLm6&DK-uGWy|QL`Tcw3@@0@s6!6O9$r}~sJ&}KD@6khKVGQ&ZaNV;LQ^BDj_m*Tl04t2 zL}_i*t&JR-Z&I$dw6uhs(f}9~AOGF)Ok@TXmX1 z1E#q(X3+;1{UZCTJ$BCbQTrfW^*5fgapGaIR!^x-fA{)$Ro`a?ZqO*D;dbt;>>dIl zLM``0k*1-Cg)xcA+(0m~vav72tJa^$%BFEe=cb+UpFFKlqR3!jUFpk?3LT?h(P^v0uWz64pZ<(T{qq#f3e7{?f-X|_AC2y6-`5 zG>(!o>L|mAmCqD|UU~6otJy zL_;OLf48hej=(>mp$}>Y&h!5v?-ZCWKjD+Dx>UNXqa8mD_g^N~rpb4vas&}!FN1;t zwX=JccFI8?%;(<{gl#iZDfwQV@=7zS^7G?vV?jh;t-vGiB`hB%zU0#dp_N+yR(Dl? zbFbGT4v+#G$Lhx?2Wx}=RU6&51+{q&;|@KU?TKH7wT3Z0K}E%Yszi|L{-F}xFbQBHMqI0u z+_;@~OW1Zf3wx)O*(t;WRsFKi`>c|vFIV>tHEu~~ykP4KOCVP{MK|tRA^S7lXR4mq zx(y6-ccg>>aZx@#q3Z`Xnn6_Pm&^mfF&9A#k#t?vc=U)uVQnPC=j0%M(8I&S7KOwu z^=Z0}jdPf5({FP;{l4>Qg%hhF>Ua)zku4X5Gg4?4v|e*?&u6p#1PJAflMO!&(y1wf zx+(Q6TPrlIYe0AopkGm+{QYCF$Cyw8{^7|G(TO1=UvyukM~^g`Ke5K7B)|dpz0~jO zT^Shd%h6s@USBYDRDG&x{wd6Vh2Cwq7o;!WJ{WN9(olx+bR2|E4-zmnLgOE!Lv_*12@zuyp&pDHNkGPIUfo1w0~U}$Y`+fh z^kn`0TQO9>V*MfH2``0#7)fCO&^L$@RNh^oX8&;TTDviCrSeE{*~e2g2obl#~?ePZJ?+2%iK>Q5DN? zhQ(*%@4vF$j=;RSD`|(|Q&FTE^J`(j^}rpWONc)#qqLiscLcHVD_|u7QSYSl;e_<+ z?u}hiN^;fDF%sxUKyB|CsBx|ig5(8XW~`W3g#djJuvx`GRQDg$G^Pw*QFB~xN5XLf z9Rq&f%u?eIvJfQ&V@Ex_ON2$BL6*~z#yTOm{Rlsxqz-+C_Z>#T@?bd zA1`- zl~NY2+HiOfBCvqm^fo~f4s-B}$pI}=@hTZ}JO%&fp5-;Et+p}JGPE6lpO2DqY3F=X zqR4&TI?(O`CnO@$0xj;>#)@VLtdaIQRL=+UDljlHN^SO(vXog-P6;2-|J*@6BrgWM z$5JU*yVZ7XW5G`@2NapKSt+yUOFup6{W7=UH)XvxUZQB?yVo#zFZW4MaOKi`qVS6z zzl;4!a5iy^ap)PA+q*!@&CVpGg%-^$t)6iK6=7zh+v{KvRey7Q?2BRwf4Tj5rtjHt zs|*9fWQnGh=I${KxzY3whIQtyGF8L96+akgxFh7T?N{n}aW9Ka5jl5*Ui7L!!$VZa ze#C_UMD{?T;=|v)y*rNC`wutzqdB9DejK&5lJ9nSZE&!%ich)|t%z#&TD9-i<8Fx8 zu1wTqg(d4P_$o#7q<>J-{_d>%td`&RXpX(1f%eI0*;-%zXgD}xYbsrra_0PavcokB>0n^X&<_<1TAESdbVl-InsI zyoY-UHlt9#WY_rrq*U`h7am+y5~AMMP} z&1LW0`>0yzSHc&Yfg>wc!Ej{tdE0mVxE8w|(an7L zz#`xg+bWmPBgzVB1_0epO4iRvW#q0nT zp3if&sELB|*8bogaLIg#PE1U+4lAfT)&w27g)XeHAhrW}PdpzcC`EI))blB8OEz8SE4LAj`on_-E%GlF|u#?cwFDGJ1-vZ9k_BvRN1<)u7mr7$3pR){c`Woc^IY)P2(B*E_rhfUI?BS>1jfV)k%f-30 z%eb@P=WDc^Yi?n2A%c=PXe}w=n5QhnY;PZ=>8+ASXP&)v&+at_21Xmb?jseY>R*zC zwPF7=>jU|&jE4YTo2;shTHQ{Wi@abW8Z#USz*3W$=p3 zvU^9fU!=YO4@^o;mCGV39Rv2jN+-DNO~?Wv0yOLS=5zAd1tlBiu+iJJ zoo`RvT`PBn^nB4ZPDyL^P$=4<^`W>8xu5E{rJv%Ts{)*WMmf=+4-{5nbd7rnFFe#b zBfQkeC%v`VXVfL;ZLlD~p(5e2Tu_&M)-%|o>#{P?@7yV4d)_Rzk~_#-^EAuB$I>d( zwspSUpvJQlM}VYdoAqzUOAB@LPO9y;LTQs|s&Qf>gP*?O1pO!ddw(; zQkxj2FWa@1&q2{1`Nz&}CFrms+pH5+QQO{3qEJbvuSic1g>KeXq)re}Z*#_PyuYFw z45?kp$&pf9+aYG=HWRGVDs_2z#Jywp1m#=LuplK1Z8{Ip0}M)U32WDQ?n$A;Uh)mj z+Z$RYdGy!%;Oh|Kix8qUiJm2X3^p>q+ca+EM1+rL0uMuVA@6PN1H**J@JyAv|APo=Rbl zqxrN9@(ql+{VwW^e@fQL0sFsqzsJzm9C9#m4;DP^w^kb(b3=F&%KH*ZbB#1}`K|Qjsn|&4-sfW5F8J0k5z({Q2w8R{-sB4nsaz7CC;YmexFIBfhFhBB{ab ztalMq0p?7yNNtr{4MKk~6dcTMC3FaZA27`d?x;R)u>0czUG3ub7tCv@S6=fNDPb;GxT(+x40X_g}^sCiplsLh)pzrnvuYAO6e6gFXb$X8f;)9K(OxyF-_b0UXs#d90!O|F#eR zWkx3fJH(um;VBcT@ZVT~tE2qeZU)hnKW$$T5@5^AMRAg%yZ*O1{%@1o8hjExfp8QEvDde-lPPFY9EFYO&1*0Eu-xs>^@ zIQZ0xiunDiRQ?x><0npQ7+HaQ8t`-hYqKw>pSxgoU+SPRsP)BtoIm&#YH{g#k#Jkj zc@RR(<>~kxIBXzFgI`}-jE_`B-aB$nn2xyJy=lf=3Z3#^8?94MmM|)~C2VsU-4P7} zZ4MQe*2>wrS%n*tf+8a$M@p+Y_*3ab5Bd^>b1D1;Cbr6F*dw@^q@+5NaEWf^q0&qN zUb%nk0F*3QN|z(%7G%~U9c8}l7dcU-2o8b8c{=Isup;{v5s;52CpO!&kFDHYL{PVOQ4IEzMa-QY)XclLBps?_0gMxwqVrAT2?$yz?01Jm zY>bxKX953eUog>1f2p!A?}bHtPme%!OiVhTkwaG~h2J;l1-_B*FN4%ll)6&JpGc56 z%MQ9#8Z)Mw5fI^br~1LQpUtX)K%VBE+EaR9p)Kycb6j5PR=;$sSu}?A3eGfDoJd&s z!Wf|r2w-9Hsh$As-qOmr5%1^@FdRc86aSDhW015*n3wIOwF`8Nnj&bUicXpjAzC}P zuyJv7jn9s41~Zc*=w#q`Y7d_l6&0yz>2R3HHAT>CRTNvsL??YsN`jO=pU}j;PNy*w z98RHAn&W$F1~(|J)M+k66-q(>tpHU zTU$26pEE#hn>Me}QroNyW!R#uN|;T54zD`#3*D#DWaQ*vgDj1EL4I$kT{nbYO_DaQ z=ClWk_uQwbxTN^isJ)Mn$Wkz>tXWv^t# zr9nscsW#=jl$wRQfOTSIWx(6o>B#-WW^nzH&+!ub^_R>j&+Y82y)ow*!qxk|Y^s*4 zLlSPdgCFW2XxG|_vu0%&e|Of-dA)dcFwuREd2;6_v^`1uLHscFEeZFyOdlcbdlUDT zC;dPiK*8mG6SnrxAG?bd>Tw0Ejkm+PCAHY9Sb#s6i|))i_74G}%Xb;N0I8gPYr%=% zMLi2LRpRY*y1$t{5O(N!I%qtqqc}ai+pE5_hr$f{?zv{uo^sY6qstX9nL_8i$>`zb z`C|EdwiQyl)J9u1_1q=pW%#(3YJyM)6;%3Sj8G!R-IHqIh#nX{J4s)6FyWwuDF+BK zuqs7YqQ;or);^F1x1Z0`2%;(vCQfc>vdXh6NB7hZ-8>V!Py}H7OWcOzMN?ktNGG^S79?y{qV_?sy6>z&=A<5 z%2hRGA2dWA_g3v0riIW-zUoO?l&+lFmyAyO^f_wFkM@z@egIf;f8Ma5Hg8_eK)y-V z;h{VA!t4J}GwMTDRu*;FOB`6@`KSwauYm6((_fn(`G_lkyC{u%Ebywp*093@@$1U~ z!QNO&XQE_Wm}xU!CC)B=8YQn`yks7oH;U0`bIJ>Z2A723FFOXOeunhtxY)WZc0KOy z_Sz29FS8TR$jqE2M5Orai?4-8@NFK|%gI@ZxvXD@mrzCAI#S>1I>zKb^26U4Fg$r) zALubsVWF7vvQs7~7kGOBu!FzAV=-Osc(p`5I*}Xfy3NV{np)yr{}a3CRs{wc!dnZ3H<`nf zl(-J}8cnFq5;jQA0J%BiN}q(9JGky=#J0OMs37qwf{AI;{r%BYJ-mv^qR5hSzybb0 zIbzVkT*Cwr2|^Bez{0fqr0RVpOncA!_ndcQL0{?fVNH7P&Dx_-^FcmQ4v~G+l!a(6 zZ6)?;bum|PqIxV%fVfzac!|q}52mnRz4x2o*Ox+ie%P$BVC?AdFm8ce^#q!8Br#n< z56bV9xRqb0H@e_I2<8+kR0y>^R*`!=HlT?A4&QSr2`qJRV*)zHcdn^b@TI$)(P~MSRNEj*AuJ!g%MD~AwEC>$g za$}D<)^HEcu*}XR2Re?)gNRyz`)?_@brV8dXM*4U!GUgpp=Y+*_;E{{${@&j2F9fg@JUNJ04ui|yk5asH*Sl+vE51( z);bs(gYyg*7E}3{_Ya}9KzQT-u=kcxRYu*~uplVih=72EQUcNq3Ifu($xTTpY`Qx{ zKuJNmk#3dRG=g+VH*BQ4yS~NyJm-0iyyuMX*ZYm}z2p32aIbOid);f@bIm!g>zdQ7 zk(huizwP=E5Tb;l;^H0y#P#&_EJ3hR&wCxa>Wo-uKKhr}<-K=JA7Y~u6WxF6D_hLg z`+@L!84VsF%mF>e-Qd)Z07U8d(zj;YGs32DKo?6h?a^uH8-6QP(upT<=et2Ik$Ir z4wT-#&aJ<+3gfZRw7#dCqDL7NG_e`ifNvAcq$(M|x_WB9x3?4JZQw(TV9H&=r_Va&l&0IH30c#$%eR7LrF>L^Vj3#E0bRwCY`yGhh#D`(&|H7)2LgI zIh`~i=;#rZIk84Ez<*GcF`u!o>`k`*(kkzWKzJpGh{l&-%?~#}=+QCnnHVl+h+xNK zxQSf+egEF$0LlU>zd|fIOEkD2h#v$UeEDx3*+emb9%tCW!HR4}2ckI19s{OZlVSyE znH@jlT^fWJh&;AN2~BV1_z?mifEd-{AV9Wa2DhjvGJJFU7QjijL!uFXjJP2JZp~0H z1^)e@H~3O4r`uHU3w96cvGEsFL-Yl&F5||SvR|LVWgOU~8 zqUFzjg3bO4Rd{Z=n6~?j(;{G+N2}b?eCS_!VGcKOi-X zBgNpd)25K)-30&QmzL2kMv2PLI@tpEh_W9gEkmhwf~& zTJh=)5Ije&VBD@v-qxo5OwPS_T_#K;c749MEK3#~98}p@H8)bMTf?PT&{DTUNZ8rYRfYK2}7iX=Q`Y@3R9+&Hnd#{sd}W$Pe>AP$ zNDn0Q^Px{()nqvFUp;Vtt`6vI5G9*5nOLU8oPe{#wLH-O;p}LV-}Nfx%a;uCz&HCB zJFv2LnVH{CHMW37paG8gmgDQrwPf=Rx73bCGLZQH%vrl%&O4fR_1m$;WMW zLdT>lMitPAoHm2hZcckZJX=ixF3=hFy5l@H@9H3%NPltr>-|&lK&)ZkI6v+0c2%9x zENuGvsmRMFAM8GTLJtZSeB{% zS}B9NH4D0shn7`yy$CZS%l&3=ebH z=7OaIPiCe4JD4REOXyhrvUdiXG{G41i``F$onzSl@zS8m>5c?8}b-=xMfU!z=H zTyWbhF!=iiFft3u>U-_RS6EFhMOJ~7%##qTTag7K8(D(PM)+`HkVB7LPFg|TTsxsN znmMPtP!9MuUk`@3NYe-NiJ8k_&Y7TiiLRK-^(8LDkwFLuz#%nGmThss^NlOPAxn+t zG+o)M+2&~GHd-Dv3K!1KkP0*3kLhvkovj{1cWhWS1;>8>a;kR%JpuzAeULUBYxn~? z8(^4EvqRTMM)Lt=v-I;1@A$j;P_w10ooe#5%?{&RvscC|M zWqy?oNOIiES1(UEortUQEAVg?z8d?2+H0~K*`i&x5-T)|ih8eeaB}j(r1ia?vcnR| z!}CL`H0}a*elOwy#>ZApnE!;Q7Ec#@nd9jI7?^8|`}dQ-u*Czkjc%+hh+^Zf$0;d`Tjdh~ixBl}vr@mLeS|OS@)Lau7p}$iRbW+6P>9<35e0>j zgat0}#J;VK7Fbgg83xcf3EE2h930o7P^9dNI`SYT8A9w;G4yszTjpbfk^S+d+bIYGDRhvYp#w3=I_p z!=S(5XxT>BP(` z`-U!O?~4wXCrJVn9*&!Ke!zUBK|sRkQIq^B&^qG0AhU4?p=hS%dYJ z9j;B;#s8j}FAed{%yMfLrmM+>TyhB%pTynICI9&aADQ^Twk3VPwFH+8NnUYR1PsN7+C@ppLY*PSvM=nl;445r zYaipI0UkF*sb|!At&mdKJ>e}R`La;+G_d~CDQxi7+q=aAh}$ZI)JRCzd}V_j!0#$seE zavNfG@tY(AXcWar%RFeCc0T?#`Wb|yZ}vU%-RPvqH(t8H$$0XV?gq%lnh9y%&iLxI z7@uv+8e?m46;qVR#&-!EodOWm2ch9!PZAUa_{aB+kye#9u6tZw3{^v5Z6&M^Vo2t- z0TZx`%ro;gQ~f=v1&PgfJiKoiAV8t0QTQ7s_xTSXsQf#)9Ga-O`d2*hZy4#nf?fWH zX86xg^xx>z|1~tj|HHA#|L083QCE2!EHt&2AmVbiB_F%e(tQt|hL@h3(}YC_2V8Bl zFkMyImp^|_{4lZi?P?iqlOm0e=h2jjS~2$pwxCNhygN$^_!R8xlQYJE(m|I7{y{RFvOt4gLFtXydn1M6eDy)kW85L;lG-Ti6iN8u$-JW3 zOzk!?CBC5-716`ooD|CB+~ zs=cjk)VQmsqyL9!%o^|!)PQd;%TVL6#RM7{nV6X5nU|aAs~06a=uRyG;TkCaDh-5< zGP38ez>)Dlr|lWlC$cEwKNGu_$Qu2QVfw+kq0?`G=}_wWT?d3d$>?sRpYEBsra-FY%t9ZXdb^!(N}DT{)C&m-mjmk2;g)A&ZCxFxeeTTh zzEnKW2yn1^J@&brEKko)nMJNXSXJlMbjC85kaFfEj+R>vD#$1D43~wAI840_8PAs2 zLA{SEc%@!1<~m%P9diXOecE?2NmH?^WKr0K{34c`^EWa2Kchfa*Oq8NN|^X5e`{_n zuNpUzS(}Rh0z$^O^Hb zLC8!z$M_R~>;YaAK5uN>-?=9=qI{o8RH)>0HI`>`n-i5983CabEhSo|GPY8G0CiF& z$C(*)pG&;oqLD}N^?aK=7X;iuh4Tyo8vS660sI6QO3b<`4}6a$fVy(b>3Mu=f&c zY9qg>?1gM^&eP6^u+hE}JU(n6DbkjuOj#;tN#Ip=QYfylI9;!0p?xiQob*jhmk~>E z1-UhWl;19@Nvt_4IysB2;Y1FwbL~6q(c#dh_Q$u@*ox_9|L%4oXv0I4#($V_U9OjO zbN*Nx$twWj&@XJ9g0h59YmK)u?Usyck;kg+HOgU=3j0gfD<`P0j4DS&rG)dJ$a?o3 z7kq3j_#z%C^7T_mgK7PMW{netNoLOa?>X_#&JK2Q{`sk*6k+$(N{I$Or=Jn6a~oPsVAQbi+_kc#<^0IiE5Nkn_dF$tuA4QUze)gX(mcMo-*sxoZ)- z(>0Q_Sx00Rpw?Q}`@r^>(Zt$fw6jCMg@s!EH+&lp= z1c!4Jsp<|#$OYm*8(e8mhw?gUynB~RnoCMGf6g;nG1zY;wK%_NnG#*@)huv>hu579 z>@RW064)fOtbpKg2p6#5)P}4`eJAC4r!!;q+?2=1$4`-qms810-g~{+%ifv>-@bI? z;#Y6{Da(id;5U-iEBq5qOQ1oDRrQz(`73Yx7_oFi$r~=#mGz?}d**$8=ZpDp0wlq6 zhE%hd2MYs7Ej-cshG$HKhX;-<51Bq8PW9=UV>Rd$HQ-_1kOn9MC$4WG*SM(6oeyZOUJUZfZFUf&Jp^1=eq>gCD!^?p;qOHkbiN<^U6zG)>tjg5HknNc0N$emuv}lw zdJ5{SkCb$2WbX#O87kIAjnOamaJ8I*NBDFN2RDbdaylKmy1FN(7nD)~E*EOm-}Iyx zjmbADBJ;7_hN`!nanv2xL4q(k3$#`R+}l@F3g^!i8zDJXy5)5hMfGQAvinZ701HoX zC#Ila!BxZsYQ(_{_Zxh^eEo)}o}8^HdRhJF6u^cF>_%?Bv*5IW0!8mrgx#`t&D1+9 z6sXk@R`%@#kDV_KCj8>^@^GWHuWPn%_L+HG7@FrrN8731puI^;Yso_(f4qBU*-yiw zQ$O@*f-iWzgFc1F*@iSVGE2sM<+D_o>)_M2^zF@(^)%RInR1ogVwq!?Y0Ix)xyP23 zu%(caa+CE3oCvq0coHtFi-&-8A#+3R42jS2%ag<=GY4RMxA=N!1JIc(^E$0(!_0?y z`tHDfr$snPZvAF!xcGWET4(IBL$=~)E`1!a!w>YNrFI3*1Qvv){qeuRAM zIf-sAO+_FcW0P{o4K`d2c*ysUS(baxB7rB0Ggh~u7``pFbfOI*L!)bjg%(}lx?TY z#I0GPn2^@zmqOz61Xl0qer$d{STOBO-nz1y@5`8lk1Z>nH; z2^+QEGSjiFO2z=k^tI8i{34(Z&(g0P~zk+xJPG1^qxP_^F6&g`IK+_`n_YMADiT|XZkzfJP z;SdGl^wvcDZ%B=C)ghrii9j7xB~2T5@2?;6d69mj8p2@qqe18a0BCC%*VONpA<@3U z;oLaLkgdSY7Go?8Rc`$VD?pg+@adW#BYJOcF-5U-3lv8bf;hJH{Z#l1c7Qf9-IIS4 zbn6xxfCK5+;xP5`#qJGKCsOw&`__n90FFAInojw?_+{WLm;DKD#x-8$gXM$S?n`U;7^*4y^wF$QHpi`Ja5k zzZ}4SFj|8_}#~4r zMTDXD$)MQgcnxpg@$rmjWrf76cc5!U>`={Trs&1xWqm}sZQy)VWQ?2iybV6`789_G z#mR(?BP<(ltGz=TbcP-7{+uc<*H_y)xfT&({5X#8NsJzpR0~|0;%c-)}Nb`9b zlqm>EOX>U0|apAJ}aVdvDih&t` zTR93Tb{=mxX54ms?{go;+b+#YiXN85Jk!k|hK z8KV%In-f6Ai!$u*vrMTSnH3%0n+&SL8}(>lj9QN&PWEa20zrDi^Xzm~e=mCQn94a< zVN+YX`T}>q#(ld=2b1I~^W+OX#ppehldj20DXDNu_U3FRYHIWG+NuZ7pbWUUY{CbK z7d`R&MWFLw&PLLso+S1`kD_m?Z3;I9QobHiHS$KU$WwDy*iyzN$Mks8eED8AdJHhcX_e$d~4{zWJv>~5DgTd z%AzF<)r)(^D#bTmMl{|0zpT(dzBYJ<&)|*EpPS;fCoN_9($`l;R#C&rsbbz5u(_8C z{Nm$taQ*b_+@ejr_T?$V?M%ANo4QJXbW8II5RI*Ou{8|z&$o5Ehv@2Eo*#}*Z8wa9 z(kM4lNMuXcNN%3D@F83hn<`J*y1_EQyfy6m8h3ty>0!bI+{{nCNUuon{0L%UQ5crW zR}7SMjONFzfU~GMS`I52w>K^I>!Q$LwXeR>%XIzu^X8TV_vR_Wq0!N*u9c0ZUf3+M zRb)RzA^B|m@o9sg-}=Vo-LpfDQC3}t=SOL-E*Im9c`8{zD_6tiwxRhXhi-9>+sRIN zcg)CbZZQ_a*!#4FgxhW7;gcs%mrt(%XeH>hk_Jki#Em*^jO~J-Vd%YBE(diwpi`^Y zJ@P|VJ|PQEPP z0d~i3SOy3g-u?PEC*Zd6u|8gyUSC+U(PRS=6c$!*P-Lwb|Avy!dkcV9>r>mkm&7fy zNDE{rL4Yx)VY@l#2=d(q5} z3_R+WXNUGv`V|(W;H@zWZyxNc7iy;ai(Wfq)lUWb=-hb566A5WRp`{{@Mj2MO#3nC zQ-}K+F7i#hpVch*6gZdY3}>|UxE-A?1AVRSemxKfn*-$}lOVHfNyA~l*RM__n)UcL zp%lWBuq&tz;&`z$Dmpy8@@f5Uz-e7WLjzC;N&)3VY)Nb-kJWhQthYXmzCKmkr5_dL z*G2D(^kl)xL~mIc8D+6ISNbN;GQFNZL-xOsI58Ar1^sQ&5KnI`8c1Cp{`SMo6+6Xn z+dLfIfArjX$TeMqsKjzu+wY!mrt_@3;$%}ZBpvGNxNTcn3>#H%VAG?Smn&h_6@a}p z&IGl%AanRR;yIftu zEsV9}c=&fT;RDJV(ILqAJ>Z>yuX3E@g&6a9v`QwyfKz&Ju(N1YKyOGwX@Qgj!wnl!vNI1Nh8EYjwdkdzdt z$Hl=>TUQ@7?-Fi7>w@jlg1-aw}X@ zA_6g(M0zhLKKw>xr9qn1M*UY1g9e3wA1~=Z|uk#D)o5P9Yc{42n=O*O)jk@&rZ#-7g1poJc(gjC@-_)8Q=l09HZMbik zg^QV3AAQcjOse!1lr&)5%B5OOyDdz^kX9)Ln z`=m{ixKtvHm%Qp~ec~&VXI(kttCo|NvwTu8pCbnLaZL9a9wTGHT_}`%kcranpfbEb z!uVf*`iR^NSe`MNWKd#i^a+kd{`a+i3|lF^bC=a~OSdBQ<_6gR+YOM=9vU~ASef3# z#l?lfU6dVd*&6gdhK0V=)1&0z;8fC7`fZ@Qy)lK+TQq@~IxRIhT3wrX&|{e}4;cB_ zPS3VpH=wv3tf0e~VUI)z)V2d`Q8en%tM2~e`TqT=?|vbNg8EO~+!B2$Uaw^p#Wmlp zP%M*)c<{L$9khY;WMW8lTc-u3ozQcI(l2rscN0udR%-O)YJ55z2f-Sy&jU2&V5n53Wh0< zt~r7iw${=X%J?5I`=66Zi@@Xx@_}tlN&PMjO#OOh(%grJA>hz9Mkl8O-+@?6*OJmJ zrmD5Y^KP?$KnX;&_cuQjwL+e#unaC~IF)h75t**Z@N{=}*j(~&oOV}I()`$!9&2b~ zskI~o0*4^bTKSQPou=?iiN1FTnIo=3vcM;h_eV7Tyln63kuo$C0a(^2z|MFBXiI$d zXGAK41NdHt7{#;juypZ&rj~LRs4mpz;)MM}fL`mxY2TcvHS5clULrHz+v2qcdkqGL zrU4pyS|2n>*s6{6rgrt&(#Wm+{r%_GtsC->bH(mKy*)+uKK`}Qz}$C6(QoDr5QLAP z!UF6QN8psM*v$3%NXzO)&+V5ixGKN5jIboyUNu$SQzqrMBLaEAoz)-V<>!gQFdh}v zL7Sxw#<8)nwDQtmHwEO9rY5n&R`froPWP8>&kh(5$AZ}|C;-_~ z%;gpQ?D%At5Z~>Tz$!blakh^Ni1pWYL0@kpP*?%(HGfaQaSh9e_VGq1v9WO`EPS-a zS^wnx@Rx>uEQ+Nyt0%cT@^M=3H#`9IwJfnD8iEQVbIk$l2=mX(8MYgl9&k`KQlGj3 zDZ85kVgBf{tMosAUpGtcD@gmB0lD}SA^n@7_I?J2_BJ5dfW&dTA6@S39=$YoW0I5a z+3gjy9??kyF-@H4H7OKsLrLN%4mx4uf*vkE^ZdUgU_5;})U#krM^8`9fBN)2&V6b& zHVum7EN_;^d@6Nn5q2cFBPJ+K>#~@ou``{&jZZ>SFrrm`z*2kJt72?oTHq~u`b+iQ z+{%Z-sU-8hI(c#ukKKkoJV#-fA|0*9a;Hr)BBI6lJo=^ib?cbs?jfvF(;gs#E3z8Y zOxK;PIGLQi&MR&q@8{ha|Di}mHx)U;i7+P{z0()@w@JB!y7m;zmzDqURI#URc&7ku zj5tl3CL`bNaLeyvN{HC5n3wr^W_` zi(EXs_E=4Q&dc5WM}veDk>BUv|FNR{+qYjJoDQ!jwp7+s`$(nk8NN@#%GFK^mKaZR z>%;9CljXet^=;`85k9cKlt|>wp28w_=Pe<=3Y#WgeeX{vCm-DKD{SoEdzaL=cl3(2 zg>7k8nP)k9r%v=YI3>{2+rjwywRmo%NN4R4D5Ya)VG-iAIl+#|AC2yZ z^Td#&3!Qh{V^i9JO%A>6E=~Wp75-m)+c$vtT^UlW$8T?s8A3En%$8msQL@5e@q4&M z;D{R+L|k88;nrTY!id8Lze->V@od}1#U=ddV!O`4hG$Z$OF187tm9I#69_t6v`Hk_ z(24+AC0dmO`lz*9E#XY2wCZ=qbyH})Ltb}%r=s%4L^-pZeD{!m3eolXw#JKlQN$03 zAJfrg{`7!n)UYXuES#OphvO64XXw$l`V{DSOvU|g>r?e20|VautXnxQHGIU}Ero@K zhUOcnB4r%);H@=U7w}!jx4-%a?@o6T=Z|@wwv1O1FA1Na#CRY2^B~SPkM)^_lXFrN z!t2hFZ2{3?bDzLQ`eAgumWpxx1@ngtu z5mqD(mtT-ylCeY?Yz;(2OY==F!6uY3KCm6cPq-5c!Dz{BV_BYRd7q9Bueg&_%+YL@ z$TiWf?Jl-e3&o5s=rK>rZTIc7vvO?>#m@}YTaLqv43c+5NTUbtIYv8(^eq2!jl3F$ znY^rgA6ew0oRrkJ)x`f%7r>2dyLSJ<-u^zjZIkf=$pZCbgB$^3ba=h~DMhAUEK#PY z{Sj6IZz-#N!f1vy;!|7dFWdrk8j-oa1VaKFAd95tW+@jd?e6KGo105drUbhFPoD^% zehm&J!NAzF+1k*0#BYbPw6ru~Ny4HXQaN)LPDcDRc%$-TM%C8PlMIscMKspB6-l1= zx3@(;-pw3zWM^NhsnI1(mPx7&IeHvVS2`Cq=VQPZd^ofFe5$HYM+z^|_m@Te8in0$ z_{)s*(?Jyq^#{*6xtlu(>6ONbB!KTydp1GtvRN)+B02rK(eY%*@Pi(D(QnrHpwpo* zie`6NNy|A+4>Xhb?epbcXV*~YFwA`K61R5l_K~%&iNVgewIz`50AD9T@EHGKV=hNv zAr@PdmSN>L#d;E;bC{Z%x*SBfn&&SR>9n{UUplAd*&hN+O2}NV<2zE6JZG@%Lo0<_ zU|&{R+7{xT)BI})rPy*Z@9T-^6}-CpRJPG%8R7H3wiaWPgPkKFs~nPry2Z^bdcLv0 z78aDyGS-e($<O5NnH!_{F9V9Vm96mVz%<{jTZTYBV5{)M2=0PdKm!D%& z0>1qY%19#naaR{Q(e!iX0g0a<^K?;2n%z1gAZ|<(RNWd``7JW-i8LklMup?gAEuG< z$mJ3#DQfPlNXDS0rJeQF@Qk*wp%qQ&CuB!_gGLAmx0U!moz;K6BzcK6AYqY54PKV^ z%?563LEh1$zQo(BYfb~MJe7e4BFjU4kTeLlU!;iK_HIFO6eJQhJdB4vz{|b6V$2iq z_g#FDfPc(^=zTkgE#-q0J9ogXBlGvuG~%H?v>|;{QrdHmzR{-(QJsW+>jO7X0*)Y7 ze$BY1I5FVY<_h;Y-TIoIV{)Q~fP>?Q!E`s7>C^K4_KqHZ zppkyo-NP(QHSTO`%43nh>lYEB9NMRK#=uKYQo+*$tCc?m(F8UT^FE;uaF!aF znjORy% z-G--su;Auje>pPz{)I}$gs679yod8hB&;wAsu`E~Qtwx-kbser{`EcV5cB5bm=xd8 zN#{3H?Q`#j2l6K5oi_zh+y!+GwpLXas}1KOR#rxeCw5r*pAggc>G^#B9)XeeqtyO$ zY;1TjW%6&}FioZeqmUx`-v8{d|Mc0jXTJyfk-UU!=l*OIPHzp3W13&9$2@57>1soG z)y*yTOP_nFE8tXYP5C_t!+FSWoaR=WmWy^Dhf1P-vEW;@eP>6@XC%+xJ*BecJPE`Y z!9m{|9H<6Z(RHew4_|16_fB|1!#}Amvl)gYCB9t_TjNW>D}H*OTkIK~Y$KyhhKx)3 zQtVA6#-jnwAHQ3xhy!p|{9;&S&aQv0+Vgq*;i637#>R1Simq_qwAN8l)BJeABPQ!Pd~#y&ciMoVcZu7h7w^B*1~OH4#$3Ed22U)JgR==M z6BXB7T@IZ@EU$<5Zc$*RS6R^h$9GzecVaoI0I#s}ufGyzws72ON6&m~Y|ylVUp{8k zqpo{A`M_a+4LP+qg?lm$2iDR`Z=lnthqXFOPMIvIULCk5LJ~EbTd_X6R7PquEE&A6 zlJ*IM>s6zp$O6ZEHh;r~-@a>p@Mo^4r*JY`>^8;Jmdj%WUw?_kjL%zii71S75z02O zW$lc9;$c+i-rQB!w%4zVHp=&>mXdJjr`mw1D7Axw-PXgEhnd;g8S&-Z6J=JPPJS7` zJ{fM%XwkQOqpazG3G+IosB=5ZZIo$>$|YwX`(ckunap{2Bt{*rj?CAdge?!zJk^ag z&d+~h*+unGAXjvvs!X!6Ui>*byO@;%GL7igY{`uOZt}+y~5}HZGAZt zzvTxNk=${cfE(a!BJ}eZ7DYNp|HpEsfkKRLng1q)m>Hksk>6{?(Mlb$?iCf zKWA_*WhSB1BKa_VlhKe5iUcdz6k{Y5vdtvlLqfx)5WbH?C`DfR^AOlj2zuhoq$IOj zC|zUJ1bqr`+t#^j@OBW23Vl=Nc4-|av_)ycEbHZk)C&Y={A8ddW$NUGlwleC5&M*s8rnPjNPm zE;@s4q%w!j) z`1r>(@ILPpI2NeCTU|&?sUdPB&gs)t`uVaeM&Y4A8kII*j0L@6TwR^;8>4w@+AL!_ zP25zgwZy=a4V8?DblTn_E{q*E@rw9y@wq3n+xck}sRV7W@@NLT zZTD;9#@8nfCnt>UY>#`;im!`KknBz6SoVXrnzFj!WcdDh^Vbe_(ynE2dhZ2{;k>`%5sr8+gXCxd}es-Jz-TVTR(hBW2xlCP7jD`z5T&OqVe-$W8B? zkR9yS(~w!&W3H*<^w?dd>tQ9;>Tll6LmHoCGN!*ipTKKe809ERMFvle z7v{gKEH=}*&q6hg zO--6RI*^j5rxDxhMPBOSV$FD|pRq=HdUsZr{GEy|nd(C59@kJ(VA4yu6LVGAd~3`6 zo?*kuf_;ud3CY;Du(bSLAm!}hV5f2Zx&LEMZ&F~bGAB2WzfPdj%3wjDQ|I<(Mf51q z^eF21TXVO2*v;1&dlVR!tfZRuh+<okEcFPj7#@2>#*B&q_7f`q z06iUtLike~Rki*ljn>trVjREadot=QExO-h&O0BSw3CJOL=b0g!`fKnLyF{&+64xd z#^5eO&@G*;grwL)+PS7R$A-Kec`q~B_JR6jSbUmvefrtPdbEqjmIXI*(S?c^6}M%o zsVD)@<@V(8TH^P$wIJ!n=%j?~>`?85M3^7dxQ3f#HU`Q4*nkHKj6s5TadXXFy{2o@ z?WHQs!947!IYduhdq9qHe(=2|64%mCqj(*GyMb)Cci@zR!7}W5dr%cMXUA zl@R!cSBKS)MX5NLl%NYe(+DI_WhJGwY_YC+4u~r9)TCv>Y5Yhn>uX`bRU`CLE^7`G zfsN2HP2!ZUm9mfR3|K3Zm>`Edw{dDf_jGzFO!JF*DfTn`@DzubPRN zsp~5Lh+DBux<@j;6{++H6OiWHyEQ&EjX^XL1@#wLY&C$EAr><+ln(OK;&vW&y zxnHDRoaZbgGV(dIKb+qt&8ts}K7M8f(uQ4DJy#2|QsD|#Yn;OqvphyCK9TV_9Q$+O zjh7GEjeX9VphpecJIe5K>%jV}`n9@n4|X>Fy^iJlNfuB_&HTJ74V*|v1aBH4I_$-Y zzE2sHWO7AJ`&_O%s?24UHy_A1a;H^Nc}@SgYrmaZJiYY0pD;f=?nB9*vwY(dq~s76j4BUQ zd}ZDtwE8VQ%AJ)zdYije&a509t5V6zIs{{sqMkUMISRk6)F*vpvklkuG^7+#`tLyr zlrB&9x0&!_BD8)Oo4z$_Svxd$`(7rE%u2fYlzY%PV#jtfpvPq`;=Wq?`JO7#li!he zXFubQ&8_+H+@3rtO4fmpp>&cnoK|_d9Mw&by|WAB{c6-z8;~U8tgNM&%JpEK*Ggh? zq=@~SKAWQghlemLwN{;Bfz@QE9*NVYC<+WycJnH8Dd9uxmeuCYChodgT+V4l>54jj z{i&hw(@;_*7>t=XTUg+2cD^CK>_Cs&#X;A0zSBLJ&G13A-@5WYH?r)DHJMGNjB?Yw z<1h}LSCWqMsay)f=rPN@+h~o1xYH$dFC#e%&%TNXU1<9sE52+~q!xIpQ0E=V zhP)`;b&60radXc%dTo%64qxDW@zM*ab(&KSlTH6mAQY$h?y)=sX>{xE%e4!xj1lK8 z;asbF?)xiXT76gQ?MK$nx}(7MpX2oChezqZ~LThF#p2cB@`T;Hzb^9aIJd53}9N^QEI2 zk*(lv-FfLf@_YarzM#1`SMKq=Omrmu z_ko@-ITAMe=A&`pT!xWJff>mZ7}mt;sosj*M6SKvdFB%{J&AeIDPQEI2PwWk9mIK( zP_TS4NV$&2HDKW-W=Q#ZMdWFb)Atgk{2JO zOF{)E8RSG`t^tlK=y9>Id-W274D2CeGCWp@>d6npUlb8wLbmjhdb7xam zx2&zZvseA+>4g-PTE?<5QL-HO!P)?RO0od3QPq~r?z*3qzq;c*QW0h5Ft0iK3VDI?pZSd|WZ%ey z0`li}ue~A1JO}1nO1vzpFxLJBPo_0&a- zT%GJRIF%gMEm!YBVZ0V%&em*)dWxmFeVXJvbK`APVq?*-p;*|>A=2|@eW{o+d-S|R zB;J)eRwOpVOZL8xGH(3-^@ldY>LKf@Y3LY3yog9;>UHngAo}G_wD6#ex4lp_-y(U= zGV^f)X7v(PT7ToH5Yvc$O8P$CUv~%T3qI#-al+bH$a-klW@azpl!~GPQrMfi`|G9$ z+tY*+5|uVbVrZsvtgh5CmK2+topStxVB6j;kn3lr`te#fy|xzFvJrB{8Z?QF456T$ zwbJ9pfE!oGdd$-t<}Q!~69vVZY1rLw(dV&jK46^xoRX4PZyOdjJ=sMK{7F5Fo76qPwcLCHf_-slUyxZV9I#`!Jf zmUWXMITKl_%A*w(w^+WOTnhBI%;f6e13!<+44r4X0WV`Okdq7QB&{mL6Dkys;rd0; z408pD)3GQz7v0bo?Yq@Tv2vAP7|l59RNi;MKhTAcifD$a44^i``+2ng@W0&yxfiBR?If@S zHmya>m-3-%r+>IMWKe`^E?lJ?u)~Ksfn--Xp zcmWA@C@d%K6oZDdJ?=Uy7o*YL&|pdG@sA}sOGZ4=j!>KEtVfyJMJE0Uzs$S z9QT9-s$qc}f%?HsBgwvTqT4SY@!4%uOsXGMKVGJxxyu@fff6jRRxD?e( z^SUN~u;7$KNo0EiQy_?*GNCA$^s)2ZSaKuB?X`4C&FJg*N;4%B6XWkhnO}E=QF)(I zfJjGeyURh!rsLc*&pTgGR&pj)c3O=;yH>P@5J^K`gk(hzt6aCCf`s7T0agGSd;!&| zEyZ)q$`iB`P?{xc&rM4+%2A^>C#9ec?k3jA@NW%Qr{&44?_(pcG#rSFFxUR#@vx4} z75iA>Q9<@;2hA;;*Co3pHHY}V!e;QB!uPV8LDZ;%D$$jH8HNvBodCj^CG*qgNqOS%|xXm zt*Ochfzi(qt<;9y>`a<(Ip z&V$hzhK78_7<0zMo#w@U@W4BOx3VV0#H#in6h*K)c&I;CB!bbSVB|92Jih~tbCs=% z`HzpD*UpBDO6HGh&!IBU>b6+x^pUG->0!n%?^o@3h4Ofv&MqV%vt}Uaq{aaDWXBPQ zjDn=~r@#=JC<(A1k1om#jaHM*JY>!|*>IQr*qCJ*d)W@VJJ~ocRU{vF4wZMVy_d%3 zsUpmoqR6?Rar|+1a;)TqO={C&>$EWwxG@DK7U?Lbqm49Pvz7EQjbM@Fwow0OwOB3#-{H2w4h=9?q;Pb8y}0`IdH+uoII zz40IgOBQ1^^|oPFOwltbg`}5m+5!2Aka3a^1o!-PLzPs~Oj1=QAyyX$h(a?azMC^o zxMAo;GPmRVe9vc#;tQpIQ?B2`^~3LB!ZRzzeA6;khMT24)$==+-y7xh=0?)*ExwZ< zDN(F^j>r$Etju~ri2v@Rk8A=N1$mC7cE?9NPEJm4D(IT};r4t<(>*Pa>Q-yC=jOoR z=}82Lq-tpH0Fh_xXBl%Ypf{wZWtQEPl~kDG^!RwTQ>bN(67_+&n!C`P04(bscE{qk z0&|qOx62Cc-Q7(+LtN!0Ym(rKlw$JFs^Himiq{|&q-ny|^$xF!Fo}pHlElSK(MwOn z?f3xFLRX+|Og7TGi4-luQXW51$&#l^=je;`DB|nN(wc%(2ITQbMYq%9T~>4|WLygM z=Flufx^IbmrbOC&0z)h2(UXtZ^uKEl%Q7__?C!Vth&=g!+WX45sJi`M8UzvPk`hq_ zX=wzJR7wd!7`l`k7#b-_VE_>oK@p@wO1fJ>lx`ThW9Y8C=A6gpJdWq{e{=7f`?;65 z%&6kM1v! z5vMF^+&EeormG-4$?;4rl<@oPbv&}=R(;&jilU&%QtiN{oG`o4&>Md|NFY?b6Q9 zRsJ#{AaJYjYH9Ji5xcJ>7dZAA#X{lmi@SKLNUY)7$b=)>FLt9@V3=JTd&698^?4mP z5~NzTV4S-(*F>x>Y+p7s=vft+h{p&m>wwn$TtOOk1l@?y;!LYJCEQbr>=U28G z?w?Jg7d-kr^WaybYBAimkD6Els`FFi)uZK#u;=xa0?=BH#;fP;3;2gvDm9Rt)zj!7 z>O;l*%+q9KMs{|eB|V+D7Xxa}*w)LPvAGXRe_R%IoPOh>c57W2*B1RgTmp!Dce(uRqd&^0|uuYM@Jj}$L z^%-3f5!>a~)>if6=2>nh;&MeJwpzue0--RQnAmuYd2V>|a826E_wpxtdR7W8eNN&( zJ)CV5P8wni#|D0WF*cyf9oQSv%HjE(9gtr!Xe+aJth%lsA(JOthp4U|ko!_&P%Mud zzXtg};E}y{sK~(m!D7Iii>1qV{a zO#Ajf`fFm7StJVPtQp_e)jVK}yT7miR@7_bu9qXnq>=}iQc~YVC@VyFtCD%k%F7!M zo-qr0sSrMeefF&(YUN5)2@mg1*CwH$#$MnK$e_~z6_C<;Y#pysv*W+vix{|Z5g6FH zX}#JK8iajM68Uf;>b)860xSG-lkLLFR!{F&vzKQZ+fvikUoD+_yJRSApOzJ+=5^}T zzvfZ?uSC}Jo9>SYsV)80JKI}2q6~26*^#}GAMg)xi=|WK?`Wh2}#w^_1f|D46 z`W3u!N}E5M$(W#x>x%KVGUcu*IcS&Ny^Nz$P&5?R#uM!W~VQ)(Xz zYk&1y^DCo+2|0O> z3XiG}rNY0{~b7fq{bW$z4-?P{@Kjwc)FO zx77c7vIC98N4OAfZrjcCv%~Wu%l-ObCg$t&^8S0;0@l`5d*8@Tj@o&wWZ&J=NLQ8t zGABY^9!rKirn`~Z#VzkhApqQCX4%-=P6U7{bR>5UcykSFBYst*oUg-oO8dwNJ?Qgc$S}2`U}s!ttW#iks~xYnglk;Bco_#gc_jo{XF| zr+5XMo9wsPeKTWYb?sLd9hN(#uw0DB-fyyVa7+{A;#J?>zlg@Zrkujvy_^km(As`7IdWXvxgt$9KD6FHIID$-Ff4!LZOq2j zi@^N++rfc>Z#^Vd@e5+ z9VxXBF3DZ%&eGycNg30e(lFDc_@VNXZZ;xVUS8e}IK>&;A}+>o2PV3%OCGEinz#Pw zXlT%;nyRGa5mC+B@kx}JmDTjixqG}=u4}2C2!+$-3olPi=5QKki|jxySdDLN&IgEf7nP@?g@R#K_hhfWPp6 zNi{;D$2KoGC@Ly~RCBokPA5nQh6x~%(veB*$;BP<;$a5?IB*(1LL?HoxQL7>o$w-m z66=Gf66XuV>`&d0!_@h%>vji+yXvm@E{Z)T;E_1LI&fY^wYhm=V+k3i6e9y9P7{}3 z-d7Yh{pJOfM)x&Uas;h?8UVO@y6&tR3R_9R;-d8{HfvK~3(1wNKF+#&9Ecl8!*9l3 zVmq3&9MEQBVVrq(i=E-n87MkEKmDog%r@nMcXeY5pDDvA3^T^hN#w(QG-Og84pO&{ zO)%Bq`IPLl?X(f-RKC}16=Jt`Zxl0~!nWqCb}*X$)>`{8MZOx8K;k69co<*BNw zm`0)Hg=1>M2Pbsk0MYIAbOMw7hT6B?40cMzFTJ8n5tCWAoiH!bgN;TKBlBk$K$Toa zPq!hdhZG_|iNS5a^T**yfr~T%khD;_Jno?Q@c1k>XMhNAMGy%pJzX?+S{|eQAY>bm zkRUZCR>s1`suJ$_I%%a?AMlg9($x$Fy*3UP3^Fc5iJ643qOYsStE*p?JU-E&lz&Eb zp$vGiK)0IiYt?d!b5<(XnHw9w49vSF`@o72sOWMQ=O~x2&P~tL;<%KSA7)nsMX zm@(`nHr-?(PIOos5iEi_HZ%l=6_<%)pVf7-$ZP^3W9FNpv|z6Q9=! zg7aGWy$I=q&wfsv$KvWLyz{*DVtKjyvJODH#(yDYRmj4y0V369mVI|dNFiT4;7bBV z>yy53vmZYum~_JCwp@7Js@T~#F!K6lh_SJ;+h!nj0koTQq@?XlTY+iIbboW0k-a58 zfJ*L;`TAk^Cj|SyC|db`yY)mYzm~==j*vx19I?2k=a+*$Yg>`)E8rq; z<6{C`VHTbBqrkNNlZ)LN3W=^7Xytg4tZ-?@q!m|-T3Nmjl;Ot9K;sfC!Tadums?!S z8V+1sTt>!rmte7UHYQoQ#->mlZ56I^M)W7RE$LTD1+6~r@Z=Q`$SYQqmX@TdI$6-H z(Ot*rQhoG;ll`Hq)`>ep?4zWoZY8INhK9suQ}l922)*!`!Lz#Eo?0sAT4tt&j`+r< z-t>NbCblb;K6}_4Z$|Qg!|fRWuwGaDbWx#&3JP;=%=N{%0w&y+b!q@8KQku}jUmSR z`yL1jyS~?ZkDV&G7e3qJy6j_mMFNU}S&6lLuio~e>}1%v*Ciq)nDf4@GrpVv3)+0Fvu8WB;Lrh^AJMaF~4 z4|%of$t8WQTrh$6ZRb`y?+OSX(1OG94=8)uE$ARtZW{a>Ed8zq`GN7={<2*v#}NP9 zbCm}~KiXS!dM<=ssqgQrc)OtV`}XRKl?nnQx0?^^FmU#eM~xV|ZP)+mHvgTU_{|{~HJF{{V>$43n>_1^LZ7Qb-lv-{bce2=;CQxUVJH`d3>Dc97jPEBT?H z@;gt!@JjA;kII85`SSu`ioht}y;1m*Vv^v$fM5SlqGzEv)OqMDh2kG0`~Wq^>i@$e zfhqd)0=)m3W04nOgTJGLK|KBsn%-Z_5)H8b52hdB`Tco;e;}Z*)HhqBeH#P!KO^ji z%3G@_{)b5dQ}pKrF5U%Ggz3cpK@l@za$4|LcJ%j?9NjX?$Bapjp$6ier1bR3YZHFh z_*1iO(XH+6IbS;~Jw0dCpRyAY5f-huIq%-nwHo$XLK8UK& zk)6KQb-vOffD5>_wcRHMT;61!L!QU3O!^%!p!QsPp_3#=;nMs%5EdIZmrgJ*duXN( z{^&`yp8{cytcArp5AX4+7E(sW2(M!-PJJI>fwi%-quB{mMT`&?#WX@TILa!@UCoU$ zcNnSYvd-Wk1PXwu#sbNtv(-mcEv%;d%S$+47(`!o)Sc-n984;8Rl z={Xb>7dM6&H%7gA6JY3bObJ51fXvLB6)w9RcS@C(*FoZFdK{7C`a1l002?e3kFJ+C zf6z%aZ-R6C7Z;AhOt)l}{D;pGt()5$I3&wvxW<$o3(I97TikPhnV4KdqX-mbk@5NwaC0rbS86vtRJq^z zNC&8~HZCOa$N-L$u4TtZd}9kMJf;aof=e|a0S7ouP4cF*tx*JH)#=LOX(}Hg_9dTF z(=M^Ktj@26UzoWu8LcKM-^u9xbmDWhN0ol%YpFcL$euH=m4Tmt^pqV#Bs^e`+BXrk zHVkNoy9{coP*!(*CQXVE;@THXJ`myy31&O$T1p$ulVbfjJ&1(9;jj7mvc|@kfNc5@ z@|*~uDFLrl`uk5!Kx9kONaq&u+*6M2eFeHsH}IoKqn0-ZY(Gw|t%V^fv5HoJGuefN z^K}8+IyN=MpI<<1!f0Pztr?D|2Z|*&BM0=LUO?(H7fSfk3|Q0BAFQ`9Wj@RS#S}MW z`9gnD%g}+F)_Qy259J(TmmQJa1tv^!Ub`P|f}wEMvWurJWk-8}3^yKP{YN#UmlE}C z2qPF2TDSUSf2MuTFr!iJlLT!n?Gu7AJ^rNS&%un3S$e;P)3<;81Wa#!+B$4oeDxR` z7@*?LTq7& zR{D6*z!RIvYo{iw7x17oF9NTTwbI0V0wP2A3p_kJ1Ry@q(b36l+1_&OOE?Bb>PC~u zO6fLJAr>$k6LF`FNSbOc3FFAZdv$E% zF}Ln>E(J!hlB0urw@`jU0FP7K8$SVALnCbWk?y`0_(>Mv_He0wlf0#G#*tc{R%I7O zRbAnN9B`m=0WdANi%i~&#$}bRO~V4|#%5;lU8y`7K*3|SmHKzuo28OhyJ=r_j;w6u z&N*ij|1ImKnQ2ke^qHuMcP?G_WT5HU16x>T>F{IpE$jyBecBQ%18(1Plz3Xjt0UFq zz+N-kmB)E)gwnm-oefx;6M5{dX6{+rS)|EZu(8F!dwTTEaANup-`RuPr7BtAU;)letH))YiTH{sl#Xmkd4Rdwdqz?IZuws=R19<@b}gOKj__{eA-6v zg-z+5m4egm5`tQ;G!#(1Ls(>jOh!w%+;+O%muW1h-Ys{&sDkXK2lyNMY-?4klwqHO z7$$Y%w0+<&JkzgJgx3i*DQ)c6rOpjPYDiCG;FG`u<64E&5+toN=YyaOQ94yfXvO0x zU7bRgoRY4pY-_!t;n8P<4$cmNwFy!}_6UeYC8x1*R_?noht1gZgyv(W>O5rJ0U*|9 zEd|?-lubsJN17aJ8z$LL)NE`mhl0Wv&p6~r$iT<6fKGJavlqVdM+w@&HUkhOg)mOD6iX{>+ct`({CwW3gN87jvPH@@ zAy#>K!%%_g=JK>$erch9@!Mh#ySh5R<<3TF=@DmhIx}x+=^XI{dKysH=WblGE_w5N zr02^*IM(l*{hSy@m&67|431;t;|N;&%)rGxW5JMEk{H^2dJ znZV;8olI^qE=+DgMKx6F%F|&e5eBw+ zj-2?-Re{dT>1{Fy!yQK&Y8h^3P62vv?=!3T-gLG8VLy7hPpErbft8@qO?_D|fO}MW z@iD?K{*{x!1>?`3UB^!CeU9<%AdwBAoD>xm`#B<=9{Nl*5mXU@Vs2=TUTMaw?60XT zSh;}E-q>A$J2r;M=VZnJb$-p@*U%MUT{A?xfe*5_ZcwBsD=%*xYq-Mxa}*8_H~MlW z{H-1v|8hR(WW@0%hKe>IyY;S#e-cGIln6C>P|I~$EzfxHm6-0Ff*H`k%}zotbR=-W zcv;EGDX1y6+_CuSzG$>WWZ7vuhruhve5ND_+f`V9OT3gobr%qhwJY?V`2%_+JS9YD zNnJ-LCA1gz`K}f!DY8XBH9+)9qY07_hPIMNlP;)_c>R};{BKK9{QoQhwY~l;)%$-o zi}=NIyqk&QITE6u{{Kkz08R4GGz5x>9fM=G8%k zUS+uEC(i^ewjX57&9!ydnsu0^rs&JI2OP=(7XRSBr%bqVatDiz`^76Gw@ZQh6weGO z`SqECe)QB#Z*oJ;)|TTYN+6`g*2dP@-dfLsftp$y^aZb251i;2kJ5K{vvYF?1w1=H zsr8w9*ac8o`Gz?QgS{jmif<8LuhB~0UU)#!h06a|{!9L!_y`};7<8!xfKQEW%mT8t z^QM801*?=)ZO17epk5CR-ELtLcg^r}a$1K2p8eEfcOw7sZ9bvq2Dv0uX*oM}`)O2~Zfq2&tYlu6)v6mzw)l7USxBkpMU$#Pf+pNK)7-)j~S9dpXlfs0ljr7U{Nc~|CE73dA}C& z8(UahQX^4&epYMV+Sb+#+JC;B5u6saE7_awsJc23=ahcz zLUr$jKc9C4j?GvZDG2kcg<(}Me z3IsAKFe`dehs03+$cs5XE~%&-(gK_fFO$+@#yYxpF7oLV zOw+&D(=lqD{Jc1r$7FY%jRk7YOh!h|w^9>pHL*lXJM`6=2<7vA+kX5aKbCUg>UcH% z@gDBkKYH&(yufm|uW^_L*r3m%M z6CO8EM9=E#>Kw#n^FKW=0tS46^}g!r zXr(r;X`K0VQ=5p(2|F1%S)UAOTBC9yAZD6LYSI`)?4O#d^)_Z$^g_R~O4OV8A!Pw> zI~$vUZ;L7Mh#5nLW4IrUwkhT$ch6Axo%&dQ0p!y zJc!`kX%6(wu*(dW4xlG{4|GdxUILw<&xW;_ly|YQTueYvwfkZK2NE0mWU;rhw1R#L z=+{6ur`-<@_GD|lPov(&h5G7?Z_Gw-7i&TAwqb+Vcubi+PKXurMCjzKrM;$Qw*r_SRan&FDMc=HNbOtpWsc>$>X4kdvzCl+fymMM2Q_iV2D2xiAPS?)+d0Rsmzz-(Mt9DFk{1 zY#O`cZfnF~9ZWCmT2FzMeSKEU>xjZzr#fr=H1R^&)rP!jiwB{Eh1X=oTvpj%Tw=N> zEsa^>vJ!MKBvMs0X>MC52s(8k6;y$l8tI@%CHd_XHg9BTlo)4q02(My0qm>QPIjVe z{O}>HfedOpQbY<;Ey|IuN;P}giICh!b^T-C@S5eCc?)?Pk@}b%lOCuf92lKPy?>)) z&xE||gDPp-Pkc2;pA!rqlB5*+{d^&uEx*JrNUyos{9bN|H7WS=`LlIyodnfhF8;jX zNU)#FN7CVT<|057a6Qvxy+W{Tb#-+Um^_;tZq4az^k7w0xRIu)zVB-ejSOoUu>DYs z8RK7#ed2itBz|gu+y)sGi0>t31r=t`pMo z@*Xmm9hf|TG=!3x_`9Fzi5n&xN{~rmUh)Owos;iQ_gx-Sy{jt6IdrE?-|a762i+{t zZE;%a!9`Wb8nZdS@(WxhyYd3z=^{-y5>p$N)HekH@xb^~2I$IejvudQX>|8Z5=IiV zNS$q}*$jWZ0Cvrh-siwTrt8a-Z4Nb0?K)Lw2tv*ZdgpvBjDVyz6jZbhc6a>W77N-T zI9>z>QUVN)x(3CB*E;qsgQ^Va_~qsHHM%?iZu&MVEFtjQX#r}p8MSr#B&Y@tyfwsn zaWs!dLZUj~bX15EFTNH^HJGcP?L*|e#|-SvRA8$^<<&pD!;NqNP61UqJjLH{XlkMe zqv9SIn0)=#89Uc~n^9q-ISdq1Lq^JaCs{Nx%5ilxwWmC=6 z=Mfs|swQhk#k_)icyM^}F_1P0h>m7G+iRFWwflC(sDuZ6z^i#Q(PG1bembBs&%ruB zf9M1}mYw}p-9iGFKTwVUP>At@vsb>>i?2Sn{`g+C#s?3@^t{o+fz1ujpko!zN(krf z!BTuN=hYA&9(R3})S|*-83S)_N7y#+`I2QS^pHcieB? z3aj!J2MqC7QD=kwb*~%t zZT0m5iP$$KB}M%HH?NhKAP~K?q$uS7doGKC%R!j|tDBo%#laV+2g-5%A&&`L(o@Wt zuS=H8ufW&{A0|$LVzA&xwtZRj+YMJsR0Y_4J=MJuXDby)D>0c|u{H-*!ajYF{UO%M z*wDtF@V2*~7%pU-(T-By&Utqk9>t1R-(O4&D7cpy$RigQbyi1UmmfXq*1bU6=FGR# z(LZc;GAkiNc(Mz!wKESGBbqV_2JV>{>WY$|kmem5e#hx1L$A)XKJD%?_zjgEcA6|L z@J!U;4HynSXMBkj!~%k9-lN!@l;2AzMj2SUm&(#qY5g}%71(P(jzO-7d*clci!M)` z9v#+qj~kqxMw?Guo$aCAbp;`ZM|lClRmm5WA73vxpzG>Q=3UOvuIo<D)%)eZyLwL)NYu#G&Mj2yed!uOwdbYy|qrlSoR~$2sY7HmuNv7*z@%*ZsN^O(z_Q zsU4nKb`_US7EFsjuj38$-M&i^cbO7;x{{!ltf3(6+|qPjb?6ZOq18A0nl0EldQ{*Ba z2m#!s8ZXa=?>*}AD|+kRK^YlOQBy?R?=?n(Yjqu5%0d+qhJK~Ew7b>V+|TxAwk?v? z)<0VkM0ee06D$q4Oj9ceAK7G=OdQ6rL$-GQVfcQYbe5 z&b@u4fmPo-_f8JMBQBZ#F7+*3cPmy>RfojA24A(=eQzqW+y$o)HO(ptOc51YKU=&14 zP*2`2HN~8F45;DZcB*%RFD^X^{wj(-)eq$pJ20skZr=4vV?{mV4rM1=bqfR6iU<5> z&tmKamWnD^G0`jhpLo%4yYC+O4_AH}2X0&z3j8_(ldwl$9vNWI$&F)oU?9Bh0Cr=Q zDs1;7^!tJ8p+cy;?Eg^yK|9`oVoUb5 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/permission.md b/doc/jbootadmin/permission.md index eded1673..32bf260b 100644 --- a/doc/jbootadmin/permission.md +++ b/doc/jbootadmin/permission.md @@ -1,3 +1,88 @@ # 权限设计 -购买后获得阅读权限。 \ No newline at end of file +权限的设计中,包含了两部分,分别是 + +- 1)权限的定义 +- 2)权限的分配 + + +## 权限的定义 + +在 JbootAdmin 系统中,我们可以通过两种方式来定义权限,但是,在定义权限之前,我们需要定义一个权限组,每个权限都应该属于一个权限组,这样我们在后台分配权限的时候,可以方便的找到该权限,或者全选某个权限组的所有权限。 + +### 定义权限组 + +定义权限组,我们需要写一个权限组的类,并实现 `PermissionGroupBuilder` 接口,这个类在项目启动的时候会被自动加载,只要有这个类的存在即可,代码如下: + +```java +public class AdminPermissionGroups implements PermissionGroupBuilder { + + public static final String ACCOUNT = "framework:account"; + + static List permissionGroups = new ArrayList<>(); + static { + permissionGroups.add(new PermissionGroupBean(ACCOUNT, "账户管理", null, PermissionTypes.ACTION, 111)); + } + + + @Override + public List buildPermissionGroups() { + return permissionGroups; + } +} +``` + +这个代码定义了一个权限组,名称为 “账户管理”,ID 为 `framework:account` 。 + +### 定义权限 + +权限的定义分为两种方式: + +- 1) 通过编码实现 `PermissionBuilder` 接口 +- 2) 通过注解 `@PermissionDef` 给 Action 进行定义 + +**通过编码进行定义权限** + +和权限组的定义类似,权限的定义也可以通过实现 `PermissionBuilder` 接口,并复写其方法 `buildPermissions()` 来实现,例如: + + ```java + public class PortalPermissions implements PermissionBuilder { + + + static List portalPermissions = new ArrayList<>(); + static { + // portalPermissions.add() 添加手动维护的权限 + } + + @Override + public List buildPermission() { + return portalPermissions; + } + } + ``` + + **通过注解 @PermissionDef 来实现** + + 例如: + +```java +@PermissionDef(title = "账户管理", groupId = AdminPermissionGroups.ACCOUNT) +public void list() { + //xxxx +} +``` + +以上的代码定义了一个名称为 `“账户管理”` 的权限,其归属在权限组的 ID 是 `AdminPermissionGroups.ACCOUNT` 。 + +## 权限的分配 + +通过如上方法定义好权限和权限组后,我们需要进入到后台,在权限构建了进行构建权限,这些权限数据才能自动同步到数据库里去。如下图所示: + +![](./images/permission-build.png) + +权限构建完成后,我们就可以在角色里,为某个角色分配权限了,如下图所示: + + +![](./images/permission-roles.png) + +![](./images/permission-assign1.png) \ No newline at end of file -- Gitee From e50f44596882eab17e17b13321a188b95c25ed5c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 25 Sep 2020 15:59:42 +0800 Subject: [PATCH 0579/1965] update docs --- doc/jbootadmin/deploy.md | 116 ++++++++++++++++++++++++++++++++++++++- doc/jbootadmin/front.md | 106 ++++++++++++++++++++++++++++++++++- 2 files changed, 219 insertions(+), 3 deletions(-) diff --git a/doc/jbootadmin/deploy.md b/doc/jbootadmin/deploy.md index 7ef3f73a..bdc1024a 100644 --- a/doc/jbootadmin/deploy.md +++ b/doc/jbootadmin/deploy.md @@ -1,3 +1,115 @@ -# 快速开始 +# JbootAdmin 部署文档 -购买后获得阅读权限。 \ No newline at end of file + +JbootAdmin 部署过程主要分为以下几个步骤: +- 第一步:编译 +- 第二步:上传到服务器 +- 第三步:启动 + +## 第一步:编译 + +编译的目的是为了让我们的源码(包含 java、js、css 图片等)打包成一个可以被执行的 jar 文件,有了 jar 文件后,我们便可以通过 java 命令 java -jar 进行启动。 + +在 JbootAdmin 中,我们需要通过 Maven 进行编译的,因此,在编译之前需要在您的电脑里,安装好 maven ,配置好 Maven 的环境变量。而 Maven 是使用 Java 开发的,因此在您的电脑安装好 Java 也是必须的。 + +当安装好 Maven 后,我们需要通过命令窗口(shell) 方式进入项目所在根目录,然后执行如下代码: + +``` +mvn clean package +``` + +稍等片刻,会在 `starter-cms/target` 目录下,生成文件:starter-cms-1.0.0-jar-with-dependencies.jar。 + + +## 第二步:上传到服务器 + +上传的工具有很多,比如 xshell 等,关于工具的使用请自行参考相关工具的文档,如果您的电脑是 linux 或者 mac 电脑,可以通过如下命令上传。 + +``` +scp 本地文件目录 root@ip:/服务器目录 +``` + +如下的命令是将本地文件 starter-cms-1.0.0-jar-with-dependencies.jar 上传到服务器 192.168.1.100 的 /data/wwww 目录里。 + +``` +scp /your/path/to/starter-cms-1.0.0-jar-with-dependencies.jar root@192.168.1.100:/data/wwww +``` + +## 第三步:启动 + +通过 ssh 登录到服务器后,进入到 `starter-cms-1.0.0-jar-with-dependencies.jar` 文件所在目录,执行命令 + +java -jar starter-cms-1.0.0-jar-with-dependencies.jar 就可以启动项目,但是这种启动的方式是 "前台" 模式,当我们推出命令窗口后,项目也会自动停止了,因此,我们需要通过 nohup 将其后台启动。 + + +命令如下: + +``` +nohup java -jar ./starter-cms-1.0.0-jar-with-dependencies.jar >./log_cms.log 2>&1 & +``` + +同时项目的相关错误日志会输出到 ./log_cms.log 这个文件里。 + +为了更加方便我们启动项目,我们也可以在 `starter-cms-1.0.0-jar-with-dependencies.jar` 目录下写一个 shell 脚本,用来启动或者关闭我们的项目,例如: + +``` +#!/bin/bash + +COMMAND="$1" + +if [[ "$COMMAND" != "start" ]] && [[ "$COMMAND" != "stop" ]] && [[ "$COMMAND" != "restart" ]]; then + echo "Usage: $0 start | stop | restart" + exit 0 +fi + + +# 生成 class path 值 + +function start() +{ + # 运行为后台进程,并且将信息输出到 log_cms.log 文件 + nohup java -jar ./starter-cms-1.0.0-jar-with-dependencies.jar --jboot.app.mode=product >./log_cms.log 2>&1 & +} + +function stop() +{ + kill `pgrep -f starter-cms` 2>/dev/null +} + +if [[ "$COMMAND" == "start" ]]; then + start +elif [[ "$COMMAND" == "stop" ]]; then + stop +else + stop + start +fi +``` + +假设这个文件名称为 `cms_deploy.sh` ,我们可以执行如下命令对项目进行控制 + +- 启动项目 `./cms_deploy.sh start` +- 停止项目 `./cms_deploy.sh stop` +- 重启项目 `./cms_deploy.sh restart` + + +## 注意事项: + +### 1、配置问题 + +当我们把项目打包成一个 jar 文件后,所有的配置内容都存放在 jar 文件里的 jboot.porperites 里了,无法对其进行修改,但是如果一定要修改其配置,可以把这个配置添加到启动参数里,例如 + +``` +java -jar ./starter-cms-1.0.0-jar-with-dependencies.jar --jboot.app.mode=product +``` + +等同于在 jboot.properties 文件里添加了 `jboot.app.mode=product` 的配置,而且会覆盖 jboot.properties 原本的配置。其他参数同理。 + +另一个方案是在项目里有两个 properties 文件,分别是 jboot.properties 和 jboot-product.properties 文件,当我们启动应用的时候,如果当前的 app 模式是 product,那么系统将会优先读取 +`jboot-product.properties` 文件里的内容,只有读取不到的时候,才会去读取 `jboot.propertie` 的内容。更多关于配置问题,请参考:http://jbootprojects.gitee.io/docs/docs/config.html + + +### 2、安全问题 + +一般情况下,我们不会把数据库(或者 redis等)的明文密码直接配置在 `jboot.properties` 文件里,配置在 `jboot.properties` 里的密码一般都是加密的,关于配置内容加密的解决方案,请参考: +http://jbootprojects.gitee.io/docs/docs/config.html#%E9%85%8D%E7%BD%AE%E5%86%85%E5%AE%B9%E5%8A%A0%E5%AF%86%E8%A7%A3%E5%AF%86 diff --git a/doc/jbootadmin/front.md b/doc/jbootadmin/front.md index a55d4611..1ed04e44 100644 --- a/doc/jbootadmin/front.md +++ b/doc/jbootadmin/front.md @@ -1,3 +1,107 @@ # 前端组件 -购买后获得阅读权限。 \ No newline at end of file +JbootAdmin 内置了大量的组件,我们几乎不需要编写任何的 JavaScript 代码就可以轻松的使用这些前段组件。 + +## Layer + +Layer 是一个功能强大的弹出层,弹出层里既可以通过一个 Div 来渲染,也可以通过一个 IFrame 来渲染弹出。 + +示例: + +```html + 示例 +``` + +当我们点击该 `a` 标签链接时,此链接将以弹窗的方式访问 `user.html` 。若是 `input` 或者 `textarea` 等添加了 `open-type="layer"` 属性,那么当 `input` 或者 `textarea` 获得焦点的时候,会自动打开弹窗 Layer。 + + +在组件中,可以配置的属性如下: + + +- data-layer-type : 打开 layer 的类型 +- data-layer-title : 打开 layer 的标题 +- data-layer-anim : 打开 layer 的动画 +- data-layer-shade-close +- data-layer-shade +- data-layer-area +- data-layer-content +- data-layer-binds : layer 关闭后,对数据进行绑定 +- data-layer-end : 当 layer 关闭后,执行的方法 + +## Switchery 开关 + + + +```html + +``` + +其支持的属性内容如下: + +- for: 自动同步 switchery 的值到其他的 input +- data-ctrl: 用于控制某个 div 显示或隐藏 +- data-open-sync :当此 switchery 开启的时候,自动同步到其他的 switchery 一起开启。 +- data-close-sync : 当此 switchery 关闭的时候,自动同步其他的 switchery 一起关闭。 +- data-change-function : 当此 switchery 点击开启或者关闭时执行的方法 + + +## 确认操作弹出组件 + +在很多场景下,在用户要进行某个操作之前,我们需要弹出一个对话框让用户进行确定。比如我们要删除某个数据,我们需要弹出一个确认框让用户确认之后,在进行下一步的删除行动。 + + +```html + +``` + +或者 如下代码用于 删除数据 的弹出确认。 + +```html + +``` + + + +其支持的属性内容如下: + +- data-title : 弹出确认框标题 +- data-text: 弹出确认框内容 +- data-btn-text: 弹出确认框按钮内容 +- data-success-title : 成功弹出标题 +- data-success-text: 成功弹出内容 +- data-success-function : ajax 提交数据成功执行的方法 +- data-success-goto: ajax 执行数据成功后,跳转到的页面url地址 +- data-success-message: ajax 支持成功后,弹出提示内容 +- data-fail-function: ajax 提交失败后,执行的 js 方法 +- data-fail-message: ajax 提交失败后,弹出的提示内容。 + + +## 链接自动 Ajax 提交 + +```html + +``` + +当 `a` 标签有 `open-type="ajax"` 时,我们点击 `a` 标签的时候,自动会自动以 ajax 方式提交到 `/href/to/your/path` ,但是往往我们需要执行完成 ajax 提交后,会执行某些动作,此时需要添加 + +`data-success-function` 属性。 + +```html + + + + + + +
      +
      +

      WebSocket Chat

      +
      + + +
      + +
      + + +
      +
      +
      + + \ No newline at end of file -- Gitee From 8bb1017adde33482c5a99f840646d17fe9957ac9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 11 Jul 2021 14:27:30 +0800 Subject: [PATCH 1327/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81,?= =?UTF-8?q?=20=E6=9F=90=E4=BA=9B=E9=A1=B9=E7=9B=AE=E6=B2=A1=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E6=B7=BB=E5=8A=A0=20JbootGatewayHandler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/components/gateway/JbootGatewayManager.java | 7 +++++-- .../gateway/discovery/GatewayDiscoveryManager.java | 6 ++++++ src/main/java/io/jboot/core/JbootCoreConfig.java | 10 +++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java index 55b595ae..1519b1cb 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -48,13 +48,12 @@ public class JbootGatewayManager { initDiscovery(); initConfigs(); - } private void initConfigs() { Map configMap = JbootConfigUtil.getConfigModels(JbootGatewayConfig.class, "jboot.gateway"); for (Map.Entry entry : configMap.entrySet()) { - if ("discovery".equals(entry.getKey()) || "instance".equals(entry.getKey())){ + if ("discovery".equals(entry.getKey()) || "instance".equals(entry.getKey())) { continue; } JbootGatewayConfig config = entry.getValue(); @@ -72,6 +71,10 @@ public class JbootGatewayManager { this.discovery = GatewayDiscoveryManager.me().getGatewayDiscovery(); } + public boolean isConfigOk() { + return configMap != null && !configMap.isEmpty(); + } + /** * 动态注册新的路由配置 diff --git a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryManager.java b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryManager.java index e47762b7..d54d92fd 100644 --- a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryManager.java +++ b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryManager.java @@ -36,6 +36,7 @@ public class GatewayDiscoveryManager { private GatewayDiscoveryConfig discoveryConfig; private GatewayDiscovery gatewayDiscovery; + private boolean isInited = false; public void init() { @@ -44,6 +45,8 @@ public class GatewayDiscoveryManager { gatewayDiscovery = createDiscovery(discoveryConfig); exportLocalInstance(gatewayDiscovery); + + isInited = true; } /** @@ -72,6 +75,9 @@ public class GatewayDiscoveryManager { } public GatewayDiscovery getGatewayDiscovery() { + if (!isInited){ + init(); + } return gatewayDiscovery; } diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index f6a7fcea..813f44aa 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -34,7 +34,7 @@ import io.jboot.app.ApplicationUtil; import io.jboot.components.cache.support.JbootCaptchaCache; import io.jboot.components.cache.support.JbootTokenCache; import io.jboot.components.gateway.JbootGatewayHandler; -import io.jboot.components.gateway.discovery.GatewayDiscoveryManager; +import io.jboot.components.gateway.JbootGatewayManager; import io.jboot.components.limiter.LimiterManager; import io.jboot.components.rpc.JbootrpcManager; import io.jboot.components.schedule.JbootScheduleManager; @@ -316,7 +316,12 @@ public class JbootCoreConfig extends JFinalConfig { //用户的 handler 优先于 jboot 的 handler 执行 JbootAppListenerManager.me().onHandlerConfig(new JfinalHandlers(handlers)); - handlers.add(new JbootGatewayHandler()); + //一般的项目没必要添加门户网关的 Gateway + //在某些情况下,必须要添加的,可以自行添加 + if (JbootGatewayManager.me().isConfigOk()) { + handlers.add(new JbootGatewayHandler()); + } + handlers.add(new AttachmentHandler()); SentinelConfig sentinelConfig = SentinelConfig.get(); @@ -365,7 +370,6 @@ public class JbootCoreConfig extends JFinalConfig { LimiterManager.me().init(); JbootSeataManager.me().init(); JbootSentinelManager.me().init(); - GatewayDiscoveryManager.me().init(); JbootAppListenerManager.me().onStart(); -- Gitee From c3b7271b499dd3807cebab8aefd633959e0b448c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 11 Jul 2021 14:30:16 +0800 Subject: [PATCH 1328/1965] optimize... --- .../components/gateway/discovery/NacosGatewayDiscovery.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/gateway/discovery/NacosGatewayDiscovery.java b/src/main/java/io/jboot/components/gateway/discovery/NacosGatewayDiscovery.java index c6f29ff4..f00b5386 100644 --- a/src/main/java/io/jboot/components/gateway/discovery/NacosGatewayDiscovery.java +++ b/src/main/java/io/jboot/components/gateway/discovery/NacosGatewayDiscovery.java @@ -109,7 +109,9 @@ public class NacosGatewayDiscovery implements GatewayDiscovery { List nacosInstanceList = ((NamingEvent) event).getInstances(); if (nacosInstanceList != null && !nacosInstanceList.isEmpty()) { for (Instance nacosInstance : nacosInstanceList) { - eventInfo.addInstances(nacosInstance2GatewayInstance(nacosInstance)); + GatewayInstance instance = nacosInstance2GatewayInstance(nacosInstance); + instance.setServiceName(eventInfo.getServiceName()); + eventInfo.addInstances(instance); } } listener.onEvent(eventInfo); -- Gitee From bb6a9587aabc9e04e12a8494622211f128ca8523 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 11 Jul 2021 15:02:15 +0800 Subject: [PATCH 1329/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9AGateway=20?= =?UTF-8?q?=E9=80=9A=E8=BF=87=20Nacos=20=E8=87=AA=E5=8A=A8=E5=8F=91?= =?UTF-8?q?=E7=8E=B0=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/GatewayErrorRender.java | 3 +- .../components/gateway/GatewayInvocation.java | 3 +- .../gateway/JbootGatewayConfig.java | 38 +++++++++++++------ .../gateway/JbootGatewayManager.java | 20 +++++----- 4 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayErrorRender.java b/src/main/java/io/jboot/components/gateway/GatewayErrorRender.java index e2592a31..0312c94a 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayErrorRender.java +++ b/src/main/java/io/jboot/components/gateway/GatewayErrorRender.java @@ -22,8 +22,7 @@ import javax.servlet.http.HttpServletResponse; public interface GatewayErrorRender { - Ret noneHealthUrl = Ret.fail().set("errorCode", 1).set("message", "None health url in gateway."); - Ret connectionError = Ret.fail().set("errorCode", 2).set("message", "Can not connect to target server."); + Ret noneHealthUrl = Ret.fail().set("errorCode", 1).set("message", "No healthy url in Gateway."); Ret sentinelBlockedError = Ret.fail().set("errorCode", 3).set("message", "Blocked by Sentinel (flow limiting) in Jboot."); diff --git a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java index f4ef72ba..23ea93e4 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java +++ b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java @@ -92,7 +92,8 @@ public class GatewayInvocation { Exception exception = proxy.getException(); if (exception != null && !skipExceptionRender) { if (exception instanceof ConnectException) { - renderError(exception, GatewayErrorRender.connectionError, config, request, response); + Ret connectionError = Ret.fail().set("errorCode", 2).set("message", "Can not connect to target server: " + proxyUrl); + renderError(exception, connectionError, config, request, response); } else { Ret ret = Ret.fail().set("errorCode", 9).set("message", exception.getMessage()); renderError(exception, ret, config, request, response); diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index 884470b9..ea59c922 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -15,7 +15,6 @@ */ package io.jboot.components.gateway; -import com.google.common.collect.Sets; import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; @@ -104,6 +103,8 @@ public class JbootGatewayConfig implements Serializable { this.uri = uri; } + //服务发现的 URI 列表 + private Set discoveryUris; //健康的 URI 缓存 private String[] healthUris; @@ -115,17 +116,25 @@ public class JbootGatewayConfig implements Serializable { if (healthUriChanged) { synchronized (this) { if (healthUriChanged) { - if (uri == null || uri.isEmpty()) { + if ((uri == null || uri.isEmpty()) && (discoveryUris == null || discoveryUris.isEmpty())) { healthUris = null; - healthUriChanged = false; } else { - HashSet healthUriSet = Sets.newHashSet(uri); + HashSet healthUriSet = new HashSet<>(); + if (uri != null && !uri.isEmpty()) { + healthUriSet.addAll(uri); + } + + if (discoveryUris != null && !discoveryUris.isEmpty()) { + healthUriSet.addAll(discoveryUris); + } + if (!unHealthUris.isEmpty()) { healthUriSet.removeAll(unHealthUris); } healthUris = healthUriSet.isEmpty() ? null : healthUriSet.toArray(new String[healthUriSet.size()]); - healthUriChanged = false; + } + healthUriChanged = false; } } } @@ -468,18 +477,23 @@ public class JbootGatewayConfig implements Serializable { return false; } - public void addUri(String uri) { - if (this.uri == null) { - this.uri = new LinkedHashSet<>(); + public void syncDiscoveryUris(Collection syncUris) { + if (syncUris == null || syncUris.isEmpty()) { + discoveryUris = null; + healthUriChanged = true; } - if (this.uri.add(uri)) { + if (discoveryUris == null) { + discoveryUris = new HashSet<>(syncUris); healthUriChanged = true; + return; } - } - public void removeUri(String uri) { - if (this.uri != null && this.uri.remove(uri)) { + if (discoveryUris.size() == syncUris.size() && discoveryUris.containsAll(syncUris)) { + return; + } else { + discoveryUris.clear(); + discoveryUris.addAll(syncUris); healthUriChanged = true; } } diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java index 1519b1cb..910b378c 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -22,8 +22,10 @@ import io.jboot.components.gateway.discovery.GatewayInstance; import io.jboot.utils.StrUtil; import javax.servlet.http.HttpServletRequest; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** @@ -90,19 +92,17 @@ public class JbootGatewayManager { if (discovery != null) { List healthyInstances = discovery.selectInstances(config.getName(), true); if (healthyInstances != null && !healthyInstances.isEmpty()) { - healthyInstances.forEach(instance -> config.addUri(instance.getUri())); + Set uris = new HashSet<>(); + healthyInstances.forEach(instance -> uris.add(instance.getUri())); + config.syncDiscoveryUris(uris); } discovery.subscribe(config.getName(), eventInfo -> { - List instances = eventInfo.getInstances(); - if (instances != null) { - instances.forEach(instance -> { - if (!instance.isHealthy()) { - config.removeUri(instance.getUri()); - } else { - config.addUri(instance.getUri()); - } - }); + List changedInstances = eventInfo.getInstances(); + if (changedInstances != null) { + Set uris = new HashSet<>(); + changedInstances.forEach(instance -> uris.add(instance.getUri())); + config.syncDiscoveryUris(uris); } }); } -- Gitee From 6da48ba8b13069c3981f064afb49291bceaf6e69 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 11 Jul 2021 15:02:28 +0800 Subject: [PATCH 1330/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9AGateway=20?= =?UTF-8?q?=E9=80=9A=E8=BF=87=20Nacos=20=E8=87=AA=E5=8A=A8=E5=8F=91?= =?UTF-8?q?=E7=8E=B0=E7=9A=84=20Demo=20=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simples/gateway/app-web1/src/main/resources/jboot.properties | 1 + simples/gateway/app-web2/src/main/resources/jboot.properties | 1 + 2 files changed, 2 insertions(+) diff --git a/simples/gateway/app-web1/src/main/resources/jboot.properties b/simples/gateway/app-web1/src/main/resources/jboot.properties index 7ebc0d74..7bdbee8f 100644 --- a/simples/gateway/app-web1/src/main/resources/jboot.properties +++ b/simples/gateway/app-web1/src/main/resources/jboot.properties @@ -5,4 +5,5 @@ jboot.gateway.discovery.enable = true jboot.gateway.discovery.type = nacos jboot.gateway.discovery.nacos.serverAddr = 127.0.0.1:8848 +## 这个配置的 myName 必须和 Gateway 里的 'jboot.gateway.name = myName' 中的 myName 一样 jboot.gateway.instance.name = myName \ No newline at end of file diff --git a/simples/gateway/app-web2/src/main/resources/jboot.properties b/simples/gateway/app-web2/src/main/resources/jboot.properties index bc58ea07..c05ad0c2 100644 --- a/simples/gateway/app-web2/src/main/resources/jboot.properties +++ b/simples/gateway/app-web2/src/main/resources/jboot.properties @@ -6,4 +6,5 @@ jboot.gateway.discovery.enable = true jboot.gateway.discovery.type = nacos jboot.gateway.discovery.nacos.serverAddr = 127.0.0.1:8848 +## 这个配置的 myName 必须和 Gateway 里的 'jboot.gateway.name = myName' 中的 myName 一样 jboot.gateway.instance.name = myName \ No newline at end of file -- Gitee From 45c24012bdd17ae8bdaec54b850ae0eba2045722 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 11 Jul 2021 15:06:48 +0800 Subject: [PATCH 1331/1965] update docs --- doc/docs/gateway.md | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/doc/docs/gateway.md b/doc/docs/gateway.md index 9bb2e32e..c841634a 100644 --- a/doc/docs/gateway.md +++ b/doc/docs/gateway.md @@ -278,6 +278,11 @@ jboot.gateway.xxx.queryContains = aa,bb Jboot Gateway 功能通过 Nacos(可以通过 SPI 进行扩展其他事项方式) 实现了自动发现服务。 +相关代码示例可以参考: + +https://gitee.com/JbootProjects/jboot/tree/master/simples/gateway + + 使用方法如下: 1、新增 nacos 依赖(Gateway 端和服务端都需要) @@ -292,31 +297,19 @@ Jboot Gateway 功能通过 Nacos(可以通过 SPI 进行扩展其他事项方 2、启动 Nacos -- Clone Nacos 项目 - -``` -git clone https://github.com/nacos-group/nacos-docker.git -cd nacos-docker -``` - -单机模式 Derby -``` -docker-compose -f example/standalone-derby.yaml up -``` +参考文档: -单机模式 Mysql -``` -docker-compose -f example/standalone-mysql.yaml up -``` +https://nacos.io/zh-cn/docs/quick-start.html -集群模式 -``` -docker-compose -f example/cluster-hostname.yaml up -``` 3、在 Gateway 网关端添加如下配置: ```properties +jboot.gateway.name = myName +jboot.gateway.enable = true +jboot.gateway.pathStartsWith = / + + jboot.gateway.discovery.enable = true #若配置其他,则自行通过 SPI 进行扩展 @@ -360,7 +353,8 @@ jboot.gateway.discovery.type = nacos #默认值为:jboot-gateway,这个值必须和 gateway 配置的一致 jboot.gateway.discovery.group = -jboot.gateway.instance.name = name +## 注意:这个配置的 myName 必须和 Gateway 里的 'jboot.gateway.name = myName' 中的 myName 一样 +jboot.gateway.instance.name = myName #不配置默认为 http,可以配置为 https jboot.gateway.instance.uriScheme = http -- Gitee From 37be0f224c10a628b8a8234ab7f6dc63f001a002 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 11 Jul 2021 15:15:23 +0800 Subject: [PATCH 1332/1965] update docs --- doc/docs/gateway.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/docs/gateway.md b/doc/docs/gateway.md index c841634a..c1928b71 100644 --- a/doc/docs/gateway.md +++ b/doc/docs/gateway.md @@ -315,7 +315,7 @@ jboot.gateway.discovery.enable = true #若配置其他,则自行通过 SPI 进行扩展 jboot.gateway.discovery.type = nacos -#默认值为:jboot-gateway +#默认值为:DEFAULT_GROUP jboot.gateway.discovery.group = jboot.gateway.discovery.nacos.serverAddr = 127.0.0.1:8848 @@ -350,7 +350,7 @@ jboot.gateway.discovery.enable = true #若配置其他,则自行通过 SPI 进行扩展 jboot.gateway.discovery.type = nacos -#默认值为:jboot-gateway,这个值必须和 gateway 配置的一致 +#默认值为:DEFAULT_GROUP,这个值必须和 gateway 配置的一致 jboot.gateway.discovery.group = ## 注意:这个配置的 myName 必须和 Gateway 里的 'jboot.gateway.name = myName' 中的 myName 一样 -- Gitee From b38333426fc555c190f66d0b09dbeb95163cf5af Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 11 Jul 2021 17:03:35 +0800 Subject: [PATCH 1333/1965] optimize... --- .../gateway/JbootGatewayConfig.java | 11 +++++---- .../gateway/JbootGatewayManager.java | 24 +++++++++++-------- src/main/java/io/jboot/utils/ArrayUtil.java | 16 +++++++++++++ 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index ea59c922..bcfafe4e 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -15,6 +15,7 @@ */ package io.jboot.components.gateway; +import io.jboot.utils.ArrayUtil; import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; @@ -478,19 +479,19 @@ public class JbootGatewayConfig implements Serializable { } public void syncDiscoveryUris(Collection syncUris) { + if (ArrayUtil.isSameElements(syncUris, discoveryUris)) { + return; + } + if (syncUris == null || syncUris.isEmpty()) { discoveryUris = null; healthUriChanged = true; + return; } if (discoveryUris == null) { discoveryUris = new HashSet<>(syncUris); healthUriChanged = true; - return; - } - - if (discoveryUris.size() == syncUris.size() && discoveryUris.containsAll(syncUris)) { - return; } else { discoveryUris.clear(); discoveryUris.addAll(syncUris); diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java index 910b378c..333c1400 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -91,19 +91,11 @@ public class JbootGatewayManager { if (discovery != null) { List healthyInstances = discovery.selectInstances(config.getName(), true); - if (healthyInstances != null && !healthyInstances.isEmpty()) { - Set uris = new HashSet<>(); - healthyInstances.forEach(instance -> uris.add(instance.getUri())); - config.syncDiscoveryUris(uris); - } + syncDiscoveryUris(healthyInstances, config); discovery.subscribe(config.getName(), eventInfo -> { List changedInstances = eventInfo.getInstances(); - if (changedInstances != null) { - Set uris = new HashSet<>(); - changedInstances.forEach(instance -> uris.add(instance.getUri())); - config.syncDiscoveryUris(uris); - } + syncDiscoveryUris(changedInstances, config); }); } @@ -112,6 +104,18 @@ public class JbootGatewayManager { } } + private void syncDiscoveryUris(List instances, JbootGatewayConfig config) { + if (instances != null) { + Set uris = new HashSet<>(); + instances.forEach(instance -> { + if (instance.isHealthy()) { + uris.add(instance.getUri()); + } + }); + config.syncDiscoveryUris(uris); + } + } + /** * 动态移除路由配置 diff --git a/src/main/java/io/jboot/utils/ArrayUtil.java b/src/main/java/io/jboot/utils/ArrayUtil.java index c95605b9..08c96003 100644 --- a/src/main/java/io/jboot/utils/ArrayUtil.java +++ b/src/main/java/io/jboot/utils/ArrayUtil.java @@ -74,4 +74,20 @@ public class ArrayUtil { return false; } + public static boolean isSameElements(Collection c1, Collection c2) { + if (c1 == c2) { + return true; + } + + if ((c1 == null || c1.isEmpty()) && (c2 == null || c2.isEmpty())) { + return true; + } + + if (c1 != null && c2 != null && c1.size() == c2.size() && c1.containsAll(c2)) { + return true; + } + + return false; + } + } -- Gitee From 74aa17eb7c4dfad8aaccbdc97e9cdcb41e4f97ce Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 11 Jul 2021 17:34:53 +0800 Subject: [PATCH 1334/1965] optimize... --- .../java/io/jboot/components/gateway/JbootGatewayManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java index 333c1400..141f758e 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -105,7 +105,9 @@ public class JbootGatewayManager { } private void syncDiscoveryUris(List instances, JbootGatewayConfig config) { - if (instances != null) { + if (instances == null) { + config.syncDiscoveryUris(null); + } else { Set uris = new HashSet<>(); instances.forEach(instance -> { if (instance.isHealthy()) { -- Gitee From ebecd6ebd563685002b5111c280e776e754e6208 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 11 Jul 2021 21:00:34 +0800 Subject: [PATCH 1335/1965] v3.10.3 release (^.^)YYa!! --- .../java/io/jboot/components/gateway/GatewayHttpProxy.java | 4 ---- .../java/io/jboot/components/gateway/GatewayInvocation.java | 5 +---- .../components/gateway/GatewayLoadBalanceStrategy.java | 1 - .../jboot/components/gateway/GatewaySentinelProcesser.java | 4 ---- .../components/gateway/discovery/GatewayDiscovery.java | 2 +- .../gateway/discovery/GatewayDiscoveryListener.java | 6 +++--- 6 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index 60d13d39..f3e0923f 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -30,10 +30,6 @@ import java.security.cert.X509Certificate; import java.util.*; import java.util.zip.GZIPInputStream; -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/24 - */ public class GatewayHttpProxy { private static final Log LOG = Log.getLog(GatewayHttpProxy.class); diff --git a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java index 23ea93e4..43c71c99 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java +++ b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java @@ -24,10 +24,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.net.ConnectException; -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/24 - */ + public class GatewayInvocation { private JbootGatewayConfig config; diff --git a/src/main/java/io/jboot/components/gateway/GatewayLoadBalanceStrategy.java b/src/main/java/io/jboot/components/gateway/GatewayLoadBalanceStrategy.java index a94a95fc..8be6f30e 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayLoadBalanceStrategy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayLoadBalanceStrategy.java @@ -19,7 +19,6 @@ import javax.servlet.http.HttpServletRequest; import java.util.concurrent.ThreadLocalRandom; /** - * @author michael yang (fuhai999@gmail.com) * 负载均衡策略 */ public interface GatewayLoadBalanceStrategy { diff --git a/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java b/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java index 037f8c11..7c3baf30 100644 --- a/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java +++ b/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java @@ -26,10 +26,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/1/7 - */ public class GatewaySentinelProcesser { diff --git a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscovery.java b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscovery.java index a9c202e7..8e139c3a 100644 --- a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscovery.java +++ b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscovery.java @@ -18,7 +18,7 @@ package io.jboot.components.gateway.discovery; import java.util.List; /** - * GatewayInterceptor. + * GatewayDiscovery. */ public interface GatewayDiscovery { diff --git a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryListener.java b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryListener.java index 50530827..5eae6246 100644 --- a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryListener.java +++ b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryListener.java @@ -19,13 +19,13 @@ import java.util.ArrayList; import java.util.List; /** - * GatewayInterceptor. + * GatewayDiscoveryListener. */ public interface GatewayDiscoveryListener { void onEvent(EventInfo eventInfo); - class EventInfo{ + class EventInfo { private String serviceName; private List instances; @@ -47,7 +47,7 @@ public interface GatewayDiscoveryListener { } public void addInstances(GatewayInstance instance) { - if (this.instances == null){ + if (this.instances == null) { this.instances = new ArrayList<>(); } this.instances.add(instance); -- Gitee From 5fc923813313ae8e85b3c89a04d8dd9ce01dabc0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 11 Jul 2021 21:09:23 +0800 Subject: [PATCH 1336/1965] v3.10.3 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index eb35bfb5..d31f16ce 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.10.2 + 3.10.3 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 82779c48..48d5a063 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.10.2 + 3.10.3 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index a6d26f87..42ec09e8 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.10.2 + 3.10.3 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index ddec63a4..e9762c5a 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.10.2 + 3.10.3 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.10.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.10.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 113ea0d8..875a7cd6 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.10.2', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.10.3', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.10.2/jboot-3.10.2.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.10.3/jboot-3.10.3.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index b85f8655..cadc690b 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.10.2"; + public static String VERSION = "3.10.3"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From d6676419e5c982a3baa729fb90ae0ef844f4ef7f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 11 Jul 2021 21:09:42 +0800 Subject: [PATCH 1337/1965] v3.10.3 release (^.^)YYa!! --- changes.txt | 16 ++++++++++++++++ pom.xml | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index bc236e36..c5f57db8 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,19 @@ +jboot v3.10.3: +新增:门户网关 Gateway 新增基于 Nacos 的自动服务发现的功能 +新增:JbootHttpImpl 新增默认的 Content-Type 配置 +新增:JbootHttpRequest 新增 "instanceFollowRedirects" 配置 +优化:允许 void 的 Controller 方法生成 retRemarks +优化:添加 ApiOper.containerClass 配置,用于对 void 类型的 Controller 设置返回值 +优化:添加 ApiDocConfig.defaultContainerClass 配置,用于配置默认的 void Controller 返回值 +优化:对 HttpRequest 进行优化,保证 headers 和 paras 的顺序 +优化:删除 JbootGatewayHandler 默认添加的必要,修改为默认不添加此 Handler +修复:@ApiPara.require() 在方法上不起作用的问题 +修复:Jboot 升级 JFinal 到最新版本后,代码生成器的路径错误的问题 +示例:新增 Gateway 通过 Nacos 自动发现的 Demo 示例 +示例:新增 WebSocket 的 Demo 示例 + + + jboot v3.10.2: 新增:注解 @ApiPara() 增加 require 参数的配置,感谢 @lijiahong 优化:Json 增加递归深度配置,修复多层级数据返回不完全问题,感谢 @lijiahong diff --git a/pom.xml b/pom.xml index 2e30622f..ee977c22 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.10.3-SNAPSHOT + 3.10.3 jar jboot -- Gitee From e4c5fab55fe09e00fe898f37b1a698e8512bf10b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 12 Jul 2021 11:34:40 +0800 Subject: [PATCH 1338/1965] update docs --- doc/jbootadmin/feature.md | 352 ++++++++++++++---- doc/jbootadmin/features/account.png | Bin 0 -> 36404 bytes doc/jbootadmin/features/action_log.jpeg | Bin 0 -> 94250 bytes doc/jbootadmin/features/apidoc_code.png | Bin 0 -> 61490 bytes doc/jbootadmin/features/apidoc_debug.jpeg | Bin 0 -> 57240 bytes doc/jbootadmin/features/apidoc_info.jpeg | Bin 0 -> 51675 bytes doc/jbootadmin/features/apidoc_mock.jpeg | Bin 0 -> 39419 bytes doc/jbootadmin/features/demo_product.jpeg | Bin 0 -> 60013 bytes .../features/demo_product_edit.jpeg | Bin 0 -> 87087 bytes doc/jbootadmin/features/demo_warehousein.jpeg | Bin 0 -> 64972 bytes .../features/demo_warehousein_edit1.jpeg | Bin 0 -> 69633 bytes .../features/demo_warehousein_edit2.jpeg | Bin 0 -> 55768 bytes .../features/demo_warehousein_view.jpeg | Bin 0 -> 74235 bytes doc/jbootadmin/features/dev_codgen.jpeg | Bin 0 -> 135388 bytes doc/jbootadmin/features/dev_codgen_edit.jpeg | Bin 0 -> 65785 bytes .../features/dev_codgen_fields.jpeg | Bin 0 -> 94207 bytes doc/jbootadmin/features/dev_codgen_gen.jpeg | Bin 0 -> 63567 bytes doc/jbootadmin/features/dev_menu_gen.jpeg | Bin 0 -> 57533 bytes .../features/dev_permission_gen.jpeg | Bin 0 -> 68774 bytes doc/jbootadmin/features/dev_project.jpeg | Bin 0 -> 46998 bytes doc/jbootadmin/features/dev_project_edit.jpeg | Bin 0 -> 63521 bytes doc/jbootadmin/features/devops_ahas.png | Bin 0 -> 137146 bytes doc/jbootadmin/features/devops_apps.jpeg | Bin 0 -> 45472 bytes doc/jbootadmin/features/devops_cache.jpeg | Bin 0 -> 92974 bytes doc/jbootadmin/features/devops_d.jpeg | Bin 0 -> 76928 bytes .../features/devops_gateway_nacos1.jpeg | Bin 0 -> 38333 bytes .../features/devops_gateway_nacos2.jpeg | Bin 0 -> 51966 bytes .../features/devops_grafana_jboot_jvm.png | Bin 0 -> 117744 bytes doc/jbootadmin/features/ketang8_buy.jpeg | Bin 0 -> 33423 bytes doc/jbootadmin/features/ketang8_buy1.png | Bin 0 -> 38139 bytes .../features/ketang8_course_detail.jpeg | Bin 0 -> 98120 bytes .../features/ketang8_course_study.jpeg | Bin 0 -> 44722 bytes .../features/ketang8_course_study2.jpeg | Bin 0 -> 66232 bytes doc/jbootadmin/features/ketang8_index.jpeg | Bin 0 -> 93475 bytes .../features/ketang8_login_mobile1.jpeg | Bin 0 -> 54815 bytes .../features/ketang8_login_mobile2.jpeg | Bin 0 -> 56286 bytes .../features/ketang8_login_wechat.jpeg | Bin 0 -> 68433 bytes .../features/ketang8_ucenter_avatar.jpeg | Bin 0 -> 52346 bytes .../features/ketang8_ucenter_modify.jpeg | Bin 0 -> 29322 bytes .../features/ketang8_ucenter_studied.jpeg | Bin 0 -> 58635 bytes doc/jbootadmin/features/role_permission.jpeg | Bin 0 -> 73063 bytes doc/jbootadmin/features/sys_area.jpeg | Bin 0 -> 86311 bytes doc/jbootadmin/features/sys_dict.jpeg | Bin 0 -> 52214 bytes doc/jbootadmin/features/sys_dict_edit.jpeg | Bin 0 -> 62564 bytes doc/jbootadmin/features/sys_option.jpeg | Bin 0 -> 48588 bytes doc/jbootadmin/features/wechat_account.jpeg | Bin 0 -> 46435 bytes doc/jbootadmin/features/wechat_keyword.jpeg | Bin 0 -> 52700 bytes 47 files changed, 287 insertions(+), 65 deletions(-) create mode 100644 doc/jbootadmin/features/account.png create mode 100644 doc/jbootadmin/features/action_log.jpeg create mode 100644 doc/jbootadmin/features/apidoc_code.png create mode 100644 doc/jbootadmin/features/apidoc_debug.jpeg create mode 100644 doc/jbootadmin/features/apidoc_info.jpeg create mode 100644 doc/jbootadmin/features/apidoc_mock.jpeg create mode 100644 doc/jbootadmin/features/demo_product.jpeg create mode 100644 doc/jbootadmin/features/demo_product_edit.jpeg create mode 100644 doc/jbootadmin/features/demo_warehousein.jpeg create mode 100644 doc/jbootadmin/features/demo_warehousein_edit1.jpeg create mode 100644 doc/jbootadmin/features/demo_warehousein_edit2.jpeg create mode 100644 doc/jbootadmin/features/demo_warehousein_view.jpeg create mode 100644 doc/jbootadmin/features/dev_codgen.jpeg create mode 100644 doc/jbootadmin/features/dev_codgen_edit.jpeg create mode 100644 doc/jbootadmin/features/dev_codgen_fields.jpeg create mode 100644 doc/jbootadmin/features/dev_codgen_gen.jpeg create mode 100644 doc/jbootadmin/features/dev_menu_gen.jpeg create mode 100644 doc/jbootadmin/features/dev_permission_gen.jpeg create mode 100644 doc/jbootadmin/features/dev_project.jpeg create mode 100644 doc/jbootadmin/features/dev_project_edit.jpeg create mode 100644 doc/jbootadmin/features/devops_ahas.png create mode 100644 doc/jbootadmin/features/devops_apps.jpeg create mode 100644 doc/jbootadmin/features/devops_cache.jpeg create mode 100644 doc/jbootadmin/features/devops_d.jpeg create mode 100644 doc/jbootadmin/features/devops_gateway_nacos1.jpeg create mode 100644 doc/jbootadmin/features/devops_gateway_nacos2.jpeg create mode 100644 doc/jbootadmin/features/devops_grafana_jboot_jvm.png create mode 100644 doc/jbootadmin/features/ketang8_buy.jpeg create mode 100644 doc/jbootadmin/features/ketang8_buy1.png create mode 100644 doc/jbootadmin/features/ketang8_course_detail.jpeg create mode 100644 doc/jbootadmin/features/ketang8_course_study.jpeg create mode 100644 doc/jbootadmin/features/ketang8_course_study2.jpeg create mode 100644 doc/jbootadmin/features/ketang8_index.jpeg create mode 100644 doc/jbootadmin/features/ketang8_login_mobile1.jpeg create mode 100644 doc/jbootadmin/features/ketang8_login_mobile2.jpeg create mode 100644 doc/jbootadmin/features/ketang8_login_wechat.jpeg create mode 100644 doc/jbootadmin/features/ketang8_ucenter_avatar.jpeg create mode 100644 doc/jbootadmin/features/ketang8_ucenter_modify.jpeg create mode 100644 doc/jbootadmin/features/ketang8_ucenter_studied.jpeg create mode 100644 doc/jbootadmin/features/role_permission.jpeg create mode 100644 doc/jbootadmin/features/sys_area.jpeg create mode 100644 doc/jbootadmin/features/sys_dict.jpeg create mode 100644 doc/jbootadmin/features/sys_dict_edit.jpeg create mode 100644 doc/jbootadmin/features/sys_option.jpeg create mode 100644 doc/jbootadmin/features/wechat_account.jpeg create mode 100644 doc/jbootadmin/features/wechat_keyword.jpeg diff --git a/doc/jbootadmin/feature.md b/doc/jbootadmin/feature.md index a0d80e5d..327b7728 100644 --- a/doc/jbootadmin/feature.md +++ b/doc/jbootadmin/feature.md @@ -1,67 +1,289 @@ # JbootAdmin 功能介绍 -**基础功能模块** -- 账户管理 - - 账户管理 - - 部门管理 - - 职位管理 -- 角色管理 - - 角色管理 - - 权限分配 - - 权限自动化维护 -- 日志管理 - - 登录日志 - - 操作日志 -- 消息管理 - - 我的消息 - - 发布消息 - - 消息类型 -- 微信管理 - - 微信账户配置(多账户支持) - - 微信菜单 - - 默认回复 - - 自动回复 - -**开发模块** - -- 代码生成管理 - - 项目管理 - - 在线代码生成 - - `main()` 方法代码生成 -- 权限自动维护构建 -- 菜单自动维护构建 - - -**运维模块** - - - 服务器管理(查看分布式服务器健康情况) - - 分布式缓存管理 - - 分布式配置中心 - - 门户网格配置 - - CDN 配置 - - 性能监控 - - 插件化热修复 - - 分布式文件同步 - - 自动扩缩绒 - - 异地多活 - - 蓝绿发布部署 - - 插件化 - - 在线热修复 - - -**备注** - -- 1、运维模块有部分功能正在完善中 - - -- 2、分布式文件同步 - -当你把你的应用部署在多个机器的时候,JbootAdmin 自带了分布式的文件同步功能,用户上传文件或者进行文件删除时,可以通过 JbootAdmin 进行配置让所有需要的机器(或者机器的指定目录)进行文件同步。 - -- 3、自动扩缩容 - -基于阿里云服务器 ECS、或者腾讯云服务器 CVS、或者 K8S 进行自动扩缩容管理配置,方便当我们的系统在高并发的时候,自动去帮我们开通机器、自动帮我们进行部署以应对高并发的流量。同时在流量减少的时候,又会自动帮我们去删除服务器而减少资金的浪费。 - -- 4、异地多活 - -方便我们把我们的程序部署在多个不同的机房里,就算因为运营商的问题导致机房出现问题,而不会影响的产品的正常服务。 \ No newline at end of file +[[TOC]] + +## 基础功能 + + +### 账户管理 +账户相关的配置和管理:账户、部门、职位、角色等。 + +![](./features/account.png) + +### 权限分配 +对角色的权限进行分配、包括菜单的权限、功能的权限、逻辑权限(根据业务进行人为定义的权限)、敏感数据权限(根据业务进行人为定义的、涉及数据敏感的权限) + +![](./features/role_permission.jpeg) + +### 参数配置 + +![](./features/sys_option.jpeg) + +### 行政区划 + +![](./features/sys_area.jpeg) + +### 数据字典 +JbootAdmin 中的数据字典不同于其他传统意义的数据字典。JbootAdmin 的数据字典可以生成枚举代码 `Enum` ,通过 `Enum` 又可以方便在在 Java 代码和模板中使用。 + +比如,在后台创建的枚举,可以直接生成如下代码: + +```java +@JFinalSharedEnum +public enum PayType { + + ALIPAY(1, "支付宝"), + WECHAT(10, "微信支付"), + + public int value; + public String text; + + PayType(int value, String text) { + this.value = value; + this.text = text; + } + + + public static String text(Integer value) { + if (value != null) { + for (AccountType type : values()) { + if (type.value == value) { + return type.text; + } + } + } + return null; + } + + + public static boolean isAlipay(Integer value) { + return value != null && ALIPAY.value == value; + } + +} +``` + +而以上代码又可以方便在 Java 或者 模板中使用,例如: + +```html + + + #(PayType.ALIPAY.text)
      + #(PayType.text(1))
      + #(PayType.isAlipay(1))
      + + +``` + +![](./features/sys_dict.jpeg) + +![](./features/sys_dict_edit.jpeg) + +### 微信公众号对接 + +支持多个微信公众号,支持菜单配置、根据关键字自动回复、默认回复配置 等等 + +![](./features/wechat_account.jpeg) + +通过关键字自动回复... + +![](./features/wechat_keyword.jpeg) + + + +## API文档生成 + +Jboot API 功能自动根据代码、自动生成文档、Debug 页面、对数据进行 Mock 等功能。 + +### API 代码 + + +![](./features/apidoc_code.png) + +### API 文档 + +![](./features/apidoc_info.jpeg) + +### API Debug + +通过 Debug 功能,我们可以方便的对 API 进行调试。 + +![](./features/apidoc_debug.jpeg) + +### API Mock + +通过 API Mock 功能,我们可以模拟 API 数据,在前后端分离的场景下,我们可以使用此功能先给前端团队正常调用数据,等我们完成 API 开发再删除 Mock 数据。 + +![](./features/apidoc_mock.jpeg) + +## 强大代码生成功能 + +在使用 JbootAdmin 的代码生成器之前,我们可以先创建项目,然后对该项目进行数据配置。 + +### 项目列表 +![](./features/dev_project.jpeg) + +### 创建项目,配置数据源 +![](./features/dev_project_edit.jpeg) + +### 根据数据表,生成代码 +![](./features/dev_codgen.jpeg) + +### 根据某个表,生成 Model、Service、Provider、Controller、Html 等代码 +![](./features/dev_codgen_gen.jpeg) + +### 配置某个表对应的 Controller 映射等 +![](./features/dev_codgen_edit.jpeg) + +### 对表的字段进行配置等 +![](./features/dev_codgen_fields.jpeg) + + +## 运维功能 + +JbootAdmin 提供了强大的运维功能。1 是内置的功能,2 是通过适配第三方来进行配置。 + +### 分布式应用列表 + +可以查看分布式下的某个应用情况 + +![](./features/devops_apps.jpeg) + +### 分布式监控大盘 + +可以查看分布式下,每个应用所在的机器硬件情况。 + +![](./features/devops_d.jpeg) + +### 分布式缓存监控 + +对分布式缓存情况查看,刷新等,支持了 ehcache、redis、caffeine、ehredis 等等。 + +![](./features/devops_cache.jpeg) + +### Sentinel 分布式限流 + +支持自建 Sentinel 控制,也支持阿里云的 AHAS 进行限流控制。 + +![](./features/devops_ahas.png) + +### 基于 Nacos 门户网关自动发现 + +![](./features/devops_gateway_nacos1.jpeg) + +![](./features/devops_gateway_nacos2.jpeg) + +### 基于 Grafana 对 Jboot 的 JVM 进行监控 + +![](./features/devops_grafana_jboot_jvm.png) + +### 更多 + + 基于 Dubbo Admin 对 Jboot RPC 控制等不再截图... + +## Demos示例 + +JbootAdmin 提供了一些 Demos 示例,方便用户对 JbootAdmin 内置的前端组件进行全面的了解。 + +### 产品列表 + +支持在产品列表里对产品进行基本的操作 + +![](./features/demo_product.jpeg) + +### 产品编辑 + +在产品编辑中可以对产品的属性进行配置、支持多规格、多单位等等... + +![](./features/demo_product_edit.jpeg) + +### 产品库存入库单 + +![](./features/demo_warehousein.jpeg) + + +### 查看入库单 + +![](./features/demo_warehousein_view.jpeg) + + +### 编辑入库单 + +![](./features/demo_warehousein_edit1.jpeg) + +![](./features/demo_warehousein_edit2.jpeg) + + + +## 课堂8 - 在线教育系统 + +课堂8 是一个基于 JbootAdmin 开发的在线教育系统。 + +### 课堂8 - 首页 + +![](./features/ketang8_index.jpeg) + +### 课堂8 - 课程详情 + +在课程详情中,可以设置课程的标题、简介、营销简介、章节目录,是否免费试看、限时价(秒杀价)、会员价等等.... + +![](./features/ketang8_course_detail.jpeg) + +### 课堂8 - 在线学习 + +当用户未登录时,需要登录才能观看。 +![](./features/ketang8_course_study.jpeg) + +用户登录后,可以正常观看视频,观看的过程中,会记录课程的当前进度,用户可以通过用户中心再次进入观看,继续学习。 +![](./features/ketang8_course_study2.jpeg) + +### 课堂8 - 用户微信注册或登录 + +其流程是:扫码微信二维码 → 关注我们的公众号 → 自动注册。这个过程,不需要用户填写信息。因此,他的注册成本大大降低。 + +![](./features/ketang8_login_wechat.jpeg) + +### 课堂8 - 用户手机登录 + +![](./features/ketang8_login_mobile1.jpeg) + +![](./features/ketang8_login_mobile2.jpeg) + +### 课堂8 - 用户中心在线学习 + +在用户中心中,可以看到自己的学习时间、每个课程的学习进度等等。 + +![](./features/ketang8_ucenter_studied.jpeg) + + +### 课堂8 - 修改个人资料 + +![](./features/ketang8_ucenter_modify.jpeg) + + +### 课堂8 - 修改个人头像 + +修改头像中,设计的技术包含了,上传图片、到分布式附件中心,对分布式附件里的图片进行预览和在线剪辑等等功能... + +![](./features/ketang8_ucenter_avatar.jpeg) + +### 课堂8 - 在线支付购买课程 + +在线支付环节,看起来内容少,但工作量和细节是巨大的。 + +- 在 PC 模式下,必须支持 微信支付 和 支付宝支付的选择。 +- 在 微信 里,必须隐藏掉微信支付和支付宝支付的选择方式,只能用微信支付。 +- 在 H5 浏览器里(比如 UC 浏览器),能够选择支付方式,并在支付的时候自动唤起(打开)手机里的支付宝和微信的 APP 进行支付。 + +![](./features/ketang8_buy.jpeg) + +![](./features/ketang8_buy1.png) + +### 课堂8 - 更多 + +除此之外,我们还做了很多你看不见的工作: + +- 基于阿里云的视频加密播放。 +- 前后台分离部署,前后台分别部署在不同的机器里。 +- 涉及到的Web安全防护,比如 XSS、CSRF 等等。 +- ...... \ No newline at end of file diff --git a/doc/jbootadmin/features/account.png b/doc/jbootadmin/features/account.png new file mode 100644 index 0000000000000000000000000000000000000000..dbb7b2be232362b28aee3385d9d612caffccdd76 GIT binary patch literal 36404 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 JPDHLkV1n03k>>ya literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/action_log.jpeg b/doc/jbootadmin/features/action_log.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..664c5b7c2ee067ee732f96c9212cc4ff752ba89e GIT binary patch literal 94250 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%0D!0yl*srP9dl<xrvq8d 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/apidoc_code.png b/doc/jbootadmin/features/apidoc_code.png new file mode 100644 index 0000000000000000000000000000000000000000..946d25db3b0e91a8a67bd6ac1aea752f1285154b GIT binary patch literal 61490 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

      GF#>ES;c~EJxAxa*0_5 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

      {YO_Glvlv(u}z-}J#os)5?W?)xFIsj{<*t3 z({L}FkR!tgxlJ{;0?Gi<%6Ll5uFO>ikm3~Nbl2j_OK@sx_UrG6Qky7Za0PHBb~D{J z{xu$_Dv{!E>KZs$6b>>-m1uXaX3Qjn0(S*h_NjMD7Bg8qebC6~@6qXlY$9N%JibR8 zMi6u52_$F}7YY1oxVED;q%t3vHQJhKHjWFea|F46viiHJ z(@s66D&4iXR!?+EYR^;t+0+u$Sn+C+jz0=N#Oud4NCAmH0=xNR^r)(}>I5P|g-u2h zn?udFk{Vm+j6Ag&PSPiyc?A^WqD{lf`1K%45}Wt0&1b|cy6j%NnR(H`CCLXdNq+L> zJN04JVN}2K6CgI?u_E2}V||DO)HLN4Ze%;JVdZ6*qhq>UKiS}@e$ndC&H<}wsYQOV zQ(10%3JdHHazt@8)cMabCmLnZ7Q%lQ7?qX%en6)GB_XL>G-S%Ux;lf2Q@Xx!28SJ& zb{`(!PS5ed=hX=N>T2bX1Ka5ah0 z#|ElPBk>UrNk!& z!wUsP35EW2mMxfG?^6T(kvCF)@3lP9oCpI+NJszxGBQ$1sz25QKzaqpDWdqu%AG@3 z$0G>!E~B4TSm9aR`%is8{umP~dBBOqbw=1|g?)F5BoTS-YYff`T}B{pyP=y@FEgE} zcFm3--^D-m0Lk3RsHS-9|ocXn3-gJP^o--&@57esAJxUiH~)9$9gP-z_TIp2uJg zO;=sYu(<7n&Am^ndn7|eh9#y|&i@Gc5C|r{D2IfW_Ma)Tv)>(3(*isk_D^|hKK3T0 zJv`NytsUEN!F)}7I^#Y9lJ+C*?VZ^n)Uo`zSi0=Kf9=}lF93#A_DSlKC%(J0tBbKv z>9E;}24cA5>S8>I2(mk0e*qflh*}o{0!T@JCu*Z4C#s*Qe*iI08}J;xI~lErg3c8> z22NqoN8}u0;))<8-Jmn2pZo1WY_BDaaAIzM$l>?$I_2RGk#@FODlES-0WF>+9uft+Ha3eycLb*>4kfz=N!5>;m@!Y}nKHDjy-Rnzx zo_^AeZ>M_P9Hc7F1O(3vd|o0%UeVm@2tz=In~7N^Ue)giSujjvV%Rq3zRCMi$MCfB zG(Go}@Zv+uuI0(Gu>DK<`r_{BJ4k^7G4NP4a|*Ix*{xzM+*{|#D*K;$%6rFlyh038 z^gf+YZ?6^DzHJC}TW#(>cuq7AQhxqF``_6cEjU%F3?VxZ>Or<1AAN-K z{~e-{3Qi0i+TO_QcdRtM#n|X}I=k5nZrl!^=vnd+tBrZsA+4g8GrP3^mkIG7X2^eD z;FXYQuC6yb)qH$Wee`-FjT!Mz#NaCZ=wyzJn_P1tv&(CH`cSN$JZVILMh1brWioJ& zIgI;TRC>29yhzt1wHx(Q>Bn~G+bOBB@*H)?RkeB*!If5QY92SYp*i%vlE*#_JDA?p z=_UX1gCB;_vS)T0cO2Isd)!62uZ1Lh_4U&Z$Tt}<(3Z9Hs(ZuzW_gl%7g@I9ndG@8 zcln3l_3r)_v6>(24lQNrm)iYhuKrlRf9Ik{KuzB-0O0Rtq{SS+4d;+UBsm zH{qm4@NvuZg1rR#02h~g%70c`q-V49|~x-sY-4nFhvLe{gv4|88MnK>o6irc2?cy8N7h|X?)PT5Bms4!>F zvm|wGO*DIc;E!i2m-jrXMSl*RO2_KLTf_4vI#1&5y>ckR<~2`pQ=hFK07eF>9C^7!N2Du zG~nKEyqdZ#w~#cs75LV!v#jmwL8924ac{Vdi1+(0ZgPBApC_^;@?&}5n3B`6+vuBm z=6J|qhsCLHK+p4I>YR7(S=xJon1l{+Rr% z2|rFLra#TRTRc`y6i0o3=eKhGh_dO~--raT9gFS{OwXO~GpMniVMIlS_B6%_8vF}z zpl7L)v}}>Tq3TQvE&aQp@GoN^AMde0Li&?WF&uf_5hOK&X_xg zh$5$OP~0O0op*os^XEv2F4$3M2cjb>%@USgC~5y2d>wjk%{Rsn+V3`0Z*_#HC8^j4 zkfMP9gK%=$Un}Y>p)C2|!yM!8B4O~D-9dj?uoeEP@4ipi*j?klyA$^5*Bdgaf`fKd zNlOC9OZ%^Ju`SV^VH-9!#CH~y02xP>ys2=TKfvl=W&mde$4zz4Yc>P@SWC;;cuL5+(mNZ|nf?WZSRH>}T} z-9I6f@?b0~V#W{@T|#KVM?jS#*yXkH!QPxD19dzR2v?d^BLBel-N3-So;+lyaV9Tq zZmKAH7wstq9LYN|Wi8wk!o3 z_617+xiLn1IJVgxA;^P{w=cSJ`Y6sKpmjR8`!;gpnQ1i{q07rcV67&!iPZ!x{yX6B zet@}rSFtPGuYE75Nt#%D`WDB!RdET44bOrvP@!sRWeDJI|3;LUEyO_dfHQ?bR7uB5 zBa#Y&9OR%5By#}$&J!C$)`ai+YNIItfXn5B8Dj+iV0Y@Z0FRha=tdQHto#noU*Tfo zB)h_uGWc=sm#&une zk^UyQxsZj>@xsSAhE|ly7DKIGM{S1zQbQesa4DJ3)0hm_&X8`RxX&Pyg!4H}0{BYS znAy)cuLNL}K%r7P`U(Rv)3{Yav{njUh#4fMlvjUK{4sRo83R#dunpFYp~SOh2qV!_ z)*ZxSJMEUl>6Qa#MNuHIg2W@P48C%DHjGq5nFTqeJEJkv07e>xss=%}_Eh2)wm@jF zMO9JO)0L&<5y%ehAAPVpt0CB8V>B)U-G58Y@!-n>b$lzDV5iQ~dOZ+!CZS_jQQA~>4nV)LRrt08A3e8$O=hlQ9CxvuH zk8uAKA|6m!FMPfX!8ZR?>n}frZ64&OR=dm04ZdZ=FmvWhCX){d^k#M15&*!?`lYPZ5nH<>@5?MaT9qrLE_*|V zoh@wy^O47q{|1J+!I{^hM;!h~-s6ZpU?be=Q>PvP0mx35CN@iRtmRL^y`efry)Ocp z*3tO}-h^%mmt=2CbLsQ&V<>+J9w>n41G6}F`Z@+~+Kc0Pg1gLstMm&~@f83pm6`Xm z=}_HgTu8QM;kvKoDgRK`F*2B6c6aLo*akYD8785`XKf3T#o29!srPf;|EPt+)cZ&< za$jm8Y(?A;){g4`5L$tG`ps`y$Ee-p_`g~*L5|h@4freT|IZH(=I-QQD)N(@qBXZR zrQGeYa5~wY(#YDs>HRU<{b=AU^|?lFDMgefzVRrK3uG7Jg!Ow)93wgjqH}Z~RFLDK z9DqMPS1n*w`|5Snc=$FrtZnUPj7v=lt1J18Dgb~BKlG;049WP2F<1W&ed1wC86TpS3TTJM|I=gS= zN8R2x1K4&LXE1`6$Vae1E#Ok&{7k=Bb7n7u-zlKg zitg3@{H==zt2=$>em7MzF{t-l9%r=d8qO)Kc1IqTsp5?BMK9)h`OsKfk;xq5dEbKk80QnThjX=X z!jD=ApziolU{055{qP1cWAC+>gw)oW_L;e0Kut43jofs?%@CcID3ow zLGkz#zOEZqgays8%oPcewJZeWijCeX9|+6H?R<99JOA00AmPNmaBqjaC+qLOccQ8YiGYB6~5Q_-wEF_b^a3(x$c9y-H^qGW5~uk`0*?GGB!CyIr}Jg ztw<(|`!KtSB7MSsfAaiQX4b$|U5h6DqF+ebcl2f*eFHhmE;ity0o}+eWIkzA!i623 zW7BU|Za?jbbbL8jV0@7^Z>TLz(fl!DXfMF#eFghqkiu%ewiyxR7i@Pz$)6I2uxJ$k zPijv%=~bExQuHz<4ZF>v0n_C39m6F&?33{kF&Cs7vWD7xuXF&RaoM6j^X-GT*mI8A zg_viBI0VDj6`(`p6HqAH48Za9-ywIo)+{n5$;#SdsawB;&n9; z{<<~hi>3$&j1la!n}>r%#=9SaWW?R4=w9*oH}HzgVG%{;5c~zOC+aLd@zbR?Rt0bU z%lkO{>7!pz{880E8pj`TJVA7-vYo*Cr-JW4HSQ$1V784U8;J3U=6-w0BdPPd=q!|4 zZu`pxfb{`s=UuMph8O4cT;4L{&WSY+_ew=Rk+l)zD=X{MR%aHQ%U6KEp<){aMVMlI zBgF>ITAI%52;{i)$NOs<197vqVpi-EnetE#x`%l?Qctz>CY4}xQux}$huCqjO!ls!ph3H?OUnv zIc=9?;toOL&+e~2<*j5yr}b%fr#HNt_&SriuBx1QD_F82Ci9|^SQHp@*D`INdfA{3 zdi*+c0|54>6BDaFQ6ZE9Ljp^SmekKYXKxN5DJocFzaTJtLH-qDx*m z?D9$AbXR*4?+;5mQ59^ZKce2xmmXF89kjQi!u2A^qoeteTrkhz!`WeYTwVW8z&`c< zFbNmyYdGF*C~If4xo#pQ))}3=)Ba<$0AEKLy-)YYk9|9QU(4G5u)Y^bOw7bbbeGxT z0K%mPmzuDMSZuMO^Z{!EojdlA0s&)1i{x66Oo4s2%k9p?$EKP~KOB57=J9j8NIXr+$vfx9J7M5|=Cf}uc5)pK@j5P-lS!oZcQ*%O@`{#LJgF$7 z5w8Se%fit?@c&NkY32!R%*d&Z?mjyip5I^=yRo7FJT@#;@oNFMMO9g-taT30Lhs)R zJ}tsQW{q7kjZ-I%39;%Nc1Eh2v|YF`qi(8YeD<@BZHK0Xd@tm$1fM4Nub3VY?~kW_ zpre`{Mk@g!E_XA~GCpQhO}r3Cp=5U}KB@ zGE)9$xjPBqUvK-7lE3;UT!h#vz*EjnrhtZ72CgZoixn3y$q!!Bilm=m z9F{LWU;?~*g%EH#t17Fg8;ABvbW`nsZW1%v4e&wxzkKc)8W~VE*7R(BBKHiv);{53 znHu)boGR> zB!C?96@LjNJkXcJ%6|py2P_;Ve4jP!FuZA+^XfryW|5IayVQrvJ}{Uk$iXM3%}Xt2 zp`1aUu&h{gpFL1AHq2H@4}d70Ox&2}PmmZ9Lj&1w`?rrhrGWdK&1DfdMCg^X6cYm8 zjRue9-r&^}JA(}j5O1)*4^A5HZ?-5;4@fJ$xf{pr$YEm)p<}8g2os`In0WZ8l%^rS zxG3s6?84jceNG>7xG+OoIRy!t4hDIma|fma07^zT$-=U0OO3SX7-#C(wd>MaXrJKx zp3sBb;}O-7Ny6#~k`Ei0v}DNJt^siW=T~R&Evi@Rn*MZCPPB&$qV+ab3;B^Zgpb(#(T zOrx&u^QaqIk+97uUrE&AZH9&t(fCSIc&)h4YIaJ(l~SC}46Hx$1~d!bwrFA~#E_U{ zAJ8$Ot!N`UHxf?5%Eo}mKFH}`ke3yzIv#B)x0{Wu$|SSYW&SA-bNafsBQ2j%|GIQ~ z@(u|5pfNVG`?r~lA^QYlqr#FUKY!5LP;i78kYsh?KB0iHWfRE>afO&*XfT`Hz1Mn@ zVsC5^9whe?BejeSpu&Cff@+?D>>?cm<~s$n+TE(1uk%+R0UC#;lK}3XHt$v<`zD`k z+$rs83A<7Y^xRw-#{ytwn79E`VUOMahz%U;!twVxR{e+IY5R*mWcMT4KP$zL*uP&s z-CTd4*DomkADa>B@Y7FOe!=Y*ReJ(#Wk}L3Odyu$z8<)-!z2vp!+HW*P{o;0*dg3i zEalAZfn^6Lw>RHM1~mH?f5GrKs`erR%wha1;0GZ+XB~qX_K6QQ82DL_y@M^$@CqsZ z6|10}@BD0^lM*+(f-7gIZarv50 zRLe-y_08Jwu2(;paer+iJnszo1+RZ!_5Ta41deL+kV1Kf2iV3-+2n)HnC-p)o_oM> zgCzwFa76$8n4FFYd@^^FTmM}B-oA!Hyg-SI);dY$5=T2eW3S|%h&_F+OWRL|oX+gu zwPqfXg(;{0J=>&MPeDx9)U z@Ii(Ai2Fz7L+8MuKxs}GNq~QmUD08W)8WAV5#t|;;1FKyomwiqe_;HAdum$M?+R{S zSoD3!F6Q1j$bb5+fY$LUCSf!vUgo|CdmY-BgOa!>cCYf!zgVome98l!x;J0^&V}DC7dc;yH{RrNJE{FVB^AF{v zqCH(kye9_^(kDJUy*_&n4~O5~~A1qbqC_>a)W4`5?%WhQ*iEBgC*z zrX20QcN$S5k_1ar=mn{q)gb%G|If$4yB|Z`*F>P0s_MM$nf(R?S>h(#TlW=KIR@*N zjv)gsFAYQygb8v}9coq&&QG7Coz9n$}9SZ*ENYEIwbO^La03~>;fpEj2GGFhw@?^ zRPpLcYA+XM`5z9?Nz4-^xRN>=&r`0<98-#4Xw1^S4N{)q_v?aA6z+j5Jwn*uNYOwJ ztA2{Ke%78t+@Api+0!<=p0kJ82tUOJM_P1Pm*erNW>W0T!1vwJw7l6m^+2 zV+apu{^5ye?sMuC`BTSp)3zKC!xk9t`fCTJy(b)vJO;jc-djf7VR>1Hd=3;`1{OxP zV9c@#$VtRsmHtsQ0*F_8%2wgnnGg_9^alpG zG+IB@{6kSD6i@V&4C*mk`lZ7E1J@s#{?UCKN01=t91vU%4OFoRJ1FfvVIG{GYt8U- zXXGO(%oqnvrSn>i1iS^M=|v~;ep&kGMT_IiaNOa9_){BNW2RJ|WCIhOtqT>GR&)g}_1~NU{&$Sar6TkfOI9V> zjnc?E8pz?`9aEmYx}R0(5cg+bZ^?rFV;)W%01yF)06;)Mg4_A`J+Q0%u>Z`1fRBia zcLoWEkOP^GTZMpTlI#SZ8dt+_bQ~S(+uE z_+sEjRPRXI+pi*+TX9*bU)#0;I8IFrKH4Y+xcXF8lGpB!6V#nr@VWA)8BaJjmj@})T4?^gf)5Kkqwcem!B2#d_I@sxiZaJea#~D%1SEn<4m-O z5yTh-+-HXDsVX$ZYDi?biawqFcNg2G9-W`^@z5KufjXVJlefU{B+-JLpKo2Eh~wS< zws_M&do<`FmaA6kwN*?mOlei7FFxdRchP!_HYUH&bYw?LKwGZ$77fhTc5GCC6Z#Im z`_M9t4BiOZoRPfMH9IGpLKOehY(m@whx5Z$`X@s`zujBJ(saL`iOJm(k|k%~HO7Ud++3vgx}MD@jNDCV({$WPw0ji(g?Pi4j9NWAxAx)Z z>aHLS;xyV=dPH6kA)%V7?v*y(-1g#>>y(A6o}#G)D7>?T+{W6Na1RO+={DDHY=*tN zRGw-`(YkKG@>-ID+PcgZx7Bzq93Vw`r=@erttNza1f%Oa>HDtFcHGaKOx=;wk^x*z z_1H5sqz=x~>D*ye%`&}infi?MiyOSt#T{m`&0G&`&JFP8IqLlTV&OgJ?xjVkuroIF z1FCtcx>6-U=A^A$;s00s4~~Wyqt}lEE4_w&&q%3Hb?e=_)5@YG0bdjD6pypTO^1hw z-Z+jlh~$?)hx)?Rs&9?%wNHGP^5i1rmu!1Zb2~PR812-5{DgpIs%j!=BS#kta??K-9o!dfn<17CTCRL6I zJ~QhS%o=`yq!G_o&LJ?BM9LvPllXax6!ZIu#~RSF3#bi&AK~!ed@?!pA|!+nQ7>CL z;_K&(J91xJzIE|+Hs)i$R8Bs>Fu#|wMRz~KYU22f4&pY$%ADtq<>jCO z+29+9g%4hrt_R5Es=Xf!2vnJ+Yn-@5G)o?2055jVYN5I!!2n#|WaEo&{Q`rf0sI-~ zb%DmGx}G}uyRNGlGX3bsrp-`{lgYZ()x%Q_Gzf^8e zrdIK6yZli|n(odA`~IF)i8(EO?_yq@chQ6Og_&Ube0e$HRLh&tI~=BAbEUV|wKc%! zvxF?AbKUbZrIz~Rs+nU%M=E`=ZOh0O=Ohy=Q%BL)-RV@jJ#%8 zK^?c#_Zrmsa)+yM7*RArKL@=ZU8{YC%oI7F*J?wbvo@@CAc6W-Xul%A=wtlMOsbf7 z;4yl6I!9o;%9G60R<(zSe0SSvGSd^CObo^HTt+%AdRsfC!F_4`P}BmeZr&k@OoIi? zF)t!t@7T)$?e@e`QvMwwE1W3<2rtghikfJ9WoHVRz@cWfvyu}&GAUP3PS*jKAc$n~ z36yd?P1Li0_bIHDqx7kfv(krc0MAX2q^0L%ZUU;(-w0nb+>on#y!mm@{uVc8Y)0a_VvCYiv)rn_-Z9*WtbFHYrtV6H{K+)bYh1L_7|7~1;`>S`^yBMt zRPhF`d@UTvxW&uGSm_cnM{U3(%J}ILYvOG{?T!Py{Pu$0uszE;Hx|cMIGRxx!gNT- zn5cTW1q&tl;|0a!n*~;nNfq^nP0w7?YUiO$ClOaBne~{SrIq5fsgJ2S1C3NI6?;SD zlgZ10hoy@eu0&!sIfO;TeqqFm8Y47Jj){I7pou&h9M$+S+r~p=d=OfWe<#A;{OMDN zm1kWn=%EcHF9VDlp=1FHtb}oau`SYA9Cm_uiRHvaZF^bv>_UoI-1GNe{MBx@nrGB0t=&7jMvX~@CutQXYf+e0cdf!; z-oBr38_+MdHn~P|@9o&7jIWp8wB2%F3 zb?Fv)4NmLk&dDINQbR%@<+B9x^2E0GYbJH9_VdZ#96rqhodDGk15xH=A{F#3#6Gy+ zt{8LS@NP{; ztvf7E!K}acb&}4x(gChWhE8gJ&$_J(IQ;@0{0PlCcu0MV;YPJf+Up^-Unv!%6}loB z7`1frigis(B(L0mN$~Kn*l2#gjo_&D|B8P(8uY@iJUBn9m%Z1kw38c-y_)?tLbS{T zQ;V3E$4&zS$*a$aRgWwZ-^R3on6I42PD&fxKllm|{pO`k^Lf2>aT5%cu)Dw z;TyJBO@k-TORY7%Zz#gA?mV$HC@mNsv|Xrh8s%S1aPIu=3Kt}MA+)lzV-o5=E)l5j zIDncW@5Sa%r2@}8ES%`cok-Ua=x{~LZ~!Usom!b!!#kM-v4pr<6~QT{W6t1S9|nK% zFlF5mJ|6;UT4vLU#r3E+_bT>~5EE}i{CF~H zmy$Yj4kc#27r*BDZ(hAALI#i&P_e$Lx>echlw)t_ZbS$Le!yG=z?Il|*SM!v`g-X& zmO*Qfm&RXhEaumlp-U|-X+>X`XwVQRQ%wO6u`GbB#JPRSJlo=_Sr$aUIc(;A=z5~# zD?gJ^@!Wx+fs%=RXPB_R+E`yt_Mb~(fluYIs31_W`Qv8a>Wb4;|VBFgI z{#&`9#`9aFHZ!>T;&4zFtaMB3Eh^N3c;i8@z7cfoY6f6AaVtRvu{wpZWq)3RvSyl2 zxkYis^%Hi75;oRRF|8MK)%F!0!>C~Ctc2LNGirTUpIR>v+|p_`zAro@uXV;R^0%m5yVRkrQl~H6|v07gJtd( zXT??HNgX7=PkbkIzUgc?oDlPshnd0I{+ZS`pnzp;WZ*{iweU{#az#Q7-k{!e#osD5vvT<(>G}VZSl)-A$hx^KwXba$m8OH(bYjfAGY^ zXEku)t_tr*B$;OMx?_D<=ARR8G6!~LzwNPFoOpDXaJIi+kjw%Sl*vnwv@!L$Z%EO- z!#d~#MWZi9R$Js_o5Xw8L3uV}+V(}O+}zn}3+`;fcZ&pOO0DN+z5C!?3++GhU|;G- zbK$vP_E|hhO=OL3!{nsLe~>Fhniyl@$+)gATO>~Qofa#dl0{stcHB-|f-qAmUmE!? zC7DvfmqZA1>DBfdZdi#A0iyIZPYppqipF8pqTH5kk@4 z-KJcMV09kHzO1ngn7zYJX z_=f+T6}Cb^)746g3iNX?rCU|^-WYrB{n&PnbBNhQB@Mnm)Y3iL3%rCrTkGi@a-;jK zW;W6m4DF$y$Z3Co&zx!`RyH#!J z)DT%}U;46$H)VI(>kdI(*=@62-B&@z*m7nFSA+v3!Ew03D8k8%UFP?=GxG;W(qp2} zOv^*B8wkEV9xa@C1hq@TAS7mF)iEx5Vd7Pfco+S@UX<7d)U>|9-uhn>fAHF8D7!7K zwnE&hzTZ2VfT5H7B@dJm=ZT&_Ne=%fGZt620b?`m4oRP6nc>5s7)js%mphd5H1_(r z8W~um{Fwb+TNty#Z$4P=)hPGxhOgh@M*(ywkyBL~nF7ip6m#ve_CoGOWgPRLR~hR1 zjdlCDHb71#a2+fJ&{8{y{F_Udib{Ufba1Xl1J# z_LrZff_fjgsQ$8r3SX*0XXl%in%TpZZ3T8`Rk1%5rh&Po-r<%l)ZXI|zL#~moo@5e zntQb!`NE6oz>5$ls)$yE#G6vI{yZe7PEp)DJITJ?O~LFizzz`6sp6L$jWQw}U5j{S z1M9)k*hjg%m`bdB>2M~|1sa8}WecEGxq>9!Ts7Dd{i!^iEnlhotR%59ZMt}LmIj&1 zHXwOmccJzwvUyiGqLtWKrnycSQiB9#Gf_;jMlGwx#KKthW?(Iw-*MFeqJTDJjkyPj zcih;%jLZxBu`8@ETJ;FT;g;zLFM^7YwTg1@eGENVwcEy7kveNs=#VmQK^*S|zS6pp zQMMovuxkE1*YM?~Y2za4jDZDkA=)!tDQvntI^!-ne_uq2o5@y8G7s#eUK*|5;mJi; zV)Hd^kcNMF{vbhi8@9H4G@~-t5*ecxAr8U42RZ|u01P1i+ZDHgFh^m!j6ntaH`|z5YhM*Me| z(S0v|kYe1zt(o!B)<-Wu9E4l&bgHc$Gq}cFMfAU1A#`mUu*7}~3D09KWG4r4%y9E{kZTseXU`*U5X!os|$|chnND=g72EXf8hGiXWc+{q*}+6J&}HuFBj@y~i8XjFHzerh@Wz|a*IzG#0smXk?xKH?lC2T%N2{c` zO-?gCLKaCFmu|Fxub?H79reNhh4gM=?wO6Rf^zzgx>rfouYF!6nWmntE`P@Lfd}H5 z)1ORO9q_GjD$m2e+BXGjE#XFeH!r$c-&!X@_V5|bCq6dym>{(@H~h4fRX-+1Q&c-5 z)uoIW5UDIZ2(wtkcSe*{=CUI12-|`dKGyp;(T(cjrB)+l^aHpGZ_aU{TKF10Ug)zY zB3R@QOLfsL1Utdjj@9?t1R+&HhE=&zswe{ecXV@gJy4^-p#0(P9tMJjo4(?%M&g-z zQ0*_3nwzRpi}U^dKve!AvR?|QGNQo1bP46rlBJc{mWJDSnCJtZs13 zo(0|52He&{*T1cR$sA#Zkura;a@5pn_1*PIoSwP5;@U4@q(uoW4i28S59P1{{tBw` z%rPvP$xn7MOuYZW;`2C0jL3!iOT~oN+({zg%_t8rQFzBMN+~x92r%Qk4CNhrXDSjy z@A2*fY2O5iV{9!^FadqpqvbGhCM0ih+;gNjJNmYzjOC}EPOq$1z!Txvuojy?$}A8y z!2}|q=_bfTD67*HPPLwttjvU`-UhsE*#Ddfet zUdE=f8M3l90jE3Wo$O`N?)l+vNU??k8z^uYvmP$GQENex#VbDFIi0JQ5)-b!e^kpP ze}0r<*40O$&ZDl-av`JV!&7prx@b(+uvKdfp#|1_P_3o@J0Q`KAAL}H{RU?)530VxLr*=t~`8!ih!lbZ@*L7R8Y$hfF9 z(f3%+2~dMx;A;ixu_b#k`CUHOs?p2Qyx;S#cs`aXKKRT;*WIK0nZMNhK_eQJr zfF{B+z3S+h@waD2`tskT8R^<|U7bZ9H&9y3)N*{LmUmV3o?vZXfqMO<5zbII5M-P( z$#8E#fiB_OfKyms@?d#u>_C#-_m;;W2lMHAYYXR{=hroO>aqR!I4Az`Vs$Cemt(E zCkv#tWf_E`q4()K%7bySr3V+Zs=nmSha=mFA^VlTTyQSCzOb>CpEjC>ls~C=x8FuH zDY9@fHr%#~!HS$dGhe_rvOHfx_Hic07p~USUfX$2&q;}P6Wkj@vgK~{M5C?!p#u~b zo3*zAuuENRoiyG`qzU;7*jRVtCftnFJNuoI2r1D@(;M(NZrTP{LpPg<4NPcRPv`mNtrkxq)xbrt|75Ni18=aI%kYeF3~C)Jg%_RzTb48 zZNxS}yWX{^Y?4@u@$xl3DuN>&OPyF(cuv?)DUe9oFNR)zx1Ve!==}XvCLz?BK~Cx} zt;`$zOz4ySpZjXm0=YO=1rgA$EfS)ww|>;A`>1o-bHR#;r}svX&eP#VNY|rStV>~z zoY<*@e49jC*`URwEvSFyjkSQnIllb$*rBJ?aF zjP_JoV;af%IFVzps4nYGp^yi*6tJQU;d*Tynf1Y*(5jBCC6-9c%3xGvbMWFra-6x` z5!{^eOZHm1&;7@TbQARK)8mA?!~4$Te#v7JG`B1bjdf+Cw43 zD@X=kFV8Xj?OBdy{u91-8TD*-HmCeeHB8B}4k=Q<3>l5yTreogPt2oX#OYgg_@%k$ zXEO`D&R{x#hdt0My&WD?kzg+DTFOI4|);4<RFE)Rc zXaY4*cH1edmga!okBB}oEh7bPD!s*R3^XNIq+)4gT=vlnkSei0jY(di9-1YH*P08-y>%oYOOVx|fwIv*#kj;&Y1J$D>(O<$4L z_%q(qpS6y~4&buch*BBZ1_apm4Kk@9`*79Ulrs^}mbx@=QE~@wW||Ydd;4iI=&STb zp75*VL3V2XWwqWsw&&;OBy+h1H?37h0#{l?2)(nPM`^8d%_{~7` z=I(h`ph}vKEIF(pMWc~=?%k+nqN38Uh+bXPd~cZ!`71-hAmpR{;O>E0B)iMYbTa9N zNPB2BMGN;d&O`wC%GLIGP&PHjmGZ2}b{ZZ#+jh1W6zV1NVP&k~Xh~jbML%67kz*GU z_KqV@_ISwsqz+48Sq`&*-c>O-Jp4N8Qt`0FivTF zC93a6$er=bT}*2N1_GcTUNuf(nJ_&wj>hSkMQ54InFYn?eUB7JwQPya)zye8>0A4k zNy7$VS;kyl^tm-^wb@|)n^~F__?1gXuksnCj-v~2ih>@;aq2}v0-MxSNVGW#pV?;A6->*M87I?2`Jkw!aO=_91K1eQPqHx^Bd6;N5@1iyLSMncGXn+Ez^kXreC7jG+j zfE+`k^ZTA|+r*UldAGN9g!gg`2!pdp-vq8%W~%a)-8YAFiCVo_E+;!jnh^$W5yQq@ z4=F|Vw;xXOTN$}V8C_OrLnLT)Q*}kNceEe-?cG?}bz;cu8Kb`O-Te50TidK&MXo6z z#j)cZw}xBmKs@BfZU_m7D!J$B9;%8S;JhXz-xUi(~B4WANvd`SyY z-_f!>3y{gdWuQ+*taG1Xk%W&FHHv?e*^^Bu`p#@d?Pu{7t z@8fWRL*7(ZD)H3FTMN!vSIdIGtu_A$c2;M|$Dnauob=24IZRs+Wr29e6OrbsTUuG_ zFP>^4Tw zg)Z26;7HHg7gvQ$f!(C9kp>HV-`bhJ9=1uzyM$DX?nF+}y%QEAiX*somSb|&wbyxI zaiW{F8c=1eW+6&T^$x#L!elQi)CJmD#X2DIgqn6UPP{#~EAHvHs#k|Hc3((Bv*t#T zs7ZU#mxeeir*n?c1Iubn?>4`*?+rUf0ubTTOJ_OCKBidV^jPws!A3K>jI?>F8`K$` zu9gf`plCTd-rDr>R=Dmxz{B0Vd&#=(BDo!3nXZlNh1}0EIGhso8Y$*M<5fe+&caC< zu^H*E?|`ZELh+EqKytrqxJ=(^fhbu4je_!dX9jDKRZrL_#Fw+L)Xd z3$gWmpDw<=~aU#d^pewOcH;YyWp5+a8mrcAXwrPxD zfPP?z&D2=J%bkeU>2t-r-NZFF&uuO2S%9uj;O`I1p|o1y%%I?ZgwnCVTWn9sdN%n-T1GWkq%!tEOW6JJQyD--}PA5%{R>Hv77#<1g@ z#S^%0vg`P&L;av)7KF<=wTT75w|tPRB$mPJ6i1FhwHq>0IUzscuz;=zQ10<&rE_0^ zrc7euy`TGTpUr~jS60rDCvp$=Uk9J7?H;eM0x`|x%HFn|YpoC9mSDUqdW(;)t+OGK zk|q|TCxBD^d85UrWpnw=zWEk@GH2mZ&^k*$=lHDxs}Nt61!<6@QpPpW6k{uHNj?31 z-LCZNKr@swv+r=b_Y)3pjzYOWGjTFq<3`xZ)0fH{1IWnOgW%%J-qvkkJil?njYo+( z@*&KuB67^iz>P_t5EsRC`?EASe8w5E=))yo<4e;)|3naTz*hQpzur4MDW!Kw!bVF1 z)e4Kc!11(orGkg5c=-tkmNYFL$>_dAL{I=kUI(Z9#k$5yU6;(+#IX7e*j2R?-k4)~ zoTroEkB%d7DeJt2VZYiCjki#9*nH5w4d6a^cIrFf$#>pa9yW`7{JDM3k5k?aOPnrF z$CnonmzT=d|G;z6S8TTQL1Kc+N{-@(0A+dsg1I@(AgMV}K2GAgjl|L-W7XY;0MUm5 z9`Hv7w0jqBu7X-!HQ(Z51}0x(f2>r7n25uSmfu@V)_g%K zzBj(Q0FAj7#H_%jd`Eet9@8dPnpBFhV;iZ!S5;z5h<}vj_u=JD{vcgIcUBpv`#kZP zc#m#OIzL%j@c1_11K@`f10BG>Jx0KpYO_K!CMgVx&K5$vt|OM zJ?}-dYG7KCo~dRB->{J|nw;xS&V9S$s@Gl5e4*YrrK+7=v%M;K23+T6Q7Jc0gE+vHiIstit#~s?VQx>;{CuetwnX}TuaDO2Et80i7lSqE*MoJ0&c1Wg zYlzGRS34Q24RSqv4XxDjScq!r;(ee-_Do50{n1+o=zHqEd|6>Ovs zc?91yM#fWSC%o78H(HVZ*7#04UYh5elbzO0e?@O98I@4?S0{C{7fb-^GI43=4o>bo zvnmnWS)8mz7bK1zTTZN8U#(M?;GtxFKh#K#39znn8OMpy4|dl?86>^Tv`pyxD0+k| z7i3fHw5XA~#X6>a&Y(EPj2&LqUs%4e7Mic&njwUZX|?feK*)X@P-;-*lvAqKbM;HG zR)?F;7n|q7&gI|vOG&-q>-#Bhg9;M6u7@F{@+O`=lCOO1&O+`P8N6pT z*QEpja}^IPO9gqSzstI)=oOu0ZVyeySCw-Vjae5Jd#Ba`e+ zwt}1gma5TBAsIjBj`ie;jG>i!br!ZSs^U~u$^;xv1v=qF(&XW^0j1L|Ul1&N4Kj6* zgW{etiqk;X08{i&Ek-2d0MWa{IF)lF)-y>pF)ZzVzmU?KyK)Gr+kk1esvkVaj>Bp_ z^N|xY3?Afie{3^|sZwOK4Inn`BZ$*Ru!KllY*!73=~tV@+Z)RPPXwBn$ktrM-t{^) zvCxLKGSQA0xs+}L^q)(fL*v5;&Eu834za1Fyw;fIn}`+b=TJuL1@uhEMayciKHG-j zI3tarL~{QMG$-QSJFw>Rq>H8xHV1tlf*H!?@LKI8c8C-f9+yc27El|3A=pRcGHm9Q z+UFH|?b6!YM|nnfuF=-v(Tw2S^YJ28x*gjTuE@BE%ES%nl6pC1Bb$eVJ1O@(fs{?? znOqS67%H6HhMi?7x>maMAflE^cBT(C$zmN`nGX7~wPus5EGaPccT?Amx-j zF~+op46LGQU6R8M`YIP8Bd`4gUCR{Zi`AV+XN{4{7{pS*@EJ0lU7PX2n-nMW0Y7Bg zk4G{)j`&zKzJCGuaJBo@#^7D9oWE7)ORh^!;LAFe5)BqQKg_O*la`O|8m_+bRrJtdT!HJWnQ%pPbM2$tx`@}q#I~tEK45>Q z6PDYmh!}fqmhM!xD6N)lz53lXXQ@j-m@1vyTm9ogWt5lGInwVYx4`E%2ziCSJ>S9` zUvJ(r1SxMqx3Xc&LFizlOei{YL4-hYue&Jq{=AJB7@=m=FW!irU^1$2^1K2v$<7|+#3FTl%T{^S8T(eT7D_UpL1HWGZkO(0whVid=s)YjEv)f{=c)$arg8}frIQH66XM!kkLY0o@lUAG>te*gd2 zd+UI>wk2PZ}HUGT$8)&df@>IV0kE-F@bH^u+sYXXoCVD!- zj#a_tJd%?|yKhY%B5+~HcsNIMd|D2vhmX0d_xUerA1o|v=0Cj}vvAX`yJmFtP26i; z(yjZUE-YVf$A5QA1YGy%Ha4k=>JHQToePYI^1}O!D7o952_1cmH`9~r%{GPbB$%^z z`w%j2QLNQQx_0={to!!!@RQ>sg6+?}ZcG-PMi^y2`6c<1`~KT7NY7~bcZXU#ydmr_ z*DjSfXCxSDGA;%A>1AKd|rV}Cy~Ir zxJX^W4_Azy<*Cn_nZVz4b$b@S|QqtLf0%8qz-*){$E&oD$ANsLSD(mHr#aFGAxWSBVgW4k0`I1k?anGrMH2#iiqTec73w$d{%6>!%@ z&c1oL2+^nzi{RL1s&Xu2)|?9GHW=HC*9bHi!AqHD(!${PlzmCP4*6L_4ESs$)A91=jVr$s=mXTE*0nS5B)$LxZApr0Y~a`k+nB(A(b{p z_2uAV)Gsd5mSW`ZGVXuY@aU9^>$G|`VPRRd+p@>}y5wmCBI{KdZwX(Cs47X$RsvaB zjaI?zM_^Y${%eQNJeghWU%&ir35Ml5`WAtqc;GmC(+AeP)M3;7mE1$xZXNC zo-Bc>8dxVw-Q3~h8f7pm^J8f=8*NDC7VBS-tzUTlY`10h@{iEvC!e+F6=wT=Lw~T< zmtNR>!+fi%on0mUCai}zjEzW>x%8ix|H;%?8KZYS{-b#mf79=4@EgaeO7zP`(jVFu zvxGe;g`6WCBU}^X@|4xy*z?CQqcLx9-Lvf%GbtPn3r2zRQxkm9`2qGIiz?LpC-O*SA4lxs4)-rP>9F25z@c{ zFIGWclwPIEYY62sK|AnD$Lh?Z{`gLjxl-L)D7>3}+&IN5NxlHeR7W*jMC4!6(8y1c zF4;7P;{t;;a^!n~p3F~>Fa5|L@fJ^5c6Tir7cCUq9oZF?eT|O^cR0cs$&U8R1{(wuSUKaYOoikK@ynUgk6AIFfAYl;Sw9{BQ;fP4Ny* zcVy~R1l&bR6z2V_aiD%dbK}M3DrWqLb++S-j}La{ysA#_I&WB3q3JD_s2!^_yB=9S z%N9cr{U(qm3R}cxOQx9lp%~2aX(|QSVZ+pALsz2KNS%6!#job!^Z&h)zVy3qTuNZS z9Q6lzg6Ub)0*g`o^c*u0QbEL=Lfkl%57pfdD@LwyHZ=z<_Z-tbcCc&|-dSdfsstdb zch42}e&^os`}PfL4irCOs@N7a)1R{YLWny~X3nX7jWx2NTJ0W-9;s%-|LTog3|;J0 zz$>TZGq#m*NeucSd4jgzi7eXvdHE(ZeSgmOBqPO2hS^8EIIieTsQ)bZucL>;qX99c zwLfB2qA3n$JxL^bI{!TU-;v;l{~t$t975OaKM+t3;r$Oh^lNzkUkTt>2i~v#D^E+oyG^2t0Q;q}yfsbE7 z-}yOc2M#gKfb2<(1@Dv7g!-S;_{<#tkV$m(2e&CiuPDBma-IOxq3fOoXyQneY`I3X zSKKZ>aP_%UJeMX+5pY!-Emi19k|-Dz9GnA zIDYZ`+yx6oP5igts{^jx<0GPbCR;RR;-veQ!%Ma1o3$-3OJrJjcQQSEAGc2aXx5(a zMqOK#9?*1V67FaMxWB0^5~rC$^KOnp+<4qDxDnthYQU*55sAV^b%BlSnA}Q$<8Zzka$? z?3RWpj9^a;OL&yCW~WJb2!pkAFVvJJPev!i6EA++F^ce$B*tt*E#v=cb;Y41 zIcr0Ke1Y#Q>wCWGRr8C^{vA>o<7;t(x2L=pa8q%+^BcWW(q9ct7ta>zX}-2(surOB zqFA>u$qF|iTCwC8A6g3zEgB*GAWE+EhHnF(@e+kF(muI>Chm7Y@0$0~-GT%;9_(Fj z=c=(oWy7qVH77sm-&#ONMN$7Bc^u>yQ5b{ZazBwfxC1GG=&L zi5bN5UZ-ZI3|jkPcLgkOKOQmFc~al&lFtiu_N!%is^K5p!4{-W@eT6`Wcle0`>FP) zk@{QngWvg>^|#fwCEJ<%T9fYiUT9wAt>5Y6gbWJ{E6;=`+sn5lUqtO2WRSJ34=~8h z{oXuI+zTWgxN}Wq(HBm~GS;Dnr4YsOn5+G6!SZfs=w1e&Tby_J%*ePI-?`~OF8!|z zrTud(>wjuQFH0ZEc$l51!C#ovTzhuxIm7QSWzH?u)Ui28xscM=`c&rvF+=U)3er=O zblkZ%F90EWv8{}WT@i45sN~0NcM*6;k2WvM`=a~SoSJ1W4R(}r%2=sUHvSKrH(Ztn zCd>TZ_$%UrBSHmf7qE{CExA2}sY$TzDLg+rx$QmvHygop)%g6AK+SWmhu0cnlU&Hn zyI{ZO3PkoYyAoE)Ab-;{$#=RBH*mmi3B2uK>w5|x+0vQ#b5h`hl&%iiYymsEAJolc z1b|h7vyOy0{-0^vBhm+bf5>vY{YsZQo@$&s=J7b1g7!fD_RdRc_Q5szK+BfLAE)GZ zY}H%=1>0sZkDMta6u!rJzD7Qm5%Yo^B6-l@5!@u<){pL;j_1}E=zx!T-*;GTDkUHAg z@HZU)4;+~767~K93tlrsei^zOihnZZO1#GZmFl+gwWe2{p&KUSrcoTk&-0ieHr$Z(bqo-v7|7A z=fNQ3WeeM&ugVC#^Ty}9b{uM`;piuz&h@7y3qr(l@Uc~!xe}ca=Z@6Qpv@5ySkYQY za&!be-3O~o_C_hs0D6{$rT)uWtRS!qUJuVeEu9>o7OeA4bY3=(!7PMEWXe1a6`$*` z3;5s0^q+J~z*FgLt&)3YfuatdYP9rezQurvI_~Kz@T3~>!o;P6`Zap6Nw|P3LGKy2 z(A@VuJF3S_ULj1G!+46BjaTfevU)aDo)%AOAdn}KpKsgmB(u_i;$J|i(l=#4x_<41 z?4EB}=Kpm;c7Lzu!QUPU$dtk#759G{pm!clW&esYGB@EtNjRL3^ynMp?PHf2Iu-wh zOX-LzrJ29?p?~iS@5mhfy<7f{CjK{j6{ahD{a?V$F~0vII&XD4|2rfX_+u3R|Cd=0 zGF6Aq1O83Z@6VD!`~3VaKup+omGs{>5!sM&uPcQ*?>DKm$bxM%DdkN0d{;lrWh7=f zYp|^|i0U!103bw8@v9GN!v7@V*chXi?g;}^`poHI;p-G^BJywdm4BeW%z@Sl-?w1i zfSh3e1e~c)vL{F58L)IOcj5_P3(Rham>8mz>I8ci*h>15_4ss;xXQFK0$y?M725F5 zr1oRK%-Gqufvqi_Hve%Q?@n(n8PK+wVqj3~ky-1YC*vE?6tZ3eW|47{KptZI9axWK2dEU(Q&b^qp1y_r4@|5?J$JkP_`b zKLwlV5&a?@`Y)!b#ErnO2`m1MiK&?3-F^#lcl#}%p`c@8VqsvRq2KMe06+ntqCG@^ z6gx&p!=oCLg+U;p7FR_?%WM3mdYM~X#omGXsieA*e+QjoC$UDrIA%7zRJ_S=dobWU z2VCF594AdD%%k_Go*K++NXpn<-0ZAgex5}z293fAB_T>US)hM?Dv<~BQEEUxsGY@s z&;qLMstn`0mfjPvQ*D5B>hZ;C$?@S{2gGjzpbzd%TY+)47QDQO3M9nAhsZeas9KY^ zmg_l-X;bzine|Fw_;O>9ugV9VT#Tc%j*nvtsL@L)dpRt{NkyeIYKX;H?IAzCGcbmg zy5W0Ak67M5cuSI;leAps^K1m^AfZ(MN12#?zWEBb`5(T|c517ik=N#dXD=zM+Nzp8 z&gAaWvSw4+f8j7|hwg3@OP`Dn<5(2^uq|{m(!6HD{|R`Nwh=cg49TE>l!CA=Wf=A? zbJIba!m2Fs4%^7{zdllgQ@d-y;j779mHxVYm0sI$u#(h=MSBq4HC0{0m=1%4*u%SKlh zwhuU=(7i)kOJQaA><%6{>?sABAd>WakMhfzxfPWSmIbW#E09>PgHN7x$yOd<=$0|% zzTq>C8}K~b&#$dc3}4RA6s`;RIQbD7Q%d-$H+EIRnLxjvWhMTp9DX0#N5gT*sT;J! z3^{YEi{E~qv&o^Bm-nUW!o7P8&f)ph)oiV;D-IRd*qf_WscMY!v0zKKbIg8f2B7qi0K6XN*DJXK8V_Qp4vt=;+YCuTpI%d z(r2oWU2scWFKy&_IcVnkQB}3PZUpD_woT^VkA?5IPE*za*uW#J+ARYN?ayWzm(QG= zZ?H_#+vjEnBOE-q+!tJHeUIlh*b`up!`WuoT%>L9z`J8QsR!~jN2Da!(#IPrJ9v4G zJS!9QPup2{m=k%*I& z(IUn%tz($&MdQQx6Ob=gX!zqp!ERmCiwoM|NPxzeUqhdyDs?zLqO)xErHO59-BwGf zf;UHqEJjzpbK2F8s5c#f7e19jbk_m33RB%3%g=x&AMT630yK}cp419H>XOCpP0$<^ zqh03^lZ${iPpoN+tLq?>e22vr4VGQ3&T3*y&iNdYU(4JV?3h7D$5BjQzPa$Ag z98*IacN1Fp|VOP;+*+Ylp1A)l)?dwzt6$m2X;|^jvBWs(> zL`OkbM)B2=L1)=P!%VQxDk8w`ItFFjXU*+w^ft6pW=INn5E+mIhnop#ROjlS^+PDDM!UoexwR_6XdgCFpZW4Two!VmaXYgF0NxPeUbt)9mg^JC%u=4|S%DtFqEaRiGq^LYOCRJ}IwPxrR#f!u zJQTj+w`qtyi)Gu2;fV&EYcXGFa<7>pCOx;LK&_b@#1;Tzv@30B)gtQ%auOLEv9Yc& zf*w)0F5O5zdVQRP&oJMu;^xAUF!B7<_VexJSB>G|2)>vvox%!c=}wt8(-3r+5hn-0 zvopGO&kQ~KNz=~JNwwB2`F%j=lx@H>v{kT@;67Xbn zl&nZiRVCj6W<05%Yn#tX5qfp?vt(NT&yYs&>UJ%-YbC@}_Db`aXcABRBTML|RL;Ft zDaAD>(WJK=ED@%XfX#A|1_ECdp?uSq+<{tffr|*1NQigdyWB8?GlMeK*coe}iVJ*n zVv2Q!%$b)IfzRTiBy4zvaWDUroxYk@0yInW=^@D5&F;22{=^H36+Pj}?2heg% zDF7}$8!y>UsY3gfSR z4f;7IoO~8Ojr+{NCAV9DKL-VqzdE+tu(IKRe9RApJVh>>9SL?z`y{i__Li4lzladxx6EfrYD8yle{xkkGcW##00o%LSrIEY5!zS;TASjx~?Z^Hz)i7ADk zG%AY4bOtxs(Ex}G33_rUSXGN+H=nHg7dmjVT;S~GLDHEnKD8@xfhOXr7^{~#rYN=X zftZc768jEJxDodomMnT>3 zk@YPZzMW;(5SN5#p(-sS+9vOxfOz_7esuZF%nBWP{E%o_$Tpu$o70_eyM&p+hiOOy z86zrVTHhc11W=9m^0e*FpS1ny+;hLAc)PLuALqw!=PsbmZscx(E$q5WF;yO=6oQl& z@>4=Ny^m3T-wG}*fo@5CCWbdfySi?r2e~7 zOOgQMkAd23rr~c>Y1gG!Vp+Xz>^07ENS4<`JSfaY%z;Lzh9_Tbm)OMfF2|XcpGoa& zmKl?)V#?7w+cs5aj!x{A9-L}Dmtoj#`Ka>+ZOteqYb6M=D{6RWq||3TA5y;N|8Z~w zqde|y9lzL1$n(E*{hlv{b%@<0z^qlaGw~sLKmivqO){I9N)i!&-KZ>kbOS1U$y(j@ zx6$A>ACSzZnYX#2z0gQ@ix}!PbuC#6A05|EfX$cV6dS@*myglX4Eegtdp`jx7qV8w z%~i)bt1-(Hu89Gz{Um`>7Q~ot?=dA;-;$3!f0U8KhM$aBc)JTASJba6%VXW|-dr$C zIbal8uxm;D_=x;Bz*Z6rS~Uqsqhlpcl4JfC{eeNKlP z`Rk~=X=K^P=aR3Ned-j;`?xqj+0N35mnd26#*V>qsnX(Jit@Njfg~SMfb-?>N!xJ| zRC1&QF+~Dgs}gsTdpsc)T89>f0|CWoFFt%Vm5e)PH7bR<`%Zg2X=oPE)Elj_PUaj*8oyj#%*F)9Y--Ky? zu_jEx90e4|LPd$LU%)d|p7c%_zsOMWF@Nj(><;m`YJHIELNLzo=)G$I3e`7$f!IEt zVE=lwca-AHtdarvDw}qTXGK?q)KQAuDZTuov0OIYunln?hb9d2N*eddNz~M33cfS6 z{s%=&`ovtKJ-r|99ELtdty8MC(vcs$=S?WX-ox^&9bupjIPCYoBwW}-2Q#8o+C493 zEQ_elD8l-|qEFZ~42bEGyELS$5Ix9~&!qIj_s4@plaC-RT@$tgB&PQH#Gy>oU)a&> zr|>hs)`3)fH2a1MFB7PEbJjK9eu_#_Jz*6yll{AkEO=9fD#F)Abw6ilb(}<1|Ec9v z$pA!T*Yt#!3*XJe%4L-1$;M>+xuEJWZRR_pWwsMd!qw+|#$r$Zk0;y%GJT|^>Y+cH z_Ef~_-t`KQ(O50A07Dtt^RZJPVYd<1#`ifu7aA_ya_E`*G);J~I-FzO0Hxy{3r)TFp|MG#Ny$g0eg|Xm zW%xurL9b)+{M$dwV`deG5!(?*qPZuvKGhdP;!CMwPerBdLzi%#JA7|7neu|pPPAZs zWqyt#@g4U;yNVBL;2{x}U*->te-{1&5afBiX~N0G^jmdI9MLtBG$;%f0WqPo?FG5I zaFd39U}iGUlzvvi_E_r4Y&RH=*Mq}XB;=89RbD zdDxk+M@I(t?(=a>OHfE``^WNBWHB_MVCe|F17rsXh+ri>ml9`Q6!*K)CAx2>@2g-9 zizo4QG4tYsEZhJ>o}JNKKKX{0?O`^b-`*Gdm2Zc2fZd^gz>vZd*dU>!Dd#wC6 zdP_~60-Ay!uJc9m0C1Q-SZ(Ds8J*r*aAT_7wJ*_|P?0%I1@?x0 z>>fpFF2LezCy*$lJJG$H3*8CFYld|A1h&!m=!AkW7Y1=ZteW81@G((aOWpf)`2bfX zs3|x0NOV}+42|>aa$&5@d80%U07z{ryMnY8i!-urVN_?%!vC~1Dhuex=od@e&>>oO z@b)Y6^q$jTs{A=cqfB(?{0g(px&a4|fSUhFH#^Vk04xOzL(Z+orQ_{0U`2fEgOG>W zK9`1jd|?R+}!8>h0~$ya%N`V7o>5>1p~0Rq38Rk6jo|Dz`$2)9qzSjm~uO< zg}Z0``4V|;bxzcz&^Z@=9!=k(oK{`WqXVlsX*$ZyNiLu`$S&boBl?D=-b|IDc2ar4=U0ZSp+@x|^B;=^+X?z|*I2pnXOTyfA+_IZ;&g2S z7p?cLmT>*+igMCPd$OuGpMWil&+m72INQ`KSyj)&XJ5EDDQ>v1mQHjyc9=!fx$5!V zPf)arWI;!&zCrJYRw+-&u#j;k)SU~0Msd|LIm1JA)@qe7F6Qi71;G>cX)nT>A2qTL zKX-|d)tSaGf4QguBo;8 zVUDQhcw3?5DE{;!=9rpG)H_29?{RtG8m%{Vy;OLnjkeFNg3Cv`)N&tztnJaYqW(;! zRNp_brC8k)`S-ELYsnsr|2+9`OayA#uNi-oBkNL3>E_$<)#B}4NX#5CQwmK&(y9Xy zz4ZzZd|sM*|*`( zvxpO_{~Uh4_?%n&zN-9w4*AaR3UN7OwSZx))_c5t(J_TlKpGyMSL$+sv3;H!O0C2_ z>!{g;(EuD_%cYJh+OE=_2-UXvKTFwJaRckcp0#$&l$b@lNHKp#oIaxXpM@2|8EW7L5)4v;cmHe)4 z8qKew2;`52fyJrM-0pB!pYAYN)LS%AlEvkC2Pn_Ny>j#LbJiZq9wcW&^gJw*e*LE;%#};V}~q{ED-q_YMJUl zzE#F!EZsmQ9JB?`ruXOA7+(JsW4MRDOKfSOG@(M1_m{Ap5G+%av_MB8Wt(t0^Mk_f)>r#b+_UE;Ac7Rz+aUiNuWQn@`7_2L<-T`h^$o^j1yA?6`X z?W_Rq=_CTq#$sXTI&zzQ>=i4JHR057Bb7`5mjKvg717$?btu{>kFgwtHg#vOhHg7p zeZcFd9u0IZkB1L^7WdzMlKkCHFllm|HV=9wGt5z`vm52rVlkC~bPaD0E025x?)H>V zDL{z{kV8G=i&OeE=;MJranyNXoENyi=dWOu?3K;=|NvyoTe28 z)mbT?Nn226A*Q?*_zs@K+_2^w>P&W{#aC5aSnw9f8ph@F#tK!XK|G$X^8)0oW8^5X zjEq9N)4qK|ee*u&Tl$fy(V)wEUM@%w?c?Btb^bh?g&F)?!nfP#3$-Zbww%1mhi?*M zqhI<*04wV1t(w_LWp&sc=2Oh8hs_eMAB?y7$(_gBo>QTejiM?SY=F30qIeG*=)r`k|V>N={ASJ(bq7w5fYm8Hal` zSDn=#K%I=SFKVstOxgz6%F1q91(tgQS$}2WM!=v2pFix~v7w9Y`vwGo7(q_T(P!Du z_o7>wxOd0-zXM>)FO~W;$d`8brg=WtPG;y8v_!pqX~Yq5P0`NgdMH+Lfp%1AD4YYe zhow)nbQmIrt=#eb)8~bI<_nu(C(na_oe$jhe2YNHm~fJdvy#@09#iE$YMUpga;=vq zBA2Y!Mcb{B7lR7~iynCI^k%+Q%(dCY9J20R%QS>~E$*1GPKclmv(a0vfV{z^3eK>s zX|>~NWxhi@P_0NEbZLOryEhFDIp{x(PHo6eko@BF`JjBW?341JfFH{0y9YJM9+ZTA zOk4zL6kyc`kZ9IZ*3|UZLDI$4_?d-k=fkS4C z#*VCG=QJxx*Z?(L6`ja^ZqglXt@vzaNaD6(@?RUTQ-R}pvq&W=388-6Pw1Zfax+ou zU!voNwey1?o5JOv`~(;!xw)Jtm|$Xcu;mJ`alzVWd~!caR#(l;o5OCKhAr_H#8mr^ z%D-NR(Wh%5btCNjvUPIoO7|XC4bPP)MG;%ok~54_nwoUc#obI2i)c(} zQ)#B?prAhUJKzrUb`5OsKEa4$gU)3hI6#{S+%vK!o>s~3p@^dWl$aBkcKQs7#D zdKbEbp0L5!jYHH!21=32*g;wIlnOqtHkAV%opg3r+0eAg#?kW~2xI{tw?@$-T(aNf z(de_@lW-Wy4Uy|Sh|6RAGegxW9fFs8h3~o@r*2+D$$e~O(DXeyUQyRk(^E2$pMYj} zq?SXg#Xf%#+4Cb_=8BM;?pVG8zC9aqJhk_HwyUg!QfsA_e?o$jU7h5FJFM&ipqbVs+2_W(@hlLE)f_%g(JuHNp4Qk- z&9Y<)n~LuJ6f(<(01~SSjr<(F6H|Dn{?EHR8#v{^Qy79 z(f|rc_n13;*N~h-F&rj}+?qTDT0mT{>-E9v+BcHin7q#v_YE=`5H?)!$(`A}YRC=g zY>wTRiY)_6FETOs7-rb2BKgi9^#jKblYacHq*_XjTTs+mVh&>A_{v66#xMb_0Nuwv z*>Xz{YeYMx+y|e*hA;dm_^!t=EAAkpyOrGL%bR`;I;N1_vAAwm$iRv)$n=O3&g&jI zOeP|09+6I0BcuFqF6STghGvM>80%V0y-vh0*Vo(_0Zjd^o^!?;f3 za=5Fsmxd+^rm%HuSstUgipsJi>QiFp0p9wD39CHd3CZJ*o=J+u(y~A6??si8aLA;G zqx3i@z+w@jRTBt7A_**Qy80`uhA&UQTl|U!?qXwd+kWsHTMRltbKI?#KQ1AaAvdmj zLB?#NEp0o#tA)+JgqKjyUXNi2Ei}6 z;AYeoWt@0pEe;(>IhGq8oWTN=#_(46a(NQHMD}JttoD?*$i3scMV+iequOp}^Xs|0 zp6-qWKI?%pXdFdZX`2chn`~n4~n!LUr_3A^m zjT+b+sKeojBA3RtK;>?zd3`_dHt#3khe$JtO* zm$G~>pr^oe)NlsW*3nRc-x43MNN|cPwq{2v4=QiE`g}*{X3^joi!5m2&5oATpbh36 zQz_+R(RCT7x*_yHg1|>9<$ctjm(*5{>Rm7BPU(2CK`&&DkZW_zA}FH z;Z?MJk)L4mV6%*?TIYq5^HW}Y&@0jUVH~so4wv;nO<>>^1VT?pK;(7SVuI!a zncB|ftuby^7OQHwBn@fmVQn5~F^XLe--GPve3 zNzB!|GNUr7;qsd~-OEF-2CCh4?L$qbBt&YjQ@+bp3+u2Log3hhF~KNhf}%K^MTZ9q zJ$_%K2JKET4_V}}Kf|29X1eB#@~;Sh3t-~b`3dv`vP?(YkS0A6n(n)Y^(J&SSX#cf zOvN*zkvfmbXo3&QF+VL~X_t?dJ)IqLz%HSj@W}E0`g$iO40Tn_E$#;6Zi*XPwe`f* z8w;_DD4`J8v-v1oZWV3ImcyRWRZa7Qu}l+>KU^{YV>YSXcv4Y|vss+U`+b={V1h{y ze@vL(tzlJH)r*OzK<%0H;8+}r_jg^1EMxpk)M&0F59DvHT?C_*;%1-ZCrk*PABQ6X z=!k_`z$Nr@3Txln)r!8=F*|a(Lt4vX6^v# z&i&B`8n!XzwbJhVG&GIC>!l(r&&cvJ0jqyj^{TC@|5Hs)vh#!`BKkIFd zvDeDj&>lG&#j&_340YSoIg#;W*9(>keLf!T3P`c&hx~*>hdklCJV%>%@qB}Nz9U%m z+KXy_(6<#XXn{rQ&6dxt$Egu@tKZBE`VKJUOhW{No5Kvqx2ArZ-Y51p7Wm^rjxa#( z0dNrsr6<(+Q3UF;AuKuFL}M%elPhwe!v00NQsUPmoln4JZ3={HRtKb|4=6M$>=v*6F}DDSISr4SCb(>C5Y ze9-7Oi`xL10jPc`SLoC4wbK>z1|UW;0&dp0S8qtX||Y|=@@vyA)r@ZCLk$zTy0sV4+kQP}^JE6635 zFmRFJ2Wy7EDOlPVq*m}GXbiw$0v1om^;g5JQi*u9X_VY;RNbVG+y*s9xn@VGfD6)h zmC|vN2T}~jOX&seaCYz2_6TP*CkBtF-hpHI7?&fF*j8UWj&Ub2@~t{zkh|tTcxQ}2 zWqoWMDe<+fp9%X@zRgfd?N+`s4St;5E@5Q>rN7%N7`A=qkJf$0Q{fH_%y4$d%C6FR z6->j^WR1k3Ysm61LsJd*W^AT$7LYrP`U!9`LA28$`w*lt&6u%`LPnQS6}~t86U$D8 z`0}{@Ql+OQFNgS;YNi;&u#;z~Y-$K+(N)W$o_(+H#I^yv{vCc4*UC} zS$O$!$Sd2z;u=p=6`#vJm*+rUpv} zC=a1;TU2}few19!>fy(ai{p}a3P^6k5vyUDRf;YP(N0S!bZOMkd1Y`vQ<8Q?;!Foz zz3S!PSn^TA2Xl))cPKVV%1w05kwGWxG+0I1#XqloY_9^yB7yj%Z)U}xnAFm4Pt4qW zMKda>fk-~D3D>AQ@4?R-lO>Kh$N>T}@hwYiI(wD!Pg?9uqYVTquIQ_~-aN#VX9JZ* z*4kehH=|~%n31aujI{w(4%@?#>wq4zecX{y~D@%G(Q?a-*rS|CQjXs z{{$3!(y=FkdGmP<3jj+-na#i!eqGb^56{Lv_4S{>7*zbCHBf44yj`9oc^&^nN;<1- zEPLYm$xX?{lh}F0{&q7AK+K>!=jf9Zpz0j`Qe1}O>+6b%;Y21R9+mJKU;YIPwOO=w z3wzI&8p&eY?WaO#-p_dSZ@oLEp))nIC7~7Hx94&NJbdB#k~U9u=|U3#t;=Gygk8I? z+8GbiG84NuWQoK@{O9bGH~@?TDwlborL-4)2HFcblsI}F)Vc4mRGufgvVl@wD86~ zJ~f-42N&6>r09FEuq;d`*<~`z(^=4rH_B6Nh``n|ZC`fwsoUTJeC~v(XX+f@u-<$U zJk=uOGV&%k{4{UdD*s@%kWN>R{~e^nheYM5*k;;aoDjvyJ65I=&g>e-hLjG8PQgo1@f+EsSQ|%bGOF3?k8Az>t=stHtWwLrcBE&&v%Izu!^$;B_tDIE6`2 zH~2EVgCQQXwP;2atok^`Rpnmo{x;Ud)XQ}09k!nSvz@UI^RI5g`dIFr+SJJ=32lm~ z&`k?P?OfRX>e#aIPEiTkBEdwmo*5VJq$1z*v5M4rge+spj%KvPrO60Wes1GEJoyC_#F-Fd zQiPmB*WqkhjeZ)F*THxB4a+@z56r*Zp0RyBeV-)4JppCQu}-3r+-|io^<7VXOkoLR zlh~iB*#GiJSAjpf9G%b(vzJC8*=@vk?q#`jcxqOcFR-e!X%96v0ddgNF?8149Wq@e z+`?)GDOV*tf;0-5m2H-lB*dK&WuLzgnOXG=5$zdW0JmZ7&Db+*mqcjX?%$i zHi!Lv%@U5XxDhgD3mGI0yM^b;Fp=_T>u2_AFD5f)@fOd3sKPvjy4iG!!#-X%{Gy7Y zxw1}w?cLZ|`nTLNC-h?=F9Bq`>z;u>Jv#trH;!~mzcnpQ*^3M`L*>wu>(|!=ndU>V zaAf=hn0~otT5<;3Q*yZSjG&&ex}viyv!kqP(vU1+C0veEywlTm@$6lHB{N8XnA*`k+x;r z{=sfO9vUydz3CG=D?E9MJT)Ceu-X6R? zUV!4;1Iw}8CfS1_8;PWK0;K$<9(Jn_TV|G~C()EF+%fouo7rS>3G&Bgzg4$fWjhTY zuKQOhnbV9=H%<}x5eVQ*>k8e+(|CYy^i~bV0e(=j!*ps)izZsyD$5NJTttsXH4Elo zAC)m;Kw8R^8z^t$?D!!E@hNsLz;L6COWSQ8S&U+>YI!s7(e}rB8Tc)U4qDiZ#uQc( z;*Dgxbt0W0+q5#f^1f!^Pr%n~?k$NkoHAy)oxMo_@^w0FiAudQWC=QryVO*VqkpgT^n9uuTHxaRGPVQbgED(W!Ur4x8)oD)#&iO> z=a^~TT&-q8G=?%Ie&GJLeW|0N+sFEx=@h7d(Ib*-d$iz~m4eA#){#p+?Tau49dQFk zOA7&Q+H;oxYv)G0c;?ocD6y-!m?n-qNYXNali-GNJ@IN)v}RUV7nBK8{@?DhloDhvK$}oVgv_|=sr!K_Sq9IHjZ-5CmHbr6r_P)$YKVwugn!6^S2Oj@M6_W zn+^6JH#5byKXz`C3K!Or1|RkO1T^{|V)aqjm<%emsGaD)xQOSUS9}-E`;74c$ipi< z<;fvx*c^7M8cW7lM2Hs}?x-%vb&HLoy8kjS;fi0SZrd!yzoQznyN|Curu;^Jx|DDT zG%g}0AS+36yg!46EOl(}H{~T&Sa+j9;+2bjL;I2)jeN2$j?+X*fpJn@o~db&z(3bb zQp5p17&|C)_QHvbZw%bm9C>`7I}~djyX# zv`J+?tYhNQh|sh22$OMqqNKI5J!8CazA<@cxaJOW+wnJQKYf_4yBriC-Y~sWaIHZy z*VQOVw^diUy}1H$>XAF}L-j5mCfg#@_rdE_M3~o&zILW8e{+rAXx|RBYm@;tH2Z`3 zbn>cO70t4hh_ZvwXr4z%&N0t`v>b?7pFQbM(T(A5>Av5NS*`WrwAVlJ84c4Yec8%6 zv3YHW^LS!1<-_Nhj@3Asu~QFGzWxL-z&n&{J556aWH)n8UAYMnA9EY_A(|s`iNkDX zkmODif&N^-y0eKT%GcwYhK?ULAqc)k5ZKq`ORdl9xUOT`qE<{MYHo>ltUGPGlz^4^ z*fVgWpez~eSPhL1rI|&OKd1!b-EK-c{5zFV^D)=rIApM;&Bc|)C|1<%CBb7I#P&U5 zp>xQY1K&>or7kG=fa}B{^Sh0`b6RfbSAi$u$yfiYz4riXs$J8CLvJFWAiW7l@4bU` zkY1!0DF#B7PUs*A(xn-s_Yz77Ep$*25P{IUh|+sint#5nelusEbLP5c_MS6)=HDwo zRu-`Gu6Mn8>U}>?so$fzk7p!~IoI+1awc>In*?QAD@wHC1dn!*FlA%5OI1$})l_umbo|^Uel?(HwE)5>ls(Xuy)nM3#Z1Q*mxmEioq}EP8Z_E3O*dOgG$qMK z#TF`|8qx6H38P`$YeJ5miQ+h#Q+%bDJq9pboTPs%;*Z9FTH&R7Fix9P+- z@6!Lp8nL{D9$y!6#SO)|^Czm=7GoXkqueuU@gi*4 znBpb{GE^!cy$fQa!G32OJ|NZD*sW&C0<^(gnwtb|4A}E}Iom|?krS?K6~sAb2)R8X8sFBXllQGvm4rz4!En`qV^q@Ll#7+85dw}{&M382!Z z3!3Otd(*V-0nxy#;a~WJxU)>A*2gkK77Q4zUU#Z^VYpTN58Y}bm=i>og5}^Jp4ZEa z3mw+Ve6n-R%g4N{C5m*R)|#lj!(RoyG&N`F z3DEf9&OO}i8Li^^vS5dwYxL#|2YxFjcGs%HI{~rzd6ksFSClWl#A`Etw<(> zv%FR25@cH&T)~>OJu%tphDGq@2LQOhk-7HRuK+d_xI?bBRmRe;j$DC~MIl$(6hA{u z*C517xU$YY=WSz93={U}i8hF}?7Y*T>6jk?{&%M9a60x-W5o}#as!&*l%xyB%X_DG zwIgA28d)S*PBz-a|=Y9UL`cpB6$n9|R}}Y?KaVdH%uwbl98LZmXbv?%GV;-pXF! z?oG4L(nLS(YQ%dwmgF&Q;chv>n*DpI)c}VLKM})wLWR@an#Vswp~p0%5jqCMiuNTr zs`DxunrYi0$0Fy*vlJ1`uJsW=gXW9S-8?8M|NE{f?AVqjSEEScpf4r-$q?#-lE}vv zT8M379c3?P5E@cd1m8yta}{H$uXbZ#20-m|#z~lcp=_Q!;?)pb3%=~qZR87i-jMhC zPuAga;!nQ9_x_)b`_%`>$+G;)08(k&-FIpaBVGQK2)+yPBWNENIIAkT3?!Ld+`>9e zeGB?=rdDTbM@LW13rn1>(fmhP{i?gybmH3M1!N?j!%6)?jk&AIf*N;c&If|Bte*GR z62?AgRHMT>!j;)|5aHCtQKFkSBb;6$FLQe^B;`tO3wjD#_#_H}Ja#k^cfsoUl!&we z5QYljBV#U9yG6~FIArENHKZd4zlZqH#Xm(#ss?jx|8jC}?6e$!Dz-JfaN{e-OG%hck8U!E`!$N+`_DS!I!S|c4@=3=Csa^ zWHP?;YbuPFoK>h;M#z(#=|vAvHDoklWF{@@LAb^~K|z*x0S%G^cUO-u^SaLJsiF7Y z#2cD^faxbE+$D)6Dm=g)$0`V*$U3>JrHa)A!?6Ht-1qXMXFPy`(bT+k)8<ihrbsl=jruZy(dxL$+u~v0wvfD((l`7v&HifhnJ;WV874x!vc) zrb_a(+McXfVa54H+hFC&xl~#auBnZ+93byZja2wxc9)y3X(?pL$xF9KF~A}oF>>;5 zvDgeMqIO|4&G)9~{hMLlnq|kGI3b?YCQU5JAWQQh;pp5VNRtu_*?7+~Zo2Il9CydI zS)S({UETusKFll|D}}tvc=7AoCbzNJZmu&R-RAq7z~Q)-co$#xM;6=;@Vap0cPmfb zi)ZHBQ&SR_^)k8i7Xk$o*%=`1J<$H5(wFB!lz>1%au5*@m$$K5x9QTQLsEwyGk9oc zy+tK!g8nuAuUUT~V>9L^;_LgM%{2*(t7D3)Mq#CK(vj?4BDz%=^icI|GRjN?3tAJ1 z&iMu2sP%4iTwwSIKp&Q?bfSZYF7tU~V8>m6f;NBD-17y|%xHefyr}9@js`X?=)pHX z#!IR(9sHQHN-xhaf(TqRSY6HQPsm8Cj(p_XrTEBrw)V%%Z!&@#9&Ijb_`_muEv4lP zgE6+j6AaJok;YjSw)73SJML~E6*rwa>BQ@8ZjY8-_iHr#ccb1xkf)<2R-XEWbG#3{ zymapR3G@@Z-gXoAFap`G$4+nZBRIKI_$=UJNgDEVWD!N5Mar3??p%_F;zES)sP2nz z4M?SHQtybUV%`StV&0sDwd&yaI|YSf-!tB$6|A!U0T3cA*{g`HSg>99I55u#)4Mjj zRJA#aKyjW?9CBh}p1^zJC$>w*N(T2wT=!s7Hh`uKiFM+2;I&RVceyA<0`K3S6-p3|C=4ENnnk(ou!e`jcXK|`Y-Dk}f&ecks(WrjD>#K67fRHbbSv`e5}RbgMggs$==^teEoT)A%*X;rr*7FTyZH&kdK2~(k*-X2A(fK6D4!F!jEz3c(tk8g*cBU*m|4yAPv{(wRz*J-gWd|eEz z=f~>nup5Y))B0I_s!>^qV@w7HV~g+O%lP0!f^{z!9+LVw-)7FU*HAaKTEY3Jey!f* zD1^(IY~-EJmZgAJ6@$j4-diIXayHmdFY7z@m_BLTtz*WYneV->8X@OtZRT}Bl$IpS zyr0olAHoA{oZ@yBWa^j{fB{WvARr48fASM&5bV7sQ>4OJs3=*^ zp#w>1Io1412hFH?NkS^>;jKf&wW%;7vGV<+6tTSeVJA>$E|Y@NoGZE($6{$>QKeIU z;*7Pe4`{ZY{0nyik|0xKX1$T&`lIcF^d(!>v!2Uy+@SIls=Y!vj=Ek<+NK!KQ;}0_ z1i2;C0*Qc}u|0om?Wn*huSA$wxm^{f%AN!Xch%X`MKMM0qjMe-FR5v{~CNnogY~9giNkdho zE6@Kz_-hp)w{13aNbye){F$WEy>gTC-jL49S@bXvtM#LBCm=PA-X$iU!k6~S#xO5! z#aU|1kKkqdvyCTUa7075f?ZG}SnDGco5-68=Ji2t=;ryje{uQ0_;npY->7JKGpa>>5I22xv8{Q0F@fTk5pyS*_(W_ z(#WcxVOU{!nOU-#5`tz*iy~B>wKF}ft~gLG65Db22BpFSYTe-T3dPigks)j4N+xT;>h{QnMAT|FgwE4BKQfL?3p zfXqQ2Q|_1M$}jF9GzFYWV$j*oX6i*rT{NG7}VR+OdJbJ9#D2^ij4+oqgk)i%sp$%#>tsA%eX%Y3CAf~nX< zw9G2_UT9zQsMy8eJ4bNU-YuS6%v7NUcY5<)DJ$mYN&f-w%_%qTR|WQE0L*;}e*paV zBDv;dRmpk9yIy%*0*QesIkgUhml<9mxwE2Xogo_~A!sRSS1{#c|L4eiKxRowW+;~F z6EH-MLCPmomy9L^h zCBG|&wruDItJ@Xwv=EfXlCH6Rt^jeuy^1D>>j|GMKYZRsAO%r3EcyPe;x#8FrDsCw z#N?uOx@cn%KbV3RS~xkUe9jKFe;bY$1J8p}?>dorXumPp83uZy6|d^8dvc5c)7Es( zGh!D=i?Q1SllZ&syf-3FAydWuOFDOPpKx_@SrqSiD}c0?CQCiJi9DCAyu{m)i}3r! z(jzY3;ysZ!oxiyx$y8>`L)7d!2W2Vpp)d59sf8HMeRnc%%%ar=7Beo%?d$sSY1@6p8hRs)3$gJA(j4dIQJ8g}Y z{=&X8u+_S>KpwaLA>4X(S#MroW^zMA8k`O;g z7un1ATC4>26vOxiTuCyrFe2nU+IPwCWltNnylSM@y%(0}kf3)SZl<6sA0$r#Z8lh> z9|}OrbH+IO6^=M_SK)+A^O3`;gy&T@faDL2rLfDG9G*kOHpaF@-4}$h>|${~ZX;Xb z^ZhM|Y!AX~yPx`#tHp=jk@$U|jp zAyyY@P3*8O+>J66o!?fC`>*yWhyK5%`{XnkMs`m8kONs?u(4jYXGgl5$#K6MF6YsCQBWPTp1 zdZy-c=&cDft0K(OcMx4>0EgZJgx+qV8YK*hsfmK2%EbHoL(h}FNOWsr8+)!PTxdN$ z?;&!AyDIIp?}*_q`t8ob8k9d^yv;DbHP=;*Sj^XU4yCem0s39{{|Y)KaLg2gP;hp{ zqlH*q=KLv76QC1mO{TJ?#+xBHlue?vp8ws>Bp1uthD_5zauuh2)ZvS;XM?70n;e9S zL5mtiHnyLMguygxwbXec8Cnf)Fp3|5adI<)Og9}Ii6-odi5g2bbMtjE{YR^16Rsr{ zoT{{PG=>9foa|DV@a4Z)up`FA*KaanI)Oi^7cT z+o65IM@j1eX@uO>Qc_-E)61im-Js1cDx1?f;xPD6LVXH}9Oh%3h=-{E43ORoQo2P= zHQl~&KcCbjV1?*V%w`_VV##s0jk?l4i7XsbN+0;ZZ+fPAqHPY?dZ_@c1_JrwpJx?#tr{DVuKptyn z`9%DLu+?ij<*X&4>PJ_C)KfmA+tpJmR4d&zQ)%gGAQdWR0~yjJ4c)C)BMrsj+udOf zT1JpnBfDlJa;Bv7_{`nQ%Cx;Qlz3$wuOEAb8;Ltbf>*8@UjAfCo%43yq?pKZx_h2Ui0VE_kV<;n7W+Q^RCa1cX`|4F*YqF2c z&a8S3u#1{5aSX{8VoPf9#ZTCFqvrrgbWKSVzJ;Md$(WO*CXA#3Ed}=155y@nBTnj4R53*o@Lgj=3O{~>pK>MKYx=gpduQtm0L`YP5qU4vw5tgw!jEqc)3iw+ zc5WEL!g{?E#MJp1Bk)|JHM$(45+C2LUM5{I0!;G$0k9{C;XzErLUP`D!)Wau?NYdH zTOt#;L?0}I*;W+Hh9&WZaPcXI_JboGvW?J8|R>2+z)_8tc%oV5wU)X zF)6TD$)xqGqDRCZf+*yU2y(snmWsPL#!b`4V8*-LunxQ;;(+wmgyVkVX}}quG2^B8 zBbu;)DBZYNwpAHxK&FXh3S8{5N62635tCfEyhv2L@9v3zvc#Nr=P=vpbPTHx;hTz+ zUErtcqrRn>@a;D1tLVkGyu3UFswwjZ=?}m)y7#Jlb?WjYio$d8&_P0yiJk)7Em1?* z<-+3S5;0NN_3b`ie}IaSjw|6;Q0&_gmBj14!W=(yiqr$xq8l9NMNPu7XPMf$n zw1GM*$_OoIAEAS9OBj%q+Qhf_1r?uof14Zn`Z+4^_N1(ai6x~})E!f50*xT`W+g9j zXQnBOyCON3$qu>5CpMmJZqnzFQCw~pGz$Q0V`tI2cPh|Tv1{3WM0yOYW26Q>D|dE@ z8Ux0p0=XfqhQ?gjQ!ac1!Tt7c?h!KZcz|Q<*@#dC5BzlHb)h{W_77Z$X$*hFu(p5i zvkXPAYKDfUePVO$Q_lVi4=3h&c%lR36i_rlqvj!rg>KUhp4lB;zf!NcmqO|1Oem?1 zRX?_TEG+T0e`97HNMX?xFe_5%ON*kde<;7=);RLiP+DDw%BrAQR)3TDyBxXF`l!$r~LVcHX4N-3;pXs9?u1yi@rRxc2e8ZKxo-tN&XPlum=hfZ2@3(Q)x$Y97S@oQ@{+9+U9h=D2a5 zSb+Es07@(oGd}zr^j81B0tq=zfT8J~+?+7dhh;_z^{4yvSS>4AojFzFBZ!;uVcIe7 z(17P_pR{5egA0D9Ek_JXAD6agdOZVi^6`~4av1g&>PscifkQPWJ21PXn{eEHKB-s{ z3kthOf+8F`QK8;prkobx8@$EN(e{HVmvMT@XoH$A2BJ^nYR!!1hvwzNSyW}S7YFRk z^L$C`cURxj`(awj#MC7k;3hQe>+d3Mf8rJYI$5~-*JuQO=&Ra3Q@XLIx2Lv{QQsDz zdLO=1OplF50g$3g>?s3YO3!xLKCe7>S9ZY+Z6l%Vl;bedI!c)?6e9F3pa+g_Q-{t$ z@AlZl4Q!j7CShq+E|5;8&=*WGx=4pywccwjN^JdQ!1NbYeKy9j!cBGLt9}I=;e4m) zoJxLR^je{6Ed@Eu89}r~tt5r;-+Dq+uRe4_Cd328pQlmz4z`NqbT&%qB8a%%Wr*Or z6f#AXU0MuOEy!^ZI_1ZiEsm23FL^$Urr|zAG5yh;jfw^jC)DU$5kyY)rR_KGjxP#^ zhYf?%KweZT>@lZ(h3{FmAVi$>{6qJy;<3KA$Gp`f@tPR=?ZfY;Lt=sZj`1bKv|cVa zCZ}Mv33WRaHB5tBVe!n_Z}DqX5*|ogLhJctL^bM$$>!zd%T3-v*p=Q7DY&+$B2<}- zI_5YrUQ+WH22q&bZZd{%m0H7x0jV$w1os)})a5n|p*4^7eMyK! zKA2rveph0%U{k}parMEZV}v5*4JxJ1(SEPrsrpTp6cJ=xS_zk%u=U+gL4YzUIX7tM zbqsivN8~skYD5=#uSU*etf${U(tL z**#GpPx+aJw+4}hj31mob10%pH}PD^lGbu5e&X73@~YJ0+DYO%n>iz7K5#tatN(1B z&wVusRCSNb{y#5@4XUf*2=QOWy4*``vIVtN7Uwm>1_#W%OyJB`7#%OVEw|} zdd^l8{}c#=hT(bb*rX?hnr8(T!ZY1wo-153%~q6Nsj)wJh-eQpL-n~W zA9g;;)$kJL$G1QWZ;!WN%CBpCyV;F7#BRS^{q1E#d4ct&*!@_>tJZi%ucCX6CiheL zrsNCmAJ8374IR?@Voz0a?WPMFW#3JUoqn1`^hLV&l9*KQVehUc1H$njzTx3E1pveS zYVfZ=I|AIEc(`PDBvw`wztiJcdp-(}u3)!dX1KSQBm`mrk*n9%s$)i-*SIgn$$n3> zjAc&hK-h{pY=;&C#z2oq^UB={D%2=JE=L$E_*U^93o$>=v>AtaW4qKt# z^m3A%Y)ERa_V;{*u35yX@f6IQ3Q;P|V@%cWHtOli>Y5;Czk3Xfq#H@>jAmDYJvA3lKM-ckBb9{0)Zl0$Gheo1PI?;tscm|sn#Ppz8 z0)chS*Ju1>VvzP;Q*~&FvN2(boSmp6BMaPu;`?U-1Y7YuG1wMdRNXPT5!W!bHuWTS z{h~P4)-R{#355mkjbcXB;_hLHdweocHl4BAIiPK7+PPk<23BN z&Ux=D7{6aJyeg=YxaZTUPLuaKzMc=;0u``L^YH9!vDP)!i0SwN_yOQvC3$4#IfjWY}IS`fv)*f*2v(hHRMQ(9Ida#j0)J^K&VB1rG-< zkHgbwu=O21m|-W8=)!0UZ_7-`N|EzhKukA~@E!`S4jZ*PapWxV2Ntg}*@9V8-(S;I zvoru)&v~B!&3JF*(qHxLe=+PB?2mYOYZ#OBk)XwVp`W*yx7eL=1%)~>)N0$m&hGzn z`N90S+G!mElQi+jmn9ofw!X{c7?qJ0J!v*A?XU|&(k7N1^LsZk8!8j8#HMVh<0y7$ z=eKv2$aaB(>C#D0&8ybM72&dD~qGJc}A@&dqNTfKLDF9(CzlUGPqX5qnQGm z8w2a8+P(Ju0%CB1Q3SICiWExiUZ z;7dG{kXNyCdj&wdXtM@ZTvxAb=5sjRVC*z)X2OeBi6ntIOw~)>@H_pT>lnkzKKy}Q)%`z^kSH>{+xdWD8AQf;_h}2a^ z;ssi7W0#~dr&J+qx%*$j$9lHXb8hA7WYyG#j)3=7{U(PBS?U-31yn7OxXrsCr7Qje zSM&ur71c}dcV6H8cL8H&aZu{O8U+4m8X zM$rZlm&0x)r(Ec|4wVEK%BmQ4Gt9Seu2p9*9F@g#n9O^3wcH4hRFVrEVY3T?tD_jr0P(pOwkB7LAB@hrOJOjac)~4IRLL7fJ{r^d?y5du+@)b+ z)Ypfuazlo~h40wg#NuvZ8j$gNXz_ptJl}w|^)D&C?jB-E-WPEYPi&K8K`fy7mdx%( z`Neiz)(K2%ShKY1PI*Q#9N%sE@t)eapH=J{AVgTb3?DHjZ`Wt_UHa@#+@554{X9u3 z9^*B(`<+=QnL~c`L6OCDVarAb+Aw#MV@?hq7Chn?pAq4Z6eQ32O8~)Sh zxoA40f5zLV8!YhhFx4uq;L4tSm93Lf>D%Bx5B?9G5Fgs2-0okr^o+~I85a*i+-?&q zcinXl$>g}#)Vl7|ch^U8d#2kG|9Hk&?h9t84s|U`X0k(-p_WCMwS&>AKZ}V`w@w0a|8{4?ieuK(!*djZ9I3h!fh4 zS)vp-+giTM8|vY2E_)^4$pun$BZSsg^|9kU>OGy@(` zvR07S7>-Ke*!QRy#9Bg8#VHZWbLaI+6_>!H;XLXS=6oil)e7ZB_iL5kA*6cebZiBN zIFSeSsMdB%j{BT}$Am)8T*-B#(Ndyyd1rakGJLp1vv8t$QOixHIEl34^{8pcrkN#0 z=ZPNWqdN;k4CqRItroHidE+LmXh{^N8Yd_RYj1~5ksH%+H@3;t7k}@%(jeVO=`Ou- zubPKPh#%e7CbeP|#Ue{TOhO_YE@aJS7{Gn$y?;W{8~g!-pu)m0Iuv+2uj<27)t-yF zWrYG~3se{9&e~wzJz3{f-MYI9P_ad*qlJY->F+wpv#|jhY{^k#^eBR%Swr4ggOX!s zNz7?b_6|UJ*JJ7vc2_l?9gwg?)r}ax&UoczJ0bK)GAzCm-g7kYBq?f_u?CdLP}mr9 zy?@L#nguMr~C%W2Qg!{)I#xO_p>yB#8{}(N*b6`<()xJC;b_;T|)25 z3%w1-B#Yrqy8={EkEZobLUsWJovHs~%?@4vX`y_*Sv})hwnMKG#;6u;RvemxXnMz+ zPaNEtffwhmH91-gjsbWHMG3cFb|qg)hG{$J?>A#m9e^&x^Mx9B2qD#^bJUfmr)AY| zzbl!9*fxyA$`6wWv6sy(ID{EhY8RP)&mrnPcbLcOKBA2jyT+ebaP!0}6l6?~Ezds0 z{^~5)aN0I4u$d@R_O`}L`O*UR$un@v53`FK!bdlF@tI&c;>EQ;rtKs(Ej2H}M+s2_ zlEze>3oflxN=j;4BPtMdbxt@mPDf}qUlgc$(YM%j$vBd)?-Fh#pb_>do;gb%N0&eW z4{6z~U~2L>1?!gboADa-BarE|)rM5ZuaqK6tOxff&{WN=zkohxP>^&s(j)N^m;O7e(AG{RSGaJY z^&Lc*^a!{iLu?}Vw|nv&s4J$cPq5Y%$F;w|byAG3NCSZnW>yso$ zq03y z8nL#C{ppKb*?%?CigCgm0U#}BFP5)CbqY+Avr#~NZrvTrZ07@*)4w_`uQ7Jf0WO7~ z{*Kd91>-z;bBhrQEN89dNrH730l`AAgqxxc^-65+LKNl8{PQI`Ygx4VWkk)v3JuXZ zRPKyYloX@uqHqjTIwdLT{Esfm2dYdjIE$TiReM$Vk86qj%1yYmr>5(=hh_8W!_E_P z4Xa7G42T^4uo}qlr?IjV&n&pYLJy5DxsrM>2Pio;OfHh3?kbGM6q|qtl|@yr)OX_& zd|DRJZff=tkI%3Zu=6T|imQ_*zrvFJDi!OK%P8qAw50X!^&M}cRh&0x!_V1e{kr-a z(;NXhPBSk$dUY);#Ok2H$%=TuZFyBDKZp1xDr%3`=OWw$+wczD1??@>sR(&r@BG5h zKYR8(_VYhgqwx$d@$*Vv2H@Jw%{_O9EpN=r2gh=<+xU68fh_cn%c2kQ(j`UG`i3b} zb%%?1J?V>}0M>;p2Qa zw0*C-KA5><-%*a3c12Ey?Ii$&<@`PB;-i|m-=+JN+k0rj^J)c~+&Qwt( z+R960jA@&(nUKBK>M!@}x1_IZle&2May726?@Xkm=%yr0MwOf2$!?5FA{#0^q0$WH zEP=XaWaV@}t*^H+itgBOkL8R5nFM4#SDVy)x_3Q3asDD=?!2LNJ|8;1SCOvE6)7P~ zQ20aSRVi6 ziK9j4d$Radn*IP7wHYQjQmo&58OLg@$pzLVFlUqhU3|RKMM(@wE?lq%H=lY58S^yd z0VxdwTmF5A5>kcd{+9p$cf7H5Y4GXaHrRj1oBc=T;=ktfk3sVFm)CItatCU%?`glV zV+uQiKORru`4;J>dYV=5oui{i8Id@^@40}0wn0urZf$o+pP%2V@m%1Ag~fS>IW7?5 zw6SsZ>_xU0U?)0eG#ijT^}i!eT}h*dEW)8OC#5>)l)4=^IP-#^FfzdjXW2fvr(|$W zC^|_xC~wS1>u}_kcOp}L9C-76Q8vmqdo2VCF1)&q>)ZFpsq`bX2)N&~JrSshVou|a z0!-JO!Xos;uv76SY|Yp8gB(rw>B_XI_Fm@aiNw$F>=Yyn`~Vy&p~{%?_cI5~J&CC* zAtsxp{{grVk|_;_e_n6P5buZo=@7IIA78F}-B2$1zE+M*5`!>35dHssS^EE3<@w*B zsPgCpF;Gp(fY!pR8@!)}`qsYaNQLsI>vD^g>E4Cw<+RtS>XfCsts9kLzx5So+rB{S z?)PYYb2HOBV`d#knHMQ44!}iZw%pzlWK#}p#1l&aZ8?RpD>htGhnr#1Q)?=F;M|y0 zpKnNR)Wq;sik0`+v#gL7ycE7AE)XF>jyyvs8V9ACKL9%v=<8zS7(W$F|5WF5v7Kg; zAwmVa(ttrA)gwuF@C2fxA>_hSwp=a`q#ZAisa1yhmEGj+H?7fHjNSYWdY<}btxuwQ zbp(5yS8r~iLMIVxe~#Ip#)ZXJ_4b6kuhSh##UqTe;}0USqkVc3VN4B^6SEf}d|YO% z`A3>e7UMcW)yS8|qw{+VIvj8_qAJOR8dYQO&`!d*Oa*=-hcP8hte@BoJ0eATaQb)+Wwgg6#g8H|F3iqvT{fOGmwGCW? zrg0h%pfn=%J*^*)IXjj!z0pnlB(gSM(|)JbIi_H#{6;kuO;Q~uX6eCf7`iPqAii(M zq_5TEoB9V94PTALIVeG9X?uPn$3dW33}R7?g~gV0o(VDKb(8L_NZw1hTv~I!%_BTj z1L(IzE6I)wN#lRPhds6NAQU_v&wz=4HsMnqr{V|rUfL3GZGC(9RCU0tl*!{(>Cm;p zvfBrVzgm8OaRdC9?D3Q2KRHEa(x%+;a~;5w619uWp@u!wCY)ttjoL|i)$60Gw$jSX ze6aY>Kltx751{KjHSYVV!n<22*W+JI*=5-dU+tCbb^qLL%m3Aa8gw-%#=rMKhtCoZP`$PPu2=o{9^-%RJ^Jr6 zT19#TXZo67_>BL}e*4#&_P3~WD#wV?6~zA^QLy_Tw(z*MW7}AnzGEqh1@b#L7G$yZ z{ZvhDsh7!Ao;*}y!-tApD7j(J{u3b{c2soJDZT$*Zm;Aot*K~7b{Z(EP2;;fV#T975Gg4qsPRQkl{zk@bKd=ds0+xtlp%W zDVGt#ZHF_BM;=}f^^>z3zV~MbJb*5_D zj_}3HdI0xv_Nv>}Mb&4GEpZtDqL51VR@S|Qw4B<{5VJs|XyE_R5}I%|lMQyEKl?De zG#F}<>+l2c&`kEoa#5+p4Rv8Tl}nkMrd+<#P|<_yV5*7>PkY~zr5hiM<*3}q^X#?9 zL7jYCd0c5Z4UY0)Q!a8E1!>e7GQx}bI~8$ljV;XH6_s1;IzPx>gcf5X-)L7}WYA1O zMJkoonj!F;aJXJNA6QOG@HLhkqdV9v&{UN<*}^W-?6T4ze@#2O_O3jTF^)I zJRAgEIhtcY~3FWTlk#CFTy(2`QF* zF}jG0Oxc6bfX=s7XVKFJ;o#!LPeE2YnQO%CUWh~b<)({>KTI7I+WzY9PuAGvl3VVw zWa+`j&+?8Zk#K*kBlF;A}q9%d?1+&BP$I_j+@4XAeK!RrT)AF>TX#P-dx_GO-4ME@J3n!{zfq$;ETXTjb7VTFf>u;iJOXD|LQ zi*e+%=3h&sm9^p!uGE^ZfAE;~I@8r39}TGAn6O(7v2cF3`_PCFi%w$Lw{0E`!`Wt;hL*bI5L1gDWD+BQ z+*?s9=1@YseCAks54~PvTOxOU+WyA#mWSV2Qo;P=-de~i!SvcNITPKA_Ah<7U+s@J zHAfdysVbqk7<5V;<~&X_yQZ&rq ze$IFCQRQHyqr~Qd`OWsk%QRT(vI!4{s?rK)!C>L|JZD)yl(8g~{A5@N}i z2O%t$cWs>CO|%ZIpJfS$(cAYx$xYwgeOE$hQIZ*vr)3%dv;Ux3= zX3H~29J&vIQj$VwMQ>Zq#RJ86HH0?hn^N@icwsN_`eQ9|8k9=UQ8ooM|M!~`nLc95&$|nIvN@_ zCKd*&s_`Ei1{xVUCdnNv;=81b?hO1wa(se1)_J<2wfC8lx<-G@0dUb!|9}fPuaHHY z*>_LhFU`#LTcjQMuD#IT5=avw`2IXy`fLA6V#m0F@XSg^iu{)(e`Y#0oPV{`Qj)2q zpV;{ACVF;RKiX@Z;_A?qQiVS&y=9e)@};y#lr8@qzs!%)%G$k@pP%?A*!ms`oPCy6 z*x4`dZw(e7yk$%ARIFZnplucU=HhKjGT!Mr)co_zstl$!XN+hF6VJxC!iDeOBqg^z z_J%3erAAVFb3BfcNA5T9o4=ak~=FuttM~6pzar`ghm){S~$L>CBHLsrL zY#98H@6=3-8pdXetuCXI(E5oht|PcZ^EaxQuQK_y^eMaQnvi+N7#SOsh;oA@2UG)TTtTF2y1GNKUsPD-9#A;CR(?% zHmTX!<8+IMwQu-eT+$&g;@ZFN@|F!PfM~<}t?OpEdK2FJ{UQO6M zccd+c)NKwu^_5Q4?q2^U_~`H(+fLhjqLG<|$@ax%U$w;HIy}%dF7Sc3Kl@>a*j}zr z@|=GS-J8ZH&zY0luJxN0Up4x7zPVNh!E2lAFZ#LGg7|wcE_*ZH=Vm5|@ArP)zwMZV zIwY2>I&zVW>f=daBR--fT*XOY#8+iw+*9}57Qc@y?w>FJuCrnN#qRaz5S0Ym>G`D) znAj!m55W6Hl<46gK|@0WpkZJFFz|j#9y9M z3%IEYc23t|Ng1$%nJ`5j9ZP8oE(K_p_Gci@&JbX*`EhmIN$Y8Q$9Kfx=Hb`9S5k-I z3-Cpt->Qi7Y`w#9Ch9xu=kA|Rwuc>3Q8!PGpz}?xi>*d2QhsfnzLwJ0-UG;+@yXrs zm>2su)i-9-I6H_=8N?63OABrvu?DGuD_QWOX@k>br4jW1@boz3$eNC)H1o!F`EiH< z%_7kkKR-l{?f2Foi>vpZUb0%>|ELG>kMi~Eq{i)+F2F$D5p+xpoL|ZS4V{D(1M`mi zT}FOEEMh)}`*J8%P|GABq#OEM7hwFM3vQ&rgY91}XM(9$dn;Z2nNzV}k8U<}x>x1Tn4{N)S$ROo+w zoJhH`mcCWHY*-KJ&-hZXJl3P@wTh>)$<}-wbn8Si-!nBWP37gePW#+(=jzbWAr&nqq#iPTHE%H`N-93DUPuNPY$Ro}k z?+824Vbfs~(R8n7N_pZWSRa0%xg~Wwn3u_ z^H&+Z!$Mg%lS}3-lOmm^E!`h2)23g2<<+>RTYjNTG)_iFSL-gl7;KB0$?d zpQxsAUA;VVb}=7Ob9W_R`CWABdb{P(Mn>z>5PVm8wQ_PEU4!FG(O+ayc7kH8`kttF z9`cKqae_a)#dJLQmi5o4zi|rQfK@6QuCgl)Hc4ZZ?kB4j>9POM*+G$=8CmBBg zOr9tS%8XKJ001Ty7V77pDh;L4NaU?UYl#_>@_yOR7-%SE_U-jo>z4CZF$Zb&f~c*v zAHKw6S<*i8msfur`MF6>89R$m6Grvg)b=fHT)cjlPu&K_jeo+_ioE~7Y)peYUN<>H yJQfBX4cny^Crc{NvrhcPW+jJ8j->}z8oz8 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/demo_warehousein.jpeg b/doc/jbootadmin/features/demo_warehousein.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..89dc6a7055fecc8211ea5ccf846a267c811ef31a GIT binary patch literal 64972 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

      b}w`F2tO&A|vV`5?x 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 z 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/demo_warehousein_edit1.jpeg b/doc/jbootadmin/features/demo_warehousein_edit1.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..b35b4c39d28cd609c2e46bfc8408fdb7b43ddce8 GIT binary patch literal 69633 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/demo_warehousein_view.jpeg b/doc/jbootadmin/features/demo_warehousein_view.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..58852dbec017a973df1dec5ca443428b0795b3ef GIT binary patch literal 74235 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/dev_codgen.jpeg b/doc/jbootadmin/features/dev_codgen.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..a050dd0e85cd93767d24887f0017a963a8a42c29 GIT binary patch literal 135388 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

      TtRPrg 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®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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/dev_codgen_fields.jpeg b/doc/jbootadmin/features/dev_codgen_fields.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..fc3761f83971997281261b57414ab17137ca7ca0 GIT binary patch literal 94207 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/dev_codgen_gen.jpeg b/doc/jbootadmin/features/dev_codgen_gen.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..4467b8efdd928667b0aea7420df1559f75f9e654 GIT binary patch literal 63567 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/dev_permission_gen.jpeg b/doc/jbootadmin/features/dev_permission_gen.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..0c235f759fa072b7ba038605d0c539f10cb8dbf8 GIT binary patch literal 68774 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

      c{i32E(csn{$CPXT@{V&s19vIF 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/dev_project.jpeg b/doc/jbootadmin/features/dev_project.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..615fdc39b3e50ee8412dd1ca45c3819913a3be84 GIT binary patch literal 46998 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/dev_project_edit.jpeg b/doc/jbootadmin/features/dev_project_edit.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..737c819e269efbbad055b0ed1071454d1f0e163d GIT binary patch literal 63521 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%0Aq`bxYzve(-X2^D^Z0(teXfY*@fy3k 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 CAOG$E literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/devops_ahas.png b/doc/jbootadmin/features/devops_ahas.png new file mode 100644 index 0000000000000000000000000000000000000000..c39e62d13e8c76ded274e236446d33b3e5317f49 GIT binary patch literal 137146 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 z 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

      1<33GtwJymBF6gU zhoMrHO50Z0Tg>;-Nm|nz)%a}fNw>g&LiuVp_|&F@%$zA`ED(RjnsPu8>E^RP5K~9& z*;f1G?4M}+*{#1fdp=_e>h1T;mnRDjuU6N~xu<4)5`e&=w6uX;gSJvzBR{Z`VvFV@ zq4rN1w%SN->j-ZUj`E+63+#Uuwgp*TX4t2SYOwrcD|2@#6RLPP<$w@s@~eD-Y4-sB zwZ$paF!4}7`+*uuw)>lE(F8w?NqtRCjTgEE(Aj&?RB;mP2dh`kwjv%^d2M9-y@&?n zA2>3=Hxw1RBNgkCFJ;YdN^&4%lL<&PAK*iCP-^@t2!V+ z08tcZtH&fo*2?Cq#Fm}zm|?yrMyPbWaRMl?HWX}K!_xeW51a&YljVVARKr}75T5KA zv}D1l=tAU10UzKI?Hbpqpvq1Dc-WxVfcwv`r+-amC$oSM-LV<0EguyR= z*{yB54?TlyPYQ4D&~$^ZECl>;sre80cF~h>{OZ6+%_Xl@_zvi@yCW_jZB|Vn8 z-q5v^*nf8D#g!z7ksXNBUK^27qkG%fwK3@`!CnVkt^;mfURhxBYkRef9!7T4x$D@4 z(bnRq&`hyUg3CT`sMLiabnL&+D$WnPxc;$T{3{^AbvZ8V*Crml{i&mr@mNMbC>sYcR?@I{no*2sv0Hz~M)e|M z5JDD&GdssU3C`F4E(FAOElTS1p}2r0VeO| z&f&Ef>k2M=kMa%L$CB2UGl@p|0u%r{rb0OR)Ip#S+5&@mtnw6yet9;o^?GVzdQs@I z2NJm|W+h3VW~+8A_ee%6mbsj`e4X@gXMW&XTElDJdcyK^+8<|w>|FRx5iI`}@kumu z-7o%vX>ZxdEd(nYogl{M4344HU#NM3z#6w?o!e`NJ`}p5B6&t?oC7gqjU+RjlBr7Q zKYt#GpFBa%VN$XN`Wc)7N_90i2d;IK-%6W+j0rM26B$@*U`ON1>^?33lilUo9^1Hf zj(1;ww*bQ@hoFfzLuPJs zCZjE=C(&NP4l2PGhC&-qco0goU!DGb>Hi`99Ktz*@Vx$&#E=iIe

      ~1g9A%ZtwWs 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/devops_apps.jpeg b/doc/jbootadmin/features/devops_apps.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..d41201949f13128f76de15db865856cc44b6977d GIT binary patch literal 45472 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/devops_cache.jpeg b/doc/jbootadmin/features/devops_cache.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..8eeb421f5b012f4ed859d9eb8425c04e9f596a02 GIT binary patch literal 92974 zcmeEvbzD`;_V}R=UD6@a(%mH`At@kIBHfLopa<#h4na~0X(d#;Q@W)?P|`&FopTV* z@m{^}z59Oe{`H;B=j=5zYpt0*Gb?7z-uiv=`x^k>H918&00II4fB?P#-)8_a01Q+# zbTl9aIvP3_CI%KRIX*594(=slQUY=Yn#+vzH1u@L9HKnTY(i{w^t^I>LgH7XWu%$7 z6;u=?RYawvuRxd}U}9n6V&hWb<5OK>p=Y`BAE)mv0DLs0*T6$01bhG@J^~Ux!uK`+ zCAgD_NC=Sro+WS=83l-n2CgLsS3uKQ9r&pbk-)XzrvX?*d$096*TvZ{ZIVL~L%m9>t&pb1h*Wz&PJ z%N_`V!2eM~DDWBS|6OA@z(%Q*)j%fB#YT1Uru%1vTWc`Bx8z*Sd9U z^pTZg=@s&t<8>g7AbhY}aOO}&@E-zbYkQ>&+*IG_F1^KkH9sE&t|dorf2z>HZ3G7Y z!{ij5_W6o_;X}2_Zx5q34D|}xrbM!v`vtLi86O*438-Y__EB^pWl|(soiRrEVC~!C zpu*({HA0?KKHV%wA&xi1~aEpi4>6&hEmo|ha1n{80;>L{l=5AdKTPyj3H z6{<%CFc3gC>1)_VdS)|l4gH%Sq(4~xfLMQ^6zG%$GFx^3CiTBS!;lBh0t^QU@_hY- zu!8>r!bdug%ZAY-Kqhrh{GQ?cToA1EzkuF)`HDLV<~>Z~2T%Ypuv@-Na5*H9P;Nli zHTH;3$ycnqHxiPs{TEHZNSVrW!B2y<5gaL8m?LPb)jY)4iz`h227=+H>UACbaK#q( z*^umio$@06A#K@>B8SGI1Ol5&B%v%(Crzh&&1z$R%^!(g8T6|fB;>ANmIJU-ndyxF ztOaV7taXik#R!06))oKj3iCkoU$;S&L>OCs&uSEo45B6V8LCUlsfY)x->r7{3t(U` zC#A2|A4O)vp+P}bdZW~}kwE?ZuLL0F2@#-Qz~PfW;sCUDv~_UP6ANG8%yNkwL0RXU zQ=uFAv)52k{$p6J#<>jbVp#<+Ldngb%o_cm`euqf-yHP$YKG?93;Tl%mW6zDFP&}V za5XuV|JWo(Hz}iM-oD$oL@;L`+q*J_Laxou2K7IpAy&3jHu-4usMU7HiY4AvSh{Jq zo*vux^gdhDA5qUbchP@?Xg#;>Vyn+ETUly81!^ik2!gvq83)xomHO9u5t>z8>ub`0 zoBmcS6l>TmMi2z}q^;1*n_`u}uIm*T7}zN^DIy793xN@IjdhL0hW%et9vCdJaPoJP zcN+ZyN09=eq6Jiz{Im2FnglQ~BQ;L3&gbxtM9@59r5rlwS2fT9L1y|@)!6{R>Y(ZZ zIw!C)IEW~Y>re9nttLt$N`k|K1GRND}L+IKiy{>G6LxX{kd4}cSAdFs& zUbrkA$jbJ6p`wPvI|m6+%pwSAjn4Bh0D+F-Z^;#m103|%bK>)^ubqD(`pPY)z`J#l zaAc<-ig_Tt-6gP^P2oa|r#`EO8#_{fGzfr~LzaD32LPfKygW#Wy(gG<+A`No^SC#C zEDdZ4Sd3a=H~`})wcm{F4Bu#CoJX#-ztWbvq`aj3l5pk5gt+k1+j^TzG6`omu>09W zdXJ>JMHA(mz@uX z6JZ>UhBte=zFkRw(OUM(;45eHVd-hVop3&Jxz_KEZ1!qC6_2__)A-J1Gj}F|ZEkf! zt$?l9RJ4?JG^on#*|NK(!MhCe!rZ;DY-vB(A_VMT(p;;vDNEh*`uYX;auxL1U~R%d zV8MtJ0GoOLCpPKyIDY*i^2nec)ezT@J#JcC2f*JC!nQZnEEKION_bBKZc(=qnJuGc zswQ%dKjxen!h=l9hZ7O27jeW()WP~Q1z1;(MQBSn#b365+jD+(Jk!i9%T0BU*`2$7 zyI8kY&nU1VEyOiY-C_GluLF0F5M#BwqHZ4hHKa5H*t-M0gP&4FLd%l}+GB{waaW z&n6zO{CvJ8!5pwsfXZY5QlR8H0uTToV}!`DY!d+7J|h+g3xJgZ?#LMi7l6?WvgU}$ zJBj;48f)CxhG`vQfM_Wq_*3wj0stT)e(+E^PL;LQLy8A?K&jlZi( z5UwMsk1MH|-YK;gP|+w?Xnvmlv(p0i=m>}2yUs=47e-|B4&`FYy-xRXIK$nn=SmsL zU{pj-N@d{XUPWZjE#IZ;3Rku;{*2y+sJOGI2H%YebyvK_Mv@vt@7=x;2GK%*a=6CMKrE88r4GWqYLGG_pqRF5u_xvHL=uYe!^25T z0@zTx4wIo?e)TYT^T5vZo`3Jwn%i>Mq1VScx3Qf;%dG9Ble@E>hQS=!XRHALZpxEc zpTgnKUmHa_UB`Xi=hzKFmK##f<;S|DV(V3%5j`K7>o%Rrh1pMUS8$hE$eZn zc?+BO7jIwj#c5n@R1=x(IQ-%|xnA1nw){nGCgY2u&A5r3>0QtHvI@hh;nkVK&X?fM zBKUWHyOJv59hr$k74;pEJ{ALULtuS@gTTi2)R4l;pk6k_mV=A1kVobPTs0bDRbUBEaCOLM*ViWdv}UAr_}I#JvGvkpUpy9z=4GfNV{n9Fh<*g(HRo|Ak>~ zf|C^p6%v39@WZr1#mMMfMT2LV2b_b>Z8tr(%~q#!+IA8MuBlV-e1b8Oq6o*Pjy>zzhKPoVdWDoR~k~8fnbFW@N9MAAfxGZKVl@$6xxK2vf~Tr?NoOPN zE&0X9(vR=%ET1z&LXG=!Ma1Xm=>&LX-+P}t0bQRM%AnJR;C~U~z^eB5o)NaDqR197 z>GQ$ey#_(#1DDG1gBP=R;2DteKO2nP+@H#fT1Xx<@zbS4ZW3ai(M{p|;yO0ZfLEpO z^Cd&{pkcp?m9dcj@yolU^W%c~6N%*gBe!tGYb(z61cHCy+kKvP2bay)s|!$LNkkk= z51<8yS`g_$fKY0FmN4oPiM0#RyjuOPr~fyN2EVBp;tv!)}xg zS#caoUg0%ge*-TkMAm*JjBu$liAD~2(8{Plg2&De^+gf5B+spY2Ui>CR zF$f?8hzub3VF1oeG8fzs{NMrCgYFq2qe)JBZ975&K84fj^PxbvZUw|j;kju-Z*E&x zX$8-RONy_4KOy(-Ljw?Gb}+&vxuh7g0X$&ilu?zCFm-1LuhQjBuFflwo|PtK4x7r^ zEhTC&do1!>!WOKfQ=LkCB|}j2O$<7175*38@L&1kz|i&WqskS^sFQmhhPrA>%O5_g zc?2EX@^2j98{~es+j>3Ow*T#>%NC3$5-?cRP;K*g?xi^M?1wA7o45ENmJ^7JlQm|? z<+X4-Dm=B+L-e@8`p&9hm4l$&_O3D$#(>zkw z#$3%R6YWVT$Y$*-b(Ks&u%~LimFDZE8_71kK5hh2{$S_JXndS4iP-k7sLuSgcXnw{ zx)EI_%qcn+BpwhORqEATMoG{tS#{`t(%q|FRgw92c7}aK7t9cd&5=>AVwqkMxfTf7 zycAW#S~-W{1PJVrDI)>8L6AD2ls_c+^usKkW5a+`V+hNiLlEnSay9!r4+FH$om2$t z!`!Bf<@!63yuif)L0mp<2o`FNP)8jkE(P6af?$L&NEtFBp}(p^jz~OnS7GQsAUM&u z=u(y9n3O_on<_AH>LI{yZMncUml~eDDez1vNx^O~A^0zlMiTssmje~a5Oqmyu>R3C zVqu4;`T>(m&Ydd-ST)Q9z2;Ya4lLWf#xFuy@AB38naESG&D|ZijS$$%^B+awR&}}y zh`zjiD-Np7#TGXrQX)ATi^d%X9;xabr7BTyc68M4VOyF~M8>D}$u&Q1v@><8;>Nz5 z9xmWq&!6_rqH5q7_Xst(4@mgUA3P7zaT-(h`&K?*5D|q3L&kWr{?^TF7UlM@P)V^s^P>cHkGg_FWrn6 zu5l!^aT9W%n0NDbnO+?)UG_+}sY$EJNXryHJpOhha!|I`?Ot~{{I=)Vq3_+NWpAHv zIO5i$)9bGrQnnmj-VUwKP=i}TLSK~#9!%oL9tg&n8x$;t+AAbL7xXabyf>%7xl05q zhc{JiG8!&1*;pW0EdYEVcC7@e5&=MDCMm`ubz~M~wzLO?G{(||h=f2%FfI@vi3zpp zIL12FN7VHFcrx& zROR?yzJQ+#%VGKA}oyK~a31ielrWm9j5K%CsWdmBUiokXe znwk{ixXfK3fgc3*b6OaHDBFDI6u=5$ZoV*M=q5n;nImHL`mioSI^gpqPt9jz{(JnWlaoGw)}KDZY-a_R`;TUIAo;CLA7&8!xWP>6kz z(S1dctCL&c@%(6Hb6|U>pjyOra-fji-R$br_{)9vq+`e8&gZ9mkx-+)Jmm4I0sDma z1q)xT9Ic#hw_y$cfZ+5OtPZ9}Cin)jfmxr&8l{OW?DdkC_2#(U?{J&hy~^57b`*zE zK$OYt+<-mj#}fzMcF=ePD(TxeE}Qp+28yJ2TrYEtT$076cnZ_ImC=6u{2n|xE$t? zRmczF0Z#v-Kh)rzNIcXw!W#%9a10~aOl_TZBBH%f7YS9RI zpurSKbKs-HcxrSI7=$F)45Mmw4>Jp3)A{VLoK^t<=f2xn!B6+UjEjPU8SPWkBCogT zd$CB!*Hla_u;bO|xXjkmWqW#+<5+n#>|zzB-QX4%g2g>iM?6$KcOMWyDTY%YPBGmZGJ>_}eV8!e6ZbQ&WS?bn4E{o0Tjx5gG zK*JgcUH&!HcW$W;t7OfBNa8a`Mn^a z{L=iEA#X%D$%-oE&BZB?&@738MrYQc-u3Ob@_Dd_ll<{O;2TUya5+Z}q2dm&t$$di z5V!Q7TNOWH)VecR^?5;t1%?lHTb`Wm)pqZpuSG+E#LL3wL@3Yy+Yke{i;%xWmEE*)d{6nD|0=k_0^-jue{O_U9Y9=M~4#3wgpf(1ONz0K&M@T1U*P;6+(F! z(Gy_-GFW%v4PDFilDSN4M~cY}sh?a9fONmFiN;EUXbg0|09Z7NPajvLX%NO#x=JS4 z3aF@RAjU|>%7H!2;gW^SADzVZH4#wNXDsA6bt$Ml8${*xZpp?L>+cWgNrpNT7!_6L zflq)Hov(Z08LFps^laSJ;Oj~+KqDk3U}mG1954Nps*AYqUk39MVf_Zc0bqS7vHtkGqp+>6;3+9=Ge1VQtE`1#k4tQk5f8vq{}#Kgu3);$-D0Fo(S zb7~Hp1n(iT8^P@0cHMSV)hm<)d3qR67=oHRLQdQIuhfQrqQ$nPAENE(ANmadz#bj0 z>U@3>@?12Bi@+PgcBXMRkAdTN?GLUoK9*&x1xE*`U<9O*su{>my9B1qao-p3=<7ABeoL%k>qwz9kFlLWs8c;VQL^K@nLm;ltQLIN0(8od zG@A3!huC#YoO|Tpi^$1+xzjbsk1J@YHO_}?%SqM19Rq9o6oP47xEh#BTPF{lg@lLp z8+K2X6>Yb4>~LlZn=BOjT)iOvf*63n^)F&RlE0vWJs6VJhjpw2d7~Nr z<`NyZXtq=A^CtgHiv;!|PQCU&*8!jh)!+!npQ}(%EGGYI?*k5fA^21OOviPV9~^_H zJ$_G36i*9{KK)z|&OyDq_&?A=un^;3zk!DQc5Q_O2q7h>>3lPQl;VtQfY7(o8oGJl z;JV55R1?*scWcSJ@p(j+fA4li_8A3u7eXihl#}l8#Ni`O{Ws_Bd$&jG!M=0?2!;U% zDS(gt;j%FBbcYm2g{uxLgN?J6EkzUgbjQmIhK%}#_Rpi5(|5zj^Bf;5oXJ=|(EG=9|Taxc&BV=kVa$PNk-mHg2a@JWx%Q_jnW_uSv(RLmYd6y4D0L;20yE&yy->NB#R?#)r16jF$N{GK*w0}bVQE=LhN61B) z7;jgQyBZ-hbO)}4AHy~#0K$dG7;sx9B=7-q*KeolH;o~lp2Jf^_JXGg)`*su>o~~9 zF)P|o+fw=}MW5Z<&o3w+%A-wL*sV5WiW=TmIHLg{%RL0giJ=EMPtFf=PJ@5vIR6S@ z{{7ooOPSf=7=TEo@LVLCjV^z{Gm6*XaMfEN0jq_hX0p>cXCU^igXJ$i2j{ZmBFM0sL0!L8*0O?8?4Hq&q#@`%o5U#DEjquvu@DnVGwF=(o)7R=?Is*^Z z3Jd}Vr10UrdT-fXYRY$(RL$AAV; za0Vkc|HeR{A^hon`tl;I7`EAimBB&3b%Ee;;KAR&$%AmcOJ!^USXN#cX_?bo_E1@R zP2){a!Gc8cx+ENj=BFi2zFDFb6?6MvwiAA9vEeA-L6qN{Dymx|tP`l5&v0(uqYzq< z3AUG*eNtEG)t+qXiz8ld&;Z8<2^g(vXu0ra1`^gE^Z5?Qwfmbo4>?-eRn>WfSC<#O ze#V@0Pa8S+VZhnI_++3YT-#vK2#NY{E+7txLPI&RoDIw;ODWlTI}>qXr=0)Q`x3wN zLw@epdG6G>fxsti@sO7}pye?0c8&%Q{C^aDc($i-i_r!3zbW4^X0Qj)^18FdX+}BF z==L$JChI#Od2H?5J>@O^NW0}i{a~0?1iRDz@`aq2jwBpDJ}lKAtoZ!u+En)6)VGf0 z)H57xEc6=olr5*7f~^wQ3#)U$g3lXns}+&G0+CDIZuOSAowf4wYX{MYdB^(hhM!JSEPD38Hi z7w)}EFh`@uS&q382hNsi!Cpmw^NDzjD6R%QXQMW>j)nFSHgu$7JRTFZIGnltorhi@ z>JDYyXi8RnZ~HHtj`5EtGT{?-*?%a%_Re`nP1*Z9AoZ{e{HAKtkJ^9l_QPtiiA!}0 zU)I{tUd*SksBxsa#snpY^#}>P|X zcz6aKk7oP4S zfGWA_6juxCXkR}<{^1gxdc;FNWB<+EZ{A<~A2PROZ~B{k{h91hIpkv>SQ%9Qnwnq* z{{=*^40&rGmLN+gnHrY=Zy?B5X)t=I6^?=B{~HMM(Fu$m>a};l^8XF=ix1LT@)VHp zc%>grmVpu!%7UR>2)D{m2L5SrR|7n{*CJQc$2efi|=#qcy&FkZ`rEvCW_JC4>BH>jbB656*Ax(!HEOYX$LJ^<=nd<;uk(x;dwnp} zP^DF#Gz|Dpdx3K!k$_rH%Z=DyeTK*3n%d73HcdCts}gsMFKrv?h9I#yv&ncWz4=uu zE(Ls^M;6!O+PgrAmFfu;;m9@C%4OhpdGiBS|I-eTK2f^%K(8`Hi-46!{R;Raa6UW$ z0x|+3G71th)cb1s9&Vu9xteT2M`tH zwhUNaRNqHQn<}x7Z=+svnAy!zowX-)Z8vkEN?jZ3T2YGi#D14XwAItQeyJ>7K)EuA zs}O}1+aV8YVRf)-VbFAs(Oas?GoD;b57+So|K2Aiq>L$_cXIEXwl9tC8_x(iyE8bF zX!nh6P6t|gb7s`;66tt)P9d2rixpA?xjQI~Q5xvIlISnAitCfb?);{pOg3=8xSA~s z7-himzGPn=Gu}P3XgXm2iW{v2g`hqoU1xLc!Tqu;p)QU~Y8R;9NRon7&_Mp-s*IV# z%4USC4#*KfinD&EG)daa%tZNL5`!xG@#hKI!dj1RpL`81*}THesaIyJ0NW2 z`eHnL$^xAXr7ce*<3j@X1^?y3VM@0>e*5bvMFT>2)yzs+@#^i&jieFzXms)k3 z+89e?6Pg6;z5`aDW3MBNMDMLS-*Cwe+0omrZt8a>xz%$N^Z{w+J0Rd*j-2pUO$piZ zsC%2&EwPKxbhx{8Sm#KXZXZGmSVxI_*)u&W1(R6+=M9U%VK}8r; z#Yd}<_m8QtA@04Wt!#sm!h`1kdZD^hf*v+j+567j(w=g1PO+%?0pJqbm8!#qJv|#S z?8scBO6JyxY{WLdB{>iO&2N4JF&6hfiKp#WqK92=R~MD>(eW`V7$Nz@Heu2}x|#V< zVLYGXo{1bg@pk~u?D&6iO^l?MG3zAfW>17x|H=?VCjl_{t~k{;K65K46Pv7@`@**%aGj!k z{0lBoNzTCoF}m>B3kr93NRaufj(!Iy4KCRjZ?3v!i6oEO3ND5l1+#jHB4vEc zs?|qBT--fo^DIQwIY57KJ%fu$tB|yZJHe8y$2ok3seLYwHBm)EvPhLaypeLzO4Wga znlFpS6+I{=p7J>~$ zRj!fD_pdF7*M+b&=tmOlzKC7EaCH@JfXAy^UMLP*=zfbosq#=L=AvCRJ*Fl@$g6PU zqYWYtmV}WF<`=?~HlF_aB_n~22F+Yt$Io)ytc}yigfywSKUNK=N%^ay_5DXdoP=bb(z(N)EzPRTW~(9HZqf*Ty}Mvn zOI%BSf;H+o**!}bjf-UxeZSSH!L@1p^D%{D^s@I0w=ZKT%bL?bj_}#Qnf(UU>XY2@ zo=XIRH(g&BI=IasB;OV2PkyzeM3eu;P*rEUy;1kTC#5f4Z|??X@RxA0>b(Z31{r#+ zQo3@-6(5CB9xg);Zkff_?N4EWUr=*ywx-~BM2A$7ZWXf{STq`SQsT8Tpt1JJxcQ8J zG67yG-bOH+G}Vb}VCx>~P#VjJMh= zYltB(QuL-Y1)VqcJ;ccFmKUi9%U7dq?J)-N20Y9TQLgSQAHG_=Q>uL*)igJ5uQV=e zN2uE*x8Wu=nf(Q-iy4KWYP25ErOQsP00HXD-ihYK!pxs?R&4Nut#rIrJg!_XU;7T= zxWwdhqma5p6}eI|6e>eugExB(K*c8YltILaHii_tYaBbgw_hbylTH=c9P}Hl-A0zU z;I|8?N+*{6>~mu_G}S+BH%O5DC~xXE<^q2&C?~$n;FWbXNf767YPei$FH7rpwLf5Mu zW6nFl2D(twno-L%&7T^_Uo?%KazO^ANO&FN#?H;d_!E-;%i&k&HIg!BsWjXzJR91N z!aO`ag8xpEp?c;!VB%%wJx&$*lNX0?_q=UbIhVXii*E`0e2g}{+xb*{c_M4??V#t; zdZYKjytvnUs>c7T_`f0fHy1+*$Gs%;b@J~3Hx|oA-zO6+b%@JB5cenp~pg%W46K6(N3PyXv}7X$jWOxE!Bal29rtKPVhQ)keXG z8x8E=0iqQBrsYq0Ik`H*{H_Awakqve6$j(lLWl@AGQNPWAU zYmQJ&+mm*mT)M&ELiMHD?EcN>(XNuLt3~2EvQuB#tlJp+iC>b}><NTkC+cr3$hHlMfmQ6@H(l(PB{psr&m)Dfydk&3pbfVfb%RLiHBMWU#klGUugb6ep zqc1lK$u~&Qe90Na${Rj;VrQ~xAd@VV%kRoeJ|#JFb&p{(NYn0}-E)wtJy!pT!n@f4 z&3p@@L8T5YOZCp0#KC)+RxO%){Pi)shK^CVHezgdW!{%YHCSn~TUuJWyXj@7+e@TO z+J*SOUE&dtT|uvy@B^kUpvHtQG2ctP`*?HI>7yYopRqD3wbF^+4P5V0)*Izy6XlZl zKa`7p`RwJ{V2Tg#yiShsp5ETCNqvew;JRBYV!oG8T&X8}cj)OF=DL7({-ja)SbEzH zW0#%lsz>RE<aWW)%A>|1k_qoe~la#1W5Txeb415QWQ;mA+dsy5awNS?_Rh0hU5nqx z`c0**t2sN-cR=rAV|hiVP|0M-qFDvh!#-QKI1IZ{$Wx|f;VW0LYRE=x2;VMm1Z9fq z^a!f*>hIUmETZA>8D-*)raBu7=m_>&;nk>*g|8|LbiB^uY1{*4uXWrMkgMr#!Kd^x z-Yb8Huk@jw#Db9RVt{-G^>9GGKmZ;3D}m5pL3{l# z3#zY33eu;vw*}EZ((K147^7b^?b2Xd#7{bsY+iFHdnRQ3q4oJy&gqR?MfXANAh`ot zOG|tGfN-=6c*(@>%ZM$m^fVuo%L5kCpM3~-FTz;nW51ap&xG-f*TEjpB4ncOe5!JV zqp{6#J!r3c$|J-U+bs@E@2)=geJP>fc+x5y;r$wKyjknRg8IoZTKCsa8sPOPGo<6V2SI>{G+zG03HBJk_H_ zh^eYCOf7vp+wqJ-a5{bNxjL%RXJPalcal=WjusV>z|fV zRd479xJQ(RwxBgi3iU>a255XM3!Dy~szC7E;Ekqu^^J_YhLFR6N-=GoetdJNEzYfM z&S<%eJaT3ZM`W+3^O?iulU@-ou>o2$7rklcmiAuWT2CAgR_WD>>24}wgXZ?W#PtO6 zS0_ym0%$P3@WbS}cs%-rra(rVs*N6(RQ4o5{sxV=2 zKRh~kZ@wS6(ouJb^|mLHiC^ax46$yN0abc<`|VrpVpL)?@XeCn7^|RH+D#4Z_Dc#i z^oY-2+MmmaxtO8!$`xF~m(`lZvX-;RSpIe!Ub4Shk70{cJk^hUVq7~sFTtkiO?xrI zT!|qTvnqaFXPb)^nWDU1GcZX^FVdonnwx2yjjhAH^9ILq?_t_q=Wv;es@Om)6J}}T zFz*&uw1tIgwCSf@%APusUk4_gdbmQr1LVur#yWwZW#ICeRD2Luf3Y!pkG2$ly8O;$ zPo$|=j&wDOjyjMd1^YrcNf3(2m`?TEj#3LkeImtRgxsaaS*I|NQ#2l zf{iAJfAS(jvSey5#o3PMW*jm3^Q%B?EKjBueB29A4Y?$cN<&s!_fK6pjVW;f&rxfr zh{bbuZ^O&-LCg>bbc0xA!lv+1r! z*(G*q)N$!|-4=V_iyu(Oo3Z#X#6Bwur#&sW&XvFu+abvCcs_LQsfACyUGb}}oirbp z{Cn+>7cG2ld>wRuUMV#5@yi=r292ZCksOVhz9`l7sa$fDdaO+={ZCfk0W3Z;P!srd zYpBjnmfeBK`Gb9H2JW?1mnWWYlg)|C^pa$H8$XE!sNpLe7SE}v<_6dAdgWpm2_xR# ze_eEUJV&n-H1Og9Ap9>4ZKUHex@V|wBHt$hkOdL8bgS< zbT9$1cARaj1&aXr<|f!plK;kk$J8g@P_lI#X?#dXL9(W9(Vx-+n7je9@k3h<0LHl- zntoF%`{W7t7{@GFFGVq0b+5TwEiiGgA-@^5EUg=3WaPHa0JIPJt;Z2a;vrM1X>l}X zt%swa$lUD6!laiM&=|gb{F0&{%Vh1VXl5*`$D6WKdZvX|%b8#LSP3rO(q_9jP^7HUV$nN<%{=YA zvh_lWvVWI&a72@P)V0l&Psvn}8=)KhQ;3t$BFz8;p&|~~nIxF^?b-~y4zGSRFnigi zkxiDMH2#T;Q1VUvC!5K-H+>(3IXw{T2;7Q~Sw?-PTT)|bTVv*qNUhd%w%Cw6 zbv?F7H21Oa)csLoJ-{=)wt}%%hl*(jz)Y!;3pI-S;|HsR12hfQy@}Z(S8G2wN|R3M zp>E-skUEZ!&7~w#j6DtQ);vz7Y}}lQd4$!m*+dc?QccF_qpj?Pru0LLw=qy6p{W%i zZUO2+9NC!vEI?7+FWd<_^xh^hF96 z+gRSjmg!r55rneR4&$EN^z1?1*%sJEZan@Y{HD_GOLw(Y1j=d69`~=N7+=h}$>lQD z5>T^yBz^z=Iz>9V3kzZKz#i>|<|s62lr1H@3o#cn@Z4cG#e&1FPDcF|iDkH`Q&*Jf zD+{9yb4uMp60GTnO);`i=_BJ?C3G_-t(mBS2H{aexb@fC15u+GFId&-mFg+<(N3U{ zf6m^t3-V*et9A{@3~^CorjQIr*>8HLfx_OCBVMm9GQ$wV>efo^i7M8 zB6QpKOV|86fMzd&EwLB;5AFoxhc8Y#+4EURRa>k&I5^gxx%S6KfMzn;+YXWa{U5N9 z$gU~y>1NKo+^D*mn)?v~z8csKe_7V*FDmLHAD_JQ7cTz#nZqq%m$kb{wf$+O(N;UQ zk~;{ggST&`R~uz-yE%?YO{qO`djutQq!)4LV5}IcOelu0vyrj9>3-agU3Z`@ax@>K zaF{`!WZ7L_&zt<1*%UELgnc>@O4-jlj77D}4_4JX7U;c#g?b1QE}y7}1oO#W2s7qu zy8-VaQW`kLZ+(@~|A#JRp{qv@cxQV^2I6>Y_MFmx>?*LZcysCvxR$T6S4IwC|?g4fqZa4CEZk zcN(g4;KU5_e+n=0f7}^L@PuCzQIea`I%?Xrx`L`@J<&OG@!I_#JDK?t8dGalU8V1U zBE3*gjXDtr%Yc2eOihILcsgly@e)5fT{g&ptNs`n^IS(xr$4*&si!b$TW- z6|E|9_f!d7j+;Q*DO0RaW6R+?>)sAK7JGp{h(^~m0jm<{w#G8lZlP0hg685Kdrz}` z+6y&8+P&BM_Yp}mUZjp?f3#g)lP{4T?TcA@;xbnuP_KdGRW8{c#^KvpciAV^!t_pa zuO8*LLaob4sSOtg$4&$4^YLJxY=E8Y=P5)B%Pg}(ax`4Uq^7X zDuM(SBi*xO*d{^PjJq$orym#Zoo>IlQyFK6Mte2cm1}tZC$}B~FVPNzU+w(A^6-04 z14~BtYG>Fc_Qf2v`GdlNq^L+3k`mN;MPs>+SV{GKJy^IaxOYd*){Nif*_)X%|e2>Vr&ghr5_hpnqL+uK&(;i6}T7dhclKR zzLslIp9OSHn7Tyi9ylCuI9XUIG}Wr=KAz4zR*Dee1*}}y^fjMt#60jokSKIwkQ<`G#eC&e};>fq-FSZ zZQ&lElfUuMM)Rev!9Y+&A=*hDOPS|tcx?R}dUg|DV)8OZ;^-7{m+F4$carn8uXNg~ zhpG~LMJ+iTLAaC$14W-PA0|3o+$IszD|HZzcJ_}ezP|R}me;C3tf33bU{HqBX2VFw z(`0Vuc}mj9kEX|u_kO5ftL~+J2Rw8|D&rbgF;hkq(@9?hZam+C2g_suJ9@2yi82&E zv1^peOseV+KTFe64&z;84j93|`BR|SOH?9qkl`P?=wEyIUJ5uHWpvT1`|D!K|JW7p zbSjAauOb){GDA$&h6fT_gCj+7{c5%Ap2fGn_&SuZD0A7kXyQd=;k}PRQ~|6&*Z!$W znI((c_!tK=t+GBeTbFugmY$(McNIuowB8H$7IJ$0$zk-Zf~w?9e(Wnx+=$DGDv=Qz zNxGEI(-Xl3)^!DJWIDLrDXq!0cQ1tB>>0eQ_ig)@G$wAB4QV)kmj4U38_&&~Z9Mgo zm^8c#PqxpOrtcWd1p_R`COZjvdYcm(Yz0H>*E;@0aX5vcNnj1nUaHc};fX5SV>%pZy{@D_dO^FX6zK;Ns@ME=^MM3M1h*27*#E{OK`CV&mWT6ixtlg6MGXvpx&xdxEd>G}Eh z+<&5J0=#5*wm2qD!y3sgzalFTWpvnH zd?f;7VDO^;PN*@hDn3yYv7W4%pZo<(?Xl$(ec@|vs*6JV{MVMOFgayo_;NLdO8xJM zoqA&6y&L9g?zueu90uL))fE@Bj?`>53lO{RbA35Aw994)b0ty>sREPTB%@3Eabvi2 zU@IVBbgY;MWd17BLhI^FALh_V@ue56@p*JHq@jaY(yAG6Zogl@ID_i7Og+8TPf}NS zHE)~G(u5(>lk061cE_1-x4p+-m)a|8lJzE6LU7G!p~}L<4?*i$_L`uSSlk@1`U|g0 zWnJ>;IXO)fa#mD(mZJ8k73BUecN?y}-V~knx|cSVljxUYq0g|!R3+HrHPFms&J<>A zA19uYGUcft-bwg=0Q)iisLWW+|*?5m(!+l7-uUV&lG z*+9%d&hG#x@07sz_(y&GwjT59r6MELIbr1@CGKrYdFmuNHL25+HqZ8`LoBUQdV+Np zn_kk0r+f#*U%@2kAzF;|GmS4GtQTlIZxYY6SB!Z~mZHhwMbN;8nkmT2g(B2yjC;xDaB;?y zBkbvo(I?NJatVJPnZWFD_Q<`y=?_Bard1Yxlw44f=<5nNc;8+r$!|b ze)3>8wEM4%uT2d&KR^;zAp`JatkP+gILR_HkOv|IbO3bj&b^ne-F4)Mt7=nTdV;TM zQ!28UUe%{1rGs)2?>fMFC@)3Ev`z>7f4Yn?nQJ$09lnkd$)mjMAau<+BQz>c2M5`E z(N%WvM8;$*V_O54!oU?d{qV~+8cMMiijEvL%*NejzqG0ra=Q0h8WuB~ew(|$m6@42 z^+{CpF8FJZ)VV|Gu^7C={io12I4l#mKYXI~DHs~Y`gkJ%-HML;STKB32fxpFdkGgm z)7Y4EWL==TB~vN=XcCLk-f5loU5j(oO1}1JfOEycZsb6MMcp8;th3nCrTtG5cNTpj zY`5KC8JGU#XfEw}`0B?g{X>`HqwO!R(0&|x;9RhD{|*>XW8Nir#Fq!8I@{Zz;CojIJP0Zu1&>AzeC@ zb!GT3&xMxtKec?;ul?YLS9I65O}yyOC&yLn9*5g1#l_f(90~=N%QkOVT;4x8vL!E1 z=a!ns>glj3UnfPWG>Wuej^|fJDJ4B-I1slX#wE41m+dE;?$fskUVE`})ZnYKPol`42U8UvCX*a)Lf-fn z%OI10LA>^|38$t~QBWkUzST;n7Gl)ZgBw>}n%H!^N1j-c^(Q?-QkLz~Ns$5~nX$M; zDNJ26wp`EB$NmB9s|#c_*u3IDz! zuVPPd)gjE^x5MJsR+b`b>jJfkEuQaar3 z=*|k>`ZVubJ^GZNTuc42eksd1p})h`Lh};>9zg-Bpk%dWSt3SJ>lz$Uf!1LS1wq<;qFOW_WuJgXG8yX~kz$)N8@i=S<*JRGpWJq+Z zOB%0zI$~YhK3dd-nt^;`l4vux!tSufGO5H%FV^s@abL1j}iskz0CZ3V=+X<+YWbp0N01ywEr=LuQsu_)!dvpyw7FsRN zEH>rTvT7LaxX{QR*{=6Z>H=2>sLXqp&q~*JnKnmzk~4^|C8Vi60QEaSz$QU0J|hvM zgmpBJWi+Ch%VeW9;KOco<$lwQmGDYjqNVrFM8Vi{j zFx?y&#g%WHY*1fdRNbyM6^BH8s01(%^xx#bN5j(8DKaoJ`VM$JY*I1msF~zRb^(bs z5?@at5}1*4do^Gcy?Rl&D?Xv?r_=C1h04-fNj1s(Dqdb}U)pgTx)QW;3HOWHyXCiY zXvlVDd}Ml={Kb>xBt@EeHuuOoG+$C=pLOu(1AE=P!LqbxGiQfsC+Lcg2KK*FDG&60 z2e>L`&ZWPbkQenDJp6+d+M}C9rC-|Z?OME7n3TlrRVOiUK7~fV}EVh zDDE)iRLO2IGE&oV^fBK|^l28?W2}OxJ58p-`{v#LB*udiGsrtZV-YpoLtTR zJ*&M69iA_GJF9FT5%rE^X=rC@%piZv5lhv-WYa33aS~Vg>A@U(`i|q*y2oN%&&-Cs zqGszjn)}sc-Q8c@cz|PBCUNgvg3Z4s4fwpMW^Q{BvDixU9q`_!lo3a@uQCeB#BmOr z(rjm<+raa6rMH*TNa?lJbY5u4vheX*)$l3msCTiu>pMIFe?mJFhr#TcuZ53z=j zG7hkNuZL7>7skb0e!$+U#rE$0%{Dd8v9O)+jQMX0;*tW*I`;@Ox}^$^f7*G(IsbBG z8`K3Kljo-i1(kZeyA&lI;^_Cl%fzPdnWaEmiwPV1OInnOFX<`eGxX~Mt-xEFX&T+= zpOJmo*hBWX_vn~aci0qW^Im&8tGb%`Fh$c=z2xFlbsO_`M#)XOG}mzZNrB#mr-mDj zMlD%4LQQb9RBvt+Cqf4gEC_$A2{>gh5~0U!)d{i{i8Mfv_dwk~}Zjw9e2AO~3U3tfTOU#3Q!X{kEFcN_0;m zm^!d9Kb0O^ZSsc!DjXiKWAR&!a?wu4_GZadM43#vh9&Xvbx}z#OKalA`DihE4&2$2 zD|jpOZeMopW;p-Km=YG=9RBWN=U%)L`abvSKwFVWZop zUgQOApUr2X580mVH!YUbVD$`PtndNkq_LME?2sIVWfy9DurKg-MG;af`E{)X zjFuX{v_k1;8#wA^2Qlgwl(e*~bSs~xCEO%m=`pDsZBa2KRa65w? zlRui8Ew!;ZeB3TSIfopt{-C>J3-qan)Pnx?mAeo;O-WJ2X`4926uM| zPH=Z8xVwcwaM$4Ofe;81oRHkf=h^!^_ulodz4w34T8CatO*PGQPj&UH&-0d zjj56~<#WTNnB@^q*LG-jW8W@1z9}jm7-mlB>CfDsc<4j8&P(|&G!Iw5HT~iB|BT}M zJ69e1oRHiX?n8>W#1$;)1F*;WTohD1WM;eE8Itd4T0dLoi4P>1BYQ+qK09Pqd;Mi# zW3zR88o!Hq>1F`M!8{V2d&}NLmNTroX=yN;{(S>5+TNDazyh|elDW(*V@T3A*20E=k*%anYrdC`q{O$Y6 z*`VaQk_IXggb}sj06?^3ej7^Z(@LBQYl6y^v&u!AG%~}5xQo)ivrW-izASzRa8u4j zsWMA9|67`w<$w z@-DKoAiUL!5jOL?V-rOc_p#}egHodQY`+dBfUsDhYEG-L3$b1?mcR0Mz=LYlX)aXF zZl7%ELnD{L^$RLzqA3#J0ptZe_*_psXOAB5NzD{Zoa78|7yt(OK3!(jvBdJEKbAN# z2zac$&4H+05s39|3dWH^F8L^UoKi4M{I7gZheIM5M zrsIyP&gx-00GKfbrcUVToMCb9*c_7_uVB2Qbploq^*(F0;r1To+cmdk{2YTfp>I=4 zEy)NjV!b*%ojoEn?jci8=R2V91wz`!5Xi@*)qa{Dx`w9bzB0Ln?k_2LOA%CLRG+J% z88Ieeij+zW)W@)#vt)#~Gh_IWlDz<6sZ)JK$#&lvyQarH%zNNvGg- zDd@>ooznvTb{Bq6p+UU@pM}VtrTlUNq63fbzP?cbO}388pTbyoYPyuaIj;_ZG+b8f zVV@@2K()-hQQ5*c+l)OFoq&m3XHsit0dD@;OLUd`*R(n5gIk|?k+Mzr`dB}%k45HW zz#EUj_M=h?Jz8;XxH=&3$`t-7Ium5skcO>Ubu=hXw`So%3T683$jZ25d~XZrn#*y8 zF{@+J>4;o8cU}pd!eGhcn>*dQU(;~2h!uam5!^n##4d_``HnRt9sL9oTeXsIGT#if zLmJ;t}}|%`D(pN10L|`owHlCGOv-CsjbVkPstaTdn zq8ft?>_bD4H7lQ?aGsr!7&TdNZ4=KIFiIHBvXeQa9aLP_4e^CIK6|6hFcE{wX4C|; z}*Y+byW7nK(GhnD7_xD3yBg)>3SZyQ()Qe0U+yS>w*z_~OsR3dg(0GHaJn5k+ybBuh_K1lR!BUz}LL5q@C(S7DgnzJpv(9`%GFhG)` z<9cdZfW#i;?FQo<0+8cl)M7faRW!eNC(lhQ;*6rTIW+ia)ZAKyWK3hzQsnde{S?$# z64NRPH1ebNXtZ7o(aAQvr80?d?=MKKA2A8lD4C|zZuJ+4Hp8N*wFs19=+R2pegRbs zUxWs3QtP6O5b`bu>Cn}ZpZCy7DG?+lQ_;q8?8QA7*pGYu9P!Vm?`J6McK}LD*?hi9 zK*&tmN4Hk5TTV`9V*Qu+juBL2d?p`zcFynwdMzdN=>$>*%x8~(Pg8vdz{m7SY1p7Q z(PEv=G%tolXDd7R*OsCI;mi76GTCs+WhUvDb1c`m?4fa?>B1GqPYNf?4qV;!)vJ*U zR-as!%GM+IQ`4yf;rO1(Uewm@lE5K$|CZSQ0R_^}4waq_bOEO0Lus(?`M>BglNjQv z<_^?<$CLzbG=}JzztTz4b4<3XQsAxzxW6ZG$UH_ccc&Fvg=fwxa(q%aV8NgtDk}2@ zbhzq0>H9&TU6yjA9XY*E*IJVjdCFjp1CJKDF-B-o_~Q~@0R)J!sD$bfZXK^2x|$J* z8$+SZVhcSLMR*-6v;T1p)>k|tt0iDe`I*{M04L~6z<#Lhg22ogxy`&mvk&rJ_)Xa? zoLs94WUEVYh~37e;iMF%Kc7s)k>j&Q%8 zyUehIQ}R@&+4fq5#DM5yjV7mdt^3ytb|lf9LGAQ>qGeFsY3kMZBinZM2K4d(b1ZA2bjYUaz23&XtHNldLX+Uxr5^^01-}$#@<9|%9u{~;ylawSZl!=K z>THetjLVnB7w3IDvd5Qk9gP@FV|IjdG|tJxr`qRQ7(zKxMbJ7td`gjwukd0A@IDfc zxc4qA)-{g79d&mPZ;#vZeg}v$?(lQg%|@$a~Tmt z>+>G9i5I6CmaN{-r>9g!t2;5X?OhZwKg7R=r&qR8oIvdouyEcu%NZ}f*x|9COs0c( zXErgW>X3jDP@r=@eq1Q6NV;Wzi3^=+}Nl*$*%) z1v@wGW)UX#p?AC@Kw1k?5MhA{;Aqt234d;&|h!`RGD`f}5FY)B;s%^z< z#cTd$`_caX`|F;6=_~HExu5QzX+z1P72!3_xu3S5`)iuN_J41GJ@!ZM_v3zVfBuv| zdw(9gruLV#Azf3Jj4hmmW7yje%+WJvzr<)Wgy@WnEU6vj-dfIa1;nNe|E&;8J}!#u zUaSx=j~tp!4qb&*anR0<({swN`OEZ=E(? zp9?$kd!Rt6v|$326PRaG1>7>b^gpc>G}+X#aA^$7$G$3?xA3H7mV&+5B2(Wz?|Hrk?y7Gc*#!> zPnd{TV_Gq_`{%X&Q(}x;NTZUw4%og|n~b^Ze90g$E;_QvR5XP;ZGJK$l#^qxu*%1S zO3QC9vLH_(ZUxYa5*)Z5z_SjI#y0kO!u-`ECk*WJA}UqCws%&-f15#rmu} zK8(vm8q7;)!tbxeo1P<|Ac+iATW(fY(4|U@N8>YMCEM3b@cbj;g*^ce-=FQ$j-th2 zP>Z@wwKCGEb&#_KrVnab`-aBDreX)oIClbmX}9rz@}~>%GY!GqIphLlgF7Iba$$k+ z@|2`nW;y)M-XwNwaDhgS*z_PfP>zs-Xhg{e(`vKHC=SRDL&=96LE)UB$CpJF!bQe1 zCf3Ks?%ob8S{ln?q7yVhU24-38PX1uH+MKSgX7#~0;mk>Ft?BT!4--qAoP(=A(A)N z8NL701l8{|Ek^AWfk!7tb?6q$z9`{0B(}O>vc$mEEQqRQGpTV_sqj^hOJyhQAs|7r zC2TPXsDCe_+8D}It+N*@cokvfdJxUIdlFb>{F%&QQnnQaa*nw~p+t409Wz?lq+sqo8VE`T74%2A!be&!-UHiMr zndg4303$T7kJ z+M>C5HQ=5iFlZ1i+2m&k>EDiNN+TF=@O=vZcw%xy_uqx*n{)C`EG%xzN7+J(+4t!2 z9mB`G^NlWewRE%D9*oy0{eMtkzApd5**Om%f!_}c(*atV6{P+fn&hK)Bo5g&Nv#S2;h;#~9seaJ-82njsDh}>dkiPRUO{gbNVGIYJO=q?1Fi&*q8 zgAPvBz*6H<)pzSk!l$hekVgU?A_cEFs(wKRF=YS zW&6~_CQ=ULk9ir#!N~9#rKX-n($}RHTXxVqmGU_FgpR^rS_nC!iTbubwKqHUgz@#X z^|dMG#ku?Q{mPkmQn{Hjzcc>%o=phGuD;s-n+1*yn%~hH=q=M{u-%4_C#s!Jan_C# zVFwqRPX&5tZ3c5=VLmS$s%+ZMWGe>vzNhZlEM^;CZ}}-5`}N zMw=6qowf5-H-_z>eQAGJ@Yhiz(T6ZG{*6QrOG*6PcMrX!y}n=nAP@3A8hov(z4`W6 zp#+H?(N2wZu>QsgjEil&f~+=!Hfp%>*L)O4-G^`(%1Pi^Zn4;Wfvd;SDavtkJgF5{ z35v$Cm@0sIu^CCW^+??toKvxkRwr>aW!>o|M$7$X{s56A%{t7MqeZN6(_3CI1*Fvg zg5LMRph2{s7sTo_7-kkdFnmb;jY9x#f^Y~Z{@@TO{>mZ1IoMxM`)vaL>>ph34EmaY z9iH?Ra4~4}e;XezvC)SLN|B69#a_Uv8 zJVCSj4{dDQ1oLf>2l{4}Z2H0blX@Ln^cS8g9dEalGAR-Vai%P?8vQXCO-nPx=>lul z3oXdPKiZEhE;-?o_;T3(T0bG$AWU2TuBGc&YoAX~D?2!aYjPDYyaYQsI^JFV^gJC4 z;`PzMI`bE(Cx zgSdya@LhCZ+bpI|5Fhr(<+(g27E{LL+`A0Sy!)A%Mr=Fz(;nKvZS44Cgr;&R22rJg zvtHa;gJ@@Hfwbue^yjY}^G;j-k%@NQf*}uJ&oh?|sljhILZ!D6LPi)|itcs#mI21W z>O{%$^;E%Gi-x6Ns3TqjNga% z44h~TAv`Q&$}R|9ZKFz->m;BqRZ<6J`WI<~yKQ-r#F!vh1Dwa}zhf%@GJczKJsNSU zG`Y}$2Yp0C+1-(NM zcLR@2-~E%nGVKQTj_-N@b2w6&^~;x-zkRzivhp5iR{Tta@x~bCXPz2-G`i5qYmPN% zzTBF=RXaG=-u`=@FTP3;0u#l)dV zAx;wcyU@{bJg-(LMP7rETzGl%V8J338FHu-z)4K-zBMui{kL+3aKaxFCda;Z}3u+b9oC`hZpp#xz=$ z4=9A~k>w{VF__PQxp#xa&7m+Bba`8i_9ww_&*Sa58znzm&3qI@11?3F7@!L5216p3 ze>VY_`oJe5%8iOSI^;{m-B*34KJH|pv^87q<=U0p=Fh~Fr}$8?41G(Qv3fbPcjiRU zmS@9gvXpC;4+>TLjd4cnXxS(Ip`Ct%_fh=?-dBMczD+0&Vf_9&eqC{4+ZKH|qe9yd z*mLHPiG_7>6h~(yo^>@n3NHQZYt2TTrV7}mO`uBVF1NRzi^W1$ud<5VR`<|4S{e||8|-rilLSw zFZ;AQxiQW5?LAk0eeefR4E>m9T-hU-gG{!-&Q`3(Ahb|Kq&B(O^g$F}GvUJ-v3Z$1 z3uvK%*ciNcOMKux$3(_9xe{@iQ#FvV;4G36RoH|jt+s}1as8Vl{yC!iV7>584n?v2 zE3fK+kZ$Y{YNr>zNH&W`+g2g8VW~L}3d57iU9al}K3>M4W}rCGxP5(s(qClEDXI|aea&3~e<;V!O1GCA&ojq7!yMDIx(!c@5N7J^f5_C1$(QmEW#eWr7x6w3U* zv$g%q>sMGv@{qk=n$ZUcF=TSFqE2-DYG?`q^8Ho0yufpRv_Yus%|Ri6w+i!2?8Q!m z#~ccIisD(Qfr^-iX3d#R&E#}ot8t(UW8G~onyv;_Lh1k0unVs&=>WVstifoD@hYux^%KU_ZbJ?l7z@ z(K$t>_gE`_1Rg*Q_HE_w&kPX+>;nIofY6v3+tAw|jVa`hQS<5GhM1gtNytmRW&)(Q zo?mATCAL1hjX0n8VAikE<+R}rF3}~`$;VLV{>;cSWm&WNlU#w61^V?VztL#0PNnqT zFd46G*S+cQZ41+m%5o?Ze#ns;*#Th_7rnR5?oXh6@w5__`h)0RZ6q30auPeW*gwv> zjw!>h|Hf?mTqNr={~Jh1_wSV6@89JgOnj2w8X)dz-jM5j98s#VEvm79kR*v5)bm~t zm(2o2De56f3{n;c7TSlY?8+)!>e(n+660T|vEO(xL3A$1wqj`3yP;f~W5&~A4S8kE z!bZ@%`820;BmE>6njpL&Jaqp$6D^%AE7B;(&$HaGX_fTeoXqPva(YSs#6h31a^pN+ zq#EwFvd7+~skNQClB`t8B6~}r=7jU=Ab=lc&W5W`*B@Q?Ia2ogS(@wN@J$S&Zdh}= znvkiDNZZwKDZXFi@{N`sQ^X3-1j>!;6qyBIt_>2>lsD8E;B}eh6cqHa$qXwrk);zzZQ3VM4Kt|`%T{M!LS)dj`<^Ib5CoJj z;D}Lv6rF1hnJ8WUO{sl`jEI$FLpy%<0SeFYYLe{x&?NM?-NIc{LVNQQ0t@8El%Na+ zXq+ozdA~y*&5hXi^dv>e`~MW@`PCcvIcE90;{4ec`r%^!kSc!X7Rj0H&6)#ZCpsEs zxx;HgsVr+c)_4l+kxtyiDFz2 z>4114ukQr~bw_ktPCI)$zbwKA@r>#2dD5+NK>_+(Z3!o3UX~lne%u(I!qO&>dmAZ} zK1=`nPus+Q^7yZQro-KO+@JNo@>C%8!36704jq0e9L9}D!P!;a`1F6b|MUnzH5qE% zKKT(U{Ie+Vqt6d#a?bzLFGYV|M>_^FQ$@!!5rKDsgGw~frG!?KpC>+Nc9Z!=2OA+Q znrZ^9WGP)jUvcn7a*AhjxI@q%XaSiRK`x!Bq>L$UYBU{FL{KCILW(+gf8xu5G-dL7nzgQAVwwctufzJR?;^(;Gp+~m{Yb_ zelcAyD?MjpOLZ?!AaDcC2;Bn(w-A-goMcOyYv}B`;ye73*9C>I-`Xs@PInCf(IrKG>LQe+PfeEl#*_$~9i4Mfz9Va?hkZjvtl z42~C1d&wp(JX$ja{OpqnQ^iV)4YAtrKz7~J%>4!yOW8qQ<4L}pU z`B6D)0Lth=%Cj*}dPN&C>idkfOyac5-ArDRlmbhqrh%lr(FvZFgMtwZw_fY73kn7ttIm@}GDw&nL}tTWdph7i3kzKbB>BQ-D*Spog+AlH zPE=D?S#$mpQYcQ`{3>7h*VBT}zalT2DGr3A##1xN=6`ZnV)|e?UQh$V%YBg(Gt++3 zKl{{UvxuI6cm#@w0e!P*Axb;hkV0l2VQ$gjISeGqM?{|zAKo*)3bnMXDMqJN#;1eX z_o}4_m7<0A1>twVc?vtm6-t6CVktJ)E`Bty5ECwLvOh!yA*4sKjCK+VK#@UB52=l_ zO@Ru=HKIf`$IUlVuR;IphC3E-@55e5+o4gd`U=|NVV#!jSQS4qIc5uG8YR8}>1 zs_%Kf4TsCY2@XtZpkfoj6H_yCbPg(jr{)q@H*raRDzWq1(f~-$0pH%8f4lw;P@V`V zOy!AZIv}488T#*EN|OCwS4({dOu$wjp_VU^ zz>zzgLncg^Z^Vr9&Yk$2s`2it;Z3U|Gq%P?NxkJo*Zvt_oP$JUWcuqT6q6bpFb<7% z1{nt}MEFWL4=0Z6LG`z)1h?+QF8t4eTjow-OYEy_q0Ib=S>7W=Vi;GwM4?@Te!Oi_ zEF#k?kK{l-6hTYB#!$FXO`+LNnlQ3`vFD$|C>fkVCId|jlwNC^jNg@`UIOoj_0+%N z%X7HLlrg!cQm8rzz>Cd9#9AVc8C}-wN*<_dhY`%#opDA35Lu>7RRwrc%9yt`3@_kU zVL-fPlKAP=b7hQEoZcj+)+Omiw#ztpp^ePte$|c>00T;*!V3lgX)3mO&JTiNeb!+b zh$fQqt-dE&v-Um8iWwx-#mPDx9)=DLm%x+oBPSad|D}-&asC9w00kY)8Alm0|{sfOFv(fhq+{>AtzFJjMk01OWP8ebmr z4av@EhXV}Jlj2|(w|4D!yLQ=!hIf&R;+g!I%hTuZ^-HPuid3#?SS+@E z6I>OIc)(ubfc2Id+SP9%;DWgF2xGm^@T-X%M-;nWMJpvYnzU<{jc`wEdKYQd=Zf{o zMfw6q1=yc!rtZ8qh1_MS`U@JNkp*Fz#Ys=B-jYw$o!y)9j_(5NaJtfK%N7P{bGXAD z$J<9O3*q1A2bZ@-AAn-qZ}M0L+m4Dl-VOn64jo{hIppYl&SgNN>`@X zjwa>*=q>Fdl7;-{MoeTYJ4o7su0UKia5bX}9!ToJ(VHK)FKs7L|4o&bL26%mt(-W8 z%DKp1J!Uka{r_-RxSGe~i7Kl=g4bNsWRZaz<(G1T9O<`5k_Z30^MAmA&{QP2ssF!R zr^|nRH@^nneh2vYSAGY?-LwYSV@O0&8@q|EN?*CwNvJrF+rqK&5-j=91@Ps*(tuq# zwws{@;_AoqOh41-l+vV>2w|f@+`vFKI_y0fwMQ&f*IhZbr3+PNkvT5aiW!)nTf;hh zex$VfcruN8yF97KgU9o>dcA+H^Q9o+2U|6F^Jl@+aKiG8?9Zik3|&fZCGL8=Qu>S> zXI^-D9}D|ycMkZm!eYT5QFwHxGNb!NV?VIid^{%j4w!~`GoPSB`}-1)zbKkebZT;6 zD#B6lSxkP6q7JkN+8mA(y7t1NvKJv0G9aRJXwuMub3$$xJdp8WZ)23A`iz_ywucOv zUXZuR0Mu-7R77rPqbYqQUTddSsM?D7EVYc@I~x|Vok_?SP)d8n zijDz*e^OhRaF*lBWWgCqao=M$Vm+vEsfbBU7cUMA%5Bi1##NPCxp%?8tzwd6BGl>` zuu{pg7oWY17M)%`aYyu{MnQH<8NYf(qn%v4>u9| zpa9;watlglpc@BWqrW0xPF*}{J{e7CgRLIIlzx6Ak0fq>No=@5UsL*s5-%09oE_vG z%Y%!973x@(grJV*FOF%AURl+pM5=RrMHX~eZo{i7n@jl^R{vQ=k9N&HUg?P>CYe&Z z;d8sVigf8l%*Zu^-rbDy61D(2vVvges|D(Uc+Z;YI{5lH`>K%!td6RYm7OeJH7Xjr z#d@l{Ns2x(P9;GI36(>FFvOV& z75k}sn$Jaf>ChH92>$525g6dkJXkOGq+qL$Go7-{D;uUhE|dP;+`U)i!)+;}JN>ec z`cn7%N9qJVU-ekz6qmNld$lLQZ2b%JOa&U99`If1_fj5%+lZd#$%Jpswyhh4@f69F z*F%wX?xAfhbnscw9him} zCDYGm85}~f4+lYeljLjwSAkJeQ6X1PXWMNKW$M%(pN5)6`d1Y)z7BZMu%^R-S`_tK z9*OUAg8cd5-s&uVnKq9ofzDZBl}7*SJ0NDsxRe?9<7H$ebA;v-3lE7fD#jrc*^U|g zdCFavGkNLZglB-;$!$_XoRS3^};$0(D5m{iueg2LpY-0RGhCN44azJ7X zjs0s-OpnUrZmS=-L_HETZz7UPJ3!@~^mgu)Pf|*CTa;kpLtt;11@v&23u(Yh^Eo+F zU3|f%>%8&zqA-+<8MdaAX?DBj>&a*E?5|9vWxt6wk`B#)(zUg4fXR~dszX(}CmV3r zFg=#~lWrnTu{--Oid<^TD2p~Y_Qui+Mp-yNFYzPyZI80?H|k2wa%tG1lnL(CgDWY79B`AhsIiLeqB5(SE-ZWAJ*eSFvOrK=0|q^XX&! z$zuwQJr_u3ps)Wl9J?%4gh}9Seu6)rO3&nIMo5|RNai)zu0NCIh@FZNY)ko09e&zl z);OIZ5fuum>jV56L}GRaa>j@5n|5g)kCr50W4%Jdf5C*D2O`CxmNtU_ykPiNTza%N zLaZnXzM&Ib$W&b~nxZ)CL+lAL_6?9w$_$#CCprx07=VhFn28D4NGf1Ft(f$|;^T`aNy-n+De}O5S=!X7HbIC1h3T#g7 z`e4VxS1%xnVJ0#P2{#RA2a!g?u$vJfI1yGYd&k@iyk-y8&dtOcH#A(egR&>7Vy5Cv z%~YnG(ABRKYc36rq+*3-1idxwl)FkW&QzHzTvw=CexlY< zjXFNqmcbX=_-uA4T$F>~`QzY_8@Mat-TN|Z8|n^#ST6IA-i#BM^7_*is zdfJqX;Gk+}T=nk&i5h~3AkQM!yQKxzL-z>a$|-KqdU`9-sA=6qn%g68kTi{IY-wPX zI|{a=aTqa9RA8oMFbZi!k4~}IIp;fVVM;mkgpLltAsZ%Gy{Rs34RlUo*$s$DoDoy= zQtLk}$1U;Q;hSY1UISex821)q7wCU(@s}Llc+NmYEw2|#vq1W-PJkWy0uvdJU#$G8 z2d2ZvP8LG8HkR<7@T-k_^6cRrRPjoDqg7nH<6R{v>=rOK^eac=FQi!vmbMpyCo!kZ z5ywty_^A6ep=lOq5MO+q1vc#XtZM(4S zw^$nUA$ddF6k(33u+Fg)yO$af>7=e%dbm!)1i@G^0O6IH5@n_N8PyY zEpC1LJg6+0J+`}Bp@H4_@n}G-M?OsjieR!Q?xw<0<3pP8(ii|bwB@|FG<3S79R*2k zm)-6z?#U%vBK)>Q77EH`qR1Mb3VH0zv;)L5pZWHDWAg~&7(AYQtA)H@M`3DM*#`qw z>e44r&NXp>1uq$TO}JPGK=S6Hx0OiI)9>(%*)s`ts%SXHzG{RFS{g?F@1g%Y0{=S# z|3gP$GLhzeA_}pmRYm}ly)CX2^=I6C?qKL5HX{#Asq^WxjXg6j=$yrMwe4K8Asi?l zUd!)TdyF1J>fTV|5Y*=>|w_WY)#|#Sa}i{rXs8qIJ4JEEE}+PWiua zBg<*9c+wiC#;#$teDfv+E$gvEdW*gm8czBe1P8r97W>MLH9`^&6zE(1*0$|ZPyM!? zbUW`fOx!4Kr9GXr)VQ{~Ul)~@qpC%0qf!+tvBab^Ksl&&$X=Di{wnlgoyi#=Rjc8Oic*$oi`X15Jc&lH z-6cymREa4tiyysz?fg5S5wtw74yU0l4N#Av4qG(mrRklI8=$0Xs2J~tnk=#+ZEdWl z{^mdZLt8*jaOuD$i6x%M>JfEri*L;!+?u&qb#TEUynk}!5-96T!ht)cr3 zQx>NiS4T44FK_rTSR4<(bPf$0LgEfDa890)_zjgFroJpNeP)^WQi8RE*$?SPt%zT^ z`GAr1jsXW_!31M|sqBanAD$*zwX~BHym9fq}FAl@o zXRfcY1H_p9+Ls|75b?bP0WeG|Xj$>(0!H*KhNVC7_|+8oY?9N`GN@c26(_6~mVYUqyPY%rEa3_k;|3BLiOe@55ZVZ^6NM~dGy3^X!p5<(JSNnS6gmoQzt)dG}T?X3W*OWsHt=stW#?+og}X0F+SV{F-gcO)SP3oDuZ(0_pZW;cz-f{ zI`ENF1BQJ{fTercp}E!2j76IYT*Zg=?rzvXOAt_L5fj-QGcfdvN74VF^x}!rN611z zuqv>bOY&cC^Lp&ZGkCPVId~8b2V~U@+{HEAacXjYsxc<$*N^oRA?o3(f7<`$(N-)1 zuZPaQ8?ztfm^-9TF#~EcosblXzyeu+krQ!Yz*L+8qqns|qUt6c=zdUGrE0Z4#C?sA0I@EWr$RJos*LO;}>67j<0iR0mx zfNaSd%C#%RXL+?6%#jS0e5y_?ESffi$ZsSnO8=aN%)a2=E z=^uFONC358r9di}J4Do;Fzu*-4vTdVZ5ohwX8N^+bOXB=Px}`ACNMA!`PkgLei!loLx^@YDTfgY)FIo3j;k~Rc5>?LY$OFs23TNAuSHo zex=UbYS;4AsL@Reus3Sw6UNhOjGLpR0VNU<74_B8$30FvANTF#0xHp>kW}B-eM(eS zQ!h1>p^xxIzXZ3K)16xXI_bM%qSM(tJ{j7ZF}NG7Rvz}u2}a0XT{r*&95M@!@t!ML4sa#-tqN??R^)rBvUa@47#2T(@{3k{zxwfmYnXb9_CCwv3v`yjy%Rj@<1@Vh zVEmP(Yzj3iF^<;J2eBA?SkMCANgC_sb3gbaa(3L+Z^VqOk7b24w(QI##>au-M!KxT z>_x~d3dy=wyuymRpGip&q=;)Ho(M!Aw+J^=cEv*bl8d6a)i%;DpHyK6SxITxe%^R* zsZT0fw}WQfRi(kfI-fmY?2~2I9C5;pvwb_5*diq)Yp{4Z*Vs#&QeqKv+!N7WpD)2y zrZH~swEzdqSRbLz>L54H=pn9N7xtqz^MG^KdAUWZ7XkSv zYHG@jYn3Hdtxvg6L(WE#s8*GpYqY)mK@#XcDz==sNZBYN9k;3v-SYjI z2V@GmrgC8?Q-LVEUUE6oW00O5eiaX5C_ujs0?JOrVbx9LLgq9Zo8F_WcSOK3@(U{B zH|c!GDXLBBd{ks;)qGl{sn-t&B^dxI==n@61eP)cm@macmPj99u3ViAhYx_N^A1Jj z6KhvEtHi(o;9p~(}>C?bPAzzqXZbv8HC}#$aQss zX&z_iOdcg9)`K+{*7z|er8MmNqhM6(Jz6wSnrq+86=!F`!B}xcJEJs*ZBNIH-TVJx zzU}Ud;i>B-wIL65pFF*Qi^txm=pP_@H7wVrk`<~%W;L(_d=>kQ#FtEXd#2l;BB<;{ z7zHpXtukcxl?Sen$EkbeTdxoSRCat}oWqr8x~fQfWMhEcV95(@h4@N6o-d6FRqCws zC|z0zbeFSL33YE8vVym~Ad^xN`pl%*i7{I{RWDlu2@y#pMbao|D-RV+;OchyVbxK3 zTg8u#oyj6hhz#E`LAxlv+>IJVz3r{5mefc@eViXDk0WNFbzFkgao@H>ygCn%UQ(N&&Ry5&PaPebdnq$cTz`Jxl(rPt6&mAK>WT_poj)5AUEaH=9VE^_!YUn1* z7>Zt>ZBbO_y_Bm|Z)(SSOuzZP!U<@s$O0{=t4Yg|ciEa3F5=1TU#142O>;A0G}maO z6?)6Y0niq`HfoIg9%|WWb%-b#Oqql}$brMWFAi?mK-%sn8o-UDmK#lAq~HNIUe`*| zNk7ejK!sU!2}VA3x()K1j~X8ZNQy1A+1=np1J7}&jlG*k13GIzHJiB9Km`&=zRfQK z6X%3hi}i!&^#%!q4T@!&uW>*T)RGoBZ&m10q^X!^UI`*w9=LYP-u0S|i?e<}zyfu3 z7WIyiKgGp$C1`84sHvxP?eCtH65-%TSwB4Q@UQ(2fO-r|#4_ivfj+0&|~r!;2uTq{6yKPB`Y13?6(DM)*b2pziN?%Yqv7&Z*9LUXyhhsy-%E zI()S@m+*?D+=HjpFAyvK07Bg5+aEn_cy9-vwc=*Fw3Ld?QHF(T^B+w87eC)79>YDcFznd z4ie&8hE;@D1rUsTMCQ)W@CBobh?hH}O+GA8zKC#Q#G&kkHNr=hGHGh9sE?yo9(Tw_ z0bnT{DZch~J)TW02!A7}dr=2nZ@hpC-X1jt2&AF7d_3>q(b7>V9htiluSCjY{^HOH z4lYVhLJ^OLO5;~{=(^k$O7AM~)HlZ8W3)CdU@LtH@5o!BqcvdTQ|+j+pG5x-5LpI& zOa+yOlgz-z!y7TJGT%iy9|PdRH^=Tn!i(o6A@TjNZ;{BvHOG)@Aji`l?$VL4DQadb z-C;|?IkuT!pcZWC_LUaZd!)Eiv(EqU#E#wRk=#Kq^)h zOTYPGqYp(_@&JcXRONg^c!s?CDnc}<5gVq(pxNkDhP$_r&WRldq|J5axBR_}ZmjBa0Eqz=oy_^9O~t zksmKHOCPBxlA1X_c&yjDc!y}4umfbM=r=HkR|?%RfZ+!znZq7|E_L4JzMjdiiE{#0 zzV$qMQIKd?uP}I6u;|yozjx7Xbeyz`-Ztl`OAtkuO{vEP4OqO@59zv4yr&rjwDbNp zcVWK?NP#h2u4Q1O>^BO#soyU9lf(C%JkOyr$H-;{EyFmC`4|*77{}}o2-DY-^;+}1 z(aUEUP<^;4=fOppfWA9c=Cwk3liu=ve8>k|hBl)NULvbj#ijnU!V%B-H4AJs-bn{X zu!(ekJ=~lioHH+WYkk>QnI~1oF3q(*bMg+fAc88}f>WQ+&cIG{aZW}#a~>bvM)zp| zSF0zefee1YFJ$f|)hG?*_#Rj?BXzqhVc+&u8V5n<6Yr@0nEL?&L86;?0ueRTpVZxA zQVed|beD*p_KfT09D@>-IIKy-xT0@|jtic8;Yc4hg4(@+>8l>m3P;zT;ABmg}_ z+XZD=lSzEfp*n>Jju)ZDrnCm%(r7d^a406#P$41Uo4$A2tN#<>3aaZzb9 z$Mt_=lL7G>9i~d-+8{v-SPW39{&NAh*)RVyn~y&xa2}f~LVo}U>p|;+o%dkVX{dTe zK`wfQQU3Z|by#mlc%^K*r!sjR|9Uw1ML8qUY_f)JQqSCY)~Q=v+m>Zn4u{!9hYb{{ zFgnb0kD72EnS;eH^_ZOWiiO6%>J5_7eei3k>Xq8G#t7~hk-Jez4E6SxH+0mC?_W}F z-=Fg;XG{l!^X;3N6gXQbNE^^Ty0vZlXwtv#mvLgjx8z{iy-|eA%BYeF^CGF-E(vBJ zy*|oz5N@za!eC*=3P)p*U>)2Ox!WaLYr4SUhB7^WXgSr9&4-@B~Kb&AbWvoOGrQFuW!5` z$KG%t#$t_vvjWmJD#OsX*=y^hmd!WkKagA^X1;FubX+_}ZL9GL8ltoKA;@YYJlpen z&HA>s!>|6G5Rj+U;qGT)ylfwiEqcXY^r z6-zGrYmeavl~X5^TU$7xJZTf4gm|r6G=sqdN3jv5hHst;PI{KWnqLAa;@m!5m0ehu zGUp+nT}a^*K|~T-7;iB8zB4Ua?}zm?np}fY9-B4u2d`VF*Xx$yefDDpm+zlc3e^;Z zuP$KgeEkHB%tU204?c&|%kzDG0^QH1ihw?eut& zg%r5|T4#eDpRy!VJJ<7rS9bKc33=HP6CD00@7$90)XuMQA6T2;K7lx8La6f8{JU?L zrQc=KLo4`X+4F8s_nwdBbfRQFYf4YlLZ@a(3LtcgW${()19_xmkPZ~n8|lTl89Otn zAyvIk8@Hlrs;gu>3T-)Q>94}Kuh=5VP^fhrTsWpoDnH7n!(w;xkHxT``NRdCma)P9M=>4{AkEP4 zK-HZ>8SP+U^tu?d6L`TD+s(4&@RHbC-S<<5a~&^rN4cg%atyVHV81S^!eSv`{l!YZ0Z3^mRxk;cI zHF=`?b&%)<@`t1N&bB3kx=28mbHm1_gX1XcH3O`}PJ`!XbpU2NWWk)E&)DVuB<#nM z%^QO;^PxRwd=IZ)Ky4X2af${(ve8O}dI`3u9(6R`LC(QaTPLKPgN?k)Vq5HAE7WCN z-XN|An2$Wb`^vP-%g{ft(0rURFwuHkuFAbLj$rl)2L&iOjex**sebOZXIL@G z>0Bi*rRGEKl)n0MG=-yn6zv+PHyu_)ju*fneReUPAHLo#T#-WQ>O?r%ZShoed9pN; zpL!|sGn9h{82o}cnUN1R7q_6u95HGjqFFZ8e6*Bp#nbzms9z#HCM6>qfB^#vPo!zi z$%cELBb!jGNsY}3D8jM33Xgp1-vFtPT#rbbAjgn;*#xOZ z7t#BdQ0_kxHmMdVyje6Vc~Cqhs%N(lb=TWD;!99tiX<$r-?IC<#_^_NlRa_p+Qdfp zY>wa5V|0tV`(a{ek<>T{P+`)P)=s^IV}xJ5P`WbgLUCDXPxXtMPt!0{Ml`X`d?r>B zr(9k*$6$Gd{NKFOQXPO0`|Udb0phx&?eIL=o`p}M(+}!h$B0M3aD*EnuJMtS#xqf^ zz{5o$UL(!&$h1k(5IqEdToDl^5jjPoZJ$xA0mch->5!QKFh*C%gpQ%kS=->JVgCs@QC%sJopdERGUm%Ush1O~-Y`GD^f|1sWSQ*7FD z2q62*bEoV97PD)gd7d;(h_u&a zh{WmpP!tZEyiQWi?W?Q#cag_BL@j=r@ybq^1NDMo3nB5KGbaUx2c#*9}CS3sU z7WbhxtGFn3a{K$;GVz#!ws0`<>uv|+>WmP}rlF>)4jR%aq9|Fya1B?B<&$da4SoC% zXAocX^EW3PUjZ3{@~efv06x_iH~|m)_{Rid2f4#KeC7pnWSCf)ofttbKhKT)*w`fO=TIh7je6Y0>f6o@{V0y+&MHgdN_YMlA#m*$xI7tLpCJs< zbX%&SB!qH7KUoOJz6g+7*Nge_nRAZ`@Z8He*~oV@6=z5kqRgGentJ!cCMVKX>Ot?A z+~(WhXDASn7)I8*rQ4jGD=8OSfaVq_S(pEAHmrMLPr{dhh|ozM=HPinGh^5qLo`H( zhwJN@5$wS+qpV8AnQo;Fk@WT$Eh&RiExRKd=GQQ5?)Ay^ljg`15~S)#Qqw(X zINZ|*z`*jtMb_sKd6ln%tk(u{;IUmjhG4xgGb^D%?^$DHJkH*-7?rd%KK`BKJE>LH z(L)={mH}1d%hO_Yr(dQR-Y{?2Nv=3G#>x|)PrIWNgPn2kJD1ZO7X^R3TUd39IV8Qf zw|UpE;VDC;f-{TX_fRV}zxhB;f*`XQwL!OW{Ux9i*xgqhs_+6RPmfcAcb3pKe~rYQtt8xEFsD+dT2s$o1)xwSDq6tZVNAcrPQ` zw?Ev|o=W-x+8t{N4D6Z*NVt?^ep?_+l+g;T>b zDIP%`$jVUKz~*w=cRG9yXIilUKcyeto{cf?aL2ERRJY8iUFF~l)9N|uaY~VKi=>ty z3-q`|LSPvf#g&%UBEnum$LS?xE-B!CX-*QD>Ul4=s$5{-t>~cxY%)MdrS}xO^+kx2 zbfdkuK!-UfcA6;4Al@bu zfo;ynT$!cl1}Way1~~fGeM;1{Z;ZQ=v{#8&5)z@58=DG`;q#foB6&eb>@hc8`&jES;RZo|Tby%#rCA9tU?f(K-(8M-BGAvee z>+wmz8tRT!!ww-BAe>E!UYW|ptVOm#{vR;aKF z5zS9kx^4jg-0SPi@V?eeOwBLvR?ebNF2aov_Fq&xW5f);-T5c?p!;F6PZ@jL2g1O# zCCIow7mq{xGi!eE#|cl0(blKkNpzJ^su3<#(59O!(QC;4t0Aold@|dmv*mQ+S|WxK zn!D6H`1V4CX?1ma5l%O6HK0Ya!PCC|NBTNsy0u$o%m{5kLe#m>q;vf(W2*rgGZBQ3U z<_eQ}{S3f1h|;znExkzl=*Mz*cMc>eqjiIt8ypYq>M%5K_HM>^kDv1N`$y)Fw zr?ywQfNSlws}rkGY!&z9JeJ02C=UDw$xm1+kXh?AQ2xmpd#}wK zYQkQr30TV}v6~ry4S^JuB!n8 zk$IKCRL-1vvevxs?z_~IC6NLJTFFy69@uMM3h~W0;<+5B1%1aL>R2hQt6rZ&MFC1p zBNNp${hnocx@9oHr}4~onb`4G4)>!k#X{?6JFfekEv9RscdYV-4r#&WoS0H9`UWOj z>CBP|c7D2@wxT@_P(n=1uBESF-UuT8Y@quMyI?2tgNisBGNngf@$B7jrq`Uir6W!J z$P9RFP|KOSZ*24iaDByu&FG11Mg^LFbW8YBS&0V?HQe00nta8Ydv~Xq#ky(bwX5m< za_f#4h3NEAOU35D0Oi_u&5ZH{E|ta})p|+W#7RndAqU}sVlU#DA{~AKaRxcJ@F!fC zdRQiOzTSr2c zWb`BPK{mg1I^WuA@76HtsH-9fm@H)$4+pgHsyks8u<><%5J*&T-_b{?IBQ9GPcn=@ zOoNxB^ZvNNf(}m88~(>qx+&L_ zrs~O!vT+@|x459x-!$}7g@iRBS#W_!@nQ-s;i2J31|n!14E+jIZl%o!2i*Mp$PaJm|(^cuNC+=e2eH zQt`;(mLhGvW#8JJ#2~Ay!@nQ;Mx;Z>*Uw4IAOb(Diy^PmX?+#krb+OnsWA6<4UAxQ zoy+d{-mzz2tYhH21ip9AhCLs2Z(0T-1YET1^2yX|-e&&i zv{+7iT=hKX@V|8A{}=oHSFiV<_4{eh-$$9l*gwM0gjO>~cSP6zONTVN-_PTB|Nr>; zcm413Kl}c7yZ=i0=WqXg{%cXHU|%j63maoDPVNp>KCe&}Wh?QE*j3zDKpAd`@QM|Y zDY1)zEQqWxN(Ps4M93I8<^xVT9-l(%aWaAc|lLcX~s2l0#B89RHK4ra|jG}e= zsZ))NjEbmvH&^2Do_6SZc1>S-2Ahm4h!9AhNe=6wr8XJ-OYJ%0K zy;jU=b0f8+zpuIMgqRLj5`XiY(Gh;W=}!NEh>50_c&>|X;)o-8W)!fsWMc$bX=_&J zsLSHMk3`+HkK4HhDxg`y%Dam|ttu zdzE&dcMnS3q~Zm3Z|eN+`l2lz{&p(a!_EI`-Av5DMGiFkDZUf;#YXelyIc1PmiQCM z{pE&EUH~U^*#`asL_Ba1B~i-M>pNI`{D)`v?+51{&)-7{n9PG%30vLO?|87u#l7vC zuQhlkEyb0*CAcY1aVnNb9OOyHct+Ex!VpW1`C`FmyEB&MN~=sYc7B^iitW3H-eJRP zkutlQV(H-C{;o@X4&M{`AZd7U_dekP5^};GG0IAyf2Vnft*ClFxp*5z<%&hrrnB##MCL)3# zRrg%&LIaO(J0GuKCW(82i;t<5lI|GCSkFGw!zE2Hm{l+{5qQ-qMqaeCtUK4JUF z2c8RkV0k@{oZ7ib!Uq`50iWCKsYz`qu$H%lE%r6(hvCr;U$A=r-Tp(&mFTk8%(^NR z?wR}g0kcC56hu9_QwjrZ3Q4`?^n~KGL@wgXrG)?V9Tn`4n{&EmzoPx2Ke<<5MGj-1 zLD=}7Hsss(Aw4B=MdWiJ^ji0#sslRaoWC;6X9*IHLtSp`qPKtNO!Fp}AV)0q@AmU- z_%`c7EAtrmI9Fv6N2xettn=qPa{jNCjWy41rqA->2-XoQ7N|ir1>Dr}4#t%W`2CLD+lT-(EK%m!3 zhe>@-WxB{gN7&%pF$Rk4PFAIu}rs6=}LorQ^y6B_s zdG}xdj-deY(BB>kRw6<4KyQmZ3#XKBcF2(&oL|VEI0uqqr~9sf{1&iJczNF{T5p{6 zR=+mg<1{`pS%S=pQ6=iQ3~02#Alsym6=LKm*TGC;Ze`!*3c2{R*FpC10Pw7oj$R_+ zB|Z*KEl0)Zp#DdKM9nRt?Zl|^KJV`uE|q?1-p;H+`(b!vPq``hX=I=7eGq_8>PGuy zHR`XpZnc&6FDerrQ^$$x#HIGXs#~Y<^*AwQGCiDDiovUi@OZJcwq@lT9Ji0q9fq1^ z2}BjO!0+zg9z^+cd&O6fC7bdLgGIslF6^F!gL>Gh8VvFAJF2Sv1X2ey`w~rTqHzU* z%wLi$?tHCY`wO5U4rRD}vkdm}f2$|1mME@|sW+33fdTNK)7vB4Q zlWRYheBFHONI-nX;ifp%U2uLot0DuxM62KP&fV?kR;CI-d_h-lBDq`=)lZ9qoG9MLbGPC+EAJsvP081E}y9LGlD z;Y$uP4t0)mOB)vMpSPYqZsWp~ei-kT&dPFewtQ3az*65$KuBmC%a`l)#_bbXT@j94 zaPzk-m&jFDXQ1cJ1io|XsijUmX3|z*sn8p>`X|G0ROHm?0>;M={BRGRVEtnq`ENtj zA6W%k>w^&%7?!;U_QLiC7{22dWJ7#dUW_V)gsB_PW#TLxq*#19_lT*|Uw(ALj=wUY zc3l(FeDR$hNP1UqY@3EV^bitQ$Tm<%g>9X`HW%F24^<{)Rf4gHy($;g$7nT1S+;U2 zkwf&h=x|bx82E_^CzrPIv6v@|71_QcsRHu_>)~uw94KVt$m*joq3Ag(W*O+YkpI&s zv2T3d4#RMsr9&bJq`%RpJc!2d@VOY=cW?U;W@coMXa6@qrC!I`XE`v?(<0xkfJ@Jv z8z9HRVXBqd%2tPSSiDjZjdzB zvMVXoKmd*63=s^rz%nWynzb|pb0|rBzhD2;0MX38s)u--8N_+_8IG4kPd%t9Kxsa* zd_rU0S5kR>{YQdzr7SE~VF(Ff!q5yS9E)i_4r3Y?Rz>8-E`o!*<~vPdU{_59zsk7^ z%h7FXUMyuAUh^gIrN@$lqJ*XtrQWNz&8Q&ItzdQB)xHO6f#jGl2QS4fqgN0M-Vk?o z_#QC@nknEi1K=u3>;@RoDP{F}H7cgXF3aE-E3ts>3fQuBcA!!ThX@7|6p%$Ug361U z!mmo;rKvRuI%`6bwi&|t-FYAkz@%sa2~Nns8KIGGWp6l;K$M6mi$Li-y9P^vX&})d zzArRif05S5nauyjx$lgb)H}4SsNACm-Dbbs+YdTEb(x%#V=xHQXqt+#fpGs-haVD@=Gc2g%b3#67D ziQ(gK6hIrHoT1Kg_vXlxZSiPuT)1uB#9_sFqqiET>v;AksjGWSo#S?h-~!N66?Tp( zqLmQTC|MhmE}1*CnS4Z$60mNEyyGCfx0w-mBBE zT5_3k-9a8S8Z<8Pw29Q)&dQS(O|K11WP6Y0N4w8`v#o5;=8;!%>rOR4Yuq(TTVu;- zd=X+*pvgqrM=|Po7BF4x%_VZoSIm0bfB807KDX&^ahL!;AMr$VPgD75H|DIdaUxefN46|M_wRtNQPg)C4Z)XT>$J~E>j_ASW zvRsPE-*+^5ZkQCu9t&E$%~sixCSI*3xOq9a6jL_+JwtS~98u{DCSJ`;rKE^{q9~Un zA{+;U7`$@-3&7c0X7ora%%+9a^-Hx5gc3zRj{BoxReC3wq-G_#jQL{&9kukWbYE)U zp@4WERxaI_V=dQ*!tK;7fXjqH?-c zKP=_ml@6u5W7Pg>0jya7lP+TzsC?+^Gl?nTq4$B`1fik6i8Lc?3Gi-Q`@LWEI8>}y-qCZ zQ-k5S^!an&aQg6Q@Qj~m3-bGep?N;mIVri9t5UD5>91Ug+~(0FAIr9#QKzZU^G*jX z$}P@@?!=l~eLnuHo5&tx23sEwdDyVJhZAAKAbP98ink4z`AN}jB-%&r0_mE zXAd1vzV$X`uQ{cDE~_iA=NSrmHJE>Ehxb|I;L>W5NbTqp-yt3OOQ{r@orUhaK3$k7 zj#jZBga0iQikc$#$Ow?@hE-T4`~^YRPh^i{C$^xkSSC4fQ1@LJ;uz2A7<+r#ot)?B zWq{d&h=_ng$47RTA7S)KEFxj|`VN?beZId#>v)%C78vVyviK1(DbuFv_X2G6Y4*4y>!sx2=TE3do?kwVNp?OiUwOoW;4`$Y$S%K~Cp)Y4y;0J6e z_#+Jw^eZJpmlyJ1PWbFed?=Dj!;9N)RYt;cswXv07t_8Ey)OmhJ)3K#aB)z?}Ze`Z1 z$n(&B5)#3&wIz#T$WGhRz1qtjSbu*maz0?bu2G#Wqg5!}2QIzh@kbQr-7e|NGCec4 z2i25jnt3&5w*nF!g3pAhYs(v&^%fm^U#?}1$mj6FhqA&F8>OU`} zUv_o%4}8A+&|`fXUvpN9RaL>u=BLJY;IK{IvR_E{&+&sNb#oSE*zDnaL%h~R z6}nV*w`-8V{1p)@SeeSE%v#!Oi#g^QTkk$twX`T|wavXr4K?{JGIFYfR=1G=-e229 zF!9;G#=@q1Hy~sp8UHGUweD%whAZ$}lc2NLXMrPpry~JPKLw6>N#S3hcfB9okmb|I zRtJsTM&BOZf!>ihk{Cb;2!-4dk8_e0UmH+6kUEfjR;pa}lsLstvBST*Y4>WZiBUzB zD3Zmy1;&_tAT7ZmlT)%glqc`+y845Dw$WkRc{fUre1(S)Xa4I#3+ZH`7f3DhDGQV^ z4F*;pBQ#%Dn98{ns>>XM`LF?Ap%CW+zB# zw?22pH?r6YmW5NF?e%i5^ymi3hL6QK_H$1tAYf0L2PWwR=}v_1&MJNtIcgz=70!tT zoBh=*np>oiu=~ccmV`UTrT3+mUGD8|5K(pUX=x-1_{%Z>{LB66&F?^oJA{5^kg-!?S0a`PW2mtvFtRuw`9h6)Q)yS4 zYKi*n7uGRO12hCyiZh(gaI2`a(?(A!F{8UlE!mZUm^#=qt@+Cf-C#`peb9@$S{W87-RWqKrYq$byI_b9>Kz#;h`( zKG|3jb`h%7P&3g`C7agaJu%#B$A`&o@znUu@s%m3-HpXhVD`G|Y|5#oegS*Ukni$~ zO8g8usV{|nr$wnw+U~v?7U^%$miBW#TfB|?Z6Kegm8~DGZu^V)fJx-o6P8mPC>l*k z!H22M^%>keSn6kwFR!8h!K2CGtUUI^!hIF8JxgqTI_s*=T*$ubS1Kdq1$<=DnhYM! z-L#>{VP`%_MoqKZAqp2U(;Q71XzDg*= zV*8782Zm_`h-0ea3%TOyG(pO(ZZ&~lNQw43D zCBM~-qdbkZwkFzZAb;H3EQv`_2IuG#rWiNHJ+%=w;w4Fqnz-S37OLV>7Hw@zt4)n# z*4Jgr)o;|7%aSOMFhX1=z|5BF?H65g#*)ku)BWcUe9P#aM_UlL_+nT9?l!v8X=%_j zh$!ZneSqWzVJ64zNAazu2{3==_x588X5uUU5h-=4{c+L>%+S98Uu4V!3r%%N(pJy*Vs=Ta$v$ZMME4>m%2=Gd#da2nTr*UORl|HYCadlVfgG6V zQEYa&Y{Z3IFDTAyuU6*C9D&5qLgv1%UM=&0c{KyEK7k00aQDpo%xmbWL@6mpfor#5 zTHg6aXr=orNZdO*CyEAbb>rTp0K57m@okr@ts$vLKVIpd6-0`GVZ`=AQg+BE)(fh& z8PAp0OiDlcOtuog*qZnJ;m5+F|E?wZGqTtU2!5Yj6w}h2tOF$O3q7q^ofL)@LgnjtBUOp;f|vU zpB(Xr?<_oTqV&T;0Kk9rL^d2`Yr@a}(_03KoFu5c6 zBDkHOQeI1zSVB|LLaxQy4fBkxGMBX=a*QyQdWt_`-gYqeSWo!wvZvRBpnfio4pl-p zX}&bDphHi0LsY-QREmF|ex+Tp>5A(jK&Z%rquS1qzVN=T{sg%d6GG~ghS@n`oZkMO z_QBTObH~es1j3d_qMU*)Ysh1~SsWThx0S~s2O2Gno#xc?_1cHvh9rA^_3={s##bg2 z*ool`L!OLk2O*LD2UObwF_ATK`bWMEg*v+D>5v_ThQ?ZQanyhv?%AtPDxUGtMMY7yA`M#u&_XC>2^YFH!zuyX3KQRBATEcoI^fPxQJS&( zu-19OrZU0%rRwY23C^c!o_~j#Rq>+n{J3L{)zs(HMbNr)X7{)Z&ByAK9qUjSHUME5 z2j{sni{+DBIWe$_H1cQgP31@jNLj5P4<1qLnjs7+`mMfUgce>4;5 zuT}(L&AL*&wh&B}Oggb$aPPW_e}3;SifnY$Y{`oyAz^xKcH@nF?T5$^dbO*>IA$Z6 z7S0tNz$&UOPX>9RfQUg8t(R9T>m0Hr7T#u*9HD3s=F|K5bM_&oXpF-Gp`F7ar4C3R z3IiXSKV#5V<9xG%A?2F19zGba6qj3IF24BzB^Uk;IE~5S)E~G((wh#~`(fd(mvld= zL{a1j*diEuU?Lm%$U=jB^`qxG?U~7XaMR-=#fx-0e4o~d^onT5NVR}|#hpGo(c2NT zjgCZz3haDP_UHjNWAUt(QT!F-DN*AW*eS%3Tt-jmOohJx1u*y>5Bqmh;~%;P206Ss zV#@Eh$#w}$CJU@@Mfyoo<;;6i6d~iWIhHBqgl!!{51$kiVIwFyNF(%Urj}Dvl1+X@g`b?- z0jI-gBP&YlDix)3&N7QVRJ$jDqqu4LHMx}h=>TAVEI-A)mO$R5N5UXZnA}t!vF7OE znkx7}%dkfj9+i-NAJy`WObItyj7!a{1;Li>w6a+T@F1;0maFO{)SuqAJ<~<>eMj^n z-*u3M@*LOkQD`@?s#G1Is>-5ykt0-sY*fO0Ap0{pe~LzUF87t?Dv>gbwM2stOB%># zDwN}5Oq~?gykOF3fF|qdUi&i=l~8PCx)D`%RyIwGUX(p4rI$z#^vMqSLP%xGU7J8G z_7RBkA9Ap}8$s)RQ8IzeTA)&pOjX@-N+{Dz*|u<`Sv8gEp2K3+=&))Yz-nV65<7pu zT@SxiWX93%VW(C7^%ZtYOa9A8lx!XF49ud|h5q=!$Mu;zGD^`R^|#NEKHDNy-f61d zv1SU5N=K0@H{Gap7x0Z0j=p zzp}7V`01;EAc>u!d~9Y#y2E0q<>=5+@bPk#78AeafRXT5@yQSZ#Bwjm0D)6TKmQmw zYmWRW#1PpZF|1R1tDiZN9r^7`qx}xNZ=3ZD^W?^@WV4uVvsj_I6ck@n#A# z8(Gc#c0fX!K{)J6fO-ym8dE&)B`)_&oZxA98kLzz# zJy%=_Q)T*fjZR7h-?KN)p0%{a!&#$L*xR@Hs1R9TtrP!gEqOt>SZS0HUA-W@XI%@S zXkm3QBI1+9B{+_yV!eGBG%E|=E zZWDfB^rCw{OY&V??4qW?Y&6c}Qy*40NBO+oOr5J?_}mmp z<$1JnWIBS(PkI~A+}Ojbur^X=;$`Drs38lf(UFb-$w^MzjFq$LNF$>1 z!E(Yo%h{0Al!Ukw+_gvT>ThJGTTGj%sxaQ3X zG4_sFy6{`w9hg+K2oHYoMZgri2CE6tyw#C?r9&K=z0#XB?ARLlWPU!>@yhd`xg_fZ zmY_Lu)pt^v*m--iPKx_a9bi#AT-_JDchYCAM_=cY;axvx$3$nZD6Tme>gS(;fKE;B zXWZlNb5*dX7ZoBj!q9AP7q9@2f({n{euMNfQ!JlGnYIkx2;0&$)wu`oL@vY=YbjL=X5!&KPX072nu!cuWJIxhXn z1#|As84tx5#P(&8)$4M^@p91?8n<|-!f)y8Y~8f1*Hm!BTjm#J#cSU9{^0Ohs;WGB zln{3;NJtYyk=q4TTQ*XsnUgs3H&2#NEr}*X7D?qbxW9B&dnoTeU1h_a_X%-l;DMHn zQzrC=evQ}B-k{{0nn9 zVTA2M7xpouTG}nCPo&JFYCTP533h^N+#@nKgR~8ZWZ!?;U5|a>pu!xlaV1)+#U05S z(6wHyrX1XYe9!CEPUGfJeOkpD!yGU0MwxC}c78)UKgFv0VM0yr_m4N)BsB8l6c4(tl{#2oKm z*~xoiBp(n;R7^%bDLcYN`ow*OWIiK#IS6gy7B2APe1Nxmbjdwf*#FRx6>ku*;Gt+eKoi-JP32mtZgCi4tLKA0xpurU9-Y{W zm3Cnh4pzfL)WFPb?)wYI8^PHFS#di(ZFp&!BP=u=f)WPu5A1fKSXdGI?|t@ZDHr+V zAFx4{`zxu*o8y-{v?E~RWCsH8OVbqrKpw_SFu{7i+{W84BwWX$d^Pe={Da0PSkxT3 z`MCYjaNL3Upyh`_2?w4((`Nrvyuy2|p!+tM)h6wP=E>y+rD}Y6hqYpd!6$iALJgIK z#0@i@Y>Ymp7q1pyyLtkRwC++Ra)?pXB@jn*9lQp5enQ9nGu8QQS8EmCK9OEgBf(an z`Et|!^aV}xPS&Xa!MBvz{bs>A*c1}u1NDy=+r~<}cV|z{xdPa9i){R#Zw2!xESps0 zm*;<>^`K(MA)>N-Hvjrzh(<~H)o3Uu>@i>}BgF{&J?X*g7Yk4Nn=O$~>bZ9wI9ykWCbOK}oY>p- z4VDEEr!aWX8 z@3<$k6x*5A*j;OH?V}}SgESxk$<+LsE!BBqMIOAI|FSK7$jmSQlI|?P#6oTx%^sv>S>+7oUC)r#Ab%8 z8s9sMSL>-!k-I|gQ@qrWg0RhY+5m_tJpOlcY#)F-37+0LfLuyLGM?oIzTfBu2B-Wh%Um9{Q< zY(AmzUh$Dd=U;$xpQS91v~4q@4t#`!eKm(=h~Aec`_uRlQbJAOIBYa&(z#f_Mab}M z$@3Q(m3gdneE-7_$nv-3OuA`m^L64^`%E||x;=S6l;I1PgogCWKqfiCjVe<`YEuua zlFN5eiC*<&1_zAT`SqgQK^*A@-@Mc(!6MMo#}i$h5k-neKvv*csP%+$G`|&R)7>;9 z)1Rt=SYCf~o2b1uNQ|qj*Xr%D3(e#gb_~f*Nm1}I3WZlb#CXps=_W8cNZb1E&Fe0y zcu>3Mv@{Hxu-SM1cdI8cV=FsjfJ7h|XRN4JDy=Lq+) zmc6Nr33l>KRTX&c5wl3{3BW7^hpK@;e(wM$;j=k+t#l#*T&)X`dpv0suB1$mF(?y@ z9nej@xpn{4bZgI7nVCnKI_YJP{?>#3WaJ%lee+@Fi3zvnZ?-6@z`S>W#04oVf8_ja zt`4ubT)@rcK03%j`J6HuZbvcnjx=d^{(&EV0!`HA=0ay?9LY9CvoTh9M>X5Tr8x(U zkRg~1v5a-M{|cizlRIcT))Fk(pIAe%OaL&wv{4jxf|4-~#DeT5BW<<>tl63A_Z-?I zJuCSDE+3lnq;P)BuibE=s-3Z;`Fix46Bg$Xn&e97MM8vhOI0W`> z!Ig{%K1xcpBOqPn3dxJ@;>Ks_jpjbuFZ#+@FByou9lar1=22{x4WhV9;{jaSl`zYJD5HBQM zh4CeL|3w}6%EOfk{1T}?+yf~bpKZLR`pgjc$Ox{VA3QN3nq7rW#WU&~?uE~`XW-&3 zgzLhRS_2g2>h1r6(A-nT^+7deFwL%I1n*H`C)Vu7m&Y0F#Uka29jO=w#Hm?TfXkr9 z@Y1%_D!xU|viv4i%tuJOj0{^r37Vbqu4%?6~Ez^vbDhpXRxx!iun z6IO%X*@~JZVCX~6;F3_8C5@($R#Fy5Oq#8LE$tfdrW^h?5WRK9gyKd~mcTk|8%cF| zrvtalN1?FTd{%+&IPovvWfL{EvXYPeMU96rS0euScOvKDqhck@3hUp!$zyy4Koz*npHbCd>R*{N$^RMC<5 z_4i7Q`VTWCR>mU4PY>hLj-6z?ex$nZzIw?Tf)H^)79rM)=yFG&qqA@)9K%FJ$?Khb z9Hl(K$?xsZy5(G&=zOYl8;jiWwPOT-OadLajY`^pAziboy!j^Gfc)_ZyQqCLh=UNl zg3^Djp-0&975^n9G_07aQMm^?nW((4=-|*C)>(^@K)Jyk?o9)aDAJ2sot13#4Yg%H zE0%3-^-wO#8v8cLrt(mZ>a@wzsLDJ3BTYb$4n`ngs&Q7iOZb`8v~l%Q$~kr_SvqZ& z`K4FhTKvU+Dl;#kLhinwK54P#qQrD4>&Th~b7`fvnuH;sLNI)K zr$?#g?}uUNjQRYth76Q3{XPXp7@TdP4V4cMFB9W2*5+GoE?H^B6$fI!y_W^?NqUK$#3GfM7SW zvHgK~p>WB1%pKTCD-0L;5_U)PBah+g+TG4?({Ai;6ha|x9CB`*4y#@;MQTa~lhCAH z@ZtO4?>TEgT13^Ro`%G6ZIthG&XIXBQcKK)94?$vA%Whdo=*|H<$FeC+IN@C7FT(} z`bH1|F4Nv27Oy(s8ejO?F8^u&M>MI9qqxIIs#TgAB&F!{IuLv zjY6FhTqUe5OBN?Uv-g|o)f)2z5pUi9dn!wUe^C(LJ{ey8*GBFApR#mM!tRepQuzEA z3WfvFz%t@W)2!X43a=A^wJZl-uu!x0GcAIcFz2VZEm^zHkao~`qnrfL0sAE(wJC%L zgw<hr+gXw8cttASat*R$OrOPrY#KwbXTiYMXquQqCXjF+CHR3vw1x=u1 zAZ$v)#XiaG12-B+gIgQOn&MQ+dT^F|esf+wl+%|d;2sUWpeu3vlif%ST;h6FW|z({ zkSje5kq9>UhlGZ9$=+4;cBXR;fid@dE>(o{OhRK@EAQ#-JA=6Au1Wt_0k&$8OsY*m zQdl*XJW+0zsS_VvSS#*e$%$!5K@z_R_)csNmVxH$3OQ{1<)*CQ}=vRH%AzCaE`+9kZ9i z_Ez z(aTUHCbde-QJRHB_If6|aFa93D`FF%c6JWTi+91sl1PphtK!cAuOf3wj=2J}SvnH> z-Om;a=bif`KK9EZ@DEoW^i5InDw1c|)Pn_MrK(nG#1Z;vTG|t%=8sLK`Bh?mD&!8S zod@pcYjtOhDl&=V4tv^(;XQ0ihlb8umhnzp=G@lN;2OXFfaaA&^7X0WpG znjYO8&mq5Y?H0VhIP?kELdG^WMM%fPCZzz3Vxt<1LY{u#AjUh+fV7aMlfP+q22t_% zwG$OcAh~4&%RrcYH!%O@*1D+Wy;NZG02p!%I!!YiH$mKQ+b`wv4oQ|90-B1kPv&!u zIXPo)6E{~?XjXD4iHA`bm@i$u#8DAbiIs}CFr?0p{yyuAOF=2IA)UX>vrYRxe+bXk z!1->pF9pCkm1m5)Tgtryzw?!m_Ge1$EEpCW<&gV#^$o*eBT+74hcT&SUhWVaEemlQk?L>3NKH0kS|lB2R>_$kps0>mG9|x8b1Gddb`|lKVn?XQFa8y-E3o?&q_1L{HjzHG*df(%xZv ze~2ILnh#nNjnYW@SWDK=t(nEtP+OE$OKf;- zCROYU)*y(=cs>jytVj-DQuFDF4$Pm`e#BKsF%pv{CpmBPjNH4oxK9HZg@tcDLXK)( z?GhZqP$Q?INJrdul~KdGj~y=>YUE7e*P858d;%YH4HgdOE$}XQ@*{Zv$AK0RA7h>c ze0VP$w!A6j!)8Z0@@;4~LaxkbIM7=7=&`7#!$h2aM-7Zl*^a3)Y()(`I!S_9M#1(pYB zRmI0<2_VnjuFlc9J5J_`PsHw-UWsGbm^??ssZQ~Oh8aW*o9gz9%AjR9j8=oXJ*Me` z3($Z!m^={Ys9{HvR!z*!w&Ye(^@p((l)Qdwd`fs_HF+HT$=RSh}fvVvD67LOaL;x*-oa)EWmPKY9Q(=)~-4`ifFRGBS(7g z+Z&L1?jzR(Cjw{?s)`f4HJ}rb@Waag@Mu82?xh(isd;&r^(^Lh0?y|B1*5^|-LYT* zt&Ek3)Y{+iRC($#`gFz4^ZdftSqX5aTFlem^V^?%(QKKY`anCc|BZ3-4+JTyJU1zf zu>xhBZ9c{V#~^<`^@KQM6H-Q3k^#hJB&18`lW3-9eYH>Zf+LlM;+7_jxa7pX#eHz| zyK$Y4_%8rOe87QY2u5>V=0-<}cn}ufmF6gZ%MxVXuGXyH$vfM9>K?$AjxvrGxZ}Ol z?sup`gcTnnBNN{(T z;2JErdjcUigy4|d`M#O&svRvDvkYrfaiz)vC2t)q0n)UZjDxyy#>2PfHdHrh<@%LVmjH#{Rrnb&bhEkV0NGhTHvXqF}j+UNlL(Yjr^hF_A zlogH8nd|W{Me+gX34&i?C>1^T>}FTtjOIE)45K|SR`RGAJnr-VQpLAa5&Bqe989An zy@rz$(XIWPFmoIO3) zp;Q6ZRq3Hy=}M4?FuJ02ruEEywMqgg0Oudo+}O!|f(Y~s6QbL*ajkn9On~vIH@!Wu zpGHu?j+bT4)F}F?t=)?9EB0p%b7lcLjHX>ZsqU0P${6gfKKuNK)qxB{mma#0lZS$n z4u%}OHA}xrxHEm;Et9S>UD3`}2vr*nHDB5lh(L027lKJ~X6ib|I_?pZxNt2n3^te$ z3#1*Dr;?pMxO}kB9~a3pYR!D>EMF1*cGIHz?9Q;5>H0$h2hPbMfES=PT?MYwv4;b; z1;yX|(vW1}^4MvoBs^c(%m=jw;)-MGHEE;@MByqg{%@;hjimV#O?UPaaF%+}fym+e zH=swSBqOv~UT#@(mDPe*2RWVv;gqZvih8Io@Mayamvs>@$pD&H1eix?Ut?yW>uAG(-mf149Dz- zX6s|O0~g49%9V^zcNJ?OPjYY({YPj6LnL|GMPya#s^VI!C6OiRpRDy$M0SB+X-r=F zUn}3Nbq+`eZ^9`~LB<2@=Qf-N9v*@S$krJvD`_8nP@p|u!gHjNszRwdO<~e&H9SBu zHrnVOS%kd*21A;;Hm@_pDh?Td@H&ptrKm(GNhh5wCbX%2MCIEwwQ3rb(5Ho$X1SCxANrdaapv}WCN4vvgrH-q+0*Z75l~b9=`n} zs|q3^)wf4Se(#$i2|yi1+?DxH!nhkAS`H9El;I|_@TZ|~yDbHKoMa$XVub6aFSY=p z+bTDZ=m-gHyp@9Lp2}ThM(1dx^iCxDlbXni<%}bkjjP<_f>0sg4^!Ff>Up{EFS{iY z`fvzJqZHXG2J=(4Y9e_A1s+Tg=7{EzT`T_tjNq^Nd}zPEehoDZU%s<^2vd;#WCoiL zRyl_q*KvW{Gmw@jsW@T>uqd z`i_Vcr7`|9;NqQ6))q+vX3cKXRjCFmU-5ex&O`i;wIP_8B z<31%WDHze{(XV!?Q@?A_MdcL!;J}5Q{!S=zU;PkmkAT?WhFB!v*$gZ#e%KOw42c=8 z_FfP8Ds@Sdu|r4BmJos(sWs7x@I)c<#Ox6f2Z=XmcMb0qCupP3$>E5X8?5M)HG+V% z1X`TmlT*%bd_HLSSF3~lNfHO#vH}1ZRTZa^u5u_;2bcm!srSwVB%m7Yn}J3R99lS zuE`CRrhR7Ju8v8&G9tdEu2lR365T@|Stf|e(nK9tuR}3GgvIFm4ov;ALAx6HygJW< z9>BJv9wfLg08jCFM+~cxF~EQ1F%QupnRt&=6c+bJ|0$WPZzl>&(K6j48Y1(IXE(JE z0%8%Iwd}-9Fv*LlLT_8DX^%-$rNLG>G`Addy9@kka9l>?W5OgAs2V#zRFM77fTv5C zN9FNr8_Ozn*>m9H8E_z;Q>4?>O2i9aqOB=FynJ)TL@n*EJr6z*ug>U)GK!_Q`QOIv&=$*^!e_0m=c&Lm5((Y=_gh zvFCA|`zV8U5T^wtfZ=Kc@=fLX$;E?5k(1bx3an^!dBj0+(Eo~}lJGxZMt%jeBUa)N zVHoS5nWYJ|Jq)-V%Y9&9sn|(4J~6N_Y+xeC5Afurs04AmakR1`bJCi^5zT61;5F(r zDF3jVND78vf5!CUsa_O9YOf~hHjVoDd7~i?$-GvOErU=xtf7qEnfBf=`5(4eeUoLL z(I`ld=AX7L_(d7DEt*P}dt1W4rBTTCt8yq;O`FoKXi8Ns-Hw0iA3phCQ~75@f3}{d z?rVrhA9Z_!cz;1@E|;%0Dc^E<^Aw3F3@@s6+cGf07^)Nq6qy5&e@rv7EG$v;VnpXJ z4?Sw{?y)rOr(~5=7(!$F=8zWzaXhFup@_GFNW&>G+mD)8u4W-ZLo)L|Nh6Bb8V;Nb zcHNulTHIUaXQqr>=(7|Ji^1%#>4PhvxHt0FXKR0(tAEWMf80)p%`)z^w+3ToW3HQj z(QnEZN{-_E7(C!MkY=%;%RK_IhbhFc#|-&pXiraPh(hlH*A& z)#CsW!Dp5xANz)=(!RXPmdYUVp<$Q_W+vVpn;5!61~@o5_;_onL}@Hk_zkmLvX506 zS|otm7u*6!P7feuyqG+g`&X+Y`3k5``}tOw`4L6UkPUT~5EhmRB`O}KF10;@!6IU1 z!qPyhuIZo!=k)Gbkv+VCH^}T*fnjUz)(uBPaXqsNyPe#Ybo7F4jc#R+bm4s&(o8;? za}+5{To^|SwO?(AKc^%B2p|-lb3ys5D@7yB-;3AGb#s z>nqoVF#-}uVc&~894jCZeX(WW!G%@!VmwKP&X{p_idRq!ofkyBRpvhWo3m97i~Yug zMT19dR(M$L>#}9uIvgCkR0@;wX-~XHR4X_es?jy6)I#^n7Tyc&ntXcAYe0Nsci@JQ zYG;02fc2QV4@S()OodUc{e&md317HPM(dV?*og>Lx}?ZW220I-bG9#j%rfz(TjNMc z+nfVo&8pYNCc5b z!9apHTlpP^XN5%ORBQ~TeCXZ!*h`@jE1X}wRz^=zAvg_~&-W+77xGbhsk@MOZc>Eh6r zd+Ls}AHT(TD7@OI^3_?_%u$yU%w63li+6M}L0`?s@9&oo=#aAb{;<%m48?;_-hCb* zSkd-5Sy5N2dKD-X>f#s1**}N5CYHJrN6b@{4<4>_ z45T~5EzDs~agdDzbP+qEg06RAQ5#HV*@unKyDcBItOMt!VWB=z()e-{@;DS$NlQ5W zJr^+R!s>z(CP#zcmX zKJSGb{*J+g$Q1bC`_Vsymg9-wrRhNQ(zP*tRiOyhO-lyA(tQE$tXN-{jd?`0Q-^P4s(Gfaj%g%{>CU z{ZEFcT!P%v#O(Sk@upw=hl%w~j7UVN0RYLXm{f5qc{?eq=BpQnR17t*sJegEW{j;n+zbC0_2086T&qZ#NhA+zj#O($b|5CO^!n}_{lRLsV^i^4f5 zW?=6i5Yj0gFoT1ILTWqykq&(bG=O4SzXKqVsL6;$`M2@)choPxsIHNC<5E339W3AT zrZRpZ8_9MBb%s-F9>(Gh_9BL=sx(E^LLE+5b=hg$i7yn-4=eL{@GH|IDNvw^lCElp zWt3V_No zS!)qacva@W`X@|sM@j=-W|539bLj@{&`w72Wxk8>nZv3}mEc-S)qKBD3NU@0?lFS2 zyXdSa^n%GIsP^@!iJ+eRTXu4I)iJB!X!Uo7hOI?Nye8NR^{e{qJk|kO@k49E{%mdR z`V8&`*Z%2+pbVqg``3RTullEy+LFejWS1m?4_6m(9QM75wcYXS9V)!*E>y_Cj!Lki zBW%@E_*@rn*N4|kFGJAlNH+}DU*ceU!FAeiN@PZm=3Ck@YzZoEk7y2H^l|B}#>=?e z$M97Ql~bUKEp5@?Qi?=f@QJghj5-P>keGDdLt+$pkwqQ!gD#1_!E@RFw?qzNTs_H3 zx34|_>9LHc|KA<|+28(m)&JxD{c0CcCqna=lJvWg@XPP|hZy^PyB{*yo6s*(NTP!z zXKobMV+P7kV4K*E^+Ab)T84UhG`uL=Q0<6>@x;_@n&`opBh$Kk;*$*ml1pC z6|oHnqfl5@B^gG#hE0Yc2v-t{ef_S3o=zrtR~jZQQ6>@4VM$?t(ffxcVipFzHX z{ZUFU&!5$h)`Sfoq8iMS1Up#}@bLWyI?R8V8!yG>Dg<$DbxGhSzyqIbiZG~9L}U@V zwT;Obs3__^iLlG*ka=4=1+qfOaWf!>ZUTiQ{84DCi`X;5xrJJ8Ak12kLW?nMZzcn% zHxXN9G!xtXwXNpIu+JT30 zLYB0Bs!9i+K{NzRF67zj{SulzMoYYoB`FIowrpPT(Zab*CM0k>w+ABkpHrj}?egb~ zYHT)~?ELb8{qcT*2O@}I)DM+$dY_P=Ye-onW4sD=im1k{6>QKQzu5~HUwVBOU6p77hi859j_hFvKHPGtfj_g z;YF5M)wGV7OQz{HEY8)V6IO$9i3?82Ymt8f>R#U3!Clrv)%Tkt$yid!K6{Bmv^4Wv z72O~P%;y^bZDyo&O_pfG53pP@M1c0EYHZ3;rpM22HCIBN5a2Vl4UA<*!^#) zEQI>{{=04SoqXA8sU^I6NyTmTynQUu{rCI+n<@Ca#=K?r0RB(3!%HGj#-9LRPS+6v z*rz)uIrhZ-nx28i*!?Tszn`oxRrt}u@+H}RddoTnNYN;GDaHI9`WycV79l?h%3 zv@`Ci94~(pv}f{>V+>dn#DfAGQm z3Aj&Y`7I`S8sT(30s3de+_-y6eNM(NhZoZgSM;}4z ziWvrWKmYEif)3^a|*LrAU+aLctJ|-j@eg*gX5;l>8%n zJiCkQ6*v0X*JY|~Z=n44*$qo6V!1ZNW`Sy?L@B*rKiGFtmr(56IUXy8GfAvh>M0Qv(_;Ua=lW0Wl{oaVraRXO?5dRbJKf`;Mw-{` zX_oe05pS+H6?v^5N8btXmF8^eXqx5Z*(;v+-Op9LO_j)g3It-mlk+x+Ql>8LnSt#~ z6)mHdo2rfNJB{>z`3d+tqeL4)C-feQ_k2}Mru#qfr1jw9)nE3IU_joJ!T*eTTR}Iiq$D@)Ot`M zT$STE{EGqq%?q-6IuP-Zsyp>?)X)lM!^J_F-e{06t^7j_rO@p{M9QxA(H;*_L z@aQh#-+_KQqbcF{qq}*#?X~C1RtrAYh*)wqqe`3!m8uT~a~gDmT>iN931HOG_@nzr zHd!Ho97?CIx=3i3SHy8DoKD)|nS*XwRIN`>+K6eLSf@jvqZc@W*aX28aT%ssaaxAu zjs4Y4KY~@tnq?y*k;Q&{B7~T(^|E8v*Ye-^KV&7OX?V>;sus*)WjYMQ9Ymy3!pt#` zEl4GLm#Ht7z4U)XhU5h*1@a}&XT_nFyE&>4@g_K#GUBUtHJM5&Agd{>Ob}zeY7qH^ z?U(~@oOy)qt_Q=vvgC`uxQRf=JGfLeEBani`*A6M?PHcsXqZ}K4=gt zE~n=QKi)h4F3*M{D4r9*Y`UNdHRlGnJxy|2Mw!#8G0Ka z-i%<_tx{d+Ar#siZT9eOMW=<^?AHl;*z#qUm(>|j`)Wku5k9!Qq6|>A&=Q6hy5|?k z$50X(ouZCldS*j0mzj0)iMeFgIa^v%M?bXFK$vG0mLw&e1*Br6|HIPDAF9ItR#E+{ zzmqpJbz;Lee2m%4qg#+Ia8r)h3RXd?n2Hj5$<}fZKw_1^4p3=7ykU-E?yfsdFN3AF z)bTdjq@V_vkzJ(6dJV-vEvOp_ z@msho^Z%@ir-M4*?|l(@YfZ?-Is7G

      Frw9BZOifQTPMKRe$kVYbkRCN{+jKdqITofq3sIpvvYq}gx7hOpOUHpO$ z8RUQ*mxusu^CNk)K}C#088{@rr?%O&y?Q0VuYSL{yYuPnZ%Pu9+k{G9)goH#?z zi|bXHYBd$hrYG$M_|r%Kb@8uf@L$*uCODoa{-KlK86y?|I%a+X5W9)~D7QRR=sNJK z7jB^*DOkV6!P2w>92TJPRCU;XdjnjZ#d$Qr@Ak}34t<8-Y4`&>id`}?eJziv<&*Sy z!{^0KIRKEN0Ix#s3mx_9BPUt zI#Jo_DrAtx*RI(Z6;a=%3WRyd^vV#YG#iHA*y$U=I+QIC)vQdAg)v)<7|dy1XR!eGsM&jf&2FRC8VyVb-4zwnP$LZWs2r z1t)9?sk(it!?G059PZG6hkDC62em0;#h`+5@h15~!{EECE^J%yK% zw(NXJVozdTJ2JeMl0K&1@r6xZV%}g@j_ql5uhtk5V+b#g=EL_af;7?fd_Ro)YD-CY zx9o9qC!j-JJWnZ`5D9UN`m1U_PFE2KC)cN3HD(+jlGMvYJhZ8)hexdOP}hr=xLn#S z3g}c}JlwUVqJsFe8jZf}RVsYYKv!;pDPaRAZ5CzU&q*m0ji>0+GmMyT8^-Oumi7OU zaWOr~sElV;wBY2fH5M;uFsI9=7d#J~Wsuk4?-YVOlsl;BC@k&!U|JjJKymiHROrk-+ZOdY+0Yy&;-VD9j?x*CWD0Pq;f8sEB5GpG6o{P;1TNO} zG`@YBeZKbNC0&-avF4KX*Y@_K$`g-r1mo)dkKjKBMZh9TkpQ8mo?pL4YO+j3sAmft z;KUzEzWa6ot(8@FM$|B)0b8~%0cUf~-rAYEJKimKAnBRcP&B#tVoPv_wIcw4-1)ME z7}tJ{rR|$E*nq^;#U2;Oxy}$DMLma)I$sOfj*;a{3N&YQFHg$|n}d~Pd~vsXv+6`X z!*npfC@Wm!BThB|V-99x?n7Nnj-D-JC8AV>I;}{DTO}Njh7At7z+UxG#r zhouj^j`e3sx(Eab&QsArZjo#ry)M~ zJoOYXXM>i)V3wXz5c*IkALZhp7EDW_S&`imdBFP`Jz^vznb8TdUu2#zVC9LPgidF$ z@e}Z~7x07tfP@M_Lq^8HKt@AB0{{R>0Av&bLK1o&4I)W(Q&fBgZV8uT zI$jgkxauzAKrml?R<_i{2GlH|>g4Al022ur2}vAqN8h0KqPIF-+Bu_gVl}(5S9e5T z`~JA?6>wB~$2rXm$e9>dv+(#|r++{8~BpBmt%C3c${Ib&Eyx@QRMX9{C*v7VlMe0b&zunU`x|^ zadF68pN7LmL-dt=F1iV6LCMsf#VOLpOH~xrTdTR0{g6VaH)~CE_A?=<+SjIB_OLvo zO-C#$_Kz4%T&3~xUk;;UU@^JxW9_Rl0S+z(@pdWskAAT(&dK|3W3nDjPT>TkR|BNz4l5rS!^YNE)G*a3o{R1@U_N!hdx44kv(17z~1=>dWoGr(A42Z9<&509idbom&Ar8AQwb z{4G{H&w=P*g=+!a@t{$CnkZWhN}qkEa`M3$Q&0U)yKm?TRYDwGr7-H2TpJ*Nq`-v7 zibM$IW-yfxlOVy2DT_d#DYFEAbT15(XTT)HsoPMWZiSOHzDc%%I^H~Rngg#Tk5cVp zwb)oUvOVXeQdfQ%-fH6fHl=r44361>FeRG5k_VvJjOU&&?1szPEhM^Ia(Wy#|J76& z4P4#S%)cbj7Ga+*_%#U^CG1c=K26rEeMbR+`l9Gd&XR~e29YeaR$oP2$|b6>0zvXd z&V2(cl)Yu@N|h7GyQTvk zgVeSJdN6Mz+ykBG_6xk2*V@ir0AU!|x@0K+&sf>6=wC{r&5DCCnjBs+&D>JBKG|uC zH>S0lxzFJlh$8C7U&)y8VB62e>E)P@;rsfGvFW|wFk_hDm^s&j3n=Q#g^bed=ryA}x@ zuq=7HL~b!wH8MY&98Z?1pnU-r!w%}2d@oYloD6_tS0l^>qrG^v$CzU0_;nSL60FC0 zFT~uIA?Pgan7V_ax~nzBm*D`bcs@)ujeo^1q%CZM!od_oPnU&ooZVX867o{6YrLtK z*pJF0TEWT)xw<($vOs-^lm}>OpAgMu5tN4JC+B|&Z1D*cezc35fJ`)w0-vUYP1AD< z2{%Z++z$!r0$b2#e^*UUM*@M_ZCmV!ENjMnSgFJN)+mU(qVujO9iQlk#^pVKarN0) z@Bk5VjNe(Wz1J-t7-&guBnZsA3ovA!nd{W{NrgnS{ix6HZ|{5AeSqgY^w& zQ;>X{E=cm0Uop5X6@7O$d@=zW#^wYdIpxiP!rPM4qOqGn$4!Nl0Y8}8|8Y}#x^4s zRVMA2j1oqml^N1J4N)Uc`*avw&GY4o&51#DI_4(;2m8?XD3^4dl(?55>v}@8xusG` z_=>ZG0)uovB*ZxVfeR%`_y-QBk~*rbu)}Kpb67FF_s87{NA3Vj~o6 ziqYyL+gs+eW#-M&lo*adY@}8p`=yo4)2f)P=}_HunJYJk(7Tnn85Mz@S6(hLa z62mma#kKYW-=69q-aMnpkZfkINa}O3A+IVb-3!!{^)m8Ig==mpf{3_4c;2EggtXO=U*6vrv?NV2fdz`9nGRm0K`ey9 z!^_fXW*};Rer}34jhRkhEF2S~+qFSa_=g4&3B7==EOd`8% zX>2E}Kc2pZJ~A&E>k|#Y^@jENTU^wSLL7?2+&|LXW4TlTp&yz+h*m^89j*>Sdl|yYUA#TJ5_R~Avuj? zVuC{E0qaCN%pg+4In*>m+xSqC0Xb4q??k7K2}3?6z~!Wwn+;RNx+6@(L8ZTe&GV~8+yAvYQTP5;tIrU zsJxpnozfvBA4_4{DTBwUzGRn5C$da;fM?#&W{fSs&{38CF(Aj`X7k7bZF;hp0*mo9 zDlfQTSFc(qe&zA3uHl7^l8tQ*VfvDKNQjU~2opU-DYqLhOP;;VvwiomODmxRJS#?5 zuWwc(X&>1ki6!JpxpLGKlei@bB0Jwsk+|*%^~hTExD0Pg{4%g!e=RxpdM;{i0hfzk zlpM(4f`=*Plqxy6<9lTbx(OhHQ_AK6x9oTAT`6zK)j&Tq@<5Kvvy=+Vu^|=@H@iJ# zi#pfeb@yD3Axki%;D5#c+)y0y0jU9zQEvJok(J5;e)I5Vw@mkb@fhYm0xaT=q}69X zFLS*uAPs$7Kj3k2cc!zAesIHK5w|-1dbRSJ6Oi6-UoCuM`&Q!a3kQNgw7rWZS z2G9Aw+ZBJsenBTzzBoQHYG;0eLGx`+jgxYx64Ui4Ck-9j+1KyE#3~9PpD#G@dP8*f z!;4Bq`myTEaiIYDQi|gMjpTurN3O7F-LA*IcUgX2u5@T#><5cLin1q#LQS{iJ^}gD zt{B?Ub9P4?d)wJ}Sb7vL%pqp<9;)>C;~F#M*gGx3&2lJh@JY|fZ-g!Oc^dHPOajZb zf&S&ENVKvf7wX|nCGF*yxttFP%6+zRKki3a0HK|*rwKfW6owW%%Fc1=OU~OZYNdu4 z^aLzDS$SYa=7A7w))BJT@JT`CJkJfxm=CCC?vkgf@Lprf@rmBdM2T)iwMc!eI5QX2 z9G1BrNq`R$y)vfdw;wqk8py=^IKqfdI`1=S5#1)>Go1~n&VQkATj_#MJfV$Y&sDNW7UVbEb=UXsXlG%gVi41&HW(uIE z>yheGHmBk=9vaKW4)830UVXxdtc4jI*duRm2!kRrw zwxcg#zrNcg=LK5G>(P^qQBzMSC)h(@Zo^|)jdJF3enA8-7b(82w*~p<1&@{2)HcD? zHdT!-kyL`1bpr8=S36_^Ur84;TTS<&KjNe+KyOP6-mMLIYszMlbKE)hO zNx0Qbs?ex94dFtE?x})~mVV6BdXGKk7W?4vUY{}(Bz@5BbiMRv(}@iV;}GN7VG^MP z`=?v{RXjbX@>>C}Ad(q>)=%yi;wEA}X5q}5yIhRAftzd}b*~F~vIo0@_IJn>TM)`* zf5DkrSd~6E`YJ;dhmB{ZlufO2wlfZz|5FPMDSShTZ1O^=hXf2m{XM{Osw_4px(T6V zVuO64ohYTlSE`}&YQ!6I?J#*JAXT9nwMjH$I*OR^M54dXCf!M3(&$zrb~1@~b~P<8v^EVJQ- zWGAi^Z!0G5gnqx;buN1~(a-~z!)jV&`!JS~Q&zI3irkXqwcYetGKaSVXcNAUyF8*m zdUB%)3uc+~NJIf`l6w#r>JG#e=}hAhLiHu5$Lv#(nw)eb(0^4GK8;y7$Gg%w!?PYh z;{l`Yj;rr&nbR_NeVQD4JEswtXLR<4r4eOCY5nuM#-B<+VidB;u>J~H~8=5&>unei)6kGLf>PMD62UEl6<1_0ez=D{F@p~H6 zQIdxKfuy`93J;jGmp|?%&Ot|t^%T_%?2A7j*M52qpWT()L~op&uHZ?09e=~zGCPqAI05BkUdKmL{E3};y3t04h1)BJq*wbHtxBLtU*N2Bl(AOOx*?H95aoR0`s{BR%QL+5<~gr z61zURdgUig3lk>}v?{E~ zK|7RkxJn=(>R5_c$k5K zsGWPzPU?-J5-z^UU3`l}nOO$mrt0r**5fl8e0IT$K7=xTNfxvtr?Fu~o5YSfB3Rj- z(@Z@1b_6Q+v%J;(^kn+-VH#x@-?3@KXm=svE7Da$6o>JAuPDrQT!5cv|LJp{S@A=>A=1{KWi%p&iRPRig!KAM zcV_2a=qwx(PvR%S1-@Z(W1AK0bCPhjhN0Nz5^)$JbWR`3vlsnTJM?1qwD#2 zz+>hJAR@rjba!GO5#m@Gi$BH@D#u<+^L;y)<{Izusym=cRIbZGA(+U{j_1V7Hrk`y z*Gfg@lPeEzU-$5;*+70?1D$5Sb4T%H7ZbYM?V-poUGdL)DJCk~7=-+3N50!tYZeV5ZSz<%y;R%J4nIO-3Zb)ynMJbs ze%O{v$D zAU1^O;Ow@WebG4){^;rVIaA9km(xfK=@-v?XE3Wx-Ua z%~hypZlN8_OEV`t2vQxZArdGi17c6p4H3UGvNn_^;i;k9S9z>X$t0qFfG+!*4bv@) z49^oidnmwQkEq#{ht};udeuP?ZVUQC>Eo7 z5{5GZS zI~?IOWNUgKOs> z*Y~a08HBxvya!8H?^Mz;iCiSm5d&$^fHfElM5h|jnuy%A9L;)DKA(Qf)W{f*rvvE7 zd#n~TRq~_gGA%N}d9Nm2nz+iDpsdIA_4AI#uNF03CWFda1M!_?=+g6PZ(sofy=JiV zGlUH2c^fPovoed}>hMZ#RD+@{1%8I#5796E5&3R+eWc4Le2%%-AttNy(8rY^(i~-a zRE8&*l~WVRtYP3=ZDQGYnqs5{&G!YxsoT+Upi zP4k6HRCMQO(V<@zY>P_e?wjYJ(*~#liNe%Z~Q{`&JLB3~$e0VAc5WnY+r>NI)9lgg!Gj`s?}tY73@Ex@3#hepIv9V# z!Kr?92D>0afi;m6%D3F~J2KqOJ`68*YFmVNuM4QH9!Txnfw!?|$XLuV>QG#HF=)Ac z)$c)RKzu$lG+%S1s5kyNi2ZR&8~bHsz0W!hAJkagg|bo0>4r&@qHkVaEwCCizR8KJ z`i?+fUp>8iXCBatsj0yDQte`G1WN%li-9C{-7RUzzQ-HEUF0Yym{@{6y?EOR#K*DM zRQ?9iW_?oaEATv!Bno}*gF2RujZd*{JbyAxVtvt*hjtYrBaDsjpXQF`ec{87bLc~z z-}d3A^XC)mWx*m08E@ZBK8JF7BuPXt1%BUKkzzwSs`SKCk9{sdUWXk1WHNxf3ld*A)v3pi+ppltHE?$^eG-Y#4VjZ=V)Ks1we;L2Na%CX8PYFV=TWR%v&AQLVrT)F*X&}0l zS)FLNjRD0yVXtZ*$|6;NS@;Zui_2^^GsoJhl)AkYY%689B?wH9jw{vfyKB7cVG{E& z>C_41%Ufx^$YeTL^-wrAGo9E&XUpwv5G$cV`;f*RzlToQe&RCG8W|FCL_~u0W2~Vb zopi>^<5}HK20k*z^;=Bwl6}~P@qF5NurSRWo)(R@9!43-We@#jz>|knj^JLrL5>kG zq7M+mvdaj)aK_7kIGxxuic!p{PYY21GW7et)N^u@9-5OjE^gGYc5!mv&;wz6ZGP;c zq26=8JQPyif&jEI9j<3U8~bkEbY;78{iIn!ZHp~ZRWMXuB))6}m&Q~m_-av5~=KQ<`W zqA1eK)WwxwcH=?H`O3I#P@${U4O^|OvEmP9bu`fE0uiy4EN8 zdJ;!_Z_&J4DquoW@dk`5mDM@tElf4q#7K_jU!n_qP*a_a?#aE4>^Zv*5hu3>S4FNV zJfWX5yOlO<3R|?hRk5Q2M&!)i4`;NH7kSbgM;1wP;TR2H_UAwFd{7(jVlIiyl8%Ph TM6Bw1J@5_Rullm`=feL2RBbz- literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/devops_d.jpeg b/doc/jbootadmin/features/devops_d.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..b18971a9d2a663c6f77f940473328ff004a1c93e GIT binary patch literal 76928 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 NlK=9E?BQ?Je*ns)+ED-i literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/devops_gateway_nacos2.jpeg b/doc/jbootadmin/features/devops_gateway_nacos2.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..869dc72d03f58ad57d2f5ff9908a387c29c8234e GIT binary patch literal 51966 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

      KC0wbre|Hi_KFM@Cu?!424x&BY#^q;aA2 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 F@o%%%A4UKG literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/devops_grafana_jboot_jvm.png b/doc/jbootadmin/features/devops_grafana_jboot_jvm.png new file mode 100644 index 0000000000000000000000000000000000000000..c9e29539cf6291675207124cdddaa0e1c9bcef45 GIT binary patch literal 117744 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�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

      EYWx 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 E5C6bGk^lez literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/ketang8_buy1.png b/doc/jbootadmin/features/ketang8_buy1.png new file mode 100644 index 0000000000000000000000000000000000000000..b178e66f273eb8da92bf28a16564208d1c6d59e2 GIT binary patch literal 38139 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%0D45~-W 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 z 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 g39NY5|1$Z}|LwC6+TFWxE5^&h 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/ketang8_course_study2.jpeg b/doc/jbootadmin/features/ketang8_course_study2.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..52daa47b386a46958c5d897e6755a50e94e40a14 GIT binary patch literal 66232 zcmeEt1yq(xxA6PY@zUMhA>G~G4JwVabP3Yk-QC?GNK1FO2na|hQcCHM-XllP@to`T z{rA83{%f6wwU}MA_spJ|J$vSTp3e)P*8#{f64DX?AP@im-u?kTF9SpYaL_QYFi>!? zFt8waI1myp3KAkB5)mdg8ZHG16(u`0KR`6ebK6e99V8Az^B*1|v05B9FI12D{F983xkznA! zFYW#IKtKY)p}=6EZ@IW%7`NPStlJU@1`YuU^?3;Z0>7<^432zDyGi~|;0uTiCG-c% z-=j>%f5pcQoht8|iVpU#9m(I{IWc)hr0_4P23GPD7XO8EFp>*{i>~ZTuIon~w2E`8 zC*zgx$@hkAGP1Hi3dFDK0sxkDgA%?5^#E}W6okS&^Za5q6^Xn_~t5YX0MKilicL0Daeekg+0T%%O z4O=liT(=!W8-3^Z3VsSh$ChJuUI#&D2^yQbRaPRc*$@0w4gdf@@~y8F@E7D297pqNt8BjH?ZBFd2^0db#~}2_xm5#%I8g=zeuI9C<=~6=CyTWm4kaG_MI(PoCR7lqb(-A$DewQ92tDlmf34$A0i@C$ z_z&=3;{iCKe_0Pa*g>$vI>0O4oy#wuOSFuBe0*$X578>x!AwSpfQEV-ek#0~`*HW=(>oKs-D@;y8>^%`qYGUxi?vP;&j_f} zCbg$4ACzxc_c(J~cb|NU5)|EW)U=)V+&P_fdXrVeW=FUK=l!oW8EH~hwaY?+DowFx zd}%oEB$s)RfaVhTvF5Vo5~UZ*4CL64>_Sm85Gyk^vK3ih!K>1|Gq$j)Fmc_x6(as1 z{ECRE=|yHi{p;mCz9)-{^MxFPiyv>EDAx2VuNE~Qc@$sOJ6`jnx1OD*^S0bm%xo>w z+sQq~@}?&caoWUnkSMV@v*MNy)Q?WDnGot7NaOzeI6}eh_F4y;-*bi zZT!NwII(T5?UEqbBSae7IWgnf3ySBf{${u0- zH4U^7cr1h|mMLvc>|P^%{up@3uKX^xiOf7}yQ{{tC8zoPhUoNroGRZ3Odyb^{O7H& zb9At&bYE*UQYiMWrC*cO-R2n}KT$@`^w7y$v@{J4Y*!g-i;+tdH?(|cc}Oos&XcY{ zGj6wjo;a*vQ6?5`syBzMj49i_w>)P(+d>ij;Y4D#BYx-2?zzRHEhm5Evm-HkVIHwl zRn3MM8Tjgn0dD%@oVdaLwHa2ju!a`<%XM)&S|*XXz!pc!fe`DI>c~fBqH>R<%@8J| zEXOlQb#)m9DYp({i1IXi%}4qr^! z0BqQVP8I+%IzWg-f{vJ+YoT4LzmDdW^WyIDvpDx*5qbNk}zW6E|zfxEp8i(nyE{h!xO*xdgIO#42ImTg+D*wyTi6yIDDDABtj%Y05AZ6 z8sMG%RZPDtxNB>G$%8KiuyAYV$A{&~Ndyjy2Opu&?$jOUuWiWMkSD5R28Nl&+qtOK zK8PN4e#4J4T2#VLh(wibON=9k_& zb>&}zi0g_Q;Ly2K@jEbq#9}Sr9RE830Dy5)^Zp+H27~YR z{CqNh6SymZzTwxX->(Az$?VUJ0{}?5WA%5{{uY3O13z+;f%#4b#!18bJM<0&->s|o zh5!LOQf7~>04R!MBUsR}3kCeKGGx$d%1MyI#d}gF29x+c^-FH|6r#UXxGR~Ivi#&) z$Q3K`Vc&GpDQxHm2y6OE(87}ZVQp_Mi^WRMesoz=nop=iggz-##BXnI% z+B;Fe;&-VjQsKK*Kc@>0JRVq3@;%r^g2d6%!y7k+6EP=$Jy!ol=v$Rx_Tsd-r zjyLV_%LUDT5cmUv%}&HIT>l2fcuKf>J8V-}Lx^X}`wbm=a5nu=Tq(E1BwwfmqqGS% zcZ$0pYkSnhW<(v>UrzDwdJN}4m1nO12V*~=OcnyxN#q;c+2EAI-oQ90IdI}mCgTAp zU_j0GDzsM>&PMQGF5%xp&3{a<9~9prz&PUHNI*a36RY;fk2_;e?u2lXT8?fW@GhJf z+06Zl4FFV~LH@BVq+N5g7(X_XH5;DrZr9deam}5^?|`ocbKTYh?`WXPLpRHDBFw*z zNWXUnKs_1|GL{xO{c{+)wee*Dhwf|v0Dm_jGqk=5 z0)0`~g54-^Xb^Vtw7={;Z1JFE066hC{&(enc+mSh3JAIMuhwUg#+{rB*hCH|dO(Vf z6$Ngv{|Tk6{m32fN8rm$_`@FaSFwA#?5-*qiY#gUge@lqRi0h>k^Y(`St3RA9rp*| zYakHIvwHxnZYLo`kmxoC#7dB-j$n9y5d;3hS^_|7uUFFyQW!A#%JIh_LFKxYpIY7f zTK$WPAD`dW)IdN^+qqrPu9J|CB(?Ot0SH5GV+m;5ud=@s;nXw3P}760DbXdSDPoXU z9+89Hj>UrakH&t(K)6w9COEhEOj1n7N5%`c8$Ubv;@8moYrAW>Bn8F`A1!NA)F_A^ z;q^Hyj#POlME{@K0{nXcTRBs9I*UCMwunQTMGqyyxK21$TgDaAXZoi-`Z{Kw-`mF~ zQU5;r|IYd$P3S^jctnAWQ0V8jzNSCrL56w@c5SUG{x1F<1ce|uTI>BC;jiZcrc?Ge zIY=CcHwyX-nwVxha%>sE2h!{@;}k8UIP66EINxM`D8HHs*`5~O>dN1hg4#f7OVmt~ z6wpEmBO637`XUNOpq~9hm2b!;{l3sQ7`S!p&f>90G#i2ll!2o^nP>HsxW zUC-U^hTl#T(kn7jLaS(IBf*Voup2AEkt!wm`2O#9zd^t4zep0dq2*Nn801F@^!~#8 zrF5?=d(E6L76>>sUNEO3lp`Ffv)fvezOBvjMa7TL*S+(5(l5Cajs<5TeX*^OLB`h{ zyNy;+GS=>(kjb3~?C5({cQ80q-S*(WF(HqIsiLDG>LwLDSfn8ArSZ=Z$a6dIwEh6- z&plcE*ApQX++p9~71cy?QiJSzI)v9gG8UYvVY%x+sJ=tK+Nk5il>>vFK}M3O>w-A9 z=8GLLiyfGgWhWd`xwZdGivU7ZxV(WnqVQxCV=7z*p}% zCvCB46Q4U2nX8j)C5n(FLTt1x$#PFkL~G-YL$g#P|KdV`>#>8Vr5(YQ7Iz~*l_JZ8 z>FqX$3AH9mg3SKTB=J)bzciQiodCT~l4f(r-Sp^Tlq-EI8oP1{-;)HUF`@mnDP%c$I zAIwh6+4yI3{@;s^fEfvL+^+Idaq?s~9QL>Y^1MmxW8-so_Bhzz`|vyTb>qU^>L&5l zAV|@(gvxD8cIQitDDtyg_jiI+wo-{3Q~Sk`k;fv!!+P93P|b&FCilCAKSEu_7mfD| z7Im~xkxWo#FoOKd(L-$*V1$}zqa4OlNriX!q;F%00>y<;NY0Yo_%kOW58tuMYK~ zN|h0su%}DzK?+CeXc5!j1w^L(ADjP|*uRyoVRP4!AJ6=y&RfRUm3#WVgnq!U z+(3+hjr{cF^i9*(twQJ<_NDxl9OZlYKf}Kr+d(M+LLmU*IPu`^ndjIY3Pc!1{P(H& z>#^)VYx^_h%Tm!x+Uk+sJyIc9RI+758R$FdYCc!Xkk-+|*q#pIiS<8_nc6C&AA_0r z`x*3P$Z2O^Jot+ZaA<%_Q7v!)Eo^#ms|#jZRyC6m&M%t;HPn8ll3pr5PFu+<|Bs93 zOT{k>h_QR5f+9`+g|e40ahyDayLaHY$$iVl#DP+QcWc_Qfp7)$zHJdWLE}>h9jg~^RY@+J>ggB zFExIDpb*SPV8X&f0F{;Nq-OA<@LX%nn0;L?yrP%2sF3mE+r|Z z+o&hqc~|p~z*pV>`hq&PFN*78+mRNFGcS&I(*L8@JN)0vl7W820R6k}-zmETevH+( zC?ef@1 ze5j$_GgI0q5k+jSzqlPi`=aP;;zpVJ4Lbj9lKzLyA5+sWv|pK{KpA+JW?vA>*w0RV zzjfx{J+1^kq7QXYj?)J1gS>xJ_2=T>OLXZXj5lzQr`$<1^#82#8|An8#9|C^1AtF{ z{Y@tMmiN~gJIUe~{@2I2OU;U2f$c~OcUUDn*@`%JEc|wc{Pg*9tL2zSmDdSKHRr>I z_M^&n<U( z;J?!Y4Br2B*5Lj!kNU5(@sFneDG#8IPekgqkVlX9GpTq3FH^<{qz4IvmDKPD09a-+ zqGSn0QXa&qK7echcrhg=G?P|lgg>$?cPD^n(+yBTl~}+m^lvmBwqL;k>iF80^iJnrdiCU@##qrv)68Tu#+>HId5!~p5~ zI+9ogt2I4*oL^1Cf7UN(EteufM7Epg>*^+kvHDkVMrYISS7x}%<08qM*wRbZf2Qn?_T_5=31k2m7!Vu?2@ZGrjR7UYm@4Wd%68Dm7|bkeoJt1vq(Z9t#AHmY!fNCc>>MJG9kHBzv&Ex3Z@)Z%x&0sk zhzj@&SSxCAny_)}q%k5Fk6dZiE2umaB@aV-Gw*Yqb4Y;$+!g^*J-m@z{M3kBV!~-& zIBT>W1c5uY{S-ywvP5Tq#5q-oG`;a%*Q?{9zK1P~x>g|XhvOT}^B5X8r*jTL(I7d6 zI=sX{@+&IonwQ7e6|><3BM|sG?OMrM@9Hdi7EHT8ln$fYeA67;$UGcFoW6xmH)CX^&LqQXO1mAT2Lsnnf>u)WOG z>nioq6qxltwp>w0<#IoH+5#oUJI_eK+!{sZ+o<-|$vsg|ia7=MLdM9n!Va6-kVVT`YM98;5~tp?OVAwGVSulKy2GUvrf14h_*Y8*!;^EpQx6$-KSZ!%pJwi_EEa8w~A>ze7F=!JL!bPM5f}oF+ z9ve4ihU5;r1C7$ti(^X%ZJa`uIRjGULRiNgVI8(!Q|{E- zb!rx+h!0~IFbFX!4%15A#2n#2ws?KWzD9&QlR=(!Lj4Rd?yzZG$9Y?S#2AN=GR0uT zcYlbc5vfhiX+_9#d=)bul(RiB$C+EF&E~0M%!qx_6s?;TZ)DI&g3<|ah+kEtd%2IzC@;dw zO~P>)c8>qpD*klmSjQCaZM0jL@6!ccTVRXg)0abrD%47R8&)$#HmA07&+D<%v+K)s zvyLBP`cN@c#LO+%+b%8a-$MmY&7lvkG()qb{t$RHrtT+b=%VKL;l8HE(8Km%?|mzK zuJMbP+&B97GzaySE^Uow`-3cv%QslZi1(T(3i@S7*eZGwMCun~_aVcVlI#-;-x_Zb z@5D#&1Q%#2Yd>y?nkCw>cu~rpwa6Tkfk>O}4-$PQ<~TF0HU-zw4+r##z~o8MK$q7) z2-*u^M9%@nCS$)r`vh&c_e{1SFdqFZxk{dWOy09fJ2Ew#ALR{GNk~Y_o___rw~|n= zm_!kg>h)SMc47!D>VAn8B|r=Z1d7?TsPRk*8E~R`qf=Vi+pWJrb>)0!&?28OG6J!g zpD^-3qnVu9C0Q7yPofducZp%)+4*y$>75J7+*u0k{8Al^jx|u@hk>=a0dEq)qYku{ zR;7J>7~;(wq(Oy6f9ahEd9ci{bOfsnNy+jbb%KoL#B#lwAxey1SOTJ|3R|BE=010E zd@An#cK1{0DKRlWK;LLJw%h19gK~p7QXsEy{r$pAYn>kGollp^At52>eYG7_cn;*p zF8WTO_LiRO^ura^oa(@3uFFDFQXVnK;M)k9g}QU&KvDJ2|9?F2j|cwo!2c&6_zc*^ z^X}CeH;oA|w1DnN!pW(zSuU_x+S z%YqNB+!W)?Ve;5&<723l5W3QrFsth+H*2+;!qJgnE|?z~+C_G7(zd;hagjW>4la`; zylP2fdfNtWW3B&Ok<*2SLs=6a6}&8qVy5~x%SXW=ZwA-{|3AsqleuOpE;KD zy@%N(Ga;gAydRnkWuu^eBxh6kB#ne5+;4=^@1+jH?qZq*>t}!rK^~Jl`+Hc;K^qn4 zo=kJD;hsyd$F1DZ9`{rqFM3jLsv&Y6L5kT}#a6Aj>c96vz4BsgC7jNGxmjYg zQ_*y|m?N`*tIwz%`ic;~g^`VpBHTLqa^f{JqM;58NLwoUGr$i#TtVd($@zA<<2l-S zpa6qFwef?J!V;4t)1!pDaLE3&uen=#@U0n+nSfWQ15eh8Y_4FF8!;v|bT);8vdl(5T7D=5z3bZmKM6#7*{+;6(u+ zV(7rtuNj0Ku^t(xDrMV6?3Qpmz`3a~Bk0^CIS|vt&7^^KA9t5!z|(t}i<<7A#RDjYlhQpVNj z=xvJB<&Ps)v#=c|D%+a-editT(- zVp#S7$=29rXB=srZ*YfnaVq`Fsi}#Qbj{XgoeBLxDonj1Yq!a@c*il@yWDar*yRM` zVV!<6j!tclYf+1eGZ95r3y0GaHs(2{H|h^<2osK;DD|i^^|hN{Jx(gi&^@j-gW|+dTqyMkmQc*mb9|Xk*Rrh9Br=(NoosrnX}_@goR11M*{+Slvb7T2b*sct zJzhknuCAr5Vktqw;P6?JELCOIX=><{SeXz z6^O`;XX=Z;66{pRUa5}Sjvd_sv%6SFRZ)=zyMSr19uLKFOc5nE+0x{~dTnI`oho-| z?PM6E>g+Jo!7EjgpLuX|V6tD&@%#gUocfB2uG&dFgP`Fdz7ug;)Q&S0r^eb4@6%WI z)X5MyIgmJd@h?k`BcE)=L*ExJmshGyM`^*(MU$6Haa$?VM6j@4C*EjMs%y+?E!&!= z@Gp*r_uS%jnY$mq;hSMWP|2(SH;ho!l;JzP^IlS9^|H$UT#=G{u{Qp7c`>HJQr>Hc z*GIRRUkY8|Rcz*&r54DBifs9YAst^6k&ivKNB_}8l93CS@xwV%l*ku4gS3;gKGdb{ z6PxHPcn__+uE=Y1mCF_hTEkW`E9EWUwiltFD!84V+r3tcA5FtSJ1^MnH>`oc5=mbT zwiF@~MZa#FgWB|7!e_i0c96evN}RAJG_ZB;u{J1vc{u6DVvk~L(BntBtZdslBL?cw zcJbTfD7eoWo!`aNVi&WJ%ynI2TM|+uIY&X18H$L zk;it|;d^}|ne}J>a-K+e1FeF!wUXAfROnGWuH}G-sGt+nhq<~Wkkx{Uf~<3H z%nII} zuy;L7b8D-QFc0?@Y18(M7_E)Uh=((orY$jo<#0&?G`RXsYU`Y#lyd62DWaPa20HX6 zgZ$9)x)tp{Bp5J=lyLPHAKeDa1qj=laa}BQw76s`Xkha2%Wx7ylFxv1Fc(|ezK0E= z?P)W3i`sH$;ALrpYNLb?8fw)~SXgXqUCrvUqdVM5_-c+HUlySmAtzd zm+@ASkj(Yg2a_8u=U~bo*_H}vJ)y3A#g+1wO2u1+rRX>*Hgq&S7VPE|RK8B9jmCP7 z8=G2j<(Vsqoh?#kx+yekMCioApl`RLmi!KO8@rYA2Ax21eaa)G*MfprT@AUVG^Jb! z_~+s{dOejI*{N`53c<_E!Au`l5-2CbEIMf701(- z=P@Ic)s`EIh0lPrM$Y>;wPBl)-hnGX!g-o!45labwQljGGjjmtGTt^K&*pczWfBBj z>&)fY2&)coZg@<^4KaJVcL>q^zKdw+%pji4ZIT6Jd&a|k%kBAGc2P=jYjCWOe zM9_v{skuw%%b>EVSmHgRXn14$UELr-qR6xjk-Q+9IZTh$zX#yU0%;}7yaFr^BFqt-NDB6NmPT3iQ02W@2(D&E-SV{&QWyz zbx@*?*Wl8C=z|qoAtmX=BFls7$G};XI7wSbUPV(Un9w0^0Tw1aEK6+R02tzXRHxc?(b2HBAob9tCOSfsi~a5*jSB(_Kd;#+LZH zR2I_<6;6HabymC@+B4HP7a)1WVnpZ473MkFY=1r_(-v@BUHUfjmWC! z=iRp>dw{<9P>)0f72?TkjdNZCR4TImLuFTR%S+QXV8adT1HjU!lq!JrQ@MF>=Z-@2 z+l)&4fR~=yskYg|H*st!@l!dkXg2J=Q{JP)m0WXI*O%nTrix2x+yH2<9xis-Jn1Do z;khedAJN=nay)noY~euO;-p6M^TfIEBnJ&b1<9q&QEP^>u&$+nK1u=@#A+&)SpG)m zSm0wD)b#e1rFzob82>a*MyGf(pwH5US$o0rwmmFWWc!pnzf%)S8|(W`Nx8OsN*;)k z+Myl_fu?o?o#Juekm;|^E60W>;Mr?lK3A^eNPn3c6UxnXxx-L@?nUdfR2Ojr#?i?7 z$Oer~0%l?zmjC`@nm{P8E*rb3*&%$+4D%#3iyd$sq2f(yQGH`TYm|@?f9Yk!GMXg? z%TuwGPnmej4I4$x#)Dm#>(-byaow+zf~*_ji~~)%meKK zP_H?jkr|(RAjxGz-MJ?#vKQNiqH0~EKR(1J(wA{bDmpmK4(e9KUb9Da=dY>DVO!#A ziEc#goUxYAB96VJdAUQ@MBu7u;Yf(Bi8EZl(gA%a)hF=k)rbMIy@907A$*c)wE-bC zBneAJRHNLb0>Z1y4ijr~c4F6_y^ReXjhV$!zx}YsQetw!*7B(aa+|oG4CCUG-W(l; z7HdOGy8!@$*Ohj5fU=tN*3QcfSP96^ZfDF{?38qy96Y_Pm5l11T*&E0iQXk!A$~R< zKM@ecvY9zmkRIgmHnL7$?Rmw_U?9%k4d0iUb}ZMw3jLvf$HRk6vgZ`chAde_E8f(I zD4|z7dw2l&*zt9JHO=z;(#dE}w&g3jOOdD>Lw2t~j!*K=%W-N#a&ic?Y;+tsth7xp zv^JU%T7x0r`tRZUO8Si9p|DG%#KPe06m2q%DETsy)`R7fBEg2*iqMxkugbknIN01* zSHZAGh64h_%)*;!f)>m53msmWX0pHKkPtUuQ4&SZk|-dbhcBj9>O32ihu>2o7&yZiwOQiT)h`$|3!q&i2&GE@m|p#qgKMVSxtM6u-GJTmrvO@)f0 zGD^F9uziWhjUu*YBXB5MIUKxh&&tE|aj@L3Qn&&;Bd(#W$=F<2#GT|xw$OL-N5y*`{gq_OXUaneCt3Q|7J)7d;l zkmWJ|u`Rt~ZNfkwFD`!(es(|cE^9=%DHk?ua|$YB9+8yFXF!=m*+j2K{7@Byv&vX% zX!2w!EPiON<{{c!NA8$JZB+(pDoX-YEQteckncc_eS5~Iq#HxF180mOeS*}LTUiV3=gqS1)O zMtcmeT$^vm>t1S3JdxNk%8h0~uaoNxeV7iG1%U@MxPcq3I$r&|4U2~rqwiVJZ1J)> zlp7wn=0MZPB-(__)RtuL;TV&i5moED=V%NG(xnW|dd}r5BbQ>Kcq+gO7b~WeS!c+w zm-{eNgE=NcSpt2B^_&*lU>nU3*3SbGAGet4wI&um7G@&#>`Gy3;@lSl3ZD)I65SJe zvsUdVy$>GJZ*WrC7{;y^ELRNfq6FGdW~;L(-5R;#7$XfNDtr4{-F@epXbQb!V}m^m zcgFU<)l0FG9q6jB1E%-L<)h;RHA?Uzm<|b2QU+2|{unrVLp_OB3qsL815Fwr`e?7k zt4$j*=O%SjGA5SPh5Tk5tUh-vR__PQo=y1SiWVi>V)}MB@R{mRp7kwez9kX8#NZtT zeL2KXx-}xR_R`B$Q>IixsdSM(F{R9JTd?;d16hs(#oQ$#w9ff$=zX&j?@N9it}=

      wHSs8U=Z}U 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/ketang8_login_wechat.jpeg b/doc/jbootadmin/features/ketang8_login_wechat.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..f2ecae1ccc942b1a84a5beb031ff021ade6c79c2 GIT binary patch literal 68433 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 YvN)6P>puuqGS7+@cWgZ;sehLL4~kqifdBvi literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/ketang8_ucenter_avatar.jpeg b/doc/jbootadmin/features/ketang8_ucenter_avatar.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..1c086f7f71cc7168346431cb1e2e0ae1059f32d5 GIT binary patch literal 52346 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%0Aa>#73b7kf# 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/role_permission.jpeg b/doc/jbootadmin/features/role_permission.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..dc47fbffb4b3f0d841505b52025367d06ba5f282 GIT binary patch literal 73063 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

        <>A-ivd79hr!AgE-+SJ- z-A3j(5iB#)k?(03O0l*EH71zUB&eD>9=-R`UjTYVd&7cPxaVtMxu_KBWKOw3tVx8$rSm?l5z8JuyQjySU?Zu&V2pJVMj8cm6X zMjmU>Kzw4i{Pj#lV;R%v#mb9A@Dqa_1@QbA4k$gIr%aqm^MhAPt%6rDsY|m;u!j*v z5M7B7^P4tz?2i#$H5Bp2aK5@UjS&jBH@FiELokmrL?*qAw{~f$OOo$pDu>PL;oGWP zS<{wjUB!;psk1aW1zl-9Q^V$w0yTNcX;`?CG<>aPh|6=yPweQ9GyDR)n9-@DG`m!! zO()=PaeN&?Rk^C>C>?vCWmvK37U%s}fy^i#1??pn{VzZaguZ9B@p*3vNF6J`P^U9|G?To}VR^8j z23~R!Ph&zASKm4^z4$2nX)|x0i4KOvxOrHGwoT$r{4?f|Uko4DW1|E(H z=>z1UH?wB4)%AlUd#qA&GPL~0AC=>L=vc%=)ncVB;%E2BygBhR4>I&q)1X!5HykucL?TS*eh$BvvZJ|7BchPG>R^<^tBBrZ?9mPj$+*6HU7!DC1Xk-(na z){4)}V^tQWK;U3WU|s`?&>?%4F-PG(u#sHe#^jdS_z{6K4^b72b@2Y0=v+2E0kg=~ zXXh@wLY+LFCPjYFsA?p#JAUlEGr9Y>@UXvtdOIW899O{?Sm3Q0)s0M-CG^Avu=U0ZwzF;lf_I_iZ%4X?obWM_P4FwRmWu3EQz+8if=E=65!Jq2!(@jBW-c^iaID>>fzEeqYLBzl~&k1Rq zC~V^|QEc2QLmr~j=@F|rMPO21hoQYp_(m*vq4`(Ks?BS-#jvg;`uDW@=CbYO`LBf& zr2BG$-kz&0o)5Sl;8!*6Z;GWu6}r5o)o$Q_M)~SZhcQjF&8bw%D&>1vqf9pEMcELJ z^hcuKF)k)*A`Nmn1BvwKEU%eYIPX$Ea6H9tPXscH*WA6o%wnw<4w-X#w!l9%L3H zAq4@6 z6=t>0gXPBm_r=&-dCm)8%xDUunZI*_O*mEhrD1Q2Ly_0O{mFi1Mz5hXOQzUAVG%bLHs3S*6L12n+ zngjrX^hs#JbTETL8hRcjv^zI1l1t2{KwK{*5%;XQuYYRgyJmqD0lTeN0t+2owi_P> z-dUH(bmP`Kn-(QPXU1EDa!l^$yYuhnU%ZXZXzuaZE?#b)* z{!O}a<^e;O-Pp&NiW`R=rfYVe+KqSAe&O5I_J3!YMG$SCX?nR2p!bI2dN<6R+L_5H*fphTu1on!-E*EFFbk4U1E``Ud#*^_En5RLw)Dc!NM!96|8)C75Mze z*(meu(#maT1h;8Z;dagwfk{Ep<>_-qTP!ZOGtom`xK~+P?On&?>#T0&WD1X>%ETt7 z%BRezWIkKXb;)Pi#q$#Z_Y{~HW67|CK&yl}tb)X46G*ggVBnD->n4dlEr<@rBW8=Hq31^G6(s%`S)zF( zCTTw&(S#o|L;X! zUERhlWtwmpls5s(8L$OA7K`cuT~zgqK^mm;*W z^ZV|I#oBRx=;%cSxKGHJv~P3rOy5XIT2oMO-)2u4-3MqWL%2eJ$GaB{pT5{QK1h?# z(9`?8>78Xu=jyVL#``q-UhdD6@#DH9AGQ=izZMcWJ!2v$h69i5n)dSOHJS)4y?D+* p$SzjIJI*$R&7CUS3F7IqGBCZA_B&1}!W-@herSa literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/sys_area.jpeg b/doc/jbootadmin/features/sys_area.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..a88d80713f619a48484700d75f6f5af8d66914e6 GIT binary patch literal 86311 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 O1+;IB9)|;eEc_b=W7%W? literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/sys_dict.jpeg b/doc/jbootadmin/features/sys_dict.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..d44fae72b9d2f2c73f3476d00b072aeceacbe11c GIT binary patch literal 52214 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 W&OhDhI7R7tS$!_5xBmcy>Hpap2@1^s literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/sys_dict_edit.jpeg b/doc/jbootadmin/features/sys_dict_edit.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..75475206ff244d217b274179639ed5e8fb7d5815 GIT binary patch literal 62564 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 n4qFp3uuCU$m?z6|WHSCL{ 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/wechat_account.jpeg b/doc/jbootadmin/features/wechat_account.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..6a4dd9a24d42f1c0e026a18da8516d32b3c0c15b GIT binary patch literal 46435 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 av|a;Kempa*fieHC{)@@Ip7;G@@xK6gCu^$! literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/wechat_keyword.jpeg b/doc/jbootadmin/features/wechat_keyword.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..91fb3e2b9fb51e5a614c27ec8ca5a3b4a449c0ea GIT binary patch literal 52700 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 z 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 Date: Mon, 12 Jul 2021 13:34:25 +0800 Subject: [PATCH 1339/1965] update docs --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d31f16ce..4abb887c 100644 --- a/README.md +++ b/README.md @@ -60,13 +60,11 @@ public class Helloworld extends JbootController { ## JbootAdmin JbootAdmin 是 Jboot 官方推出的、收费的、企业级快速开发框架,真诚的为各位开发者提供一站式、保姆式的开发服务。 -关于 JbootAdmin 的功能详情或者演示,请咨询海哥(微信:wx198819880)。 +关于 JbootAdmin 的更多的功能请咨询海哥(微信:wx198819880),或请访问以下网址: + +[http://jboot.io/jbootadmin/feature.html](http://jboot.io/jbootadmin/feature.html) -![](./doc/jbootadmin/images/jbootadmin-demo.jpg) +![](./doc/jbootadmin/images/jbootadmin-demo.jpg) -更多关于 JbootAdmin: -- 福利1:https://mp.weixin.qq.com/s/UjaqF6v8yNufm7X5r4UFSw -- 福利2:https://mp.weixin.qq.com/s/7eZDyjxX3jD4QxgIWLfGBQ -- 福利3:https://mp.weixin.qq.com/s/reV48hBRkWY2c9O9Fi1TfA \ No newline at end of file -- Gitee From 5d5b5df6e827224c83a500e0cbb6038b99be8d17 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 13 Jul 2021 12:09:07 +0800 Subject: [PATCH 1340/1965] update docs --- simples/gateway/asserts/gateway-nacos.png | Bin 0 -> 86590 bytes simples/gateway/readme.md | 7 ++++++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 simples/gateway/asserts/gateway-nacos.png diff --git a/simples/gateway/asserts/gateway-nacos.png b/simples/gateway/asserts/gateway-nacos.png new file mode 100644 index 0000000000000000000000000000000000000000..aee2ab85ee588508cf6f8c4e6a5d9af06ef21bfd GIT binary patch literal 86590 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

        bd&TI;!G?^s(|l|6Sbq8{~M&dJ1F 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 H%Ygp_)7pTa literal 0 HcmV?d00001 diff --git a/simples/gateway/readme.md b/simples/gateway/readme.md index 34fb1de3..07692b4f 100644 --- a/simples/gateway/readme.md +++ b/simples/gateway/readme.md @@ -4,4 +4,9 @@ 把 Nacos 运行起来。 Nacos 启动文档: -https://nacos.io/zh-cn/docs/quick-start.html \ No newline at end of file +https://nacos.io/zh-cn/docs/quick-start.html + + +## 架构图 + +![](./asserts/gateway-nacos.png) \ No newline at end of file -- Gitee From 2156b7b63d98191ecfed47db459d8834b4e023c3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 13 Jul 2021 15:08:33 +0800 Subject: [PATCH 1341/1965] v3.10.4 snapshot --- pom.xml | 2 +- src/main/java/io/jboot/db/ArpManager.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index ee977c22..06becfc4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.10.3 + 3.10.4-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/db/ArpManager.java b/src/main/java/io/jboot/db/ArpManager.java index 8e9649d6..3a663f40 100644 --- a/src/main/java/io/jboot/db/ArpManager.java +++ b/src/main/java/io/jboot/db/ArpManager.java @@ -123,7 +123,7 @@ public class ArpManager { activeRecordPlugin.setCache(jbootCache); } - configSqlTemplate(config, activeRecordPlugin); + configSqlTemplate(activeRecordPlugin, config); configDialect(activeRecordPlugin, config); /** @@ -188,7 +188,7 @@ public class ArpManager { * @param datasourceConfig * @param activeRecordPlugin */ - private void configSqlTemplate(DataSourceConfig datasourceConfig, ActiveRecordPlugin activeRecordPlugin) { + private void configSqlTemplate(ActiveRecordPlugin activeRecordPlugin, DataSourceConfig datasourceConfig) { String sqlTemplatePath = datasourceConfig.getSqlTemplatePath(); if (StrUtil.isNotBlank(sqlTemplatePath)) { activeRecordPlugin.setBaseSqlTemplatePath(sqlTemplatePath); -- Gitee From 1245d5cd083a3b6c8e6af12e120a23361c48d320 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 14 Jul 2021 11:13:46 +0800 Subject: [PATCH 1342/1965] upgrade jfinal-undertow version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 06becfc4..7bea9c54 100644 --- a/pom.xml +++ b/pom.xml @@ -44,7 +44,7 @@ 4.9.15 2020.4 2.5 - 3.1 + 3.2 3.3.0 4.0.3 2.12.4 -- Gitee From 57e017325e427c46d3f43a0a766985072ce3beb4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 14 Jul 2021 11:42:36 +0800 Subject: [PATCH 1343/1965] fixed bug, close #I40KIZ --- src/main/java/io/jboot/apidoc/ApiDocUtil.java | 44 ++++++++++++++++--- .../java/io/jboot/apidoc/ApiDocument.java | 6 +++ .../listener/JbootAppListenerManager.java | 5 ++- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/apidoc/ApiDocUtil.java b/src/main/java/io/jboot/apidoc/ApiDocUtil.java index 9b1b881b..6d68ed63 100644 --- a/src/main/java/io/jboot/apidoc/ApiDocUtil.java +++ b/src/main/java/io/jboot/apidoc/ApiDocUtil.java @@ -17,20 +17,20 @@ package io.jboot.apidoc; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.parser.Feature; +import com.jfinal.config.Routes; import com.jfinal.core.ActionKey; import com.jfinal.core.Path; import com.jfinal.kit.StrKit; import io.jboot.apidoc.annotation.ApiResp; import io.jboot.apidoc.annotation.ApiResps; +import io.jboot.core.listener.JbootAppListener; +import io.jboot.core.listener.JbootAppListenerManager; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.StrUtil; import io.jboot.web.controller.annotation.*; import java.lang.reflect.Method; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; +import java.util.*; public class ApiDocUtil { @@ -55,7 +55,41 @@ public class ApiDocUtil { if (gm != null) { return AnnotationUtil.get(gm.value()); } - return null; + + + return tryToGetInAppListener(controllerClass); + } + + private static Map, String> controllerPathMap = null; + + private static String tryToGetInAppListener(Class controllerClass) { + + if (controllerPathMap != null) { + controllerPathMap.get(controllerClass); + } + + + List listeners = JbootAppListenerManager.me().getListeners(); + if (listeners == null || listeners.isEmpty()) { + return null; + } + + controllerPathMap = new HashMap<>(); + Routes baseRoutes = new Routes() { + @Override + public void config() { + } + }; + + listeners.forEach(jbootAppListener -> jbootAppListener.onRouteConfig(baseRoutes)); + List routeItemList = baseRoutes.getRouteItemList(); + + List routesList = Routes.getRoutesList(); + routesList.forEach(routes -> routeItemList.addAll(routes.getRouteItemList())); + + routeItemList.forEach(route -> controllerPathMap.put(route.getControllerClass(), route.getControllerPath())); + + return controllerPathMap.get(controllerClass); } diff --git a/src/main/java/io/jboot/apidoc/ApiDocument.java b/src/main/java/io/jboot/apidoc/ApiDocument.java index 192ac168..2f331c90 100644 --- a/src/main/java/io/jboot/apidoc/ApiDocument.java +++ b/src/main/java/io/jboot/apidoc/ApiDocument.java @@ -15,6 +15,8 @@ */ package io.jboot.apidoc; +import io.jboot.utils.StrUtil; + import java.io.Serializable; import java.util.LinkedList; import java.util.List; @@ -57,6 +59,10 @@ public class ApiDocument implements Serializable { } public void setFilePathByControllerPath(String controllerPath) { + if (controllerPath == null || StrUtil.isBlank(controllerPath)) { + throw new IllegalArgumentException("The request mapping path of Controller \"" + getControllerClass().getName() + "\" is empty."); + } + if ("/".equals(controllerPath)) { controllerPath = "index"; } else if (controllerPath.startsWith("/")) { diff --git a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java index b8d93685..b8bfd9ea 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java @@ -44,7 +44,7 @@ public class JbootAppListenerManager implements JbootAppListener { } - List listeners = new ArrayList<>(); + private List listeners = new ArrayList<>(); private JbootAppListenerManager() { @@ -75,6 +75,9 @@ public class JbootAppListenerManager implements JbootAppListener { WeightUtil.sort(listeners); } + public List getListeners() { + return listeners; + } @Override public void onInit() { -- Gitee From d8f93421d8b8c8c5372ce0c4597280db3c12283a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 14 Jul 2021 12:04:51 +0800 Subject: [PATCH 1344/1965] fixed bug, close #I40KIZ --- src/main/java/io/jboot/apidoc/ApiDocUtil.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/apidoc/ApiDocUtil.java b/src/main/java/io/jboot/apidoc/ApiDocUtil.java index 6d68ed63..8251d2ef 100644 --- a/src/main/java/io/jboot/apidoc/ApiDocUtil.java +++ b/src/main/java/io/jboot/apidoc/ApiDocUtil.java @@ -65,16 +65,17 @@ public class ApiDocUtil { private static String tryToGetInAppListener(Class controllerClass) { if (controllerPathMap != null) { - controllerPathMap.get(controllerClass); + return controllerPathMap.get(controllerClass); + } else { + controllerPathMap = new HashMap<>(); } - List listeners = JbootAppListenerManager.me().getListeners(); if (listeners == null || listeners.isEmpty()) { return null; } - controllerPathMap = new HashMap<>(); + Routes baseRoutes = new Routes() { @Override public void config() { @@ -82,12 +83,15 @@ public class ApiDocUtil { }; listeners.forEach(jbootAppListener -> jbootAppListener.onRouteConfig(baseRoutes)); - List routeItemList = baseRoutes.getRouteItemList(); - List routesList = Routes.getRoutesList(); - routesList.forEach(routes -> routeItemList.addAll(routes.getRouteItemList())); + //base Routes + baseRoutes.getRouteItemList().forEach(route -> controllerPathMap.put(route.getControllerClass(), route.getControllerPath())); + + + //all child routes + Routes.getRoutesList().forEach(routes -> routes.getRouteItemList() + .forEach(route -> controllerPathMap.put(route.getControllerClass(), route.getControllerPath()))); - routeItemList.forEach(route -> controllerPathMap.put(route.getControllerClass(), route.getControllerPath())); return controllerPathMap.get(controllerClass); } -- Gitee From 723d74438947b3d40d02a61c78969cfd2147a6e0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 14 Jul 2021 12:48:28 +0800 Subject: [PATCH 1345/1965] fixed bug, close #I40KIZ --- src/main/java/io/jboot/apidoc/ApiDocUtil.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/apidoc/ApiDocUtil.java b/src/main/java/io/jboot/apidoc/ApiDocUtil.java index 8251d2ef..287e82a2 100644 --- a/src/main/java/io/jboot/apidoc/ApiDocUtil.java +++ b/src/main/java/io/jboot/apidoc/ApiDocUtil.java @@ -80,6 +80,15 @@ public class ApiDocUtil { @Override public void config() { } + + @Override + public Routes add(Routes childRoutes) { + childRoutes.config(); + //all child routes + childRoutes.getRouteItemList() + .forEach(route -> controllerPathMap.put(route.getControllerClass(), route.getControllerPath())); + return this; + } }; listeners.forEach(jbootAppListener -> jbootAppListener.onRouteConfig(baseRoutes)); @@ -87,12 +96,6 @@ public class ApiDocUtil { //base Routes baseRoutes.getRouteItemList().forEach(route -> controllerPathMap.put(route.getControllerClass(), route.getControllerPath())); - - //all child routes - Routes.getRoutesList().forEach(routes -> routes.getRouteItemList() - .forEach(route -> controllerPathMap.put(route.getControllerClass(), route.getControllerPath()))); - - return controllerPathMap.get(controllerClass); } -- Gitee From f20d659e4732d8782bdd7f45ae23ef923da5ff59 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 15 Jul 2021 15:51:42 +0800 Subject: [PATCH 1346/1965] add JbootActionReporter.colorRenderEnable config --- .../jboot/web/handler/JbootActionReporter.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index 08c52116..edc68724 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -54,6 +54,7 @@ public class JbootActionReporter { private static Writer writer = new SystemOutWriter(); private static ActionReporter actionReporter = JFinal.me().getConstants().getActionReporter(); private static boolean reportEnable = Jboot.isDevMode(); + private static boolean colorRenderEnable = true; private static final ThreadLocal sdf = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); @@ -84,6 +85,14 @@ public class JbootActionReporter { JbootActionReporter.reportEnable = reportEnable; } + public static boolean isColorRenderEnable() { + return colorRenderEnable; + } + + public static void setColorRenderEnable(boolean colorRenderEnable) { + JbootActionReporter.colorRenderEnable = colorRenderEnable; + } + /** * Report the action */ @@ -120,9 +129,9 @@ public class JbootActionReporter { Class cc = action.getMethod().getDeclaringClass(); sb.append("Controller : ").append(cc.getName()).append(".(").append(getClassFileName(cc)).append(".java:" + lineNumber + ")"); if (JbootActionReporterInvocation.isControllerInvoked()) { - sb.append(ConsoleColor.GREEN_BRIGHT + " ---> invoked √" + ConsoleColor.RESET); + sb.append((colorRenderEnable ? ConsoleColor.GREEN_BRIGHT : "") + " ---> invoked √" + (colorRenderEnable ? ConsoleColor.RESET : "")); } else { - sb.append(ConsoleColor.RED_BRIGHT + " ---> skipped ×" + ConsoleColor.RESET); + sb.append((colorRenderEnable ? ConsoleColor.RED_BRIGHT : "") + " ---> skipped ×" + (colorRenderEnable ? ConsoleColor.RESET : "")); } sb.append("\nMethod : ").append(JbootActionReporterUtil.getMethodString(action.getMethod())).append("\n"); @@ -156,9 +165,9 @@ public class JbootActionReporter { sb.append(icMethod.getDeclaringClass().getName()).append(".(").append(getClassFileName(interClass)).append(".java:" + icLineNumber + ")"); if (invokedInterceptors.contains(inter)) { - sb.append(ConsoleColor.GREEN_BRIGHT + " ---> invoked √" + ConsoleColor.RESET); + sb.append((colorRenderEnable ? ConsoleColor.GREEN_BRIGHT : "") + " ---> invoked √" + (colorRenderEnable ? ConsoleColor.RESET : "")); } else { - sb.append(ConsoleColor.RED_BRIGHT + " ---> skipped ×" + ConsoleColor.RESET); + sb.append((colorRenderEnable ? ConsoleColor.RED_BRIGHT : "") + " ---> skipped ×" + (colorRenderEnable ? ConsoleColor.RESET : "")); } } sb.append("\n"); -- Gitee From 5ffe95e9108b6034097435e74d95448ff3cabd47 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 19 Jul 2021 09:28:25 +0800 Subject: [PATCH 1347/1965] v3.10.4 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/gateway/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 4abb887c..6e08a4c6 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.10.3 + 3.10.4 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 48d5a063..3281a02e 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.10.3 + 3.10.4 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 42ec09e8..ba24142d 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.10.3 + 3.10.4 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index e9762c5a..13db0214 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.10.3 + 3.10.4 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.10.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.10.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 875a7cd6..522504db 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.10.3', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.10.4', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.10.3/jboot-3.10.3.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.10.4/jboot-3.10.4.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/gateway/pom.xml b/simples/gateway/pom.xml index 0e56e6e1..c4c8c083 100644 --- a/simples/gateway/pom.xml +++ b/simples/gateway/pom.xml @@ -25,7 +25,7 @@ io.jboot jboot - 3.10.3-SNAPSHOT + 3.10.4 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index cadc690b..ac2468b5 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.10.3"; + public static String VERSION = "3.10.4"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 322b7cbe55b4f8cb4f43ee0a2df3c68da61b76c4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 19 Jul 2021 09:28:48 +0800 Subject: [PATCH 1348/1965] v3.10.4 release (^.^)YYa!! --- changes.txt | 7 +++++++ pom.xml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index c5f57db8..e665ca5d 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.10.4: +优化:升级 JFinal-Weixin 到最新版本 +优化:添加 JbootActionReporter.colorRenderEnable 的配置 +修复:通过在 JFinal Routes 配置的 Controller,APIDoc 无法正常生成的问题 + + + jboot v3.10.3: 新增:门户网关 Gateway 新增基于 Nacos 的自动服务发现的功能 新增:JbootHttpImpl 新增默认的 Content-Type 配置 diff --git a/pom.xml b/pom.xml index 7bea9c54..bafb260a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.10.4-SNAPSHOT + 3.10.4 jar jboot -- Gitee From c1e7457a8eb46203be050d3f42dc1f953635f7b6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 20 Jul 2021 08:51:48 +0800 Subject: [PATCH 1349/1965] v3.10.5 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bafb260a..b142f7c6 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.10.4 + 3.10.5-SNAPSHOT jar jboot -- Gitee From 4c4e6def2cfc1aad53251bb59dbd78f7d42e20e9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 20 Jul 2021 08:54:07 +0800 Subject: [PATCH 1350/1965] fixed JbootResourceLoader bugs --- src/main/java/io/jboot/app/JbootResourceLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/app/JbootResourceLoader.java b/src/main/java/io/jboot/app/JbootResourceLoader.java index 0f816717..29a19f8f 100644 --- a/src/main/java/io/jboot/app/JbootResourceLoader.java +++ b/src/main/java/io/jboot/app/JbootResourceLoader.java @@ -53,7 +53,7 @@ public class JbootResourceLoader { String classPath = url.toURI().getPath(); File srcRootPath = new File(classPath, "../..").getCanonicalFile(); - if (new File(srcRootPath.getParent(), "pom.xml").exists()) { + while (new File(srcRootPath.getParent(), "pom.xml").exists()) { srcRootPath = srcRootPath.getParentFile(); } -- Gitee From b88d247351b4f6109a698597cd31a677f9b08276 Mon Sep 17 00:00:00 2001 From: orangej Date: Tue, 20 Jul 2021 14:14:20 +0800 Subject: [PATCH 1351/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9Aredis?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E5=A2=9E=E5=8A=A0globalKeyPrefix=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=EF=BC=8C=E8=A7=A3=E5=86=B3=E5=A4=9A=E4=B8=AA=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E5=85=B1=E7=94=A8=E4=B8=80=E4=B8=AAredis=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B=E6=97=B6=E7=9A=84key=E5=86=B2=E7=AA=81=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cache/redis/JbootRedisCacheConfig.java | 13 ++++++++++- .../cache/redis/JbootRedisCacheImpl.java | 23 +++++++++++++++---- .../test/cache/redis/RedisCacheTester.java | 1 + 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheConfig.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheConfig.java index b5d2cbc2..25913474 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheConfig.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -25,5 +25,16 @@ import io.jboot.support.redis.JbootRedisConfig; @ConfigModel(prefix = "jboot.cache.redis") public class JbootRedisCacheConfig extends JbootRedisConfig { + /** + * 全局的key前缀,所有缓存的key都会自动添加该前缀 + */ + private String globalKeyPrefix; + public String getGlobalKeyPrefix() { + return globalKeyPrefix; + } + + public void setGlobalKeyPrefix(String globalKeyPrefix) { + this.globalKeyPrefix = globalKeyPrefix; + } } diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java index f2985523..b5efbaba 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java @@ -22,6 +22,7 @@ import io.jboot.support.redis.JbootRedisManager; import io.jboot.components.cache.JbootCacheBase; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.support.redis.RedisScanResult; +import io.jboot.utils.StrUtil; import java.util.*; @@ -30,13 +31,18 @@ public class JbootRedisCacheImpl extends JbootCacheBase { private JbootRedis redis; - private static final String redisCacheNamesKey = "jboot_cache_names"; + private JbootRedisCacheConfig cacheConfig; + private static String redisCacheNamesKey = "jboot_cache_names"; public JbootRedisCacheImpl() { - JbootRedisCacheConfig redisConfig = Jboot.config(JbootRedisCacheConfig.class); - if (redisConfig.isConfigOk()) { - redis = JbootRedisManager.me().getRedis(redisConfig); + cacheConfig = Jboot.config(JbootRedisCacheConfig.class); + if (StrUtil.isNotBlank(cacheConfig.getGlobalKeyPrefix())) { + redisCacheNamesKey = cacheConfig.getGlobalKeyPrefix() + ":" + redisCacheNamesKey; + } + + if (cacheConfig.isConfigOk()) { + redis = JbootRedisManager.me().getRedis(cacheConfig); } else { redis = Jboot.getRedis(); } @@ -119,7 +125,14 @@ public class JbootRedisCacheImpl extends JbootCacheBase { private String buildKey(String cacheName, Object key) { - StringBuilder keyBuilder = new StringBuilder(cacheName).append(":"); + StringBuilder keyBuilder = new StringBuilder(); + + if (StrUtil.isNotBlank(cacheConfig.getGlobalKeyPrefix())) { + keyBuilder.append(cacheConfig.getGlobalKeyPrefix()) + .append(":"); + } + + keyBuilder.append(cacheName).append(":"); if (key instanceof String) { keyBuilder.append("S"); } else if (key instanceof Number) { diff --git a/src/test/java/io/jboot/test/cache/redis/RedisCacheTester.java b/src/test/java/io/jboot/test/cache/redis/RedisCacheTester.java index aeb5068e..0b8b9dc3 100644 --- a/src/test/java/io/jboot/test/cache/redis/RedisCacheTester.java +++ b/src/test/java/io/jboot/test/cache/redis/RedisCacheTester.java @@ -15,6 +15,7 @@ public class RedisCacheTester { JbootApplication.setBootArg("jboot.cache.type", "redis"); JbootApplication.setBootArg("jboot.cache.redis.host", "127.0.0.1"); JbootApplication.setBootArg("jboot.cache.redis.port", "6379"); + JbootApplication.setBootArg("jboot.cache.redis.globalKeyPrefix", "myapp"); } @Test -- Gitee From 9ed85a0fc090d0eb3532feecc2778848660afef5 Mon Sep 17 00:00:00 2001 From: orangej Date: Tue, 20 Jul 2021 14:25:17 +0800 Subject: [PATCH 1352/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9A=E4=BF=9D?= =?UTF-8?q?=E5=AD=98globalKeyPrefix=EF=BC=8C=E4=BC=98=E5=8C=96buildKey?= =?UTF-8?q?=E5=AF=B9globalKeyPrefix=E7=9A=84=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/cache/redis/JbootRedisCacheImpl.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java index b5efbaba..6d4cbbb6 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java @@ -33,12 +33,14 @@ public class JbootRedisCacheImpl extends JbootCacheBase { private JbootRedis redis; private JbootRedisCacheConfig cacheConfig; private static String redisCacheNamesKey = "jboot_cache_names"; + private String globalKeyPrefix = ""; public JbootRedisCacheImpl() { cacheConfig = Jboot.config(JbootRedisCacheConfig.class); if (StrUtil.isNotBlank(cacheConfig.getGlobalKeyPrefix())) { - redisCacheNamesKey = cacheConfig.getGlobalKeyPrefix() + ":" + redisCacheNamesKey; + globalKeyPrefix = cacheConfig.getGlobalKeyPrefix() + ":"; + redisCacheNamesKey = globalKeyPrefix + redisCacheNamesKey; } if (cacheConfig.isConfigOk()) { @@ -125,14 +127,9 @@ public class JbootRedisCacheImpl extends JbootCacheBase { private String buildKey(String cacheName, Object key) { - StringBuilder keyBuilder = new StringBuilder(); + StringBuilder keyBuilder = new StringBuilder(globalKeyPrefix) + .append(cacheName).append(":"); - if (StrUtil.isNotBlank(cacheConfig.getGlobalKeyPrefix())) { - keyBuilder.append(cacheConfig.getGlobalKeyPrefix()) - .append(":"); - } - - keyBuilder.append(cacheName).append(":"); if (key instanceof String) { keyBuilder.append("S"); } else if (key instanceof Number) { -- Gitee From 0972be297b82fed43f0879b338936c6fd349ae30 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 20 Jul 2021 16:39:14 +0800 Subject: [PATCH 1353/1965] update docs --- doc/docs/sentinel.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/docs/sentinel.md b/doc/docs/sentinel.md index edc5bd47..193edac6 100644 --- a/doc/docs/sentinel.md +++ b/doc/docs/sentinel.md @@ -97,21 +97,18 @@ https://github.com/alibaba/Sentinel/wiki/%E5%90%AF%E5%8A%A8%E9%85%8D%E7%BD%AE%E9 com.alibaba.csp sentinel-core ${sentinel.version} - provided com.alibaba.csp sentinel-cluster-client-default ${sentinel.version} - provided com.alibaba.csp sentinel-transport-simple-http ${sentinel.version} - provided ``` -- Gitee From c16c44f21f0320cb4bc1f43e565f41389e7ef1e1 Mon Sep 17 00:00:00 2001 From: orangej Date: Wed, 21 Jul 2021 21:26:28 +0800 Subject: [PATCH 1354/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E4=B8=BAJ?= =?UTF-8?q?bootRedis=E5=A2=9E=E5=8A=A0eval=E6=96=B9=E6=B3=95=EF=BC=8C?= =?UTF-8?q?=E7=94=A8=E4=BA=8E=E6=89=A7=E8=A1=8Clua=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/support/redis/JbootRedis.java | 1 + .../redis/jedis/JbootJedisClusterImpl.java | 8 ++++-- .../support/redis/jedis/JbootJedisImpl.java | 9 +++++++ .../redis/lettuce/JbootLettuceImpl.java | 5 ++++ .../redis/redisson/JbootRedissonImpl.java | 5 ++++ .../java/io/jboot/test/redis/RedisTester.java | 26 +++++++++++++++++++ 6 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/support/redis/JbootRedis.java b/src/main/java/io/jboot/support/redis/JbootRedis.java index 9d9f14b9..a6ec93f1 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedis.java +++ b/src/main/java/io/jboot/support/redis/JbootRedis.java @@ -659,6 +659,7 @@ public interface JbootRedis { public List valueListFromBytesList(Collection data); + Object eval(String script, int keyCount, String... params); } diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java index c43fc4bb..f68fb6c9 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java @@ -52,8 +52,8 @@ public class JbootJedisClusterImpl extends JbootRedisBase { if (timeout != null) { this.timeout = timeout; } - if(maxAttempts == null) { - maxAttempts = this.maxAttempts; + if (maxAttempts == null) { + maxAttempts = this.maxAttempts; } GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); @@ -1227,6 +1227,10 @@ public class JbootJedisClusterImpl extends JbootRedisBase { return new RedisScanResult<>(scanResult.getStringCursor(), scanResult.getResult()); } + @Override + public Object eval(String script, int keyCount, String... params) { + return jedisCluster.eval(script, keyCount, params); + } public JedisCluster getJedisCluster() { return jedisCluster; diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java index 9cf5a4a1..a7181b98 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java @@ -1563,6 +1563,15 @@ public class JbootJedisImpl extends JbootRedisBase { } } + @Override + public Object eval(String script, int keyCount, String... params) { + Jedis jedis = getJedis(); + try { + return jedis.eval(script, keyCount, params); + } finally { + returnResource(jedis); + } + } public Jedis getJedis() { try { diff --git a/src/main/java/io/jboot/support/redis/lettuce/JbootLettuceImpl.java b/src/main/java/io/jboot/support/redis/lettuce/JbootLettuceImpl.java index 830ad87a..653c336f 100644 --- a/src/main/java/io/jboot/support/redis/lettuce/JbootLettuceImpl.java +++ b/src/main/java/io/jboot/support/redis/lettuce/JbootLettuceImpl.java @@ -525,4 +525,9 @@ public class JbootLettuceImpl implements JbootRedis { public List valueListFromBytesList(Collection data) { return null; } + + @Override + public Object eval(String script, int keyCount, String... params) { + return null; + } } diff --git a/src/main/java/io/jboot/support/redis/redisson/JbootRedissonImpl.java b/src/main/java/io/jboot/support/redis/redisson/JbootRedissonImpl.java index 7c20d105..d7625904 100644 --- a/src/main/java/io/jboot/support/redis/redisson/JbootRedissonImpl.java +++ b/src/main/java/io/jboot/support/redis/redisson/JbootRedissonImpl.java @@ -514,4 +514,9 @@ public class JbootRedissonImpl implements JbootRedis { public List valueListFromBytesList(Collection data) { return null; } + + @Override + public Object eval(String script, int keyCount, String... params) { + return null; + } } diff --git a/src/test/java/io/jboot/test/redis/RedisTester.java b/src/test/java/io/jboot/test/redis/RedisTester.java index a5913738..e46940cc 100644 --- a/src/test/java/io/jboot/test/redis/RedisTester.java +++ b/src/test/java/io/jboot/test/redis/RedisTester.java @@ -4,12 +4,38 @@ import io.jboot.app.JbootApplication; import io.jboot.support.redis.JbootRedis; import io.jboot.support.redis.JbootRedisManager; import io.jboot.support.redis.RedisScanResult; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; import java.util.ArrayList; import java.util.List; public class RedisTester { + @Before + public void config() { + JbootApplication.setBootArg("jboot.redis.host", "127.0.0.1"); + JbootApplication.setBootArg("jboot.redis.port", "6379"); + } + + @Test + public void testGetAndSet() { + JbootRedis redis = JbootRedisManager.me().getRedis(); + String key = "JbootRedisValue"; + Assert.assertEquals("OK", redis.set(key, "10")); + Assert.assertEquals("10", redis.get(key)); + redis.del(key); + } + + @Test + public void testEval() { + JbootRedis redis = JbootRedisManager.me().getRedis(); + String response = (String) redis.eval("return KEYS[1]", 1, "key1"); + Assert.assertEquals("key1", response); + } + + public static void main(String[] args) { JbootApplication.setBootArg("jboot.redis.host", "127.0.0.1"); JbootApplication.setBootArg("jboot.redis.database", "3"); -- Gitee From 8827bb1d920897e1df9bd53b838a619290ca82f7 Mon Sep 17 00:00:00 2001 From: orangej Date: Wed, 21 Jul 2021 21:28:56 +0800 Subject: [PATCH 1355/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E9=80=9A?= =?UTF-8?q?=E8=BF=87redis=E5=AE=9E=E7=8E=B0=E9=9B=86=E7=BE=A4=E7=9A=84?= =?UTF-8?q?=E9=99=90=E6=AC=A1=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/components/limiter/LimitScope.java | 15 ++++++ .../limiter/annotation/EnableLimit.java | 7 +++ .../interceptor/BaseLimiterInterceptor.java | 12 +++++ .../interceptor/LimiterInterceptor.java | 10 +++- .../limiter/redis/RedisRateLimitUtil.java | 50 +++++++++++++++++++ .../jboot/test/aop/inject/AopController.java | 6 +++ .../java/io/jboot/test/redis/RedisTester.java | 8 +++ 7 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/jboot/components/limiter/LimitScope.java create mode 100644 src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java diff --git a/src/main/java/io/jboot/components/limiter/LimitScope.java b/src/main/java/io/jboot/components/limiter/LimitScope.java new file mode 100644 index 00000000..7e4dd25f --- /dev/null +++ b/src/main/java/io/jboot/components/limiter/LimitScope.java @@ -0,0 +1,15 @@ +package io.jboot.components.limiter; + +public enum LimitScope { + + /** + * 整个集群限次,多实例共享 + */ + CLUSTER, + + /** + * 每个实例单独限次 + */ + NODE; + +} diff --git a/src/main/java/io/jboot/components/limiter/annotation/EnableLimit.java b/src/main/java/io/jboot/components/limiter/annotation/EnableLimit.java index 582cf8a0..248069a1 100644 --- a/src/main/java/io/jboot/components/limiter/annotation/EnableLimit.java +++ b/src/main/java/io/jboot/components/limiter/annotation/EnableLimit.java @@ -15,6 +15,7 @@ */ package io.jboot.components.limiter.annotation; +import io.jboot.components.limiter.LimitScope; import io.jboot.components.limiter.LimitType; import java.lang.annotation.*; @@ -38,6 +39,12 @@ public @interface EnableLimit { */ String type() default LimitType.TOKEN_BUCKET; + + /** + * 作用域,默认为单节点本地限次 + */ + LimitScope scope() default LimitScope.NODE; + /** * 频率 * diff --git a/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java index 39fb0877..2ab9177d 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java @@ -19,6 +19,7 @@ package io.jboot.components.limiter.interceptor; import com.google.common.util.concurrent.RateLimiter; import com.jfinal.aop.Invocation; import io.jboot.components.limiter.LimiterManager; +import io.jboot.components.limiter.redis.RedisRateLimitUtil; import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; @@ -60,6 +61,17 @@ public abstract class BaseLimiterInterceptor { } } + protected void doInterceptForTokenBucketWithCluster(int rate, String resource, String fallback, Invocation inv) { + //允许通行 + if (RedisRateLimitUtil.tryAcquire(resource, rate)) { + inv.invoke(); + } + //不允许通行 + else { + doExecFallback(resource, fallback, inv); + } + } + protected void doExecFallback(String resource, String fallback, Invocation inv) { LimiterManager.me().processFallback(resource, fallback, inv); } diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java index c400a53d..58defb32 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java @@ -18,6 +18,7 @@ package io.jboot.components.limiter.interceptor; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; +import io.jboot.components.limiter.LimitScope; import io.jboot.components.limiter.LimitType; import io.jboot.components.limiter.annotation.EnableLimit; import io.jboot.utils.AnnotationUtil; @@ -40,10 +41,17 @@ public class LimiterInterceptor extends BaseLimiterInterceptor implements Interc String type = AnnotationUtil.get(enableLimit.type()); switch (type) { case LimitType.CONCURRENCY: + if (LimitScope.CLUSTER == enableLimit.scope()) { + throw new IllegalArgumentException("Concurrency limit for cluster not implement!"); + } doInterceptForConcurrency(enableLimit.rate(), resource, enableLimit.fallback(), inv); break; case LimitType.TOKEN_BUCKET: - doInterceptForTokenBucket(enableLimit.rate(), resource, enableLimit.fallback(), inv); + if (LimitScope.CLUSTER == enableLimit.scope()) { + doInterceptForTokenBucketWithCluster(enableLimit.rate(), resource, enableLimit.fallback(), inv); + } else { + doInterceptForTokenBucket(enableLimit.rate(), resource, enableLimit.fallback(), inv); + } break; } } diff --git a/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java b/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java new file mode 100644 index 00000000..2152ce7d --- /dev/null +++ b/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java @@ -0,0 +1,50 @@ +package io.jboot.components.limiter.redis; + +import io.jboot.support.redis.JbootRedis; +import io.jboot.support.redis.JbootRedisManager; + +/** + * 通过lua脚本来进行限次 + */ +public class RedisRateLimitUtil { + + private static final String RATE_LIMIT_SCRIPT = "local c" + + "\nc = redis.call('get',KEYS[1])" + + // 调用量已经超过最大值,直接返回 + "\nif c and tonumber(c) > tonumber(ARGV[1]) then" + + "\nreturn tonumber(c);" + + "\nend" + + // 自增 + "\nc = redis.call('incr',KEYS[1])" + + "\nif tonumber(c) == 1 then" + + // 从第一次调用开始限流,设置对应键值的过期 + "\nredis.call('expire',KEYS[1],ARGV[2])" + + "\nend" + + "\nreturn c;"; + + private static JbootRedis redis; + + /** + * 限制时长默认为1秒 + */ + public static boolean tryAcquire(String resource, int rate) { + return tryAcquire(resource, rate, 1); + } + + /** + * 尝试是否能正常执行 + * + * @param resource 资源名 + * @param rate 限制次数 + * @param periodSeconds 限制时长,单位为秒 + * @return true 可以执行 + * false 限次,禁止 + */ + public static boolean tryAcquire(String resource, int rate, int periodSeconds) { + if (redis == null) { + redis = JbootRedisManager.me().getRedis(); + } + Long count = (Long) redis.eval(RATE_LIMIT_SCRIPT, 1, resource, String.valueOf(rate), String.valueOf(periodSeconds)); + return count <= rate; + } +} diff --git a/src/test/java/io/jboot/test/aop/inject/AopController.java b/src/test/java/io/jboot/test/aop/inject/AopController.java index 5026f256..4a234bae 100644 --- a/src/test/java/io/jboot/test/aop/inject/AopController.java +++ b/src/test/java/io/jboot/test/aop/inject/AopController.java @@ -2,6 +2,7 @@ package io.jboot.test.aop.inject; import com.jfinal.aop.Inject; import io.jboot.aop.annotation.ConfigValue; +import io.jboot.components.limiter.LimitScope; import io.jboot.components.limiter.annotation.EnableLimit; import io.jboot.test.aop.staticconstruct.StaticConstructManager; import io.jboot.web.controller.JbootController; @@ -36,6 +37,11 @@ public class AopController extends JbootController { renderText("host:" + host + " port:" + port + " xxx:" + xxx); } + @EnableLimit(rate = 1, fallback = "aaa", scope = LimitScope.CLUSTER) + public void bbb() { + renderText("host:" + host + " port:" + port + " xxx:" + xxx); + } + public void aaa(){ renderText("aaa"); } diff --git a/src/test/java/io/jboot/test/redis/RedisTester.java b/src/test/java/io/jboot/test/redis/RedisTester.java index e46940cc..45bb80db 100644 --- a/src/test/java/io/jboot/test/redis/RedisTester.java +++ b/src/test/java/io/jboot/test/redis/RedisTester.java @@ -1,6 +1,7 @@ package io.jboot.test.redis; import io.jboot.app.JbootApplication; +import io.jboot.components.limiter.redis.RedisRateLimitUtil; import io.jboot.support.redis.JbootRedis; import io.jboot.support.redis.JbootRedisManager; import io.jboot.support.redis.RedisScanResult; @@ -35,6 +36,13 @@ public class RedisTester { Assert.assertEquals("key1", response); } + @Test + public void testRateLimit() { + String resource = "limited-resource"; + Assert.assertTrue(RedisRateLimitUtil.tryAcquire(resource, 2, 1)); + Assert.assertTrue(RedisRateLimitUtil.tryAcquire(resource, 2, 1)); + Assert.assertFalse(RedisRateLimitUtil.tryAcquire(resource, 2, 1)); + } public static void main(String[] args) { JbootApplication.setBootArg("jboot.redis.host", "127.0.0.1"); -- Gitee From 1d6e2c9aebda98e25223ad1b4a155ce0f691ade8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 23 Jul 2021 09:21:55 +0800 Subject: [PATCH 1356/1965] optimize --- .../io/jboot/components/limiter/redis/RedisRateLimitUtil.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java b/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java index 2152ce7d..5e37dddf 100644 --- a/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java +++ b/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java @@ -1,5 +1,6 @@ package io.jboot.components.limiter.redis; +import io.jboot.exception.JbootIllegalConfigException; import io.jboot.support.redis.JbootRedis; import io.jboot.support.redis.JbootRedisManager; @@ -43,6 +44,9 @@ public class RedisRateLimitUtil { public static boolean tryAcquire(String resource, int rate, int periodSeconds) { if (redis == null) { redis = JbootRedisManager.me().getRedis(); + if (redis == null) { + throw new JbootIllegalConfigException("Redis config not well, can not use LimitScope.CLUSTER in @EnableLimit() "); + } } Long count = (Long) redis.eval(RATE_LIMIT_SCRIPT, 1, resource, String.valueOf(rate), String.valueOf(periodSeconds)); return count <= rate; -- Gitee From b5fa3bbadb5e3b1e05f1713427c09e9de9e140a2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 23 Jul 2021 09:23:50 +0800 Subject: [PATCH 1357/1965] optimize --- .../io/jboot/components/limiter/LimitScope.java | 15 +++++++++++++++ .../limiter/redis/RedisRateLimitUtil.java | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/main/java/io/jboot/components/limiter/LimitScope.java b/src/main/java/io/jboot/components/limiter/LimitScope.java index 7e4dd25f..ae8fed0c 100644 --- a/src/main/java/io/jboot/components/limiter/LimitScope.java +++ b/src/main/java/io/jboot/components/limiter/LimitScope.java @@ -1,3 +1,18 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.jboot.components.limiter; public enum LimitScope { diff --git a/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java b/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java index 5e37dddf..2850d171 100644 --- a/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java +++ b/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java @@ -1,3 +1,18 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.jboot.components.limiter.redis; import io.jboot.exception.JbootIllegalConfigException; -- Gitee From 672fbf981a0298b9a5448b3985a21dd1855e4dbc Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 23 Jul 2021 09:30:18 +0800 Subject: [PATCH 1358/1965] v3.10.5 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/gateway/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6e08a4c6..49420f8e 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.10.4 + 3.10.5 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 3281a02e..b910c5e7 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.10.4 + 3.10.5 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index ba24142d..081fd1b8 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.10.4 + 3.10.5 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 13db0214..5108836a 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.10.4 + 3.10.5 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.10.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.10.5', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 522504db..f7513e30 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.10.4', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.10.5', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.10.4/jboot-3.10.4.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.10.5/jboot-3.10.5.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/gateway/pom.xml b/simples/gateway/pom.xml index c4c8c083..47a28142 100644 --- a/simples/gateway/pom.xml +++ b/simples/gateway/pom.xml @@ -25,7 +25,7 @@ io.jboot jboot - 3.10.4 + 3.10.5 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index ac2468b5..a998abd3 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.10.4"; + public static String VERSION = "3.10.5"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From ae7e4cfdf5c665172626019e592f6d4fed10deef Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 23 Jul 2021 09:30:30 +0800 Subject: [PATCH 1359/1965] v3.10.5 release (^.^)YYa!! --- changes.txt | 9 +++++++++ pom.xml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index e665ca5d..7bd3f4a0 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,12 @@ +jboot v3.10.5: +新增:@EnableLimit() 注解新增通过 redis 实现集群的限流的功能,感谢 @orangeJ +新增:为 JbootRedis 增加 eval 方法,用于执行 lua 脚本,感谢 @orangeJ +新增:redis 缓存增加 globalKeyPrefix 配置,解决多个应用共用一个 redis 实例时的 key 冲突问题,感谢 @orangeJ +修复:JbootResourceLoader 在 Maven 二级目录下启动,无法扫描上级资源文件的问题 +修复:Sentinel 文档错误的问题 + + + jboot v3.10.4: 优化:升级 JFinal-Weixin 到最新版本 优化:添加 JbootActionReporter.colorRenderEnable 的配置 diff --git a/pom.xml b/pom.xml index b142f7c6..eceb635a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.10.5-SNAPSHOT + 3.10.5 jar jboot -- Gitee From 6537794d1afa162fd6ddba1deb5747da2df95cbf Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 29 Jul 2021 16:42:33 +0800 Subject: [PATCH 1360/1965] v3.10.6 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eceb635a..440f7e2d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.10.5 + 3.10.6-SNAPSHOT jar jboot -- Gitee From 84a99ac97069f3bd14a9e234cc2d2c220233f96d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 29 Jul 2021 17:01:16 +0800 Subject: [PATCH 1361/1965] optimize ClassScanner --- .../java/io/jboot/utils/ClassScanner.java | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index f419db12..e380a073 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -53,7 +53,7 @@ public class ClassScanner { public static void addScanJarPrefix(String prefix) { - scanJars.add(prefix.trim()); + scanJars.add(prefix.toLowerCase().trim()); } static { @@ -62,7 +62,7 @@ public class ClassScanner { public static void addUnscanJarPrefix(String prefix) { - excludeJars.add(prefix.trim()); + excludeJars.add(prefix.toLowerCase().trim()); } static { @@ -276,8 +276,24 @@ public class ClassScanner { excludeJars.add("redisson-"); excludeJars.add("bcprov-"); excludeJars.add("pay-java-"); + excludeJars.add("alipay-sdk-"); excludeJars.add("mapper-extras-"); excludeJars.add("org.jacoco"); + excludeJars.add("jxl-"); + excludeJars.add("jxls-"); + excludeJars.add("jstl-"); + excludeJars.add("batik-"); + excludeJars.add("xmlsec-"); + excludeJars.add("pdfbox-"); + excludeJars.add("fontbox-"); + excludeJars.add("xk-time-"); + excludeJars.add("geohash-"); + excludeJars.add("ezmorph-"); + excludeJars.add("async-http-"); + excludeJars.add("jsr-"); + excludeJars.add("jsr250"); + excludeJars.add("pinyin4j"); + excludeJars.add("ijpay-"); } @@ -356,6 +372,7 @@ public class ClassScanner { excludeClasses.add("com.mysql."); excludeClasses.add("com.papertrail."); excludeClasses.add("com.egzosn."); + excludeClasses.add("com.alipay.api"); excludeClasses.add("org.gjt."); excludeClasses.add("org.fusesource."); excludeClasses.add("org.redisson."); @@ -379,11 +396,15 @@ public class ClassScanner { excludeClasses.add("freemarker."); excludeClasses.add("com.twelvemonkeys."); excludeClasses.add("eu.bitwalker."); + excludeClasses.add("jstl."); + excludeClasses.add("jxl."); + excludeClasses.add("org.jxls"); + excludeClasses.add("org.kordamp"); } public static void addScanClassPrefix(String prefix) { - scanClasses.add(prefix.trim()); + scanClasses.add(prefix.toLowerCase().trim()); } static { -- Gitee From 7254ec37491b9655b406a7197fd586db700cb6d0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 30 Jul 2021 17:17:21 +0800 Subject: [PATCH 1362/1965] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=E5=BD=93?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=20@Api(collect=3D{})=20=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E5=AD=90=20Controller=20=E8=B7=AF=E5=BE=84=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/apidoc/ApiDocManager.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/apidoc/ApiDocManager.java b/src/main/java/io/jboot/apidoc/ApiDocManager.java index c569974c..e643c2cb 100644 --- a/src/main/java/io/jboot/apidoc/ApiDocManager.java +++ b/src/main/java/io/jboot/apidoc/ApiDocManager.java @@ -339,6 +339,7 @@ public class ApiDocManager { List apiDocuments = new ArrayList<>(); + //所有 Controller 构建为同一个文档 if (config.isAllInOneEnable()) { ApiDocument document = new ApiDocument(); document.setValue(config.getAllInOneTitle()); @@ -348,7 +349,9 @@ public class ApiDocManager { buildAllInOneDocument(document, controllerClasses, config); apiDocuments.add(document); - } else { + } + //不同的 Controller 构建为不同的文档 + else { for (Class controllerClass : controllerClasses) { if (StrUtil.isNotBlank(config.getPackagePrefix())) { if (controllerClass.getName().startsWith(config.getPackagePrefix())) { @@ -504,6 +507,9 @@ public class ApiDocManager { List collectMethods = ReflectUtil.searchMethodList(collectClass, method -> method.getAnnotation(ApiOper.class) != null && Modifier.isPublic(method.getModifiers())); + String collectControllerPath = ApiDocUtil.getControllerPath(collectClass); + HttpMethod collectDefaultHttpMethod = ApiDocUtil.getControllerMethod(controllerClass); + for (Method method : collectMethods) { ApiOper apiOper = method.getAnnotation(ApiOper.class); @@ -511,7 +517,7 @@ public class ApiDocManager { apiOperation.setControllerClass(collectClass); Class containerClass = apiOper.containerClass() != void.class ? apiOper.containerClass() : config.getDefaultContainerClass(); - apiOperation.setMethodAndInfo(method, controllerPath, ApiDocUtil.getMethodHttpMethods(method, defaultHttpMethod), containerClass); + apiOperation.setMethodAndInfo(method, collectControllerPath, ApiDocUtil.getMethodHttpMethods(method, collectDefaultHttpMethod), containerClass); apiOperation.setValue(apiOper.value()); -- Gitee From e0bdaf6fc409832d604705756523b2d4d2c63d3d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 30 Jul 2021 18:00:54 +0800 Subject: [PATCH 1363/1965] v3.10.6 release (^.^)YYa!! --- .../java/io/jboot/apidoc/ApiDocConfig.java | 4 +- .../jboot/test/apidoc/CollectController.java | 38 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 src/test/java/io/jboot/test/apidoc/CollectController.java diff --git a/src/main/java/io/jboot/apidoc/ApiDocConfig.java b/src/main/java/io/jboot/apidoc/ApiDocConfig.java index c019896c..915cbe60 100644 --- a/src/main/java/io/jboot/apidoc/ApiDocConfig.java +++ b/src/main/java/io/jboot/apidoc/ApiDocConfig.java @@ -23,7 +23,7 @@ import java.io.File; public class ApiDocConfig { - private String basePath = ""; + private String basePath = "apidoc"; private String packagePrefix; @@ -46,7 +46,7 @@ public class ApiDocConfig { if (FileUtil.isAbsolutePath(basePath)) { return basePath; } - return new File(PathKit.getRootClassPath(), "../../" + basePath).getAbsolutePath(); + return FileUtil.getCanonicalPath(new File(PathKit.getRootClassPath(), "../../" + basePath)); } public void setBasePath(String basePath) { diff --git a/src/test/java/io/jboot/test/apidoc/CollectController.java b/src/test/java/io/jboot/test/apidoc/CollectController.java new file mode 100644 index 00000000..00e9606a --- /dev/null +++ b/src/test/java/io/jboot/test/apidoc/CollectController.java @@ -0,0 +1,38 @@ +package io.jboot.test.apidoc; + +import com.jfinal.plugin.activerecord.Page; +import io.jboot.apidoc.ApiRet; +import io.jboot.apidoc.annotation.Api; +import io.jboot.apidoc.annotation.ApiOper; +import io.jboot.web.controller.annotation.RequestMapping; + +import java.util.List; +import java.util.Map; + +@RequestMapping("/apidoc/ccc") +@Api(value = "CollectController",collect = {APIController.class,APIController2.class}) +public class CollectController extends BaseController { + + @ApiOper("index") + public List index(List list) { + return null; + } + + @ApiOper("users") + public ApiRet> users(Map map) { + return null; + } + + @ApiOper("index2") + public String index2(long str) { + return null; + } + + + @ApiOper("index3") + public void index3() { + return; + } + + +} -- Gitee From 35e63498a853f5f756ce91b0af4ae28db11e1cc7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 30 Jul 2021 18:04:48 +0800 Subject: [PATCH 1364/1965] v3.10.6 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 49420f8e..18efbf0f 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.10.5 + 3.10.6 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index b910c5e7..426423cd 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.10.5 + 3.10.6 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 081fd1b8..e1a1bcdf 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.10.5 + 3.10.6 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 5108836a..cffb206d 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.10.5 + 3.10.6 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.10.5', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.10.6', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index f7513e30..9ee74b14 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.10.5', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.10.6', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.10.5/jboot-3.10.5.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.10.6/jboot-3.10.6.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index a998abd3..8d32f7e3 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.10.5"; + public static String VERSION = "3.10.6"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 347766dc1a3b222a7cca40d12237bddb9b8d21b3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 30 Jul 2021 18:04:53 +0800 Subject: [PATCH 1365/1965] v3.10.6 release (^.^)YYa!! --- changes.txt | 6 ++++++ pom.xml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 7bd3f4a0..762204fc 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.10.6: +修复:ClassScanner 添加排除的前缀时,如果有大写字母不生效的问题 +修复:当使用 @Api(collect={}) 时,子 Controller 路径错误的问题 + + + jboot v3.10.5: 新增:@EnableLimit() 注解新增通过 redis 实现集群的限流的功能,感谢 @orangeJ 新增:为 JbootRedis 增加 eval 方法,用于执行 lua 脚本,感谢 @orangeJ diff --git a/pom.xml b/pom.xml index 440f7e2d..55851f98 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.10.6-SNAPSHOT + 3.10.6 jar jboot -- Gitee From c48207dab6f0d102e9a741a03f12059db30ddc8b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 3 Aug 2021 11:04:16 +0800 Subject: [PATCH 1366/1965] v3.10.7 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 55851f98..d7fa18a1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.10.6 + 3.10.7-SNAPSHOT jar jboot -- Gitee From 66db8d2db767d4f78ec4d188a97f360aac85f964 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 3 Aug 2021 11:15:58 +0800 Subject: [PATCH 1367/1965] add JbootHttpRequest.sslContext config --- .../io/jboot/components/http/JbootHttpRequest.java | 12 ++++++++++++ .../jboot/components/http/jboot/JbootHttpImpl.java | 14 +++++++++++--- .../jboot/components/http/okhttp/OKHttpImpl.java | 10 ++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/components/http/JbootHttpRequest.java b/src/main/java/io/jboot/components/http/JbootHttpRequest.java index c347c9e8..2aab755f 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpRequest.java +++ b/src/main/java/io/jboot/components/http/JbootHttpRequest.java @@ -17,6 +17,7 @@ package io.jboot.components.http; import io.jboot.utils.StrUtil; +import javax.net.ssl.SSLContext; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -75,6 +76,9 @@ public class JbootHttpRequest { // 遇到重定向是否自动跟随 private boolean instanceFollowRedirects = true; + // 自定义 sslContext + private SSLContext sslContext; + public static JbootHttpRequest create(String url) { return new JbootHttpRequest(url); @@ -362,4 +366,12 @@ public class JbootHttpRequest { public void setInstanceFollowRedirects(boolean instanceFollowRedirects) { this.instanceFollowRedirects = instanceFollowRedirects; } + + public SSLContext getSslContext() { + return sslContext; + } + + public void setSslContext(SSLContext sslContext) { + this.sslContext = sslContext; + } } diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index 1e348e13..7db503ef 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -193,7 +193,7 @@ public class JbootHttpImpl implements JbootHttp { connection.setRequestMethod(request.getMethod()); //如果 reqeust 的 header 不配置 content-Type, 使用默认的 - connection.setRequestProperty("Content-Type",request.getContentType()); + connection.setRequestProperty("Content-Type", request.getContentType()); if (request.getHeaders() != null && request.getHeaders().size() > 0) { for (Map.Entry entry : request.getHeaders().entrySet()) { @@ -219,7 +219,13 @@ public class JbootHttpImpl implements JbootHttp { URL url = new URL(request.getRequestUrl()); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); - if (request.getCertPath() != null && request.getCertPass() != null) { + //自定义 sslContext + if (request.getSslContext() != null) { + SSLSocketFactory ssf = request.getSslContext().getSocketFactory(); + conn.setSSLSocketFactory(ssf); + } + //配置证书的路径和密码 + else if (request.getCertPath() != null && request.getCertPass() != null) { KeyStore clientStore = KeyStore.getInstance("PKCS12"); clientStore.load(request.getCertInputStream(), request.getCertPass().toCharArray()); @@ -237,7 +243,9 @@ public class JbootHttpImpl implements JbootHttp { conn.setSSLSocketFactory(sslContext.getSocketFactory()); - } else { + } + // 默认的 sslContext + else { conn.setHostnameVerifier(hnv); SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); if (sslContext != null) { diff --git a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java index 38c77870..0fc79da3 100644 --- a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java +++ b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java @@ -65,7 +65,7 @@ public class OKHttpImpl implements JbootHttp { } catch (Throwable ex) { response.setError(ex); - }finally { + } finally { response.close(); } } @@ -135,7 +135,13 @@ public class OKHttpImpl implements JbootHttp { public OkHttpClient getHttpsClient(JbootHttpRequest request) throws Exception { OkHttpClient.Builder builder = new OkHttpClient.Builder(); - if (request.getCertPath() != null && request.getCertPass() != null) { + //自定义 sslContext + if (request.getSslContext() != null) { + SSLSocketFactory ssf = request.getSslContext().getSocketFactory(); + builder.sslSocketFactory(ssf, trustAnyTrustManager); + } + //配置证书的路径和密码 + else if (request.getCertPath() != null && request.getCertPass() != null) { KeyStore clientStore = KeyStore.getInstance("PKCS12"); clientStore.load(request.getCertInputStream(), request.getCertPass().toCharArray()); -- Gitee From c3d065d1c1138919d6999a39c030e8f8260cafbc Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 3 Aug 2021 18:59:46 +0800 Subject: [PATCH 1368/1965] add JbootConfigManager.setBootProperties --- .../jboot/app/config/JbootConfigManager.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 53296418..8d062de3 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -21,6 +21,7 @@ import io.jboot.app.config.annotation.ConfigModel; import io.jboot.app.config.support.apollo.ApolloConfigManager; import io.jboot.app.config.support.nacos.NacosConfigManager; +import java.io.File; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -454,6 +455,25 @@ public class JbootConfigManager { argMap.put(key, value.toString()); } + public static void setBootProperties(Properties properties) { + Objects.requireNonNull(properties, "properties must not be null"); + properties.forEach((o, o2) -> setBootArg(o.toString(), o2)); + } + + + public static void setBootProperties(String propertiesFileName) { + setBootProperties(new JbootProp(propertiesFileName).getProperties()); + } + + + public static void setBootProperties(File propertiesFile) { + if (propertiesFile.exists()) { + setBootProperties(new JbootProp(propertiesFile).getProperties()); + } else { + System.err.println("Warning: properties file not exists: " + propertiesFile); + } + } + /** * 获取启动参数 * -- Gitee From b6a4713b855ba6eac5fbe84121ecf4076933552a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 3 Aug 2021 19:07:06 +0800 Subject: [PATCH 1369/1965] add JbootConfigManager.setBootProperties --- .../java/io/jboot/app/config/JbootConfigManager.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 8d062de3..f737a9e1 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -461,8 +461,13 @@ public class JbootConfigManager { } - public static void setBootProperties(String propertiesFileName) { - setBootProperties(new JbootProp(propertiesFileName).getProperties()); + public static void setBootProperties(String propertiesFilePath) { + File file = new File(propertiesFilePath); + if (file.exists()) { + setBootProperties(file); + } else { + setBootProperties(new JbootProp(propertiesFilePath).getProperties()); + } } -- Gitee From 5611ff8b54f21ac20eabffc355b726771209e3cf Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 8 Aug 2021 16:32:26 +0800 Subject: [PATCH 1370/1965] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20ReflectUtil.sear?= =?UTF-8?q?chFieldList()=20=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/core/JbootCoreConfig.java | 2 +- src/main/java/io/jboot/utils/ReflectUtil.java | 46 +++++++++++++++++-- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 813f44aa..dff2351b 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -131,7 +131,7 @@ public class JbootCoreConfig extends JFinalConfig { * 此方法的目的是为了防止 webRootPath 为 null */ private void initWebRootPath() { - String webRootPath = ReflectUtil.getFieldValue(PathKit.class, "webRootPath"); + String webRootPath = ReflectUtil.getStaticFieldValue(PathKit.class, "webRootPath"); if (webRootPath == null) { PathKit.setWebRootPath(PathKit.getRootClassPath()); } diff --git a/src/main/java/io/jboot/utils/ReflectUtil.java b/src/main/java/io/jboot/utils/ReflectUtil.java index 45faa648..5bb3f1a1 100644 --- a/src/main/java/io/jboot/utils/ReflectUtil.java +++ b/src/main/java/io/jboot/utils/ReflectUtil.java @@ -17,6 +17,8 @@ package io.jboot.utils; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.function.Predicate; @@ -26,7 +28,7 @@ import java.util.function.Predicate; */ public class ReflectUtil { - public static T getFieldValue(Class dClass, String fieldName) { + public static T getStaticFieldValue(Class dClass, String fieldName) { return getFieldValue(dClass, fieldName, null); } @@ -61,6 +63,35 @@ public class ReflectUtil { } + public static List searchFieldList(Class dClass, Predicate filter) { + List fields = new ArrayList<>(); + doSearchFieldList(fields, dClass, filter); + return fields; + } + + + private static void doSearchFieldList(List searchToList, Class dClass, Predicate filter) { + if (dClass == null || dClass == Object.class) { + return; + } + + Field[] fields = dClass.getDeclaredFields(); + if (fields.length > 0) { + if (filter != null) { + for (Field field : fields) { + if (filter.test(field)) { + searchToList.add(field); + } + } + } else { + searchToList.addAll(Arrays.asList(fields)); + } + } + + doSearchFieldList(searchToList, dClass.getSuperclass(), filter); + } + + public static Method searchMethod(Class dClass, Predicate filter) { if (dClass == null) { return null; @@ -86,11 +117,18 @@ public class ReflectUtil { return; } Method[] methods = dClass.getDeclaredMethods(); - for (Method method : methods) { - if (filter.test(method)) { - list.add(method); + if (methods.length > 0) { + if (filter != null) { + for (Method method : methods) { + if (filter.test(method)) { + list.add(method); + } + } + } else { + list.addAll(Arrays.asList(methods)); } } + doSearchMethodList(dClass.getSuperclass(), filter, list); } } -- Gitee From 8fa85078e845a6aaa2ff57c0de9b4228ea6a4d6f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 8 Aug 2021 16:34:55 +0800 Subject: [PATCH 1371/1965] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20ReflectUtil.sear?= =?UTF-8?q?chFieldList()=20=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/utils/ReflectUtil.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/utils/ReflectUtil.java b/src/main/java/io/jboot/utils/ReflectUtil.java index 5bb3f1a1..3e055c40 100644 --- a/src/main/java/io/jboot/utils/ReflectUtil.java +++ b/src/main/java/io/jboot/utils/ReflectUtil.java @@ -17,7 +17,6 @@ package io.jboot.utils; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -64,13 +63,13 @@ public class ReflectUtil { public static List searchFieldList(Class dClass, Predicate filter) { - List fields = new ArrayList<>(); - doSearchFieldList(fields, dClass, filter); + List fields = new LinkedList<>(); + doSearchFieldList(dClass, filter, fields); return fields; } - private static void doSearchFieldList(List searchToList, Class dClass, Predicate filter) { + private static void doSearchFieldList(Class dClass, Predicate filter, List searchToList) { if (dClass == null || dClass == Object.class) { return; } @@ -88,7 +87,7 @@ public class ReflectUtil { } } - doSearchFieldList(searchToList, dClass.getSuperclass(), filter); + doSearchFieldList(dClass.getSuperclass(), filter, searchToList); } @@ -112,7 +111,8 @@ public class ReflectUtil { return methods; } - private static void doSearchMethodList(Class dClass, Predicate filter, List list) { + + private static void doSearchMethodList(Class dClass, Predicate filter, List searchToList) { if (dClass == null) { return; } @@ -121,14 +121,14 @@ public class ReflectUtil { if (filter != null) { for (Method method : methods) { if (filter.test(method)) { - list.add(method); + searchToList.add(method); } } } else { - list.addAll(Arrays.asList(methods)); + searchToList.addAll(Arrays.asList(methods)); } } - doSearchMethodList(dClass.getSuperclass(), filter, list); + doSearchMethodList(dClass.getSuperclass(), filter, searchToList); } } -- Gitee From 8039b1f87d08241bb181fa938de3ad873d2cc0c3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 9 Aug 2021 11:02:17 +0800 Subject: [PATCH 1372/1965] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20TypeConverterFun?= =?UTF-8?q?c=EF=BC=8C=E7=94=A8=E4=BA=8E=E5=A4=84=E7=90=86=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E4=BC=A0=E5=85=A5=E6=9E=9A=E4=B8=BE=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 20 +++++-- .../java/io/jboot/core/JbootCoreConfig.java | 5 ++ .../java/io/jboot/web/TypeConverterFunc.java | 54 +++++++++++++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 src/main/java/io/jboot/web/TypeConverterFunc.java diff --git a/pom.xml b/pom.xml index d7fa18a1..708b8690 100644 --- a/pom.xml +++ b/pom.xml @@ -41,22 +41,22 @@ - 4.9.15 + 4.9.16 2020.4 - 2.5 + 2.7 3.2 3.3.0 4.0.3 2.12.4 5.1.49 - 2.2.8.Final + 2.2.9.Final 1.7.31 2.57 1.2.76 30.1.1-jre 2.2.5 2.10.2 - 1.13.1 + 1.14.1 2.10.9.2 2.9.1 3.12.0 @@ -106,6 +106,18 @@ com.jfinal jfinal-undertow ${jfinal-undertow.version} + + + + org.jboss.spec.javax.servlet + jboss-servlet-api_4.0_spec + + + org.jboss.spec.javax.annotation + + jboss-annotations-api_1.3_spec + + diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index dff2351b..b9774273 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -20,6 +20,7 @@ import com.jfinal.aop.AopManager; import com.jfinal.config.*; import com.jfinal.core.Controller; import com.jfinal.core.Path; +import com.jfinal.core.converter.TypeConverter; import com.jfinal.json.JsonManager; import com.jfinal.kit.PathKit; import com.jfinal.kit.PropKit; @@ -55,6 +56,7 @@ import io.jboot.support.swagger.JbootSwaggerManager; import io.jboot.utils.*; import io.jboot.web.JbootAciontMapping; import io.jboot.web.JbootWebConfig; +import io.jboot.web.TypeConverterFunc; import io.jboot.web.attachment.AttachmentHandler; import io.jboot.web.attachment.LocalAttachmentContainerConfig; import io.jboot.web.controller.JbootControllerManager; @@ -373,6 +375,9 @@ public class JbootCoreConfig extends JFinalConfig { JbootAppListenerManager.me().onStart(); + //自定义参数转换方法 + TypeConverter.me().setConvertFunc(new TypeConverterFunc()); + //使用场景:需要等所有组件 onStart() 完成之后,再去执行某些工作的时候 JbootAppListenerManager.me().onStartFinish(); diff --git a/src/main/java/io/jboot/web/TypeConverterFunc.java b/src/main/java/io/jboot/web/TypeConverterFunc.java new file mode 100644 index 00000000..b8a10ef5 --- /dev/null +++ b/src/main/java/io/jboot/web/TypeConverterFunc.java @@ -0,0 +1,54 @@ +package io.jboot.web; + +import com.jfinal.core.JFinal; +import com.jfinal.core.converter.IConverter; +import com.jfinal.core.converter.TypeConverter; +import com.jfinal.kit.Func; + +import java.text.ParseException; + +public class TypeConverterFunc implements Func.F21, String, Object> { + + + @Override + public Object call(Class type, String s) { + if (s == null) { + return null; + } + + // mysql type: varchar, char, enum, set, text, tinytext, mediumtext, longtext + if (type == String.class) { + return ("".equals(s) ? null : s); // 用户在表单域中没有输入内容时将提交过来 "", 因为没有输入,所以要转成 null. + } + s = s.trim(); + if ("".equals(s)) { // 前面的 String跳过以后,所有的空字符串全都转成 null, 这是合理的 + return null; + } + + // 以上两种情况无需转换,直接返回, 注意, 本方法不接受null为 s 参数(经测试永远不可能传来null, 因为无输入传来的也是"") + //String.class提前处理 + + if (type.isEnum()) { + for (Enum e : ((Class>) type).getEnumConstants()) { + if (e.name().equals(s)) { + return e; + } + } + } + + // -------- + IConverter converter = TypeConverter.me().getConverterMap().get(type); + if (converter != null) { + try { + return converter.convert(s); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + if (JFinal.me().getConstants().getDevMode()) { + throw new RuntimeException("Please add code in " + TypeConverter.class + ". The type can't be converted: " + type.getName()); + } else { + throw new RuntimeException(type.getName() + " can not be converted, please use other type of attributes in your model!"); + } + } +} -- Gitee From cf6fa80469a239a5b7f21eb0bc6f9376af352fb5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 19 Aug 2021 17:41:31 +0800 Subject: [PATCH 1373/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=20JbootShiroInterc?= =?UTF-8?q?eptor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../support/shiro/JbootShiroInterceptor.java | 21 +++++++++++++++---- .../shiro/JbootShiroInvokeListener.java | 5 +++-- .../support/shiro/JbootShiroManager.java | 11 +++++----- .../shiro/ShiroAuthorizeProcesserInvoker.java | 2 +- .../processer/IShiroAuthorizeProcesser.java | 4 +++- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java b/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java index 85c99c25..c3e34ec6 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java @@ -18,19 +18,32 @@ package io.jboot.support.shiro; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import io.jboot.support.shiro.processer.AuthorizeResult; + /** * Shiro 拦截器 */ public class JbootShiroInterceptor implements Interceptor { - @Override public void intercept(Invocation inv) { + // 优先执行 onInvokeBefore,得到 AuthorizeResult + // 如果 AuthorizeResult 不为 null,则说用户自定义了其认证方式,比如 jwt、oss 等,此时直接返回给 onInvokeAfter + // 如果 AuthorizeResult 为 null,则有系统去执行(主要是去判断 Shiro 注解,然后通过对于的 Processer 去执行 ) + + JbootShiroManager manager = JbootShiroManager.me(); + + AuthorizeResult result = manager.getInvokeListener().onInvokeBefore(inv); + + if (result == null) { + result = manager.invoke(inv.getActionKey()); + } + + if (result == null) { + result = AuthorizeResult.ok(); + } - JbootShiroManager.me().getInvokeListener().onInvokeBefore(inv); - AuthorizeResult result = JbootShiroManager.me().invoke(inv.getActionKey()); - JbootShiroManager.me().getInvokeListener().onInvokeAfter(inv, result == null ? AuthorizeResult.ok() : result); + manager.getInvokeListener().onInvokeAfter(inv, result); } } diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroInvokeListener.java b/src/main/java/io/jboot/support/shiro/JbootShiroInvokeListener.java index cabc8366..c3b73d83 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroInvokeListener.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroInvokeListener.java @@ -36,7 +36,7 @@ public interface JbootShiroInvokeListener { * * @param inv */ - void onInvokeBefore(Invocation inv); + AuthorizeResult onInvokeBefore(Invocation inv); /** * 通过这个方法,可以用来自定义shiro 处理结果 和 错误逻辑 @@ -54,8 +54,9 @@ public interface JbootShiroInvokeListener { @Override - public void onInvokeBefore(Invocation inv) { + public AuthorizeResult onInvokeBefore(Invocation inv) { //do nothing + return null; } @Override diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroManager.java b/src/main/java/io/jboot/support/shiro/JbootShiroManager.java index e0b54b67..b6a3bbf3 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroManager.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroManager.java @@ -49,10 +49,6 @@ public class JbootShiroManager { private ConcurrentHashMap invokers = new ConcurrentHashMap<>(); - private ShiroRequiresAuthenticationProcesser requiresAuthenticationProcesser = new ShiroRequiresAuthenticationProcesser(); - private ShiroRequiresUserProcesser requiresUserProcesser = new ShiroRequiresUserProcesser(); - private ShiroRequiresGuestProcesser requiresGuestProcesser = new ShiroRequiresGuestProcesser(); - public void init(List routes) { if (!jbootShiroConfig.isConfigOK()) { @@ -67,6 +63,11 @@ public class JbootShiroManager { private void initInvokers(List routes) { Set excludedMethodName = JbootShiroUtil.buildExcludedMethodName(); + ShiroRequiresAuthenticationProcesser requiresAuthenticationProcesser = new ShiroRequiresAuthenticationProcesser(); + ShiroRequiresUserProcesser requiresUserProcesser = new ShiroRequiresUserProcesser(); + ShiroRequiresGuestProcesser requiresGuestProcesser = new ShiroRequiresGuestProcesser(); + + for (Routes.Route route : routes) { Class controllerClass = route.getControllerClass(); @@ -92,7 +93,6 @@ public class JbootShiroManager { String actionKey = JbootShiroUtil.createActionKey(controllerClass, method, controllerKey); ShiroAuthorizeProcesserInvoker invoker = new ShiroAuthorizeProcesserInvoker(); - for (Annotation annotation : allAnnotations) { if (annotation.annotationType() == RequiresPermissions.class) { ShiroRequiresPermissionsProcesser processer = new ShiroRequiresPermissionsProcesser((RequiresPermissions) annotation); @@ -112,7 +112,6 @@ public class JbootShiroManager { if (invoker.getProcessers() != null && invoker.getProcessers().size() > 0) { invokers.put(actionKey, invoker); } - } } } diff --git a/src/main/java/io/jboot/support/shiro/ShiroAuthorizeProcesserInvoker.java b/src/main/java/io/jboot/support/shiro/ShiroAuthorizeProcesserInvoker.java index 256f1f5b..48271fe5 100644 --- a/src/main/java/io/jboot/support/shiro/ShiroAuthorizeProcesserInvoker.java +++ b/src/main/java/io/jboot/support/shiro/ShiroAuthorizeProcesserInvoker.java @@ -22,7 +22,7 @@ import java.util.ArrayList; import java.util.List; /** - * Shiro 认证处理器 执行者 + * Shiro 认证处理器 执行者,每个 actionKey 都有一个独立的 ShiroAuthorizeProcesserInvoker *

        * 它是对 IShiroAuthorizeProcesser 的几个集合处理 */ diff --git a/src/main/java/io/jboot/support/shiro/processer/IShiroAuthorizeProcesser.java b/src/main/java/io/jboot/support/shiro/processer/IShiroAuthorizeProcesser.java index 32d40d47..211897ec 100644 --- a/src/main/java/io/jboot/support/shiro/processer/IShiroAuthorizeProcesser.java +++ b/src/main/java/io/jboot/support/shiro/processer/IShiroAuthorizeProcesser.java @@ -17,7 +17,9 @@ package io.jboot.support.shiro.processer; /** * Shiro 的认证处理器 - * 用于对每个controller 的 每个方法进行认证 + * 用于对每个 controller 的 每个方法进行认证 + * + * 每个 shiro 注解,都有一个对于的 Processer,比如 注解 @RequiresGuest 的处理器为 ShiroRequiresGuestProcesser.java */ public interface IShiroAuthorizeProcesser { -- Gitee From fec49d3429a9ad5c7756adbf16bcbf7357c35e44 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Aug 2021 17:08:53 +0800 Subject: [PATCH 1374/1965] v3.10.7 snapshot --- src/main/java/io/jboot/db/SqlDebugger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/SqlDebugger.java b/src/main/java/io/jboot/db/SqlDebugger.java index 429e8508..681f58c1 100644 --- a/src/main/java/io/jboot/db/SqlDebugger.java +++ b/src/main/java/io/jboot/db/SqlDebugger.java @@ -95,7 +95,7 @@ public class SqlDebugger { if (value instanceof Date) { sb.append(DateUtil.toDateTimeString((Date) value)); } else { - sb.append(value.toString()); + sb.append(value); } sb.append("'"); sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(sb.toString())); -- Gitee From 95d8ff5c1ad8ef81e50094bf32029bc5ab8f0090 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Aug 2021 17:16:56 +0800 Subject: [PATCH 1375/1965] update docs --- doc/docs/shiro.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/doc/docs/shiro.md b/doc/docs/shiro.md index ae46117a..45e5462a 100644 --- a/doc/docs/shiro.md +++ b/doc/docs/shiro.md @@ -232,7 +232,7 @@ public class MyshiroListener implements JbootShiroInvokeListener { @Override - public void onInvokeBefore(Invocation inv) { + public AuthorizeResult onInvokeBefore(Invocation inv) { //do nothing } @@ -279,4 +279,12 @@ jboot.shiro.invokeListener=com.xxx.MyshiroListener ### Shiro 与 Jwt 整合 -### Shiro 与 SSO 整合 \ No newline at end of file + 在 `JbootShiroInvokeListener.onInvokeBefore()` 中,接收 JWT 数据,并 JWT 处理的相关认证,然后返回 + `AuthorizeResult`,返回的 `AuthorizeResult` 直接交给 `onInvokeAfter` 处理,不再交给 +Shiro 内部处理。 + +若 `JbootShiroInvokeListener.onInvokeBefore()` 返回 null,则交给 Shiro 内部处理 + +### Shiro 与 SSO 整合 + +同 Jwt 处理方案。 \ No newline at end of file -- Gitee From d177416cff215c9b9672eb261e4c94fd658c3078 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 22 Aug 2021 17:18:27 +0800 Subject: [PATCH 1376/1965] update docs --- doc/.vuepress/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/.vuepress/config.js b/doc/.vuepress/config.js index 4234c89b..44c3e141 100644 --- a/doc/.vuepress/config.js +++ b/doc/.vuepress/config.js @@ -67,7 +67,6 @@ module.exports = { {title: 'WebSocket', path: '/docs/websocket'}, {title: 'Json', path: '/docs/json'}, {title: 'Jwt', path: '/docs/jwt'}, - {title: 'Swagger', path: '/docs/swagger'}, {title: 'AOP', path: '/docs/aop'}, {title: '数据库', path: '/docs/db'}, {title: '缓存', path: '/docs/cache'}, @@ -90,6 +89,7 @@ module.exports = { {title: '项目部署', path: '/docs/deploy'}, {title: 'Docker', path: '/docs/docker'}, {title: '热加载', path: '/docs/hotload'}, + {title: 'Shiro', path: '/docs/shiro'}, {title: 'Swagger', path: '/docs/swagger'}, ], }, -- Gitee From 43767ded54539d9ae42f4fa289ad71cf8d83636e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Aug 2021 12:03:05 +0800 Subject: [PATCH 1377/1965] upgrade jedis version to newest --- .../redis/jedis/JbootJedisClusterImpl.java | 27 +++++++++++++------ .../support/redis/jedis/JbootJedisImpl.java | 2 +- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java index f68fb6c9..2a8ccdf9 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java @@ -378,8 +378,9 @@ public class JbootJedisClusterImpl extends JbootRedisBase { */ public String select(int databaseIndex) { - return jedisCluster.select(databaseIndex); - +// return jedisCluster.select(databaseIndex); + throw new IllegalStateException("Redis Cluster does not support multiple databases like the stand alone version of Redis, " + + "there is just database 0, and SELECT is not allowed."); } /** @@ -851,9 +852,21 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 通常用于测试与服务器的连接是否仍然生效,或者用于测量延迟值。 */ public String ping() { - - return jedisCluster.ping(); - +// jedisCluster.getClusterNodes().get("aa").getResource().ping +// return jedisCluster..ping(); + + Map nodes = jedisCluster.getClusterNodes(); + if (nodes != null) { + for (JedisPool pool : nodes.values()) { + try (Jedis node = pool.getResource()) { + String ret = node.ping(); + if (ret != null) { + return ret; + } + } + } + } + return null; } /** @@ -1141,9 +1154,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * @param message */ public void publish(byte[] channel, byte[] message) { - jedisCluster.publish(channel, message); - } @@ -1224,7 +1235,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { ScanParams params = new ScanParams(); params.match(pattern).count(scanCount); ScanResult scanResult = jedisCluster.scan(cursor, params); - return new RedisScanResult<>(scanResult.getStringCursor(), scanResult.getResult()); + return new RedisScanResult<>(scanResult.getCursor(), scanResult.getResult()); } @Override diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java index a7181b98..2449f076 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java @@ -1559,7 +1559,7 @@ public class JbootJedisImpl extends JbootRedisBase { params.match(pattern).count(scanCount); try (Jedis jedis = getJedis()) { ScanResult scanResult = jedis.scan(cursor, params); - return new RedisScanResult<>(scanResult.getStringCursor(), scanResult.getResult()); + return new RedisScanResult<>(scanResult.getCursor(), scanResult.getResult()); } } -- Gitee From 25c628636b5f588dcfcf184f88f9d67404f4b6ae Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Aug 2021 12:03:33 +0800 Subject: [PATCH 1378/1965] upgrade jedis/jsoup/fastjson --- pom.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 708b8690..778d199e 100644 --- a/pom.xml +++ b/pom.xml @@ -44,7 +44,7 @@ 4.9.16 2020.4 2.7 - 3.2 + 3.3 3.3.0 4.0.3 2.12.4 @@ -52,25 +52,25 @@ 2.2.9.Final 1.7.31 2.57 - 1.2.76 + 1.2.78 30.1.1-jre 2.2.5 - 2.10.2 - 1.14.1 + 3.6.3 + 1.14.2 2.10.9.2 2.9.1 3.12.0 - 2.10.0 + 2.11.0 1.15 4.5.12 4.4.13 4.1.52.Final - 1.8.1 + 1.8.2 2.7.12 3.13.2.Final 1.4.2 1.1.8 - 4.2.2 + 4.2.3 1.8 1.8 -- Gitee From 091f8b9f592e9d68746d70baa9d36cacf2373c61 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Aug 2021 12:17:19 +0800 Subject: [PATCH 1379/1965] v3.10.7 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 18efbf0f..03a600a8 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.10.6 + 3.10.7 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 426423cd..970afa6f 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.10.6 + 3.10.7 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index e1a1bcdf..a611647e 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.10.6 + 3.10.7 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index cffb206d..fa849f58 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.10.6 + 3.10.7 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.10.6', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.10.7', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 9ee74b14..8390bf96 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.10.6', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.10.7', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.10.6/jboot-3.10.6.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.10.7/jboot-3.10.7.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 8d32f7e3..2ade50c5 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.10.6"; + public static String VERSION = "3.10.7"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From df70d95ab2a8234385ee21cc04738bef7340c73f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Aug 2021 12:17:23 +0800 Subject: [PATCH 1380/1965] v3.10.7 release (^.^)YYa!! --- changes.txt | 10 ++++++++++ pom.xml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 762204fc..b7fd0999 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,13 @@ +jboot v3.10.7: +新增:JbootHttpRequest 添加 sslContext 的配置,方便自定义 ssl +新增:JbootConfigManager 添加 setBootProperties 方法,方便用于添加启动配置 +新增:工具类 ReflectUtil 新增 searchFieldList() 方法 +新增:添加 TypeConverterFunc,用于处理前端传入枚举内容 +优化:JbootShiroInvokeListener,在 onInvokeBefore() 方法添加返回值 AuthorizeResult,更加方便整合 Jwt sso 等 +优化:升级 jedis/jsoup/jfinal/fastjson 等到最新版本 + + + jboot v3.10.6: 修复:ClassScanner 添加排除的前缀时,如果有大写字母不生效的问题 修复:当使用 @Api(collect={}) 时,子 Controller 路径错误的问题 diff --git a/pom.xml b/pom.xml index 778d199e..44322cc3 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.10.7-SNAPSHOT + 3.10.7 jar jboot -- Gitee From 0c12a4dec9dc09262cfeb183f68b7252896f9d34 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Aug 2021 18:56:38 +0800 Subject: [PATCH 1381/1965] v3.10.8 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 44322cc3..0ee54546 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.10.7 + 3.10.8-SNAPSHOT jar jboot -- Gitee From 0cd474898f9d8b54a22834a1c80ebe2159090251 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Aug 2021 19:31:23 +0800 Subject: [PATCH 1382/1965] =?UTF-8?q?JbootCache=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E9=80=9A=E8=BF=87=E5=89=8D=E7=BC=80=E4=BA=86=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=20"=E5=A4=9A=E5=AE=9E=E4=BE=8B"=20=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/components/cache/AopCache.java | 7 +++-- .../io/jboot/components/cache/JbootCache.java | 1 + .../components/cache/JbootCacheBase.java | 27 +++++++++++++++++++ .../components/cache/JbootCacheConfig.java | 21 +++++++++++++++ .../components/cache/JbootCacheManager.java | 17 ++++++------ .../cache/caffeine/CaffeineCacheImpl.java | 2 ++ .../cache/ehcache/JbootEhcacheImpl.java | 1 + .../components/cache/j2cache/J2cacheImpl.java | 8 ++++++ .../components/cache/none/NoneCacheImpl.java | 5 ++++ .../cache/redis/JbootRedisCacheImpl.java | 1 + src/main/java/io/jboot/utils/CacheUtil.java | 6 ++++- .../java/io/jboot/web/JbootWebConfig.java | 9 +++++-- 12 files changed, 91 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/AopCache.java b/src/main/java/io/jboot/components/cache/AopCache.java index c82df24d..936cc2a7 100644 --- a/src/main/java/io/jboot/components/cache/AopCache.java +++ b/src/main/java/io/jboot/components/cache/AopCache.java @@ -3,7 +3,6 @@ package io.jboot.components.cache; import com.jfinal.log.Log; import com.jfinal.plugin.ehcache.IDataLoader; -import io.jboot.Jboot; import java.util.List; @@ -13,11 +12,15 @@ public class AopCache { private static JbootCache aopCache; + public static JbootCache usePrefix(String cacheNamePrefix) { + return getAopCache().use(cacheNamePrefix); + } + static JbootCache getAopCache() { if (aopCache == null) { synchronized (AopCache.class) { if (aopCache == null) { - aopCache = JbootCacheManager.me().getCache(Jboot.config(JbootCacheConfig.class).getAopCacheType()); + aopCache = JbootCacheManager.me().getCache(JbootCacheConfig.getInstance().getAopCacheType()); } } } diff --git a/src/main/java/io/jboot/components/cache/JbootCache.java b/src/main/java/io/jboot/components/cache/JbootCache.java index 6a2f09e7..ba60845a 100644 --- a/src/main/java/io/jboot/components/cache/JbootCache.java +++ b/src/main/java/io/jboot/components/cache/JbootCache.java @@ -22,6 +22,7 @@ import java.util.List; public interface JbootCache extends com.jfinal.plugin.activerecord.cache.ICache { + public JbootCache use(String cacheNamePrefix); @Override public T get(String cacheName, Object key); diff --git a/src/main/java/io/jboot/components/cache/JbootCacheBase.java b/src/main/java/io/jboot/components/cache/JbootCacheBase.java index 2577db0c..1a5725bb 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheBase.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheBase.java @@ -16,8 +16,35 @@ package io.jboot.components.cache; +import io.jboot.utils.StrUtil; + public abstract class JbootCacheBase implements JbootCache { + private ThreadLocal CACHE_NAME_PREFIX_TL = new ThreadLocal<>(); + + @Override + public JbootCache use(String cacheNamePrefix) { + if (StrUtil.isNotBlank(cacheNamePrefix)) { + CACHE_NAME_PREFIX_TL.set(cacheNamePrefix); + } else { + CACHE_NAME_PREFIX_TL.remove(); + } + return this; + } + + protected String buildCacheName(String cacheName) { + String cacheNamePrefix = CACHE_NAME_PREFIX_TL.get(); + if (StrUtil.isBlank(cacheNamePrefix)) { + cacheNamePrefix = JbootCacheConfig.getInstance().getDefaultCacheNamePrefix(); + } + + if (StrUtil.isBlank(cacheNamePrefix)) { + return cacheName; + } + return cacheNamePrefix + ":" + cacheName; + } + + @Override public void refresh(String cacheName, Object key) { diff --git a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java index ddae361f..9f559137 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java @@ -16,6 +16,7 @@ package io.jboot.components.cache; +import io.jboot.app.config.JbootConfigManager; import io.jboot.app.config.annotation.ConfigModel; import io.jboot.utils.StrUtil; @@ -33,11 +34,13 @@ public class JbootCacheConfig { private String type = TYPE_CAFFEINE; + private String defaultCacheNamePrefix; // AOP 缓存的默认有效时间,0为永久有效,单位秒, // 当 @Cacheable 和 @CachePut 注解不配置的时候默认用这个配置 private int aopCacheLiveSeconds = 0; private String aopCacheType; + private String aopCacheDefaultCacheNamePrefix; public String getType() { return type; @@ -47,6 +50,14 @@ public class JbootCacheConfig { this.type = type; } + public String getDefaultCacheNamePrefix() { + return defaultCacheNamePrefix; + } + + public void setDefaultCacheNamePrefix(String defaultCacheNamePrefix) { + this.defaultCacheNamePrefix = defaultCacheNamePrefix; + } + public int getAopCacheLiveSeconds() { return aopCacheLiveSeconds; } @@ -65,4 +76,14 @@ public class JbootCacheConfig { public void setAopCacheType(String aopCacheType) { this.aopCacheType = aopCacheType; } + + private static JbootCacheConfig me; + + public static JbootCacheConfig getInstance() { + if (me == null) { + me = JbootConfigManager.me().get(JbootCacheConfig.class); + } + return me; + } + } diff --git a/src/main/java/io/jboot/components/cache/JbootCacheManager.java b/src/main/java/io/jboot/components/cache/JbootCacheManager.java index d24e08f0..74fdcb55 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheManager.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheManager.java @@ -54,16 +54,15 @@ public class JbootCacheManager { } JbootCache cache = cacheMap.get(type); - if (cache != null) { - return cache; - } - synchronized (type.intern()) { - if (cache == null) { - JbootCacheConfig cacheConfig = new JbootCacheConfig(); - cacheConfig.setType(type); - cache = buildCache(cacheConfig); - cacheMap.put(type, cache); + if (cache == null) { + synchronized (type.intern()) { + if (cache == null) { + JbootCacheConfig cacheConfig = new JbootCacheConfig(); + cacheConfig.setType(type); + cache = buildCache(cacheConfig); + cacheMap.put(type, cache); + } } } diff --git a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java index 3546358b..39dd20b1 100644 --- a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java @@ -30,10 +30,12 @@ public class CaffeineCacheImpl extends JbootCacheBase { private Map cacheMap = new ConcurrentHashMap<>(); protected Cache getCacheOnly(String cacheName) { + cacheName = buildCacheName(cacheName); return cacheMap.get(cacheName); } protected Cache getCache(String cacheName) { + cacheName = buildCacheName(cacheName); Cache cache = cacheMap.get(cacheName); if (cache == null) { synchronized (CaffeineCacheImpl.class) { diff --git a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java index 0abe461b..6a87df7f 100644 --- a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java @@ -65,6 +65,7 @@ public class JbootEhcacheImpl extends JbootCacheBase { } public Cache getOrAddCache(String cacheName) { + cacheName = buildCacheName(cacheName); Cache cache = cacheManager.getCache(cacheName); if (cache == null) { synchronized (locker) { diff --git a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java index cbaf00e2..e47fadc5 100644 --- a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java +++ b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java @@ -36,28 +36,33 @@ public class J2cacheImpl extends JbootCacheBase { @Override public T get(String cacheName, Object key) { + cacheName = buildCacheName(cacheName); CacheObject cacheObject = J2Cache.getChannel().get(cacheName, key.toString(), false); return cacheObject != null ? (T) cacheObject.getValue() : null; } @Override public void put(String cacheName, Object key, Object value) { + cacheName = buildCacheName(cacheName); J2Cache.getChannel().set(cacheName, key.toString(), value); } @Override public void put(String cacheName, Object key, Object value, int liveSeconds) { + cacheName = buildCacheName(cacheName); J2Cache.getChannel().set(cacheName, key.toString(), value, liveSeconds); } @Override public void remove(String cacheName, Object key) { + cacheName = buildCacheName(cacheName); J2Cache.getChannel().evict(cacheName, key.toString()); } @Override public void removeAll(String cacheName) { + cacheName = buildCacheName(cacheName); J2Cache.getChannel().clear(cacheName); } @@ -91,6 +96,7 @@ public class J2cacheImpl extends JbootCacheBase { @Override public void refresh(String cacheName, Object key) { + cacheName = buildCacheName(cacheName); if (sendEvictCmdMethod == null) { synchronized (this) { if (sendEvictCmdMethod == null) { @@ -110,6 +116,7 @@ public class J2cacheImpl extends JbootCacheBase { @Override public void refresh(String cacheName) { + cacheName = buildCacheName(cacheName); if (sendClearCmdMethod == null) { synchronized (this) { if (sendClearCmdMethod == null) { @@ -135,6 +142,7 @@ public class J2cacheImpl extends JbootCacheBase { @Override public List getKeys(String cacheName) { + cacheName = buildCacheName(cacheName); Collection keys = J2Cache.getChannel().keys(cacheName); return keys != null ? new ArrayList(keys) : null; } diff --git a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java index 14556496..617cbe00 100644 --- a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java @@ -25,6 +25,11 @@ import java.util.List; * noneCache 存在的目的:方便通过配置文件的方式关闭缓存功能 */ public class NoneCacheImpl implements JbootCache { + @Override + public JbootCache use(String cacheNamePrefix) { + return this; + } + @Override public T get(String cacheName, Object key) { return null; diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java index 6d4cbbb6..8cd32b2b 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java @@ -127,6 +127,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { private String buildKey(String cacheName, Object key) { + cacheName = buildCacheName(cacheName); StringBuilder keyBuilder = new StringBuilder(globalKeyPrefix) .append(cacheName).append(":"); diff --git a/src/main/java/io/jboot/utils/CacheUtil.java b/src/main/java/io/jboot/utils/CacheUtil.java index 63a169db..234f806e 100644 --- a/src/main/java/io/jboot/utils/CacheUtil.java +++ b/src/main/java/io/jboot/utils/CacheUtil.java @@ -29,10 +29,14 @@ import java.util.List; */ public class CacheUtil { - public static JbootCache use(String type) { + public static JbootCache useType(String type) { return JbootCacheManager.me().getCache(type); } + public static JbootCache usePrefix(String cacheNamePrefix) { + return JbootCacheManager.me().getCache().use(cacheNamePrefix); + } + public static T get(String cacheName, Object key) { return Jboot.getCache().get(cacheName, key); } diff --git a/src/main/java/io/jboot/web/JbootWebConfig.java b/src/main/java/io/jboot/web/JbootWebConfig.java index f37f3dd4..2ba46335 100644 --- a/src/main/java/io/jboot/web/JbootWebConfig.java +++ b/src/main/java/io/jboot/web/JbootWebConfig.java @@ -64,7 +64,12 @@ public class JbootWebConfig { this.escapeParas = escapeParas; } - public static JbootWebConfig getInstance(){ - return JbootConfigManager.me().get(JbootWebConfig.class); + private static JbootWebConfig me; + + public static JbootWebConfig getInstance() { + if (me == null) { + me = JbootConfigManager.me().get(JbootWebConfig.class); + } + return me; } } -- Gitee From 24e87c2a2acb060e08c657c97a30bb4a9ad3216c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Aug 2021 19:41:45 +0800 Subject: [PATCH 1383/1965] =?UTF-8?q?datasourceConfig=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=A1=A8=E5=89=8D=E7=BC=80=E9=85=8D=E7=BD=AE=E7=9A=84=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/ArpManager.java | 9 +++++---- src/main/java/io/jboot/db/TableInfoManager.java | 2 +- .../java/io/jboot/db/datasource/DataSourceConfig.java | 9 +++++++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/db/ArpManager.java b/src/main/java/io/jboot/db/ArpManager.java index 3a663f40..d2a1c639 100644 --- a/src/main/java/io/jboot/db/ArpManager.java +++ b/src/main/java/io/jboot/db/ArpManager.java @@ -129,8 +129,8 @@ public class ArpManager { /** * 不需要添加映射的直接返回 * - * 在一个表有多个数据源的情况下,应该只需要添加一个映射就可以了, - * 添加映射:默认为该model的数据源, + * 在一个表有多个数据源的情况下,应该只需要添加一个映射就可以了 + * 添加映射:默认为该 model 的数据源 * 不添加映射:通过 model.use("xxx").save() 这种方式去调用该数据源 * 不添加映射使用从场景一般是:读写分离时,用于读取只读数据库的数据 */ @@ -145,10 +145,11 @@ public class ArpManager { } for (TableInfo table : tableInfos) { + String tableName = StrUtil.isNotBlank(config.getTablePrefix()) ? config.getTablePrefix() + table.getTableName() : table.getTableName(); if (StrUtil.isNotBlank(table.getPrimaryKey())) { - activeRecordPlugin.addMapping(table.getTableName(), table.getPrimaryKey(), (Class>) table.getModelClass()); + activeRecordPlugin.addMapping(tableName, table.getPrimaryKey(), (Class>) table.getModelClass()); } else { - activeRecordPlugin.addMapping(table.getTableName(), (Class>) table.getModelClass()); + activeRecordPlugin.addMapping(tableName, (Class>) table.getModelClass()); } } diff --git a/src/main/java/io/jboot/db/TableInfoManager.java b/src/main/java/io/jboot/db/TableInfoManager.java index a6e4fdcd..df5c15a1 100644 --- a/src/main/java/io/jboot/db/TableInfoManager.java +++ b/src/main/java/io/jboot/db/TableInfoManager.java @@ -64,7 +64,7 @@ public class TableInfoManager { for (TableInfo tableInfo : getAllTableInfos()) { - //说明该表已经被指定到datasource了 + //说明该表已经被指定到 datasource 了 if (tableInfo.getDatasources() != null) { continue; } diff --git a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java index 3dca186a..37b6c7a9 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java @@ -75,6 +75,7 @@ public class DataSourceConfig { private String table; //此数据源包含哪些表 private String exTable; //该数据源排除哪些表 + private String tablePrefix; //表前缀,假设 @Table(tableName="xxx"),那么实际表名为:tablePrefix + tableName private String dialectClass; private String activeRecordPluginClass; @@ -291,6 +292,14 @@ public class DataSourceConfig { this.exTable = exTable; } + public String getTablePrefix() { + return tablePrefix; + } + + public void setTablePrefix(String tablePrefix) { + this.tablePrefix = tablePrefix; + } + public Long getMaxLifetime() { return maxLifetime; } -- Gitee From 8b3fb26586172e6655b34a4308e34bdfa418ea92 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Aug 2021 20:20:51 +0800 Subject: [PATCH 1384/1965] =?UTF-8?q?JbootCache=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E9=80=9A=E8=BF=87=E5=89=8D=E7=BC=80=E4=BA=86=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=20"=E5=A4=9A=E5=AE=9E=E4=BE=8B"=20=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/components/cache/AopCache.java | 5 ++++- src/main/java/io/jboot/components/cache/JbootCache.java | 1 + src/main/java/io/jboot/components/cache/JbootCacheBase.java | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/cache/AopCache.java b/src/main/java/io/jboot/components/cache/AopCache.java index 936cc2a7..dd41b9c7 100644 --- a/src/main/java/io/jboot/components/cache/AopCache.java +++ b/src/main/java/io/jboot/components/cache/AopCache.java @@ -16,6 +16,10 @@ public class AopCache { return getAopCache().use(cacheNamePrefix); } + public static void clearPrefix() { + getAopCache().clear(); + } + static JbootCache getAopCache() { if (aopCache == null) { synchronized (AopCache.class) { @@ -32,7 +36,6 @@ public class AopCache { } - public static void put(String cacheName, Object key, Object value) { try { getAopCache().put(cacheName, key, value); diff --git a/src/main/java/io/jboot/components/cache/JbootCache.java b/src/main/java/io/jboot/components/cache/JbootCache.java index ba60845a..74d90b13 100644 --- a/src/main/java/io/jboot/components/cache/JbootCache.java +++ b/src/main/java/io/jboot/components/cache/JbootCache.java @@ -23,6 +23,7 @@ import java.util.List; public interface JbootCache extends com.jfinal.plugin.activerecord.cache.ICache { public JbootCache use(String cacheNamePrefix); + public void clear(); @Override public T get(String cacheName, Object key); diff --git a/src/main/java/io/jboot/components/cache/JbootCacheBase.java b/src/main/java/io/jboot/components/cache/JbootCacheBase.java index 1a5725bb..fe6c81b2 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheBase.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheBase.java @@ -32,6 +32,11 @@ public abstract class JbootCacheBase implements JbootCache { return this; } + public void clear(){ + CACHE_NAME_PREFIX_TL.remove(); + } + + protected String buildCacheName(String cacheName) { String cacheNamePrefix = CACHE_NAME_PREFIX_TL.get(); if (StrUtil.isBlank(cacheNamePrefix)) { -- Gitee From aa07e17ec3b13a8742adb42531276dce23a48fb8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 24 Aug 2021 20:21:47 +0800 Subject: [PATCH 1385/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20Model=E3=80=81Db?= =?UTF-8?q?=20=E5=8A=A8=E6=80=81=E8=AE=BE=E7=BD=AE=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=BA=90=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/JbootDb.java | 17 ++++++++- .../java/io/jboot/db/model/JbootModel.java | 36 +++++++++++++------ src/main/java/io/jboot/utils/CacheUtil.java | 4 +++ 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/jboot/db/JbootDb.java b/src/main/java/io/jboot/db/JbootDb.java index 9578161d..c52fd066 100644 --- a/src/main/java/io/jboot/db/JbootDb.java +++ b/src/main/java/io/jboot/db/JbootDb.java @@ -19,19 +19,34 @@ import com.jfinal.plugin.activerecord.Db; import com.jfinal.plugin.activerecord.Record; import io.jboot.db.dbpro.JbootDbPro; import io.jboot.db.model.Columns; +import io.jboot.utils.StrUtil; import java.util.List; public class JbootDb extends Db { + private static ThreadLocal CONFIG_NAME_TL = new ThreadLocal<>(); + + public static String getCurentConfigName() { + return CONFIG_NAME_TL.get(); + } + + public static void setCurrentConfigName(String configName) { + CONFIG_NAME_TL.set(configName); + } + + public static void clearCurrentConfigName() { + CONFIG_NAME_TL.remove(); + } public static JbootDbPro use(String configName) { return (JbootDbPro) Db.use(configName); } public static JbootDbPro use() { - return (JbootDbPro) Db.use(); + String currentConfigName = getCurentConfigName(); + return StrUtil.isBlank(currentConfigName) ? (JbootDbPro) Db.use() : use(currentConfigName); } diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index fed1723d..db8e9da4 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -18,6 +18,7 @@ package io.jboot.db.model; import com.jfinal.log.Log; import com.jfinal.plugin.activerecord.*; import com.jfinal.plugin.activerecord.dialect.Dialect; +import io.jboot.db.JbootDb; import io.jboot.db.SqlDebugger; import io.jboot.db.dialect.JbootDialect; import io.jboot.exception.JbootException; @@ -131,7 +132,7 @@ public class JbootModel> extends Model { if (isCopyModel) { return (M) this; } else { - M model = copy().superUse(datasourceName); + M model = copy()._setConfigName(datasourceName); model.isCopyModel = true; return model; } @@ -211,7 +212,7 @@ public class JbootModel> extends Model { synchronized (configName.intern()) { newDao = this.get(DATASOURCE_CACHE_PREFIX + configName); if (newDao == null) { - newDao = this.copy().superUse(configName); + newDao = this.copy()._setConfigName(configName); if (newDao._getConfig() == null) { if (validateExist) { throw new JbootIllegalConfigException("the datasource \"" + configName + "\" not config well, please config it in jboot.properties."); @@ -219,7 +220,6 @@ public class JbootModel> extends Model { return null; } } else { - newDao.datasourceName = configName; this.put(DATASOURCE_CACHE_PREFIX + configName, newDao); } } @@ -229,8 +229,23 @@ public class JbootModel> extends Model { } - M superUse(String configName) { - return super.use(configName); + M _setConfigName(String configName) { + this.datasourceName = configName; + return (M) this; + } + + @Override + protected Config _getConfig() { + if (datasourceName != null) { + return DbKit.getConfig(datasourceName); + } + + String currentConfigName = JbootDb.getCurentConfigName(); + if (StrUtil.isNotBlank(currentConfigName)) { + return DbKit.getConfig(currentConfigName); + } + + return DbKit.getConfig(_getUsefulClass()); } @@ -324,10 +339,10 @@ public class JbootModel> extends Model { protected M loadByCache(Object... idValues) { //临时关闭 id 缓存的情况 - if (JbootModelHintManager.me().isClosedIdCache(getClass())){ + if (JbootModelHintManager.me().isClosedIdCache(getClass())) { try { return JbootModel.super.findByIds(idValues); - }finally { + } finally { JbootModelHintManager.me().clearIdCacheFlag(); } } @@ -350,9 +365,10 @@ public class JbootModel> extends Model { /** * 临时关闭 id 缓存,关闭后通过 findById 执行后又会开启了 * 一般情况下的使用方法是 DAO.closeIdCacheTemporary().findById(...) + * * @return */ - public M closeIdCacheTemporary(){ + public M closeIdCacheTemporary() { JbootModelHintManager.me().closeIdCache(getClass()); return (M) this; } @@ -720,12 +736,12 @@ public class JbootModel> extends Model { conn = config.getConnection(); // String totalRowSql = config.dialect.forPaginateTotalRow(select, sqlExceptSelect, this); String totalRowSqlExceptSelect = _getDialect().forPaginateFrom(alias, joins, _getTableName(), columns.getList(), null); - String totalRowSql = config.getDialect().forPaginateTotalRow(selectPartSql, totalRowSqlExceptSelect,this); + String totalRowSql = config.getDialect().forPaginateTotalRow(selectPartSql, totalRowSqlExceptSelect, this); StringBuilder findSql = new StringBuilder(); findSql.append(selectPartSql).append(' ').append(fromPartSql); - return doPaginateByFullSql(config, conn, pageNumber, pageSize, null, totalRowSql, findSql, columns.getValueArray()); + return doPaginateByFullSql(config, conn, pageNumber, pageSize, null, totalRowSql, findSql, columns.getValueArray()); } catch (Exception e) { throw new ActiveRecordException(e); } finally { diff --git a/src/main/java/io/jboot/utils/CacheUtil.java b/src/main/java/io/jboot/utils/CacheUtil.java index 234f806e..fd2b00cc 100644 --- a/src/main/java/io/jboot/utils/CacheUtil.java +++ b/src/main/java/io/jboot/utils/CacheUtil.java @@ -37,6 +37,10 @@ public class CacheUtil { return JbootCacheManager.me().getCache().use(cacheNamePrefix); } + public static void clearPrefix() { + JbootCacheManager.me().getCache().clear(); + } + public static T get(String cacheName, Object key) { return Jboot.getCache().get(cacheName, key); } -- Gitee From 800b3da1a6fd9f44a8a685c214c333138f70a0cc Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 25 Aug 2021 18:17:27 +0800 Subject: [PATCH 1386/1965] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=94=B1=E4=BA=8E?= =?UTF-8?q?=20RPC=20=E6=B7=BB=E5=8A=A0=20Reference=20=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E5=90=8E=EF=BC=8C=E9=80=A0=E6=88=90=20Controller=20=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=20Service=20=E6=97=A0=E6=B3=95=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/cache/none/NoneCacheImpl.java | 5 ++++ .../rpc/JbootrpcReferenceConfig.java | 18 ------------- .../components/rpc/ReferenceConfigCache.java | 9 ++++--- .../test/rpc/commons/BlogServiceMock.java | 2 +- .../test/rpc/commons/BlogServiceProvider.java | 2 +- .../jboot/test/rpc/commons/BookService.java | 12 +++++++++ .../test/rpc/commons/BookServiceProvider.java | 26 +++++++++++++++++++ .../rpc/dubbonacos/DubboClientNacosDemo.java | 13 +++++++--- 8 files changed, 60 insertions(+), 27 deletions(-) create mode 100644 src/test/java/io/jboot/test/rpc/commons/BookService.java create mode 100644 src/test/java/io/jboot/test/rpc/commons/BookServiceProvider.java diff --git a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java index 617cbe00..c9eb8930 100644 --- a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java @@ -30,6 +30,11 @@ public class NoneCacheImpl implements JbootCache { return this; } + @Override + public void clear() { + + } + @Override public T get(String cacheName, Object key) { return null; diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java index a2649eeb..42e05828 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java @@ -16,7 +16,6 @@ package io.jboot.components.rpc; import java.io.Serializable; -import java.util.Objects; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) @@ -274,21 +273,4 @@ public class JbootrpcReferenceConfig implements Serializable { this.id = id; } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - JbootrpcReferenceConfig that = (JbootrpcReferenceConfig) o; - return Objects.equals(version, that.version) && Objects.equals(group, that.group) && Objects.equals(url, that.url) && Objects.equals(generic, that.generic) && Objects.equals(check, that.check) && Objects.equals(retries, that.retries) && Objects.equals(loadbalance, that.loadbalance) && Objects.equals(async, that.async) && Objects.equals(actives, that.actives) && Objects.equals(timeout, that.timeout) && Objects.equals(application, that.application) && Objects.equals(module, that.module) && Objects.equals(consumer, that.consumer) && Objects.equals(monitor, that.monitor) && Objects.equals(registry, that.registry) && Objects.equals(protocol, that.protocol) && Objects.equals(tag, that.tag) && Objects.equals(id, that.id); - } - - @Override - public int hashCode() { - return Objects.hash(version, group, url, generic, check, retries, loadbalance, async, actives, timeout, application, module, consumer, monitor, registry, protocol, tag, id); - } } diff --git a/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java b/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java index 7f024dfb..e982f65f 100644 --- a/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java +++ b/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java @@ -23,17 +23,18 @@ import java.util.concurrent.ConcurrentHashMap; public class ReferenceConfigCache { - private static Map configs = new ConcurrentHashMap<>(); + private static Map configs = new ConcurrentHashMap<>(); public static JbootrpcReferenceConfig getReferenceConfig(RPCInject rpcInject) { - JbootrpcReferenceConfig referenceConfig = (JbootrpcReferenceConfig) configs.get(rpcInject); + int identityHashCode = System.identityHashCode(rpcInject); + JbootrpcReferenceConfig referenceConfig = (JbootrpcReferenceConfig) configs.get(identityHashCode); if (referenceConfig == null) { synchronized (rpcInject) { - referenceConfig = (JbootrpcReferenceConfig) configs.get(rpcInject); + referenceConfig = (JbootrpcReferenceConfig) configs.get(identityHashCode); if (referenceConfig == null) { referenceConfig = new JbootrpcReferenceConfig(); RPCUtil.appendAnnotation(RPCInject.class, rpcInject, referenceConfig); - configs.put(rpcInject, referenceConfig); + configs.put(identityHashCode, referenceConfig); } } } diff --git a/src/test/java/io/jboot/test/rpc/commons/BlogServiceMock.java b/src/test/java/io/jboot/test/rpc/commons/BlogServiceMock.java index d8ddfaa2..a4efa8a3 100644 --- a/src/test/java/io/jboot/test/rpc/commons/BlogServiceMock.java +++ b/src/test/java/io/jboot/test/rpc/commons/BlogServiceMock.java @@ -11,7 +11,7 @@ public class BlogServiceMock implements BlogService { @Override public String findById() { System.err.println("BlogServiceMock.findById() invoked."); - return "id from mock"; + return "id from BlogServiceMock"; } @Override diff --git a/src/test/java/io/jboot/test/rpc/commons/BlogServiceProvider.java b/src/test/java/io/jboot/test/rpc/commons/BlogServiceProvider.java index e428065d..88896f36 100644 --- a/src/test/java/io/jboot/test/rpc/commons/BlogServiceProvider.java +++ b/src/test/java/io/jboot/test/rpc/commons/BlogServiceProvider.java @@ -11,7 +11,7 @@ public class BlogServiceProvider implements BlogService { @Override public String findById() { System.err.println("BlogServiceProvider.findById() invoked."); - return "id from provider"; + return "id from BlogServiceProvider"; } @Override diff --git a/src/test/java/io/jboot/test/rpc/commons/BookService.java b/src/test/java/io/jboot/test/rpc/commons/BookService.java new file mode 100644 index 00000000..9c34a5b6 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/commons/BookService.java @@ -0,0 +1,12 @@ +package io.jboot.test.rpc.commons; + + +import java.util.List; + +public interface BookService { + + public String findById(); + public List findAll(); + + public void doOther(); +} diff --git a/src/test/java/io/jboot/test/rpc/commons/BookServiceProvider.java b/src/test/java/io/jboot/test/rpc/commons/BookServiceProvider.java new file mode 100644 index 00000000..3be92a5f --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/commons/BookServiceProvider.java @@ -0,0 +1,26 @@ +package io.jboot.test.rpc.commons; + +import com.google.common.collect.Lists; +import io.jboot.components.rpc.annotation.RPCBean; + +import java.util.List; + +@RPCBean +public class BookServiceProvider implements BookService { + + @Override + public String findById() { + System.err.println("BookServiceProvider.findById() invoked."); + return "id from BookServiceProvider"; + } + + @Override + public List findAll() { + return Lists.newArrayList("item1","item2"); + } + + @Override + public void doOther() { + + } +} diff --git a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboClientNacosDemo.java b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboClientNacosDemo.java index b1d55ef1..39ac6f30 100644 --- a/src/test/java/io/jboot/test/rpc/dubbonacos/DubboClientNacosDemo.java +++ b/src/test/java/io/jboot/test/rpc/dubbonacos/DubboClientNacosDemo.java @@ -3,6 +3,7 @@ package io.jboot.test.rpc.dubbonacos; import io.jboot.app.JbootApplication; import io.jboot.components.rpc.annotation.RPCInject; import io.jboot.test.rpc.commons.BlogService; +import io.jboot.test.rpc.commons.BookService; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; @@ -22,7 +23,6 @@ public class DubboClientNacosDemo extends JbootController { //注册中心地址,即 nacos 的地址 JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address", "127.0.0.1:8848"); - JbootApplication.run(args); } @@ -30,11 +30,18 @@ public class DubboClientNacosDemo extends JbootController { @RPCInject private BlogService blogService; + + @RPCInject + private BookService bookService; + public void index() { System.out.println("DubboClientNacosDemo.index()"); - System.out.println(blogService); - renderText(blogService.findById()); + System.out.println(blogService.findById()); + System.out.println(bookService.findById()); + + + renderText("ok"); } } -- Gitee From 78d7c82ed48f1c10a515861a949a83426b7391c2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 25 Aug 2021 18:24:14 +0800 Subject: [PATCH 1387/1965] v3.10.8 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 03a600a8..163b794e 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.10.7 + 3.10.8 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 970afa6f..9c9537cb 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.10.7 + 3.10.8 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index a611647e..84ab8520 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.10.7 + 3.10.8 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index fa849f58..2b494439 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.10.7 + 3.10.8 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.10.7', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.10.8', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 8390bf96..20c56a35 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.10.7', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.10.8', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.10.7/jboot-3.10.7.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.10.8/jboot-3.10.8.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 2ade50c5..1edd4146 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.10.7"; + public static String VERSION = "3.10.8"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From ea9e96ebdaca6e492af519a641a4f3bc555626a8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 25 Aug 2021 18:24:18 +0800 Subject: [PATCH 1388/1965] v3.10.8 release (^.^)YYa!! --- changes.txt | 8 ++++++++ pom.xml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index b7fd0999..1521516c 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,11 @@ +jboot v3.10.8: +新增:JbootCache 新增通过前缀了使用 "多实例" 的支持 +新增:Model、Db 动态设置默认数据源的支持 +新增:DatasourceConfig 新增表前缀配置的支持 +修复:由于 RPC 添加 Reference 缓存后,造成 Controller 多个 Service 无法注入的问题 + + + jboot v3.10.7: 新增:JbootHttpRequest 添加 sslContext 的配置,方便自定义 ssl 新增:JbootConfigManager 添加 setBootProperties 方法,方便用于添加启动配置 diff --git a/pom.xml b/pom.xml index 0ee54546..5835e870 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.10.8-SNAPSHOT + 3.10.8 jar jboot -- Gitee From 02483260e2a132ae1ed9407c649eab34372b4e88 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 29 Aug 2021 12:18:16 +0800 Subject: [PATCH 1389/1965] v3.10.9 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5835e870..e8b36e7e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.10.8 + 3.10.9-SNAPSHOT jar jboot -- Gitee From c1d7b7d1ac3902b3863a58d4641f6a0f4e393953 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 29 Aug 2021 12:18:51 +0800 Subject: [PATCH 1390/1965] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20JbootActionMappi?= =?UTF-8?q?ng=20=E6=8B=BC=E5=86=99=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/core/JbootCoreConfig.java | 4 ++-- .../web/{JbootAciontMapping.java => JbootActionMapping.java} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/main/java/io/jboot/web/{JbootAciontMapping.java => JbootActionMapping.java} (98%) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index b9774273..5dc6bddc 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -54,7 +54,7 @@ import io.jboot.support.swagger.JbootSwaggerConfig; import io.jboot.support.swagger.JbootSwaggerController; import io.jboot.support.swagger.JbootSwaggerManager; import io.jboot.utils.*; -import io.jboot.web.JbootAciontMapping; +import io.jboot.web.JbootActionMapping; import io.jboot.web.JbootWebConfig; import io.jboot.web.TypeConverterFunc; import io.jboot.web.attachment.AttachmentHandler; @@ -159,7 +159,7 @@ public class JbootCoreConfig extends JFinalConfig { constants.setBaseUploadPath(LocalAttachmentContainerConfig.getInstance().buildUploadAbsolutePath()); - constants.setActionMapping(JbootAciontMapping::new); + constants.setActionMapping(JbootActionMapping::new); JbootAppListenerManager.me().onConstantConfig(constants); diff --git a/src/main/java/io/jboot/web/JbootAciontMapping.java b/src/main/java/io/jboot/web/JbootActionMapping.java similarity index 98% rename from src/main/java/io/jboot/web/JbootAciontMapping.java rename to src/main/java/io/jboot/web/JbootActionMapping.java index bfd8624a..fefda077 100644 --- a/src/main/java/io/jboot/web/JbootAciontMapping.java +++ b/src/main/java/io/jboot/web/JbootActionMapping.java @@ -9,9 +9,9 @@ import io.jboot.utils.ClassUtil; import java.lang.reflect.*; -public class JbootAciontMapping extends ActionMapping { +public class JbootActionMapping extends ActionMapping { - public JbootAciontMapping(Routes routes) { + public JbootActionMapping(Routes routes) { super(routes); } -- Gitee From d63cef11abf92adece9c457bb3c4f956fed532d9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 29 Aug 2021 12:19:08 +0800 Subject: [PATCH 1391/1965] =?UTF-8?q?AttachmentManager=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=A4=9A=E5=AE=9E=E4=BE=8B=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/attachment/AttachmentManager.java | 35 ++++++++++++++++--- .../attachment/LocalAttachmentContainer.java | 4 +-- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index c6373f88..d269c57e 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -20,12 +20,13 @@ import com.jfinal.log.Log; import com.jfinal.render.IRenderFactory; import com.jfinal.render.Render; import com.jfinal.render.RenderManager; +import io.jboot.utils.StrUtil; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.InputStream; -import java.util.List; +import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; /** @@ -36,10 +37,22 @@ public class AttachmentManager { private static final Log LOG = Log.getLog(AttachmentManager.class); - private static final AttachmentManager ME = new AttachmentManager(); + private static Map managers = new HashMap<>(); public static AttachmentManager me() { - return ME; + return use("default"); + } + + public static AttachmentManager use(String name) { + AttachmentManager manager = managers.get(name); + if (manager == null) { + synchronized (AttachmentManager.class) { + if (manager == null) { + manager = new AttachmentManager(); + } + } + } + return manager; } private AttachmentManager() { @@ -207,6 +220,18 @@ public class AttachmentManager { } + /** + * 创建一个新的文件 + * 使用创建一般是创建一个空的文件,然后由外部逻辑进行写入 + * + * @param suffix + * @return + */ + public File createNewFile(String suffix) { + return getDefaultContainer().creatNewFile(suffix); + } + + /** * 渲染文件到浏览器 * @@ -216,7 +241,9 @@ public class AttachmentManager { * @return true 渲染成功,false 不进行渲染 */ public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { - if (target.startsWith(defaultContainer.getTargetPrefix()) && target.lastIndexOf('.') != -1) { + if (StrUtil.isNotBlank(defaultContainer.getTargetPrefix()) + && target.startsWith(defaultContainer.getTargetPrefix()) + && target.lastIndexOf('.') != -1) { Render render = getFileRender(getFile(target)); render.setContext(request, response).render(); return true; diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java index 8f0df7d3..71126934 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -52,7 +52,7 @@ public class LocalAttachmentContainer implements AttachmentContainer { @Override public String saveFile(File file) { - File newfile = newRandomFile(FileUtil.getSuffix(file.getName())); + File newfile = creatNewFile(FileUtil.getSuffix(file.getName())); if (!newfile.getParentFile().exists()) { newfile.getParentFile().mkdirs(); @@ -133,7 +133,7 @@ public class LocalAttachmentContainer implements AttachmentContainer { } - public File newRandomFile(String suffix) { + public File creatNewFile(String suffix) { String rootPath = getRootPath(); StringBuilder newFileName = new StringBuilder(rootPath).append(targetPrefix) -- Gitee From d93c5f166feb90b4bf33c1065ac8ca72e1c3836a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 30 Aug 2021 17:27:36 +0800 Subject: [PATCH 1392/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E5=90=8D=EF=BC=8C=E5=90=A6=E5=88=99=E5=8F=AF=E8=83=BD=E4=BC=9A?= =?UTF-8?q?=E9=80=A0=E6=88=90=E8=AF=AF=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/components/cache/AopCache.java | 8 ++++---- .../io/jboot/components/cache/JbootCache.java | 5 +++-- .../components/cache/JbootCacheBase.java | 7 ++++--- .../components/cache/JbootCacheConfig.java | 19 ++++++++++++++----- .../components/cache/JbootCacheManager.java | 4 ++-- .../components/cache/none/NoneCacheImpl.java | 4 ++-- src/main/java/io/jboot/utils/CacheUtil.java | 8 ++++---- 7 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/AopCache.java b/src/main/java/io/jboot/components/cache/AopCache.java index dd41b9c7..2f08624b 100644 --- a/src/main/java/io/jboot/components/cache/AopCache.java +++ b/src/main/java/io/jboot/components/cache/AopCache.java @@ -12,12 +12,12 @@ public class AopCache { private static JbootCache aopCache; - public static JbootCache usePrefix(String cacheNamePrefix) { - return getAopCache().use(cacheNamePrefix); + public static JbootCache setCurrentPrefix(String cacheNamePrefix) { + return getAopCache().setCurrentCacheNamePrefix(cacheNamePrefix); } - public static void clearPrefix() { - getAopCache().clear(); + public static void clearCurrentPrefix() { + getAopCache().removeCurrentCacheNamePrefix(); } static JbootCache getAopCache() { diff --git a/src/main/java/io/jboot/components/cache/JbootCache.java b/src/main/java/io/jboot/components/cache/JbootCache.java index 74d90b13..95601637 100644 --- a/src/main/java/io/jboot/components/cache/JbootCache.java +++ b/src/main/java/io/jboot/components/cache/JbootCache.java @@ -22,8 +22,9 @@ import java.util.List; public interface JbootCache extends com.jfinal.plugin.activerecord.cache.ICache { - public JbootCache use(String cacheNamePrefix); - public void clear(); + public JbootCache setCurrentCacheNamePrefix(String cacheNamePrefix); + + public void removeCurrentCacheNamePrefix(); @Override public T get(String cacheName, Object key); diff --git a/src/main/java/io/jboot/components/cache/JbootCacheBase.java b/src/main/java/io/jboot/components/cache/JbootCacheBase.java index fe6c81b2..4eca9167 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheBase.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheBase.java @@ -23,7 +23,7 @@ public abstract class JbootCacheBase implements JbootCache { private ThreadLocal CACHE_NAME_PREFIX_TL = new ThreadLocal<>(); @Override - public JbootCache use(String cacheNamePrefix) { + public JbootCache setCurrentCacheNamePrefix(String cacheNamePrefix) { if (StrUtil.isNotBlank(cacheNamePrefix)) { CACHE_NAME_PREFIX_TL.set(cacheNamePrefix); } else { @@ -32,7 +32,8 @@ public abstract class JbootCacheBase implements JbootCache { return this; } - public void clear(){ + @Override + public void removeCurrentCacheNamePrefix(){ CACHE_NAME_PREFIX_TL.remove(); } @@ -40,7 +41,7 @@ public abstract class JbootCacheBase implements JbootCache { protected String buildCacheName(String cacheName) { String cacheNamePrefix = CACHE_NAME_PREFIX_TL.get(); if (StrUtil.isBlank(cacheNamePrefix)) { - cacheNamePrefix = JbootCacheConfig.getInstance().getDefaultCacheNamePrefix(); + cacheNamePrefix = JbootCacheConfig.getInstance().getDefaultCachePrefix(); } if (StrUtil.isBlank(cacheNamePrefix)) { diff --git a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java index 9f559137..4bffdaa3 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java @@ -34,7 +34,8 @@ public class JbootCacheConfig { private String type = TYPE_CAFFEINE; - private String defaultCacheNamePrefix; + private String name = "default"; + private String defaultCachePrefix; // AOP 缓存的默认有效时间,0为永久有效,单位秒, // 当 @Cacheable 和 @CachePut 注解不配置的时候默认用这个配置 @@ -50,12 +51,20 @@ public class JbootCacheConfig { this.type = type; } - public String getDefaultCacheNamePrefix() { - return defaultCacheNamePrefix; + public String getName() { + return name; } - public void setDefaultCacheNamePrefix(String defaultCacheNamePrefix) { - this.defaultCacheNamePrefix = defaultCacheNamePrefix; + public void setName(String name) { + this.name = name; + } + + public String getDefaultCachePrefix() { + return defaultCachePrefix; + } + + public void setDefaultCachePrefix(String defaultCachePrefix) { + this.defaultCachePrefix = defaultCachePrefix; } public int getAopCacheLiveSeconds() { diff --git a/src/main/java/io/jboot/components/cache/JbootCacheManager.java b/src/main/java/io/jboot/components/cache/JbootCacheManager.java index 74fdcb55..416b9bea 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheManager.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheManager.java @@ -38,14 +38,14 @@ public class JbootCacheManager { } private Map cacheMap = new ConcurrentHashMap<>(); - private JbootCacheConfig config = Jboot.config(JbootCacheConfig.class); + private JbootCacheConfig defaultConfig = Jboot.config(JbootCacheConfig.class); public static JbootCacheManager me() { return me; } public JbootCache getCache() { - return getCache(config.getType()); + return getCache(defaultConfig.getType()); } public JbootCache getCache(String type) { diff --git a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java index c9eb8930..13c1ccf7 100644 --- a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java @@ -26,12 +26,12 @@ import java.util.List; */ public class NoneCacheImpl implements JbootCache { @Override - public JbootCache use(String cacheNamePrefix) { + public JbootCache setCurrentCacheNamePrefix(String cacheNamePrefix) { return this; } @Override - public void clear() { + public void removeCurrentCacheNamePrefix() { } diff --git a/src/main/java/io/jboot/utils/CacheUtil.java b/src/main/java/io/jboot/utils/CacheUtil.java index fd2b00cc..678a24da 100644 --- a/src/main/java/io/jboot/utils/CacheUtil.java +++ b/src/main/java/io/jboot/utils/CacheUtil.java @@ -33,12 +33,12 @@ public class CacheUtil { return JbootCacheManager.me().getCache(type); } - public static JbootCache usePrefix(String cacheNamePrefix) { - return JbootCacheManager.me().getCache().use(cacheNamePrefix); + public static JbootCache setCurrentPrefix(String cacheNamePrefix) { + return JbootCacheManager.me().getCache().setCurrentCacheNamePrefix(cacheNamePrefix); } - public static void clearPrefix() { - JbootCacheManager.me().getCache().clear(); + public static void clearCurrentPrefix() { + JbootCacheManager.me().getCache().removeCurrentCacheNamePrefix(); } public static T get(String cacheName, Object key) { -- Gitee From 11c6f890e9c897abd26b04f038d1abe017f1e119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=A1=E7=89=99=E7=9A=84=E5=B0=8F=E6=9C=8B=E5=8F=8B?= Date: Tue, 31 Aug 2021 17:15:13 +0800 Subject: [PATCH 1393/1965] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E7=8E=B0=E6=9C=89s?= =?UTF-8?q?wagger=E5=AE=9E=E7=8E=B0=E4=B8=AD=EF=BC=8C=E4=BC=9A=E5=BF=BD?= =?UTF-8?q?=E7=95=A5ActionKey=E6=B3=A8=E8=A7=A3=E4=B8=AD=E7=9A=84URL?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/support/swagger/Reader.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/support/swagger/Reader.java b/src/main/java/io/jboot/support/swagger/Reader.java index 1cf1ef4a..a1702aeb 100644 --- a/src/main/java/io/jboot/support/swagger/Reader.java +++ b/src/main/java/io/jboot/support/swagger/Reader.java @@ -15,6 +15,7 @@ */ package io.jboot.support.swagger; +import com.jfinal.core.ActionKey; import com.jfinal.core.Controller; import io.jboot.web.controller.JbootControllerManager; import io.swagger.models.Operation; @@ -72,7 +73,11 @@ public class Reader { String methodPath = "index".equals(method.getName()) ? "" : "/" + method.getName(); String operationPath = JbootControllerManager.me().getPathByController((Class) context.getCls()) + methodPath; - + //如果有ActionKey注解的URL路径,则使用该路径而不是方法名 + ActionKey actionKeyAnnotation = ReflectionUtils.getAnnotation(method, ActionKey.class); + if(actionKeyAnnotation != null && !actionKeyAnnotation.value().isEmpty()){ + operationPath = actionKeyAnnotation.value(); + } String httpMethod = extension.getHttpMethod(context, method); if (operationPath == null || httpMethod == null) { -- Gitee From 929d4fcbe358cfa3ce67321edeb771564c935bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=A1=E7=89=99=E7=9A=84=E5=B0=8F=E6=9C=8B=E5=8F=8B?= Date: Tue, 31 Aug 2021 17:21:48 +0800 Subject: [PATCH 1394/1965] =?UTF-8?q?action=E6=94=AF=E6=8C=81=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E5=8F=98=E9=87=8F=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加了PathVariableActionHandler和PathVariableActionMapping --- .../java/io/jboot/utils/AntPathMatcher.java | 846 ++++++++++++++++++ .../jboot/web/PathVariableActionMapping.java | 194 ++++ .../handler/PathVariableActionHandler.java | 182 ++++ 3 files changed, 1222 insertions(+) create mode 100644 src/main/java/io/jboot/utils/AntPathMatcher.java create mode 100644 src/main/java/io/jboot/web/PathVariableActionMapping.java create mode 100644 src/main/java/io/jboot/web/handler/PathVariableActionHandler.java diff --git a/src/main/java/io/jboot/utils/AntPathMatcher.java b/src/main/java/io/jboot/utils/AntPathMatcher.java new file mode 100644 index 00000000..c777c050 --- /dev/null +++ b/src/main/java/io/jboot/utils/AntPathMatcher.java @@ -0,0 +1,846 @@ +package io.jboot.utils; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class AntPathMatcher { + + /** + * Default path separator: "/". + */ + public static final String DEFAULT_PATH_SEPARATOR = "/"; + + private static final int CACHE_TURNOFF_THRESHOLD = 65536; + + private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{[^/]+?}"); + + private static final char[] WILDCARD_CHARS = {'*', '?', '{'}; + + + private String pathSeparator; + + private PathSeparatorPatternCache pathSeparatorPatternCache; + + private boolean caseSensitive = true; + + private boolean trimTokens = false; + + + private volatile Boolean cachePatterns; + + private final Map tokenizedPatternCache = new ConcurrentHashMap<>(256); + + final Map stringMatcherCache = new ConcurrentHashMap<>(256); + + + /** + * Create a new instance with the {@link #DEFAULT_PATH_SEPARATOR}. + */ + public AntPathMatcher() { + this.pathSeparator = DEFAULT_PATH_SEPARATOR; + this.pathSeparatorPatternCache = new PathSeparatorPatternCache(DEFAULT_PATH_SEPARATOR); + } + + /** + * A convenient, alternative constructor to use with a custom path separator. + * + * @param pathSeparator the path separator to use, must not be {@code null}. + */ + public AntPathMatcher(String pathSeparator) { + this.pathSeparator = pathSeparator; + this.pathSeparatorPatternCache = new PathSeparatorPatternCache(pathSeparator); + } + + + /** + * Set the path separator to use for pattern parsing. + *

        Default is "/", as in Ant. + */ + public void setPathSeparator(String pathSeparator) { + this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR); + this.pathSeparatorPatternCache = new PathSeparatorPatternCache(this.pathSeparator); + } + + /** + * Specify whether to perform pattern matching in a case-sensitive fashion. + *

        Default is {@code true}. Switch this to {@code false} for case-insensitive matching. * + */ + public void setCaseSensitive(boolean caseSensitive) { + this.caseSensitive = caseSensitive; + } + + /** + * Specify whether to trim tokenized paths and patterns. + *

        Default is {@code false}. + */ + public void setTrimTokens(boolean trimTokens) { + this.trimTokens = trimTokens; + } + + /** + * Specify whether to cache parsed pattern metadata for patterns passed + * into this matcher's {@link #match} method. A value of {@code true} + * activates an unlimited pattern cache; a value of {@code false} turns + * the pattern cache off completely. + *

        Default is for the cache to be on, but with the variant to automatically + * turn it off when encountering too many patterns to cache at runtime + * (the threshold is 65536), assuming that arbitrary permutations of patterns + * are coming in, with little chance for encountering a recurring pattern. + * + * @see #getStringMatcher(String) + */ + public void setCachePatterns(boolean cachePatterns) { + this.cachePatterns = cachePatterns; + } + + private void deactivatePatternCache() { + this.cachePatterns = false; + this.tokenizedPatternCache.clear(); + this.stringMatcherCache.clear(); + } + + + public boolean isPattern(String path) { + if (path == null) { + return false; + } + boolean uriVar = false; + for (int i = 0; i < path.length(); i++) { + char c = path.charAt(i); + if (c == '*' || c == '?') { + return true; + } + if (c == '{') { + uriVar = true; + continue; + } + if (c == '}' && uriVar) { + return true; + } + } + return false; + } + + + public boolean match(String pattern, String path) { + return doMatch(pattern, path, true, null); + } + + + public boolean matchStart(String pattern, String path) { + return doMatch(pattern, path, false, null); + } + + /** + * Actually match the given {@code path} against the given {@code pattern}. + * + * @param pattern the pattern to match against + * @param path the path to test + * @param fullMatch whether a full pattern match is required (else a pattern match + * as far as the given base path goes is sufficient) + * @return {@code true} if the supplied {@code path} matched, {@code false} if it didn't + */ + protected boolean doMatch(String pattern, String path, boolean fullMatch, + Map uriTemplateVariables) { + + if (path == null || path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) { + return false; + } + + String[] pattDirs = tokenizePattern(pattern); + if (fullMatch && this.caseSensitive && !isPotentialMatch(path, pattDirs)) { + return false; + } + + String[] pathDirs = tokenizePath(path); + int pattIdxStart = 0; + int pattIdxEnd = pattDirs.length - 1; + int pathIdxStart = 0; + int pathIdxEnd = pathDirs.length - 1; + + // Match all elements up to the first ** + while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) { + String pattDir = pattDirs[pattIdxStart]; + if ("**".equals(pattDir)) { + break; + } + if (!matchStrings(pattDir, pathDirs[pathIdxStart], uriTemplateVariables)) { + return false; + } + pattIdxStart++; + pathIdxStart++; + } + + if (pathIdxStart > pathIdxEnd) { + // Path is exhausted, only match if rest of pattern is * or **'s + if (pattIdxStart > pattIdxEnd) { + return (pattern.endsWith(this.pathSeparator) == path.endsWith(this.pathSeparator)); + } + if (!fullMatch) { + return true; + } + if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) { + return true; + } + for (int i = pattIdxStart; i <= pattIdxEnd; i++) { + if (!pattDirs[i].equals("**")) { + return false; + } + } + return true; + } else if (pattIdxStart > pattIdxEnd) { + // String not exhausted, but pattern is. Failure. + return false; + } else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) { + // Path start definitely matches due to "**" part in pattern. + return true; + } + + // up to last '**' + while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) { + String pattDir = pattDirs[pattIdxEnd]; + if (pattDir.equals("**")) { + break; + } + if (!matchStrings(pattDir, pathDirs[pathIdxEnd], uriTemplateVariables)) { + return false; + } + pattIdxEnd--; + pathIdxEnd--; + } + if (pathIdxStart > pathIdxEnd) { + // String is exhausted + for (int i = pattIdxStart; i <= pattIdxEnd; i++) { + if (!pattDirs[i].equals("**")) { + return false; + } + } + return true; + } + + while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) { + int patIdxTmp = -1; + for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) { + if (pattDirs[i].equals("**")) { + patIdxTmp = i; + break; + } + } + if (patIdxTmp == pattIdxStart + 1) { + // '**/**' situation, so skip one + pattIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = (patIdxTmp - pattIdxStart - 1); + int strLength = (pathIdxEnd - pathIdxStart + 1); + int foundIdx = -1; + + strLoop: + for (int i = 0; i <= strLength - patLength; i++) { + for (int j = 0; j < patLength; j++) { + String subPat = pattDirs[pattIdxStart + j + 1]; + String subStr = pathDirs[pathIdxStart + i + j]; + if (!matchStrings(subPat, subStr, uriTemplateVariables)) { + continue strLoop; + } + } + foundIdx = pathIdxStart + i; + break; + } + + if (foundIdx == -1) { + return false; + } + + pattIdxStart = patIdxTmp; + pathIdxStart = foundIdx + patLength; + } + + for (int i = pattIdxStart; i <= pattIdxEnd; i++) { + if (!pattDirs[i].equals("**")) { + return false; + } + } + + return true; + } + + private boolean isPotentialMatch(String path, String[] pattDirs) { + if (!this.trimTokens) { + int pos = 0; + for (String pattDir : pattDirs) { + int skipped = skipSeparator(path, pos, this.pathSeparator); + pos += skipped; + skipped = skipSegment(path, pos, pattDir); + if (skipped < pattDir.length()) { + return (skipped > 0 || (pattDir.length() > 0 && isWildcardChar(pattDir.charAt(0)))); + } + pos += skipped; + } + } + return true; + } + + private int skipSegment(String path, int pos, String prefix) { + int skipped = 0; + for (int i = 0; i < prefix.length(); i++) { + char c = prefix.charAt(i); + if (isWildcardChar(c)) { + return skipped; + } + int currPos = pos + skipped; + if (currPos >= path.length()) { + return 0; + } + if (c == path.charAt(currPos)) { + skipped++; + } + } + return skipped; + } + + private int skipSeparator(String path, int pos, String separator) { + int skipped = 0; + while (path.startsWith(separator, pos + skipped)) { + skipped += separator.length(); + } + return skipped; + } + + private boolean isWildcardChar(char c) { + for (char candidate : WILDCARD_CHARS) { + if (c == candidate) { + return true; + } + } + return false; + } + + /** + * Tokenize the given path pattern into parts, based on this matcher's settings. + *

        Performs caching based on {@link #setCachePatterns}, delegating to + * {@link #tokenizePath(String)} for the actual tokenization algorithm. + * + * @param pattern the pattern to tokenize + * @return the tokenized pattern parts + */ + protected String[] tokenizePattern(String pattern) { + String[] tokenized = null; + Boolean cachePatterns = this.cachePatterns; + if (cachePatterns == null || cachePatterns.booleanValue()) { + tokenized = this.tokenizedPatternCache.get(pattern); + } + if (tokenized == null) { + tokenized = tokenizePath(pattern); + if (cachePatterns == null && this.tokenizedPatternCache.size() >= CACHE_TURNOFF_THRESHOLD) { + // Try to adapt to the runtime situation that we're encountering: + // There are obviously too many different patterns coming in here... + // So let's turn off the cache since the patterns are unlikely to be reoccurring. + deactivatePatternCache(); + return tokenized; + } + if (cachePatterns == null || cachePatterns.booleanValue()) { + this.tokenizedPatternCache.put(pattern, tokenized); + } + } + return tokenized; + } + + /** + * Tokenize the given path into parts, based on this matcher's settings. + * + * @param path the path to tokenize + * @return the tokenized path parts + */ + protected String[] tokenizePath(String path) { + return StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true); + } + + /** + * Test whether or not a string matches against a pattern. + * + * @param pattern the pattern to match against (never {@code null}) + * @param str the String which must be matched against the pattern (never {@code null}) + * @return {@code true} if the string matches against the pattern, or {@code false} otherwise + */ + private boolean matchStrings(String pattern, String str, + Map uriTemplateVariables) { + + return getStringMatcher(pattern).matchStrings(str, uriTemplateVariables); + } + + /** + * Build or retrieve an {@link AntPathStringMatcher} for the given pattern. + *

        The default implementation checks this AntPathMatcher's internal cache + * (see {@link #setCachePatterns}), creating a new AntPathStringMatcher instance + * if no cached copy is found. + *

        When encountering too many patterns to cache at runtime (the threshold is 65536), + * it turns the default cache off, assuming that arbitrary permutations of patterns + * are coming in, with little chance for encountering a recurring pattern. + *

        This method may be overridden to implement a custom cache strategy. + * + * @param pattern the pattern to match against (never {@code null}) + * @return a corresponding AntPathStringMatcher (never {@code null}) + * @see #setCachePatterns + */ + protected AntPathStringMatcher getStringMatcher(String pattern) { + AntPathStringMatcher matcher = null; + Boolean cachePatterns = this.cachePatterns; + if (cachePatterns == null || cachePatterns.booleanValue()) { + matcher = this.stringMatcherCache.get(pattern); + } + if (matcher == null) { + matcher = new AntPathStringMatcher(pattern, this.caseSensitive); + if (cachePatterns == null && this.stringMatcherCache.size() >= CACHE_TURNOFF_THRESHOLD) { + // Try to adapt to the runtime situation that we're encountering: + // There are obviously too many different patterns coming in here... + // So let's turn off the cache since the patterns are unlikely to be reoccurring. + deactivatePatternCache(); + return matcher; + } + if (cachePatterns == null || cachePatterns.booleanValue()) { + this.stringMatcherCache.put(pattern, matcher); + } + } + return matcher; + } + + /** + * Given a pattern and a full path, determine the pattern-mapped part.

        For example:

          + *
        • '{@code /docs/cvs/commit.html}' and '{@code /docs/cvs/commit.html} -> ''
        • + *
        • '{@code /docs/*}' and '{@code /docs/cvs/commit} -> '{@code cvs/commit}'
        • + *
        • '{@code /docs/cvs/*.html}' and '{@code /docs/cvs/commit.html} -> '{@code commit.html}'
        • + *
        • '{@code /docs/**}' and '{@code /docs/cvs/commit} -> '{@code cvs/commit}'
        • + *
        • '{@code /docs/**\/*.html}' and '{@code /docs/cvs/commit.html} -> '{@code cvs/commit.html}'
        • + *
        • '{@code /*.html}' and '{@code /docs/cvs/commit.html} -> '{@code docs/cvs/commit.html}'
        • + *
        • '{@code *.html}' and '{@code /docs/cvs/commit.html} -> '{@code /docs/cvs/commit.html}'
        • + *
        • '{@code *}' and '{@code /docs/cvs/commit.html} -> '{@code /docs/cvs/commit.html}'
        + *

        Assumes that {@link #match} returns {@code true} for '{@code pattern}' and '{@code path}', but + * does not enforce this. + */ + + public String extractPathWithinPattern(String pattern, String path) { + String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true); + String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true); + StringBuilder builder = new StringBuilder(); + boolean pathStarted = false; + + for (int segment = 0; segment < patternParts.length; segment++) { + String patternPart = patternParts[segment]; + if (patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) { + for (; segment < pathParts.length; segment++) { + if (pathStarted || (segment == 0 && !pattern.startsWith(this.pathSeparator))) { + builder.append(this.pathSeparator); + } + builder.append(pathParts[segment]); + pathStarted = true; + } + } + } + + return builder.toString(); + } + + + public Map extractUriTemplateVariables(String pattern, String path) { + Map variables = new LinkedHashMap<>(); + boolean result = doMatch(pattern, path, true, variables); + if (!result) { + throw new IllegalStateException("Pattern \"" + pattern + "\" is not a match for \"" + path + "\""); + } + return variables; + } + + /** + * Given a full path, returns a {@link Comparator} suitable for sorting patterns in order of + * explicitness. + *

        This {@code Comparator} will {@linkplain List#sort(Comparator) sort} + * a list so that more specific patterns (without URI templates or wild cards) come before + * generic patterns. So given a list with the following patterns, the returned comparator + * will sort this list so that the order will be as indicated. + *

          + *
        1. {@code /hotels/new}
        2. + *
        3. {@code /hotels/{hotel}}
        4. + *
        5. {@code /hotels/*}
        6. + *
        + *

        The full path given as parameter is used to test for exact matches. So when the given path + * is {@code /hotels/2}, the pattern {@code /hotels/2} will be sorted before {@code /hotels/1}. + * + * @param path the full path to use for comparison + * @return a comparator capable of sorting patterns in order of explicitness + */ + + public Comparator getPatternComparator(String path) { + return new AntPatternComparator(path); + } + + + /** + * Tests whether or not a string matches against a pattern via a {@link Pattern}. + *

        The pattern may contain special characters: '*' means zero or more characters; '?' means one and + * only one character; '{' and '}' indicate a URI template pattern. For example /users/{user}. + */ + protected static class AntPathStringMatcher { + + private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?}|[^/{}]|\\\\[{}])+?)}"); + + private static final String DEFAULT_VARIABLE_PATTERN = "((?s).*)"; + + private final String rawPattern; + + private final boolean caseSensitive; + + private final boolean exactMatch; + + + private final Pattern pattern; + + private final List variableNames = new ArrayList<>(); + + public AntPathStringMatcher(String pattern) { + this(pattern, true); + } + + public AntPathStringMatcher(String pattern, boolean caseSensitive) { + this.rawPattern = pattern; + this.caseSensitive = caseSensitive; + StringBuilder patternBuilder = new StringBuilder(); + Matcher matcher = GLOB_PATTERN.matcher(pattern); + int end = 0; + while (matcher.find()) { + patternBuilder.append(quote(pattern, end, matcher.start())); + String match = matcher.group(); + if ("?".equals(match)) { + patternBuilder.append('.'); + } else if ("*".equals(match)) { + patternBuilder.append(".*"); + } else if (match.startsWith("{") && match.endsWith("}")) { + int colonIdx = match.indexOf(':'); + if (colonIdx == -1) { + patternBuilder.append(DEFAULT_VARIABLE_PATTERN); + this.variableNames.add(matcher.group(1)); + } else { + String variablePattern = match.substring(colonIdx + 1, match.length() - 1); + patternBuilder.append('('); + patternBuilder.append(variablePattern); + patternBuilder.append(')'); + String variableName = match.substring(1, colonIdx); + this.variableNames.add(variableName); + } + } + end = matcher.end(); + } + // No glob pattern was found, this is an exact String match + if (end == 0) { + this.exactMatch = true; + this.pattern = null; + } else { + this.exactMatch = false; + patternBuilder.append(quote(pattern, end, pattern.length())); + this.pattern = (this.caseSensitive ? Pattern.compile(patternBuilder.toString()) : + Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE)); + } + } + + private String quote(String s, int start, int end) { + if (start == end) { + return ""; + } + return Pattern.quote(s.substring(start, end)); + } + + /** + * Main entry point. + * + * @return {@code true} if the string matches against the pattern, or {@code false} otherwise. + */ + public boolean matchStrings(String str, Map uriTemplateVariables) { + if (this.exactMatch) { + return this.caseSensitive ? this.rawPattern.equals(str) : this.rawPattern.equalsIgnoreCase(str); + } else if (this.pattern != null) { + Matcher matcher = this.pattern.matcher(str); + if (matcher.matches()) { + if (uriTemplateVariables != null) { + if (this.variableNames.size() != matcher.groupCount()) { + throw new IllegalArgumentException("The number of capturing groups in the pattern segment " + + this.pattern + " does not match the number of URI template variables it defines, " + + "which can occur if capturing groups are used in a URI template regex. " + + "Use non-capturing groups instead."); + } + for (int i = 1; i <= matcher.groupCount(); i++) { + String name = this.variableNames.get(i - 1); + String value = matcher.group(i); + uriTemplateVariables.put(name, value); + } + } + return true; + } + } + return false; + } + + } + + + /** + * The default {@link Comparator} implementation returned by + * {@link #getPatternComparator(String)}. + *

        In order, the most "generic" pattern is determined by the following: + *

          + *
        • if it's null or a capture all pattern (i.e. it is equal to "/**")
        • + *
        • if the other pattern is an actual match
        • + *
        • if it's a catch-all pattern (i.e. it ends with "**"
        • + *
        • if it's got more "*" than the other pattern
        • + *
        • if it's got more "{foo}" than the other pattern
        • + *
        • if it's shorter than the other pattern
        • + *
        + */ + protected static class AntPatternComparator implements Comparator { + + private final String path; + + public AntPatternComparator(String path) { + this.path = path; + } + + /** + * Compare two patterns to determine which should match first, i.e. which + * is the most specific regarding the current path. + * + * @return a negative integer, zero, or a positive integer as pattern1 is + * more specific, equally specific, or less specific than pattern2. + */ + + public int compare(String pattern1, String pattern2) { + PatternInfo info1 = new PatternInfo(pattern1); + PatternInfo info2 = new PatternInfo(pattern2); + + if (info1.isLeastSpecific() && info2.isLeastSpecific()) { + return 0; + } else if (info1.isLeastSpecific()) { + return 1; + } else if (info2.isLeastSpecific()) { + return -1; + } + + boolean pattern1EqualsPath = pattern1.equals(this.path); + boolean pattern2EqualsPath = pattern2.equals(this.path); + if (pattern1EqualsPath && pattern2EqualsPath) { + return 0; + } else if (pattern1EqualsPath) { + return -1; + } else if (pattern2EqualsPath) { + return 1; + } + + if (info1.isPrefixPattern() && info2.isPrefixPattern()) { + return info2.getLength() - info1.getLength(); + } else if (info1.isPrefixPattern() && info2.getDoubleWildcards() == 0) { + return 1; + } else if (info2.isPrefixPattern() && info1.getDoubleWildcards() == 0) { + return -1; + } + + if (info1.getTotalCount() != info2.getTotalCount()) { + return info1.getTotalCount() - info2.getTotalCount(); + } + + if (info1.getLength() != info2.getLength()) { + return info2.getLength() - info1.getLength(); + } + + if (info1.getSingleWildcards() < info2.getSingleWildcards()) { + return -1; + } else if (info2.getSingleWildcards() < info1.getSingleWildcards()) { + return 1; + } + + if (info1.getUriVars() < info2.getUriVars()) { + return -1; + } else if (info2.getUriVars() < info1.getUriVars()) { + return 1; + } + + return 0; + } + + + /** + * Value class that holds information about the pattern, e.g. number of + * occurrences of "*", "**", and "{" pattern elements. + */ + private static class PatternInfo { + + + private final String pattern; + + private int uriVars; + + private int singleWildcards; + + private int doubleWildcards; + + private boolean catchAllPattern; + + private boolean prefixPattern; + + + private Integer length; + + public PatternInfo(String pattern) { + this.pattern = pattern; + if (this.pattern != null) { + initCounters(); + this.catchAllPattern = this.pattern.equals("/**"); + this.prefixPattern = !this.catchAllPattern && this.pattern.endsWith("/**"); + } + if (this.uriVars == 0) { + this.length = (this.pattern != null ? this.pattern.length() : 0); + } + } + + protected void initCounters() { + int pos = 0; + if (this.pattern != null) { + while (pos < this.pattern.length()) { + if (this.pattern.charAt(pos) == '{') { + this.uriVars++; + pos++; + } else if (this.pattern.charAt(pos) == '*') { + if (pos + 1 < this.pattern.length() && this.pattern.charAt(pos + 1) == '*') { + this.doubleWildcards++; + pos += 2; + } else if (pos > 0 && !this.pattern.substring(pos - 1).equals(".*")) { + this.singleWildcards++; + pos++; + } else { + pos++; + } + } else { + pos++; + } + } + } + } + + public int getUriVars() { + return this.uriVars; + } + + public int getSingleWildcards() { + return this.singleWildcards; + } + + public int getDoubleWildcards() { + return this.doubleWildcards; + } + + public boolean isLeastSpecific() { + return (this.pattern == null || this.catchAllPattern); + } + + public boolean isPrefixPattern() { + return this.prefixPattern; + } + + public int getTotalCount() { + return this.uriVars + this.singleWildcards + (2 * this.doubleWildcards); + } + + /** + * Returns the length of the given pattern, where template variables are considered to be 1 long. + */ + public int getLength() { + if (this.length == null) { + this.length = (this.pattern != null ? + VARIABLE_PATTERN.matcher(this.pattern).replaceAll("#").length() : 0); + } + return this.length; + } + } + } + + + /** + * A simple cache for patterns that depend on the configured path separator. + */ + private static class PathSeparatorPatternCache { + + private final String endsOnWildCard; + + private final String endsOnDoubleWildCard; + + public PathSeparatorPatternCache(String pathSeparator) { + this.endsOnWildCard = pathSeparator + "*"; + this.endsOnDoubleWildCard = pathSeparator + "**"; + } + + public String getEndsOnWildCard() { + return this.endsOnWildCard; + } + + public String getEndsOnDoubleWildCard() { + return this.endsOnDoubleWildCard; + } + } + +} + +abstract class StringUtils { + + private static final String[] EMPTY_STRING_ARRAY = {}; + + private static final String FOLDER_SEPARATOR = "/"; + + private static final String WINDOWS_FOLDER_SEPARATOR = "\\"; + + private static final String TOP_PATH = ".."; + + private static final String CURRENT_PATH = "."; + + private static final char EXTENSION_SEPARATOR = '.'; + + + public static String[] tokenizeToStringArray(String str, String delimiters) { + return tokenizeToStringArray(str, delimiters, true, true); + } + + public static String[] tokenizeToStringArray( + String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { + + if (str == null) { + return EMPTY_STRING_ARRAY; + } + + StringTokenizer st = new StringTokenizer(str, delimiters); + List tokens = new ArrayList<>(); + while (st.hasMoreTokens()) { + String token = st.nextToken(); + if (trimTokens) { + token = token.trim(); + } + if (!ignoreEmptyTokens || token.length() > 0) { + tokens.add(token); + } + } + return toStringArray(tokens); + } + + /** + * Copy the given {@link Collection} into a {@code String} array. + *

        The {@code Collection} must contain {@code String} elements only. + * + * @param collection the {@code Collection} to copy + * (potentially {@code null} or empty) + * @return the resulting {@code String} array + */ + public static String[] toStringArray(Collection collection) { + return ((collection != null && collection.size() != 0) ? collection.toArray(EMPTY_STRING_ARRAY) : EMPTY_STRING_ARRAY); + } + +} \ No newline at end of file diff --git a/src/main/java/io/jboot/web/PathVariableActionMapping.java b/src/main/java/io/jboot/web/PathVariableActionMapping.java new file mode 100644 index 00000000..f473afcf --- /dev/null +++ b/src/main/java/io/jboot/web/PathVariableActionMapping.java @@ -0,0 +1,194 @@ +package io.jboot.web; + +import com.google.common.base.Joiner; +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.InterceptorManager; +import com.jfinal.config.Routes; +import com.jfinal.core.Action; +import com.jfinal.core.ActionKey; +import com.jfinal.core.Controller; +import com.jfinal.core.NotAction; +import io.jboot.utils.AntPathMatcher; +import io.jboot.utils.ArrayUtil; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class PathVariableActionMapping extends JbootActionMapping { + private static final String PATH_VARIABLE_URL_PATTERN = ".*\\{[a-zA-Z0-9]+\\}.*"; + protected Map pathVariableUrlMapping = new ConcurrentHashMap<>(); + private static final AntPathMatcher antPathMatcher = new AntPathMatcher(); + + public PathVariableActionMapping(Routes routes) { + super(routes); + } + + @Override + protected void buildActionMapping() { + mapping.clear(); + Class dc; + InterceptorManager interMan = InterceptorManager.me(); + for (Routes routes : getRoutesList()) { + for (Routes.Route route : routes.getRouteItemList()) { + Class controllerClass = route.getControllerClass(); + Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass); + + boolean declaredMethods = !routes.getMappingSuperClass() || controllerClass.getSuperclass() == Controller.class; + + Method[] methods = (declaredMethods ? controllerClass.getDeclaredMethods() : controllerClass.getMethods()); + for (Method method : methods) { + if (declaredMethods) { + if (!Modifier.isPublic(method.getModifiers())) + continue; + } else { + dc = method.getDeclaringClass(); + if (dc == Controller.class || dc == Object.class) + continue; + } + + if (method.getAnnotation(NotAction.class) != null) { + continue; + } + + Interceptor[] actionInters = interMan.buildControllerActionInterceptor(routes.getInterceptors(), controllerInters, controllerClass, method); + String controllerPath = route.getControllerPath(); + + String methodName = method.getName(); + ActionKey ak = method.getAnnotation(ActionKey.class); + String actionKey; + if (ak != null) { + actionKey = ak.value().trim(); + if ("".equals(actionKey)) { + throw new IllegalArgumentException(controllerClass.getName() + "." + methodName + "(): The argument of ActionKey can not be blank."); + } + if (actionKey.matches(PATH_VARIABLE_URL_PATTERN)) { + Action pathVariableAction = new Action(controllerPath, actionKey, controllerClass, method, methodName, actionInters, + route.getFinalViewPath(routes.getBaseViewPath())); + pathVariableUrlMapping.put(actionKey, pathVariableAction); + } + if (actionKey.startsWith(SLASH)) { + //actionKey = actionKey + } else if (actionKey.startsWith("./")) { + actionKey = controllerPath + actionKey.substring(1); + } else { + actionKey = SLASH + actionKey; + } +// if (!actionKey.startsWith(SLASH)) { +// actionKey = SLASH + actionKey; +// } + } else if (methodName.equals("index")) { + actionKey = controllerPath; + } else { + actionKey = controllerPath.equals(SLASH) ? SLASH + methodName : controllerPath + SLASH + methodName; + } + +// Action action = new Action(controllerPath, actionKey, controllerClass, method, methodName, actionInters, route.getFinalViewPath(routes.getBaseViewPath())); +// if (mapping.put(actionKey, action) != null) { +// throw new RuntimeException(buildMsg(actionKey, controllerClass, method)); +// } + + Action newAction = new Action(controllerPath, actionKey, controllerClass, method, methodName, actionInters, route.getFinalViewPath(routes.getBaseViewPath())); + Action existAction = mapping.get(actionKey); + if (existAction == null) { + mapping.put(actionKey, newAction); + } else { + + Type controllerType = controllerClass.getGenericSuperclass(); + Method existActionMethod = existAction.getMethod(); + + // 不是泛型 + if (!(controllerType instanceof ParameterizedType)) { + throw new RuntimeException(buildMsg(actionKey, method, existActionMethod)); + } + + if (method.getParameterCount() == 0 + || method.getParameterCount() != existActionMethod.getParameterCount() + || method.getDeclaringClass() != existActionMethod.getDeclaringClass()) { + throw new RuntimeException(buildMsg(actionKey, method, existActionMethod)); + } + + Type[] argumentTypes = ((ParameterizedType) controllerType).getActualTypeArguments(); + + Class[] paraTypes = method.getParameterTypes(); + Class[] existParaTypes = existActionMethod.getParameterTypes(); + + for (int i = 0; i < paraTypes.length; i++) { + Class newType = paraTypes[i]; + Class existType = existParaTypes[i]; + if (newType == existType) { + continue; + } + // newType 是父类 + else if (newType.isAssignableFrom(existType) && ArrayUtil.contains(argumentTypes, existType)) { + break; + } + // newType 是子类 + else if (existType.isAssignableFrom(newType) && ArrayUtil.contains(argumentTypes, newType)) { + mapping.put(actionKey, newAction); + break; + } else { + throw new RuntimeException(buildMsg(actionKey, method, existActionMethod)); + } + } + + } + } + } + } + routes.clear(); + + // support url = controllerPath + urlParas with "/" of controllerPath + Action action = mapping.get("/"); + if (action != null) { + mapping.put("", action); + } + } + + + + + /** + * Support four types of url + * 1: http://abc.com/controllerPath ---> 00 + * 2: http://abc.com/controllerPath/para ---> 01 + * 3: http://abc.com/controllerPath/method ---> 10 + * 4: http://abc.com/controllerPath/method/para ---> 11 + * 5: http://abc.com/foo/{id}/bar/{name} + * The controllerPath can also contains "/" + * Example: http://abc.com/uvw/xyz/method/para + */ + @Override + public Action getAction(String url, String[] urlPara) { + Action action = mapping.get(url); + if (action != null) { + return action; + } + for (String pattern : pathVariableUrlMapping.keySet()) { + //判断是否有匹配包含路径参数的URL映射 + if (antPathMatcher.match(pattern, url)) { + Action pathVariableUrlAction = pathVariableUrlMapping.get(pattern); + Map pathVariableValues = antPathMatcher.extractUriTemplateVariables(pattern, url); + urlPara[0] = null; + if (urlPara.length > 1) { + //urlPara[1]作为路径参数传入controller + urlPara[1] = Joiner.on("&").withKeyValueSeparator("=").join(pathVariableValues); + } + return pathVariableUrlAction; + } + } + // -------- + int i = url.lastIndexOf('/'); + if (i != -1) { + action = mapping.get(url.substring(0, i)); + if (action != null) { + urlPara[0] = url.substring(i + 1); + } + } + + return action; + } +} diff --git a/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java b/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java new file mode 100644 index 00000000..a079ac0d --- /dev/null +++ b/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.handler; + +import com.google.common.base.Splitter; +import com.jfinal.aop.Invocation; +import com.jfinal.core.*; +import com.jfinal.log.Log; +import com.jfinal.render.Render; +import com.jfinal.render.RenderException; +import io.jboot.components.valid.ValidException; +import io.jboot.web.controller.JbootControllerContext; +import io.jboot.web.render.JbootRenderFactory; +import io.jboot.web.session.JbootServletRequestWrapper; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Collections; +import java.util.Map; +import java.util.TreeMap; + +/** + * @author 没牙的小朋友 (mjl@nxu.edu.cn) + * @version V1.0 + */ +public class PathVariableActionHandler extends JbootActionHandler { + + private static final Log LOG = Log.getLog(PathVariableActionHandler.class); + + /** + * handle + * 1: Action action = actionMapping.getAction(target) + * 2: new Invocation(...).invoke() + * 3: render(...) + */ + @Override + public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { + if (target.lastIndexOf('.') != -1) { + return; + } + + isHandled[0] = true; + //urlPara数组增加第2个元素存储路径参数 + String[] urlPara = {null, null}; + Action action = getAction(target, urlPara, request); + + if (action == null) { + if (LOG.isWarnEnabled()) { + String qs = request.getQueryString(); + LOG.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs)); + } + renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); + return; + } + + + Controller controller = null; + try { + controller = controllerFactory.getController(action.getControllerClass()); + //controller.init(request, response, urlPara[0]); + //存在封装的路径参数 + if(urlPara[1] != null){ + Map params = Splitter.on("&").withKeyValueSeparator("=").split(urlPara[1]); + PathVariableWrappedRequest wrappedRequest = new PathVariableWrappedRequest(request, response, params); + CPI._init_(controller, action, wrappedRequest, response, urlPara[0]); + }else { + CPI._init_(controller, action, request, response, urlPara[0]); + } + JbootControllerContext.hold(controller); + + //Invocation invocation = new Invocation(action, controller); + Invocation invocation = getInvocation(action, controller); + + if (JbootActionReporter.isReportEnable()) { + long time = System.currentTimeMillis(); + try { + doStartRender(target, request, response, isHandled, action, controller, invocation); + } finally { + JbootActionReporter.report(target, controller, action, invocation, time); + } + } else { + doStartRender(target, request, response, isHandled, action, controller, invocation); + } + + } catch (RenderException e) { + if (LOG.isErrorEnabled()) { + String qs = request.getQueryString(); + LOG.error(qs == null ? target : target + "?" + qs, e); + } + } catch (ActionException e) { + handleActionException(target, request, response, action, e); + } catch (ValidException e) { + handleValidException(target, request, response, action, e); + } catch (Exception e) { + handleException(target, request, response, action, e); + } finally { + JbootControllerContext.release(); + controllerFactory.recycle(controller); + } + } + + + private void doStartRender(String target + , HttpServletRequest request + , HttpServletResponse response + , boolean[] isHandled + , Action action + , Controller controller + , Invocation invocation) { + + invocation.invoke(); + + Render render = controller.getRender(); + if (render instanceof ForwardActionRender) { + String actionUrl = ((ForwardActionRender) render).getActionUrl(); + if (target.equals(actionUrl)) { + throw new RuntimeException("The forward action url is the same as before."); + } else { + handle(actionUrl, request, response, isHandled); + } + } else { + if (render == null && void.class != action.getMethod().getReturnType() && renderManager.getRenderFactory() instanceof JbootRenderFactory) { + JbootRenderFactory jbootRenderFactory = (JbootRenderFactory) renderManager.getRenderFactory(); + render = jbootRenderFactory.getReturnValueRender(action, invocation.getReturnValue()); + } + + if (render == null) { + render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); + } + + render.setContext(request, response, action.getViewPath()).render(); + } + } + + /** + * 请求包装类用于将路径变量的URL中的额外参数加入request中 + */ + private class PathVariableWrappedRequest extends JbootServletRequestWrapper{ + private final Map modifiableParameters; + private Map allParameters = null; + public PathVariableWrappedRequest(HttpServletRequest request, HttpServletResponse response, + Map params) { + super(request, response); + modifiableParameters = new TreeMap<>(); + params.keySet().forEach(k -> { + modifiableParameters.put(k, new String[]{params.get(k)}); + }); + } + + @Override + public Map getParameterMap() { + if (allParameters == null) { + allParameters = new TreeMap(); + allParameters.putAll(super.getParameterMap()); + allParameters.putAll(modifiableParameters); + } + return Collections.unmodifiableMap(allParameters); + } + + @Override + public String getParameter(final String name) { + String[] strings = getParameterMap().get(name); + if (strings != null) { + return strings[0]; + } + return super.getParameter(name); + } + } +} -- Gitee From ec506b065a8384d978778fa017fd9c041f9a73a2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 1 Sep 2021 10:05:41 +0800 Subject: [PATCH 1395/1965] =?UTF-8?q?add=20"jboot.web.pathVariableEnable"?= =?UTF-8?q?=20=E9=85=8D=E7=BD=AE=E7=9A=84=E6=94=AF=E6=8C=81=EF=BC=8C?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E4=BF=AE=E6=94=B9=20"jboot.web.escapeParas"?= =?UTF-8?q?=20=E4=B8=BA=20"jboot.web.escapeParasEnable"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/core/JbootCoreConfig.java | 16 +++++++++++++--- .../java/io/jboot/web/JbootWebConfig.java | 19 ++++++++++++++----- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 5dc6bddc..0530b85d 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -56,6 +56,7 @@ import io.jboot.support.swagger.JbootSwaggerManager; import io.jboot.utils.*; import io.jboot.web.JbootActionMapping; import io.jboot.web.JbootWebConfig; +import io.jboot.web.PathVariableActionMapping; import io.jboot.web.TypeConverterFunc; import io.jboot.web.attachment.AttachmentHandler; import io.jboot.web.attachment.LocalAttachmentContainerConfig; @@ -67,6 +68,7 @@ import io.jboot.web.directive.SharedEnumObject; import io.jboot.web.directive.annotation.*; import io.jboot.web.handler.JbootActionHandler; import io.jboot.web.handler.JbootHandler; +import io.jboot.web.handler.PathVariableActionHandler; import io.jboot.web.json.JbootJson; import io.jboot.web.render.JbootRenderFactory; import io.jboot.web.xss.XSSHandler; @@ -159,7 +161,11 @@ public class JbootCoreConfig extends JFinalConfig { constants.setBaseUploadPath(LocalAttachmentContainerConfig.getInstance().buildUploadAbsolutePath()); - constants.setActionMapping(JbootActionMapping::new); + if (JbootWebConfig.getInstance().isPathVariableEnable()){ + constants.setActionMapping(PathVariableActionMapping::new); + }else { + constants.setActionMapping(JbootActionMapping::new); + } JbootAppListenerManager.me().onConstantConfig(constants); @@ -344,7 +350,7 @@ public class JbootCoreConfig extends JFinalConfig { } } - if (JbootWebConfig.getInstance().isEscapeParas()) { + if (JbootWebConfig.getInstance().isEscapeParasEnable()) { handlers.add(new XSSHandler()); } @@ -352,7 +358,11 @@ public class JbootCoreConfig extends JFinalConfig { //若用户自己没配置 ActionHandler,默认使用 JbootActionHandler if (handlers.getActionHandler() == null) { - handlers.setActionHandler(new JbootActionHandler()); + if (JbootWebConfig.getInstance().isPathVariableEnable()){ + handlers.setActionHandler(new PathVariableActionHandler()); + }else { + handlers.setActionHandler(new JbootActionHandler()); + } } } diff --git a/src/main/java/io/jboot/web/JbootWebConfig.java b/src/main/java/io/jboot/web/JbootWebConfig.java index 2ba46335..4a8f503f 100644 --- a/src/main/java/io/jboot/web/JbootWebConfig.java +++ b/src/main/java/io/jboot/web/JbootWebConfig.java @@ -30,7 +30,8 @@ public class JbootWebConfig { private String cookieEncryptKey = DEFAULT_COOKIE_ENCRYPT_KEY; private int cookieMaxAge = 60 * 60 * 24 * 2; // 2 days(单位:秒) private String webSocketEndpoint; - private boolean escapeParas = false; + private boolean escapeParasEnable = false; + private boolean pathVariableEnable = false; public String getCookieEncryptKey() { return cookieEncryptKey; @@ -56,12 +57,20 @@ public class JbootWebConfig { this.webSocketEndpoint = webSocketEndpoint; } - public boolean isEscapeParas() { - return escapeParas; + public boolean isEscapeParasEnable() { + return escapeParasEnable; } - public void setEscapeParas(boolean escapeParas) { - this.escapeParas = escapeParas; + public void setEscapeParasEnable(boolean escapeParasEnable) { + this.escapeParasEnable = escapeParasEnable; + } + + public boolean isPathVariableEnable() { + return pathVariableEnable; + } + + public void setPathVariableEnable(boolean pathVariableEnable) { + this.pathVariableEnable = pathVariableEnable; } private static JbootWebConfig me; -- Gitee From e9dc3c047dd7786aed2b50f281148ec4f63ab3e2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 1 Sep 2021 10:43:28 +0800 Subject: [PATCH 1396/1965] v3.11.0 snapshot --- .../jboot/web/PathVariableActionMapping.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/io/jboot/web/PathVariableActionMapping.java b/src/main/java/io/jboot/web/PathVariableActionMapping.java index f473afcf..f0863b41 100644 --- a/src/main/java/io/jboot/web/PathVariableActionMapping.java +++ b/src/main/java/io/jboot/web/PathVariableActionMapping.java @@ -1,3 +1,18 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.jboot.web; import com.google.common.base.Joiner; @@ -18,6 +33,10 @@ import java.lang.reflect.Type; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +/** + * @author 没牙的小朋友 (mjl@nxu.edu.cn) + * @version V1.0 + */ public class PathVariableActionMapping extends JbootActionMapping { private static final String PATH_VARIABLE_URL_PATTERN = ".*\\{[a-zA-Z0-9]+\\}.*"; protected Map pathVariableUrlMapping = new ConcurrentHashMap<>(); -- Gitee From 3da5756312dc390b841db637be4ec5eb48caedba Mon Sep 17 00:00:00 2001 From: orangej Date: Tue, 7 Sep 2021 22:17:12 +0800 Subject: [PATCH 1397/1965] =?UTF-8?q?=E6=B7=BB=E5=8A=A0filter=E5=8F=82?= =?UTF-8?q?=E6=95=B0=EF=BC=8C=E8=87=AA=E5=AE=9A=E4=B9=89shiro=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=9A=84filter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/app/JbootApplication.java | 6 +++++- .../java/io/jboot/support/shiro/JbootShiroConfig.java | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/app/JbootApplication.java b/src/main/java/io/jboot/app/JbootApplication.java index ec886721..40e1d3fe 100644 --- a/src/main/java/io/jboot/app/JbootApplication.java +++ b/src/main/java/io/jboot/app/JbootApplication.java @@ -21,6 +21,7 @@ import com.jfinal.server.undertow.WebBuilder; import io.jboot.app.config.JbootConfigManager; import io.jboot.app.undertow.JbootUndertowConfig; import io.jboot.app.undertow.JbootUndertowServer; +import io.jboot.utils.StrUtil; import javax.servlet.DispatcherType; @@ -130,8 +131,11 @@ public class JbootApplication { if (urlMapping == null) { urlMapping = "/*"; } + String filterClass = StrUtil.defaultIfBlank(ApplicationUtil.getConfigValue("jboot.shiro.filter"), + "io.jboot.support.shiro.JbootShiroFilter"); + webBuilder.addListener("org.apache.shiro.web.env.EnvironmentLoaderListener"); - webBuilder.addFilter("shiro", "io.jboot.support.shiro.JbootShiroFilter") + webBuilder.addFilter("shiro", filterClass) .addFilterUrlMapping("shiro", urlMapping, DispatcherType.REQUEST); webBuilder.getDeploymentInfo().addInitParameter("shiroEnvironmentClass", "io.jboot.support.shiro.JbootShiroWebEnvironment"); diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroConfig.java b/src/main/java/io/jboot/support/shiro/JbootShiroConfig.java index 102f882d..b0002a38 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroConfig.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroConfig.java @@ -27,6 +27,7 @@ public class JbootShiroConfig { private String urlMapping = "/*"; private String invokeListener; + private String filter = "io.jboot.support.shiro.JbootShiroFilter"; public String getLoginUrl() { return loginUrl; @@ -79,6 +80,14 @@ public class JbootShiroConfig { public boolean isConfigOK() { return ini != null; } + + public String getFilter() { + return filter; + } + + public void setFilter(String filter) { + this.filter = filter; + } } -- Gitee From dfbac37d5eaec838dfeb67e987c8870deb1e02e5 Mon Sep 17 00:00:00 2001 From: orangej Date: Wed, 8 Sep 2021 10:59:56 +0800 Subject: [PATCH 1398/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9A=E5=9C=A8s?= =?UTF-8?q?hiro=E6=8B=A6=E6=88=AA=E5=99=A8=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E6=97=B6=E5=A4=84=E7=90=86=E5=AF=B9=E5=BA=94=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E7=9A=84=E6=B3=A8=E8=A7=A3=EF=BC=8C=E5=8F=96=E6=B6=88=E5=AF=B9?= =?UTF-8?q?=E8=B7=AF=E7=94=B1=E3=80=81actionKey=E7=9A=84=E5=80=9A=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../support/shiro/JbootShiroInterceptor.java | 7 +- .../support/shiro/JbootShiroManager.java | 108 ++++++++---------- .../jboot/support/shiro/JbootShiroUtil.java | 14 ++- .../shiro/ShiroInterceptorBuilder.java | 11 +- 4 files changed, 66 insertions(+), 74 deletions(-) diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java b/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java index c3e34ec6..e2e41c9c 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java @@ -24,19 +24,18 @@ import io.jboot.support.shiro.processer.AuthorizeResult; */ public class JbootShiroInterceptor implements Interceptor { - @Override public void intercept(Invocation inv) { // 优先执行 onInvokeBefore,得到 AuthorizeResult - // 如果 AuthorizeResult 不为 null,则说用户自定义了其认证方式,比如 jwt、oss 等,此时直接返回给 onInvokeAfter - // 如果 AuthorizeResult 为 null,则有系统去执行(主要是去判断 Shiro 注解,然后通过对于的 Processer 去执行 ) + // 如果 AuthorizeResult 不为 null,则说明用户自定义了其认证方式,比如 jwt、oss 等,此时直接返回给 onInvokeAfter + // 如果 AuthorizeResult 为 null,则由系统去执行(主要是去判断 Shiro 注解,然后通过对应的 Processor 去执行 ) JbootShiroManager manager = JbootShiroManager.me(); AuthorizeResult result = manager.getInvokeListener().onInvokeBefore(inv); if (result == null) { - result = manager.invoke(inv.getActionKey()); + result = manager.invoke(inv); } if (result == null) { diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroManager.java b/src/main/java/io/jboot/support/shiro/JbootShiroManager.java index b6a3bbf3..a5426544 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroManager.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroManager.java @@ -15,11 +15,12 @@ */ package io.jboot.support.shiro; +import com.jfinal.aop.Invocation; import com.jfinal.config.Routes; import com.jfinal.core.Controller; import io.jboot.Jboot; -import io.jboot.support.shiro.processer.*; import io.jboot.exception.JbootIllegalConfigException; +import io.jboot.support.shiro.processer.*; import io.jboot.utils.ArrayUtil; import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; @@ -28,16 +29,19 @@ import org.apache.shiro.authz.annotation.*; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.List; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * shiro 管理器. */ public class JbootShiroManager { - private static JbootShiroManager me = new JbootShiroManager(); + private final static JbootShiroManager me = new JbootShiroManager(); + + private final JbootShiroConfig jbootShiroConfig = Jboot.config(JbootShiroConfig.class); - private JbootShiroConfig jbootShiroConfig = Jboot.config(JbootShiroConfig.class); + private final ShiroRequiresAuthenticationProcesser requiresAuthenticationProcessor = new ShiroRequiresAuthenticationProcesser(); + private final ShiroRequiresUserProcesser requiresUserProcessor = new ShiroRequiresUserProcesser(); + private final ShiroRequiresGuestProcesser requiresGuestProcessor = new ShiroRequiresGuestProcesser(); private JbootShiroManager() { } @@ -51,74 +55,54 @@ public class JbootShiroManager { public void init(List routes) { - if (!jbootShiroConfig.isConfigOK()) { - return; - } - initInvokers(routes); + // do nothing } /** - * 初始化 invokers 变量 + * 根据类和方法上的注解生成shiro的注解处理器 + * + * @return 返回是否有shiro处理器,ShiroInterceptorBuilder 根据这一结果来决定是否对方法进行拦截 */ - private void initInvokers(List routes) { - Set excludedMethodName = JbootShiroUtil.buildExcludedMethodName(); - - ShiroRequiresAuthenticationProcesser requiresAuthenticationProcesser = new ShiroRequiresAuthenticationProcesser(); - ShiroRequiresUserProcesser requiresUserProcesser = new ShiroRequiresUserProcesser(); - ShiroRequiresGuestProcesser requiresGuestProcesser = new ShiroRequiresGuestProcesser(); - - - for (Routes.Route route : routes) { - Class controllerClass = route.getControllerClass(); - - String controllerKey = route.getControllerPath(); - - Annotation[] controllerAnnotations = controllerClass.getAnnotations(); - - Method[] methods = controllerClass.getMethods(); - for (Method method : methods) { - if (excludedMethodName.contains(method.getName())) { - continue; - } - - if (method.getAnnotation(ShiroClear.class) != null) { - continue; - } - - - Annotation[] methodAnnotations = method.getAnnotations(); - Annotation[] allAnnotations = ArrayUtil.concat(controllerAnnotations, methodAnnotations); - - - String actionKey = JbootShiroUtil.createActionKey(controllerClass, method, controllerKey); - ShiroAuthorizeProcesserInvoker invoker = new ShiroAuthorizeProcesserInvoker(); + public boolean buildShiroInvoker(Class clazz, Method method) { + if (Controller.class.isAssignableFrom(clazz) && + JbootShiroUtil.getControllerExcludedMethodName().contains(method.getName())) { + // 忽略 JbootController 中的方法 + return false; + } - for (Annotation annotation : allAnnotations) { - if (annotation.annotationType() == RequiresPermissions.class) { - ShiroRequiresPermissionsProcesser processer = new ShiroRequiresPermissionsProcesser((RequiresPermissions) annotation); - invoker.addProcesser(processer); - } else if (annotation.annotationType() == RequiresRoles.class) { - ShiroRequiresRolesProcesser processer = new ShiroRequiresRolesProcesser((RequiresRoles) annotation); - invoker.addProcesser(processer); - } else if (annotation.annotationType() == RequiresUser.class) { - invoker.addProcesser(requiresUserProcesser); - } else if (annotation.annotationType() == RequiresAuthentication.class) { - invoker.addProcesser(requiresAuthenticationProcesser); - } else if (annotation.annotationType() == RequiresGuest.class) { - invoker.addProcesser(requiresGuestProcesser); - } - } + if (method.getAnnotation(ShiroClear.class) != null) { + return false; + } - if (invoker.getProcessers() != null && invoker.getProcessers().size() > 0) { - invokers.put(actionKey, invoker); - } + Annotation[] allAnnotations = ArrayUtil.concat(clazz.getAnnotations(), method.getAnnotations()); + ShiroAuthorizeProcesserInvoker invoker = new ShiroAuthorizeProcesserInvoker(); + for (Annotation annotation : allAnnotations) { + if (annotation.annotationType() == RequiresPermissions.class) { + ShiroRequiresPermissionsProcesser processor = new ShiroRequiresPermissionsProcesser((RequiresPermissions) annotation); + invoker.addProcesser(processor); + } else if (annotation.annotationType() == RequiresRoles.class) { + ShiroRequiresRolesProcesser processor = new ShiroRequiresRolesProcesser((RequiresRoles) annotation); + invoker.addProcesser(processor); + } else if (annotation.annotationType() == RequiresUser.class) { + invoker.addProcesser(requiresUserProcessor); + } else if (annotation.annotationType() == RequiresAuthentication.class) { + invoker.addProcesser(requiresAuthenticationProcessor); + } else if (annotation.annotationType() == RequiresGuest.class) { + invoker.addProcesser(requiresGuestProcessor); } } - } + if (invoker.getProcessers() != null && invoker.getProcessers().size() > 0) { + invokers.put(method.toGenericString(), invoker); + return true; + } + + return false; + } - public AuthorizeResult invoke(String actionKey) { - ShiroAuthorizeProcesserInvoker invoker = invokers.get(actionKey); + public AuthorizeResult invoke(Invocation invocation) { + String key = invocation.getMethod().toGenericString(); + ShiroAuthorizeProcesserInvoker invoker = invokers.get(key); if (invoker == null) { return AuthorizeResult.ok(); } diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroUtil.java b/src/main/java/io/jboot/support/shiro/JbootShiroUtil.java index 1af86a27..eba9bcb3 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroUtil.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroUtil.java @@ -2,6 +2,7 @@ package io.jboot.support.shiro; import com.jfinal.core.ActionKey; import com.jfinal.core.Controller; +import io.jboot.web.controller.JbootController; import java.lang.reflect.Method; import java.util.HashSet; @@ -45,13 +46,16 @@ public class JbootShiroUtil { return actionKey; } + private static final Set excludedMethodName = new HashSet(); - public static Set buildExcludedMethodName() { - Set excludedMethodName = new HashSet(); - Method[] methods = Controller.class.getMethods(); - for (Method m : methods) { - excludedMethodName.add(m.getName()); + public static Set getControllerExcludedMethodName() { + if (excludedMethodName.isEmpty()) { + Method[] methods = JbootController.class.getMethods(); + for (Method m : methods) { + excludedMethodName.add(m.getName()); + } } + return excludedMethodName; } diff --git a/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java b/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java index f7c28779..56dacde2 100644 --- a/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java +++ b/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java @@ -28,13 +28,18 @@ import java.lang.reflect.Method; @AutoLoad public class ShiroInterceptorBuilder implements InterceptorBuilder { - private static JbootShiroConfig config = Jboot.config(JbootShiroConfig.class); + private static final JbootShiroConfig config = Jboot.config(JbootShiroConfig.class); @Override public void build(Class targetClass, Method method, Interceptors interceptors) { - if (Util.isController(targetClass) && config.isConfigOK()) { - interceptors.add(JbootShiroInterceptor.class); + if (config.isConfigOK() && + Util.isController(targetClass) // 暂时只对 controller 层的方法进行拦截 + ) { + boolean needIntercept = JbootShiroManager.me().buildShiroInvoker(targetClass, method); + if (needIntercept) { + interceptors.add(JbootShiroInterceptor.class); + } } } -- Gitee From b0808e9c90ba7ea11985234feaafe45f6203a8b6 Mon Sep 17 00:00:00 2001 From: orangej Date: Thu, 9 Sep 2021 10:38:17 +0800 Subject: [PATCH 1399/1965] =?UTF-8?q?=E5=AE=8C=E5=96=84shiro=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/base/JbootJunit4TestRunner.java | 16 ++++ .../io/jboot/test/base/JbootTestBase.java | 95 +++++++++++++++++++ .../io/jboot/test/shiro/ShiroController.java | 41 ++++++-- .../io/jboot/test/shiro/ShiroSupportTest.java | 79 +++++++++++++++ .../test/shiro/TestAuthenticationToken.java | 16 ---- .../io/jboot/test/shiro/TestShiroReam.java | 25 ++++- src/test/resources/jboot.properties | 2 + 7 files changed, 244 insertions(+), 30 deletions(-) create mode 100644 src/test/java/io/jboot/test/base/JbootJunit4TestRunner.java create mode 100644 src/test/java/io/jboot/test/base/JbootTestBase.java create mode 100644 src/test/java/io/jboot/test/shiro/ShiroSupportTest.java delete mode 100644 src/test/java/io/jboot/test/shiro/TestAuthenticationToken.java diff --git a/src/test/java/io/jboot/test/base/JbootJunit4TestRunner.java b/src/test/java/io/jboot/test/base/JbootJunit4TestRunner.java new file mode 100644 index 00000000..102c3a64 --- /dev/null +++ b/src/test/java/io/jboot/test/base/JbootJunit4TestRunner.java @@ -0,0 +1,16 @@ +package io.jboot.test.base; + +import com.jfinal.aop.Aop; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.InitializationError; + +public class JbootJunit4TestRunner extends BlockJUnit4ClassRunner { + public JbootJunit4TestRunner(Class testClass) throws InitializationError { + super(testClass); + } + + @Override + protected Object createTest() throws Exception { + return Aop.get(this.getTestClass().getJavaClass()); + } +} diff --git a/src/test/java/io/jboot/test/base/JbootTestBase.java b/src/test/java/io/jboot/test/base/JbootTestBase.java new file mode 100644 index 00000000..ab43a755 --- /dev/null +++ b/src/test/java/io/jboot/test/base/JbootTestBase.java @@ -0,0 +1,95 @@ +package io.jboot.test.base; + + +import com.jfinal.json.Json; +import io.jboot.app.JbootApplication; +import io.jboot.utils.HttpUtil; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.runner.RunWith; + +import java.net.ServerSocket; +import java.util.HashMap; +import java.util.Map; + +/** + * 测试基类,从随机端口启动jboot + */ +@Ignore +@RunWith(JbootJunit4TestRunner.class) +public class JbootTestBase { + + public static int PORT = 0; + public static String BASE_URL = String.format("http://localhost:%s", PORT); + + @BeforeClass + public synchronized static void startApp() { + if (PORT != 0) { + return; + } + + PORT = getAvailablePort(); + BASE_URL = String.format("http://localhost:%s", PORT); + JbootApplication.setBootArg("jboot.app.mode", "test"); + JbootApplication.setBootArg("undertow.port", PORT); + JbootApplication.setBootArg("undertow.ioThreads", 2); + + // 禁用undertow的dev模式,避免JbootTestRunner.createTest出现问题 + JbootApplication.setBootArg("undertow.devMode", false); + + JbootApplication.run(null); + } + + protected static int getAvailablePort() { + while (true) { + try { + ServerSocket socket = new ServerSocket(0); // 随机端口 + socket.close(); + Thread.sleep(50); // 等待端口完全关闭 + + return socket.getLocalPort(); + } catch (Exception ignore) { + } + } + } + + public static String httpGet(String url) { + return HttpUtil.httpGet(BASE_URL + url); + } + + public static String httpGet(String url, Map paras) { + return HttpUtil.httpGet(BASE_URL + url, paras); + } + + public static String httpGet(String url, Map paras, Map headers) { + return HttpUtil.httpGet(BASE_URL + url, paras, headers); + } + + public static String httpPost(String url) { + return HttpUtil.httpPost(BASE_URL + url, null, null, null); + } + + public static String httpPost(String url, String postData) { + return HttpUtil.httpPost(BASE_URL + url, null, null, postData); + } + + public static String httpPost(String url, Map paras) { + return HttpUtil.httpPost(BASE_URL + url, paras, null, null); + } + + public static String httpPost(String url, Map paras, String postData) { + return HttpUtil.httpPost(BASE_URL + url, paras, null, postData); + } + + public static String httpPost(String url, Map paras, Map headers, String postData) { + return HttpUtil.httpPost(BASE_URL + url, paras, headers, postData); + } + + public static String httpPostJson(String url, Object body) { + Map header = new HashMap<>(); + header.put("Content-Type", "application/json; charset=utf-8"); + + return HttpUtil.httpPost(BASE_URL + url, null, header, Json.getJson().toJson(body)); + } + +} diff --git a/src/test/java/io/jboot/test/shiro/ShiroController.java b/src/test/java/io/jboot/test/shiro/ShiroController.java index 87292940..6375bcf4 100644 --- a/src/test/java/io/jboot/test/shiro/ShiroController.java +++ b/src/test/java/io/jboot/test/shiro/ShiroController.java @@ -3,12 +3,16 @@ package io.jboot.test.shiro; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; +import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.annotation.RequiresAuthentication; import org.apache.shiro.authz.annotation.RequiresGuest; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.subject.Subject; -@RequestMapping(value = "/shiro",viewPath = "/htmls/shiro") +@RequestMapping(value = "/shiro", viewPath = "/htmls/shiro") public class ShiroController extends JbootController { @@ -16,37 +20,56 @@ public class ShiroController extends JbootController { renderText("index"); } - public void login(){ + public void login() { renderText("login"); } - public void doLogin(){ - + public void doLogin() { Subject subject = SecurityUtils.getSubject(); - subject.login(new TestAuthenticationToken()); + // 默认为admin登陆 + UsernamePasswordToken token = new UsernamePasswordToken(getPara("username", "admin"), "123"); + subject.login(token); // subject.isAuthenticated(); // subject.isPermitted() renderText("logined success"); - } - public void logout(){ + public void logout() { Subject subject = SecurityUtils.getSubject(); subject.logout(); renderText("logouted success"); } @RequiresAuthentication - public void usercenter(){ + public void usercenter() { renderText("usercenter"); } @RequiresGuest - public void guest(){ + public void guest() { renderText("guest"); } + @RequiresRoles("editor") + public void editor() { + renderText("editor"); + } + + @RequiresRoles("admin") + public void admin() { + renderText("admin"); + } + + @RequiresPermissions("all:read") + public void readAll() { + renderText("all"); + } + + @RequiresPermissions("news:read") + public void readNews() { + renderText("news"); + } } diff --git a/src/test/java/io/jboot/test/shiro/ShiroSupportTest.java b/src/test/java/io/jboot/test/shiro/ShiroSupportTest.java new file mode 100644 index 00000000..71127132 --- /dev/null +++ b/src/test/java/io/jboot/test/shiro/ShiroSupportTest.java @@ -0,0 +1,79 @@ +package io.jboot.test.shiro; + +import io.jboot.components.http.JbootHttpRequest; +import io.jboot.components.http.JbootHttpResponse; +import io.jboot.test.base.JbootTestBase; +import io.jboot.utils.HttpUtil; +import io.jboot.utils.StrUtil; +import org.junit.Test; + + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.*; + + +public class ShiroSupportTest extends JbootTestBase { + + + @Test + public void test() { + // 不需要登录的页面 + assertEquals("index", httpGet("/shiro")); + + // 未登陆,重定向到 /shiro/loing + assertEquals("login", httpGet("/shiro/usercenter")); + + // 使用 admin 账号登录 + String userCookie = loginWith("admin"); + + // 使用 cookie 访问 usercenter + assertEquals("usercenter", getWithCookie("/shiro/usercenter", userCookie)); + + // 访问 admin 相关的页面 + assertEquals("admin", getWithCookie("/shiro/admin", userCookie)); + assertEquals("all", getWithCookie("/shiro/readAll", userCookie)); + + // 访问 editor 页面受限 + assertTrue(getWithCookie("/shiro/editor", userCookie).contains("403")); + assertTrue(getWithCookie("/shiro/readNews", userCookie).contains("403")); + + // 退出登陆 + assertEquals("logouted success", getWithCookie("/shiro/logout", userCookie)); + + // 再次访问 usercenter,重定向到 login + assertEquals("login", getWithCookie("/shiro/usercenter", userCookie)); + + // 使用 editor 登陆 + userCookie = loginWith("editor"); + assertEquals("editor", getWithCookie("/shiro/editor", userCookie)); + assertEquals("news", getWithCookie("/shiro/readNews", userCookie)); + + assertTrue(getWithCookie("/shiro/admin", userCookie).contains("403")); + assertTrue(getWithCookie("/shiro/readAll", userCookie).contains("403")); + } + + /** + * 使用指定的cookie信息,访问url + */ + private String getWithCookie(String url, String cookie) { + JbootHttpRequest req = JbootHttpRequest.create(BASE_URL + url, null, JbootHttpRequest.METHOD_GET); + req.addHeader("Cookie", cookie); + JbootHttpResponse rsp = HttpUtil.handle(req); + return rsp.getContent(); + } + + /** + * 使用指定用户名登录,返回cookie信息 + */ + private String loginWith(String username) { + Map params = new HashMap<>(); + params.put("username", username); + JbootHttpRequest req = JbootHttpRequest.create(BASE_URL + "/shiro/doLogin", params, JbootHttpRequest.METHOD_GET); + + JbootHttpResponse rsp = HttpUtil.handle(req); + assertEquals("logined success", rsp.getContent()); + return StrUtil.join(rsp.getHeaders().get("Set-Cookie"), ";"); + } +} diff --git a/src/test/java/io/jboot/test/shiro/TestAuthenticationToken.java b/src/test/java/io/jboot/test/shiro/TestAuthenticationToken.java deleted file mode 100644 index e4308181..00000000 --- a/src/test/java/io/jboot/test/shiro/TestAuthenticationToken.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.jboot.test.shiro; - -import org.apache.shiro.authc.AuthenticationToken; - - -public class TestAuthenticationToken implements AuthenticationToken { - @Override - public Object getPrincipal() { - return "Principal"; - } - - @Override - public Object getCredentials() { - return "Credentials"; - } -} diff --git a/src/test/java/io/jboot/test/shiro/TestShiroReam.java b/src/test/java/io/jboot/test/shiro/TestShiroReam.java index a653c248..d8849031 100644 --- a/src/test/java/io/jboot/test/shiro/TestShiroReam.java +++ b/src/test/java/io/jboot/test/shiro/TestShiroReam.java @@ -4,15 +4,19 @@ import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.realm.Realm; +import org.apache.shiro.subject.PrincipalCollection; -public class TestShiroReam implements Realm { +public class TestShiroReam extends AuthorizingRealm { @Override public String getName() { System.out.println("MyRealm.getName()"); - return null; + return "TestShiroReam"; } @Override @@ -22,9 +26,20 @@ public class TestShiroReam implements Realm { } @Override - public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { - System.out.println("MyRealm.getAuthenticationInfo" + token); - return new SimpleAuthenticationInfo("1", "2", "3"); + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { + return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName()); } + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { + String username = (String) principalCollection.getPrimaryPrincipal(); + SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); + info.addRole(username); // admin | editor + if ("admin".equalsIgnoreCase(username)) { + info.addStringPermission("all:read"); + } else if ("editor".equalsIgnoreCase(username)) { + info.addStringPermission("news:read"); + } + return info; + } } \ No newline at end of file diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index 0b31d369..345f5600 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -61,3 +61,5 @@ jboot.sentinel.enable = true #jboot.sentinel.datasource = redis #jboot.sentinel.datasource.redis.host = 127.0.0.1 #jboot.sentinel.datasource.redis.database = 1 +jboot.shiro.ini=shiro.ini +jboot.shiro.loginUrl=/shiro/login \ No newline at end of file -- Gitee From b9262abe4107c0962b4b802c4d707b4ad5cf1a9e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Sep 2021 14:54:14 +0800 Subject: [PATCH 1400/1965] v3.11.0 snapshot --- .../io/jboot/test/base/JbootTestBase.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/test/java/io/jboot/test/base/JbootTestBase.java b/src/test/java/io/jboot/test/base/JbootTestBase.java index ab43a755..7611e313 100644 --- a/src/test/java/io/jboot/test/base/JbootTestBase.java +++ b/src/test/java/io/jboot/test/base/JbootTestBase.java @@ -8,6 +8,7 @@ import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.runner.RunWith; +import java.io.IOException; import java.net.ServerSocket; import java.util.HashMap; import java.util.Map; @@ -40,17 +41,22 @@ public class JbootTestBase { JbootApplication.run(null); } - protected static int getAvailablePort() { - while (true) { - try { - ServerSocket socket = new ServerSocket(0); // 随机端口 - socket.close(); - Thread.sleep(50); // 等待端口完全关闭 - return socket.getLocalPort(); - } catch (Exception ignore) { + private static Integer getAvailablePort() { + ServerSocket serverSocket = null; + try { + serverSocket = new ServerSocket(0); + return serverSocket.getLocalPort(); + } catch (IOException e) { + } finally { + if (serverSocket != null) { + try { + serverSocket.close(); + } catch (IOException e) { + } } } + return null; } public static String httpGet(String url) { -- Gitee From dcc465d024607c23746cb47cd31c65140c64f011 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 9 Sep 2021 15:16:35 +0800 Subject: [PATCH 1401/1965] v3.11.0 snapshot --- .../jboot/support/shiro/JbootShiroManager.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroManager.java b/src/main/java/io/jboot/support/shiro/JbootShiroManager.java index a5426544..4fb3f9f5 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroManager.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroManager.java @@ -18,6 +18,7 @@ package io.jboot.support.shiro; import com.jfinal.aop.Invocation; import com.jfinal.config.Routes; import com.jfinal.core.Controller; +import com.jfinal.template.expr.ast.MethodKeyBuilder; import io.jboot.Jboot; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.support.shiro.processer.*; @@ -51,7 +52,7 @@ public class JbootShiroManager { return me; } - private ConcurrentHashMap invokers = new ConcurrentHashMap<>(); + private ConcurrentHashMap invokers = new ConcurrentHashMap<>(); public void init(List routes) { @@ -93,7 +94,7 @@ public class JbootShiroManager { } if (invoker.getProcessers() != null && invoker.getProcessers().size() > 0) { - invokers.put(method.toGenericString(), invoker); + invokers.put(getMethodKey(method), invoker); return true; } @@ -101,8 +102,7 @@ public class JbootShiroManager { } public AuthorizeResult invoke(Invocation invocation) { - String key = invocation.getMethod().toGenericString(); - ShiroAuthorizeProcesserInvoker invoker = invokers.get(key); + ShiroAuthorizeProcesserInvoker invoker = invokers.get(getMethodKey(invocation.getMethod())); if (invoker == null) { return AuthorizeResult.ok(); } @@ -110,6 +110,14 @@ public class JbootShiroManager { return invoker.invoke(); } + + private static MethodKeyBuilder keyBuilder = new MethodKeyBuilder.FastMethodKeyBuilder(); + + public static Long getMethodKey(Method method) { + return keyBuilder.getMethodKey(method.getDeclaringClass(), method.getName(), method.getParameterTypes()); + } + + private JbootShiroInvokeListener invokeListener; public JbootShiroInvokeListener getInvokeListener() { -- Gitee From 11ba351a52b92d7dffa2307883b093d3399eb2f3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 10 Sep 2021 15:02:03 +0800 Subject: [PATCH 1402/1965] v3.11.0 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 163b794e..84ade35c 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.10.8 + 3.11.0 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 9c9537cb..f06ba81f 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.10.8 + 3.11.0 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 84ab8520..e49a7df8 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.10.8 + 3.11.0 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 2b494439..43888cdc 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.10.8 + 3.11.0 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.10.8', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.11.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 20c56a35..e4a18d6a 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.10.8', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.11.0', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.10.8/jboot-3.10.8.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.11.0/jboot-3.11.0.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 1edd4146..42f8579a 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.10.8"; + public static String VERSION = "3.11.0"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From fd35bd86a76925f28e9159082134bd6812fa3719 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 10 Sep 2021 15:02:08 +0800 Subject: [PATCH 1403/1965] v3.11.0 release (^.^)YYa!! --- changes.txt | 12 ++++++++++++ pom.xml | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 1521516c..c01f7753 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,15 @@ +jboot v3.11.0: +新增:实验性新增 Restful 支持,默认关闭,可以通过 "jboot.web.pathVariableEnable=true" 开启,感谢 @没牙的小朋友 +新增:shiro 配置添加 filter 参数,自定义shiro相关的filter,感谢 @orangeJ +优化:在shiro拦截器初始化时处理对应方法的注解,取消对路由、actionKey的倚赖,感谢 @orangeJ +优化:修改 JbootActionMapping 拼写错误 +优化:AttachmentManager 新增多实例的支持 +优化:优化缓存前缀配置的方法名,否则可能会造成误解 +优化:同时修改 "jboot.web.escapeParas"配置为 "jboot.web.escapeParasEnable" +修复:修正现有swagger实现中,会忽略ActionKey注解中的URL的问题,感谢 @没牙的小朋友 + + + jboot v3.10.8: 新增:JbootCache 新增通过前缀了使用 "多实例" 的支持 新增:Model、Db 动态设置默认数据源的支持 diff --git a/pom.xml b/pom.xml index e8b36e7e..65c62ab7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.10.9-SNAPSHOT + 3.11.0 jar jboot -- Gitee From f5eefe4678ad27453fb70ef8288f9e279e27eacc Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 11 Sep 2021 18:07:31 +0800 Subject: [PATCH 1404/1965] v3.11.1 snapshot --- pom.xml | 2 +- src/main/java/io/jboot/utils/RequestUtil.java | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 65c62ab7..9ca919d9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.11.0 + 3.11.1-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/utils/RequestUtil.java b/src/main/java/io/jboot/utils/RequestUtil.java index 106d9a42..21518d9b 100644 --- a/src/main/java/io/jboot/utils/RequestUtil.java +++ b/src/main/java/io/jboot/utils/RequestUtil.java @@ -42,7 +42,7 @@ public class RequestUtil { return "XMLHttpRequest".equalsIgnoreCase(header); } - public static boolean isJsonContentType(HttpServletRequest request){ + public static boolean isJsonContentType(HttpServletRequest request) { String contentType = request.getContentType(); return contentType != null && contentType.toLowerCase().contains("application/json"); } @@ -117,9 +117,9 @@ public class RequestUtil { } public static String getIpAddress(HttpServletRequest request) { - String ip = request.getHeader("X-requested-For"); + String ip = request.getHeader("X-Forwarded-For"); if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) { - ip = request.getHeader("X-Forwarded-For"); + ip = request.getHeader("X-Real-IP"); } if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); @@ -139,8 +139,7 @@ public class RequestUtil { if (ip != null && ip.contains(",")) { String[] ips = ip.split(","); - for (int index = 0; index < ips.length; index++) { - String strIp = ips[index]; + for (String strIp : ips) { if (!("unknown".equalsIgnoreCase(strIp))) { ip = strIp; break; -- Gitee From 8ead4dd061e8b811bf6adfdf55acf891074a8e9a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 17 Sep 2021 11:45:55 +0800 Subject: [PATCH 1405/1965] fixed bugs close #I4AB1U --- src/main/java/io/jboot/web/attachment/AttachmentManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index d269c57e..9daa6ead 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -49,6 +49,7 @@ public class AttachmentManager { synchronized (AttachmentManager.class) { if (manager == null) { manager = new AttachmentManager(); + managers.put(name,manager); } } } -- Gitee From ada31d9b6a5f031d483b463cb89f957a4839912e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 17 Sep 2021 11:50:58 +0800 Subject: [PATCH 1406/1965] =?UTF-8?q?SqlDebugPrinter=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E6=89=93=E5=8D=B0=20SQL=20=E7=9A=84=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/SqlDebugger.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/io/jboot/db/SqlDebugger.java b/src/main/java/io/jboot/db/SqlDebugger.java index 681f58c1..5af3e675 100644 --- a/src/main/java/io/jboot/db/SqlDebugger.java +++ b/src/main/java/io/jboot/db/SqlDebugger.java @@ -34,6 +34,11 @@ public class SqlDebugger { private boolean printSqlEnable = Jboot.isDevMode(); + @Override + public void setPrintEnable(boolean enable) { + this.printSqlEnable = enable; + } + @Override public boolean isPrintEnable(Config config) { return printSqlEnable; @@ -108,6 +113,9 @@ public class SqlDebugger { public static interface SqlDebugPrinter { + + public void setPrintEnable(boolean enable); + public boolean isPrintEnable(Config config); public void print(String sql, Long takedTimeMillis); -- Gitee From 3b721233fbc8e74705ce60b065533511b24aaee2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 17 Sep 2021 18:55:35 +0800 Subject: [PATCH 1407/1965] =?UTF-8?q?JbootApplication.createServer(JbootAp?= =?UTF-8?q?plicationConfig=20appConfig=20=20,=20UndertowConfig=20undertowC?= =?UTF-8?q?onfig=20,=20JbootWebBuilderConfiger=20configer=20=20,=20Consume?= =?UTF-8?q?r=20builder)=20=E6=96=B9=E6=B3=95=E3=80=82=20?= =?UTF-8?q?close=20#I4AJAO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/app/JbootApplication.java | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/app/JbootApplication.java b/src/main/java/io/jboot/app/JbootApplication.java index 40e1d3fe..42064414 100644 --- a/src/main/java/io/jboot/app/JbootApplication.java +++ b/src/main/java/io/jboot/app/JbootApplication.java @@ -22,8 +22,10 @@ import io.jboot.app.config.JbootConfigManager; import io.jboot.app.undertow.JbootUndertowConfig; import io.jboot.app.undertow.JbootUndertowServer; import io.jboot.utils.StrUtil; +import io.undertow.Undertow; import javax.servlet.DispatcherType; +import java.util.function.Consumer; public class JbootApplication { @@ -59,7 +61,7 @@ public class JbootApplication { */ public static UndertowServer createServer(String[] args) { JbootApplicationConfig appConfig = ApplicationUtil.getAppConfig(args); - return createServer(appConfig, createUndertowConfig(appConfig), null); + return createServer(appConfig, createUndertowConfig(appConfig), null, null); } /** @@ -77,9 +79,29 @@ public class JbootApplication { } + public static UndertowServer createServer(String[] args, JbootWebBuilderConfiger configer, Consumer builder) { + JbootApplicationConfig appConfig = ApplicationUtil.getAppConfig(args); + return createServer(appConfig, createUndertowConfig(appConfig), configer, builder); + } + + + public static UndertowServer createServer(String[] args, Consumer builder) { + JbootApplicationConfig appConfig = ApplicationUtil.getAppConfig(args); + return createServer(appConfig, createUndertowConfig(appConfig), null, builder); + } + + public static UndertowServer createServer(JbootApplicationConfig appConfig , UndertowConfig undertowConfig , JbootWebBuilderConfiger configer) { + return createServer(appConfig, undertowConfig, configer, null); + } + + + public static UndertowServer createServer(JbootApplicationConfig appConfig + , UndertowConfig undertowConfig + , JbootWebBuilderConfiger configer + , Consumer builder) { ApplicationUtil.printBannerInfo(appConfig); ApplicationUtil.printApplicationInfo(appConfig); @@ -95,7 +117,7 @@ public class JbootApplication { if (configer != null) { configer.onConfig(webBuilder); } - }); + }).onStart(builder); } -- Gitee From f8146ed1a912430c07b9689423aa97592560ee86 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 17 Sep 2021 18:55:57 +0800 Subject: [PATCH 1408/1965] upgrade dependencies --- pom.xml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index 9ca919d9..ac1cd0c3 100644 --- a/pom.xml +++ b/pom.xml @@ -41,21 +41,21 @@ - 4.9.16 + 4.9.17 2020.4 2.7 3.3 3.3.0 4.0.3 - 2.12.4 + 2.12.5 5.1.49 - 2.2.9.Final - 1.7.31 + 2.2.10.Final + 1.7.32 2.57 1.2.78 30.1.1-jre 2.2.5 - 3.6.3 + 3.7.0 1.14.2 2.10.9.2 2.9.1 @@ -66,7 +66,7 @@ 4.4.13 4.1.52.Final 1.8.2 - 2.7.12 + 2.7.13 3.13.2.Final 1.4.2 1.1.8 @@ -379,7 +379,7 @@ com.alibaba.nacos nacos-client - 2.0.2 + 2.0.3 provided @@ -574,14 +574,14 @@ io.prometheus simpleclient_dropwizard - 0.11.0 + 0.12.0 provided io.prometheus simpleclient_httpserver - 0.11.0 + 0.12.0 provided @@ -589,14 +589,14 @@ org.apache.shiro shiro-core - 1.7.1 + 1.8.0 provided org.apache.shiro shiro-web - 1.7.1 + 1.8.0 provided -- Gitee From fd0816b6ce7c474489b5092f9096b2c4e9f9faa3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 17 Sep 2021 19:05:31 +0800 Subject: [PATCH 1409/1965] v3.11.1 release (^.^)YYa!! --- src/main/java/io/jboot/utils/ClassScanner.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index e380a073..c2038078 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -294,6 +294,7 @@ public class ClassScanner { excludeJars.add("jsr250"); excludeJars.add("pinyin4j"); excludeJars.add("ijpay-"); + excludeJars.add("wildfly-"); } @@ -345,6 +346,8 @@ public class ClassScanner { excludeClasses.add("org.hamcrest."); excludeClasses.add("org.objectweb."); excludeClasses.add("org.joda."); + excludeClasses.add("org.wildfly."); + excludeClasses.add("org.owasp."); excludeClasses.add("aj.org."); excludeClasses.add("ch.qos."); excludeClasses.add("joptsimple."); -- Gitee From 6e4406dd9130c6e35d8bd9efdc37cfe37664ab21 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 17 Sep 2021 19:06:16 +0800 Subject: [PATCH 1410/1965] v3.11.1 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 84ade35c..9d5d1af9 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.11.0 + 3.11.1 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index f06ba81f..82168a7e 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.11.0 + 3.11.1 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index e49a7df8..4e23955f 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.11.0 + 3.11.1 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 43888cdc..e0c8f681 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.11.0 + 3.11.1 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.11.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.11.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index e4a18d6a..1c9835fb 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.11.0', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.11.1', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.11.0/jboot-3.11.0.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.11.1/jboot-3.11.1.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 42f8579a..8b1b42b9 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.11.0"; + public static String VERSION = "3.11.1"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 042dab832ec1c86771b00ac1435d0ac5ceaecfe7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 17 Sep 2021 19:08:54 +0800 Subject: [PATCH 1411/1965] v3.11.1 release (^.^)YYa!! --- changes.txt | 8 ++++++++ pom.xml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index c01f7753..ab2c5d25 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,11 @@ +jboot v3.11.1: +新增:SqlDebugPrinter 新增是否打印 SQL 的独立开关 +新增:新增更多的 JbootApplication.createServer(...) 方法 +优化:升级JFinal、Jedis、Naocs、Dubbo 等到最新版本 +修复:AttachmentManager.use(name) 无法使用的问题 + + + jboot v3.11.0: 新增:实验性新增 Restful 支持,默认关闭,可以通过 "jboot.web.pathVariableEnable=true" 开启,感谢 @没牙的小朋友 新增:shiro 配置添加 filter 参数,自定义shiro相关的filter,感谢 @orangeJ diff --git a/pom.xml b/pom.xml index ac1cd0c3..3b982adf 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.11.1-SNAPSHOT + 3.11.1 jar jboot -- Gitee From 238a052a55d786cd8106e75faa1d67b86faa9974 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 19 Sep 2021 11:19:54 +0800 Subject: [PATCH 1412/1965] v3.11.2 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3b982adf..30da5b8d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.11.1 + 3.11.2-SNAPSHOT jar jboot -- Gitee From b84fe330b59a6fd34d1d37ec6d8ef2a6a879d49a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 19 Sep 2021 11:33:39 +0800 Subject: [PATCH 1413/1965] v3.11.2 snapshot --- src/main/java/io/jboot/web/handler/JbootActionHandler.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index fb8c7ed7..2cb7169d 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -144,8 +144,6 @@ public class JbootActionHandler extends ActionHandler { } - - private void doStartRender(String target , HttpServletRequest request , HttpServletResponse response @@ -249,11 +247,10 @@ public class JbootActionHandler extends ActionHandler { String qs = request.getQueryString(); String targetInfo = qs == null ? target : target + "?" + qs; String info = ClassUtil.buildMethodString(action.getMethod()); - LOG.error(info + " : " + targetInfo, e); + LOG.error(info + " \nQuery: " + targetInfo + "\n", e); } renderManager.getRenderFactory().getErrorRender(500).setContext(request, response, action.getViewPath()).render(); } - } -- Gitee From 4e384ddffa9870be9244ddb2fc28f5413c382e8c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 23 Sep 2021 11:57:39 +0800 Subject: [PATCH 1414/1965] add Columns.containsName() method --- src/main/java/io/jboot/db/model/Columns.java | 44 +++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index b11129d2..b626060c 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -60,7 +60,6 @@ public class Columns implements Serializable { } - public static Columns create(Column column) { Columns that = new Columns(); that.add(column); @@ -86,11 +85,10 @@ public class Columns implements Serializable { public static Columns safeCreate(String name, Object value) { - return safeMode().eq(name,value); + return safeMode().eq(name, value); } - /** * add new column in Columns * @@ -114,6 +112,7 @@ public class Columns implements Serializable { /** * add Columns + * * @param columns * @return */ @@ -393,7 +392,7 @@ public class Columns implements Serializable { * @param name * @return */ - public Columns groupBy(String name){ + public Columns groupBy(String name) { add(new GroupBy(name)); return this; } @@ -402,7 +401,7 @@ public class Columns implements Serializable { * @param name * @return */ - public Columns having(String name){ + public Columns having(String name) { add(new Having(name)); return this; } @@ -412,8 +411,8 @@ public class Columns implements Serializable { * @param sql * @return */ - public Columns having(String sql,Object ... paras){ - add(new Having(sql,paras)); + public Columns having(String sql, Object... paras) { + add(new Having(sql, paras)); return this; } @@ -422,7 +421,7 @@ public class Columns implements Serializable { * @param columns * @return */ - public Columns having(Columns columns){ + public Columns having(Columns columns) { add(new Having(columns)); return this; } @@ -628,6 +627,19 @@ public class Columns implements Serializable { return cols; } + public boolean containsName(String name) { + if (isEmpty()) { + return false; + } + + for (Column col : cols) { + if (col.getName() != null && col.getName().equals(name)) { + return true; + } + } + return false; + } + public String getCacheKey() { if (isEmpty()) { @@ -731,6 +743,7 @@ public class Columns implements Serializable { /** * 输出 where 后面的 sql 部分,风格是 mysql 的风格 SQL + * * @return */ public String toWherePartSql() { @@ -738,9 +751,9 @@ public class Columns implements Serializable { } - /** * 输出 where 后面的 sql 部分,风格是 mysql 的风格 SQL + * * @param withWhereKeyword 是否带上 where 关键字 * @return */ @@ -749,10 +762,10 @@ public class Columns implements Serializable { } - /** * 输出 where 部分的 sql - * @param separator 字段分隔符 + * + * @param separator 字段分隔符 * @param withWhereKeyword 是否带上 "where 关键字" * @return */ @@ -804,7 +817,7 @@ public class Columns implements Serializable { columns.or(); // columns.sqlPartWithoutLink("group by xxx"); - columns.groupBy("aaa").having(Columns.create("aaa","bbb").ge("ccc",111)); + columns.groupBy("aaa").having(Columns.create("aaa", "bbb").ge("ccc", 111)); // columns.or(); // columns.or(); // columns.or(); @@ -813,7 +826,7 @@ public class Columns implements Serializable { System.out.println(Arrays.toString(columns.getValueArray())); System.out.println(columns.toMysqlSql()); System.out.println("-----------"); - System.out.println(columns.toWherePartSql('"',true)); + System.out.println(columns.toWherePartSql('"', true)); } @@ -824,17 +837,18 @@ public class Columns implements Serializable { */ private String toMysqlSql() { JbootMysqlDialect dialect = new JbootMysqlDialect(); - return dialect.forFindByColumns(null,null, "table", "*", getList(), null, null); + return dialect.forFindByColumns(null, null, "table", "*", getList(), null, null); } /** * 这个只是用于调试 + * * @return */ private String toSqlServerSql() { JbootSqlServerDialect dialect = new JbootSqlServerDialect(); - return dialect.forFindByColumns(null,null, "table", "*", getList(), null, null); + return dialect.forFindByColumns(null, null, "table", "*", getList(), null, null); } } -- Gitee From 5ff8e41de202e22210d576bfa90626b435b157cd Mon Sep 17 00:00:00 2001 From: orangej Date: Sun, 26 Sep 2021 10:11:03 +0800 Subject: [PATCH 1415/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9A=E6=8D=95?= =?UTF-8?q?=E8=8E=B7=20ScheduledThreadPoolExecutor=20=E6=89=80=E6=9C=89?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E5=BC=82=E5=B8=B8=EF=BC=8C=E9=98=B2=E6=AD=A2?= =?UTF-8?q?=E5=85=B6=E6=84=8F=E5=A4=96=E7=BB=88=E6=AD=A2=E8=B0=83=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/docs/schedule.md | 2 ++ .../schedule/JbootSafeRunnable.java | 28 +++++++++++++++++++ .../schedule/JbootScheduleManager.java | 7 ++++- 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/jboot/components/schedule/JbootSafeRunnable.java diff --git a/doc/docs/schedule.md b/doc/docs/schedule.md index 6ff2bada..5c09c61a 100644 --- a/doc/docs/schedule.md +++ b/doc/docs/schedule.md @@ -53,6 +53,8 @@ public class MyTask implements Runnable { } } ``` +注意:由于 jdk `ScheduledThreadPoolExecutor` 自身实现的问题,任务的 run 方法如果抛出异常,会造成线程池停止调度, +请务必在任务的 run 方法中使用 try catch 自行捕捉异常。 **方案3:** 使用 JFinal 自带的任务调度方案,参考文档:https://www.jfinal.com/doc/9-1 diff --git a/src/main/java/io/jboot/components/schedule/JbootSafeRunnable.java b/src/main/java/io/jboot/components/schedule/JbootSafeRunnable.java new file mode 100644 index 00000000..94270c88 --- /dev/null +++ b/src/main/java/io/jboot/components/schedule/JbootSafeRunnable.java @@ -0,0 +1,28 @@ +package io.jboot.components.schedule; + +import com.jfinal.log.Log; + +/** + * 使用 try catch 包裹业务代码,防止业务现场抛出异常导致 ScheduledThreadPoolExecutor 终止调度 + * + * @author orangej + * @since 2021-9-26 + */ +public class JbootSafeRunnable implements Runnable { + private static final Log LOG = Log.getLog(JbootSafeRunnable.class); + + private Runnable job; + + public JbootSafeRunnable(Runnable job) { + this.job = job; + } + + @Override + public void run() { + try { + job.run(); + } catch (Throwable ex) { + LOG.error(ex.toString(), ex); + } + } +} diff --git a/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java b/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java index e5cf0a1f..54d67d54 100644 --- a/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java +++ b/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java @@ -53,7 +53,7 @@ public class JbootScheduleManager { public JbootScheduleManager() { config = Jboot.config(JbooScheduleConfig.class); - fixedScheduler = new ScheduledThreadPoolExecutor(config.getPoolSize(),new NamedThreadFactory("jboot-scheduler")); + fixedScheduler = new ScheduledThreadPoolExecutor(config.getPoolSize(), new NamedThreadFactory("jboot-scheduler")); File cron4jProperties = new File(PathKit.getRootClassPath(), config.getCron4jFile()); cron4jPlugin = cron4jProperties.exists() @@ -90,6 +90,10 @@ public class JbootScheduleManager { Runnable executeRunnable = runnableClass.getAnnotation(EnableDistributedRunnable.class) == null ? runnable : new JbootDistributedRunnable(runnable, fixedDelayJob.period()); + + // ScheduledThreadPoolExecutor 线程池在遇到未捕获的异常时会终止调度 + // JbootSafeRunnable 可以捕捉业务代码异常,防止线程池意外终止调度 + executeRunnable = new JbootSafeRunnable(executeRunnable); try { scheduleRunnableCache.put(runnableClass, executeRunnable); // modified by lixin 08.08, 用于remove fixedScheduler @@ -106,6 +110,7 @@ public class JbootScheduleManager { Runnable executeRunnable = runnableClass.getAnnotation(EnableDistributedRunnable.class) == null ? runnable : new JbootDistributedRunnable(runnable, fixedRateJob.period()); + executeRunnable = new JbootSafeRunnable(executeRunnable); try { scheduleRunnableCache.put(runnableClass, executeRunnable); // modified by lixin 08.08, 用于 remove fixedScheduler -- Gitee From 643509f3afa97334317a014f8b1cd379e3ce583e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 2 Oct 2021 17:08:56 +0800 Subject: [PATCH 1416/1965] optimize PaginateDirectiveBase.java --- .../io/jboot/web/directive/base/PaginateDirectiveBase.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/directive/base/PaginateDirectiveBase.java b/src/main/java/io/jboot/web/directive/base/PaginateDirectiveBase.java index b34a986e..c0e7dc06 100644 --- a/src/main/java/io/jboot/web/directive/base/PaginateDirectiveBase.java +++ b/src/main/java/io/jboot/web/directive/base/PaginateDirectiveBase.java @@ -98,10 +98,14 @@ public abstract class PaginateDirectiveBase extends JbootDirectiveBase { int currentPageNumber = page == null ? 1 : page.getPageNumber(); int totalPage = page == null ? 0 : page.getTotalPage(); - if ((totalPage <= 0) || (currentPageNumber > totalPage)) { + if (totalPage == 0) { return; } + if (currentPageNumber > totalPage) { + currentPageNumber = totalPage; + } + int startPage = currentPageNumber - siblingsItemCount; if (startPage < 1) { startPage = 1; -- Gitee From c4d3cd4803decf64d729e948f08335c36f245a58 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 2 Oct 2021 20:26:57 +0800 Subject: [PATCH 1417/1965] update docs --- doc/jbootadmin/feature.md | 43 ++++++++++++++++-- doc/jbootadmin/features/build_memu.jpeg | Bin 0 -> 102256 bytes doc/jbootadmin/features/build_permission.jpeg | Bin 0 -> 125328 bytes doc/jbootadmin/features/dept.jpeg | Bin 0 -> 77145 bytes doc/jbootadmin/features/role.jpeg | Bin 0 -> 80627 bytes doc/jbootadmin/features/station.jpeg | Bin 0 -> 80919 bytes .../features/wechat_keyword_config.jpeg | Bin 0 -> 89208 bytes doc/jbootadmin/features/wechat_reply.jpeg | Bin 0 -> 87116 bytes 8 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 doc/jbootadmin/features/build_memu.jpeg create mode 100644 doc/jbootadmin/features/build_permission.jpeg create mode 100644 doc/jbootadmin/features/dept.jpeg create mode 100644 doc/jbootadmin/features/role.jpeg create mode 100644 doc/jbootadmin/features/station.jpeg create mode 100644 doc/jbootadmin/features/wechat_keyword_config.jpeg create mode 100644 doc/jbootadmin/features/wechat_reply.jpeg diff --git a/doc/jbootadmin/feature.md b/doc/jbootadmin/feature.md index 327b7728..c464e5df 100644 --- a/doc/jbootadmin/feature.md +++ b/doc/jbootadmin/feature.md @@ -6,11 +6,23 @@ ### 账户管理 -账户相关的配置和管理:账户、部门、职位、角色等。 ![](./features/account.png) -### 权限分配 +### 部门管理 + +![](./features/dept.jpeg) + +### 职位管理 + +![](./features/station.jpeg) + +### 角色管理 + +![](./features/role.jpeg) + + +### 角色的权限分配 对角色的权限进行分配、包括菜单的权限、功能的权限、逻辑权限(根据业务进行人为定义的权限)、敏感数据权限(根据业务进行人为定义的、涉及数据敏感的权限) ![](./features/role_permission.jpeg) @@ -85,13 +97,36 @@ public enum PayType { ![](./features/wechat_account.jpeg) +自动回复 +![](./features/wechat_reply.jpeg) + 通过关键字自动回复... ![](./features/wechat_keyword.jpeg) +![](./features/wechat_keyword_config.jpeg) + + +## 特色功能(独创) + +### 特色功能1:同一套代码支持 Tab 模式和独立页面模式 +JbootAdmin 同一套代码,后台支持 Tab 模式,也支持独立页面模式,同时 Tab 模式和独立页面模式支持用户自主切换,也支持后台配置为固定,不允许用户切换。 如下图所示是 Tab 模式: + +![](./images/jbootadmin-demo.jpg) + +### 特色功能2:免手动维护的权限列表 +在一般的系统中,需要我们一边开发,一边手动定义系统有哪些权限,但是在 JbootAdmin 中,所有的权限都是免手动维护的,我们可以通过后台,一键自动生成权限列表,存储到数据库里去。这样避免了繁杂的人为手动维护,也大大减少了出错的可能性。 + +![](./features/build_permission.jpeg) + +### 特色功能3:免手动维护的系统菜单 + +原理同免维护的权限列表。后台一键构建左边菜单的功能。 + +![](./features/build_memu.jpeg) -## API文档生成 +## API文档自动生成 Jboot API 功能自动根据代码、自动生成文档、Debug 页面、对数据进行 Mock 等功能。 @@ -100,7 +135,7 @@ Jboot API 功能自动根据代码、自动生成文档、Debug 页面、对数 ![](./features/apidoc_code.png) -### API 文档 +### API 生成文档 ![](./features/apidoc_info.jpeg) diff --git a/doc/jbootadmin/features/build_memu.jpeg b/doc/jbootadmin/features/build_memu.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..607b26fb07c59f38da15bc1e435e04bcf0258823 GIT binary patch literal 102256 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%0A{e=OTa7yll; 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/build_permission.jpeg b/doc/jbootadmin/features/build_permission.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..93cb2a55d7c3e535c774e3c932c4d838838fc269 GIT binary patch literal 125328 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/role.jpeg b/doc/jbootadmin/features/role.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..ac1ae47f0f9e418cd6e34c202912a7192677aa7d GIT binary patch literal 80627 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 mD?aXT+S|E( literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/station.jpeg b/doc/jbootadmin/features/station.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..79ff0d795a6544ce1e59e9f15b15e962754e93d8 GIT binary patch literal 80919 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­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%0DbQ&REy;-^5{Ed4F3$c0V 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/wechat_reply.jpeg b/doc/jbootadmin/features/wechat_reply.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..5400f7019e462eee675536b2de3c650a467b184a GIT binary patch literal 87116 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�^nZF##1m3jfQ^f9*YfNsud1zCXbde@cU}1afic3Jw?`lC3rk@P zb%?Kr>4Kr#N?eAzl8nkhs{><|x@U@6WOSL+hED*ar9n*mLzV{pAC>}Bm14tt9qWcZ z321Tf-s^*TqzEUk@q${5)&<+vxV|2Ch8F)$U8vp)s?onvYB%G$G&vm?RgMu-II4&G zO!x^7Z@F0ewhfx>xd#|uZFTB6xZ!VDPk28RFFfe6fT_#EVOmlcV1{#y9OVZYu2lhJ z#pPS6qA)N*0Rc?OQL)O8v~yamL2fj&&)~8shLga6xz-Zx?R&5bU4Vuts|%1 z3Ju|lq;5h@^KHZUB|nAX`B4%9dX7m=VDRpv!rB~=pr}ZZzCIP}b8V@>K0Tj5puG!O ziLVc1e$g6=MOzTmMkwr<$)%~xY(?_BHeyKjWUtPEOnPp&l$~*p;X8*CC+v)IH=?@e zKp{-}vxga>2vwTzmZTm&?Vw_iMJ^F_!ZaaD#?|ki>l-6Gq2YDctXa?jsHc@)>T=94 zi)-XNV^RfUYB_xzLK7OSX^E*v&=(nY0U5gr4&{QyYTaO#j)i1>vWxtHCpjd`*1q=M3Xz&zwK7-h%?)YIg+XJgjV^{4C5PureU+~Z^qKCvk!JVY&7~+Z z&fGLD!qy*yjvnC)@L$O%2|U(+NATYB(^rdqe_x%9Y%kBgyvpIH6Tp`K z34oBC5-fh09o)e_aLjY)q4G}V;;{w!^i`q9RllBDD}Mh!jRO9gFRut4lWR9AJI>~ruL~Nvoz&ukqFfoDywr_Ol#QfQb65M$+!}*AaQjA|W1fm~B8^Zu4a9x~{ zkY|$vNntDaQg7>aba8(T>re=BVDZ+)RIel#hh3vnyukpEEqe46l{Y@!~rPb-ilc$@%u#)3d20`nY#!oatm1V1RGWnz01T)^|xq)4RC4PIHwcV z&spy7@rzL-W$y*Xl)ojf)BivUmD9S6VSzoO6JaVDtlm}3ZWlAC`sB@HrCo0IKiGTk zxTdysZIq=;T`K4Tq=PJ}(gh41TubRyN)kFsm4qUp7iEEhw15FaO8}*l5RhQ#MWu!+ zkkCRvO6VPFg7{_MZ@aeZe0!gE&$;K`-`)8q@0^))%#ktQG3GnRc;4p$3)a?J(x8+& z@m%_&wod%jl|S8m>6@v^B<_W~7DL=le8Lk^9h)6IYP#7+=q3>YD-IZf_^EaRKvm6b z{GI4)&yiRmY;{<|qU}@BHR3V1Y(Z7K_uGF%NI87ciDO04SP^iee7`xPG+`L0b2%#- zzF^0=)%!NCQMXK&%=#$B-)K+0YJKTVNYg0!=C~E%=8~VUyt1a2sl5FDo#C}!wvb0@ zrHgNTLNEg(%yew-kis)$RC^YQ?S^#L@S7@rK);}A3BqF;;n_UDVvWbH%N@cp^~&w)3R{M!Fg!(baRN7+ z)jQ=T1HUr+gk~f5SCMsTJW;K97?;+wU|0%8l!clF5|}B~C79xRewl5t zZkZ#Y;qZN?a?q{LBFW~}6f1gCxk97WH1HF^uNUAhhWfnH{Gubvz9fCWq4+Z>Bd#R3 zxEd25-1@oEho8tQQ!Yh&ldocRFlt%icu}ETCAc8F`(xCH_wnt!Q0vYKoHl_K>mBiP zC83Lq^{aT3|KYPZRgYORCyg_hS3}*GAq}Yk^&J zC9O3s8V5=zIVb=(UtCzCulwHvu7`|G51U< z&ia${bbMbuRUmG)YpF0}$Qn zuU-?>ZgF!Maa~fSfNa zP;%DE3sQJ{-b~{9EuEE|Y2aqROMthHb7fs}VQwbQdac8kc_8n!?fCA!sK7*a0ZjP? ze+&jw3+#i@zj6qKcJ1tvZpfn8u)!E;(umAQ2(*Td?{|!l<~$O$F&kA!2Lo)9e{xAW zgzit(af9z1iCjNyM&}N1o{(IYd*{OX2DgHa*(UeDWb}_DyR2Ku*DBD$6ghqa9V@&n zMQl~PK)FQ;e}t$~WfVGdAk%~YtxqS0ly_dr#yZJ$>y7akRJ&%r6V ztJ!Pohydg2a+ZS>@AB{4_|Kj>tMegKi2!)8cJkmzbObpgGTGqkU%mvH-!-@U%Funw zdu$o?XwFpiOZO$wKlp+JQ)c= z8P5)J#1p{pZ0-+_@x`&N=81p^n7z_c7y-!Hx=h)*Tlp_q!KAf7_3Hy_fvPCwIX8K@5F1F+^>CXJI9cNui{Ch3$Ag zMaQC#pPF;M8cS;VA>%6p!q_T&OoU$h8qbi5Ke=(=ls@Fvtm&3cpiCa2e-m@yR_WTj zNS|5TIro);eCuS`%LjY%E?a-MT=YQ71T}1FpcK__V1$HF;7&P_KcZsS9d|3(T*sI@ z!O?4SRKgPh#(HJd5*oPfbk->e~v`Q*=N`dX4l!vzLD^pnaj2{n=$ z`Lqth`#T5Y`-@#iW_tE5%v+64SjmJL$ga3xCV4ERuCDL3jGw}rK3Fiu9m=G1V1?+| zL3j1?^ROl=YOhf?j?!v={Up!SWek}YK-Q_a5U?ip%J-wv(RmaeUJ{7F#|LymFo$^p zAqPXni&HM8={Ox?gGyO@Mu~y)9Sg0PWD3-Pap?B4bdH-)1>UF{B zf@~oM&ZSD^&g*X}2qizd^<(m_D216&q@r({CmGycpn;=>0-;j3MAAA(TFLEtDP9im z7EN6OSzJYy$5+JNFjzQ2wSbiszhQm2w1jhZ13CAVP)o?q!ytuUMhMI`?<=M>J|uEzIf2ZiQ*DV zZ35u&S9CDagSTQQ44Ug!9KxnA<#lS^;fnY3FD!|x2OQkbyb@|BRB}Q6YW!iZMdZz~ z@TB+WykHsXOgAze$yRTqu$GBQJ(h<4B0xfjo>QW;*8mt^a!N2IiVfHm-`WU)dpiv! zlWG`kHwRwz&?K~`p~IFq!%^#2ssJKE3+92F2JXionS3$vV{Zp z6(MxCcDwbbF&0K!+ zt0>ZL*r|E#)YZn{FmbU$Cf;>pO4Xnu2XmkzXtmKZ8y8oEY6hQ4etd_IDT50{nX$}L z)1_%QS~hyp=|o(9`TgG{^kM$f?CAxW0ntvQcaM9L7P|Knm4GQY35Fk{fB(LQ|1Yvn z(YY}Q-eG%>CU-JvO9XwDiV3hopD4Ccp1-y<_4?NS zwZbOFD-1ZC?K$ucM{wW33Ll-VG@oMRYdNLXN^t1_6>GWFQI0wFr@y3 z{_F4WzMXK&QsCRq=e`|({@(vl-rIi`_s_Qbr=$FfUiQyk@y`+Ee`ff6@oi1m>RYG0 zP$g7?s7q-~3g=M-0j%TKc{ar9T&(8wQ2Xd_q4vM_P(A%U?N@M2NZ{(_FP-k?S%II4 zL}f`=N=&n6&Nam|M{>7vWG0oOI2KdpL_RjV>g>>tI(Ws2!^qzmT7Lak9(@0kKm9;g z7#go`3e{LESo*^(Ea?^ARpRYCI~H_>K~?!j6lV`of0I(3RLRKBW+vtw+v|+-Y`%c; zsQ1d2c{+C4>4;#9&M|Ix_6J(;UH$yRrG5Us2l@hSQrzD*u0%ekHC}>@gFJ>JDBcmU z`XrK-M39tTu^hX{v{d7poGV<}X#{QUTGO746^YE1%W&n^6(_s8TtG2d^AgTgLQD$t z#;JY$5o+S<#f$f0O^*R%XeLUakBpgT)5+F$xf!+I=K~gGRoW|{#VcE3Cpd}41Guvw zm`0nReT8lrX;mFa_aXb;?wQGS4hm*9Iy=%C*Q3Szu!ToBgixZ-=2)&MvTeMpBfj}C z-8j(t{_6?s3=Ax<=txLt8>dI%0tGxi&Qm~ByzFMxgQ2XD_Ps`sR%{<^mHPZrR9w{3 zaKJm`V0J9K-arLr$52ZXtm@sX3Dl%M+P(A9`?gMr#IjIZ-cE%JD&zvp!M{HKB8sIV zp1W~0D(bcF0ZJ&*t2bm9vzaU4w--SsZ>)T=3uo?n41?RKINDrTNla~>IFjA)-4EsL zlR1kV6y$p!H>U|Y*gyT!DuJlqPQ0qoJw8X}9Yq(H9CJ9z<>s!I;9u2-sM<|W6^gq| zmGKDM!j@9)vixt72a=qwruf`>+i(Fh;?+BnH5O90@!o=<-7`2Di~B3u&{PN4Yw$@PEE{XSfqjj~c1e9gf5;QNx#h*bCM6})i`HG98| zOJRdo1_0h9mtJ3-@l8R`XC5jkYEDHY22fVXcFzZFMZf%lE*8lm)vE4(GAdbWVLD(Y z!{Cdw?A04Vj7vS*BTZ*@@yezrOJ!~sYJqnvSjFVsUcMx)Xmo{r33sX@&}0m^lkx~v z`9p`cE?(l?4uT*!JEh(p-?}K2mi>A@ZyxqgnXmbRxWE4{Imy*c=7X!htC*bi{oWzV zu0zf_d;MJrI~Ql?K9C-FZ<|#~Pp(jMuIz+P0pTYl_`+a)Wy4=GSKjRjy6~q zaANbKfyH?t2)wR(^jRCCnpUnlR%GwTIM?=@454N=@Jc0B}-lnFYk#jo4IAb3I8XD;ryUx@2HT zEz!8W2VgJd5R7|byKy!?vLIHtQckvbKhL^PIPhY~02@ep?IolNvuC(Xh~HD}=vu$5 zGriM&Mt7TanGvv<6_c6#2%5`go&`4@pWK`g3cY`s&UJjypYH7ff9Tzt+BP0+%^*7%0(529);|~0@Pw2 zp*J`Uj_!+Tld$*u?ezy=x<^XPr>JM*ar$wV%UtZezSoR-wK8H(*zr2D#nfZH zO7pgjb(`lmnoEes)bi$_OTWJhkh-+m|LYbj%*wirfXs@)x4)@ziLJ{e z^fiqp>tgGp2Bp{R5MZ3f70|JOjL-CEn;SBzY zEsQ{!en%~PMcG}~L`6EY@86TTk{~K)fp|;QC8YVg>)>V8z2j{ zY-`$i)zBdqhhRl1j6sX!w;Fw`;%`<@$dxYm#>;`m4sAWl**x5%$>*wc4-k#ELsa~F z$?Rxt3F6hGJ;8ziE~*(Y(vRL#;xsjZPE}jdU0DX3DQxiuvmZF6+g?#K7WsaNT(#yK z%Z{|mNNo&u^N=&FsOZ<7)dV*?Pes*ZL{?^u$c%~-T{b?SU|s>4-M~iUNJ`{IL;Gv%pwcaplX9UwDbyKiTMpTQPmh%})A2U- zvN!C$HS)P{t)4#=GHH9Fe?jg(pP7Vz>3F%^sLoc^`3zQsbqrb@DP?8-2*_hPBxI_*xN=82z;J&U8X#Z()Z7wzi6#P3w$H zntY>MGE!Lt4SNj07!3A(EBy4gtC=lPgLPEXtDjg8->IsMVUIGd>GbxGj9;H>sYi7*JJ>JAS6j(N`@^hyvc(C`ewP>Q;8zAaR<8mPi6+qhR)vejHSjgIt$2zaT2ny8L zNXoYc`PM?`<}a(lNfu@&a*q}AQk9)x4~dMihXbAD73e?+@=99)?WzTFu41dwXvEOJ z+JYW5DQ}!j)zT(Rxbw@cWj5G|Iy4l-6Q9bJ{mUVQZCn*7jln%felT~OU9U@S%IZHK zZI=r_HcE-8FWN%|H9RQ%)u4w{{|{gO=5*&j+8f;d=iZS2|Fpw_o_?Ximvgf#9af?l zA38@f;ZRDiI`u)8O>TwCiP>lb;)^fGF{Nq7ltn*?A@2K7>fdMo0hzV`<3$OA9(4E% za@I2~;@8uaDR@6Oag}f*=hv(T1rAJ7szO#GTKcX+hm7b@PpR1Kwm`+hMMMQNXFRzM zi>_IQg2vuEZDkmm89`ZHIY`a+F6D|(Dq}(C*-E|?b~R0}td9+&umMn?-_JSH4sD9U zPp7@Qqh;3Nj9=RX&sn*;7wSXyG_=)sln3<$saHZ9`=+6$y-=R^8R8bf z{@Y&}qUcD23=h1U;iovxL8U!7rgJD0ENEM1H)r2e*6{NWlgdfXSJS$dZE8Cf6EGdH zPpQL-_8sC6o3sMD;*)$EzcM^pZr)34zjp(Mdhr%wcPDEADdwc&)w2=XU%-ebmkVZN z86opn%eaxv@(V$kD3ANUU)pFr5?G#jGdCWfbf^t7`baUPdb0A{UDFXz;Qz#w-r?h4 zU0#A)SzcKx#TCoANZVn$C8Jxn4R}0_Q+nXONb^KZsOkF33Jg$Rwt^3edHMJ5(huLy zbE>s|5002ar^}zkWE=+w$!#9Emry2X29D`bSvOYpc>E#2wC6#$X zj?%*5sw&=>4Y<7dc*=TF1R?MMRKLQ)KHxgFmk?yya9(9gSqWht`nJ{;=Pqp^I-FUL zpo(_!i3meg*75fFRRSF2Q-3#TO^R05kh+nI^=g?W%-)FsfxE9=bYGmXX zyMAokGcy=joDOf=~oz?H~|_iaXPNlhqEU15LT* z6rjkDYF*Y3%Uw`bCq5W^$f;&ArYmQLOYOF+Gyo>+@Z?%Kyj@Uk(32S8aK#$!bZ7~2 zetz9G$4U+#ySrySNhJl+mIeep9or+55lKS{86k7!D#A%*fD z&PP?f3X>3$o<_vbX{pKkNX2FIT)V<;g-^rmb+cQbV*5xV^@K;cjxbqL$J4 zu>aBG=m8?^LF0*;qhG*fCUq$AC1S@sV)tVhUgEG&eR+SgD3+~jouElIWmX)!Xav%W zfBi+&8j`t_InT$k!!@EkLNH~+KEA|u$fmF&>uL)>C~bPtsdC-h9j=OWJ~AAsS|etb zrRpNp9+0TEaWV6yopD(@UWx{@L$YovM;y)s*LqBDU3bmB*si$w*4RT7*1EpP!Hge1 z@Y$v2!Rw39%9ii82^l(}>NAsNdk}Q7V|1dg?Ht&%)O+mi3kRSQH(4MMZ%9j0r0z!tXL5*WN#HtYCa@!sBoS4pJ{O8tAncGYyaK zuy9wLe%b9(L}KZ8V@`@qy^(QI70YM#BAHGT`Dn012oqCwyqE8_VllC1fRvajQRnf= ze{KmM&~F18izq8tP*slge`_v$SgS%RrsR*9zY-LR#sY0F*BGTII)>YhzCB?D5M&^0 z_XE2=xxq8;g=?I+QYrc>+s*J%6?a_wykLL@*aM*&%j(UMR2o$-4ST*~af9sTeq6Ae zTrDeXU9&WPF!O{8=$j1mZv6zco-BYJAQjBj+7~Ati`gP!R#A@a2ZM@puD(2Nvlftg z=Rj+W44k7hT}v=oN4$3B7-*3#=@s-4UukIBzaXJ- zVj^F(ogR-Ii4ZeQ1%#AMq05m=RTH{6&_*+zpYFnh1U_-7@5yaBw9<0b8YmWKlIk30 z7=CF>0?1aXBREd7`_gSwZTfs09n&gQ0sqkO{L^18awjOgVuxV{!n^|oco-$#=e@E}a z$B&z5A9jt<``D&X7pA18Iw?xy&gJrOiNw|qCQOZj1F4d8tM3q=AWZWw2gCMfqK!o* z&{%wJaZGRH?3)`Zf~O1fqei2M?Oj_tvUj9Gqz@G+j~E0|ph7`&d3kw1O6p>v`yJPF zPJ=^Ic{cJM)`%U%ed`lVO?b!4Bd4yH~K z>k%AOZ+sD`mFrMrDD_@}b!$Kl-+7PT z3kuz{_F|LekC)r%DH>qssbigZ*7xnf8tvUNMA{AasK+c{GNrPWIe3zym>Y~Cs$bZ; zJ1Wy2bA<7?zG-tpnJC}UzqdB3pRVbD+?eeXv}TRuh)AxzrQA0YR%SBI-%xBf{mJ#> z>68?ygt!*b)E~>%VWi}uQ>yV_0>yJTHpN(vGZ!Zx_datH^~Loa$rDCOm6MPw_2^RY z=I(L!_BA@W^o^va9j1NM#^MqKZyw=MT8GP`?6G(iXTy0>))0!IO;f-30-(ns_(GOC z`klMyoy^uYp2d%t_EB+t6wd4MPgJS=sqjIMPhj!gTBmSptVP#W$gLlqdM<8~8qP{S z;U3E^+lANXiu+VQ^Wwlr(=inG?8oOsm-tkU)jc(8I)`|GYY1c}22o2N6}i3FhvIRj zl=VhvUHm(D$FNK?7X7}^e#mQSlJFNp#^R$=5Xw+Uba5%zcjmV}APUYN8eP%jZ}rDc7Y`U{*rKSe+#dtn3z zZaSVE!QtjnLT)r+gf;d}JFbh5{_<3ID zu=-JJ!P6I;Kl~ZE``sD;*u0)|1lsR+qrPeU!y+r^Hx-@EcuSEv_4_%uUl&_xbNHme4<_P!hvD0@8zBd% z%G?nDat9CvlgsZZMI0s!`;Nq$xHm+luc}Itrj2gn^MvOGKyRPQEgR=6~K>imE4TgCUaV~B|Q zVr(+M_UMQ>$0)N53s;10Ogaedm@qK^`lsXmvm2+*>g@J7?06l;>di4Gt)w^4Q}offT^S` z=Xx1EvH=T7cg0o|+2*@5nZMdiymPMffeu)>PwH8fuvY9EA_%dC@s|KUE`QQmPZuyq zGZ{3dmI{sm`_4HXuvH2+3pRo zfr9nRwh&K%nRpOyQiPmpX{SVPY|NpsqepA#-b^Sn8%4GYw{N_>B6Mk<41SZSlQIgq zlSE1ml0GY$fW*vC$MG=xMHQ!liy~|LwGH>wx!S53vu}bVsL#gwmwIo#^s`sIRVX!A zYNSt{SCPy|D%VWGTmfu2$VRgqw)t(bV|Q0HVg9;BycpvRr~9ctU5iG`RyZMqbSl=M z%tDa-!NNW&^VQ-Q)QjM-VI5UR2`lA6op;t3=#V?8)fLk!1pu>3iYq|_NCo38Y+X5& z4cio|uBKkptwVE`w#Z-dM5nCzZa@G^Y__CnNM3kS!bT>{I_-z}Z0?)N=qhnS?aX55 zKwKpNUyGI<@&z^+G*QZ9*+P1C<;XMxkW>iLwnr!-55)bj^gvv`Ig_49EYM_$rY&o3 z71ykly6koy3HXfCK6Jat8q))>CsOVpjz*U_Qg2kL(V!S-D_Ep!lFcEgegI}99oIE$ z2e-YYb>F3UoD8s+#W_ktlpD;I`(ufH5=u0CHtp5yr8U6NEzC|xiiDFq0zYh7ZO>gP z{DAVKH~rn2hv#jpc3zO9o0$kD*Pp$n%);NEm3I}SBBj zw!x!9*~v996@Z=9G@JO1cKVu#joq@^o7Xz0H(YSzmU*v<6mFt?*85~_Oh>MFH7n3G z?7`h+STGM4bjaCL2c+y~;ft~^1?C##8hd;IrQY$a!>>4h|E^!2CLA)*oS2Dy=5+Q? zf{QDMIeGJ92Dc4<_bf(S2%+Az;ygpO&U$2IL8b4j3aJv$_WEMWu6M}jr{L^j;~neh zw4kk4yM|BIxhYbN}0k*9?p||55Qje_QhkR{LWx@1Dzc|9I;9KRV?WM|%xf*Q^9v|Atz%gdq`CO3|63|IDZAbYhS-=>(i^WI_6_iMl{-( z-ii)^7blT6$$65#{WouTUykhDlaD(`0NUrET2TToUytnkVsAp|hM7)N^YJAx7tKUr z%$JD<-cV5xWmBNMGk%k6GM~)uaEarVYjmltv8u*s`AGNtOifwp87MiX9USEZH1=Wc z!qCN#L?*-oFUrGA;yNg)oDPHOovXRT=SZPARk6O>m?i1_*bza5Ey^n~umKbGn(Vst z$nZ$aC^JxGi5xGki4+2F3gk-E1Wb97S;J>6&F4m5csxB1iT<6GdLOJ@BBWF@Nd{r^ zQs}%6xw*N=7vcf|>cFVx)#$cg%ezVm1(`2W-E9#(0YGG5B2X_!U{lhT*GOvxCfH=t zE1FP=9^gpt1z=DgYq%cd;km61cId)Z)ix>u`f;SJ&XK~cT#>Gh<}XH3(dFY<2)v}k z4ks1LHs@!4M}jR0zGjCW0sJKo(9|?cr?c}=ucr2@DL`B2?V5!_Fsh2>qN$|0W>VI= zc4ylP6hV*kw!{*D{=OUOx6N!TH^4o#1zwCuQxLD;{U{06(825q2n6-_WXoTR>Ktoz zDd`5SDchZi5%#_~i@;W9mTcy}qGY!}3eETP$|VkvOy~(SWieB`1q1CQU^#vjy7K_3*#Ig*4>4LFYrb)tvpW|CSVY)+)7qpfwH-Oa_8$1MCTO z=8Y9rIwE54c!e6iQ8$6UPGtk$6$t2dG@Ese*l8sw$E(A5hdWzd71NJzp}I zkM#5%P!IuX&D(bTM%UyKWhH~d{mNnG*6H_7i;>W;<^fC^&*BQyT!ky{u*&Y5ZonHz zDY_#*h6}`w4%a}5nreF=;>@zlEUg8=#u%Vpb3xiwCE<$YZZiUIKboGPBXJ+!E`Skj zAbn+s9sbBzFW>dJxO2HavxURUP-^AP${^xFzkqT@8>6Ym7%vT>sGEtw&;qU3v2`0V zm%B0@Wk+OJUVi#AGKZ-uI8rE`8-{x7Ic3=HdR7t2tOT5pmhdg{Af)NTdq0-qMq$e5 z%faoSjt=>nWP)4Q;_UJkz$J9D`EniXp!4N7*ZAxAKcA-G8sy(x1ANZFa5+!HDwRvJ z!(OND0zn8-3WTx+(v?L-j-)Ba7sB2$7#2nKIA1fkDGQctU128aw4BZ+XB!;|XlWkr zd()sGqE>crSBZb5sbE@%C zD1v0rPqy<#r`gu2rV2)xaW6rqj8^RpIOi3G6Dv)fwLn6fbpV)XlIMUTT{~h8Ht`}T z&|MN^n_(uz9NId_#vt~&k+yDf0c;PkQ5D~;5Te=$}B^UAWs=j)kig`W}^sDDQ1 zoiJ5A5suO@3fAuYHMRHwlb$P^Z)WnHxU4lGUHg!pEmbEjPL~^d>Gb>Oe{;b9)}a5_ zx3J?@Fz=*xl_0Y|G|jVf^j;*pM2;Xi1i`H4HgCf9{QaEAj2wxE4M3Mgwz5=5ZFQ2z zdMyr2@BY;BC*hU9qw@TJCn@CAS&m)hTPVW*hUgzKr{J)Z-v3lQ`=>wr*NvaJ_(x>J zd+XPQMblS9B2&F(**yHr4bH0mvb<3PH#wL5{*H>l6|bqlI$;Q4Ire=b(2b=r8Ia0i z_49wr0`_yOr8xg>?ljih6Jc}g-oG)HQq5x%5%fHE{{y6cOIGkaRxJSAxk!sz67u$j zmuXSiZq1yXco+E$u?8)duX~(u<3T6vlaCc=i8IZ!)X*y`j&iB&<3pC_NyB?C=oSyqeA_xsl630cX%*^_)V#FVQD;7Bng9tmdfj`Q-erW9ZS zZvtHDmGU(j9U7JN7>kM+D~;mr=1WL!saEWhJ`ozz;Af|c^2C!|p3CDaTAS%BX z$lD*qi)g1!dHpyWvKl)Zu#Clfo^6L-FwRb_>==DZjwFaH$KKCYv$sEAgZ_l*T4B|% zxD~r4YgZ?(>Qz=CYpmzFL2SRp#Du+T`xr9O8=EYtwjz>vRucz&<<8%_JVan-!!~#= z2R055Y`;o|EbGm8bf`CKQkY8z$Fj^yeK9-mL=BBI%Ro)A!0IE}W9%H3yttZung4kK zU&znkC zT$^aO!lI9a%G$lp^3?W1>(%YP8aM1EYDn;VS>6xVcM`?V zs0H(^Q=Z($a8pYTb-I@!!?NXh`q>(8cLYGO4T5ib(p}93Js;E1?vAsVI`g($i|_g1phKqj zm%6b577I}Rx^|-?oLK>+PG{)w@Y8mC@x>HD4g^F?Po&$7xQ`SF3}kEiI>mA3j^UNN z1-uXdAdtQSN(K8*+VdlCwmOwLjr_KBR89nX8tlal z#g=$(nRxKOCoW~dSKsfP6KQndvf=0Lo6#rdNaQ79Tgk|ckYkOd?pXo)Q{iu)K$(gh z`z1b}V!WR}>sV|(s`yPsU0MWQm01EoY{iAwYS#km>nEqj9(7@gK+AE1L!=D-l=)XZ zdThk>@%%@*naT7x6PkE?mpd={^gtf@*s(-z2|7_K7{`nvB z)mc6g^Sr#@SR(E$nD0k?uI;11_Y@ZJSWs@9MFu;Ea*(OB*ShxBro>eo0`X>^4n3(E zbMbf%idY;cSSLi|R0b-xM#|3wP0q`56WJUD%2}yjMk%~LF86F-GYI_tx54AzX`NyH z$^e->Ts^8vEBM3AEV`LSqKhl?xO}{;U_Q1(JSS=!x83q=a3aHfJ-Tgt2)NDq+qCcK z%7T{^ zvu*#s1zm6b8o*my1ZMVSeZD)J>5ToSkbxHxax!+9g1C!tpg}(QXgL; z(9`~6TSupyCZ%U!KA{qfL3{YU{DP;64W@PEQuH3@JRtQK4|vE3oURRPxmYuwkpxyv z*EBL5OK&Te8)!VI-fy0L>uiXQ;uQ#bXyj4m+E)fZNLtC`GY6M*p7i$wf#pTz%I6RJ z*rsPFkNJ*m)~2B@e1h1EkcX(1jZLwPD$^qxClr(~tQD0X+8Wt%`05rTdnh}`;pFn}*miBea@;yr6LU>^Rl zuytAOrb_h~YeGqvw5oc^7KfvL5X6F@#j}s=xESDeOVvaDd4AG@Fd|kkD7ZriLejM8 zbN--Yq=jRZPn~PIg_*BE<@l;ix@?I2F}kiYGmiUpJ6=V@Qz`fjymMWoaJjxdFs9Nt z;(jq^x_AM9<@V@13kyAaFFZta^_tx1L3dt<$XmyM-MRK7B#8Cem_a3kJ1$wtiV z8G{GQ6;35u^{G&Jyx(TYKJbKmM8{9O<7rx_f*g3HvO@l~AYTa`(_8%?nd&$VR!8#Y z+YU@rZYyi&h*_eccIdE_`%dlsAxGn%17`Jd7}>cG?}s80fQE5F=Y7boHrD(>-%FZI z3X>7`5IRSpr+SV*W(+`NQ&*#{gTXB{uzBT=04^Si{(IP62 zuB=PUrG-kEB^+j|K)CC&dxDyhOA?c90K-a!9*gm+dvHOBpmn>1UDJ^Vd*fWkm`#_k z2fLufa<;x}+g(ykSZ~eNC>m}c9aqp<1e4Q~G^FmqLy%dIr6{wyTpNywHKdbH-jVC6 zyK|h6t|b$j1=+&b;;&T7>aJ=*ybx6xcx|lE#d@2jRUj|~i;Ts-w;G;E-OkJ3YIwP# z65@uGHWMkHG^Bdv!9cL#iQ`h+ph&}rC)oICVHkD=;?qd zcjhm;*=X2kXqW+agWh#$6s{hI7<_pnsXvep4742RvsdQ8g}ExD_IQlzDmh9@+_Qej zZ201U^}nj!2mN_$CROq&$WHqrqlM0>hq(d!QY%_b&1glFBXaQV8?co1eu|9K)tCBp z-Lm0#XXSm|>2P9~RX>(r55;k*P0u3RJf#~u_XISt63C!5KkbKEf(5~@dnPlVdK9*} zx)Z!ufzIpgpW=I}D3G^#)@Mn-qrXjP14>MZVDYeY0{pIH%N zvel3qiL$O_G!rVtN>#P|2wcuDXM2d_Og7z#Z-#1_1Ts~B zusI!5YXqxR^Hb&$vSA7heo=U!Ko?Nl>Z&X{ogb19p#w*6oOIZOx2#cjR_Z{X>=hP7 zIL9-?e~2g?b1v}a7h+8MWeKOpuR*k{+o2;Esm`+g%H9vt55=Fiv2$aDvx0=wp7H|g_V`Tq z`w{k79)1BkhGj4%`2B@|F!Z1bNIv$~uXKGBbZ01)CAt%&<d5I zIuX~x-F5*}?_v=m%987WnkJqNXT>N>4QBMP2Y8Tql;S3awY>t-gcfc(d1;m=!nO>8 z!KV1z699WT74b+7lTVj^xY<-j3jShcJds$fU)am0(Hf`6VnnGz0rzI4fWTk%@8?+>x%#-vT}UZScsdN!j@U=vHGc*W2WpVx%%qyHXe zfjHOX4q5}}fuPkQ9yh@7!EV7c*9y218GImcjpRLYSOjW!1K+hTPwcJ~Oh&$T;&CA@ zwSMw|%xPl?3Uo5QUGLU_wYW{2Nd2l;&L6tS&WOh1`9=(iz6=1rvZUsUdLnDXq#td) zJqPRY<5(edzHW>4*p`g>M48x9^@D7*mIdycc0y3W1z=YtDPR^6qKPTdLIK_%v5OyBjP za=)ph@G#VqXkPE@zTnE|*w!v9l&_?(zz%aa>|BMzmo~a^%E_4nM*E+(Z+w*NT2}O2 zI=H!hhp(voKm`DjcDLW}A=wSVIa{6M;5vee7Fr?Rj@MA#6*styjH$Xk3W376SIuXR z)f3D0$P*pDaPJEng8N2k2V7@wT@zlC))7%4YnI%<2Q7jK7{?5kEA=7+9XG00@ayrU z`{Pjgvu?KF%kI!n&?O+s#!Ssb zrH|I*1(P#ww)Kj_TW5Da?KrPZJ4Wk!bXDnzt7dw$bZ|JFPH&Pf%j3Zr^|g$Y$3}@H z{a_c|$(0y6m%Nj~#o37*)ZRaV3)y8BH$G@Op;d_mSKb^{Sr7{p4bLBruTTTy@EcL( zSAHHrUISs3luX%reGx$;SpS2cpP5)K*B_iAbM7lxE2BVa?0}B(?2v(qb$D#cX-p2%&&}A0Q>*78>az{|WQErH{7?U)1e7zCchOcEK4qHrC z0{!Msb3C3G&w^S>wosj}S?yFLx@0JsauWqt?1lH+a%uBD-M1G2_ND`*?tGKDd?t^b z?u1%1D~GDixrJFM5NHZCZIU{<9dPb$y=+SJivVq<*ZmpktJccaB!Yxes+A>nc#T@m z9aAr^iN$Z!#=$~*40gthRLxE}M2-6ZLfM9o^q?{}Wb~z(Ory_8upg!U(JOMDw5Rnb zSQONxst+KQSis{JRHiPUo*V|SnO0N=jo15Q=MI?lWaC<=AJ{$QfdN1BpK~k(U>#^8n^&phY#Q`?XJ;Gl|zIz{2?VzTY z3Wzh?R_eh6eK|}SAoIL)7x1L`E6^V01p8{Ce!mzv#ZSwr8DRImN_b2meVE`YV{5Nh zVbRsb(zQ7LDU4kZvRe6@Xq@goJC$_X;y3quSD)Sc+-L9o-q-g&zt`{k>|dFg%zS3fXU?3N zbI!Y*i$4_!b9LQ7a#pgGMjAtf`OKU9*gyF1sAo@}M1r zf15M4v?=CjCU$ek>_K3q%1pBeN1{FrfN^Gqz_g+WIAGcPWlx8bohn9SQQ$t&7 zZ%|4|V@4(kw*not=xtjx9%|o>$&UR$0$%=RI{KBAY990pU4qBxJyY#}37Y;76b(1; z9>4DV{BHgW-BEkakyDSpUc(o<4C&(n#Q>U<7CgY*ttT>rr|2RmmQJRX4CLE+v@z2x z@W$3Q8Ba{fZmBxeWkzN+%WN_VWFSgofJs=4+H7A=AwXCd#cb6SVC`SJqXoa|9snN5 z(T>IC5lk@Re9y%NU6q$7K^}~ywg zvtFgUCK-1TP)uL#b27ME&9E~ym^*1gZ# zCOg+@-_T5LIs z2DsUZn9=-&?z(~{4BM#VUn+%)hIDCRt4#1*eQ=WOMU{Ox@H)z*?@8JJ`0bbsmy9D2kf0_Ti zt1P3nhAfREfL{$@27RFmNx7x&Yo?!$XuXMge(974B2`o4ylp#8$1g0J!n%RC@_?RWJ-Vvo~QNyvffv0=hyf>sVWJUCUynKUk)*B#)+I>7LA{PPrwHc#ictB$Yu@T?&N{xgCy$5ytAYc6EfH z`s_2^;gI~ebI~u`A4?eD6kSAWtaeyqG-dsci;X_fOrh6%1IO^L)}c#5?ZF^f=AauFobOTdfMb^-^fv?gE*v}hHYf3&Y!uYB5UpD}7PBt>uh7k{-G-^r{Vj|r7hd0tQ0 zF-?4AT(W-TO2C5skxy)j`oQsF%4G7hF4PygYQep%H?67wM3t-2Jb5j=DKS6M__OmN z|3}Q^jL1Ayp$v+YIi|E3q%MA;^EX&Gxb>;x3*C6+q0tAM6fE$8Le3U-Pjgxy+#&2I z@6Yg_v%Uq~L#ih;#$|1b)B9Bc%Hc%S+8ls}6eqFeUs4Ki12zMV4c?3nods-zCF z!Nhjed?e$y_=@7yyEMKT(-ynkxA@RdPbPnU`JxqRsB{48XsyP&ke z;%MFHuP31YgA{TxR9a;CDJ7Z{f$;%Wvx47}=4KhN+I%eZNviW}&UiNjMdp}Q5~|Ps zHF=|xRCHU<=UqvaU*n(rn#9}D-)<4lwl!KKHLVyjb)t z{I?`3@OqN&efIeCCjD18X{%^Zn(@)}1AVo6kVeH^_*OcyJ*{Cu}<9 zq`rq_C#HYbL>Pw}j`I6L_j>z4)5ATlsY@fp>H|n!S`M9JCG9eEWwYcGsi-tL+D$Pt zUIYi&2{&QzFBtwKB}g7s69bRG)#~_NoAZr`@&9{PT>u@=sLS+^badQ-F1J7FiHO`d zeB)q2xVacRV3rk9TR0-wfdHf0dgHLv4N`5Ysn z=^nT+YSG@(IUb}z>uoA_wIe|ckXazU|3%=NVa2Koi zN{U=s<_9BLFhh`MU7O!Jqv!7aM11hc(tUyHEd9kK#idM*Ja}KwFog?!XA0`ba2q0I zIw${r2C+_BG2lSSx@!tM${q!%7Zt0jW>Ts;ztB0tA<2>vu4{j{Sf@2a*_YTDJM%{X>73g#&eFM9Ms+C8NdMrEViLdj7(@LT(d z;~yicjMa9_-FIJhh-(&?jK)t9JrCWtk_(^FcuvO$%hNi$NjEB0Csod;frEtK_h)PjX{!F};Wa z2>hTLBtvy~QTT*qW>74T@;e+N>6liXS$9pNgwRtZdEqd2^7P}`x zr90Lcxdl|g4tpxmzMM5SYqBx#_o(eivg?`)O%-ur+^KAEKY2r7-6K1;c#w|yw-44y z&m%MsIZ?Hv=twOnjI(h`>u7Z17?rZXrz*N{dDNJCET-CJzU30hGxphFqI3JFq4M96 z(tfY#%F|wAeKh$;nyx3l>7qn7ef#9A(w_a7J`#Rgkgv<%f8)ePHhqKNit=SW{XL2J z>+XN}`;P=*kLgs)$D3|sOTZ(&}Ru z^GQR|r}$kwB1xHCYnol?CSs!PL}o&4O?|kgVWZR9Y+(*q1`jGICu{@zy0$4k;NC}s zXfvpLucWc!fMYtWca8>A;H z-*}uoxKcfpm%UPV7&d6XJ?uub=u*e!?_}<7Qc`cG+V%iD80>>9wN^UrBKG{0vzAAz z%KPFEOLMXU$1=W?lFxuF;5&`tJ|&D!j$_yeA?hYL$&syCMJKU4n2a|`547bnkw;cs z+n3x4cl9>6G$dmdS-odzVxXVBzt9OXZI<`iW~18gG!Kc2e9vO~>5!h4f`RcO)1J4w zYm{)54u+s>$gp8^p0~1>1MFz~Bm`&(GV&n(=)(*1Oq?T; z*Gjmi7YaNf%IHl+)zjfRk}49+lYrgg9$?)UI(Mi_>)qCT2j69X;VoB5mrKNwd`5Qj z4gvp>)>3IjGIxtwpRDtpWp8#10oPm`ZEKB^!JJ%Ctd#PhZ|&#~&U23+x)(bm)m*p^ zm9_90W=Vh1Z4Z91D^(op`F$b*|}&hcRDPa=AH)$u2h5lqVMV% zn*!X`D|FE;b$i`50FuW!dDXJN%>9mnl~RG!(*7A6G}*EybBi*9=nwC2vX9; zV1(}G>%fj2!$7UI+3(i3DZ^P7xcK6DgQ4nZf8k0aUqs5>LaYZ3>c3Fjr^jUHLNfu1mMRZdOlC;Bn>E##3Mc@*@1s^J~f>1VjK6=#Ijf9wQ&kQ2EaG{GeOE2Y>yx zIgoiWrV7AAk#Lum6kX_g6RXzag+5 zJK+Uanf`}RSZPGrR!4Bc$mdIkcQ;0uXLpW&4qym8xs`Iyr*_$Jt@fib_mY8aK05x` z99lUx{=sFECweenzHEzg@8$K&f8FW*r&Xwrtgr6C*8o2pmezYws^5&)J-X#>G{yrD z54x=B!#ISWw5|Sn^oyBNk@n@I)5pB$Pj)A-9T@xG@Kzd3Zhyci`|W5|F`|`=%P0AD zwx6!P-hDBgHt}=USlteMFE0P<(bx8!LNg5v6+B0bs^itot^n-@mm8W5sLjyTWVH#sVvIIRl8*D0nPIX>FqOTahwP1 zUZ!phlN<3G_$p91TZY*ns^emQ_NU2w z)y$He$~_E_g|92qnopV7x8I2Qk%Tmu^rhcgYl@H*@Iz7M83^8UI5Ml;6_HO=b4N-=aeg60WRdn};ho&p%-i1mB$kztlc z{^y<{NgzuXu}pQKIL`sgIH6Zj2LpsEH&+@j%oed-GPo-kR<|sFz7{S*pEo~rfh&hk z%#LNF$UQB_X~1bm6m!bl`m6 zvgVw9qe|;f$WDnEDibLj8*P@{W;*gY9MIBllRcj^kIqDQH>|MoS`5iqMqOwtvg9&T zVyFj9fn~E?*+%rC=^keK=N`FXfdR_+cHT0B)iS@bJHv3+_0NTwclqDSp2Bmu7@9Zr z`I+A+6zXzHCus7<k&{Q&2T-b%Dl3cuJ*+k)q6W>!rAH%GUYUeFLT$8nj{)W|r{^qq}8{HlSF z`P}-hYpikkw3Con(}3oyGG0SKI0}(ZQd2k9uPT#O?3iiMvAy7qWb<`~E0}{s`(DJy zI}~aWhS-X!@`_<#01ls~)uq_glBkq=I^ERT&@@^>a4r=#+OJ(hn1V|fm0A^36?atZ z`l&j`qRsl-_6~!AEzN+akVazjlsbGnHHG)b-+%vlYcBB|wa>36AwQGCpsqlD8FcL8@W4#54@n z$IMYBv#?vy89{1U;ipZcj|T)ip*xDu=J#$E8Qmp$fPo&g!E*H8@9#6+$!^t$G=#2; zf$w5UUz=C?Z*P3)1@_XK-??jD``zoLk)sfm@sRGuhrfyWZS|Y_$n-|_JBfh^^~SLv zg$$o`)7fX%6UZ2kCpcxwJ#xO)5j;cv>5oX@Sl8RpltoSHq-757=Hp^hgYW6>l!j%L zB99)=A}MKiNvi#!=RQ7boAc3lfe0XQb$Ap%_7MEs_2}jgz{J(ZA65*u)8aaIF!Z#6 zP>87D^D|!~eGRSt%i684k;1-)E=WH6Ez;M}ER63@ev9-qc5031pBMS-P5LvF|5Bag zvn1=V-)qq1b>=7$uA84r_F}-+a~gs=kufxGr8;efS?w-s{lW#(Xx-%^CYd+dY4OP_ zws^#)ygkP>$3YC((_EO?%%u+p<}gMrgd^VubqYQ$i(OM|9(t=J1hHvKLzx*SZpxdE z&hm{)Od!&R!_+k>x!wRE2H`)k)y9UGhD>2PlY7f-OeC8M2eAq!nxs{q#Oyk);e@3I z*<2dZw)YnKUy&&L2lK#x{rsXk4Z=*fnpkI>H062@OIo>b%S{)S<&mF4Krg16u<#U?5 zz^BU62tSw08qJqt2MI*hiqNFl!F_Q+LiN0p~RVqZ3QLaZt`6?+8nsRqPYvQ!j;xN2td`C z)0dv)T@ zG0iKde)T%h?c@JlP14%z5sC9pPmlhbD8guq=^=JtxI%VH+(FJqk3%=O=-3-Ywh9Y3 zUPLyj00pz63rcTnYSf!%rLOjMi&aDs!k;Ay?c>In7uD}>zTR6h3fF-m4z^~M&Tz2{ zkhieHozibY3<5B(6q$^ziuGCY&F?viR}FADhZ5rhz_XHlS8+4X2}8V4ps%wUd?}>* zJnA(vj6V-u?p>=gIAir&~Mn;;PXnk!2J|S=)Uq0Ju35c(AOWd<3ULDAI z(H0@eYdnE;f4~83O2C&YE*)$I(l8dEs_BUzEfzXIjxLh&Tn!l>yssKH2M8Rv6#n>( zYVl^?SB5RI9j6$DI42`BTO{Vonf!TSc-e>7JGc7Nddr-YIzo~Gj7Lo%?{8ug3hEzd zPTDq4nep34EnAx4Y4J&y+Yt>zemTt@}N9FJZ2ZtMQ$XaApu}Hy{Z_6p_s*%W@88SICCDcc)ToJs6*>eI1 zKtlpt;FwSUt?LLYA_h9Mt7ji#vaw}vSdFPs;iI@_e~LCuP~tVSDlB9b|J1+k50~|( ztTZ*kZ&vA>u9qTR+$3usnPP37HWN0T-4FJp6=K{BID2MfQL#WGcaaW_cC-gS3gmPk+2FzwjH}A!rGYr_g@Y0{!>UH5hwHodq zCy1yr%k-wKBKG<^tV$kCW;Gp*Q9J~l&G$KxW<}EF>G5Fv*!CU>OfY!%J81i^hZ(>HhD((2b5I?+>WrV+&~V+JDCe_nSS0E6w)Sgzn^}|9C5&X3zGr zpGIoi%=LW6MXYZ_Q@^-aT^Bf}G7S(C`WO4C2OO-u|1rORM zww9`=L&j%u>H}w-NU@p08xEHVD&nOAa^?Nz)pzLq4~=yA3}#8~ecYUpC<8BdOSc&b1KaHY!Fbik_DiX5L7DDZ&18LO3B8CA@(lM# zlz6-C_Am7s9gnVQ-i3v^uriqFtHhLCs~?1b$TMziNl zi&$5rdCYmb*Tmu+fumv=fTruZtFXX`XK#11*`n?80uO8_bUHzOX8wN9hc5U}DPXgp zb9^X=c}Tjuy+Ip)U`W!mUv`v5D4*8c6X$M^Yq1?Bu5mQtlLjIV{eF^U+`N$6QWt+O zDf9+7AE7-w070ZjZstZQ_e3c$7&5x4v51c)>EzsisGbNgUT{oQSkf0iGfaZpSe=E! zo#&LCDcEyWtY*TV$Sjycy6-~E+>;Xa(!+cBNxh0r6)&TA`67^?qv*9{wnf8Wua%8j zxD%Zf9avZSFUrwVZn~A)B@wIR9kmK6!F?`_Ns5jk9QQQ#@>iX$yP#kzL1>Ezyc`DQ4BOi;RZ{!RqW`6YP$ z$-2`(>A{khYhYRP>%o^_=;Xn7!Tv(cm=wkww}i*m8xdd+c|KWEa7x$pI!#`_xJ?5- zf_g#a)#)V*CPlE}#>sRigjZNY!|1q3`A}DU(Y=iG`k)LEwh_Zy(|CKDjIH9mW=5;v zkXhM<8E~NZ7At>laVD>hY5)Eu_3+RW_sN9zh`Hf~rNAor_00L2l=Cd>P0cVzB8jUp z6^Kjbr#8;@2kI#y&bmVdTvH75S16V6 zkZI>5IC{#mdNO5_oev=r0pys0m0K3B)>J9Se6%W7Y}Bck!8|NboSKMM^cnC7WDgL+y+HC&n4J2XBeb*U-E~f1gWc!2YZ&eEIIJh54Z{(Z(|IA#w5h@O)UV zaIY5IEdJqybJJ0uhyS6aeW4REE)ZSuc%92SLILL1P059EqEcsr-fldARC`QzSD}w7rI3T~1uA{u!oe;t>Du$VnpCVkb970YfyHZU1 z1=dP%7`-xgst!>*JIb-Up@{X)%-O-i-n_x$VNR6lW#BfNy*g#5)CU4S{b7F zfon10EUX?WoC^DSa_h_WwbfSR^o_hZTcEZeu%YHF|F_Aluy=~UN6}=+4lrMB0_-`E zZd&HeBS1Y?*}U(>6xgnM;5X-&E z(sN#s&G+h1rj2c%lChz7< zJF`y~NuRp*Ga9D)IDg4U^eVKv^G92|S^R{nBg)CfwL`Ov%1cN5dVayAM|8Ytfo60L z%o1!wXEwmRu%liR`{C!6V%F#tY;HI@0kAZUEE`Ac8PzTS{6d-a0=1*)ai8I0U4pzd z)no7C`F^SB=>D_i*ONfpSyIxZ%Bkc&Y{bAvL|T-Q8#SO_mzU=?@l`#w7Pg(QW!w&Z zfNk$=uce&AF0I8eCP0B8UT_Ivab)zM36Ky8N?IC_Z3=^@re=Q1S$+HYk*EADv7$8S zswGJL0Vnb@boBA(4;uZshye#?JR@g?h>geE`VXkg@b|EVsmv_y!4#OJ#yMsw7Uf=q zem|u;Wi=IdrPY`FmnUv(MJV4km-A_04}c?n(jdyiU|r}%!XGIQmKr^Sj~=0V;( zha-A`;x0f4m6;~{=zo-uYahuA@r_Q6XKm#$!*U)))Xk;>0JQtni{7|=ZqkE888-t3 zGPjSojT7X7Y;uv-QPHDrZpp9f2TZw&bb_1GyG2T^t_q}I93Ni;oj~!j89#9<3NQgyzdFp2y^s28THqY0`wS*Prd}LqY(`fN{ z;I;}P`R)bG&m4zgNf9*z+T474G0AWMZd*9jR|&B6ut%%SJwOSpl#(TIJ7y=jF4_U? z)8)>M$h>yibu$X2GRpLPabv#$Z9Cn7S02 zd+FO0=8w^iz1RX!WR2mv7XE@&@UEt4?g+-6rahVJGqTOZj4Op)I?QQOoSuKxI#OIf z=`)*P&WaEij?f@+Vc>FXX&7cj>gw9I0WD9aio20bk92d5n&mV&86Pf~Y6*~J*7YM& z6}1yOlt)CY61h}l^kPs`3rm2%yo~Ao5FK{nn{!C|#y9r`opVC}k|4OD>JDKwxsz<~ z{cQ<{)b`UqBmJz@ika>b=|8$B|9R=m?>ao2PqxAbRSyz+#uewbkt{6IPQUz-AD--g z^21w9Y|Gq^I%QU!Avc$OvGHAOub#Xfjy+`JrwQ1?8W) zu=0_i!;Jh)W}&+hedW}<5%u?Du6_SRu~4qk#Zhkp1H}!FqW4FqL&*5ogC8XL-}ZB* zZk$L*%_jE58*$#O(BaHW?riDYy&0ls> za%C@81i3|F^r6}!1J7W#V{*%8_<;d(%pTKc2W)yRqU7~VfONj}&|y-g?_LPAxKuHU z_}Zp$g#I~Shyd_FQK5^6H*tk!30Eaee8iN5k2Wc}+Bq#pSgV{0ipNM@V5Y=ib93W9e|KfrqHt%Qr;{LxbXHNFFxKD(8g@4Bd%YoS30v>DAC}P?`26~BCIY`ntx80AWhI%v_)s{# z_)wC>EzSIR-qa#$u}Bgv8g5;P{7L@Exf+oN8d8wcr%^+@zZ0md9~6+dY#T%>K~xF; z_!*Tm&COR>5Sf+^s2pj$=77 zj5eNOy^A&4ixXGn?Aox1tX(l%YrOyqs;ji8Usn5wk=qsBRZ8_0u0AJo#Re)G8i^Kc z7zun_x}g0%2GU`y(Mbj=u=*2{CTR|sik-Yqd$FeZ;tpp7G`gpRfH6X_g_fulO_}^? zS%<_%q%S=NU6@63{*=8^RPq!}lyqlEb34MZ|I#k64I?rWjQ!jKU&2I991+V`l%9dO zJJhgvEbD0QGN%`@$@_S3|WO? zHIK|AKLFjw($M&D-xB1ap~c|Pq;ZJtYEe|{Kz~9~`kCOh0!!)8=KH;`lq(><`ieo! zTOT!tZ7|f1k|t_b3yuuf8#opaQAz4&l}2aVcl835kOQWoRWrqP4K!AIe{H*VUBhbW z5|X_YG6D+iSLHw!&GII&wZF|6Rd6p3pf*5d#Ahc7L{(DO<-hw$Z=AC;S#5Ph1qq$R*1=6jJyE>+P7KWM zYMQbPa(ayr;Eapj;%w1*cL;r88(u5E5EF{|ewFM9?B;hfugi0jGs;}zTjt$= z`Z*Astg{r8Kc>$FmpIgg`U;qhzJ@z0xo(P$=)})HoY+MmOYPiz1dq~@AQfY!?=K7% z1_iN9fVhifaRjG88tAu4o741^^OaEHxt`&IvJ+Zy020)Qyml{ScQ4*Em|^2EhW$} zQe^7|SfqdY(1=srk_Y#TUNbhKDd7pbtnjqRqBysGS!)BLbY}G9w~nr)6$f)Z49KU{ zGOl;5DzAwa*PkQVWXxP~v|nIF%9rqX%58hj`jsqOpY8g+1l3ULN}?`2b8P>%TyWv= zN`82wj{5q-<0MGE=TJwj9w$!)l){xL^e$P(cwwD8`NE{=;^*>Z^{_q?SFuif8~;U_ zwL6PT`5bzRlyGZTQAen-#2E;=j*N=xn%9Z7NEVZ&{W-37;Alv-sQ#mXq_00>qJ*W)R!knRE6hCJio}>QShS64O38l95$6 zmHp^}{`Y>Frqt0z3k;PDhcA@C1WZDU=Bba63h_Zn+;=0(lu^%HSIl*Fo#4&{$D6&0 zy3PmAjgljKeV?jSrcc&+n#w(Q4`A15P!dD;UTz}g7tTvD7T!C29E@hV&sQicUte}* zg#xes&e&R#Bah(LV7)LSq3*zLqS+oECd3=g*Lp=qs!C8fdzNHXN3#)WcWxnXC%rin z%9+f<|BId-?3#dWy{%D>^SM-Km9_*i6lU?`hGQQBKnbaIQePM%qgDdz)%$MFD%FmJ zXn2ZLXvVoEWMC3%+g4KIs;?ee1|%Hn{G>y8wazVG{z=hGDC3xUt~-_VXdE^2B9!V> zIwtk-kKMEW+dLHhGGX(S{z5VV;G0ufOqK2h(An%lYlpxBD%=?m~?jM4;biB=^r@7b7h{ zYem_`eNY#yZfN2@FDwBOyZ|O-6x3V{)qkwpk(qu_9DRMbkl;;-L32A>Ln-TZ!J%~o zs55PJaxht5-q&q8S?f`Qwajv*kx)RUS^LymlC5p&_}cqZg_nS zKmrgc-;_tJsegraAjBB@xQAV*HuBv&&@*#4){9R*!y9wl8eg?jYibkd$BmhNWwMm?WK&XDb3SJp|=|w!}mgi-%m$Xoo~H3%w{$Fq(nrADe+#C zLs1VJ1oEI7I=w;d=tOcItBSEGDtWd_niidU>{#p1N~9^gV0Oh26ge<=lkuZOKQudOG z{)k?)t1L4&fJ7*PyICeMR?9MY_YizFv|-mr#deG%Pq+3f@?3+m3_1k~GP?e-PRv6u z8G9?!Cf&!;Txy*17S9>C&?5%c0d`o{b{ihj?*1BvgIk58HfUqzi z^FYE7zH(2NbHOk=X_OytGVQCMbJaFQi4kka`GszBX+>O|K-n_hNAd?{ibSz(zIMah zw2}$~%-Uy%7asd1Ujn4rnaz0Tq80a)N+)(7=M)OP0L{qSLxPs|PNhSWD>2dX8SL^7 z!{pSgo#c%-#TQlD?dON0Gh+LJZ+z0Q_+xxLSvVksM|#zJdWf@MibcU|M5@YeEwj09 z21Fa8%zD@K`<~Fh70B@aAo}@)U*NznO=Hnr+F;?ZK{2h*q5wqZbJUc;G+}6y&lCEE zVcLB0n(Z=SzTpT`2Vx};v`zyXX8k~y6y9Mg`I8ypWwR-sxnZqi(As1ZGvOm=Nm*gP z8j}1OXf*!gQ-SvhZ6L>sgs3t`cuUc(mvPPQO#}x}`;d1Oq#<*DRhDr~WlPp1tv>x6 zrrjJ@0*yY)eG%8F$rVZ-?$a-(f^+e7mU@J3O!T`}NScr$sIc`FvOfh-uOvT##m!ho z$y*hxxRto$8nyPb;krp){1Il!djv|JTZDX&CN6cgZhC=s!OTCNqX6UAfRe4zr6o47 zG+zZ`&%(0mIwcppHzpVYX2YuD~cQp%I)8JI_J4^XsLw~f&9p?)> z)&VLHDqS0Llk83ZM;M6(-pX1#@CP3oZ(Y7zHWM&oaJ#!h=c&=2U$w{=I$`fKFGe=1 zH?rQCT6~vMyS)Ek-{`|lmGP)AbWzIR)qG?+=5cBHLf4Y~Y+~my|1jqh3oG5ejE54nKsaC2CU4Wr~_Z6!yBqI|Il zt6)oV`MZgfw^#f7Q_>gI4pYCbzVY{*a{aV%G9=v=zn>EzyRq(zdQ zy=mQoVp}t~@or;>A!Z4hb#;EluqEFZdXSmwn2jzdSSXTT z+|6?L@;BQOry!E8kB`F7df&7{i0hH_h~SAs0T89Q|>{UB(gKMX()nI zC_GxRM|k=EDR4_+l|ZvFPPoje%QJD>)QwJQk{V<*b6_CarK5tBzQhqN3Tguq387Ck zPugl~gZM)|r>9KtpilYgnyGJxQ6>H?hba3=WOPQ8Y!2_pzV*QRo{~H|8>1;gX$6Pc z3UcI=r`A(z^-f4gI9J^ja^5!O4-#QiG0_yw;T;aM+yV1KLTLSQxbH8we%nd|HMt_S z22)|lyEs__+?n=f0ti;6nr1u)*UC7DX^j$mxDtSQ`Wb%#Xi)v@eJTVp^DElOxZAkd zyxi{oLb1uTOMRLEBt|a=1AYw~S&?FbWOjrnB=JyPq?YZ@rj# z%BIF5!-N3e5imGQ;P-az52>|vFtQU?3)xA1LScsF^bW|cKAnMtofa;X)-Waput9dGNEUG%Nr>^lm0*b;NGK+EQDbF}CPirVRO(>3=aP?_VNaobmXgT52w2=Jzn}`a zTQLGfbxr$K0!?@+qGe;*dC!X8ri4~YQ)iRTfi4+uTZ{&55x9ZtGrdCo5t)Ty;CZy- zAUCu++m(%Or|}*vPBlWoGlF{L*Nz@SahD__{O?c(H=^IX5Gyvaux`WV!CN`U1_uHS zP4f`jnH_3+FDqgS6$8!ohn^^g)zZuryL7S*A=5|@zCx6z?$ZZxmEG~%yZrv*7k;3b z*4c2Q%|$T=&pI3+A=UL9FW&w3aiTlf*#Cy^Ps6vbIr(P{-@fkqpMU>Ivl}SL|8m5Y z^;6`uz{hG^&CQvE#vf9TuK4~k%DU8a>|5+}>z3aGMA8Yn-Bd!) z$1%Hn+JwrTeZlyDNxc8>o$NQ#O}^Ww-(+Rk7q-V{7AeQwrzoO^!-$BQR6G=o%MMwN zpA#k-<~msIl5$~*v8AyTq+pz1E~Yup!s*!7OQZ?^GH-9PV4ydrs#UmVW+EO16`A%f zbKms8Y*nLk^WVWQxK8Je_0v3-L0L%z@j zq^w82cM*Dg>)UGB*FD&8UTFSv`xm-D^YGUi^JgCZkMfWL8Eiq>%uP9lMLIM27O8*% zzGw46#s;rmpPF%`JNx@b_Mch%hxSTzOVi7`?%0r8x1`*#*F8*0&c$7F=KJ<3n&PwR z*BkIc6x)#2`|ctM4ZR*DYBo)4g)8;(uyKU2?8oU%W@n{Ez4_p#>E!vV;j`m)2M87M z(@L9(eR@^QcNXs~T-7f=me&EL-0w{gd6qn8&$1==D81EWBCnqqd6%mYS<+wWOzq-j zUZ`5M1yo%nl%!>K7q7kEP!zbHV>3mv>~*$rZ0m0cZsl6E3$-gTd`0qP4oU1@3In4~ z2k>0`e0#`oxxe$?G6dqv+X!3ttxsSv9PRU5E%BAR@)i=gp5!g$sof*oZ7vP( zIV_wST~?KG3gUmoSIU)?>LG>L$W-=7$1?zk*}Kl2<3;-JIpS3vB@Tx7=L_RRpB zmAaZ~J??lA<~o?63e@?55n4%n;UB@moA5q;Cg%lw#T)LqOfT{2 z<1u-*gLux1LxOrL5yY*+2vQYGz4_TfDg86TIG-3nBe`9FX;9Hm>R?I#;)@?cIHejI zRwv=ghKz@oe(v#JyaRRPr`|kzlsZLJ>@wxmiO*oKWwh4Z3>iGCrHr_1l|l~)pRt*g znURxH17;@eK0jojuK@AL%3jQ*Wnu^FiaxV`@syhTn2h@9*{6S}=AZd@`m6q(^ZmDO z`bX5n|4W|^iTuH4VK9rMC?1K*D#NRPW&fPu@0+sUJ3(W`=T!<{7TUSKmVGm8 zFvIgkcWup^jTC73`JY?-LcmW&Z~pe*@BaKV!}HH1(CW#bN%%7fe{C`TnVRtW(8*ta zF`k@CN!jPnx;Aj+KG*4TtU6<95D9sII?Ll)d0*-=@lK z6uMR7K5A)aB|(r<&QW~n=|E>6`=v|4wgwyFmr=twyzp?ZcVLWHl&STwM$2H@tSN0$ z9vr}OydGt<$!LS9FcFC($BC-kHF`)?nD@4HG;Z%lFR#VTZK^`X;J8LpXz8Ap^Qw5PFO#MhCxCC-?P$ykO>whU&E3Mid!^@ccN zL_-$_@hnnamKS$mun9EG<_xev1pT(!r2_GbPRyV3(&?N(;z@XM z0aG)c!pWtZ_I*dxBX452ayPNHf`rRP(}3MNRm7+ zm$g8bU0Gfg|KKJU-mHD6vN|2;F<3wx&)QL^ktHFhL-l>|oD=i78$7q=uyFS2*t`U%I5bR$v_^&RG zpPm)Vtry*B)MVDw+n}I6^9a*UtRzj74AZEaJ(iTjx1KwQhAl^WJYe@K;a*+ zGE3?n9>*-&z!&0L_SMD(T$jl^cfZgTOB@?%_J;rF`WmEt!{IlL!y+=x^`@_{Nt!ux z-@eT@C+g&rre;<)Wyg9x>j4Di91>D4G;T6zUPkDd`T(!sNL%XadNCe!b(dRZaAdf| zid&ADk?B9YI3K1mJMP0qiLA_W&^O9?Y>A3|AqkdbI&2#|ckv21;`#&e`F7dtS)9hS zu`!5hXlP`kK8kFwh6)}%+DiFC$8&UXF8#&FYGEIFqahzS=4wV-mZly=5gwZf02~WX z?!4qZ`!c1_H)-*6|A!&a0AXIBA}0?jkW)EQt2?JKCq;L;J;nQ5ZT@dt_+jywjGQa` zLdUE9i{cl$ywQD{%VblJ@odEFC&0u@g=>HL&b^>4nL^Hxy((ceg~dBCA~*}us%0N= zb~S|_vO2}=F+aV8ZaxrwLNePrLhl2DB$`9@Ntm!I_s6g>kTD2B%vB43cqUb2npgAo zSjBFZ8nhe^9$%6GpMt!M7Oh|tFf;LC6ah9{%s6=W@xnc;cz#*DA%6N%CaOW{Oi;#) zvLLb)sD&&#pEG?WH!qGzEa}TL9j|T2=~nSmZFbAO*VfKFvYa`iZr0sjT&XB{qrt2& z#%o^oq_xM*2Cc!p$oG-%vNqUJp)zyoz(C)3Dc5uE8r$DI@|EIu612+w!FX=q0EAhI z28--@m(NCk_bRYWNj}cy4^LkA&wej}O-o<=xGI~(dsUNPX_eTj6kl(5@j6yeCQ;mD zPBgS4>;1q%^gx-5oh183OY2(0o>h#i%z_sFWAKo|Uf0Ta7+8r4T+uJ{X$r>Znx05D zn~@z{3cI4m(iS9M%E_?=)f4T~c3rRPE#*VnXmtS)xh=gW0AZS+59*HJm?{1)UTXMf zy?MKz@Y#Ijr1&1|4D4=d!8S4tT=a~00%UaxX4qVj7WD}i+W*u-tErkt?BEL2!N4jZ z9r2!Nldn)RVVgG@0|&bAcI9j^y!)G~_0Nq$E5KBZTskAa&?zQP(uSd5T(nzo+TRG6 zGy2Og^y@#6bMgYWN~{K0e4$$vH~5@$8FX2A-{xp_G3986<~ISlb|BF-aOhrgto4Ns zJQEA190I6bB0$2cNeyMi8=WsTGIvUG{A_TMdee)Vl}I!Six*#eixc0i9iw-*_9k;}5* z_Z1YD$>((`$HvAZ`bZwOE^@1DG?xbtTF@1db^L#_c@gL-daqnFh9KOJw27kM9^4Js z+?ABG;V;qI4bmw8e806_7w^=r#L_}PeOzFttk-*8R@c{s2Kmw{6l7~KfgMqw3S60a zccvJV>3QU#wg(?npHc3~-z~7`7_ysUI4hP1fFlWg3RlU89QJIRbglB$=B~HaQ(`Le6$9*afAn)D)Zt=Y?WXlaP)I#Xl9Lj=vhG8E3e77*?yL z#5&P|d}Cp0VNqdr=bDkVzE2r4;}$PFt;!XYVaf2Jx!7);tdx`+k!C(Rn9IeEaUuD7 zUPINB{ou2EAU~Je+uf+N(MsvjvCK)qkF}8-p1ZQgyO%5Mh5^p!;ojtd@P*FpYRqf{hdm{}%nqmfx2OW7#8lHRsOGJxd1-wpl3$Qvxo;O2nE zyuI2bwN9EpoP9PWQp0{a%q1tZkC6NAMDuSa^ubF%%uki`e|dSt6(dgk5{>EA-@K42 zF_n33+U9ep(V8AtM2xlbyz%+cuO}p)>D~UDQT0Di{g$gO;mrCt_S*?U{#)^fLw@0V zzn$QCbJKxLG{w43kAz%0p$-0{@AyxJZ}g*8chx@~>U}Kf`j_Qdc)3540*>L>839K* z2STxSzn#c^@s^8ZGW&z0D79l^x2xgH0WQI_GI__P;H`R&&v+&0sf=__>$zT*vy_z^ zesO*aNED*DJ2Sa@NvAIF-`%)e&khZJbrfaAZwvPqijALqj#^bT^}px+e&tn1qiV>j zQ3PCMjc4xRtZc$YYW;D*UtMFu-|XfPE8|AZlMGNYrF5KBf3@ zZ}zV%^M{X?97Af!y5H)Eq4Yem6#1%YekG%L(cd6?Tn^=k}tu>$g&jg4^$W zuKKHMR^8oD*yJ^z#K#wmL@#hL_Nlb6%e65_DJkc}PO}H$PXrK7C~GDWg)9gKZyPR- zFH{?Yr(V8k?qaoh-wT~fi8QGl3wpQ~J> zH2uFud8$TNelJ+j^i15-99B}fIDBiPQj87;^iOd4AYOdO>Ve~r^z2*##ex$J2N+6O79@SO6ne60bqHHAt%}tD$j19bzQ8ntd<3 zIeQO=_9A7Ovj%oQedj&4lyn=s+{;{z;b5$TZ|{R!1|VY|=9SAUhUB221 z-M-;*Dm?wr7fwLiNGB^)VD3>7Qlxl%2>{jaT-&=Vh|;>ZI%$+G5h(B-PYA^x(Z`}0h{Ipm2BD8ba23$q9sx2!!MVieE zjPnH9rUdy^K6N*M>^rU|Mb2%H%6H*>DH5u&Lo%{6ki(K+Su@XIakAXFBe?yWW=>=PXB5?U*=`3G?I@PTtIPuS0|Joz}?1_Kc z#9w;gFXcaN;-5Y7mmc^_`TxWwb^v8I+jdTIvyhj&kYnQkpLZ65Z%*=+`p5EJczJ>w zA3E{p@X+u5#qVo>A3=5ZGa&rw`@=U!YKL5^uHN6j{*P5V`KV)>OCWgt>f;3Mn;(Xw}gmNEs`m;oQsmr&cY*Ti@*k>pP zCT__4erQxwf2rfLlw3516Nw1i402jo1^x=zx5lWrt!O{8E0H!rafxM*m4-&nYqxr) zhqJCX+CNXEWc1q~4dfBpWtte9?;MXUgmTpd+m=Fz0(MdSUj8q|C3$=MGEE`u)Nq#s zhUxdC=FAr91XDhDk$`0mjN;PtHkN<5nBz?1ru)}Xv_kb$uImCN;`@b375>X$aceLd ztXXUcvv-G8Lt}v^jM+f^Jk(|X;Xxd;W9DSxBX9D(*{#eJnS@3NR-WN&d@f)DMva!T z^FZPHxyop{xvNXhmlhyXl=*{TMnI>v z`h?bjPuMFbL>c4=LQU%OH+`UtwwiWRfs{C@X9-<78Qm??*38@KxF$yVCX1Us$R;b> zH5AwCuNKcexxM{D=`<~+!=c?MF?H_ga8A*akBV8DhBq2u3@7~jKuB7Ua~~C}bxv^q zB>hlrpgNeZsB~#{{e@E?7}#(YtpA+CIHB$7!MyZBwbSQn=Gn)-uU$kS@jis1iV~9H?=*89o1>;W|I->7h7-hMPQ10qH6VvErY6Pll{aJnqoqa z58Mjmv=26Z^mQjdqmn5uEr*jHWyl^r>K$~y0mMAvoinEOmH%xDQv4LrEptk(dFiSc zC$>McZIS#+0P5%=u+C-J_s2UQ7BU|NOdLcw{``8IoiH%$8}XQWGl0PllsU^k;5K#c zJ7xna@l8fW0WlF10*EQ5G*BnthCP|<!_&lB7Ay+Yp~FUFl|(uG0-4ZRkwNWJspv zSHTxQdaDXpb(@c81%>4=C4G~S8w@6~<+rN8~%Fb&mpC%`gTanYd|A(snf9ak6Z7l!M zkNCUse$2n_+PW_E6tzU~TKo-#>K`@zKmSaO*Xv)sadt|f+HWUh(*gx8Z9HFnp{A|_ ze4z8bPPHMqFIU9&s}bD1)tLTREuwS15dK?I;nLcLBINXJ?=Tf|K0ZO0W?_s53wz-S z9gQyDfVQo&i%OF#alV!NIuUkz4=jd4O_M)iFILJwD4IhL{#xDQJwG2h8?gCyPyZ~d zY*dEeVW(g`h{x$lTjbyVRE&&pE|u*ApquLIvDxFYiVBVXO8Zb#lc~VNDiT*2CM_5| z;XV74lHxX~0MRF6wyt5l)czxRro?S^xZ9vfYxHfh%an(KWgwSHmQ4mN)~Ss@vPtn3 zaC^2IVG@jJ^*$Crxi2*LcfgYESurpNpjJ0PnLw_Ll70B~rXSmHvS#06nW|h6SfTdt z@J8bKJbAGAX*UsVJ4HgP)PRCT8`Vhr&G_??qU-S%-@1j$v$5Kx4}PjmPkSb~a%GoS zw)1*#K1czf;dEL3VBMs+Rz1dBf1*<@QqFL)7m%iy-7r@=^|tZSrWVsg^M_DVg#Zu@ z@p}InPYw-gRJc@u^8j(%iS*|R31MzwRyBVzPV|usQ2OL}<>m5S zER);|m)vz0(j}#x=f`BQL)udVY$ojiO(7DzUjR+DH2+_8pjI{7z|a9JD{Z+?X=tQQ zG3LH^wVA)ll4Nm;y9E3#8C)*ULgwaaLKC3|P)ybUUAx4!^u$K`1I1o`bXj~aLv36z zAdS+o@hqNB z)AJpQF22%buBC*Fm+J(JgzNw;I1cx6=zDju7c=+n27M`fGc#@P?N%Y=1H(JfKH$KU zgBn3l=m;NwkaI`)@2&NJse$^_vgeLd9PSebE0wmPsGwQoht?UZ!5?gWBpG4^#^sWk zDJ~OYS^Pk~yC1#pXyKVldLKMH_~0F)i>4JEk9ItRwn`R(Q37r8WuuqqnR@;cxg47-vz_; z?<^q)pT|A<>DHK8-ZM0(<|6EUIeJdEKUodI6^9;_<|~&X*hB_Fof>?0_4_iLh4fQO zd1ZAXY|i^I;<|Hc($(*v^ClY+>+@w7tgl&zbiMd`md>*5KNz1+?6U?;`Q2erj@L%`QF4*{oV81A0zR) z`itQo{v2T8EdAcN|FLRcy`H~+^52sA|Kl(FPq|28MzKT*BiiUp|%BsC%`XC{OwsKS3)uflN6?iGUWS-M@X#&)`jaD4 zF3plk%UP9yP%evz^WnQl6H2Ax5*OI>azv)psny-VBI{bmQfj7x5@CF{azWZhU5>@q z-asxUIR@qPj856Q6-J&mv0o7hp$9Hus7B2$w^8NGBO%bXc?vWE;?G~NQ4)GY6a7_L zu{q@0(>soJfbnF`gp^?KC^E#_d7wrjV_h>OBqWqMOzlKR9?ldq*2gQ+s7~a5nW;1? zLzG`^5L`USh>73V28mWC209OgJvMJ%x&B2?;gccy?8O6dvN^-E#vdOCp8|_ngJ>8c zZX_L#mS<_&Mf6P`no~j0PX#{0XYKprq#}3WOojXeq-5z}UI7}^+E^1SFDW}#bmk7; zcqBqZN^LzC_ll`BN%tEIE9>s}ai#vK-IprXcb1b04=t1>H#py{QqQv46L3)AMb%=ACJFQRaazo{Y=SRXM zReA(4bk}`PD)rsqx5StwQs>BESuhK+ibrB?w*(c^CtEUVO=%*Dl{eD@g0}5D=Cw!_ zjc^N}5R~beuNmZBSR|n{RGzjNN9SU2Gv}3Dn(s=}>H5>Y`1n97A|Dk-1e8rfq0W=nhonw^_pa^6?#$B1np$oDmzUm8wqbGLrlx8YIM9BG#XZZ`>^A z#AT*sBOfv#=GhG~G1(Det1;P;k}iOlyTWZlRIo}rrJw}39ejl;QbbrL^%eMp5g(qd zKh_O^Q$H9|s^GB|@l99hA`{odaabGi;2$7m34Tb@<^u_Uymk~+vkDq+QnzJ0X>mUX zaGcz(Kmk5rL3?g?wO<~?_-9KQ=S*+~#Us}ZM%p;b*D%6Op33T57Z?bN(v1UgMK382 zJk6YFPsYWtuUW6k-hO&VPsZ)LjMb*ZdIAgg&egB=`o5B>4-kA>5*&N6Wp3(`h@_4~ zMKD^!w+H*m!}z{~FZi^cb_x+g&8e;I_TAGY5wqnjnp$W`u>?dAh>xx30WmPA{Wz*K zoVk>kW$nH#JU<98G3)?|o;6~QJ@hy!KC`^FHZ&vbEM&)rX0)mtDm?x9#&W+Ka}Ufw zV8`-8><7@RgGKxUU0gsGHQ421Z^M+E#$~`Kzxs??3uaNO*e^-OQq%$^-a22meP4sn za;TJnt-s(HzZ$Ld1-`{=d8{(B?jo)~;;M>YMlaVLjcBGE-&wO_H?yrihbb+sL(e6f zOWYh#9E#pu-iEg*nakk}^OL#<20KToV-#Of0t8~~yf1Ti^T%mXPs?-V*ohHqB&-zp zpw$U)SY!hjWbjc9416Sm!sW>&>5?*Eo2|>i-ZK*WGG8Q_4VH5yRLL})lJx9Y1QY55 zF5cLM2Cs~{eU}0ZjRxwZ^^S-=zH5D!adouP)Aa1|g$xJ97-tn$TN7h#BpAl(gc{gF zpxq4hug_aOVLW1>2kiYorO7634JlD#IO@JH1}fxB6c7r8T82O6CCoODHOvR`T3$1m z3o2byRC(p=#^ouAJEcrknpCPQ3v=cF&dj8wpE?b?4)^~iqq=GC{=Vl73t>ueX|Rkc z?;x!Zv0p~UDgz;~qByFFsR&O9w4JjPZ7LO+df`H(Q&bjquHt-L2SQ8Ts0(ZpOCF*e z)@Ekq*_D;3w5XY(d`Fag#A@5qu4-Ry=W>a3!Ud+dXq?f0>~ew~5DJA)>>C*9{TK>- zA#W7tZ2GJ=9#=hJBKSk`@B@ZF@6^bR=8*ceaCTfOY>@U@o7AURl-A;CHl=gEfq^z2 zlV`FEj5CK=)}R^B$!S2Ao^N#uYKW9bnrAg_ZEoG)7^-|%`P%K^KWR$GUTUu%#J!*M znO6~}ynlUEg2-%cSahM&ku<+VwccrhH$xQ8Res%cDC1sc)RHV6k8y6;nVotXh&}eL&RzDrDCz283Saox zXZ>czr>7_t?Yw!Y0Qz|I%;SzE_9H4r?286Vpl(Us?|8k|0H~E`&skkMDBn}=mvfpZ zh24^Sd*~qj@{Q|QZeC{1Sh6~czodmcF<>Pi^u6DsX6WOm1{{C4b+^MEkKt5}PPyyr zU{+T^0`g!oW^P}{B$nCSQ!OgWtBcpF+_(wi>29IVI*S%X#9lNYY|J1S~+ybS&j87&jEhWQUvNDCLjn%w&VL3N8U%Xb-QQ!t~;zCWRVkEHM-e$#Xw`wGrxQUeU#BT5%W_0!s=0VZM#+I{>m3|NEG=p zUwoQ~I9q_#B6zy{B!S+{#?JU~g|Jha;ws})JmTv1^oe3FDt*Ok*Z|}$$-f!B4y?dPi_1x6SDxAvZ8EPtw$arhlHSP7r zx!m=820&jm4=pv>MUy3yzy@a23lCFRutITt$5`K+^ zahfMT3~FaiDhtZWM4=xh)gz^T@y!~PMWz2~F4P!EN(hDfNTmg>I9FF5@QgG4ZM5z6 z3-ABrmT}={nWfcHPa2bOAD{|t@DqR^SR;+ zO-mOc(2){upQreTkkUh2N!W*^w723`S1G|3yr)9zBbZYWL)?SJNB%;$V=KG?%jxz*W=HXiBi1fbvZNQCw|UVPk2g+~s*?BBGK4$pPyy`x{@9dxA0?viQp68l< zXO*o03tA$Qw%DaXk_s!7xngiAShyZd_1tX-RXi{O(b4M2ofTd6M%Tqb1{7*v=EV>1 z4?dYyE%&aPEc>U}ZCE6HKE+l^ z(RtQT8rP2@wlsz^geFJ$_(GJeW*D@xEFOL6ocGL&#!Q*+xw@LXN$*NIks4hjxU2&+$ufy6@^e#Exy%!vBYsd1$WcEXRP+8pe&avhyZ)CvIa_Oa>x-r3R3474XiC4aaXM3jB9L6mOAjY64~G@!z3z3DwcHhB=f)ny`J;i2^EfN@6$86o5MUA zNXA8P@pQr+RarEcz;N1EK$)j_0_O;0N}v4$5Y~PTKYl8n^P>V)n-em{@-McFhjNwB zdTgV5%VomSqPffEvlJ^f*v7Z7G-<+(mQl0?9^2U>{@Tt2r`}r{rjLjF#k$0)+ONxP zT|4VXQuJJ@E{m^ez9&C?FjL6-RErb%AtE$zE?L*FLafotnh~?G$$(O4F-*(ZLGv_I zgYyX!X|Oz;c7v>2Bn3DOz4qetilcS=d-3Q;^tQ`kdz_i$WjDyqwVH zm_2-K5q^<*%B$K(a+4i6VI^M8fZ8;(I;$(8ZZ@KBdUnDu=RXh!MjYr;l+xwJ^@pu;R9Zt8?l>$Lhr(VQd=(_z*3hM7M9&a z?W}DAw^YbF(o}haWGR;gu+<`UW=j%PQh6sntIg=?&7Vzo==JugPc5wmWus#o1RV4y2ToW!>P})JWK5JvjGmxwf&1UhzJvW;IdB!!jY;ZWiQu$LywG{|>UZ2rFIT`ms)^?QnQ6poKi=ldp)DB(JfV z%gYKXa2F<=hq&(U&r-C<^(z^}6N(h>O`g$TkLc<`XnV{+pw9b{;X~VA1&Yi1;dp); zT?HZ&|F}-Qi-Wg`ihbMQ=0KN6;kUq(N9)()5Dj#uZ}-mwqdn;T>6W`i)z8w~(JJ32 zR|LC>pVfY}xa+shZE<#=={G?XC2ld62Ygt3P@%~$r!|t^hkS}^w`R*FMW^2Kw69IX zubS++`+zjCt9A?l;eIrz)5v!)8(C*2d;8}P7)=qoKVu(Gob-C2v8S=r>ALzL&AIVO zE0^7Qk-8K3qvP$-G87G`hhEE~1EKBJTq^BNP^BWS}2jQs{49~5u^vC1M zP4S_1pS?b&=wGrxI^hY-%R4SQS#RW(zD!%8uRJjBEqyOMj83D?ePom;y-$wsNY$*8 ztXCrx{ZhWT18Z8`Gc0V0iEXS~Gm-LHJA-ISe>>W|?JE1cWlr_u%(Sg<%b2vU`}N*h zg|;0;?MH2o2|dG&M;6E;mUQ{)GEem{hK3(r3Rs~UA@uUWEG$VC9@WK7J-NLkZQ#R= zl%+fbplX7YUi75}+LYh-D7Rhp+;Gc>53Ju|jwY2?lqPpA5!!|%+X45R8z^trY59}K zW-FvBzj1B@@uYhG<>hfXy-Uu=C%>acDi=|pb$GNGfql|4sB7fM03 zir3;>Inop#@bj)#Yt2GCBOcDQAturP&exLi?5Qtj+`S{scN&FPXFdY-R54dR<;eU% z!DJ%yA20>&x{D1^POH0k3|3{rm+ITBx?oA zx+CxM@Z>@Okm-FlkOr6k(S1!HAD&1NqoFJaa2hQ4VBg>RQ!zw=Armg{G}l(`$Ej3) zS)cW|A*Q&3t0%>kXe8t9{EBfg#kj29b=cn^1i1L9#}Zu1wC#y6bWDeSzvbNsuwIL9 wTE*KMCC9OAt=PbSAx=Ym|L3Um^jTDc%YW3{x>yp=48g}3%`y0AI=(Ch5!Hn literal 0 HcmV?d00001 -- Gitee From 42bd67131df6848c7bed6e4868b17fce068251fa Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 6 Oct 2021 15:14:56 +0800 Subject: [PATCH 1418/1965] optimize ClassScanner.java --- src/main/java/io/jboot/utils/ClassScanner.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index c2038078..35e9a105 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -295,6 +295,11 @@ public class ClassScanner { excludeJars.add("pinyin4j"); excludeJars.add("ijpay-"); excludeJars.add("wildfly-"); + excludeJars.add("liquibase-"); + excludeJars.add("flowable-"); + excludeJars.add("mybatis-"); + excludeJars.add("ip2region-"); + excludeJars.add("java-uuid-generator-"); } @@ -403,6 +408,9 @@ public class ClassScanner { excludeClasses.add("jxl."); excludeClasses.add("org.jxls"); excludeClasses.add("org.kordamp"); + excludeClasses.add("org.mybatis"); + excludeClasses.add("org.lisonsoul"); + excludeClasses.add("org.flowable"); } -- Gitee From 426ef5228ba7955d3f0cb3203b451372577e0de3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 7 Oct 2021 17:26:47 +0800 Subject: [PATCH 1419/1965] optimize CacheableInterceptor.java --- .../components/cache/interceptor/CacheableInterceptor.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java index 517ab502..8a2ece5b 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java @@ -18,7 +18,6 @@ package io.jboot.components.cache.interceptor; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; -import com.jfinal.kit.LogKit; import com.jfinal.plugin.activerecord.Page; import io.jboot.Jboot; import io.jboot.components.cache.AopCache; @@ -73,9 +72,6 @@ public class CacheableInterceptor implements Interceptor { } else { inv.setReturnValue(data); } - if (devMode) { - LogKit.debug("Return value by @Cacheable for method: " + ClassUtil.buildMethodString(method)); - } } else { inv.invoke(); data = inv.getReturnValue(); -- Gitee From 0dcbec7218674dcd971eed223b13ec85ea0cf4e0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 7 Oct 2021 17:33:20 +0800 Subject: [PATCH 1420/1965] optimize SqlDebugger.java --- src/main/java/io/jboot/db/SqlDebugger.java | 81 ++++++++++++++-------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/src/main/java/io/jboot/db/SqlDebugger.java b/src/main/java/io/jboot/db/SqlDebugger.java index 5af3e675..2a5f433e 100644 --- a/src/main/java/io/jboot/db/SqlDebugger.java +++ b/src/main/java/io/jboot/db/SqlDebugger.java @@ -15,6 +15,7 @@ */ package io.jboot.db; +import com.jfinal.log.Log; import com.jfinal.plugin.activerecord.Config; import io.jboot.Jboot; import io.jboot.utils.DateUtil; @@ -30,31 +31,8 @@ import java.util.regex.Matcher; */ public class SqlDebugger { - private static SqlDebugPrinter defaultPrinter = new SqlDebugPrinter() { - private boolean printSqlEnable = Jboot.isDevMode(); - - @Override - public void setPrintEnable(boolean enable) { - this.printSqlEnable = enable; - } - - @Override - public boolean isPrintEnable(Config config) { - return printSqlEnable; - } - - @Override - public void print(String sql, Long takedTimeMillis) { - if (takedTimeMillis != null) { - System.out.println("Jboot exec sql taked " + takedTimeMillis + " ms >>> " + sql); - } else { - System.out.println("Jboot exec sql >>> " + sql); - } - } - }; - - private static SqlDebugPrinter printer = defaultPrinter; + private static SqlDebugPrinter printer = SqlDebugPrinter.DEFAULT_PRINTER; public static SqlDebugPrinter getPrinter() { return printer; @@ -114,11 +92,60 @@ public class SqlDebugger { public static interface SqlDebugPrinter { - public void setPrintEnable(boolean enable); + SqlDebugPrinter DEFAULT_PRINTER = new SqlDebugPrinter() { + + private boolean printSqlEnable = Jboot.isDevMode(); + + @Override + public void setPrintEnable(boolean enable) { + this.printSqlEnable = enable; + } + + @Override + public boolean isPrintEnable(Config config) { + return printSqlEnable; + } + + @Override + public void print(String sql, Long takedTimeMillis) { + if (takedTimeMillis != null) { + System.out.println("Jboot exec sql taked " + takedTimeMillis + " ms >>> " + sql); + } else { + System.out.println("Jboot exec sql >>> " + sql); + } + } + }; + + SqlDebugPrinter LOG_PRINTER = new SqlDebugPrinter() { + + private boolean printSqlEnable = Jboot.isDevMode(); + private Log log = Log.getLog("SqlDebugPrinter.LogPrinter"); + + @Override + public void setPrintEnable(boolean enable) { + this.printSqlEnable = enable; + } + + @Override + public boolean isPrintEnable(Config config) { + return printSqlEnable; + } + + @Override + public void print(String sql, Long takedTimeMillis) { + if (takedTimeMillis != null) { + log.debug("Jboot exec sql taked " + takedTimeMillis + " ms >>> " + sql); + } else { + log.debug("Jboot exec sql >>> " + sql); + } + } + }; + + void setPrintEnable(boolean enable); - public boolean isPrintEnable(Config config); + boolean isPrintEnable(Config config); - public void print(String sql, Long takedTimeMillis); + void print(String sql, Long takedTimeMillis); } public static interface SqlRunner { -- Gitee From 4f51b273e55184ddd425984c33ef4de02e678152 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 7 Oct 2021 17:36:46 +0800 Subject: [PATCH 1421/1965] optimize JbootActionReporter.java --- src/main/java/io/jboot/web/handler/JbootActionReporter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index edc68724..fc8d7ce8 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -184,7 +184,7 @@ public class JbootActionReporter { if (values.length == 1) { sb.append(name).append("="); if (values[0] != null && values[0].length() > maxOutputLengthOfParaValue) { - sb.append(values[0].substring(0, maxOutputLengthOfParaValue)).append("..."); + sb.append(values[0], 0, maxOutputLengthOfParaValue).append("..."); } else { sb.append(values[0]); } @@ -269,7 +269,7 @@ public class JbootActionReporter { } - private static String getClassFileName(Class clazz) { + private static String getClassFileName(Class clazz) { String classFileName = clazz.getName(); if (classFileName.contains("$")) { int indexOf = classFileName.contains(".") ? classFileName.lastIndexOf(".") + 1 : 0; -- Gitee From 3a0fb091b57c2f46a466d21506afb909ff68a776 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 8 Oct 2021 16:45:10 +0800 Subject: [PATCH 1422/1965] v3.11.2 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9d5d1af9..9ce29a61 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.11.1 + 3.11.2 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 82168a7e..a17f574b 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.11.1 + 3.11.2 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 4e23955f..f1f22460 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.11.1 + 3.11.2 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index e0c8f681..36f5201f 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.11.1 + 3.11.2 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.11.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.11.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 1c9835fb..bfe1623a 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.11.1', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.11.2', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.11.1/jboot-3.11.1.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.11.2/jboot-3.11.2.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 8b1b42b9..b0596de2 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.11.1"; + public static String VERSION = "3.11.2"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From a5abe88057f22ccde933adb639f80d20bd4074f2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 8 Oct 2021 16:46:46 +0800 Subject: [PATCH 1423/1965] v3.11.2 release (^.^)YYa!! --- changes.txt | 10 ++++++++++ pom.xml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index ab2c5d25..27d11fc8 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,13 @@ +jboot v3.11.2: +新增:Columns.containsName() 方法 +新增:SqlDebugger 新增对 Sql 输出到日志的实现方案,只要只有接口需要自己实现 +优化:捕获 ScheduledThreadPoolExecutor 所有业务异常,防止其意外终止调度 +优化:PaginateDirectiveBase,当其有数据的时候,显示分页内容 +优化:ClassScanner,新增某些排除对象 +优化:删除 CacheableInterceptor 一些不必要的 debug 信息 + + + jboot v3.11.1: 新增:SqlDebugPrinter 新增是否打印 SQL 的独立开关 新增:新增更多的 JbootApplication.createServer(...) 方法 diff --git a/pom.xml b/pom.xml index 30da5b8d..3fdde92a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.11.2-SNAPSHOT + 3.11.2 jar jboot -- Gitee From 860dee4134b6c79c07f0c8e6ca883c2fcba6ad84 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 12 Oct 2021 20:10:19 +0800 Subject: [PATCH 1424/1965] v3.11.3 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3fdde92a..ee7d9d4f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.11.2 + 3.11.3-SNAPSHOT jar jboot -- Gitee From 23c5e9975988a63a21f50d122df05c75e2c3fbc0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 12 Oct 2021 20:10:50 +0800 Subject: [PATCH 1425/1965] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=E5=9C=A8?= =?UTF-8?q?=20MySql=20=E4=B8=8B=E7=94=9F=E6=88=90=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E5=8F=AF=E8=83=BD=E5=9C=A8=20Oracle=20?= =?UTF-8?q?=E4=B8=8B=E6=97=A0=E6=B3=95=E4=BD=BF=E7=94=A8=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/db/model/JbootModel.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index db8e9da4..66a48d10 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -27,6 +27,8 @@ import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; import java.lang.reflect.Array; +import java.math.BigDecimal; +import java.math.BigInteger; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -918,4 +920,22 @@ public class JbootModel> extends Model { }, config, sql, paras); } + + ////// + + @Override + public BigInteger getBigInteger(String attr) { + Object data = _getAttrs().get(attr); + if (data instanceof BigInteger) { + return (BigInteger) data; + } + //oracle 下和 mysql 下不一致 + else if (data instanceof BigDecimal) { + return ((BigDecimal) data).toBigInteger(); + } else if (data instanceof Number) { + return BigInteger.valueOf(((Number) data).longValue()); + } + //可能会抛出异常,应该让其抛出 + return (BigInteger) data; + } } -- Gitee From f6ee858c6f48de47736575000aa0ef2fb4af6a62 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 12 Oct 2021 21:02:28 +0800 Subject: [PATCH 1426/1965] v3.11.3 snapshot --- src/main/java/io/jboot/db/model/JbootModel.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 66a48d10..eb86ee92 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -938,4 +938,17 @@ public class JbootModel> extends Model { //可能会抛出异常,应该让其抛出 return (BigInteger) data; } + + + @Override + public BigDecimal getBigDecimal(String attr) { + Object data = _getAttrs().get(attr); + if (data instanceof BigDecimal) { + return (BigDecimal) data; + } else if (data instanceof Number) { + return new BigDecimal(data.toString()); + } + //可能会抛出异常,应该让其抛出 + return (BigDecimal) data; + } } -- Gitee From 46efa10b0234e9ade3f17b05f29f8dbe724787ca Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 12 Oct 2021 21:10:30 +0800 Subject: [PATCH 1427/1965] v3.11.3 snapshot --- src/main/java/io/jboot/db/model/JbootModel.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index eb86ee92..d4b56f34 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -929,7 +929,9 @@ public class JbootModel> extends Model { if (data instanceof BigInteger) { return (BigInteger) data; } - //oracle 下和 mysql 下不一致 + //数据类型 id(19 number)在 Oracle Jdbc 下对应的是 BigDecimal, + //但是在 MySql 下对应的是 BigInteger,这会导致在 MySql 下生成的代码无法在 Oracle 数据库中使用 + //此处是为了解决这个问题的 else if (data instanceof BigDecimal) { return ((BigDecimal) data).toBigInteger(); } else if (data instanceof Number) { -- Gitee From 6920e2407d54c856c9744bf43fdbb6c0286b4bf1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 12 Oct 2021 21:12:23 +0800 Subject: [PATCH 1428/1965] v3.11.3 snapshot --- .../io/jboot/codegen/model/JbootBaseModelGenerator.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/io/jboot/codegen/model/JbootBaseModelGenerator.java b/src/main/java/io/jboot/codegen/model/JbootBaseModelGenerator.java index 46ae9707..e7493f92 100644 --- a/src/main/java/io/jboot/codegen/model/JbootBaseModelGenerator.java +++ b/src/main/java/io/jboot/codegen/model/JbootBaseModelGenerator.java @@ -31,6 +31,12 @@ public class JbootBaseModelGenerator extends BaseModelGenerator { this.metaBuilder = CodeGenHelpler.createMetaBuilder(); } + @Override + protected void initEngine() { + this.getterTypeMap.put("java.math.BigInteger", "getBigInteger"); + super.initEngine(); + } + public void generate() { super.generate(metaBuilder.build()); } -- Gitee From bb51b1c82b676ab76a4a0acbf98307e3e94145ce Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 17 Oct 2021 11:36:36 +0800 Subject: [PATCH 1429/1965] v3.11.3 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9ce29a61..2a34872e 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.11.2 + 3.11.3 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index a17f574b..5bfcf662 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.11.2 + 3.11.3 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index f1f22460..ef9aaa1c 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.11.2 + 3.11.3 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 36f5201f..33d33b73 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.11.2 + 3.11.3 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.11.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.11.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index bfe1623a..157a9b4d 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.11.2', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.11.3', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.11.2/jboot-3.11.2.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.11.3/jboot-3.11.3.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index b0596de2..db4aa53a 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.11.2"; + public static String VERSION = "3.11.3"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 3bbab8d52b479ed1f77a4365c24ad66d387d8a98 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 17 Oct 2021 11:36:40 +0800 Subject: [PATCH 1430/1965] v3.11.3 release (^.^)YYa!! --- changes.txt | 7 +++++++ pom.xml | 8 ++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/changes.txt b/changes.txt index 27d11fc8..79c51d9c 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.11.3: +新增:重写 Model 的 getBigInteger,防止转换异常 +新增:代码生成器 生成的 Model,如果字段是 BigInteger ,自动调用 getBigInteger 方法的功能 +优化:升级相关依赖到最新版本 + + + jboot v3.11.2: 新增:Columns.containsName() 方法 新增:SqlDebugger 新增对 Sql 输出到日志的实现方案,只要只有接口需要自己实现 diff --git a/pom.xml b/pom.xml index ee7d9d4f..1a96c079 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.11.3-SNAPSHOT + 3.11.3 jar jboot @@ -53,10 +53,10 @@ 1.7.32 2.57 1.2.78 - 30.1.1-jre + 31.0.1-jre 2.2.5 3.7.0 - 1.14.2 + 1.14.3 2.10.9.2 2.9.1 3.12.0 @@ -70,7 +70,7 @@ 3.13.2.Final 1.4.2 1.1.8 - 4.2.3 + 4.2.4 1.8 1.8 -- Gitee From bc9c5c528cb8cca5f62dda380ee822802139ef91 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 29 Oct 2021 10:07:04 +0800 Subject: [PATCH 1431/1965] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9ARecord=20?= =?UTF-8?q?=E5=9C=A8=20MySql=20=E4=B8=8B=E7=94=9F=E6=88=90=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=8C=E5=8F=AF=E8=83=BD=E5=9C=A8=20Oracle?= =?UTF-8?q?=20=E4=B8=8B=E6=97=A0=E6=B3=95=E4=BD=BF=E7=94=A8=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/ArpManager.java | 4 ++ .../java/io/jboot/db/dbpro/JbootDbPro.java | 23 +++--- .../java/io/jboot/db/record/JbootRecord.java | 57 +++++++++++++++ .../jboot/db/record/JbootRecordBuilder.java | 71 +++++++++++++++++++ 4 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 src/main/java/io/jboot/db/record/JbootRecord.java create mode 100644 src/main/java/io/jboot/db/record/JbootRecordBuilder.java diff --git a/src/main/java/io/jboot/db/ArpManager.java b/src/main/java/io/jboot/db/ArpManager.java index d2a1c639..bed441c7 100644 --- a/src/main/java/io/jboot/db/ArpManager.java +++ b/src/main/java/io/jboot/db/ArpManager.java @@ -24,6 +24,7 @@ import io.jboot.db.datasource.DataSourceConfig; import io.jboot.db.datasource.DataSourceConfigManager; import io.jboot.db.dbpro.JbootDbProFactory; import io.jboot.db.dialect.*; +import io.jboot.db.record.JbootRecordBuilder; import io.jboot.exception.JbootException; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.ArrayUtil; @@ -126,6 +127,9 @@ public class ArpManager { configSqlTemplate(activeRecordPlugin, config); configDialect(activeRecordPlugin, config); + //配置 Record 构建器 + activeRecordPlugin.getConfig().getDialect().setRecordBuilder(new JbootRecordBuilder()); + /** * 不需要添加映射的直接返回 * diff --git a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java index 1ce24f25..7e0aa9c3 100644 --- a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java +++ b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java @@ -75,22 +75,15 @@ public class JbootDbPro extends DbPro { //add sql debug support return SqlDebugger.run(() -> { - PreparedStatement pst; - if (dialect.isOracle()) { - pst = conn.prepareStatement(sql.toString(), pKeys); - } else { - pst = conn.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS); + try (PreparedStatement pst = + dialect.isOracle() ? + conn.prepareStatement(sql.toString(), pKeys) : + conn.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS)) { + dialect.fillStatement(pst, paras); + int result = pst.executeUpdate(); + dialect.getRecordGeneratedKey(pst, record, pKeys); + return result >= 1; } - dialect.fillStatement(pst, paras); - int result = pst.executeUpdate(); - dialect.getRecordGeneratedKey(pst, record, pKeys); - - if (pst != null) { - pst.close(); - } - - return result >= 1; - }, config, sql.toString(), paras.toArray()); } diff --git a/src/main/java/io/jboot/db/record/JbootRecord.java b/src/main/java/io/jboot/db/record/JbootRecord.java new file mode 100644 index 00000000..c4764d33 --- /dev/null +++ b/src/main/java/io/jboot/db/record/JbootRecord.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.db.record; + +import com.jfinal.plugin.activerecord.Record; + +import java.math.BigDecimal; +import java.math.BigInteger; + +public class JbootRecord extends Record { + + + ////// + @Override + public BigInteger getBigInteger(String attr) { + Object data = get(attr); + if (data instanceof BigInteger) { + return (BigInteger) data; + } + //数据类型 id(19 number)在 Oracle Jdbc 下对应的是 BigDecimal, + //但是在 MySql 下对应的是 BigInteger,这会导致在 MySql 下生成的代码无法在 Oracle 数据库中使用 + //此处是为了解决这个问题的 + else if (data instanceof BigDecimal) { + return ((BigDecimal) data).toBigInteger(); + } else if (data instanceof Number) { + return BigInteger.valueOf(((Number) data).longValue()); + } + //可能会抛出异常,应该让其抛出 + return (BigInteger) data; + } + + + @Override + public BigDecimal getBigDecimal(String attr) { + Object data = get(attr); + if (data instanceof BigDecimal) { + return (BigDecimal) data; + } else if (data instanceof Number) { + return new BigDecimal(data.toString()); + } + //可能会抛出异常,应该让其抛出 + return (BigDecimal) data; + } +} diff --git a/src/main/java/io/jboot/db/record/JbootRecordBuilder.java b/src/main/java/io/jboot/db/record/JbootRecordBuilder.java new file mode 100644 index 00000000..4d8ba24e --- /dev/null +++ b/src/main/java/io/jboot/db/record/JbootRecordBuilder.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.db.record; + +import com.jfinal.plugin.activerecord.*; + +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +public class JbootRecordBuilder extends RecordBuilder { + + @Override + public List build(Config config, ResultSet rs, Function func) throws SQLException { + List result = new ArrayList(); + ResultSetMetaData rsmd = rs.getMetaData(); + int columnCount = rsmd.getColumnCount(); + String[] labelNames = new String[columnCount + 1]; + int[] types = new int[columnCount + 1]; + buildLabelNamesAndTypes(rsmd, labelNames, types); + while (rs.next()) { + Record record = new JbootRecord(); + CPI.setColumnsMap(record,config.getContainerFactory().getColumnsMap()); + Map columns = record.getColumns(); + for (int i=1; i<=columnCount; i++) { + Object value; + if (types[i] < Types.BLOB) { + value = rs.getObject(i); + } else { + if (types[i] == Types.CLOB) { + value = ModelBuilder.me.handleClob(rs.getClob(i)); + } else if (types[i] == Types.NCLOB) { + value = ModelBuilder.me.handleClob(rs.getNClob(i)); + } else if (types[i] == Types.BLOB) { + value = ModelBuilder.me.handleBlob(rs.getBlob(i)); + } else { + value = rs.getObject(i); + } + } + columns.put(labelNames[i], value); + } + + if (func == null) { + result.add(record); + } else { + if ( ! func.apply(record) ) { + break ; + } + } + } + return result; + } +} -- Gitee From 7c21edeace1a438c4d436373472261f0ba13e153 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 29 Oct 2021 10:09:16 +0800 Subject: [PATCH 1432/1965] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9AAttachment?= =?UTF-8?q?Manager=20=E6=96=B0=E5=A2=9E=E5=88=A0=E9=99=A4=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=20MQ=20=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../attachment/AttachmentDeleteAction.java | 51 ++++++++ .../web/attachment/AttachmentManager.java | 116 ++++++++++++++---- .../attachment/LocalAttachmentContainer.java | 27 +++- 3 files changed, 167 insertions(+), 27 deletions(-) create mode 100644 src/main/java/io/jboot/web/attachment/AttachmentDeleteAction.java diff --git a/src/main/java/io/jboot/web/attachment/AttachmentDeleteAction.java b/src/main/java/io/jboot/web/attachment/AttachmentDeleteAction.java new file mode 100644 index 00000000..641a1b92 --- /dev/null +++ b/src/main/java/io/jboot/web/attachment/AttachmentDeleteAction.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt + *

        + * 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 io.jboot.web.attachment; + +import java.io.Serializable; + +/** + * @author michael yang (fuhai999@gmail.com) + * @Date: 2020/3/1 + */ +public class AttachmentDeleteAction implements Serializable { + + private static final long serialVersionUID = 1L; + + private String id; + private String path; + + public AttachmentDeleteAction(String id, String path) { + this.id = id; + this.path = path; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } +} diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index 9daa6ead..2625ecb3 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -20,6 +20,7 @@ import com.jfinal.log.Log; import com.jfinal.render.IRenderFactory; import com.jfinal.render.Render; import com.jfinal.render.RenderManager; +import io.jboot.components.mq.Jbootmq; import io.jboot.utils.StrUtil; import javax.servlet.http.HttpServletRequest; @@ -48,36 +49,98 @@ public class AttachmentManager { if (manager == null) { synchronized (AttachmentManager.class) { if (manager == null) { - manager = new AttachmentManager(); - managers.put(name,manager); + manager = new AttachmentManager(name); + managers.put(name, manager); } } } return manager; } - private AttachmentManager() { + private AttachmentManager(String name) { + this.name = name; } /** * 默认的 附件容器 */ - private LocalAttachmentContainer defaultContainer = new LocalAttachmentContainer(); + private LocalAttachmentContainer localContainer = new LocalAttachmentContainer(); /** * 其他附件容器 */ private List containers = new CopyOnWriteArrayList<>(); + /** + * 自定义文件渲染器 + */ private IRenderFactory renderFactory = RenderManager.me().getRenderFactory(); + /** + * manager 的名称 + */ + private final String name; + + /** + * 文件同步的 MQ + */ + private Jbootmq deleteMq; + + /** + * MQ 发送消息的 Channel + */ + private String deleteMqChannel = "attachmentDelete"; + + /** + * 节点消息ID + */ + private String deleteMqActionId = StrUtil.uuid(); + + + public String getName() { + return name; + } + + public Jbootmq getDeleteMq() { + return deleteMq; + } + + public void setDeleteMq(Jbootmq deleteMq) { + this.deleteMq = deleteMq; + this.deleteMq.addMessageListener(localContainer, deleteMqChannel); + } + + public String getDeleteMqChannel() { + return deleteMqChannel; + } + + public void setDeleteMqChannel(String deleteMqChannel) { + this.deleteMqChannel = deleteMqChannel; + } + + public String getDeleteMqActionId() { + return deleteMqActionId; + } + + public void setDeleteMqActionId(String deleteMqActionId) { + this.deleteMqActionId = deleteMqActionId; + } - public LocalAttachmentContainer getDefaultContainer() { - return defaultContainer; + public IRenderFactory getRenderFactory() { + return renderFactory; } - public void setDefaultContainer(LocalAttachmentContainer defaultContainer) { - this.defaultContainer = defaultContainer; + public void setRenderFactory(IRenderFactory renderFactory) { + this.renderFactory = renderFactory; + } + + + public LocalAttachmentContainer getLocalContainer() { + return localContainer; + } + + public void setLocalContainer(LocalAttachmentContainer localContainer) { + this.localContainer = localContainer; } public void addContainer(AttachmentContainer container) { @@ -102,12 +165,12 @@ public class AttachmentManager { */ public String saveFile(File file) { //优先从 默认的 container 去保存文件 - String relativePath = defaultContainer.saveFile(file); - File defaultContainerFile = defaultContainer.getFile(relativePath); + String relativePath = localContainer.saveFile(file); + File defaultContainerFile = localContainer.getFile(relativePath); for (AttachmentContainer container : containers) { try { - if (container != defaultContainer) { + if (container != localContainer) { container.saveFile(defaultContainerFile); } } catch (Exception ex) { @@ -126,12 +189,12 @@ public class AttachmentManager { */ public String saveFile(File file, String toRelativePath) { //优先从 默认的 container 去保存文件 - String relativePath = defaultContainer.saveFile(file, toRelativePath); - File defaultContainerFile = defaultContainer.getFile(relativePath); + String relativePath = localContainer.saveFile(file, toRelativePath); + File defaultContainerFile = localContainer.getFile(relativePath); for (AttachmentContainer container : containers) { try { - if (container != defaultContainer) { + if (container != localContainer) { container.saveFile(defaultContainerFile, toRelativePath); } } catch (Exception ex) { @@ -149,12 +212,12 @@ public class AttachmentManager { */ public String saveFile(InputStream inputStream, String toRelativePath) { //优先从 默认的 container 去保存文件 - String relativePath = defaultContainer.saveFile(inputStream, toRelativePath); - File defaultContainerFile = defaultContainer.getFile(relativePath); + String relativePath = localContainer.saveFile(inputStream, toRelativePath); + File defaultContainerFile = localContainer.getFile(relativePath); for (AttachmentContainer container : containers) { try { - if (container != defaultContainer) { + if (container != localContainer) { container.saveFile(defaultContainerFile, toRelativePath); } } catch (Exception ex) { @@ -179,7 +242,14 @@ public class AttachmentManager { LOG.error("Delete file error in container :" + container, ex); } } - return defaultContainer.deleteFile(relativePath); + boolean success = localContainer.deleteFile(relativePath); + + //删除,同步到其他 Server + if (success && this.deleteMq != null) { + deleteMq.publish(new AttachmentDeleteAction(deleteMqActionId, relativePath), deleteMqChannel); + } + + return success; } /** @@ -191,7 +261,7 @@ public class AttachmentManager { public File getFile(String relativePath) { //优先从 默认的 container 去获取 - File file = defaultContainer.getFile(relativePath); + File file = localContainer.getFile(relativePath); if (file != null && file.exists()) { return file; } @@ -216,7 +286,7 @@ public class AttachmentManager { * @return */ public String getRelativePath(File file) { - String relativePath = defaultContainer.getRelativePath(file); + String relativePath = localContainer.getRelativePath(file); return relativePath != null ? relativePath.replace("\\", "/") : null; } @@ -229,7 +299,7 @@ public class AttachmentManager { * @return */ public File createNewFile(String suffix) { - return getDefaultContainer().creatNewFile(suffix); + return getLocalContainer().creatNewFile(suffix); } @@ -242,8 +312,8 @@ public class AttachmentManager { * @return true 渲染成功,false 不进行渲染 */ public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { - if (StrUtil.isNotBlank(defaultContainer.getTargetPrefix()) - && target.startsWith(defaultContainer.getTargetPrefix()) + if (StrUtil.isNotBlank(localContainer.getTargetPrefix()) + && target.startsWith(localContainer.getTargetPrefix()) && target.lastIndexOf('.') != -1) { Render render = getFileRender(getFile(target)); render.setContext(request, response).render(); diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java index 71126934..65a40c4d 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -17,6 +17,7 @@ package io.jboot.web.attachment; import com.jfinal.ext.kit.DateKit; import com.jfinal.kit.LogKit; +import io.jboot.components.mq.JbootmqMessageListener; import io.jboot.utils.FileUtil; import io.jboot.utils.StrUtil; @@ -29,7 +30,7 @@ import java.util.Date; /** * @author michael yang (fuhai999@gmail.com) */ -public class LocalAttachmentContainer implements AttachmentContainer { +public class LocalAttachmentContainer implements AttachmentContainer, JbootmqMessageListener { private String rootPath; private String targetPrefix; @@ -40,6 +41,7 @@ public class LocalAttachmentContainer implements AttachmentContainer { this.targetPrefix = config.getTargetPrefix(); } + /** * @param rootPath * @param targetPrefix 不能以 / 开头 @@ -76,7 +78,7 @@ public class LocalAttachmentContainer implements AttachmentContainer { try { //相同的文件,不需要做任何处理 - if (toFile.equals(file)){ + if (toFile.equals(file)) { return toRelativePath; } @@ -101,7 +103,7 @@ public class LocalAttachmentContainer implements AttachmentContainer { File toFile = new File(getRootPath(), toRelativePath); - if (toFile.exists()){ + if (toFile.exists()) { toFile.delete(); } @@ -129,7 +131,7 @@ public class LocalAttachmentContainer implements AttachmentContainer { @Override public boolean deleteFile(String relativePath) { File file = getFile(relativePath); - return file != null && file.delete(); + return file.exists() && file.delete(); } @@ -176,4 +178,21 @@ public class LocalAttachmentContainer implements AttachmentContainer { public void setTargetPrefix(String targetPrefix) { this.targetPrefix = targetPrefix; } + + + /** + * 接收 mq 的文件删除消息 + * + * @param channel of topic + * @param message topic message + */ + @Override + public void onMessage(String channel, Object message) { + if (message instanceof AttachmentDeleteAction && + //不同是相同的节点发送消息 + !AttachmentManager.me().getDeleteMqActionId().equals(((AttachmentDeleteAction) message).getId())) { + String path = ((AttachmentDeleteAction) message).getPath(); + deleteFile(path); + } + } } -- Gitee From 5c981a2dc931b066d55030acf8534f7f19fccf62 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 29 Oct 2021 10:09:53 +0800 Subject: [PATCH 1433/1965] v3.11.4 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1a96c079..754e3928 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.11.3 + 3.11.4-snapshot jar jboot -- Gitee From 09d48f85ca65e7f0cc02900152aefc4d9dbf907c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 31 Oct 2021 21:53:44 +0800 Subject: [PATCH 1434/1965] remove codegen.PathKit --- src/main/java/io/jboot/codegen/CodeGenHelpler.java | 5 +++++ src/main/java/io/jboot/codegen/GenTester.java | 4 ++-- src/main/java/io/jboot/codegen/PathKit.java | 10 ---------- .../java/io/jboot/codegen/model/ModeGenTester.java | 6 +++--- .../codegen/service/JbootServiceImplGenerator.java | 3 +-- .../service/JbootServiceInterfaceGenerator.java | 3 +-- 6 files changed, 12 insertions(+), 19 deletions(-) delete mode 100644 src/main/java/io/jboot/codegen/PathKit.java diff --git a/src/main/java/io/jboot/codegen/CodeGenHelpler.java b/src/main/java/io/jboot/codegen/CodeGenHelpler.java index 8ce75e4c..cc5d934a 100644 --- a/src/main/java/io/jboot/codegen/CodeGenHelpler.java +++ b/src/main/java/io/jboot/codegen/CodeGenHelpler.java @@ -35,6 +35,11 @@ import java.util.Set; */ public class CodeGenHelpler { + public static String getUserDir() { + return System.getProperty("user.dir"); + } + + /** * 获取数据源 diff --git a/src/main/java/io/jboot/codegen/GenTester.java b/src/main/java/io/jboot/codegen/GenTester.java index 245faf3f..bcc2d00f 100644 --- a/src/main/java/io/jboot/codegen/GenTester.java +++ b/src/main/java/io/jboot/codegen/GenTester.java @@ -35,8 +35,8 @@ public class GenTester { String modelPackage = "io.jboot.codegen.test.model"; String baseModelPackage = modelPackage + ".base"; - String modelDir = PathKit.getUserDir() + "/src/main/java/" + modelPackage.replace(".", "/"); - String baseModelDir = PathKit.getUserDir() + "/src/main/java/" + baseModelPackage.replace(".", "/"); + String modelDir = CodeGenHelpler.getUserDir() + "/src/main/java/" + modelPackage.replace(".", "/"); + String baseModelDir = CodeGenHelpler.getUserDir() + "/src/main/java/" + baseModelPackage.replace(".", "/"); System.out.println("start generate..."); System.out.println("generate dir:" + modelDir); diff --git a/src/main/java/io/jboot/codegen/PathKit.java b/src/main/java/io/jboot/codegen/PathKit.java deleted file mode 100644 index 3e7cb8b6..00000000 --- a/src/main/java/io/jboot/codegen/PathKit.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.jboot.codegen; - -public class PathKit { - - - public static String getUserDir() { - return System.getProperty("user.dir"); - } - -} diff --git a/src/main/java/io/jboot/codegen/model/ModeGenTester.java b/src/main/java/io/jboot/codegen/model/ModeGenTester.java index 4ac7cda8..deee3829 100644 --- a/src/main/java/io/jboot/codegen/model/ModeGenTester.java +++ b/src/main/java/io/jboot/codegen/model/ModeGenTester.java @@ -17,7 +17,7 @@ package io.jboot.codegen.model; import io.jboot.app.JbootApplication; -import io.jboot.codegen.PathKit; +import io.jboot.codegen.CodeGenHelpler; public class ModeGenTester { @@ -29,8 +29,8 @@ public class ModeGenTester { String modelPackage = "io.jboot.codegen.test"; String baseModelPackage = modelPackage + ".base"; - String modelDir = PathKit.getUserDir() + "/src/main/java/" + modelPackage.replace(".", "/"); - String baseModelDir = PathKit.getUserDir() + "/src/main/java/" + baseModelPackage.replace(".", "/"); + String modelDir = CodeGenHelpler.getUserDir() + "/src/main/java/" + modelPackage.replace(".", "/"); + String baseModelDir = CodeGenHelpler.getUserDir() + "/src/main/java/" + baseModelPackage.replace(".", "/"); System.out.println("start generate..."); System.out.println("generate dir:" + modelDir); diff --git a/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java b/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java index 612a8231..76b579e2 100644 --- a/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java +++ b/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java @@ -24,7 +24,6 @@ import com.jfinal.plugin.activerecord.generator.TableMeta; import com.jfinal.template.Engine; import com.jfinal.template.source.ClassPathSourceFactory; import io.jboot.codegen.CodeGenHelpler; -import io.jboot.codegen.PathKit; import java.io.File; import java.io.FileOutputStream; @@ -68,7 +67,7 @@ public class JbootServiceImplGenerator { } private String buildOutPutDir() { - return PathKit.getUserDir() + "/src/main/java/" + (basePackage + "." + implName).replace(".", "/"); + return CodeGenHelpler.getUserDir() + "/src/main/java/" + (basePackage + "." + implName).replace(".", "/"); } diff --git a/src/main/java/io/jboot/codegen/service/JbootServiceInterfaceGenerator.java b/src/main/java/io/jboot/codegen/service/JbootServiceInterfaceGenerator.java index 5343d9e4..6864ebe8 100644 --- a/src/main/java/io/jboot/codegen/service/JbootServiceInterfaceGenerator.java +++ b/src/main/java/io/jboot/codegen/service/JbootServiceInterfaceGenerator.java @@ -24,7 +24,6 @@ import com.jfinal.plugin.activerecord.generator.TableMeta; import com.jfinal.template.Engine; import com.jfinal.template.source.ClassPathSourceFactory; import io.jboot.codegen.CodeGenHelpler; -import io.jboot.codegen.PathKit; import java.io.File; import java.io.FileOutputStream; @@ -43,7 +42,7 @@ public class JbootServiceInterfaceGenerator extends BaseModelGenerator { public JbootServiceInterfaceGenerator(String basePackage, String modelPacket) { - super(basePackage, PathKit.getUserDir() + "/src/main/java/" + basePackage.replace(".", "/")); + super(basePackage, CodeGenHelpler.getUserDir() + "/src/main/java/" + basePackage.replace(".", "/")); this.modelPacket = modelPacket; -- Gitee From 0530a99650387825163733cf57c74c60efd0b80a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 11 Nov 2021 16:04:07 +0800 Subject: [PATCH 1435/1965] =?UTF-8?q?MockMvc=20=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E6=96=B0=E5=A2=9E=20holdCookie=20=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=EF=BC=8C=E7=94=A8=E4=BA=8E=E5=9C=A8=E4=B8=8D=E5=90=8C=E7=9A=84?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=E9=87=8C=EF=BC=8C=E4=BF=9D?= =?UTF-8?q?=E6=8C=81=20cookie=20=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/test/MockMvc.java | 50 +++++++++++++++++-- .../test/web/MockHttpServletResponse.java | 5 ++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/test/MockMvc.java b/src/main/java/io/jboot/test/MockMvc.java index 8de23e01..3d127e3e 100644 --- a/src/main/java/io/jboot/test/MockMvc.java +++ b/src/main/java/io/jboot/test/MockMvc.java @@ -20,14 +20,40 @@ import io.jboot.test.web.MockHttpServletResponse; import io.jboot.test.web.MockServletInputStream; import io.jboot.utils.StrUtil; -import java.util.HashMap; -import java.util.Map; +import javax.servlet.http.Cookie; +import java.util.*; import java.util.function.Consumer; public class MockMvc { - private Consumer requestStartListener; - private Consumer requestFinishedListener; + protected boolean holdCookiesEnable = false; + protected Set holdCookies = new HashSet<>(); + + protected Consumer requestStartListener; + protected Consumer requestFinishedListener; + + public MockMvc() { + } + + public MockMvc(boolean holdCookiesEnable) { + this.holdCookiesEnable = holdCookiesEnable; + } + + public boolean isHoldCookiesEnable() { + return holdCookiesEnable; + } + + public void setHoldCookiesEnable(boolean holdCookiesEnable) { + this.holdCookiesEnable = holdCookiesEnable; + } + + public Set getHoldCookies() { + return holdCookies; + } + + public void setHoldCookies(Set holdCookies) { + this.holdCookies = holdCookies; + } public Consumer getRequestStartListener() { return requestStartListener; @@ -141,16 +167,32 @@ public class MockMvc { private MockMvcResult doStartMockRequest(MockHttpServletRequest request) { MockHttpServletResponse response = new MockHttpServletResponse(); try { + //开启 cookie 保持, 用户未设置自己的 cookie, 上次的 cookie 有值 + if (isHoldCookiesEnable() && request.getCookies().length == 0 && holdCookies.size() > 0) { + request.setCookies(holdCookies); + } + doSendRequest(request, response); } finally { if (requestFinishedListener != null) { requestFinishedListener.accept(response); } + + if (isHoldCookiesEnable() && response.getCookies().size() > 0) { + response.getCookies().forEach(this::doSetCookie); + } } return new MockMvcResult(response); } + public void doSetCookie(Cookie newCookie) { + holdCookies.removeIf(cookie -> Objects.equals(cookie.getName(), newCookie.getName())); + if (newCookie.getValue() != null) { + holdCookies.add(newCookie); + } + } + public void doSendRequest(MockHttpServletRequest request, MockHttpServletResponse response) { MockApp.mockRequest(request, response); diff --git a/src/main/java/io/jboot/test/web/MockHttpServletResponse.java b/src/main/java/io/jboot/test/web/MockHttpServletResponse.java index d14c8503..d6fe548e 100644 --- a/src/main/java/io/jboot/test/web/MockHttpServletResponse.java +++ b/src/main/java/io/jboot/test/web/MockHttpServletResponse.java @@ -52,6 +52,11 @@ public class MockHttpServletResponse extends HttpServletResponseWrapper { cookies.add(cookie); } + public Set getCookies() { + return cookies; + } + + @Override public void addDateHeader(String key, long value) { headers.put(key, "" + value); -- Gitee From d1b970e709b8eed6e9c55904a465ae221d4e1f2a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 11 Nov 2021 17:16:12 +0800 Subject: [PATCH 1436/1965] =?UTF-8?q?=E6=81=A2=E5=A4=8D=20attacmentManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../attachment/AttachmentDeleteAction.java | 51 ---------- .../web/attachment/AttachmentManager.java | 92 +++++-------------- .../attachment/LocalAttachmentContainer.java | 18 +--- 3 files changed, 24 insertions(+), 137 deletions(-) delete mode 100644 src/main/java/io/jboot/web/attachment/AttachmentDeleteAction.java diff --git a/src/main/java/io/jboot/web/attachment/AttachmentDeleteAction.java b/src/main/java/io/jboot/web/attachment/AttachmentDeleteAction.java deleted file mode 100644 index 641a1b92..00000000 --- a/src/main/java/io/jboot/web/attachment/AttachmentDeleteAction.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) 2016-2020, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.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.gnu.org/licenses/lgpl-3.0.txt - *

        - * 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 io.jboot.web.attachment; - -import java.io.Serializable; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/1 - */ -public class AttachmentDeleteAction implements Serializable { - - private static final long serialVersionUID = 1L; - - private String id; - private String path; - - public AttachmentDeleteAction(String id, String path) { - this.id = id; - this.path = path; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } -} diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index 2625ecb3..a8c4bfed 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -20,14 +20,15 @@ import com.jfinal.log.Log; import com.jfinal.render.IRenderFactory; import com.jfinal.render.Render; import com.jfinal.render.RenderManager; -import io.jboot.components.mq.Jbootmq; import io.jboot.utils.StrUtil; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.InputStream; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; /** @@ -64,7 +65,7 @@ public class AttachmentManager { /** * 默认的 附件容器 */ - private LocalAttachmentContainer localContainer = new LocalAttachmentContainer(); + private LocalAttachmentContainer defaultContainer = new LocalAttachmentContainer(); /** * 其他附件容器 @@ -81,51 +82,11 @@ public class AttachmentManager { */ private final String name; - /** - * 文件同步的 MQ - */ - private Jbootmq deleteMq; - - /** - * MQ 发送消息的 Channel - */ - private String deleteMqChannel = "attachmentDelete"; - - /** - * 节点消息ID - */ - private String deleteMqActionId = StrUtil.uuid(); - public String getName() { return name; } - public Jbootmq getDeleteMq() { - return deleteMq; - } - - public void setDeleteMq(Jbootmq deleteMq) { - this.deleteMq = deleteMq; - this.deleteMq.addMessageListener(localContainer, deleteMqChannel); - } - - public String getDeleteMqChannel() { - return deleteMqChannel; - } - - public void setDeleteMqChannel(String deleteMqChannel) { - this.deleteMqChannel = deleteMqChannel; - } - - public String getDeleteMqActionId() { - return deleteMqActionId; - } - - public void setDeleteMqActionId(String deleteMqActionId) { - this.deleteMqActionId = deleteMqActionId; - } - public IRenderFactory getRenderFactory() { return renderFactory; } @@ -135,12 +96,12 @@ public class AttachmentManager { } - public LocalAttachmentContainer getLocalContainer() { - return localContainer; + public LocalAttachmentContainer getDefaultContainer() { + return defaultContainer; } - public void setLocalContainer(LocalAttachmentContainer localContainer) { - this.localContainer = localContainer; + public void setDefaultContainer(LocalAttachmentContainer defaultContainer) { + this.defaultContainer = defaultContainer; } public void addContainer(AttachmentContainer container) { @@ -165,12 +126,12 @@ public class AttachmentManager { */ public String saveFile(File file) { //优先从 默认的 container 去保存文件 - String relativePath = localContainer.saveFile(file); - File defaultContainerFile = localContainer.getFile(relativePath); + String relativePath = defaultContainer.saveFile(file); + File defaultContainerFile = defaultContainer.getFile(relativePath); for (AttachmentContainer container : containers) { try { - if (container != localContainer) { + if (container != defaultContainer) { container.saveFile(defaultContainerFile); } } catch (Exception ex) { @@ -189,12 +150,12 @@ public class AttachmentManager { */ public String saveFile(File file, String toRelativePath) { //优先从 默认的 container 去保存文件 - String relativePath = localContainer.saveFile(file, toRelativePath); - File defaultContainerFile = localContainer.getFile(relativePath); + String relativePath = defaultContainer.saveFile(file, toRelativePath); + File defaultContainerFile = defaultContainer.getFile(relativePath); for (AttachmentContainer container : containers) { try { - if (container != localContainer) { + if (container != defaultContainer) { container.saveFile(defaultContainerFile, toRelativePath); } } catch (Exception ex) { @@ -212,12 +173,12 @@ public class AttachmentManager { */ public String saveFile(InputStream inputStream, String toRelativePath) { //优先从 默认的 container 去保存文件 - String relativePath = localContainer.saveFile(inputStream, toRelativePath); - File defaultContainerFile = localContainer.getFile(relativePath); + String relativePath = defaultContainer.saveFile(inputStream, toRelativePath); + File defaultContainerFile = defaultContainer.getFile(relativePath); for (AttachmentContainer container : containers) { try { - if (container != localContainer) { + if (container != defaultContainer) { container.saveFile(defaultContainerFile, toRelativePath); } } catch (Exception ex) { @@ -242,14 +203,7 @@ public class AttachmentManager { LOG.error("Delete file error in container :" + container, ex); } } - boolean success = localContainer.deleteFile(relativePath); - - //删除,同步到其他 Server - if (success && this.deleteMq != null) { - deleteMq.publish(new AttachmentDeleteAction(deleteMqActionId, relativePath), deleteMqChannel); - } - - return success; + return defaultContainer.deleteFile(relativePath); } /** @@ -261,7 +215,7 @@ public class AttachmentManager { public File getFile(String relativePath) { //优先从 默认的 container 去获取 - File file = localContainer.getFile(relativePath); + File file = defaultContainer.getFile(relativePath); if (file != null && file.exists()) { return file; } @@ -286,7 +240,7 @@ public class AttachmentManager { * @return */ public String getRelativePath(File file) { - String relativePath = localContainer.getRelativePath(file); + String relativePath = defaultContainer.getRelativePath(file); return relativePath != null ? relativePath.replace("\\", "/") : null; } @@ -299,7 +253,7 @@ public class AttachmentManager { * @return */ public File createNewFile(String suffix) { - return getLocalContainer().creatNewFile(suffix); + return getDefaultContainer().creatNewFile(suffix); } @@ -312,8 +266,8 @@ public class AttachmentManager { * @return true 渲染成功,false 不进行渲染 */ public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { - if (StrUtil.isNotBlank(localContainer.getTargetPrefix()) - && target.startsWith(localContainer.getTargetPrefix()) + if (StrUtil.isNotBlank(defaultContainer.getTargetPrefix()) + && target.startsWith(defaultContainer.getTargetPrefix()) && target.lastIndexOf('.') != -1) { Render render = getFileRender(getFile(target)); render.setContext(request, response).render(); diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java index 65a40c4d..56ecd08b 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -17,7 +17,6 @@ package io.jboot.web.attachment; import com.jfinal.ext.kit.DateKit; import com.jfinal.kit.LogKit; -import io.jboot.components.mq.JbootmqMessageListener; import io.jboot.utils.FileUtil; import io.jboot.utils.StrUtil; @@ -30,7 +29,7 @@ import java.util.Date; /** * @author michael yang (fuhai999@gmail.com) */ -public class LocalAttachmentContainer implements AttachmentContainer, JbootmqMessageListener { +public class LocalAttachmentContainer implements AttachmentContainer { private String rootPath; private String targetPrefix; @@ -180,19 +179,4 @@ public class LocalAttachmentContainer implements AttachmentContainer, JbootmqMes } - /** - * 接收 mq 的文件删除消息 - * - * @param channel of topic - * @param message topic message - */ - @Override - public void onMessage(String channel, Object message) { - if (message instanceof AttachmentDeleteAction && - //不同是相同的节点发送消息 - !AttachmentManager.me().getDeleteMqActionId().equals(((AttachmentDeleteAction) message).getId())) { - String path = ((AttachmentDeleteAction) message).getPath(); - deleteFile(path); - } - } } -- Gitee From 3cd18ab33371861e37d969c62a270f3851367bcf Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 14 Nov 2021 14:14:06 +0800 Subject: [PATCH 1437/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20DAO.distinct()?= =?UTF-8?q?=20=E6=96=B9=E6=B3=95=EF=BC=8C=E7=94=A8=E4=BA=8E=E5=AF=B9?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E8=BF=9B=E8=A1=8C=E5=8E=BB=E9=87=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/db/dialect/JbootAnsiSqlDialect.java | 11 +++- .../db/dialect/JbootClickHouseDialect.java | 24 +++++--- .../io/jboot/db/dialect/JbootDialect.java | 2 +- .../jboot/db/dialect/JbootMysqlDialect.java | 10 +++- .../jboot/db/dialect/JbootOracleDialect.java | 10 +++- .../db/dialect/JbootPostgreSqlDialect.java | 9 ++- .../db/dialect/JbootSqlServerDialect.java | 9 ++- .../jboot/db/dialect/JbootSqlite3Dialect.java | 9 ++- src/main/java/io/jboot/db/model/CPI.java | 23 ++++++++ .../java/io/jboot/db/model/JbootModel.java | 57 ++++++++++++++++++- .../io/jboot/db/model/JbootModelExts.java | 22 +++++++ .../java/io/jboot/db/model/SqlBuilder.java | 25 +++++++- 12 files changed, 183 insertions(+), 28 deletions(-) create mode 100644 src/main/java/io/jboot/db/model/CPI.java create mode 100644 src/main/java/io/jboot/db/model/JbootModelExts.java diff --git a/src/main/java/io/jboot/db/dialect/JbootAnsiSqlDialect.java b/src/main/java/io/jboot/db/dialect/JbootAnsiSqlDialect.java index 97c1d978..d40ce3ad 100644 --- a/src/main/java/io/jboot/db/dialect/JbootAnsiSqlDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootAnsiSqlDialect.java @@ -17,8 +17,8 @@ package io.jboot.db.dialect; import com.jfinal.plugin.activerecord.dialect.AnsiSqlDialect; import io.jboot.db.model.Column; -import io.jboot.db.model.SqlBuilder; import io.jboot.db.model.Join; +import io.jboot.db.model.SqlBuilder; import io.jboot.exception.JbootException; import java.util.List; @@ -39,8 +39,8 @@ public class JbootAnsiSqlDialect extends AnsiSqlDialect implements JbootDialect } @Override - public String forFindCountByColumns(String alias, List joins, String table, List columns) { - return SqlBuilder.forFindCountByColumns(alias, joins, table, columns, ' '); + public String forFindCountByColumns(String alias, List joins, String table, String loadColumns, List columns) { + return SqlBuilder.forFindCountByColumns(alias, joins, table, loadColumns, columns, ' '); } @Override @@ -60,5 +60,10 @@ public class JbootAnsiSqlDialect extends AnsiSqlDialect implements JbootDialect return SqlBuilder.forPaginateFrom(alias, joins, table, columns, orderBy, ' '); } + @Override + public String forPaginateTotalRow(String select, String sqlExceptSelect, Object ext) { + String distinctSql = SqlBuilder.forPaginateDistinctTotalRow(select, sqlExceptSelect, ext); + return distinctSql != null ? distinctSql : super.forPaginateTotalRow(select, sqlExceptSelect, ext); + } } diff --git a/src/main/java/io/jboot/db/dialect/JbootClickHouseDialect.java b/src/main/java/io/jboot/db/dialect/JbootClickHouseDialect.java index 5c68da2b..41ac4228 100644 --- a/src/main/java/io/jboot/db/dialect/JbootClickHouseDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootClickHouseDialect.java @@ -15,14 +15,12 @@ */ package io.jboot.db.dialect; -import com.jfinal.plugin.activerecord.CPI; import com.jfinal.plugin.activerecord.Model; import com.jfinal.plugin.activerecord.Record; import com.jfinal.plugin.activerecord.Table; import com.jfinal.plugin.activerecord.dialect.AnsiSqlDialect; -import io.jboot.db.model.Column; -import io.jboot.db.model.Join; -import io.jboot.db.model.SqlBuilder; +import io.jboot.db.model.*; +import io.jboot.utils.StrUtil; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -135,8 +133,8 @@ public class JbootClickHouseDialect extends AnsiSqlDialect implements JbootDiale @Override - public String forFindCountByColumns(String alias, List joins, String table, List columns) { - return SqlBuilder.forFindCountByColumns(alias, joins, table, columns, ' '); + public String forFindCountByColumns(String alias, List joins, String table, String loadColumns, List columns) { + return SqlBuilder.forFindCountByColumns(alias, joins, table, loadColumns, columns, ' '); } @@ -169,12 +167,20 @@ public class JbootClickHouseDialect extends AnsiSqlDialect implements JbootDiale @Override public String forPaginateTotalRow(String select, String sqlExceptSelect, Object ext) { if (ext instanceof Model) { - String[] primaryKeys = CPI.getTable((Model) ext).getPrimaryKey(); - if (primaryKeys != null && primaryKeys.length == 1) { - return "select count(" + primaryKeys[0] + ") " + replaceOrderBy(sqlExceptSelect); + if (io.jboot.db.model.CPI.hasAnyJoinEffective((JbootModel) ext)) { + String distinct = ((JbootModel) ext).get(JbootModelExts.DISTINCT); + if (StrUtil.isNotBlank(distinct)) { + return "SELECT count(DISTINCT " + distinct + ") " + replaceOrderBy(sqlExceptSelect); + } + } else { + String[] primaryKeys = com.jfinal.plugin.activerecord.CPI.getTable((Model) ext).getPrimaryKey(); + if (primaryKeys != null && primaryKeys.length == 1) { + return "select count(" + primaryKeys[0] + ") " + replaceOrderBy(sqlExceptSelect); + } } } + //return "select count(*) " + replaceOrderBy(sqlExceptSelect); return super.forPaginateTotalRow(select, sqlExceptSelect, ext); } diff --git a/src/main/java/io/jboot/db/dialect/JbootDialect.java b/src/main/java/io/jboot/db/dialect/JbootDialect.java index ca9035e1..771c66e7 100644 --- a/src/main/java/io/jboot/db/dialect/JbootDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootDialect.java @@ -27,7 +27,7 @@ public interface JbootDialect { public String forFindByColumns(String alias, List joins, String table, String loadColumns, List columns, String orderBy, Object limit); - public String forFindCountByColumns(String alias, List joins, String table, List columns); + public String forFindCountByColumns(String alias, List joins, String table, String loadColumns, List columns); public String forDeleteByColumns(String alias, List joins, String table, List columns); diff --git a/src/main/java/io/jboot/db/dialect/JbootMysqlDialect.java b/src/main/java/io/jboot/db/dialect/JbootMysqlDialect.java index 0353b701..bd783114 100644 --- a/src/main/java/io/jboot/db/dialect/JbootMysqlDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootMysqlDialect.java @@ -37,8 +37,8 @@ public class JbootMysqlDialect extends MysqlDialect implements JbootDialect { } @Override - public String forFindCountByColumns(String alias, List joins, String table, List columns) { - return SqlBuilder.forFindCountByColumns(alias, joins, table, columns, '`'); + public String forFindCountByColumns(String alias, List joins, String table, String loadColumns, List columns) { + return SqlBuilder.forFindCountByColumns(alias, joins, table, loadColumns, columns, '`'); } @Override @@ -58,4 +58,10 @@ public class JbootMysqlDialect extends MysqlDialect implements JbootDialect { return SqlBuilder.forPaginateFrom(alias, joins, table, columns, orderBy, '`'); } + @Override + public String forPaginateTotalRow(String select, String sqlExceptSelect, Object ext) { + String distinctSql = SqlBuilder.forPaginateDistinctTotalRow(select, sqlExceptSelect, ext); + return distinctSql != null ? distinctSql : super.forPaginateTotalRow(select, sqlExceptSelect, ext); + } + } diff --git a/src/main/java/io/jboot/db/dialect/JbootOracleDialect.java b/src/main/java/io/jboot/db/dialect/JbootOracleDialect.java index 74a9da4f..def16e21 100644 --- a/src/main/java/io/jboot/db/dialect/JbootOracleDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootOracleDialect.java @@ -59,8 +59,8 @@ public class JbootOracleDialect extends OracleDialect implements JbootDialect { } @Override - public String forFindCountByColumns(String alias, List joins, String table, List columns) { - return SqlBuilder.forFindCountByColumns(alias, joins, table, columns, ' '); + public String forFindCountByColumns(String alias, List joins, String table, String loadColumns, List columns) { + return SqlBuilder.forFindCountByColumns(alias, joins, table, loadColumns, columns, ' '); } @Override @@ -80,5 +80,11 @@ public class JbootOracleDialect extends OracleDialect implements JbootDialect { return SqlBuilder.forPaginateFrom(alias, joins, table, columns, orderBy, ' '); } + @Override + public String forPaginateTotalRow(String select, String sqlExceptSelect, Object ext) { + String distinctSql = SqlBuilder.forPaginateDistinctTotalRow(select, sqlExceptSelect, ext); + return distinctSql != null ? distinctSql : super.forPaginateTotalRow(select, sqlExceptSelect, ext); + } + } diff --git a/src/main/java/io/jboot/db/dialect/JbootPostgreSqlDialect.java b/src/main/java/io/jboot/db/dialect/JbootPostgreSqlDialect.java index 4e505593..6997fb9c 100644 --- a/src/main/java/io/jboot/db/dialect/JbootPostgreSqlDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootPostgreSqlDialect.java @@ -51,8 +51,8 @@ public class JbootPostgreSqlDialect extends PostgreSqlDialect implements JbootDi } @Override - public String forFindCountByColumns(String alias, List joins, String table, List columns) { - return SqlBuilder.forFindCountByColumns(alias, joins, table, columns, '"'); + public String forFindCountByColumns(String alias, List joins, String table, String loadColumns, List columns) { + return SqlBuilder.forFindCountByColumns(alias, joins, table, loadColumns, columns, '"'); } @Override @@ -72,5 +72,10 @@ public class JbootPostgreSqlDialect extends PostgreSqlDialect implements JbootDi return SqlBuilder.forPaginateFrom(alias, joins, table, columns, orderBy, '"'); } + @Override + public String forPaginateTotalRow(String select, String sqlExceptSelect, Object ext) { + String distinctSql = SqlBuilder.forPaginateDistinctTotalRow(select, sqlExceptSelect, ext); + return distinctSql != null ? distinctSql : super.forPaginateTotalRow(select, sqlExceptSelect, ext); + } } diff --git a/src/main/java/io/jboot/db/dialect/JbootSqlServerDialect.java b/src/main/java/io/jboot/db/dialect/JbootSqlServerDialect.java index 3dcb02d6..4492d174 100644 --- a/src/main/java/io/jboot/db/dialect/JbootSqlServerDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootSqlServerDialect.java @@ -63,8 +63,8 @@ public class JbootSqlServerDialect extends SqlServerDialect implements JbootDial } @Override - public String forFindCountByColumns(String alias, List joins, String table, List columns) { - return SqlBuilder.forFindCountByColumns(alias, joins, table, columns, ' '); + public String forFindCountByColumns(String alias, List joins, String table, String loadColumns, List columns) { + return SqlBuilder.forFindCountByColumns(alias, joins, table, loadColumns, columns, ' '); } @Override @@ -84,5 +84,10 @@ public class JbootSqlServerDialect extends SqlServerDialect implements JbootDial return SqlBuilder.forPaginateFrom(alias, joins, table, columns, orderBy, ' '); } + @Override + public String forPaginateTotalRow(String select, String sqlExceptSelect, Object ext) { + String distinctSql = SqlBuilder.forPaginateDistinctTotalRow(select, sqlExceptSelect, ext); + return distinctSql != null ? distinctSql : super.forPaginateTotalRow(select, sqlExceptSelect, ext); + } } diff --git a/src/main/java/io/jboot/db/dialect/JbootSqlite3Dialect.java b/src/main/java/io/jboot/db/dialect/JbootSqlite3Dialect.java index 96e5736d..5f1af6d7 100644 --- a/src/main/java/io/jboot/db/dialect/JbootSqlite3Dialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootSqlite3Dialect.java @@ -38,8 +38,8 @@ public class JbootSqlite3Dialect extends Sqlite3Dialect implements JbootDialect } @Override - public String forFindCountByColumns(String alias, List joins, String table, List columns) { - return SqlBuilder.forFindCountByColumns(alias, joins, table, columns, ' '); + public String forFindCountByColumns(String alias, List joins, String table, String loadColumns, List columns) { + return SqlBuilder.forFindCountByColumns(alias, joins, table, loadColumns, columns, ' '); } @Override @@ -59,5 +59,10 @@ public class JbootSqlite3Dialect extends Sqlite3Dialect implements JbootDialect return SqlBuilder.forPaginateFrom(alias, joins, table, columns, orderBy, ' '); } + @Override + public String forPaginateTotalRow(String select, String sqlExceptSelect, Object ext) { + String distinctSql = SqlBuilder.forPaginateDistinctTotalRow(select, sqlExceptSelect, ext); + return distinctSql != null ? distinctSql : super.forPaginateTotalRow(select, sqlExceptSelect, ext); + } } diff --git a/src/main/java/io/jboot/db/model/CPI.java b/src/main/java/io/jboot/db/model/CPI.java new file mode 100644 index 00000000..5d3fc563 --- /dev/null +++ b/src/main/java/io/jboot/db/model/CPI.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.db.model; + +public class CPI { + + public static boolean hasAnyJoinEffective(JbootModel model){ + return model.hasAnyJoinEffective(); + } +} diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index d4b56f34..ccc1c00d 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -130,6 +130,15 @@ public class JbootModel> extends Model { } + public M distinct(String distinctColumnName) { + if (StrUtil.isBlank(distinctColumnName)) { + throw new IllegalArgumentException("distinctColumnName must not be null or empty."); + } + M model = getOrCopyModel(); + model.put(JbootModelExts.DISTINCT, distinctColumnName); + return model; + } + private M getOrCopyModel() { if (isCopyModel) { return (M) this; @@ -671,13 +680,38 @@ public class JbootModel> extends Model { loadColumns = this.loadColumns; } if (StrUtil.isBlank(loadColumns)) { - loadColumns = "*"; + //使用 distinct + if (hasAnyJoinEffective() && StrUtil.isNotBlank(get(JbootModelExts.DISTINCT))) { + loadColumns = "DISTINCT " + get(JbootModelExts.DISTINCT) + + "," + (StrUtil.isNotBlank(alias) ? alias : _getTableName()) + ".*"; + } + //未使用 distinct + else { + loadColumns = "*"; + } } + + String sql = _getDialect().forFindByColumns(alias, joins, _getTableName(), loadColumns, columns.getList(), orderBy, count); return columns.isEmpty() ? find(sql) : find(sql, columns.getValueArray()); } + boolean hasAnyJoinEffective() { + if (joins == null || joins.size() == 0) { + return false; + } + + for (Join join : joins) { + if (join.isEffective()) { + return true; + } + } + + return false; + } + + public Page paginate(int pageNumber, int pageSize) { return paginateByColumns(pageNumber, pageSize, Columns.create(), null); } @@ -721,8 +755,17 @@ public class JbootModel> extends Model { if (StrUtil.isBlank(loadColumns) && this.loadColumns != null) { loadColumns = this.loadColumns; } + if (StrUtil.isBlank(loadColumns)) { - loadColumns = "*"; + //使用 distinct + if (hasAnyJoinEffective() && StrUtil.isNotBlank(get(JbootModelExts.DISTINCT))) { + loadColumns = "DISTINCT " + get(JbootModelExts.DISTINCT) + + "," + (StrUtil.isNotBlank(alias) ? alias : _getTableName()) + ".*"; + } + //未使用 distinct + else { + loadColumns = "*"; + } } String selectPartSql = _getDialect().forPaginateSelect(loadColumns); @@ -758,7 +801,15 @@ public class JbootModel> extends Model { public long findCountByColumns(Columns columns) { - String sql = _getDialect().forFindCountByColumns(alias, joins, _getTableName(), columns.getList()); + String loadColumns = "*"; + + //使用 distinct + if (hasAnyJoinEffective() && StrUtil.isNotBlank(get(JbootModelExts.DISTINCT))) { + loadColumns = "DISTINCT " + get(JbootModelExts.DISTINCT); + } + + + String sql = _getDialect().forFindCountByColumns(alias, joins, _getTableName(), loadColumns, columns.getList()); Long value = Db.use(_getConfig().getName()).queryLong(sql, Util.getValueArray(columns.getList())); return value == null ? 0 : value; } diff --git a/src/main/java/io/jboot/db/model/JbootModelExts.java b/src/main/java/io/jboot/db/model/JbootModelExts.java new file mode 100644 index 00000000..77bd5552 --- /dev/null +++ b/src/main/java/io/jboot/db/model/JbootModelExts.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.db.model; + +public class JbootModelExts { + + public static final String DISTINCT = "__ext_distinct"; + +} diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index 8cac1328..66aab64e 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -19,6 +19,7 @@ import io.jboot.utils.ArrayUtil; import io.jboot.utils.StrUtil; import java.util.List; +import java.util.regex.Pattern; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) @@ -235,6 +236,26 @@ public class SqlBuilder { return sqlBuilder; } + //来源于 @link Dialect.java + private static final Pattern ORDER_BY_PATTERN = Pattern.compile( + "order\\s+by\\s+[^,\\s]+(\\s+asc|\\s+desc)?(\\s*,\\s*[^,\\s]+(\\s+asc|\\s+desc)?)*", + Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + + private static String replaceOrderBy(String sql) { + return ORDER_BY_PATTERN.matcher(sql).replaceAll(""); + } + + public static String forPaginateDistinctTotalRow(String select, String sqlExceptSelect, Object ext) { + if (ext instanceof JbootModel && CPI.hasAnyJoinEffective((JbootModel) ext)) { + String distinct = ((JbootModel) ext).get(JbootModelExts.DISTINCT); + if (StrUtil.isNotBlank(distinct)) { + return "SELECT count(DISTINCT " + distinct + ") " + replaceOrderBy(sqlExceptSelect); + } + } + return null; + } + + public static String forPaginateFrom(String alias, List joins, String table, List columns, String orderBy, char separator) { StringBuilder sqlBuilder = new StringBuilder(" FROM ") .append(separator) @@ -281,8 +302,8 @@ public class SqlBuilder { } - public static String forFindCountByColumns(String alias, List joins, String table, List columns, char separator) { - StringBuilder sqlBuilder = new StringBuilder("SELECT count(*) FROM ") + public static String forFindCountByColumns(String alias, List joins, String table, String loadColumns, List columns, char separator) { + StringBuilder sqlBuilder = new StringBuilder("SELECT count(" + loadColumns + ") FROM ") .append(separator) .append(table) .append(separator); -- Gitee From 8fdbad2a5fde98fd96f101a25ad467e2a5482987 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 14 Nov 2021 17:09:10 +0800 Subject: [PATCH 1438/1965] v3.11.4 snapshot --- src/main/java/io/jboot/test/MockMvc.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/test/MockMvc.java b/src/main/java/io/jboot/test/MockMvc.java index 3d127e3e..20f1e4dc 100644 --- a/src/main/java/io/jboot/test/MockMvc.java +++ b/src/main/java/io/jboot/test/MockMvc.java @@ -104,10 +104,6 @@ public class MockMvc { paras.forEach(request::addParameter); - if (requestStartListener != null) { - requestStartListener.accept(request); - } - return doStartMockRequest(request); } @@ -156,23 +152,26 @@ public class MockMvc { request.setInputStream(new MockServletInputStream(postData)); } - if (requestStartListener != null) { - requestStartListener.accept(request); - } - return doStartMockRequest(request); } private MockMvcResult doStartMockRequest(MockHttpServletRequest request) { + + MockHttpServletResponse response = new MockHttpServletResponse(); try { + if (requestStartListener != null) { + requestStartListener.accept(request); + } + //开启 cookie 保持, 用户未设置自己的 cookie, 上次的 cookie 有值 if (isHoldCookiesEnable() && request.getCookies().length == 0 && holdCookies.size() > 0) { request.setCookies(holdCookies); } doSendRequest(request, response); + } finally { if (requestFinishedListener != null) { requestFinishedListener.accept(response); @@ -188,7 +187,7 @@ public class MockMvc { public void doSetCookie(Cookie newCookie) { holdCookies.removeIf(cookie -> Objects.equals(cookie.getName(), newCookie.getName())); - if (newCookie.getValue() != null) { + if (StrUtil.isNotEmpty(newCookie.getValue())) { holdCookies.add(newCookie); } } -- Gitee From 33888658a7e97d092efef5694a7515c3aecd095e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 14 Nov 2021 17:13:50 +0800 Subject: [PATCH 1439/1965] v3.11.4 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2a34872e..03a728fe 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.11.3 + 3.11.4 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 5bfcf662..671b8774 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.11.3 + 3.11.4 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index ef9aaa1c..cc430b48 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.11.3 + 3.11.4 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 33d33b73..b3fb8a66 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.11.3 + 3.11.4 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.11.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.11.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 157a9b4d..64828aa3 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.11.3', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.11.4', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.11.3/jboot-3.11.3.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.11.4/jboot-3.11.4.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index db4aa53a..3dc99f0b 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.11.3"; + public static String VERSION = "3.11.4"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From ee1d2ffe76f8398249794d97fead32e6b39152c7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 14 Nov 2021 17:13:58 +0800 Subject: [PATCH 1440/1965] v3.11.4 release (^.^)YYa!! --- changes.txt | 8 ++++++++ pom.xml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 79c51d9c..d1980149 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,11 @@ +jboot v3.11.4: +新增:新增 DAO.distinct() 方法,用于对内容进行去重 +新增:MockMvc 新增 holdCookie 配置,用于在不同的测试用例里,保持 cookie 的功能 +优化:保证 Model 和 Record 的 getBigInteger 的行为一致 +优化:移除 codegen 下的 PathKit 类,同时使用 CodeGenHelpler 答题 + + + jboot v3.11.3: 新增:重写 Model 的 getBigInteger,防止转换异常 新增:代码生成器 生成的 Model,如果字段是 BigInteger ,自动调用 getBigInteger 方法的功能 diff --git a/pom.xml b/pom.xml index 754e3928..27eb7822 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.11.4-snapshot + 3.11.4 jar jboot -- Gitee From 48195b5111e363a5bfb010066f547210719149c7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 14 Nov 2021 17:27:36 +0800 Subject: [PATCH 1441/1965] v3.11.4 release (^.^)YYa!! --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 27eb7822..f08d720b 100644 --- a/pom.xml +++ b/pom.xml @@ -43,13 +43,13 @@ 4.9.17 2020.4 - 2.7 + 2.8 3.3 3.3.0 4.0.3 2.12.5 5.1.49 - 2.2.10.Final + 2.2.12.Final 1.7.32 2.57 1.2.78 -- Gitee From f5da2f7ab55675c5d4ec54ae4e6ef8fe713e72a0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 14 Nov 2021 17:29:37 +0800 Subject: [PATCH 1442/1965] v3.11.4 release (^.^)YYa!! --- changes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index d1980149..de97ccf4 100644 --- a/changes.txt +++ b/changes.txt @@ -2,7 +2,7 @@ jboot v3.11.4: 新增:新增 DAO.distinct() 方法,用于对内容进行去重 新增:MockMvc 新增 holdCookie 配置,用于在不同的测试用例里,保持 cookie 的功能 优化:保证 Model 和 Record 的 getBigInteger 的行为一致 -优化:移除 codegen 下的 PathKit 类,同时使用 CodeGenHelpler 答题 +优化:移除 codegen 下的 PathKit 类,使用 CodeGenHelpler 代替 -- Gitee From f42bda3b08b8e151af5c07ca526cf035dea24afd Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 19 Nov 2021 10:26:22 +0800 Subject: [PATCH 1443/1965] v3.12.0 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f08d720b..86dc6fc4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.11.4 + 3.12.0-SNAPSHOT jar jboot -- Gitee From 376497598dc20f51801814f6c8738f799c220271 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 19 Nov 2021 10:27:15 +0800 Subject: [PATCH 1444/1965] support config the Class name for spi --- src/main/java/io/jboot/core/spi/JbootSpiLoader.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/core/spi/JbootSpiLoader.java b/src/main/java/io/jboot/core/spi/JbootSpiLoader.java index 46ed8278..fe9a0d10 100644 --- a/src/main/java/io/jboot/core/spi/JbootSpiLoader.java +++ b/src/main/java/io/jboot/core/spi/JbootSpiLoader.java @@ -50,14 +50,16 @@ public class JbootSpiLoader { */ public static T load(Class clazz, String spiName) { T returnObject = loadByServiceLoader(clazz, spiName); - if (returnObject != null) return returnObject; + if (returnObject != null) { + return returnObject; + } if (StrUtil.isBlank(spiName)) { return null; } - List> classes = ClassScanner.scanSubClass(clazz,true); - if (classes == null || classes.isEmpty()) { + List> classes = ClassScanner.scanSubClass(clazz, true); + if (classes.isEmpty()) { return null; } @@ -66,7 +68,12 @@ public class JbootSpiLoader { if (spiConfig != null && spiName.equals(AnnotationUtil.get(spiConfig.value()))) { return ClassUtil.newInstance(c); } + //support config class name + else if (spiName.equals(c.getName())) { + return ClassUtil.newInstance(c); + } } + return null; } -- Gitee From dd96b6dc6421dc512b74533aa51132e98f17a57c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 19 Nov 2021 10:50:10 +0800 Subject: [PATCH 1445/1965] =?UTF-8?q?MQ=20=E6=96=B0=E5=A2=9E=E5=8F=91?= =?UTF-8?q?=E9=80=81=E5=8E=9F=E7=94=9F=20Message=20=E7=9A=84=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mq/aliyunmq/JbootAliyunmqImpl.java | 22 ++++++++++++++----- .../components/mq/qpidmq/JbootQpidmqImpl.java | 16 ++++++++------ .../mq/redismq/JbootRedismqImpl.java | 2 +- .../mq/rocketmq/JbootRocketmqImpl.java | 15 +++++++++---- 4 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java index d0df1a9a..2e5c5bf2 100644 --- a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java +++ b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java @@ -71,17 +71,27 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { @Override public void enqueue(Object message, String toChannel) { - byte[] bytes = getSerializer().serialize(message); - Message onsMessage = new Message(toChannel, "*", bytes); - getProducer().send(onsMessage); + Message sendMsg = null; + if (message instanceof Message) { + sendMsg = (Message) message; + } else { + byte[] bytes = getSerializer().serialize(message); + sendMsg = new Message(toChannel, "*", bytes); + } + getProducer().send(sendMsg); } @Override public void publish(Object message, String toChannel) { - byte[] bytes = getSerializer().serialize(message); - Message onsMessage = new Message(aliyunmqConfig.getBroadcastChannelPrefix() + toChannel, "*", bytes); - getProducer().send(onsMessage); + Message sendMsg = null; + if (message instanceof Message) { + sendMsg = (Message) message; + } else { + byte[] bytes = getSerializer().serialize(message); + sendMsg = new Message(aliyunmqConfig.getBroadcastChannelPrefix() + toChannel, "*", bytes); + } + getProducer().send(sendMsg); } diff --git a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java index e3bc820d..5d0bb9dd 100644 --- a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java +++ b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java @@ -83,17 +83,19 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { MessageProducer producer = session.createProducer(destination); producer.setTimeToLive(30000); - Message m = null; - if (!serializerEnable) { - m = session.createTextMessage((String) message); + Message sendMsg = null; + if (message instanceof Message){ + sendMsg = (Message) message; + }else if (!serializerEnable) { + sendMsg = session.createTextMessage((String) message); } else { byte[] data = getSerializer().serialize(message); - m = session.createBytesMessage(); - m.setIntProperty("data-len", data.length); - ((BytesMessage) m).writeBytes(data); + sendMsg = session.createBytesMessage(); + sendMsg.setIntProperty("data-len", data.length); + ((BytesMessage) sendMsg).writeBytes(data); } - producer.send(m); + producer.send(sendMsg); } catch (Exception e) { LOG.error(e.toString(), e); diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java index cc5d0b80..f1a254f0 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java @@ -59,7 +59,7 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { } }, redis.keysToBytesArray(channels)); - dequeueThread = new Thread(this,"redis-dequeue-thread"); + dequeueThread = new Thread(this, "redis-dequeue-thread"); dequeueThread.start(); } diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java index aaad3c32..8ca7fe7d 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java @@ -145,8 +145,14 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { private void trySendMessage(Object message, String topic, int tryTimes) { if (tryTimes < 3) { try { - Message rocketMessage = new Message(topic, getSerializer().serialize(message)); - SendResult result = getMQProducer().send(rocketMessage); + Message sendMsg = null; + if (message instanceof Message) { + sendMsg = (Message) message; + } else { + sendMsg = new Message(topic, getSerializer().serialize(message)); + } + + SendResult result = getMQProducer().send(sendMsg); // if (result.getSendStatus() != SendStatus.SEND_OK) { // 只要不等于 null 就是发送成功 if (result == null) { @@ -162,7 +168,8 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { } - private MQProducer getMQProducer() throws MQClientException { + + protected MQProducer getMQProducer() throws MQClientException { if (mqProducer == null) { synchronized (this) { if (mqProducer == null) { @@ -174,7 +181,7 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { } - private void createMqProducer() throws MQClientException { + protected void createMqProducer() throws MQClientException { DefaultMQProducer producer = new DefaultMQProducer(rocketmqConfig.getProducerGroup()); producer.setNamesrvAddr(rocketmqConfig.getNamesrvAddr()); -- Gitee From 9ff96cc1625eafae15d94f287786ff987455bc1c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 19 Nov 2021 11:18:03 +0800 Subject: [PATCH 1446/1965] =?UTF-8?q?MQ=20=E6=96=B0=E5=A2=9E=E5=A4=9A?= =?UTF-8?q?=E5=AE=9E=E4=BE=8B=20MQ=20=E7=9A=84=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/components/mq/JbootmqConfig.java | 43 +++++++++++++------ .../jboot/components/mq/JbootmqManager.java | 41 +++++++++++++++--- .../mq/aliyunmq/JbootAliyunmqImpl.java | 15 ++++++- .../components/mq/qpidmq/JbootQpidmqImpl.java | 17 +++++++- .../mq/rabbitmq/JbootRabbitmqImpl.java | 15 ++++++- .../mq/redismq/JbootRedismqImpl.java | 18 +++++++- .../mq/rocketmq/JbootRocketmqImpl.java | 17 +++++++- 7 files changed, 141 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/JbootmqConfig.java b/src/main/java/io/jboot/components/mq/JbootmqConfig.java index bf3c77cd..8641c8bb 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqConfig.java +++ b/src/main/java/io/jboot/components/mq/JbootmqConfig.java @@ -27,19 +27,21 @@ public class JbootmqConfig { public static final String TYPE_RABBITMQ = "rabbitmq"; public static final String TYPE_ROCKETMQ = "rocketmq"; public static final String TYPE_QPID = "qpid"; - public static final String TYPE_LOCAL= "local"; + public static final String TYPE_LOCAL = "local"; - private String type; - private String channel; + private String name = "default"; // MQ 的名称,可以配置多个 MQ 实例,但是需要名称不能一样 + private String type; // MQ 的类型: redis、rocketmq 等 + private String typeName; // MQ 相同的类型,可能有多一个实例,比如两个 redis,此时需要配置示例的名称 + private String channel; // 发送的通道,或者是 topic,多个用英文逗号二开 private String syncRecevieMessageChannel; //可同步接收消息的 channel 配置 - private String serializer; + private String serializer; // MQ 默认的序列化方案 - public String getChannel() { - return channel; + public String getName() { + return name; } - public void setChannel(String channel) { - this.channel = channel; + public void setName(String name) { + this.name = name; } public String getType() { @@ -50,13 +52,20 @@ public class JbootmqConfig { this.type = type; } + public String getTypeName() { + return typeName; + } - public String getSerializer() { - return serializer; + public void setTypeName(String typeName) { + this.typeName = typeName; } - public void setSerializer(String serializer) { - this.serializer = serializer; + public String getChannel() { + return channel; + } + + public void setChannel(String channel) { + this.channel = channel; } public String getSyncRecevieMessageChannel() { @@ -67,7 +76,15 @@ public class JbootmqConfig { this.syncRecevieMessageChannel = syncRecevieMessageChannel; } - public boolean isConfigOk(){ + public String getSerializer() { + return serializer; + } + + public void setSerializer(String serializer) { + this.serializer = serializer; + } + + public boolean isConfigOk() { return StrUtil.isNotEmpty(type); } } diff --git a/src/main/java/io/jboot/components/mq/JbootmqManager.java b/src/main/java/io/jboot/components/mq/JbootmqManager.java index 94806054..35977a53 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqManager.java +++ b/src/main/java/io/jboot/components/mq/JbootmqManager.java @@ -16,6 +16,7 @@ package io.jboot.components.mq; import io.jboot.Jboot; +import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.mq.aliyunmq.JbootAliyunmqImpl; import io.jboot.components.mq.local.JbootLocalmqImpl; import io.jboot.components.mq.qpidmq.JbootQpidmqImpl; @@ -23,8 +24,12 @@ import io.jboot.components.mq.rabbitmq.JbootRabbitmqImpl; import io.jboot.components.mq.redismq.JbootRedismqImpl; import io.jboot.components.mq.rocketmq.JbootRocketmqImpl; import io.jboot.core.spi.JbootSpiLoader; +import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.ClassUtil; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + public class JbootmqManager { @@ -37,15 +42,41 @@ public class JbootmqManager { return manager; } + private Map jbootmqMap = new ConcurrentHashMap<>(); - private Jbootmq jbootmq; public Jbootmq getJbootmq() { - if (jbootmq == null) { - JbootmqConfig config = Jboot.config(JbootmqConfig.class); - jbootmq = getJbootmq(config); + Jbootmq defaultMq = jbootmqMap.get("default"); + if (defaultMq == null) { + synchronized (this) { + defaultMq = jbootmqMap.get("default"); + if (defaultMq == null) { + JbootmqConfig config = Jboot.config(JbootmqConfig.class); + defaultMq = getJbootmq(config); + jbootmqMap.put("default", defaultMq); + } + } } - return jbootmq; + return getJbootmq("default"); + } + + public Jbootmq getJbootmq(String name) { + Jbootmq mq = jbootmqMap.get(name); + if (mq == null) { + synchronized (this) { + mq = jbootmqMap.get(name); + if (mq == null) { + Map configModels = JbootConfigUtil.getConfigModels(JbootmqConfig.class, "jboot.mq"); + if (!configModels.containsKey(name)) { + throw new JbootIllegalConfigException("Please config \"jboot.mq." + name + ".type\" in your jboot.properties."); + } + mq = getJbootmq(configModels.get(name)); + jbootmqMap.put(name, mq); + } + } + } + + return mq; } public Jbootmq getJbootmq(JbootmqConfig config) { diff --git a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java index 2e5c5bf2..76ae76cb 100644 --- a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java +++ b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java @@ -17,9 +17,13 @@ package io.jboot.components.mq.aliyunmq; import com.aliyun.openservices.ons.api.*; import io.jboot.Jboot; +import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; +import io.jboot.exception.JbootIllegalConfigException; +import io.jboot.utils.StrUtil; +import java.util.Map; import java.util.Properties; @@ -31,7 +35,16 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { public JbootAliyunmqImpl() { super(); - aliyunmqConfig = Jboot.config(JbootAliyunmqConfig.class); + String typeName = config.getTypeName(); + if (StrUtil.isNotBlank(typeName)) { + Map configModels = JbootConfigUtil.getConfigModels(JbootAliyunmqConfig.class, "jboot.mq.aliyun"); + if (!configModels.containsKey(typeName)) { + throw new JbootIllegalConfigException("Please config \"jboot.mq.aliyun." + typeName + ".addr\" in your jboot.properties."); + } + aliyunmqConfig = configModels.get(typeName); + } else { + aliyunmqConfig = Jboot.config(JbootAliyunmqConfig.class); + } } diff --git a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java index 5d0bb9dd..83168762 100644 --- a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java +++ b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java @@ -17,9 +17,11 @@ package io.jboot.components.mq.qpidmq; import com.jfinal.log.Log; import io.jboot.Jboot; +import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; import io.jboot.exception.JbootException; +import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.ArrayUtil; import io.jboot.utils.StrUtil; import org.apache.qpid.client.AMQAnyDestination; @@ -27,6 +29,7 @@ import org.apache.qpid.client.AMQConnection; import org.apache.qpid.jms.Connection; import javax.jms.*; +import java.util.Map; /** * @author 徐海峰 (27533892@qq.com) @@ -41,7 +44,19 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { public JbootQpidmqImpl() { super(); - JbootQpidmqConfig qpidConfig = Jboot.config(JbootQpidmqConfig.class); + JbootQpidmqConfig qpidConfig = null; + String typeName = config.getTypeName(); + if (StrUtil.isNotBlank(typeName)) { + Map configModels = JbootConfigUtil.getConfigModels(JbootQpidmqConfig.class, "jboot.mq.qpid"); + if (!configModels.containsKey(typeName)) { + throw new JbootIllegalConfigException("Please config \"jboot.mq.qpid." + typeName + ".host\" in your jboot.properties."); + } + qpidConfig = configModels.get(typeName); + } else { + qpidConfig = Jboot.config(JbootQpidmqConfig.class); + } + + serializerEnable = qpidConfig.isSerializerEnable(); try { diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index dea616c2..c727377d 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -18,9 +18,11 @@ package io.jboot.components.mq.rabbitmq; import com.rabbitmq.client.*; import io.jboot.Jboot; import io.jboot.app.JbootApplicationConfig; +import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; import io.jboot.exception.JbootException; +import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.StrUtil; import java.io.IOException; @@ -42,8 +44,17 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { public JbootRabbitmqImpl() { super(); - rabbitmqConfig = Jboot.config(JbootRabbitmqConfig.class); - appConfig = Jboot.config(JbootApplicationConfig.class); + + String typeName = config.getTypeName(); + if (StrUtil.isNotBlank(typeName)) { + Map configModels = JbootConfigUtil.getConfigModels(JbootRabbitmqConfig.class, "jboot.mq.rabbitmq"); + if (!configModels.containsKey(typeName)) { + throw new JbootIllegalConfigException("Please config \"jboot.mq.rabbitmq." + typeName + ".host\" in your jboot.properties."); + } + rabbitmqConfig = configModels.get(typeName); + } else { + rabbitmqConfig = Jboot.config(JbootRabbitmqConfig.class); + } try { ConnectionFactory factory = new ConnectionFactory(); diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java index f1a254f0..8c6d3c5c 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java @@ -17,13 +17,17 @@ package io.jboot.components.mq.redismq; import com.jfinal.log.Log; import io.jboot.Jboot; +import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.support.redis.JbootRedis; import io.jboot.support.redis.JbootRedisManager; +import io.jboot.utils.StrUtil; import redis.clients.jedis.BinaryJedisPubSub; +import java.util.Map; + public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { @@ -34,7 +38,19 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { public JbootRedismqImpl() { super(); - JbootRedismqConfig redisConfig = Jboot.config(JbootRedismqConfig.class); + + JbootRedismqConfig redisConfig = null; + String typeName = config.getTypeName(); + if (StrUtil.isNotBlank(typeName)) { + Map configModels = JbootConfigUtil.getConfigModels(JbootRedismqConfig.class, "jboot.mq.redis"); + if (!configModels.containsKey(typeName)) { + throw new JbootIllegalConfigException("Please config \"jboot.mq.redis." + typeName + ".host\" in your jboot.properties."); + } + redisConfig = configModels.get(typeName); + } else { + redisConfig = Jboot.config(JbootRedismqConfig.class); + } + if (redisConfig.isConfigOk()) { redis = JbootRedisManager.me().getRedis(redisConfig); } else { diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java index 8ca7fe7d..cd56d6ce 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java @@ -17,8 +17,10 @@ package io.jboot.components.mq.rocketmq; import com.jfinal.log.Log; import io.jboot.Jboot; +import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; +import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.StrUtil; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; @@ -32,6 +34,8 @@ import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; +import java.util.Map; + public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { @@ -41,7 +45,17 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { public JbootRocketmqImpl() { super(); - rocketmqConfig = Jboot.config(JbootRocketmqConfig.class); + + String typeName = config.getTypeName(); + if (StrUtil.isNotBlank(typeName)) { + Map configModels = JbootConfigUtil.getConfigModels(JbootRocketmqConfig.class, "jboot.mq.rocket"); + if (!configModels.containsKey(typeName)) { + throw new JbootIllegalConfigException("Please config \"jboot.mq.rocket." + typeName + ".namesrvAddr\" in your jboot.properties."); + } + rocketmqConfig = configModels.get(typeName); + } else { + rocketmqConfig = Jboot.config(JbootRocketmqConfig.class); + } } @Override @@ -168,7 +182,6 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { } - protected MQProducer getMQProducer() throws MQClientException { if (mqProducer == null) { synchronized (this) { -- Gitee From d26f9c36a7c7c5821773bf56466bb1141b451a04 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 19 Nov 2021 11:24:01 +0800 Subject: [PATCH 1447/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?=20Redis=20=E5=AE=9E=E4=BE=8B=E9=85=8D=E7=BD=AE=E7=9A=84?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/support/redis/JbootRedisConfig.java | 8 ++++++ .../support/redis/JbootRedisManager.java | 28 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/support/redis/JbootRedisConfig.java b/src/main/java/io/jboot/support/redis/JbootRedisConfig.java index 0cb97277..ea8ee05a 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedisConfig.java +++ b/src/main/java/io/jboot/support/redis/JbootRedisConfig.java @@ -29,6 +29,7 @@ public class JbootRedisConfig { public static final String TYPE_REDISSON = "redisson"; public static final String TYPE_LETTUCE = "lettuce"; + private String name = "default"; private String host; private Integer port = 6379; private Integer timeout = 2000; @@ -51,6 +52,13 @@ public class JbootRedisConfig { private String serializer; + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } public String getHost() { return host; diff --git a/src/main/java/io/jboot/support/redis/JbootRedisManager.java b/src/main/java/io/jboot/support/redis/JbootRedisManager.java index 298c58b4..d0de95e6 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedisManager.java +++ b/src/main/java/io/jboot/support/redis/JbootRedisManager.java @@ -17,9 +17,14 @@ package io.jboot.support.redis; import io.jboot.Jboot; +import io.jboot.app.config.JbootConfigUtil; +import io.jboot.exception.JbootException; +import io.jboot.exception.JbootIllegalConfigException; import io.jboot.support.redis.jedis.JbootJedisClusterImpl; import io.jboot.support.redis.jedis.JbootJedisImpl; -import io.jboot.exception.JbootException; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * 参考: com.jfinal.plugin.redis @@ -37,6 +42,7 @@ public class JbootRedisManager { } private JbootRedis redis; + private Map jbootRedisMap = new ConcurrentHashMap<>(); public JbootRedis getRedis() { if (redis == null) { @@ -47,6 +53,26 @@ public class JbootRedisManager { return redis; } + public JbootRedis getRedis(String name) { + JbootRedis redis = jbootRedisMap.get(name); + if (redis == null) { + synchronized (this) { + redis = jbootRedisMap.get(name); + if (redis == null) { + Map configModels = JbootConfigUtil.getConfigModels(JbootRedisConfig.class, "jboot.redis"); + if (!configModels.containsKey(name)) { + throw new JbootIllegalConfigException("Please config \"jboot.redis." + name + ".host\" in your jboot.properties."); + } + JbootRedisConfig jbootRedisConfig = configModels.get(name); + redis = getRedis(jbootRedisConfig); + jbootRedisMap.put(name, redis); + } + } + } + return redis; + } + + public JbootRedis getRedis(JbootRedisConfig config) { if (config == null || !config.isConfigOk()) { return null; -- Gitee From 28ff6c51b0817e3288320c2001d82b311a572ad0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 19 Nov 2021 11:58:13 +0800 Subject: [PATCH 1448/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=A4=9A=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E5=AE=9E=E4=BE=8B=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/components/cache/AopCache.java | 2 +- .../components/cache/JbootCacheBase.java | 8 ++- .../components/cache/JbootCacheConfig.java | 58 ++++++------------- .../components/cache/JbootCacheManager.java | 39 ++++++++----- .../cache/caffeine/CaffeineCacheImpl.java | 5 ++ .../cache/caredis/JbootCaredisCacheImpl.java | 8 ++- .../cache/ehcache/JbootEhcacheImpl.java | 13 ++--- .../cache/ehredis/JbootEhredisCacheImpl.java | 10 ++-- .../components/cache/interceptor/Utils.java | 7 +-- .../components/cache/j2cache/J2cacheImpl.java | 5 ++ .../cache/redis/JbootRedisCacheImpl.java | 12 ++-- .../io/jboot/test/ehcache/EhcacheTester.java | 3 +- 12 files changed, 91 insertions(+), 79 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/AopCache.java b/src/main/java/io/jboot/components/cache/AopCache.java index 2f08624b..894a3c8f 100644 --- a/src/main/java/io/jboot/components/cache/AopCache.java +++ b/src/main/java/io/jboot/components/cache/AopCache.java @@ -24,7 +24,7 @@ public class AopCache { if (aopCache == null) { synchronized (AopCache.class) { if (aopCache == null) { - aopCache = JbootCacheManager.me().getCache(JbootCacheConfig.getInstance().getAopCacheType()); + aopCache = JbootCacheManager.me().getCache(JbootAopCacheConfig.getInstance().getUseCacheName()); } } } diff --git a/src/main/java/io/jboot/components/cache/JbootCacheBase.java b/src/main/java/io/jboot/components/cache/JbootCacheBase.java index 4eca9167..467657ff 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheBase.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheBase.java @@ -20,6 +20,12 @@ import io.jboot.utils.StrUtil; public abstract class JbootCacheBase implements JbootCache { + protected JbootCacheConfig config; + + public JbootCacheBase(JbootCacheConfig config) { + this.config = config; + } + private ThreadLocal CACHE_NAME_PREFIX_TL = new ThreadLocal<>(); @Override @@ -41,7 +47,7 @@ public abstract class JbootCacheBase implements JbootCache { protected String buildCacheName(String cacheName) { String cacheNamePrefix = CACHE_NAME_PREFIX_TL.get(); if (StrUtil.isBlank(cacheNamePrefix)) { - cacheNamePrefix = JbootCacheConfig.getInstance().getDefaultCachePrefix(); + cacheNamePrefix = config.getDefaultCachePrefix(); } if (StrUtil.isBlank(cacheNamePrefix)) { diff --git a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java index 4bffdaa3..a24d1888 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java @@ -16,9 +16,10 @@ package io.jboot.components.cache; -import io.jboot.app.config.JbootConfigManager; +import com.google.common.collect.Sets; import io.jboot.app.config.annotation.ConfigModel; -import io.jboot.utils.StrUtil; + +import java.util.Set; @ConfigModel(prefix = "jboot.cache") @@ -32,24 +33,14 @@ public class JbootCacheConfig { public static final String TYPE_CAREDIS = "caredis"; public static final String TYPE_NONE = "none"; + public static final Set TYPES = Sets.newHashSet(TYPE_EHCACHE, TYPE_REDIS, TYPE_EHREDIS, TYPE_J2CACHE, TYPE_CAFFEINE, TYPE_CAREDIS, TYPE_NONE); - private String type = TYPE_CAFFEINE; private String name = "default"; - private String defaultCachePrefix; - - // AOP 缓存的默认有效时间,0为永久有效,单位秒, - // 当 @Cacheable 和 @CachePut 注解不配置的时候默认用这个配置 - private int aopCacheLiveSeconds = 0; - private String aopCacheType; - private String aopCacheDefaultCacheNamePrefix; + private String type = TYPE_CAFFEINE; + private String typeName; - public String getType() { - return type; - } + private String defaultCachePrefix; - public void setType(String type) { - this.type = type; - } public String getName() { return name; @@ -59,40 +50,29 @@ public class JbootCacheConfig { this.name = name; } - public String getDefaultCachePrefix() { - return defaultCachePrefix; - } - public void setDefaultCachePrefix(String defaultCachePrefix) { - this.defaultCachePrefix = defaultCachePrefix; + public String getType() { + return type; } - public int getAopCacheLiveSeconds() { - return aopCacheLiveSeconds; + public void setType(String type) { + this.type = type; } - public void setAopCacheLiveSeconds(int aopCacheLiveSeconds) { - this.aopCacheLiveSeconds = aopCacheLiveSeconds; + public String getTypeName() { + return typeName; } - public String getAopCacheType() { - if (StrUtil.isBlank(aopCacheType)){ - aopCacheType = getType(); - } - return aopCacheType; + public void setTypeName(String typeName) { + this.typeName = typeName; } - public void setAopCacheType(String aopCacheType) { - this.aopCacheType = aopCacheType; + public String getDefaultCachePrefix() { + return defaultCachePrefix; } - private static JbootCacheConfig me; - - public static JbootCacheConfig getInstance() { - if (me == null) { - me = JbootConfigManager.me().get(JbootCacheConfig.class); - } - return me; + public void setDefaultCachePrefix(String defaultCachePrefix) { + this.defaultCachePrefix = defaultCachePrefix; } } diff --git a/src/main/java/io/jboot/components/cache/JbootCacheManager.java b/src/main/java/io/jboot/components/cache/JbootCacheManager.java index 416b9bea..029b299e 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheManager.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheManager.java @@ -16,6 +16,7 @@ package io.jboot.components.cache; import io.jboot.Jboot; +import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.cache.caffeine.CaffeineCacheImpl; import io.jboot.components.cache.caredis.JbootCaredisCacheImpl; import io.jboot.components.cache.ehcache.JbootEhcacheImpl; @@ -24,6 +25,7 @@ import io.jboot.components.cache.j2cache.J2cacheImpl; import io.jboot.components.cache.none.NoneCacheImpl; import io.jboot.components.cache.redis.JbootRedisCacheImpl; import io.jboot.core.spi.JbootSpiLoader; +import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.StrUtil; import java.util.Map; @@ -45,23 +47,30 @@ public class JbootCacheManager { } public JbootCache getCache() { - return getCache(defaultConfig.getType()); + return getCache(defaultConfig.getName()); } - public JbootCache getCache(String type) { - if (StrUtil.isBlank(type)) { - throw new IllegalArgumentException("type must not be null or blank."); + public JbootCache getCache(String name) { + if (StrUtil.isBlank(name)) { + throw new IllegalArgumentException("cache name must not be null or blank."); } - JbootCache cache = cacheMap.get(type); + JbootCache cache = cacheMap.get(name); if (cache == null) { - synchronized (type.intern()) { + synchronized (this) { + cache = cacheMap.get(name); if (cache == null) { - JbootCacheConfig cacheConfig = new JbootCacheConfig(); - cacheConfig.setType(type); + Map configModels = JbootConfigUtil.getConfigModels(JbootCacheConfig.class, "jboot.cache"); + JbootCacheConfig.TYPES.forEach(configModels::remove); + + if (!configModels.containsKey(name)) { + throw new JbootIllegalConfigException("Please config \"jboot.cache." + name + ".type\" in your jboot.properties."); + } + + JbootCacheConfig cacheConfig = configModels.get(name); cache = buildCache(cacheConfig); - cacheMap.put(type, cache); + cacheMap.put(name, cache); } } } @@ -74,17 +83,17 @@ public class JbootCacheManager { switch (config.getType()) { case JbootCacheConfig.TYPE_EHCACHE: - return new JbootEhcacheImpl(); + return new JbootEhcacheImpl(config); case JbootCacheConfig.TYPE_REDIS: - return new JbootRedisCacheImpl(); + return new JbootRedisCacheImpl(config); case JbootCacheConfig.TYPE_EHREDIS: - return new JbootEhredisCacheImpl(); + return new JbootEhredisCacheImpl(config); case JbootCacheConfig.TYPE_J2CACHE: - return new J2cacheImpl(); + return new J2cacheImpl(config); case JbootCacheConfig.TYPE_CAFFEINE: - return new CaffeineCacheImpl(); + return new CaffeineCacheImpl(config); case JbootCacheConfig.TYPE_CAREDIS: - return new JbootCaredisCacheImpl(); + return new JbootCaredisCacheImpl(config); case JbootCacheConfig.TYPE_NONE: return new NoneCacheImpl(); default: diff --git a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java index 39dd20b1..dd97528a 100644 --- a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java @@ -18,6 +18,7 @@ package io.jboot.components.cache.caffeine; import com.github.benmanes.caffeine.cache.Cache; import com.jfinal.plugin.ehcache.IDataLoader; import io.jboot.components.cache.JbootCacheBase; +import io.jboot.components.cache.JbootCacheConfig; import java.util.ArrayList; import java.util.List; @@ -29,6 +30,10 @@ public class CaffeineCacheImpl extends JbootCacheBase { private Map cacheMap = new ConcurrentHashMap<>(); + public CaffeineCacheImpl(JbootCacheConfig config) { + super(config); + } + protected Cache getCacheOnly(String cacheName) { cacheName = buildCacheName(cacheName); return cacheMap.get(cacheName); diff --git a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java index 9e4cd28b..a6da211e 100644 --- a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java @@ -20,6 +20,7 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.jfinal.plugin.ehcache.IDataLoader; import io.jboot.Jboot; import io.jboot.components.cache.JbootCacheBase; +import io.jboot.components.cache.JbootCacheConfig; import io.jboot.components.cache.caffeine.CaffeineCacheImpl; import io.jboot.components.cache.redis.JbootRedisCacheImpl; import io.jboot.components.serializer.JbootSerializer; @@ -53,9 +54,10 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { .build(); - public JbootCaredisCacheImpl() { - this.caffeineCacheImpl = new CaffeineCacheImpl(); - this.redisCacheImpl = new JbootRedisCacheImpl(); + public JbootCaredisCacheImpl(JbootCacheConfig config) { + super(config); + this.caffeineCacheImpl = new CaffeineCacheImpl(config); + this.redisCacheImpl = new JbootRedisCacheImpl(config); this.clientId = StrUtil.uuid(); this.serializer = Jboot.getSerializer(); this.redis = redisCacheImpl.getRedis(); diff --git a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java index 6a87df7f..25cc7721 100644 --- a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java @@ -20,6 +20,7 @@ import com.jfinal.log.Log; import com.jfinal.plugin.ehcache.IDataLoader; import io.jboot.Jboot; import io.jboot.components.cache.JbootCacheBase; +import io.jboot.components.cache.JbootCacheConfig; import io.jboot.utils.StrUtil; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; @@ -39,12 +40,13 @@ public class JbootEhcacheImpl extends JbootCacheBase { private CacheEventListener cacheEventListener; - public JbootEhcacheImpl() { - JbootEhCacheConfig config = Jboot.config(JbootEhCacheConfig.class); - if (StrUtil.isBlank(config.getConfigFileName())) { + public JbootEhcacheImpl(JbootCacheConfig config) { + super(config); + JbootEhCacheConfig ehconfig = Jboot.config(JbootEhCacheConfig.class); + if (StrUtil.isBlank(ehconfig.getConfigFileName())) { cacheManager = CacheManager.create(); } else { - String configPath = config.getConfigFileName(); + String configPath = ehconfig.getConfigFileName(); if (!configPath.startsWith("/")){ configPath = PathKit.getRootClassPath()+"/"+configPath; } @@ -52,9 +54,6 @@ public class JbootEhcacheImpl extends JbootCacheBase { } } - public JbootEhcacheImpl(CacheManager cacheManager) { - this.cacheManager = cacheManager; - } public CacheEventListener getCacheEventListener() { return cacheEventListener; diff --git a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java index 21cd4d81..9d886d8e 100644 --- a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java @@ -19,11 +19,12 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import com.jfinal.plugin.ehcache.IDataLoader; import io.jboot.Jboot; -import io.jboot.support.redis.JbootRedis; import io.jboot.components.cache.JbootCacheBase; +import io.jboot.components.cache.JbootCacheConfig; import io.jboot.components.cache.ehcache.JbootEhcacheImpl; import io.jboot.components.cache.redis.JbootRedisCacheImpl; import io.jboot.components.serializer.JbootSerializer; +import io.jboot.support.redis.JbootRedis; import io.jboot.utils.StrUtil; import net.sf.ehcache.CacheException; import net.sf.ehcache.Ehcache; @@ -57,11 +58,12 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL .build(key -> null); - public JbootEhredisCacheImpl() { - this.ehcacheImpl = new JbootEhcacheImpl(); + public JbootEhredisCacheImpl(JbootCacheConfig config) { + super(config); + this.ehcacheImpl = new JbootEhcacheImpl(config); this.ehcacheImpl.setCacheEventListener(this); - this.redisCacheImpl = new JbootRedisCacheImpl(); + this.redisCacheImpl = new JbootRedisCacheImpl(config); this.clientId = StrUtil.uuid(); this.serializer = Jboot.getSerializer(); diff --git a/src/main/java/io/jboot/components/cache/interceptor/Utils.java b/src/main/java/io/jboot/components/cache/interceptor/Utils.java index e22e7b86..592c962b 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/Utils.java +++ b/src/main/java/io/jboot/components/cache/interceptor/Utils.java @@ -16,9 +16,8 @@ package io.jboot.components.cache.interceptor; import com.jfinal.template.Engine; -import io.jboot.Jboot; import io.jboot.components.cache.AopCache; -import io.jboot.components.cache.JbootCacheConfig; +import io.jboot.components.cache.JbootAopCacheConfig; import io.jboot.components.cache.annotation.CacheEvict; import io.jboot.exception.JbootException; import io.jboot.utils.AnnotationUtil; @@ -194,10 +193,10 @@ class Utils { } } - private static final JbootCacheConfig CONFIG = Jboot.config(JbootCacheConfig.class); + private static final JbootAopCacheConfig CONFIG = JbootAopCacheConfig.getInstance(); static void putDataToCache(String cacheName, String cacheKey, Object data, int liveSeconds) { - liveSeconds = liveSeconds > 0 ? liveSeconds : CONFIG.getAopCacheLiveSeconds(); + liveSeconds = liveSeconds > 0 ? liveSeconds : CONFIG.getLiveSeconds(); if (liveSeconds > 0) { AopCache.put(cacheName, cacheKey, data, liveSeconds); } else { diff --git a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java index e47fadc5..0ba3819d 100644 --- a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java +++ b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java @@ -17,6 +17,7 @@ package io.jboot.components.cache.j2cache; import com.jfinal.plugin.ehcache.IDataLoader; import io.jboot.components.cache.JbootCacheBase; +import io.jboot.components.cache.JbootCacheConfig; import io.jboot.exception.JbootException; import net.oschina.j2cache.CacheChannel; import net.oschina.j2cache.CacheObject; @@ -34,6 +35,10 @@ import java.util.stream.Collectors; */ public class J2cacheImpl extends JbootCacheBase { + public J2cacheImpl(JbootCacheConfig config) { + super(config); + } + @Override public T get(String cacheName, Object key) { cacheName = buildCacheName(cacheName); diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java index 8cd32b2b..43cb294b 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java @@ -17,14 +17,17 @@ package io.jboot.components.cache.redis; import com.jfinal.plugin.ehcache.IDataLoader; import io.jboot.Jboot; -import io.jboot.support.redis.JbootRedis; -import io.jboot.support.redis.JbootRedisManager; import io.jboot.components.cache.JbootCacheBase; +import io.jboot.components.cache.JbootCacheConfig; import io.jboot.exception.JbootIllegalConfigException; +import io.jboot.support.redis.JbootRedis; +import io.jboot.support.redis.JbootRedisManager; import io.jboot.support.redis.RedisScanResult; import io.jboot.utils.StrUtil; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; public class JbootRedisCacheImpl extends JbootCacheBase { @@ -36,7 +39,8 @@ public class JbootRedisCacheImpl extends JbootCacheBase { private String globalKeyPrefix = ""; - public JbootRedisCacheImpl() { + public JbootRedisCacheImpl(JbootCacheConfig config) { + super(config); cacheConfig = Jboot.config(JbootRedisCacheConfig.class); if (StrUtil.isNotBlank(cacheConfig.getGlobalKeyPrefix())) { globalKeyPrefix = cacheConfig.getGlobalKeyPrefix() + ":"; diff --git a/src/test/java/io/jboot/test/ehcache/EhcacheTester.java b/src/test/java/io/jboot/test/ehcache/EhcacheTester.java index 0b1f1ea7..3cfb6b22 100644 --- a/src/test/java/io/jboot/test/ehcache/EhcacheTester.java +++ b/src/test/java/io/jboot/test/ehcache/EhcacheTester.java @@ -2,6 +2,7 @@ package io.jboot.test.ehcache; import io.jboot.components.cache.JbootCache; +import io.jboot.components.cache.JbootCacheConfig; import io.jboot.components.cache.ehcache.JbootEhcacheImpl; public class EhcacheTester { @@ -9,7 +10,7 @@ public class EhcacheTester { private static final String CACHE_NAME = "test"; public static void main(String[] args) throws Exception { - JbootCache cache = new JbootEhcacheImpl(); + JbootCache cache = new JbootEhcacheImpl(new JbootCacheConfig()); cache.put(CACHE_NAME, "key1", "value1"); cache.put(CACHE_NAME, "key2", "value2", 2); -- Gitee From 258f05119868371b99298a8ff4c0ae4b333e868f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 19 Nov 2021 11:58:26 +0800 Subject: [PATCH 1449/1965] =?UTF-8?q?=E7=A7=BB=E5=8A=A8=20AOP=20=E7=9A=84?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/cache/JbootAopCacheConfig.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java diff --git a/src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java b/src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java new file mode 100644 index 00000000..49bc6789 --- /dev/null +++ b/src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.cache; + + +import io.jboot.app.config.JbootConfigManager; +import io.jboot.app.config.annotation.ConfigModel; + + +@ConfigModel(prefix = "jboot.aop.cache") +public class JbootAopCacheConfig { + + // AOP 缓存的默认有效时间,0为永久有效,单位秒, + // 当 @Cacheable 和 @CachePut 注解不配置的时候默认用这个配置 + private int liveSeconds = 0; + private String useCacheName = "default"; + private String defaultCacheNamePrefix; + + public int getLiveSeconds() { + return liveSeconds; + } + + public void setLiveSeconds(int liveSeconds) { + this.liveSeconds = liveSeconds; + } + + public String getUseCacheName() { + return useCacheName; + } + + public void setUseCacheName(String useCacheName) { + this.useCacheName = useCacheName; + } + + public String getDefaultCacheNamePrefix() { + return defaultCacheNamePrefix; + } + + public void setDefaultCacheNamePrefix(String defaultCacheNamePrefix) { + this.defaultCacheNamePrefix = defaultCacheNamePrefix; + } + + private static JbootAopCacheConfig me; + + public static JbootAopCacheConfig getInstance() { + if (me == null) { + me = JbootConfigManager.me().get(JbootAopCacheConfig.class); + } + return me; + } + +} -- Gitee From 613b0684349e9edd9d77656a5e90863c02213426 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 19 Nov 2021 12:15:38 +0800 Subject: [PATCH 1450/1965] update docs --- doc/docs/mq.md | 53 ++++++++++++++++++- src/main/java/io/jboot/Jboot.java | 31 +++++++++++ .../io/jboot/components/mq/JbootmqConfig.java | 7 +++ .../jboot/components/mq/JbootmqManager.java | 2 + 4 files changed, 92 insertions(+), 1 deletion(-) diff --git a/doc/docs/mq.md b/doc/docs/mq.md index 692425bd..b2c8b376 100644 --- a/doc/docs/mq.md +++ b/doc/docs/mq.md @@ -1,5 +1,7 @@ # MQ 消息队列 +## 基本使用 + Jboot 内置了对MQ消息队列的功能支持,使用MQ需要以下几步步骤。 **第一步:配置jboot.properties文件,内容如下:** @@ -96,4 +98,53 @@ jboot.mq.rocket.consumerGroup = "jboot_default_consumer_group"; jboot.mq.rocket.consumeMessageBatchMaxSize jboot.mq.rocket.broadcastChannelPrefix "broadcast-"; jboot.mq.rocket.producerGroup "jboot_default_producer_group"; -``` \ No newline at end of file +``` + +## 多 MQ 实例 + +若在一个应用在,里面有多个 MQ 实例,假设有两个 redis 实例和两个 Rabbitmq 实例,配置如下: + +```properties +# 默认 MQ 及其类型 +jboot.mq.type=redis +jboot.mq.channel=channel1,channel2,myChannel + +jboot.mq.redis.host=127.0.0.1 +jboot.mq.redis.port=6379 +jboot.mq.redis.timeout=2000 + + +# 其他另一个 mq1 的类型 +jboot.mq.other1.type=redis +jboot.mq.other1.typeName=redis1 +jboot.mq.other1.channel=channel1,channel2.... + + +jboot.mq.redis.redis1.host=127.0.0.1 +jboot.mq.redis.redis1.port=6379 +jboot.mq.redis.redis1.timeout=2000 + + +# 其他另一个 mq2 的类型 +jboot.mq.other2.type=rabbitmq +jboot.mq.other2.typeName=rabbitmqaaa +jboot.mq.other2.channel=channel1,channel2.... + +jboot.mq.rabbitmq.rabbitmqaaa.username=guest +jboot.mq.rabbitmq.rabbitmqaaa.password=guest +jboot.mq.rabbitmq.rabbitmqaaa.host=127.0.0.1 +jboot.mq.rabbitmq.rabbitmqaaa.port=5672 + +# 其他另一个 mq3 的类型 +jboot.mq.other3.type=rabbitmq +jboot.mq.other3.typeName=rabbitmqbbb +jboot.mq.other3.channel=channel1,channel2.... + +jboot.mq.rabbitmq.rabbitmqbbb.username=guest +jboot.mq.rabbitmq.rabbitmqbbb.password=guest +jboot.mq.rabbitmq.rabbitmqbbb.host=127.0.0.1 +jboot.mq.rabbitmq.rabbitmqbbb.port=5672 + + +``` + diff --git a/src/main/java/io/jboot/Jboot.java b/src/main/java/io/jboot/Jboot.java index e07dfec9..4436d50b 100644 --- a/src/main/java/io/jboot/Jboot.java +++ b/src/main/java/io/jboot/Jboot.java @@ -57,6 +57,17 @@ public class Jboot { } + /** + * 获取指定的缓存 + * + * @param name + * @return + */ + public static JbootCache getCache(String name) { + return JbootCacheManager.me().getCache(name); + } + + /** * 获取 JbootRedis 工具类,方便操作Redis请求 * @@ -66,6 +77,16 @@ public class Jboot { return JbootRedisManager.me().getRedis(); } + /** + * 获取指定的 Redis + * + * @param name + * @return + */ + public static JbootRedis getRedis(String name) { + return JbootRedisManager.me().getRedis(name); + } + /** * 获取 MetricRegistry @@ -86,6 +107,16 @@ public class Jboot { return JbootmqManager.me().getJbootmq(); } + /** + * 获取指定的 MQ + * + * @param name + * @return + */ + public static Jbootmq getMq(String name) { + return JbootmqManager.me().getJbootmq(name); + } + /** * 获取序列化对象 diff --git a/src/main/java/io/jboot/components/mq/JbootmqConfig.java b/src/main/java/io/jboot/components/mq/JbootmqConfig.java index 8641c8bb..6fd4e72b 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqConfig.java +++ b/src/main/java/io/jboot/components/mq/JbootmqConfig.java @@ -15,9 +15,12 @@ */ package io.jboot.components.mq; +import com.google.common.collect.Sets; import io.jboot.app.config.annotation.ConfigModel; import io.jboot.utils.StrUtil; +import java.util.Set; + @ConfigModel(prefix = "jboot.mq") public class JbootmqConfig { @@ -29,6 +32,10 @@ public class JbootmqConfig { public static final String TYPE_QPID = "qpid"; public static final String TYPE_LOCAL = "local"; + public static final Set TYPES = Sets.newHashSet(TYPE_REDIS, TYPE_ACTIVEMQ, TYPE_ALIYUNMQ, TYPE_RABBITMQ + , TYPE_ROCKETMQ, TYPE_QPID, TYPE_LOCAL); + + private String name = "default"; // MQ 的名称,可以配置多个 MQ 实例,但是需要名称不能一样 private String type; // MQ 的类型: redis、rocketmq 等 private String typeName; // MQ 相同的类型,可能有多一个实例,比如两个 redis,此时需要配置示例的名称 diff --git a/src/main/java/io/jboot/components/mq/JbootmqManager.java b/src/main/java/io/jboot/components/mq/JbootmqManager.java index 35977a53..2202522e 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqManager.java +++ b/src/main/java/io/jboot/components/mq/JbootmqManager.java @@ -67,6 +67,8 @@ public class JbootmqManager { mq = jbootmqMap.get(name); if (mq == null) { Map configModels = JbootConfigUtil.getConfigModels(JbootmqConfig.class, "jboot.mq"); + JbootmqConfig.TYPES.forEach(configModels::remove); + if (!configModels.containsKey(name)) { throw new JbootIllegalConfigException("Please config \"jboot.mq." + name + ".type\" in your jboot.properties."); } -- Gitee From e9311f64cd419aa31423b886f53c3aa035b2b76b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 19 Nov 2021 12:30:33 +0800 Subject: [PATCH 1451/1965] optimize --- .../jboot/components/cache/JbootCacheManager.java | 4 +++- .../java/io/jboot/db/model/JbootModelConfig.java | 14 ++++---------- src/main/java/io/jboot/utils/CacheUtil.java | 4 ++-- .../web/session/JbootServletRequestWrapper.java | 8 ++------ .../io/jboot/web/session/JbootSessionConfig.java | 12 +++++------- 5 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/JbootCacheManager.java b/src/main/java/io/jboot/components/cache/JbootCacheManager.java index 029b299e..fdd77d0f 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheManager.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheManager.java @@ -64,7 +64,9 @@ public class JbootCacheManager { Map configModels = JbootConfigUtil.getConfigModels(JbootCacheConfig.class, "jboot.cache"); JbootCacheConfig.TYPES.forEach(configModels::remove); - if (!configModels.containsKey(name)) { + configModels.putIfAbsent("default",new JbootCacheConfig()); + + if ( !configModels.containsKey(name)) { throw new JbootIllegalConfigException("Please config \"jboot.cache." + name + ".type\" in your jboot.properties."); } diff --git a/src/main/java/io/jboot/db/model/JbootModelConfig.java b/src/main/java/io/jboot/db/model/JbootModelConfig.java index 198750e9..70dae079 100644 --- a/src/main/java/io/jboot/db/model/JbootModelConfig.java +++ b/src/main/java/io/jboot/db/model/JbootModelConfig.java @@ -18,7 +18,6 @@ package io.jboot.db.model; import io.jboot.Jboot; import io.jboot.app.config.annotation.ConfigModel; import io.jboot.components.cache.JbootCache; -import io.jboot.components.cache.JbootCacheConfig; import io.jboot.components.cache.JbootCacheManager; import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; @@ -65,7 +64,7 @@ public class JbootModelConfig { private boolean idCacheByCopyEnable = true; - private String idCacheType = Jboot.config(JbootCacheConfig.class).getType(); + private String idCacheName = "default"; @@ -141,15 +140,10 @@ public class JbootModelConfig { this.idCacheByCopyEnable = idCacheByCopyEnable; } - public String getIdCacheType() { - return idCacheType; + public JbootModelConfig(String idCacheName) { + this.idCacheName = idCacheName; } - public void setIdCacheType(String idCacheType) { - this.idCacheType = idCacheType; - } - - private JbootModelFilter filter; public JbootModelFilter getFilter() { if (filter == null){ @@ -206,7 +200,7 @@ public class JbootModelConfig { public JbootCache getIdCache() { if (idCache == null) { - idCache = JbootCacheManager.me().getCache(idCacheType); + idCache = JbootCacheManager.me().getCache(idCacheName); } return idCache; } diff --git a/src/main/java/io/jboot/utils/CacheUtil.java b/src/main/java/io/jboot/utils/CacheUtil.java index 678a24da..3af1a526 100644 --- a/src/main/java/io/jboot/utils/CacheUtil.java +++ b/src/main/java/io/jboot/utils/CacheUtil.java @@ -29,8 +29,8 @@ import java.util.List; */ public class CacheUtil { - public static JbootCache useType(String type) { - return JbootCacheManager.me().getCache(type); + public static JbootCache use(String name) { + return JbootCacheManager.me().getCache(name); } public static JbootCache setCurrentPrefix(String cacheNamePrefix) { diff --git a/src/main/java/io/jboot/web/session/JbootServletRequestWrapper.java b/src/main/java/io/jboot/web/session/JbootServletRequestWrapper.java index c480f4a7..cd67f13f 100644 --- a/src/main/java/io/jboot/web/session/JbootServletRequestWrapper.java +++ b/src/main/java/io/jboot/web/session/JbootServletRequestWrapper.java @@ -17,9 +17,7 @@ package io.jboot.web.session; import io.jboot.Jboot; import io.jboot.components.cache.JbootCache; -import io.jboot.components.cache.JbootCacheConfig; import io.jboot.components.cache.JbootCacheManager; -import io.jboot.utils.StrUtil; import javax.servlet.http.*; import java.util.Enumeration; @@ -38,12 +36,10 @@ public class JbootServletRequestWrapper extends HttpServletRequestWrapper { private static String cookieDomain = config.getCookieDomain(); private static int cookieMaxAge = config.getCookieMaxAge(); private static String cacheName = config.getCacheName(); - private static String cacheType = config.getCacheType(); + private static String useCacheName = config.getUseCacheName(); private static JbootCache jbootCache = JbootCacheManager.me() - .getCache(StrUtil.isBlank(cacheType) || JbootCacheConfig.TYPE_NONE.equals(cacheType) - ? JbootCacheConfig.TYPE_CAFFEINE - : cacheType); + .getCache(useCacheName); private HttpServletResponse response; diff --git a/src/main/java/io/jboot/web/session/JbootSessionConfig.java b/src/main/java/io/jboot/web/session/JbootSessionConfig.java index 33e9c6d8..b24b6e2d 100644 --- a/src/main/java/io/jboot/web/session/JbootSessionConfig.java +++ b/src/main/java/io/jboot/web/session/JbootSessionConfig.java @@ -15,9 +15,7 @@ */ package io.jboot.web.session; -import io.jboot.Jboot; import io.jboot.app.config.annotation.ConfigModel; -import io.jboot.components.cache.JbootCacheConfig; @ConfigModel(prefix = "jboot.web.session") public class JbootSessionConfig { @@ -36,7 +34,7 @@ public class JbootSessionConfig { private int cookieMaxAge = DEFAULT_COOKIE_MAX_AGE; private String cacheName = DEFAULT_SESSION_CACHE_NAME; - private String cacheType = Jboot.config(JbootCacheConfig.class).getType(); + private String useCacheName = "default"; public String getCookieName() { @@ -87,12 +85,12 @@ public class JbootSessionConfig { this.cacheName = cacheName; } - public String getCacheType() { - return cacheType; + public String getUseCacheName() { + return useCacheName; } - public void setCacheType(String cacheType) { - this.cacheType = cacheType; + public void setUseCacheName(String useCacheName) { + this.useCacheName = useCacheName; } } -- Gitee From f5b54e9e03c45db13616bee7f4ef2459d347074f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 19 Nov 2021 14:16:54 +0800 Subject: [PATCH 1452/1965] optimize --- .../components/cache/JbootCacheManager.java | 6 ++-- .../io/jboot/components/mq/JbootmqBase.java | 5 ++-- .../jboot/components/mq/JbootmqManager.java | 14 +++++----- .../components/mq/activemq/JbootActivemq.java | 5 ++++ .../mq/aliyunmq/JbootAliyunmqImpl.java | 5 ++-- .../components/mq/local/JbootLocalmqImpl.java | 5 ++++ .../components/mq/qpidmq/JbootQpidmqImpl.java | 5 ++-- .../mq/rabbitmq/JbootRabbitmqImpl.java | 5 ++-- .../mq/redismq/JbootRedismqImpl.java | 5 ++-- .../mq/rocketmq/JbootRocketmqImpl.java | 5 ++-- .../io/jboot/core/spi/JbootSpiLoader.java | 28 +++++++++++++++++++ .../io/jboot/test/mq/redis/RedisMqSender.java | 16 ++++++++--- 12 files changed, 78 insertions(+), 26 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/JbootCacheManager.java b/src/main/java/io/jboot/components/cache/JbootCacheManager.java index fdd77d0f..2be20edc 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheManager.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheManager.java @@ -64,9 +64,9 @@ public class JbootCacheManager { Map configModels = JbootConfigUtil.getConfigModels(JbootCacheConfig.class, "jboot.cache"); JbootCacheConfig.TYPES.forEach(configModels::remove); - configModels.putIfAbsent("default",new JbootCacheConfig()); + configModels.putIfAbsent("default", new JbootCacheConfig()); - if ( !configModels.containsKey(name)) { + if (!configModels.containsKey(name)) { throw new JbootIllegalConfigException("Please config \"jboot.cache." + name + ".type\" in your jboot.properties."); } @@ -99,7 +99,7 @@ public class JbootCacheManager { case JbootCacheConfig.TYPE_NONE: return new NoneCacheImpl(); default: - return JbootSpiLoader.load(JbootCache.class, config.getType()); + return JbootSpiLoader.load(JbootCache.class, config.getType(), config); } } } diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index 95acf120..e16255a3 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -38,7 +38,7 @@ public abstract class JbootmqBase implements Jbootmq { private List globalListeners = new CopyOnWriteArrayList<>(); private Multimap channelListeners = ArrayListMultimap.create(); - protected JbootmqConfig config = Jboot.config(JbootmqConfig.class); + protected final JbootmqConfig config ; protected Set channels = Sets.newHashSet(); protected Set syncRecevieMessageChannels = Sets.newHashSet(); @@ -50,7 +50,8 @@ public abstract class JbootmqBase implements Jbootmq { new SynchronousQueue<>(), new NamedThreadFactory("jbootmq")); - public JbootmqBase() { + public JbootmqBase(JbootmqConfig config) { + this.config = config; String channelString = config.getChannel(); if (StrUtil.isBlank(channelString)) { return; diff --git a/src/main/java/io/jboot/components/mq/JbootmqManager.java b/src/main/java/io/jboot/components/mq/JbootmqManager.java index 2202522e..17f6389c 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqManager.java +++ b/src/main/java/io/jboot/components/mq/JbootmqManager.java @@ -96,21 +96,21 @@ public class JbootmqManager { switch (config.getType()) { case JbootmqConfig.TYPE_REDIS: - return new JbootRedismqImpl(); + return new JbootRedismqImpl(config); case JbootmqConfig.TYPE_ALIYUNMQ: - return new JbootAliyunmqImpl(); + return new JbootAliyunmqImpl(config); case JbootmqConfig.TYPE_RABBITMQ: - return new JbootRabbitmqImpl(); + return new JbootRabbitmqImpl(config); case JbootmqConfig.TYPE_ROCKETMQ: - return new JbootRocketmqImpl(); + return new JbootRocketmqImpl(config); case JbootmqConfig.TYPE_QPID: - return new JbootQpidmqImpl(); + return new JbootQpidmqImpl(config); case JbootmqConfig.TYPE_ACTIVEMQ: throw new RuntimeException("not finished!!!!"); case JbootmqConfig.TYPE_LOCAL: - return new JbootLocalmqImpl(); + return new JbootLocalmqImpl(config); default: - return JbootSpiLoader.load(Jbootmq.class, config.getType()); + return JbootSpiLoader.load(Jbootmq.class, config.getType(), config); } } diff --git a/src/main/java/io/jboot/components/mq/activemq/JbootActivemq.java b/src/main/java/io/jboot/components/mq/activemq/JbootActivemq.java index 4996a4bb..a62ecadf 100644 --- a/src/main/java/io/jboot/components/mq/activemq/JbootActivemq.java +++ b/src/main/java/io/jboot/components/mq/activemq/JbootActivemq.java @@ -17,10 +17,15 @@ package io.jboot.components.mq.activemq; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; +import io.jboot.components.mq.JbootmqConfig; public class JbootActivemq extends JbootmqBase implements Jbootmq { + public JbootActivemq(JbootmqConfig config) { + super(config); + } + @Override public void enqueue(Object message, String toChannel) { diff --git a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java index 76ae76cb..ce35091a 100644 --- a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java +++ b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java @@ -20,6 +20,7 @@ import io.jboot.Jboot; import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; +import io.jboot.components.mq.JbootmqConfig; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.StrUtil; @@ -33,8 +34,8 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { private Consumer consumer; private JbootAliyunmqConfig aliyunmqConfig; - public JbootAliyunmqImpl() { - super(); + public JbootAliyunmqImpl(JbootmqConfig config) { + super(config); String typeName = config.getTypeName(); if (StrUtil.isNotBlank(typeName)) { Map configModels = JbootConfigUtil.getConfigModels(JbootAliyunmqConfig.class, "jboot.mq.aliyun"); diff --git a/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java b/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java index 2b898f0b..3639cb59 100644 --- a/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java +++ b/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java @@ -16,6 +16,7 @@ package io.jboot.components.mq.local; import io.jboot.components.mq.JbootmqBase; +import io.jboot.components.mq.JbootmqConfig; /** * @author michael yang (fuhai999@gmail.com) @@ -23,6 +24,10 @@ import io.jboot.components.mq.JbootmqBase; */ public class JbootLocalmqImpl extends JbootmqBase { + public JbootLocalmqImpl(JbootmqConfig config) { + super(config); + } + @Override protected void onStartListening() { //do nothing diff --git a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java index 83168762..1be75dac 100644 --- a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java +++ b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java @@ -20,6 +20,7 @@ import io.jboot.Jboot; import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; +import io.jboot.components.mq.JbootmqConfig; import io.jboot.exception.JbootException; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.ArrayUtil; @@ -42,8 +43,8 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { private Connection connection = null; private boolean serializerEnable = true; - public JbootQpidmqImpl() { - super(); + public JbootQpidmqImpl(JbootmqConfig config) { + super(config); JbootQpidmqConfig qpidConfig = null; String typeName = config.getTypeName(); if (StrUtil.isNotBlank(typeName)) { diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index c727377d..8c022c90 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -21,6 +21,7 @@ import io.jboot.app.JbootApplicationConfig; import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; +import io.jboot.components.mq.JbootmqConfig; import io.jboot.exception.JbootException; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.StrUtil; @@ -42,8 +43,8 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { private JbootApplicationConfig appConfig; - public JbootRabbitmqImpl() { - super(); + public JbootRabbitmqImpl(JbootmqConfig config) { + super(config); String typeName = config.getTypeName(); if (StrUtil.isNotBlank(typeName)) { diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java index 8c6d3c5c..1078184e 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java @@ -20,6 +20,7 @@ import io.jboot.Jboot; import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; +import io.jboot.components.mq.JbootmqConfig; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.support.redis.JbootRedis; import io.jboot.support.redis.JbootRedisManager; @@ -36,8 +37,8 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { private JbootRedis redis; private Thread dequeueThread; - public JbootRedismqImpl() { - super(); + public JbootRedismqImpl(JbootmqConfig config) { + super(config); JbootRedismqConfig redisConfig = null; String typeName = config.getTypeName(); diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java index cd56d6ce..4d99b79d 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java @@ -20,6 +20,7 @@ import io.jboot.Jboot; import io.jboot.app.config.JbootConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; +import io.jboot.components.mq.JbootmqConfig; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.StrUtil; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; @@ -43,8 +44,8 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { private JbootRocketmqConfig rocketmqConfig; private MQProducer mqProducer; - public JbootRocketmqImpl() { - super(); + public JbootRocketmqImpl(JbootmqConfig config) { + super(config); String typeName = config.getTypeName(); if (StrUtil.isNotBlank(typeName)) { diff --git a/src/main/java/io/jboot/core/spi/JbootSpiLoader.java b/src/main/java/io/jboot/core/spi/JbootSpiLoader.java index fe9a0d10..5584a46a 100644 --- a/src/main/java/io/jboot/core/spi/JbootSpiLoader.java +++ b/src/main/java/io/jboot/core/spi/JbootSpiLoader.java @@ -40,6 +40,34 @@ import java.util.ServiceLoader; public class JbootSpiLoader { + /** + * 通过 SPI 去加载相应的扩展子类 + * + * @param clazz + * @param spiName + * @param + * @return + */ + public static T load(Class clazz, String spiName, Object... paras) { + List> classes = ClassScanner.scanSubClass(clazz, true); + if (classes.isEmpty()) { + return null; + } + + for (Class c : classes) { + JbootSpi spiConfig = c.getAnnotation(JbootSpi.class); + if (spiConfig != null && spiName.equals(AnnotationUtil.get(spiConfig.value()))) { + return ClassUtil.newInstance(c, paras); + } + //support config class name + else if (spiName.equals(c.getName())) { + return ClassUtil.newInstance(c, paras); + } + } + + return null; + } + /** * 通过 SPI 去加载相应的扩展子类 * diff --git a/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java b/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java index 45267dc2..8d67da1f 100644 --- a/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java +++ b/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java @@ -11,26 +11,34 @@ public class RedisMqSender { public static void main(String[] args) throws InterruptedException { //Undertow端口号配置 - JbootApplication.setBootArg("undertow.port", "8000"); + JbootApplication.setBootArg("undertow.port", "8659"); //设置 mq 的相关信息 JbootApplication.setBootArg("jboot.mq.type", "redis"); JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); +// JbootApplication.setBootArg("jboot.mq.type", "redis"); +// JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); + + JbootApplication.setBootArg("jboot.mq.other1.type", "rocketmq"); + JbootApplication.setBootArg("jboot.mq.other1.channel", "channel1,channel2,myChannel"); + JbootApplication.setBootArg("jboot.mq.other1.typeName", "rktmq"); + JbootApplication.setBootArg("jboot.mq.rocket.rktmq.namesrvAddr", "127.0.0.1:9876"); + //启动应用程序 JbootApplication.run(args); - while (true) { +// while (true) { Jboot.getMq().publish("message from RedisMqSender", "channel1"); Jboot.getMq().publish("message from RedisMqSender", "channel2"); Jboot.getMq().publish("message from RedisMqSender", "myChannel"); - Jboot.getMq().enqueue("message from RedisMqSender by enqueue : " + UUID.randomUUID(), "channel1"); + Jboot.getMq("other1").enqueue("message from RedisMqSender by enqueue : " + UUID.randomUUID(), "channel1"); Thread.sleep(2000); System.out.println("jboot mq publish success..."); - } +// } } -- Gitee From 1307a68d63d562f678f2fa533b8762c7bea88e06 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 21 Nov 2021 11:38:09 +0800 Subject: [PATCH 1453/1965] =?UTF-8?q?=E4=B8=BA=20rocketmq=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=9B=B4=E5=A4=9A=E7=9A=84=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mq/rocketmq/JbootRocketmqConfig.java | 92 +++++++++++++++++++ .../mq/rocketmq/JbootRocketmqImpl.java | 42 ++++++++- 2 files changed, 133 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqConfig.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqConfig.java index c6f98d83..88684a5e 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqConfig.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqConfig.java @@ -30,6 +30,18 @@ public class JbootRocketmqConfig implements Serializable { private String broadcastChannelPrefix = "broadcast-"; private String producerGroup = "jboot_default_producer_group"; + private String instanceName; + private String clientIP; + private String createTopicKey; + private Boolean useTLS; + + private Boolean sendLatencyFaultEnable; + private Boolean sendMessageWithVIPChannel; + private Integer sendMsgTimeout; + + private Boolean retryAnotherBrokerWhenNotStoreOK; + private Integer retryTimesWhenSendAsyncFailed; + private Integer retryTimesWhenSendFailed; public String getNamesrvAddr() { return namesrvAddr; @@ -78,4 +90,84 @@ public class JbootRocketmqConfig implements Serializable { public void setProducerGroup(String producerGroup) { this.producerGroup = producerGroup; } + + public String getInstanceName() { + return instanceName; + } + + public void setInstanceName(String instanceName) { + this.instanceName = instanceName; + } + + public String getClientIP() { + return clientIP; + } + + public void setClientIP(String clientIP) { + this.clientIP = clientIP; + } + + public String getCreateTopicKey() { + return createTopicKey; + } + + public void setCreateTopicKey(String createTopicKey) { + this.createTopicKey = createTopicKey; + } + + public Boolean getUseTLS() { + return useTLS; + } + + public void setUseTLS(Boolean useTLS) { + this.useTLS = useTLS; + } + + public Boolean getSendLatencyFaultEnable() { + return sendLatencyFaultEnable; + } + + public void setSendLatencyFaultEnable(Boolean sendLatencyFaultEnable) { + this.sendLatencyFaultEnable = sendLatencyFaultEnable; + } + + public Boolean getSendMessageWithVIPChannel() { + return sendMessageWithVIPChannel; + } + + public void setSendMessageWithVIPChannel(Boolean sendMessageWithVIPChannel) { + this.sendMessageWithVIPChannel = sendMessageWithVIPChannel; + } + + public Integer getSendMsgTimeout() { + return sendMsgTimeout; + } + + public void setSendMsgTimeout(Integer sendMsgTimeout) { + this.sendMsgTimeout = sendMsgTimeout; + } + + public Boolean getRetryAnotherBrokerWhenNotStoreOK() { + return retryAnotherBrokerWhenNotStoreOK; + } + + public void setRetryAnotherBrokerWhenNotStoreOK(Boolean retryAnotherBrokerWhenNotStoreOK) { + this.retryAnotherBrokerWhenNotStoreOK = retryAnotherBrokerWhenNotStoreOK; + } + + public Integer getRetryTimesWhenSendAsyncFailed() { + return retryTimesWhenSendAsyncFailed; + } + + public void setRetryTimesWhenSendAsyncFailed(Integer retryTimesWhenSendAsyncFailed) { + this.retryTimesWhenSendAsyncFailed = retryTimesWhenSendAsyncFailed; + } + + public Integer getRetryTimesWhenSendFailed() { + return retryTimesWhenSendFailed; + } + + public void setRetryTimesWhenSendFailed(Integer retryTimesWhenSendFailed) { + this.retryTimesWhenSendFailed = retryTimesWhenSendFailed; + } } diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java index 4d99b79d..8abb75ed 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java @@ -203,8 +203,48 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { producer.setNamespace(rocketmqConfig.getNamespace()); } - producer.start(); + if (StrUtil.isNotBlank(rocketmqConfig.getInstanceName())) { + producer.setInstanceName(rocketmqConfig.getInstanceName()); + } + + if (StrUtil.isNotBlank(rocketmqConfig.getClientIP())) { + producer.setClientIP(rocketmqConfig.getClientIP()); + } + + if (StrUtil.isNotBlank(rocketmqConfig.getCreateTopicKey())) { + producer.setCreateTopicKey(rocketmqConfig.getCreateTopicKey()); + } + + if (rocketmqConfig.getUseTLS() != null) { + producer.setUseTLS(rocketmqConfig.getUseTLS()); + } + + if (rocketmqConfig.getSendLatencyFaultEnable() != null) { + producer.setSendLatencyFaultEnable(rocketmqConfig.getSendLatencyFaultEnable()); + } + + if (rocketmqConfig.getSendMessageWithVIPChannel() != null) { + producer.setSendMessageWithVIPChannel(rocketmqConfig.getSendMessageWithVIPChannel()); + } + + if (rocketmqConfig.getSendMsgTimeout() != null) { + producer.setSendMsgTimeout(rocketmqConfig.getSendMsgTimeout()); + } + + if (rocketmqConfig.getRetryAnotherBrokerWhenNotStoreOK() != null) { + producer.setRetryAnotherBrokerWhenNotStoreOK(rocketmqConfig.getRetryAnotherBrokerWhenNotStoreOK()); + } + + if (rocketmqConfig.getRetryTimesWhenSendAsyncFailed() != null) { + producer.setRetryTimesWhenSendAsyncFailed(rocketmqConfig.getRetryTimesWhenSendAsyncFailed()); + } + + if (rocketmqConfig.getRetryTimesWhenSendFailed() != null) { + producer.setRetryTimesWhenSendFailed(rocketmqConfig.getRetryTimesWhenSendFailed()); + } + mqProducer = producer; + producer.start(); } } -- Gitee From 2022dae04530ee3f971ad4deb55a723b91f18dbe Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 21 Nov 2021 12:18:38 +0800 Subject: [PATCH 1454/1965] rename JbootConfigUtil to ConfigUtil --- .../java/io/jboot/aop/JbootAopFactory.java | 4 +- .../{ConfigUtil.java => JbootConfigKit.java} | 54 ++----------------- .../jboot/app/config/JbootConfigManager.java | 52 +++++++++--------- .../java/io/jboot/app/config/JbootProp.java | 2 +- .../support/apollo/ApolloConfigManager.java | 4 +- .../support/apollo/ApolloServerConfig.java | 4 +- .../support/nacos/NacosConfigManager.java | 4 +- .../support/nacos/NacosServerConfig.java | 4 +- .../components/cache/JbootCacheManager.java | 4 +- .../gateway/JbootGatewayManager.java | 4 +- .../discovery/GatewayDiscoveryManager.java | 4 +- .../jboot/components/mq/JbootmqManager.java | 4 +- .../mq/aliyunmq/JbootAliyunmqImpl.java | 4 +- .../components/mq/qpidmq/JbootQpidmqImpl.java | 4 +- .../mq/rabbitmq/JbootRabbitmqImpl.java | 4 +- .../mq/redismq/JbootRedismqImpl.java | 4 +- .../mq/rocketmq/JbootRocketmqImpl.java | 4 +- .../jboot/components/rpc/dubbo/DubboUtil.java | 4 +- .../jboot/components/rpc/motan/MotanUtil.java | 4 +- .../datasource/DataSourceConfigManager.java | 4 +- .../support/redis/JbootRedisManager.java | 4 +- .../java/io/jboot/utils/AnnotationUtil.java | 4 +- .../ConfigUtil.java} | 32 +++++++---- .../jboot/test/config/ConfigController.java | 4 +- 24 files changed, 92 insertions(+), 128 deletions(-) rename src/main/java/io/jboot/app/config/{ConfigUtil.java => JbootConfigKit.java} (84%) rename src/main/java/io/jboot/{app/config/JbootConfigUtil.java => utils/ConfigUtil.java} (70%) diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 89b2c598..b15be315 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -24,7 +24,7 @@ import com.jfinal.proxy.Proxy; import com.jfinal.proxy.ProxyManager; import io.jboot.aop.annotation.*; import io.jboot.aop.cglib.JbootCglibProxyFactory; -import io.jboot.app.config.ConfigUtil; +import io.jboot.app.config.JbootConfigKit; import io.jboot.app.config.JbootConfigManager; import io.jboot.app.config.annotation.ConfigModel; import io.jboot.components.event.JbootEventListener; @@ -288,7 +288,7 @@ public class JbootAopFactory extends AopFactory { Object fieldObject = null; if (StrUtil.isNotBlank(value)) { - fieldObject = ConfigUtil.convert(fieldInjectedClass, value, field.getGenericType()); + fieldObject = JbootConfigKit.convert(fieldInjectedClass, value, field.getGenericType()); } if (fieldObject == null) { diff --git a/src/main/java/io/jboot/app/config/ConfigUtil.java b/src/main/java/io/jboot/app/config/JbootConfigKit.java similarity index 84% rename from src/main/java/io/jboot/app/config/ConfigUtil.java rename to src/main/java/io/jboot/app/config/JbootConfigKit.java index 7a76f597..409903a6 100644 --- a/src/main/java/io/jboot/app/config/ConfigUtil.java +++ b/src/main/java/io/jboot/app/config/JbootConfigKit.java @@ -16,14 +16,12 @@ package io.jboot.app.config; - import java.io.File; -import java.io.UnsupportedEncodingException; import java.lang.reflect.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -public class ConfigUtil { +public class JbootConfigKit { public static T newInstance(Class clazz) { @@ -55,7 +53,7 @@ public class ConfigUtil { para.setStart(index - 1); } else if (c == '}' && para != null) { para.setEnd(index); - if (paras == null){ + if (paras == null) { paras = new LinkedList<>(); } paras.add(para); @@ -159,21 +157,9 @@ public class ConfigUtil { return true; } - public static String map2string(Map map) { - if (map == null || map.isEmpty()) { - return "{ }"; - } - - StringJoiner joiner = new StringJoiner(", ", "{ ", " }"); - for (Object key : map.keySet()) { - joiner.add(key + "='" + map.get(key) + "'"); - } - return joiner.toString(); - } - static Properties readExternalProperties() { - String currentJarFilePath = ConfigUtil.class.getProtectionDomain().getCodeSource().getLocation().getFile(); + String currentJarFilePath = JbootConfigKit.class.getProtectionDomain().getCodeSource().getLocation().getFile(); File fileDir = new File(currentJarFilePath).getParentFile(); File externalProperties = new File(fileDir, "jboot.properties"); if (externalProperties.exists()) { @@ -183,43 +169,11 @@ public class ConfigUtil { } - private static String rootClassPath; - - public static String getRootClassPath() { - if (rootClassPath == null) { - try { - String path = getClassLoader().getResource("").toURI().getPath(); - rootClassPath = new File(path).getAbsolutePath(); - } catch (Exception e) { - try { - String path = ConfigUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath(); - path = java.net.URLDecoder.decode(path, "UTF-8"); - if (path.endsWith(File.separator)) { - path = path.substring(0, path.length() - 1); - } - /** - * Fix path 带有文件名 - */ - if (path.endsWith(".jar")) { - path = path.substring(0, path.lastIndexOf("/") + 1); - } - rootClassPath = path; - } catch (UnsupportedEncodingException e1) { - throw new RuntimeException(e1); - } - } - } - return rootClassPath; - } - - public static ClassLoader getClassLoader() { ClassLoader ret = Thread.currentThread().getContextClassLoader(); - return ret != null ? ret : ConfigUtil.class.getClassLoader(); + return ret != null ? ret : JbootConfigKit.class.getClassLoader(); } - public static void doNothing(Throwable ex) { - } public static Object convert(Class convertClass, String s, Type genericType) { diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index f737a9e1..d51e9543 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -70,12 +70,12 @@ public class JbootConfigManager { String mode = getConfigValue("jboot.app.mode"); - if (ConfigUtil.isNotBlank(mode)) { + if (JbootConfigKit.isNotBlank(mode)) { String modePropertiesName = "jboot-" + mode + ".properties"; mainProperties.putAll(new JbootProp(modePropertiesName).getProperties()); } - Properties externalProperties = ConfigUtil.readExternalProperties(); + Properties externalProperties = JbootConfigKit.readExternalProperties(); if (externalProperties != null && !externalProperties.isEmpty()) { mainProperties.putAll(externalProperties); } @@ -93,11 +93,11 @@ public class JbootConfigManager { } public T get(Class clazz) { - ConfigModel propertyConfig = clazz.getAnnotation(ConfigModel.class); - if (propertyConfig == null) { + ConfigModel configModel = clazz.getAnnotation(ConfigModel.class); + if (configModel == null) { return get(clazz, null, null); } - return get(clazz, propertyConfig.prefix(), propertyConfig.file()); + return get(clazz, configModel.prefix(), configModel.file()); } @@ -142,11 +142,11 @@ public class JbootConfigManager { * @return */ public T refreshAndGet(Class clazz) { - ConfigModel propertyConfig = clazz.getAnnotation(ConfigModel.class); - if (propertyConfig == null) { + ConfigModel configModel = clazz.getAnnotation(ConfigModel.class); + if (configModel == null) { return refreshAndGet(clazz, null, null); } - return refreshAndGet(clazz, propertyConfig.prefix(), propertyConfig.file()); + return refreshAndGet(clazz, configModel.prefix(), configModel.file()); } @@ -174,7 +174,7 @@ public class JbootConfigManager { mainProperties.putAll(properties); String mode = getConfigValue(properties, "jboot.app.mode"); - if (ConfigUtil.isNotBlank(mode)) { + if (JbootConfigKit.isNotBlank(mode)) { String modePropertiesName = "jboot-" + mode + ".properties"; mainProperties.putAll(new JbootProp(modePropertiesName).getProperties()); } @@ -191,21 +191,21 @@ public class JbootConfigManager { * @return */ public T createConfigObject(Class clazz, String prefix, String file) { - T configObject = ConfigUtil.newInstance(clazz); - for (Method setterMethod : ConfigUtil.getClassSetMethods(clazz)) { + T configObject = JbootConfigKit.newInstance(clazz); + for (Method setterMethod : JbootConfigKit.getClassSetMethods(clazz)) { String key = buildKey(prefix, setterMethod); String value = getConfigValue(key); - if (ConfigUtil.isNotBlank(file)) { + if (JbootConfigKit.isNotBlank(file)) { JbootProp prop = new JbootProp(file); String filePropValue = getConfigValue(prop.getProperties(), key); - if (ConfigUtil.isNotBlank(filePropValue)) { + if (JbootConfigKit.isNotBlank(filePropValue)) { value = filePropValue; } } - if (ConfigUtil.isNotBlank(value)) { - Object val = ConfigUtil.convert(setterMethod.getParameterTypes()[0], value, setterMethod.getGenericParameterTypes()[0]); + if (JbootConfigKit.isNotBlank(value)) { + Object val = JbootConfigKit.convert(setterMethod.getParameterTypes()[0], value, setterMethod.getGenericParameterTypes()[0]); if (val != null) { try { setterMethod.invoke(configObject, val); @@ -221,8 +221,8 @@ public class JbootConfigManager { private String buildKey(String prefix, Method method) { - String key = ConfigUtil.firstCharToLowerCase(method.getName().substring(3)); - if (ConfigUtil.isNotBlank(prefix)) { + String key = JbootConfigKit.firstCharToLowerCase(method.getName().substring(3)); + if (JbootConfigKit.isNotBlank(prefix)) { key = prefix.trim() + "." + key; } return key; @@ -235,13 +235,13 @@ public class JbootConfigManager { public String getConfigValue(Properties properties, String key) { - if (ConfigUtil.isBlank(key)) { + if (JbootConfigKit.isBlank(key)) { return ""; } String originalValue = getOriginalConfigValue(properties, key); String stringValue = decryptor != null ? decryptor.decrypt(key, originalValue) : originalValue; - return ConfigUtil.parseValue(stringValue); + return JbootConfigKit.parseValue(stringValue); } @@ -258,20 +258,20 @@ public class JbootConfigManager { //优先读取分布式配置内容 if (remoteProperties != null) { value = (String) remoteProperties.get(key); - if (ConfigUtil.isNotBlank(value)) { + if (JbootConfigKit.isNotBlank(value)) { return value.trim(); } } //boot arg value = getBootArg(key); - if (ConfigUtil.isNotBlank(value)) { + if (JbootConfigKit.isNotBlank(value)) { return value.trim(); } //env value = System.getenv(key); - if (ConfigUtil.isNotBlank(value)) { + if (JbootConfigKit.isNotBlank(value)) { return value.trim(); } @@ -280,19 +280,19 @@ public class JbootConfigManager { // 例如:jboot.datasource.url 转换为 JBOOT_DATASOURCE_URL String tempKey = key.toUpperCase().replace('.', '_'); value = System.getenv(tempKey); - if (ConfigUtil.isNotBlank(value)) { + if (JbootConfigKit.isNotBlank(value)) { return value.trim(); } //system property value = System.getProperty(key); - if (ConfigUtil.isNotBlank(value)) { + if (JbootConfigKit.isNotBlank(value)) { return value.trim(); } //user properties value = (String) properties.get(key); - if (ConfigUtil.isNotBlank(value)) { + if (JbootConfigKit.isNotBlank(value)) { return value.trim(); } @@ -377,7 +377,7 @@ public class JbootConfigManager { } String prefix = configModel.prefix(); - List setterMethods = ConfigUtil.getClassSetMethods(forClass); + List setterMethods = JbootConfigKit.getClassSetMethods(forClass); if (setterMethods != null) { for (Method setterMethod : setterMethods) { String key = buildKey(prefix, setterMethod); diff --git a/src/main/java/io/jboot/app/config/JbootProp.java b/src/main/java/io/jboot/app/config/JbootProp.java index eb17ab97..5d5b9b1b 100644 --- a/src/main/java/io/jboot/app/config/JbootProp.java +++ b/src/main/java/io/jboot/app/config/JbootProp.java @@ -31,7 +31,7 @@ class JbootProp { properties = new Properties(); InputStream inputStream = null; try { - inputStream = ConfigUtil.getClassLoader().getResourceAsStream(fileName); + inputStream = JbootConfigKit.getClassLoader().getResourceAsStream(fileName); if (inputStream != null) { properties.load(new InputStreamReader(inputStream, encoding)); } diff --git a/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java b/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java index 6fe3d317..ae019677 100644 --- a/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java @@ -19,7 +19,7 @@ import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.ConfigService; import com.ctrip.framework.apollo.enums.PropertyChangeType; import com.ctrip.framework.apollo.model.ConfigChange; -import io.jboot.app.config.ConfigUtil; +import io.jboot.app.config.JbootConfigKit; import io.jboot.app.config.JbootConfigManager; import java.util.HashMap; @@ -81,7 +81,7 @@ public class ApolloConfigManager { private Config getDefaultConfig(JbootConfigManager configManager) { ApolloServerConfig apolloServerConfig = configManager.get(ApolloServerConfig.class); - if (ConfigUtil.isNotBlank(apolloServerConfig.getDefaultNamespace())) { + if (JbootConfigKit.isNotBlank(apolloServerConfig.getDefaultNamespace())) { return ConfigService.getConfig(apolloServerConfig.getDefaultNamespace()); } else { return ConfigService.getAppConfig(); diff --git a/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java b/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java index f4d4b39e..2dd46e3e 100644 --- a/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java +++ b/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java @@ -15,7 +15,7 @@ */ package io.jboot.app.config.support.apollo; -import io.jboot.app.config.ConfigUtil; +import io.jboot.app.config.JbootConfigKit; import io.jboot.app.config.annotation.ConfigModel; @ConfigModel(prefix = "jboot.config.apollo") @@ -60,6 +60,6 @@ public class ApolloServerConfig { } public boolean isConfigOk() { - return ConfigUtil.areNotBlank(appId, meta); + return JbootConfigKit.areNotBlank(appId, meta); } } diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java index 37e3a76c..b46d8af2 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java @@ -17,7 +17,7 @@ package io.jboot.app.config.support.nacos; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; -import io.jboot.app.config.ConfigUtil; +import io.jboot.app.config.JbootConfigKit; import io.jboot.app.config.JbootConfigManager; import java.io.IOException; @@ -56,7 +56,7 @@ public class NacosConfigManager { String content = configService.getConfig(nacosServerConfig.getDataId() , nacosServerConfig.getGroup(), 3000); - if (ConfigUtil.isNotBlank(content)) { + if (JbootConfigKit.isNotBlank(content)) { contentProperties = str2Properties(content); if (contentProperties != null) { configManager.setRemoteProperties(contentProperties); diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java b/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java index 91841856..65cc9d73 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java @@ -16,7 +16,7 @@ package io.jboot.app.config.support.nacos; -import io.jboot.app.config.ConfigUtil; +import io.jboot.app.config.JbootConfigKit; import io.jboot.app.config.annotation.ConfigModel; import java.util.Properties; @@ -250,6 +250,6 @@ public class NacosServerConfig { public boolean isConfigOk() { - return ConfigUtil.areNotBlank(serverAddr, dataId, group); + return JbootConfigKit.areNotBlank(serverAddr, dataId, group); } } diff --git a/src/main/java/io/jboot/components/cache/JbootCacheManager.java b/src/main/java/io/jboot/components/cache/JbootCacheManager.java index 2be20edc..d006c52b 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheManager.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheManager.java @@ -16,7 +16,7 @@ package io.jboot.components.cache; import io.jboot.Jboot; -import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.ConfigUtil; import io.jboot.components.cache.caffeine.CaffeineCacheImpl; import io.jboot.components.cache.caredis.JbootCaredisCacheImpl; import io.jboot.components.cache.ehcache.JbootEhcacheImpl; @@ -61,7 +61,7 @@ public class JbootCacheManager { synchronized (this) { cache = cacheMap.get(name); if (cache == null) { - Map configModels = JbootConfigUtil.getConfigModels(JbootCacheConfig.class, "jboot.cache"); + Map configModels = ConfigUtil.getConfigModels(JbootCacheConfig.class); JbootCacheConfig.TYPES.forEach(configModels::remove); configModels.putIfAbsent("default", new JbootCacheConfig()); diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java index 141f758e..7c141b91 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -15,7 +15,7 @@ */ package io.jboot.components.gateway; -import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.ConfigUtil; import io.jboot.components.gateway.discovery.GatewayDiscovery; import io.jboot.components.gateway.discovery.GatewayDiscoveryManager; import io.jboot.components.gateway.discovery.GatewayInstance; @@ -53,7 +53,7 @@ public class JbootGatewayManager { } private void initConfigs() { - Map configMap = JbootConfigUtil.getConfigModels(JbootGatewayConfig.class, "jboot.gateway"); + Map configMap = ConfigUtil.getConfigModels(JbootGatewayConfig.class, "jboot.gateway"); for (Map.Entry entry : configMap.entrySet()) { if ("discovery".equals(entry.getKey()) || "instance".equals(entry.getKey())) { continue; diff --git a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryManager.java b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryManager.java index d54d92fd..498229f0 100644 --- a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryManager.java +++ b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryManager.java @@ -17,7 +17,7 @@ package io.jboot.components.gateway.discovery; import io.jboot.Jboot; -import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.ConfigUtil; import io.jboot.core.spi.JbootSpiLoader; import io.jboot.utils.NetUtil; @@ -59,7 +59,7 @@ public class GatewayDiscoveryManager { return; } - Map instanceConfigMap = JbootConfigUtil.getConfigModels(GatewayInstanceConfig.class, "jboot.gateway.instance"); + Map instanceConfigMap = ConfigUtil.getConfigModels(GatewayInstanceConfig.class, "jboot.gateway.instance"); for (GatewayInstanceConfig instanceConfig : instanceConfigMap.values()) { GatewayInstance instance = new GatewayInstance(); diff --git a/src/main/java/io/jboot/components/mq/JbootmqManager.java b/src/main/java/io/jboot/components/mq/JbootmqManager.java index 17f6389c..719dd180 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqManager.java +++ b/src/main/java/io/jboot/components/mq/JbootmqManager.java @@ -16,7 +16,7 @@ package io.jboot.components.mq; import io.jboot.Jboot; -import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.ConfigUtil; import io.jboot.components.mq.aliyunmq.JbootAliyunmqImpl; import io.jboot.components.mq.local.JbootLocalmqImpl; import io.jboot.components.mq.qpidmq.JbootQpidmqImpl; @@ -66,7 +66,7 @@ public class JbootmqManager { synchronized (this) { mq = jbootmqMap.get(name); if (mq == null) { - Map configModels = JbootConfigUtil.getConfigModels(JbootmqConfig.class, "jboot.mq"); + Map configModels = ConfigUtil.getConfigModels(JbootmqConfig.class); JbootmqConfig.TYPES.forEach(configModels::remove); if (!configModels.containsKey(name)) { diff --git a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java index ce35091a..02004020 100644 --- a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java +++ b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java @@ -17,7 +17,7 @@ package io.jboot.components.mq.aliyunmq; import com.aliyun.openservices.ons.api.*; import io.jboot.Jboot; -import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.ConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; import io.jboot.components.mq.JbootmqConfig; @@ -38,7 +38,7 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { super(config); String typeName = config.getTypeName(); if (StrUtil.isNotBlank(typeName)) { - Map configModels = JbootConfigUtil.getConfigModels(JbootAliyunmqConfig.class, "jboot.mq.aliyun"); + Map configModels = ConfigUtil.getConfigModels(JbootAliyunmqConfig.class); if (!configModels.containsKey(typeName)) { throw new JbootIllegalConfigException("Please config \"jboot.mq.aliyun." + typeName + ".addr\" in your jboot.properties."); } diff --git a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java index 1be75dac..ad502fd9 100644 --- a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java +++ b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java @@ -17,7 +17,7 @@ package io.jboot.components.mq.qpidmq; import com.jfinal.log.Log; import io.jboot.Jboot; -import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.ConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; import io.jboot.components.mq.JbootmqConfig; @@ -48,7 +48,7 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { JbootQpidmqConfig qpidConfig = null; String typeName = config.getTypeName(); if (StrUtil.isNotBlank(typeName)) { - Map configModels = JbootConfigUtil.getConfigModels(JbootQpidmqConfig.class, "jboot.mq.qpid"); + Map configModels = ConfigUtil.getConfigModels(JbootQpidmqConfig.class); if (!configModels.containsKey(typeName)) { throw new JbootIllegalConfigException("Please config \"jboot.mq.qpid." + typeName + ".host\" in your jboot.properties."); } diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index 8c022c90..d0466c20 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -18,7 +18,7 @@ package io.jboot.components.mq.rabbitmq; import com.rabbitmq.client.*; import io.jboot.Jboot; import io.jboot.app.JbootApplicationConfig; -import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.ConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; import io.jboot.components.mq.JbootmqConfig; @@ -48,7 +48,7 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { String typeName = config.getTypeName(); if (StrUtil.isNotBlank(typeName)) { - Map configModels = JbootConfigUtil.getConfigModels(JbootRabbitmqConfig.class, "jboot.mq.rabbitmq"); + Map configModels = ConfigUtil.getConfigModels(JbootRabbitmqConfig.class); if (!configModels.containsKey(typeName)) { throw new JbootIllegalConfigException("Please config \"jboot.mq.rabbitmq." + typeName + ".host\" in your jboot.properties."); } diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java index 1078184e..6cb191d3 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java @@ -17,7 +17,7 @@ package io.jboot.components.mq.redismq; import com.jfinal.log.Log; import io.jboot.Jboot; -import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.ConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; import io.jboot.components.mq.JbootmqConfig; @@ -43,7 +43,7 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { JbootRedismqConfig redisConfig = null; String typeName = config.getTypeName(); if (StrUtil.isNotBlank(typeName)) { - Map configModels = JbootConfigUtil.getConfigModels(JbootRedismqConfig.class, "jboot.mq.redis"); + Map configModels = ConfigUtil.getConfigModels(JbootRedismqConfig.class); if (!configModels.containsKey(typeName)) { throw new JbootIllegalConfigException("Please config \"jboot.mq.redis." + typeName + ".host\" in your jboot.properties."); } diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java index 8abb75ed..fee6337b 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java @@ -17,7 +17,7 @@ package io.jboot.components.mq.rocketmq; import com.jfinal.log.Log; import io.jboot.Jboot; -import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.ConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; import io.jboot.components.mq.JbootmqConfig; @@ -49,7 +49,7 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { String typeName = config.getTypeName(); if (StrUtil.isNotBlank(typeName)) { - Map configModels = JbootConfigUtil.getConfigModels(JbootRocketmqConfig.class, "jboot.mq.rocket"); + Map configModels = ConfigUtil.getConfigModels(JbootRocketmqConfig.class); if (!configModels.containsKey(typeName)) { throw new JbootIllegalConfigException("Please config \"jboot.mq.rocket." + typeName + ".namesrvAddr\" in your jboot.properties."); } diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index edbc825b..45c9d685 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -18,7 +18,7 @@ package io.jboot.components.rpc.dubbo; import io.jboot.Jboot; import io.jboot.app.config.JbootConfigManager; -import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.ConfigUtil; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; import io.jboot.components.rpc.RPCUtil; @@ -271,7 +271,7 @@ class DubboUtil { private static Map configs(Class clazz, String prefix) { - Map ret = JbootConfigUtil.getConfigModels(clazz, prefix); + Map ret = ConfigUtil.getConfigModels(clazz, prefix); if (ret.size() > 0 && !RPCUtil.isDefaultConfigExist(clazz, ret)) { for (Map.Entry entry : ret.entrySet()) { diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java index 40a38207..ead37e12 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java @@ -17,7 +17,7 @@ package io.jboot.components.rpc.motan; import com.weibo.api.motan.config.*; import com.weibo.api.motan.util.MotanFrameworkUtil; -import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.ConfigUtil; import io.jboot.components.rpc.JbootrpcReferenceConfig; import io.jboot.components.rpc.JbootrpcServiceConfig; import io.jboot.components.rpc.RPCUtil; @@ -216,7 +216,7 @@ public class MotanUtil { } private static Map configs(Class clazz, String prefix) { - Map ret = JbootConfigUtil.getConfigModels(clazz, prefix); + Map ret = ConfigUtil.getConfigModels(clazz, prefix); if (ret.size() > 0 && (clazz == BasicServiceInterfaceConfig.class || clazz == BasicRefererInterfaceConfig.class) diff --git a/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java b/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java index 9293e9a6..804b53a3 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java @@ -16,7 +16,7 @@ package io.jboot.db.datasource; import com.google.common.collect.Maps; -import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.ConfigUtil; import io.jboot.utils.StrUtil; import java.util.Map; @@ -36,7 +36,7 @@ public class DataSourceConfigManager { private DataSourceConfigManager() { - Map configMap = JbootConfigUtil.getConfigModels(DataSourceConfig.class,"jboot.datasource"); + Map configMap = ConfigUtil.getConfigModels(DataSourceConfig.class,"jboot.datasource"); for (Map.Entry entry : configMap.entrySet()){ DataSourceConfig config = entry.getValue(); diff --git a/src/main/java/io/jboot/support/redis/JbootRedisManager.java b/src/main/java/io/jboot/support/redis/JbootRedisManager.java index d0de95e6..91e3d395 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedisManager.java +++ b/src/main/java/io/jboot/support/redis/JbootRedisManager.java @@ -17,7 +17,7 @@ package io.jboot.support.redis; import io.jboot.Jboot; -import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.ConfigUtil; import io.jboot.exception.JbootException; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.support.redis.jedis.JbootJedisClusterImpl; @@ -59,7 +59,7 @@ public class JbootRedisManager { synchronized (this) { redis = jbootRedisMap.get(name); if (redis == null) { - Map configModels = JbootConfigUtil.getConfigModels(JbootRedisConfig.class, "jboot.redis"); + Map configModels = ConfigUtil.getConfigModels(JbootRedisConfig.class); if (!configModels.containsKey(name)) { throw new JbootIllegalConfigException("Please config \"jboot.redis." + name + ".host\" in your jboot.properties."); } diff --git a/src/main/java/io/jboot/utils/AnnotationUtil.java b/src/main/java/io/jboot/utils/AnnotationUtil.java index 7f038e3e..10e8916f 100644 --- a/src/main/java/io/jboot/utils/AnnotationUtil.java +++ b/src/main/java/io/jboot/utils/AnnotationUtil.java @@ -1,7 +1,7 @@ package io.jboot.utils; -import io.jboot.app.config.ConfigUtil; +import io.jboot.app.config.JbootConfigKit; public class AnnotationUtil { @@ -11,7 +11,7 @@ public class AnnotationUtil { public static String get(String value, String defaultValue) { if (StrUtil.isNotBlank(value)) { - String ret = ConfigUtil.parseValue(value.trim()); + String ret = JbootConfigKit.parseValue(value.trim()); if (StrUtil.isNotBlank(ret)) { return ret; } diff --git a/src/main/java/io/jboot/app/config/JbootConfigUtil.java b/src/main/java/io/jboot/utils/ConfigUtil.java similarity index 70% rename from src/main/java/io/jboot/app/config/JbootConfigUtil.java rename to src/main/java/io/jboot/utils/ConfigUtil.java index 4daea496..fcaa3932 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigUtil.java +++ b/src/main/java/io/jboot/utils/ConfigUtil.java @@ -13,9 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.jboot.app.config; +package io.jboot.utils; -import io.jboot.Jboot; + +import io.jboot.app.config.JbootConfigKit; +import io.jboot.app.config.JbootConfigManager; +import io.jboot.app.config.annotation.ConfigModel; import org.apache.commons.lang3.StringUtils; import java.util.*; @@ -25,39 +28,46 @@ import java.util.*; * @author michael yang (fuhai999@gmail.com) * @Date: 2020/3/19 */ -public class JbootConfigUtil { +public class ConfigUtil { + + public static Map getConfigModels(Class tClass) { + ConfigModel configModel = tClass.getAnnotation(ConfigModel.class); + if (configModel == null) { + throw new IllegalStateException("The Class \"" + tClass.getName() + "\" is not have @ConfigModel annotation"); + } + return getConfigModels(tClass, configModel.prefix()); + } + + public static Map getConfigModels(Class tClass, String prefix) { Map objMap = new HashMap<>(); - boolean initDefault = false; - Set objNames = new HashSet<>(); String objPrefix = prefix + "."; int pointCount = StringUtils.countMatches(prefix, '.'); - Properties prop = JbootConfigManager.me().getProperties(); for (Map.Entry entry : prop.entrySet()) { - if (entry.getKey() == null || ConfigUtil.isBlank(entry.getKey().toString())) { + if (entry.getKey() == null || JbootConfigKit.isBlank(entry.getKey().toString())) { continue; } String key = entry.getKey().toString().trim(); //配置来源于 Docker 的环境变量配置 - if (key.contains("_") && Character.isUpperCase(key.charAt(0))){ - key = key.toLowerCase().replace('_','.'); + if (key.contains("_") && Character.isUpperCase(key.charAt(0))) { + key = key.toLowerCase().replace('_', '.'); } //初始化默认的配置 if (!initDefault && key.startsWith(prefix) && StringUtils.countMatches(key, '.') == pointCount + 1) { initDefault = true; - T defaultObj = Jboot.config(tClass, prefix); + T defaultObj = JbootConfigManager.me().get(tClass, prefix, null); objMap.put("default", defaultObj); } @@ -71,7 +81,7 @@ public class JbootConfigUtil { } for (String name : objNames) { - T obj = Jboot.config(tClass, objPrefix + name); + T obj = JbootConfigManager.me().get(tClass, objPrefix + name, null); objMap.put(name, obj); } diff --git a/src/test/java/io/jboot/test/config/ConfigController.java b/src/test/java/io/jboot/test/config/ConfigController.java index c48a8ea0..16f98c0c 100644 --- a/src/test/java/io/jboot/test/config/ConfigController.java +++ b/src/test/java/io/jboot/test/config/ConfigController.java @@ -15,7 +15,7 @@ */ package io.jboot.test.config; -import io.jboot.app.config.JbootConfigUtil; +import io.jboot.utils.ConfigUtil; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; @@ -27,6 +27,6 @@ import io.jboot.web.controller.annotation.RequestMapping; public class ConfigController extends JbootController { public void index(){ - renderJson(JbootConfigUtil.getConfigModels(TestConfigModel.class,"config.test.test")); + renderJson(ConfigUtil.getConfigModels(TestConfigModel.class,"config.test.test")); } } -- Gitee From 2a634d707d71b3751e3e605aac79dd9e5e1cde24 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 22 Nov 2021 16:07:54 +0800 Subject: [PATCH 1455/1965] =?UTF-8?q?limiter=20=E7=BB=84=E4=BB=B6=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20IP=20=E9=99=90=E6=B5=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/components/limiter/LimitType.java | 8 ++++++++ .../components/limiter/LimiterManager.java | 18 ++++++++++++++++-- .../interceptor/BaseLimiterInterceptor.java | 13 +++++++++++++ .../interceptor/LimiterGlobalInterceptor.java | 3 +++ .../interceptor/LimiterInterceptor.java | 6 ++++++ src/test/resources/jboot.properties | 5 +++-- 6 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/components/limiter/LimitType.java b/src/main/java/io/jboot/components/limiter/LimitType.java index f4b7f7d3..fee57e11 100644 --- a/src/main/java/io/jboot/components/limiter/LimitType.java +++ b/src/main/java/io/jboot/components/limiter/LimitType.java @@ -22,12 +22,20 @@ public class LimitType { /** * 令牌桶,通过 guava 的 RateLimiter 来实现 + * 时间有关,每秒钟允许有多少个请求 */ public static final String TOKEN_BUCKET = "tb"; /** * 并发量,通过 Semaphore 来实现 + * 和并发有关,和请求时间无关 */ public static final String CONCURRENCY = "cc"; + /** + * IP 并发量限制,通过 Semaphore 来实现 + * 和并发有关,和请求时间无关 + */ + public static final String IP = "ip"; + } diff --git a/src/main/java/io/jboot/components/limiter/LimiterManager.java b/src/main/java/io/jboot/components/limiter/LimiterManager.java index 6863d243..c2bccf95 100644 --- a/src/main/java/io/jboot/components/limiter/LimiterManager.java +++ b/src/main/java/io/jboot/components/limiter/LimiterManager.java @@ -16,6 +16,8 @@ package io.jboot.components.limiter; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import com.google.common.util.concurrent.RateLimiter; import com.jfinal.aop.Invocation; import io.jboot.Jboot; @@ -25,6 +27,7 @@ import io.jboot.utils.StrUtil; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -35,6 +38,11 @@ public class LimiterManager { private Map semaphoreCache = new ConcurrentHashMap<>(); + + private Cache ipSemaphoreCache = Caffeine.newBuilder() + .expireAfterAccess(10, TimeUnit.MINUTES) + .build(); + private Map rateLimiterCache = new ConcurrentHashMap<>(); private LimitConfig limitConfig = Jboot.config(LimitConfig.class); @@ -131,7 +139,7 @@ public class LimiterManager { if (limiter == null || limiter.getRate() != rate) { synchronized (resource.intern()) { limiter = rateLimiterCache.get(resource); - if (limiter == null) { + if (limiter == null || limiter.getRate() != rate) { limiter = RateLimiter.create(rate); rateLimiterCache.put(resource, limiter); } @@ -154,6 +162,10 @@ public class LimiterManager { return semaphore; } + public Semaphore getOrCreateIpSemaphore(String ipKey, int rate) { + return ipSemaphoreCache.get(ipKey, s -> new Semaphore(rate)); + } + public Set getConfigPackageOrTargets() { return configPackageOrTargets; } @@ -181,7 +193,9 @@ public class LimiterManager { return false; } - if (!LimitType.CONCURRENCY.equals(type) && !LimitType.TOKEN_BUCKET.equals(type)) { + if (!LimitType.CONCURRENCY.equals(type) + && !LimitType.TOKEN_BUCKET.equals(type) + && !LimitType.IP.equals(type)) { return false; } diff --git a/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java index 2ab9177d..62a4f80b 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java @@ -21,6 +21,7 @@ import com.jfinal.aop.Invocation; import io.jboot.components.limiter.LimiterManager; import io.jboot.components.limiter.redis.RedisRateLimitUtil; import io.jboot.utils.ClassUtil; +import io.jboot.utils.RequestUtil; import io.jboot.utils.StrUtil; import javax.servlet.http.HttpServletRequest; @@ -31,6 +32,18 @@ public abstract class BaseLimiterInterceptor { protected void doInterceptForConcurrency(int rate, String resource, String fallback, Invocation inv) { Semaphore semaphore = LimiterManager.me().getOrCreateSemaphore(resource, rate); + doInterceptorBySemaphore(resource, fallback, inv, semaphore); + } + + + protected void doInterceptForIpConcurrency(int rate, String resource, String fallback, Invocation inv) { + String ipKey = RequestUtil.getIpAddress(inv.getController().getRequest()) + ":" + resource; + Semaphore semaphore = LimiterManager.me().getOrCreateIpSemaphore(ipKey, rate); + doInterceptorBySemaphore(resource, fallback, inv, semaphore); + } + + + private void doInterceptorBySemaphore(String resource, String fallback, Invocation inv, Semaphore semaphore) { boolean acquire = false; try { acquire = semaphore.tryAcquire(); diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java index 0332581a..2113d86b 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java @@ -43,6 +43,9 @@ public class LimiterGlobalInterceptor extends BaseLimiterInterceptor implements case LimitType.CONCURRENCY: doInterceptForConcurrency(typeAndRate.getRate(), resource, null, inv); break; + case LimitType.IP: + doInterceptForIpConcurrency(typeAndRate.getRate(), resource, null, inv); + break; case LimitType.TOKEN_BUCKET: doInterceptForTokenBucket(typeAndRate.getRate(), resource, null, inv); break; diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java index 58defb32..6d9f0add 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java @@ -53,6 +53,12 @@ public class LimiterInterceptor extends BaseLimiterInterceptor implements Interc doInterceptForTokenBucket(enableLimit.rate(), resource, enableLimit.fallback(), inv); } break; + case LimitType.IP: + if (LimitScope.CLUSTER == enableLimit.scope()) { + throw new IllegalArgumentException("Ip limit for cluster not implement!"); + } + doInterceptForIpConcurrency(enableLimit.rate(), resource, enableLimit.fallback(), inv); + break; } } diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index 345f5600..b35679ce 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -10,8 +10,9 @@ jboot.redis.host=127.0.0.1 jboot.rpc.autoExportEnable=false -jboot.limit.enable = false -jboot.limit.rule = /*?a=b*c:tb:1,io.jboot.test.aop*.get*(*):tb:1 +jboot.limit.enable = true +#jboot.limit.rule = /*?a=b*c:tb:1,io.jboot.test.aop*.get*(*):tb:1 +jboot.limit.rule = /:ip:1,io.jboot.test.aop*.get*(*):tb:1 jboot.model.unscanPackage = * -- Gitee From 92f23cd363bffa20a6fcc961715a3a2ed384e0ec Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 23 Nov 2021 10:25:50 +0800 Subject: [PATCH 1456/1965] =?UTF-8?q?limiter=20=E7=BB=84=E4=BB=B6=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20IP=20=E9=99=90=E6=B5=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/components/limiter/LimitType.java | 16 +++++- .../components/limiter/LimiterManager.java | 50 ++++++------------- .../interceptor/BaseLimiterInterceptor.java | 13 ----- .../interceptor/LimiterGlobalInterceptor.java | 23 ++++----- .../interceptor/LimiterInterceptor.java | 16 ++++-- src/test/resources/jboot.properties | 2 +- 6 files changed, 55 insertions(+), 65 deletions(-) diff --git a/src/main/java/io/jboot/components/limiter/LimitType.java b/src/main/java/io/jboot/components/limiter/LimitType.java index fee57e11..6ce618d1 100644 --- a/src/main/java/io/jboot/components/limiter/LimitType.java +++ b/src/main/java/io/jboot/components/limiter/LimitType.java @@ -15,6 +15,10 @@ */ package io.jboot.components.limiter; +import com.google.common.collect.Sets; + +import java.util.Set; + /** * 限制类型 */ @@ -26,6 +30,12 @@ public class LimitType { */ public static final String TOKEN_BUCKET = "tb"; + /** + * IP 并发量限制,通过 RateLimiter 来实现 + * 每个 ip 在每 1 秒内允许请求的数量 + */ + public static final String IP_TOKEN_BUCKET = "iptb"; + /** * 并发量,通过 Semaphore 来实现 * 和并发有关,和请求时间无关 @@ -36,6 +46,10 @@ public class LimitType { * IP 并发量限制,通过 Semaphore 来实现 * 和并发有关,和请求时间无关 */ - public static final String IP = "ip"; + public static final String IP_CONCURRENCY = "ipcc"; + + + public static Set types = Sets.newHashSet(TOKEN_BUCKET, IP_TOKEN_BUCKET, CONCURRENCY, IP_CONCURRENCY); + } diff --git a/src/main/java/io/jboot/components/limiter/LimiterManager.java b/src/main/java/io/jboot/components/limiter/LimiterManager.java index c2bccf95..c5cebe3a 100644 --- a/src/main/java/io/jboot/components/limiter/LimiterManager.java +++ b/src/main/java/io/jboot/components/limiter/LimiterManager.java @@ -25,7 +25,6 @@ import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; @@ -35,17 +34,21 @@ public class LimiterManager { private Set configPackageOrTargets = new HashSet<>(); private Map typeAndRateCache = new HashMap<>(); + private LimitConfig limitConfig = Jboot.config(LimitConfig.class); + + private Cache semaphoreCache = Caffeine.newBuilder() + .expireAfterAccess(10, TimeUnit.MINUTES) + .expireAfterWrite(10,TimeUnit.MINUTES) + .build(); - private Map semaphoreCache = new ConcurrentHashMap<>(); - private Cache ipSemaphoreCache = Caffeine.newBuilder() + private Cache rateLimiterCache = Caffeine.newBuilder() .expireAfterAccess(10, TimeUnit.MINUTES) + .expireAfterWrite(10,TimeUnit.MINUTES) .build(); - private Map rateLimiterCache = new ConcurrentHashMap<>(); - private LimitConfig limitConfig = Jboot.config(LimitConfig.class); private LimitFallbackProcesser fallbackProcesser; private static LimiterManager me = new LimiterManager(); @@ -102,6 +105,7 @@ public class LimiterManager { if (!ensureLegal(packageOrTarget, type, rate.trim())) { continue; } + packageOrTarget = packageOrTarget.replace(".", "\\.") .replace("(", "\\(") .replace(")", "\\)") @@ -134,36 +138,14 @@ public class LimiterManager { return null; } - public RateLimiter getOrCreateRateLimiter(String resource, int rate) { - RateLimiter limiter = rateLimiterCache.get(resource); - if (limiter == null || limiter.getRate() != rate) { - synchronized (resource.intern()) { - limiter = rateLimiterCache.get(resource); - if (limiter == null || limiter.getRate() != rate) { - limiter = RateLimiter.create(rate); - rateLimiterCache.put(resource, limiter); - } - } - } - return limiter; - } - public Semaphore getOrCreateSemaphore(String resource, int rate) { - Semaphore semaphore = semaphoreCache.get(resource); - if (semaphore == null) { - synchronized (resource.intern()) { - semaphore = semaphoreCache.get(resource); - if (semaphore == null) { - semaphore = new Semaphore(rate); - semaphoreCache.put(resource, semaphore); - } - } - } - return semaphore; + public RateLimiter getOrCreateRateLimiter(String resKey, int rate) { + return rateLimiterCache.get(resKey, s -> RateLimiter.create(rate)); } - public Semaphore getOrCreateIpSemaphore(String ipKey, int rate) { - return ipSemaphoreCache.get(ipKey, s -> new Semaphore(rate)); + + public Semaphore getOrCreateSemaphore(String resKey, int rate) { + return semaphoreCache.get(resKey, s -> new Semaphore(rate)); } public Set getConfigPackageOrTargets() { @@ -193,9 +175,7 @@ public class LimiterManager { return false; } - if (!LimitType.CONCURRENCY.equals(type) - && !LimitType.TOKEN_BUCKET.equals(type) - && !LimitType.IP.equals(type)) { + if (!LimitType.types.contains(type)) { return false; } diff --git a/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java index 62a4f80b..2ab9177d 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java @@ -21,7 +21,6 @@ import com.jfinal.aop.Invocation; import io.jboot.components.limiter.LimiterManager; import io.jboot.components.limiter.redis.RedisRateLimitUtil; import io.jboot.utils.ClassUtil; -import io.jboot.utils.RequestUtil; import io.jboot.utils.StrUtil; import javax.servlet.http.HttpServletRequest; @@ -32,18 +31,6 @@ public abstract class BaseLimiterInterceptor { protected void doInterceptForConcurrency(int rate, String resource, String fallback, Invocation inv) { Semaphore semaphore = LimiterManager.me().getOrCreateSemaphore(resource, rate); - doInterceptorBySemaphore(resource, fallback, inv, semaphore); - } - - - protected void doInterceptForIpConcurrency(int rate, String resource, String fallback, Invocation inv) { - String ipKey = RequestUtil.getIpAddress(inv.getController().getRequest()) + ":" + resource; - Semaphore semaphore = LimiterManager.me().getOrCreateIpSemaphore(ipKey, rate); - doInterceptorBySemaphore(resource, fallback, inv, semaphore); - } - - - private void doInterceptorBySemaphore(String resource, String fallback, Invocation inv, Semaphore semaphore) { boolean acquire = false; try { acquire = semaphore.tryAcquire(); diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java index 2113d86b..52d67f07 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java @@ -20,8 +20,9 @@ import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import io.jboot.components.limiter.LimitType; import io.jboot.components.limiter.LimiterManager; +import io.jboot.utils.RequestUtil; -public class LimiterGlobalInterceptor extends BaseLimiterInterceptor implements Interceptor { +public class LimiterGlobalInterceptor extends BaseLimiterInterceptor implements Interceptor { @Override public void intercept(Invocation inv) { @@ -30,11 +31,9 @@ public class LimiterGlobalInterceptor extends BaseLimiterInterceptor implements if (typeAndRate != null) { doInterceptByTypeAndRate(typeAndRate, packageOrTarget, inv); - return; + }else { + inv.invoke(); } - - - inv.invoke(); } @@ -43,19 +42,19 @@ public class LimiterGlobalInterceptor extends BaseLimiterInterceptor implements case LimitType.CONCURRENCY: doInterceptForConcurrency(typeAndRate.getRate(), resource, null, inv); break; - case LimitType.IP: - doInterceptForIpConcurrency(typeAndRate.getRate(), resource, null, inv); + case LimitType.IP_CONCURRENCY: + String resKey1 = RequestUtil.getIpAddress(inv.getController().getRequest()) + ":" + resource; + doInterceptForConcurrency(typeAndRate.getRate(), resKey1, null, inv); break; case LimitType.TOKEN_BUCKET: doInterceptForTokenBucket(typeAndRate.getRate(), resource, null, inv); break; + case LimitType.IP_TOKEN_BUCKET: + String resKey2 = RequestUtil.getIpAddress(inv.getController().getRequest()) + ":" + resource; + doInterceptForTokenBucket(typeAndRate.getRate(), resKey2, null, inv); + break; } } - - - - - } diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java index 6d9f0add..a07be153 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java @@ -22,6 +22,7 @@ import io.jboot.components.limiter.LimitScope; import io.jboot.components.limiter.LimitType; import io.jboot.components.limiter.annotation.EnableLimit; import io.jboot.utils.AnnotationUtil; +import io.jboot.utils.RequestUtil; import io.jboot.utils.StrUtil; public class LimiterInterceptor extends BaseLimiterInterceptor implements Interceptor { @@ -46,6 +47,13 @@ public class LimiterInterceptor extends BaseLimiterInterceptor implements Interc } doInterceptForConcurrency(enableLimit.rate(), resource, enableLimit.fallback(), inv); break; + case LimitType.IP_CONCURRENCY: + if (LimitScope.CLUSTER == enableLimit.scope()) { + throw new IllegalArgumentException("Ip limit for cluster not implement!"); + } + String resKey1 = RequestUtil.getIpAddress(inv.getController().getRequest()) + ":" + resource; + doInterceptForConcurrency(enableLimit.rate(), resKey1, enableLimit.fallback(), inv); + break; case LimitType.TOKEN_BUCKET: if (LimitScope.CLUSTER == enableLimit.scope()) { doInterceptForTokenBucketWithCluster(enableLimit.rate(), resource, enableLimit.fallback(), inv); @@ -53,11 +61,13 @@ public class LimiterInterceptor extends BaseLimiterInterceptor implements Interc doInterceptForTokenBucket(enableLimit.rate(), resource, enableLimit.fallback(), inv); } break; - case LimitType.IP: + case LimitType.IP_TOKEN_BUCKET: + String resKey2 = RequestUtil.getIpAddress(inv.getController().getRequest()) + ":" + resource; if (LimitScope.CLUSTER == enableLimit.scope()) { - throw new IllegalArgumentException("Ip limit for cluster not implement!"); + doInterceptForTokenBucketWithCluster(enableLimit.rate(), resKey2, enableLimit.fallback(), inv); + } else { + doInterceptForTokenBucket(enableLimit.rate(), resKey2, enableLimit.fallback(), inv); } - doInterceptForIpConcurrency(enableLimit.rate(), resource, enableLimit.fallback(), inv); break; } } diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index b35679ce..cee55b7f 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -12,7 +12,7 @@ jboot.rpc.autoExportEnable=false jboot.limit.enable = true #jboot.limit.rule = /*?a=b*c:tb:1,io.jboot.test.aop*.get*(*):tb:1 -jboot.limit.rule = /:ip:1,io.jboot.test.aop*.get*(*):tb:1 +jboot.limit.rule = /:iptb:1,io.jboot.test.aop*.get*(*):tb:1 jboot.model.unscanPackage = * -- Gitee From 770e747d70790f76d9e95b45f4d5b72fccf8e0e0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 23 Nov 2021 10:33:06 +0800 Subject: [PATCH 1457/1965] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/docs/limit.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/doc/docs/limit.md b/doc/docs/limit.md index 1eeb9d4f..376bdf02 100644 --- a/doc/docs/limit.md +++ b/doc/docs/limit.md @@ -33,7 +33,7 @@ Jboot 提供了两种方案: 在 `jboot.properties` 文件中定义如下: ``` jboot.limit.enable = true -jboot.limit.rule = /user*:tb:1,io.jboot.aop*.get*(*):tb:1 +jboot.limit.rule = /user*:tb:1,/other*:iptb:1,io.jboot.aop*.get*(*):tb:1 ``` - jboot.limit.enable : 限流功能的开关 @@ -42,16 +42,18 @@ jboot.limit.rule = /user*:tb:1,io.jboot.aop*.get*(*):tb:1 > 规则说明: > - 1、可以配置多个规则,每个规则用英文逗号隔开 > - 2、规则分为三个部分,用冒号(:)隔开,分别是:资源、限流类型、限流参数值 -> - 3、限流的类型有2种、分别是:tb 和 cc。tb:TOKEN BUCKET(令牌桶),cc:CONCURRENCY(并发量) +> - 3、限流的类型有4种、分别是:tb、cc、iptb 和 ipcc。tb:TOKEN BUCKET(令牌桶,每秒允许访问的数量),cc:CONCURRENCY(并发量),iptb:单个ip每秒允许访问的数量,ipcc:单个ip允许同时的并发量。 > - 4、星号(*)匹配任意字符,也可以是空字符。 -在以上配置中,配置了两个规则,分别是: +在以上配置中,配置了三个规则,分别是: - `/user*:tb:1` +- `/other*:iptb:1` - `io.jboot.aop*.get*(*):cc:1` -第一个规则:匹配 `/user` 开头的所有url地址,每个 url 地址,1秒钟之内只允许访问1次。 -第二个规则:匹配 `io.jboot.aop` 开头的所有包名,并且 `get` 开头的所有任意参数的方法。并发量为 1。 +第一个规则:匹配 `/user` 开头的所有url地址,每个 url 地址,1 秒钟之内只允许访问 1 次。 +第二个规则:匹配 `/other` 开头的所有url地址,每个 ip 地址在 1 秒钟之内只允许访问 1 次。 +第三个规则:匹配 `io.jboot.aop` 开头的所有包名,并且 `get` 开头的所有任意参数的方法。并发量为 1。 **使用方案2:通过注解 `@EnableLimit`** @@ -83,8 +85,9 @@ public class IndexController extends JbootController { ## 限流的实现 上文中提到,Jboot 提供了两种限流类型,他们分别是: -- TOKEN BUCKET : 令牌桶 -- CONCURRENCY : 并发量 + +- TOKEN BUCKET : 令牌桶,每秒允许访问的次数。 +- CONCURRENCY : 并发量,和时间无关。 TOKEN BUCKET 令牌桶,是通过 Google Guava 的 RateLimiter 来实现的。 -- Gitee From d60ca4f79fe55a717aaa88c92651502b1ebe8449 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 23 Nov 2021 11:14:09 +0800 Subject: [PATCH 1458/1965] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/docs/codegen.md | 8 ++++---- doc/docs/start.md | 8 ++++---- src/test/java/io/jboot/test/codegen/GenTester.java | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/docs/codegen.md b/doc/docs/codegen.md index d03b4a9e..30dc3e38 100644 --- a/doc/docs/codegen.md +++ b/doc/docs/codegen.md @@ -30,8 +30,8 @@ public class GenTester { String baseModelPackage = "io.jboot.test.codegen.modelbase"; //生成的BaseModel的包名 //Model存放的路径,一般情况下是 /src/main/java 下,如下是放在 test 目录下 - String modelDir = PathKit.getWebRootPath() + "/src/test/java/" + modelPackage.replace(".", "/"); - String baseModelDir = PathKit.getWebRootPath() + "/src/test/java/" + baseModelPackage.replace(".", "/"); + String modelDir = CodeGenHelpler.getUserDir() + "/src/test/java/" + modelPackage.replace(".", "/"); + String baseModelDir = CodeGenHelpler.getUserDir() + "/src/test/java/" + baseModelPackage.replace(".", "/"); System.out.println("start generate..."); System.out.println("generate dir:" + modelDir); @@ -46,8 +46,8 @@ public class GenTester { //设置 service 层代码的存放目录 - String serviceOutputDir = PathKit.getWebRootPath() + "/src/test/java/" + servicePackage.replace(".", "/"); - String serviceImplOutputDir = PathKit.getWebRootPath() + "/src/test/java/" + serviceImplPackage.replace(".", "/"); + String serviceOutputDir = CodeGenHelpler.getUserDir() + "/src/test/java/" + servicePackage.replace(".", "/"); + String serviceImplOutputDir = CodeGenHelpler.getUserDir() + "/src/test/java/" + serviceImplPackage.replace(".", "/"); //开始生成代码 diff --git a/doc/docs/start.md b/doc/docs/start.md index b3fb8a66..047cef70 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -226,8 +226,8 @@ public class CodeGenerator { String modelPackage = "io.jboot.test.codegen.model"; String baseModelPackage = modelPackage + ".base"; - String modelDir = PathKit.getWebRootPath() + "/src/main/java/" + modelPackage.replace(".", "/"); - String baseModelDir = PathKit.getWebRootPath() + "/src/main/java/" + baseModelPackage.replace(".", "/"); + String modelDir = CodeGenHelpler.getUserDir() + "/src/main/java/" + modelPackage.replace(".", "/"); + String baseModelDir = CodeGenHelpler.getUserDir() + "/src/main/java/" + baseModelPackage.replace(".", "/"); System.out.println("start generate..."); System.out.println("generate dir:" + modelDir); @@ -240,8 +240,8 @@ public class CodeGenerator { String servicePackage = "io.jboot.test.codegen.service"; String serviceImplPackage = "io.jboot.test.codegen.service.impl"; - String serviceOutputDir = PathKit.getWebRootPath() + "/src/main/java/" + servicePackage.replace(".", "/"); - String serviceImplOutputDir = PathKit.getWebRootPath() + "/src/main/java/" + serviceImplPackage.replace(".", "/"); + String serviceOutputDir = CodeGenHelpler.getUserDir() + "/src/main/java/" + servicePackage.replace(".", "/"); + String serviceImplOutputDir = CodeGenHelpler.getUserDir() + "/src/main/java/" + serviceImplPackage.replace(".", "/"); // 生成 Service 接口 及其 实现类 new JbootServiceInterfaceGenerator(servicePackage, serviceOutputDir, modelPackage).generate(); diff --git a/src/test/java/io/jboot/test/codegen/GenTester.java b/src/test/java/io/jboot/test/codegen/GenTester.java index 7ebad98e..6d437ca3 100644 --- a/src/test/java/io/jboot/test/codegen/GenTester.java +++ b/src/test/java/io/jboot/test/codegen/GenTester.java @@ -1,8 +1,8 @@ package io.jboot.test.codegen; -import com.jfinal.kit.PathKit; import io.jboot.app.JbootApplication; +import io.jboot.codegen.CodeGenHelpler; import io.jboot.codegen.model.JbootBaseModelGenerator; import io.jboot.codegen.model.JbootModelGenerator; import io.jboot.codegen.service.JbootServiceImplGenerator; @@ -19,8 +19,8 @@ public class GenTester { String modelPackage = "io.jboot.test.codegen.model"; String baseModelPackage = modelPackage + ".base"; - String modelDir = PathKit.getWebRootPath() + "/src/test/java/" + modelPackage.replace(".", "/"); - String baseModelDir = PathKit.getWebRootPath() + "/src/test/java/" + baseModelPackage.replace(".", "/"); + String modelDir = CodeGenHelpler.getUserDir() + "/src/test/java/" + modelPackage.replace(".", "/"); + String baseModelDir = CodeGenHelpler.getUserDir() + "/src/test/java/" + baseModelPackage.replace(".", "/"); System.out.println("start generate..."); System.out.println("generate dir:" + modelDir); @@ -33,8 +33,8 @@ public class GenTester { String servicePackage = "io.jboot.test.codegen.service"; String serviceImplPackage = "io.jboot.test.codegen.service.provider"; - String serviceOutputDir = PathKit.getWebRootPath() + "/src/test/java/" + servicePackage.replace(".", "/"); - String serviceImplOutputDir = PathKit.getWebRootPath() + "/src/test/java/" + serviceImplPackage.replace(".", "/"); + String serviceOutputDir = CodeGenHelpler.getUserDir() + "/src/test/java/" + servicePackage.replace(".", "/"); + String serviceImplOutputDir = CodeGenHelpler.getUserDir() + "/src/test/java/" + serviceImplPackage.replace(".", "/"); new JbootServiceInterfaceGenerator(servicePackage, serviceOutputDir, modelPackage).generate(); -- Gitee From d372c23d2bf67075661c72be3ab2e38cae3c2526 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 23 Nov 2021 16:58:32 +0800 Subject: [PATCH 1459/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9ALimiterMan?= =?UTF-8?q?ager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/limiter/LimiterManager.java | 59 ++++++++++--------- .../interceptor/LimiterGlobalInterceptor.java | 20 +++---- .../LimiterInterceptorBuilder.java | 2 +- 3 files changed, 43 insertions(+), 38 deletions(-) diff --git a/src/main/java/io/jboot/components/limiter/LimiterManager.java b/src/main/java/io/jboot/components/limiter/LimiterManager.java index c5cebe3a..7735882f 100644 --- a/src/main/java/io/jboot/components/limiter/LimiterManager.java +++ b/src/main/java/io/jboot/components/limiter/LimiterManager.java @@ -27,25 +27,23 @@ import io.jboot.utils.StrUtil; import java.util.*; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; import java.util.regex.Pattern; public class LimiterManager { - private Set configPackageOrTargets = new HashSet<>(); - private Map typeAndRateCache = new HashMap<>(); + private HashSet limitConfigBeans = new HashSet<>(); private LimitConfig limitConfig = Jboot.config(LimitConfig.class); private Cache semaphoreCache = Caffeine.newBuilder() .expireAfterAccess(10, TimeUnit.MINUTES) - .expireAfterWrite(10,TimeUnit.MINUTES) + .expireAfterWrite(10, TimeUnit.MINUTES) .build(); private Cache rateLimiterCache = Caffeine.newBuilder() .expireAfterAccess(10, TimeUnit.MINUTES) - .expireAfterWrite(10,TimeUnit.MINUTES) + .expireAfterWrite(10, TimeUnit.MINUTES) .build(); @@ -111,8 +109,7 @@ public class LimiterManager { .replace(")", "\\)") .replace("*", ".*"); - configPackageOrTargets.add(packageOrTarget.trim()); - typeAndRateCache.put(packageOrTarget.trim(), new TypeAndRate(type.trim(), Integer.valueOf(rate.trim()))); + limitConfigBeans.add(new LimitConfigBean(packageOrTarget.trim(),type.trim(), Integer.valueOf(rate.trim()))); } } @@ -123,15 +120,15 @@ public class LimiterManager { * @param packageOrTarget * @return */ - public TypeAndRate matchConfig(String packageOrTarget) { + public LimitConfigBean matchConfig(String packageOrTarget) { - if (!isEnable() || configPackageOrTargets.isEmpty()) { + if (!isEnable() || limitConfigBeans.isEmpty()) { return null; } - for (String configPackageOrTarget : configPackageOrTargets) { - if (match(packageOrTarget, configPackageOrTarget)) { - return typeAndRateCache.get(configPackageOrTarget); + for (LimitConfigBean value : limitConfigBeans) { + if (value.isMatched(packageOrTarget)){ + return value; } } @@ -140,7 +137,7 @@ public class LimiterManager { public RateLimiter getOrCreateRateLimiter(String resKey, int rate) { - return rateLimiterCache.get(resKey, s -> RateLimiter.create(rate)); + return rateLimiterCache.get(resKey, s -> RateLimiter.create(rate)); } @@ -148,18 +145,8 @@ public class LimiterManager { return semaphoreCache.get(resKey, s -> new Semaphore(rate)); } - public Set getConfigPackageOrTargets() { - return configPackageOrTargets; - } - - public void setConfigPackageOrTargets(Set configPackageOrTargets) { - this.configPackageOrTargets = configPackageOrTargets; - } - - private static boolean match(String string, String regex) { - Pattern pattern = Pattern.compile(regex); - Matcher matcher = pattern.matcher(string); - return matcher.matches(); + public HashSet getLimitConfigBeans() { + return limitConfigBeans; } /** @@ -194,13 +181,27 @@ public class LimiterManager { fallbackProcesser.process(resource, fallback, inv); } - public static class TypeAndRate { + public static class LimitConfigBean { + + private String packageOrTarget; private String type; private int rate; + private Pattern pattern; + - public TypeAndRate(String type, int rate) { + public LimitConfigBean(String packageOrTarget, String type, int rate) { + this.packageOrTarget = packageOrTarget; this.type = type; this.rate = rate; + this.pattern = Pattern.compile(packageOrTarget); + } + + public String getPackageOrTarget() { + return packageOrTarget; + } + + public void setPackageOrTarget(String packageOrTarget) { + this.packageOrTarget = packageOrTarget; } public String getType() { @@ -218,5 +219,9 @@ public class LimiterManager { public void setRate(int rate) { this.rate = rate; } + + public boolean isMatched(String packageOrTarget){ + return pattern.matcher(packageOrTarget).matches(); + } } } diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java index 52d67f07..8a276e14 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java @@ -27,31 +27,31 @@ public class LimiterGlobalInterceptor extends BaseLimiterInterceptor implements @Override public void intercept(Invocation inv) { String packageOrTarget = getPackageOrTarget(inv); - LimiterManager.TypeAndRate typeAndRate = LimiterManager.me().matchConfig(packageOrTarget); + LimiterManager.LimitConfigBean configBean = LimiterManager.me().matchConfig(packageOrTarget); - if (typeAndRate != null) { - doInterceptByTypeAndRate(typeAndRate, packageOrTarget, inv); - }else { + if (configBean != null) { + doInterceptByTypeAndRate(configBean, packageOrTarget, inv); + } else { inv.invoke(); } } - private void doInterceptByTypeAndRate(LimiterManager.TypeAndRate typeAndRate, String resource, Invocation inv) { - switch (typeAndRate.getType()) { + private void doInterceptByTypeAndRate(LimiterManager.LimitConfigBean limitConfigBean, String resource, Invocation inv) { + switch (limitConfigBean.getType()) { case LimitType.CONCURRENCY: - doInterceptForConcurrency(typeAndRate.getRate(), resource, null, inv); + doInterceptForConcurrency(limitConfigBean.getRate(), resource, null, inv); break; case LimitType.IP_CONCURRENCY: String resKey1 = RequestUtil.getIpAddress(inv.getController().getRequest()) + ":" + resource; - doInterceptForConcurrency(typeAndRate.getRate(), resKey1, null, inv); + doInterceptForConcurrency(limitConfigBean.getRate(), resKey1, null, inv); break; case LimitType.TOKEN_BUCKET: - doInterceptForTokenBucket(typeAndRate.getRate(), resource, null, inv); + doInterceptForTokenBucket(limitConfigBean.getRate(), resource, null, inv); break; case LimitType.IP_TOKEN_BUCKET: String resKey2 = RequestUtil.getIpAddress(inv.getController().getRequest()) + ":" + resource; - doInterceptForTokenBucket(typeAndRate.getRate(), resKey2, null, inv); + doInterceptForTokenBucket(limitConfigBean.getRate(), resKey2, null, inv); break; } } diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java index 46293265..b19e8465 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java @@ -34,7 +34,7 @@ public class LimiterInterceptorBuilder implements InterceptorBuilder { @Override public void build(Class targetClass, Method method, Interceptors interceptors) { - if (manager.isEnable() && !manager.getConfigPackageOrTargets().isEmpty()) { + if (manager.isEnable() && !manager.getLimitConfigBeans().isEmpty()) { interceptors.add(LimiterGlobalInterceptor.class); return; } -- Gitee From 100941e3118468b95243cb3e109804d22f780eb4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 24 Nov 2021 14:20:43 +0800 Subject: [PATCH 1460/1965] =?UTF-8?q?LimiterManager=20=E9=99=90=E6=B5=81?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E6=96=B0=E5=A2=9E=20IP=20=E7=99=BD=E5=90=8D?= =?UTF-8?q?=E5=8D=95=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/components/limiter/LimitConfig.java | 14 ++++++++++++++ .../components/limiter/LimiterManager.java | 11 +++++++++++ .../interceptor/LimiterGlobalInterceptor.java | 17 +++++++++++------ .../limiter/interceptor/LimiterInterceptor.java | 15 +++++++++------ src/test/resources/jboot.properties | 1 + 5 files changed, 46 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/jboot/components/limiter/LimitConfig.java b/src/main/java/io/jboot/components/limiter/LimitConfig.java index d66f3fe3..0256c660 100644 --- a/src/main/java/io/jboot/components/limiter/LimitConfig.java +++ b/src/main/java/io/jboot/components/limiter/LimitConfig.java @@ -37,6 +37,12 @@ public class LimitConfig { */ private String rule; + + /** + * IP 白名单,不受限流的配置 + */ + private String ipWhitelist; + /** * 默认的降级处理器(被限流后的处理器) */ @@ -73,6 +79,14 @@ public class LimitConfig { this.rule = rule; } + public String getIpWhitelist() { + return ipWhitelist; + } + + public void setIpWhitelist(String ipWhitelist) { + this.ipWhitelist = ipWhitelist; + } + public String getFallbackProcesser() { return fallbackProcesser; } diff --git a/src/main/java/io/jboot/components/limiter/LimiterManager.java b/src/main/java/io/jboot/components/limiter/LimiterManager.java index 7735882f..7e998d76 100644 --- a/src/main/java/io/jboot/components/limiter/LimiterManager.java +++ b/src/main/java/io/jboot/components/limiter/LimiterManager.java @@ -32,6 +32,8 @@ import java.util.regex.Pattern; public class LimiterManager { private HashSet limitConfigBeans = new HashSet<>(); + private HashSet ipWhitelist = new HashSet<>(); + private LimitConfig limitConfig = Jboot.config(LimitConfig.class); @@ -61,6 +63,11 @@ public class LimiterManager { public void init() { doInitFallbackProcesser(); doParseConfig(); + + Set ips = StrUtil.splitToSetByComma(limitConfig.getIpWhitelist()); + if (ips != null) { + ipWhitelist.addAll(ips); + } } private void doInitFallbackProcesser() { @@ -136,6 +143,10 @@ public class LimiterManager { } + public boolean isInIpWhitelist(String ip){ + return !ipWhitelist.isEmpty() && ipWhitelist.contains(ip); + } + public RateLimiter getOrCreateRateLimiter(String resKey, int rate) { return rateLimiterCache.get(resKey, s -> RateLimiter.create(rate)); } diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java index 8a276e14..43e113bf 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java @@ -24,15 +24,20 @@ import io.jboot.utils.RequestUtil; public class LimiterGlobalInterceptor extends BaseLimiterInterceptor implements Interceptor { + @Override public void intercept(Invocation inv) { - String packageOrTarget = getPackageOrTarget(inv); - LimiterManager.LimitConfigBean configBean = LimiterManager.me().matchConfig(packageOrTarget); - - if (configBean != null) { - doInterceptByTypeAndRate(configBean, packageOrTarget, inv); - } else { + LimiterManager manager = LimiterManager.me(); + if (inv.isActionInvocation() && manager.isInIpWhitelist(RequestUtil.getIpAddress(inv.getController().getRequest()))) { inv.invoke(); + } else { + String packageOrTarget = getPackageOrTarget(inv); + LimiterManager.LimitConfigBean configBean = manager.matchConfig(packageOrTarget); + if (configBean != null) { + doInterceptByTypeAndRate(configBean, packageOrTarget, inv); + } else { + inv.invoke(); + } } } diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java index a07be153..2c5a4660 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java @@ -20,6 +20,7 @@ import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import io.jboot.components.limiter.LimitScope; import io.jboot.components.limiter.LimitType; +import io.jboot.components.limiter.LimiterManager; import io.jboot.components.limiter.annotation.EnableLimit; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.RequestUtil; @@ -29,12 +30,14 @@ public class LimiterInterceptor extends BaseLimiterInterceptor implements Interc @Override public void intercept(Invocation inv) { - - String packageOrTarget = getPackageOrTarget(inv); - EnableLimit enableLimit = inv.getMethod().getAnnotation(EnableLimit.class); - String resource = StrUtil.obtainDefault(enableLimit.resource(), packageOrTarget); - - doInterceptByLimitInfo(enableLimit, resource, inv); + if (inv.isActionInvocation() && LimiterManager.me().isInIpWhitelist(RequestUtil.getIpAddress(inv.getController().getRequest()))) { + inv.invoke(); + } else { + String packageOrTarget = getPackageOrTarget(inv); + EnableLimit enableLimit = inv.getMethod().getAnnotation(EnableLimit.class); + String resource = StrUtil.obtainDefault(enableLimit.resource(), packageOrTarget); + doInterceptByLimitInfo(enableLimit, resource, inv); + } } diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index cee55b7f..8e026eef 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -13,6 +13,7 @@ jboot.rpc.autoExportEnable=false jboot.limit.enable = true #jboot.limit.rule = /*?a=b*c:tb:1,io.jboot.test.aop*.get*(*):tb:1 jboot.limit.rule = /:iptb:1,io.jboot.test.aop*.get*(*):tb:1 +jboot.limit.ipWhitelist = 127.0.0.1 jboot.model.unscanPackage = * -- Gitee From e22b2dc399ad30a78876fc9ecc877494ec38cbb3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 24 Nov 2021 14:26:16 +0800 Subject: [PATCH 1461/1965] update docs --- doc/docs/limit.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/docs/limit.md b/doc/docs/limit.md index 376bdf02..546cc05f 100644 --- a/doc/docs/limit.md +++ b/doc/docs/limit.md @@ -34,10 +34,13 @@ Jboot 提供了两种方案: ``` jboot.limit.enable = true jboot.limit.rule = /user*:tb:1,/other*:iptb:1,io.jboot.aop*.get*(*):tb:1 +jboot.limit.ipWhitelist = 127.0.0.1 ``` - jboot.limit.enable : 限流功能的开关 - jboot.limit.rule : 限流规则 +- jboot.limit.ipWhitelist : IP 白名单,多个 IP 用英文逗号隔开,此 IP 下的访问,将不受限流配置的影响 + > 规则说明: > - 1、可以配置多个规则,每个规则用英文逗号隔开 -- Gitee From 0a9577e375b411d6ca82df25fb583cca47a8cc6e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 24 Nov 2021 14:29:52 +0800 Subject: [PATCH 1462/1965] update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c496fee1..662be2ee 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ logs *.log .logs *.iws +*.report *.classpath .DS_Store .ossutil_checkpoint -- Gitee From 47c3efe3b2ee2534b3c8a794d32999465edae2e6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 26 Nov 2021 09:57:51 +0800 Subject: [PATCH 1463/1965] =?UTF-8?q?rocket=20mq=20=E6=B7=BB=E5=8A=A0=20su?= =?UTF-8?q?bscribeSubExpression=20=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/mq/rocketmq/JbootRocketmqConfig.java | 9 +++++++++ .../jboot/components/mq/rocketmq/JbootRocketmqImpl.java | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqConfig.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqConfig.java index 88684a5e..51d9749c 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqConfig.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqConfig.java @@ -28,6 +28,7 @@ public class JbootRocketmqConfig implements Serializable { private String consumerGroup = "jboot_default_consumer_group"; private Integer consumeMessageBatchMaxSize; private String broadcastChannelPrefix = "broadcast-"; + private String subscribeSubExpression = "*"; private String producerGroup = "jboot_default_producer_group"; private String instanceName; @@ -83,6 +84,14 @@ public class JbootRocketmqConfig implements Serializable { this.broadcastChannelPrefix = broadcastChannelPrefix; } + public String getSubscribeSubExpression() { + return subscribeSubExpression; + } + + public void setSubscribeSubExpression(String subscribeSubExpression) { + this.subscribeSubExpression = subscribeSubExpression; + } + public String getProducerGroup() { return producerGroup; } diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java index fee6337b..c1b20df3 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java @@ -87,7 +87,7 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { } for (String channel : channels) { - consumer.subscribe(channel, "*"); + consumer.subscribe(channel, rocketmqConfig.getSubscribeSubExpression()); } // 注册回调实现类来处理从broker拉取回来的消息 @@ -126,7 +126,7 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { } for (String channel : channels) { - consumer.subscribe(rocketmqConfig.getBroadcastChannelPrefix() + channel, "*"); + consumer.subscribe(rocketmqConfig.getBroadcastChannelPrefix() + channel, rocketmqConfig.getSubscribeSubExpression()); } final int len = rocketmqConfig.getBroadcastChannelPrefix().length(); -- Gitee From 8ab38e994f298886f5f222c0dfa4763b078b9ee7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 26 Nov 2021 10:18:34 +0800 Subject: [PATCH 1464/1965] optimize --- .../mq/aliyunmq/JbootAliyunmqConfig.java | 23 ++++++++++++++++++- .../mq/aliyunmq/JbootAliyunmqImpl.java | 10 ++++---- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqConfig.java b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqConfig.java index 99a71035..a89b7e62 100644 --- a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqConfig.java +++ b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqConfig.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -29,6 +29,11 @@ public class JbootAliyunmqConfig { private String broadcastChannelPrefix = "broadcast-"; + private String subscribeSubExpression = "*"; + + private String instanceName; + + public String getAccessKey() { return accessKey; } @@ -76,4 +81,20 @@ public class JbootAliyunmqConfig { public void setBroadcastChannelPrefix(String broadcastChannelPrefix) { this.broadcastChannelPrefix = broadcastChannelPrefix; } + + public String getSubscribeSubExpression() { + return subscribeSubExpression; + } + + public void setSubscribeSubExpression(String subscribeSubExpression) { + this.subscribeSubExpression = subscribeSubExpression; + } + + public String getInstanceName() { + return instanceName; + } + + public void setInstanceName(String instanceName) { + this.instanceName = instanceName; + } } diff --git a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java index 02004020..bbeeab1d 100644 --- a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java +++ b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java @@ -60,7 +60,7 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { Properties properties = createProperties(); consumer = ONSFactory.createConsumer(properties); for (String channel : channels) { - consumer.subscribe(aliyunmqConfig.getBroadcastChannelPrefix() + channel, "*", (message, consumeContext) -> { + consumer.subscribe(aliyunmqConfig.getBroadcastChannelPrefix() + channel, aliyunmqConfig.getSubscribeSubExpression(), (message, consumeContext) -> { notifyListeners(channel, getSerializer().deserialize(message.getBody())); return Action.CommitMessage; }); @@ -74,7 +74,7 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { properties.put(PropertyKeyConst.MessageModel, PropertyValueConst.BROADCASTING); consumer = ONSFactory.createConsumer(properties); for (String channel : channels) { - consumer.subscribe(channel, "*", (message, consumeContext) -> { + consumer.subscribe(channel, aliyunmqConfig.getSubscribeSubExpression(), (message, consumeContext) -> { notifyListeners(channel, getSerializer().deserialize(message.getBody())); return Action.CommitMessage; }); @@ -128,15 +128,17 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { } - private Properties createProperties() { - + protected Properties createProperties() { Properties properties = new Properties(); properties.put(PropertyKeyConst.AccessKey, aliyunmqConfig.getAccessKey());//AccessKey 阿里云身份验证,在阿里云服务器管理控制台创建 properties.put(PropertyKeyConst.SecretKey, aliyunmqConfig.getSecretKey());//SecretKey 阿里云身份验证,在阿里云服务器管理控制台创建 properties.put(PropertyKeyConst.ProducerId, aliyunmqConfig.getProducerId());//您在控制台创建的Producer ID properties.put(PropertyKeyConst.NAMESRV_ADDR, aliyunmqConfig.getAddr()); + properties.put(PropertyKeyConst.InstanceName, aliyunmqConfig.getInstanceName()); properties.setProperty(PropertyKeyConst.SendMsgTimeoutMillis, aliyunmqConfig.getSendMsgTimeoutMillis());//设置发送超时时间,单位毫秒 + + return properties; } } -- Gitee From 7fa899d49d32b0e02e0fcfd3d6a8100ec421c676 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 26 Nov 2021 10:24:58 +0800 Subject: [PATCH 1465/1965] optimize --- src/main/java/io/jboot/Jboot.java | 11 ++++++++++ .../serializer/JbootSerializerManager.java | 21 ++++++++++--------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/jboot/Jboot.java b/src/main/java/io/jboot/Jboot.java index 4436d50b..b31bc4e8 100644 --- a/src/main/java/io/jboot/Jboot.java +++ b/src/main/java/io/jboot/Jboot.java @@ -128,6 +128,17 @@ public class Jboot { } + /** + * 获取序列化对象 + * @param name + * @return + */ + public static JbootSerializer getSerializer(String name) { + return JbootSerializerManager.me().getSerializer(name); + } + + + /** * 获取配置信息 * diff --git a/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java b/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java index 0236b344..0e2ea564 100644 --- a/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java +++ b/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java @@ -17,9 +17,9 @@ package io.jboot.components.serializer; import io.jboot.Jboot; import io.jboot.core.spi.JbootSpiLoader; -import io.jboot.exception.JbootException; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.ClassUtil; +import io.jboot.utils.StrUtil; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -42,17 +42,18 @@ public class JbootSerializerManager { public JbootSerializer getSerializer() { JbootSerializerConfig config = Jboot.config(JbootSerializerConfig.class); + if (StrUtil.isBlank(config.getType())) { + throw new JbootIllegalConfigException("can not get serializer config, please set jboot.serializer value to jboot.proerties"); + } return getSerializer(config.getType()); } public JbootSerializer getSerializer(String serializerName) { - JbootSerializer serializer = serializerCaches.get(serializerName); - if (serializer == null) { - synchronized (this){ + synchronized (this) { serializer = serializerCaches.get(serializerName); - if (serializer == null){ + if (serializer == null) { serializer = buildSerializer(serializerName); serializerCaches.put(serializerName, serializer); } @@ -64,14 +65,14 @@ public class JbootSerializerManager { public JbootSerializer buildSerializer(String serializerName) { - if (serializerName == null){ - throw new JbootException("can not get serializer config, please set jboot.serializer value to jboot.proerties"); + if (serializerName == null) { + throw new NullPointerException("SerializerName must not be null"); } //可能是某个类名 - if (serializerName != null && serializerName.contains(".")) { - JbootSerializer serializer = ClassUtil.newInstance(serializerName,false); - if (serializer == null){ + if (serializerName.contains(".")) { + JbootSerializer serializer = ClassUtil.newInstance(serializerName, false); + if (serializer == null) { throw new JbootIllegalConfigException("can not new instance serializer by class: " + serializerName); } } -- Gitee From cde5ccbf56558740b2e15169e12094c3d6f3eca0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 26 Nov 2021 10:46:02 +0800 Subject: [PATCH 1466/1965] v3.12.0 release (^.^)YYa!! --- README.md | 5 +---- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 03a728fe..ba07c55e 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,6 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos - 10、Docker、K8S 友好 -> 另外:Jboot 目前已经开源超过了 5 年的时间,迭代了 200+ 个版本,已经被超过 10000+ 公司在使用,多个产品用户量超过 1 亿+ 。 - - ## 开始 **maven 依赖** @@ -26,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.11.4 + 3.12.0 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 671b8774..92ab82ef 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.11.4 + 3.12.0 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index cc430b48..035b0013 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.11.4 + 3.12.0 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 047cef70..e278bf18 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.11.4 + 3.12.0 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.11.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.12.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 64828aa3..4cd7aca4 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.11.4', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.12.0', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.11.4/jboot-3.11.4.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.12.0/jboot-3.12.0.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 3dc99f0b..f8245afd 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.11.4"; + public static String VERSION = "3.12.0"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From da97b247c4f8f44ff702b7fc78e5e71b5fc9e99e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 26 Nov 2021 10:46:09 +0800 Subject: [PATCH 1467/1965] v3.12.0 release (^.^)YYa!! --- changes.txt | 16 ++++++++++++++++ pom.xml | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index de97ccf4..7fd163e6 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,19 @@ +jboot v3.12.0: +新增:SPI 新增直接配置类名的功能 +新增:MQ 新增支持多个实例配置的功能支持 +新增:Redis 新增配置多个实例的支持 +新增:Cache 缓存新增配置多个实例的支持 +新增:限流组件新增 IP 限流功能 +新增:限流组件新增 IP 白名单功能 +优化:Rokect MQ 新增更多的配置支持 +优化:移动 AOP 的默认配置到 "jboot.aop.cache" +优化:"jboot.model.idCacheType" 配置名称修改为 "jboot.model.idCacheName" +优化:"jboot.web.session.cacheType" 配置名称修改为 "jboot.web.session.useCacheName" +优化:重命名 JbootConfigUtil 为 ConfigUtil +文档:修改文档错误已经添加部分文档 + + + jboot v3.11.4: 新增:新增 DAO.distinct() 方法,用于对内容进行去重 新增:MockMvc 新增 holdCookie 配置,用于在不同的测试用例里,保持 cookie 的功能 diff --git a/pom.xml b/pom.xml index 86dc6fc4..0ad6c7ca 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.12.0-SNAPSHOT + 3.12.0 jar jboot -- Gitee From 931b8047a9b369f5cc4ad9363abe1dad2ae3c4f5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 26 Nov 2021 10:59:38 +0800 Subject: [PATCH 1468/1965] v3.12.0 release (^.^)YYa!! --- src/main/java/io/jboot/db/model/JbootModelConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/model/JbootModelConfig.java b/src/main/java/io/jboot/db/model/JbootModelConfig.java index 70dae079..1dd7ff7c 100644 --- a/src/main/java/io/jboot/db/model/JbootModelConfig.java +++ b/src/main/java/io/jboot/db/model/JbootModelConfig.java @@ -66,7 +66,8 @@ public class JbootModelConfig { private String idCacheName = "default"; - + public JbootModelConfig() { + } public String getScanPackage() { return scanPackage; -- Gitee From 6ec9d017369343cdea718bda069f79077f39d53d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 26 Nov 2021 11:02:40 +0800 Subject: [PATCH 1469/1965] v3.12.0 release (^.^)YYa!! --- changes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 7fd163e6..0f686295 100644 --- a/changes.txt +++ b/changes.txt @@ -1,6 +1,6 @@ jboot v3.12.0: 新增:SPI 新增直接配置类名的功能 -新增:MQ 新增支持多个实例配置的功能支持 +新增:MQ 新增支持多个实例配置的支持 新增:Redis 新增配置多个实例的支持 新增:Cache 缓存新增配置多个实例的支持 新增:限流组件新增 IP 限流功能 -- Gitee From 4548f642da40d28283f3f29df251923bde48fef6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 30 Nov 2021 12:21:59 +0800 Subject: [PATCH 1470/1965] v3.12.1 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0ad6c7ca..76b261d9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.12.0 + 3.12.1-SNAPSHOT jar jboot -- Gitee From 84df64f3f2c909da1761eecf6d5a016e78e7cb0d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 30 Nov 2021 12:22:08 +0800 Subject: [PATCH 1471/1965] optimize --- .../java/io/jboot/aop/cglib/JbootCglibProxyFactory.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java b/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java index 80eb30c8..48bcfa3e 100644 --- a/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java @@ -35,7 +35,7 @@ public class JbootCglibProxyFactory extends ProxyFactory { /** * 方便在单元测试的时候,可以对任意 class 进行 Mock */ - private static Function,MethodInterceptor> methodInterceptor = aClass -> new JbootCglibCallback(); + private static Function, MethodInterceptor> methodInterceptor = aClass -> new JbootCglibCallback(); public static Function, MethodInterceptor> getMethodInterceptor() { return methodInterceptor; @@ -47,7 +47,12 @@ public class JbootCglibProxyFactory extends ProxyFactory { @Override public T get(Class target) { - return (T) net.sf.cglib.proxy.Enhancer.create(target, methodInterceptor.apply(target)); + try { + return (T) net.sf.cglib.proxy.Enhancer.create(target, methodInterceptor.apply(target)); + } catch (Exception e) { + //原始的错误,无法给出哪个类无法被创建,错误信息不够友好 + throw new RuntimeException("Can not create object for class:\"" + target.getName() + "\", cause:" + e.getMessage(), e); + } } -- Gitee From 12c136ee3a935a0be40f23140c67993deedf62b9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 30 Nov 2021 19:14:17 +0800 Subject: [PATCH 1472/1965] fixed... --- .../io/jboot/components/cache/JbootCacheManager.java | 4 +++- .../java/io/jboot/components/mq/JbootmqManager.java | 10 +++++++--- .../java/io/jboot/support/redis/JbootRedisManager.java | 4 +++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/JbootCacheManager.java b/src/main/java/io/jboot/components/cache/JbootCacheManager.java index d006c52b..4f869e5c 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheManager.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheManager.java @@ -72,7 +72,9 @@ public class JbootCacheManager { JbootCacheConfig cacheConfig = configModels.get(name); cache = buildCache(cacheConfig); - cacheMap.put(name, cache); + if (cache != null) { + cacheMap.put(name, cache); + } } } } diff --git a/src/main/java/io/jboot/components/mq/JbootmqManager.java b/src/main/java/io/jboot/components/mq/JbootmqManager.java index 719dd180..b2417d0e 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqManager.java +++ b/src/main/java/io/jboot/components/mq/JbootmqManager.java @@ -16,7 +16,6 @@ package io.jboot.components.mq; import io.jboot.Jboot; -import io.jboot.utils.ConfigUtil; import io.jboot.components.mq.aliyunmq.JbootAliyunmqImpl; import io.jboot.components.mq.local.JbootLocalmqImpl; import io.jboot.components.mq.qpidmq.JbootQpidmqImpl; @@ -26,6 +25,7 @@ import io.jboot.components.mq.rocketmq.JbootRocketmqImpl; import io.jboot.core.spi.JbootSpiLoader; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.ClassUtil; +import io.jboot.utils.ConfigUtil; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -53,7 +53,9 @@ public class JbootmqManager { if (defaultMq == null) { JbootmqConfig config = Jboot.config(JbootmqConfig.class); defaultMq = getJbootmq(config); - jbootmqMap.put("default", defaultMq); + if (defaultMq != null) { + jbootmqMap.put("default", defaultMq); + } } } } @@ -73,7 +75,9 @@ public class JbootmqManager { throw new JbootIllegalConfigException("Please config \"jboot.mq." + name + ".type\" in your jboot.properties."); } mq = getJbootmq(configModels.get(name)); - jbootmqMap.put(name, mq); + if (mq != null) { + jbootmqMap.put(name, mq); + } } } } diff --git a/src/main/java/io/jboot/support/redis/JbootRedisManager.java b/src/main/java/io/jboot/support/redis/JbootRedisManager.java index 91e3d395..53aa6ea1 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedisManager.java +++ b/src/main/java/io/jboot/support/redis/JbootRedisManager.java @@ -65,7 +65,9 @@ public class JbootRedisManager { } JbootRedisConfig jbootRedisConfig = configModels.get(name); redis = getRedis(jbootRedisConfig); - jbootRedisMap.put(name, redis); + if (redis != null) { + jbootRedisMap.put(name, redis); + } } } } -- Gitee From ec561f43248e2ef4c15d80bf6cf69e9d91534d9d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 1 Dec 2021 11:14:12 +0800 Subject: [PATCH 1473/1965] optimize --- .../io/jboot/components/rpc/JbootrpcBase.java | 23 +++++++++---------- .../jboot/components/rpc/JbootrpcConfig.java | 4 +++- .../jboot/components/rpc/JbootrpcManager.java | 17 +++++++++----- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java index b015b9d7..254d2c86 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java @@ -23,20 +23,19 @@ import java.util.concurrent.ConcurrentHashMap; public abstract class JbootrpcBase implements Jbootrpc { - protected static final Map objectCache = new ConcurrentHashMap<>(); + protected static final Map objectCache = new ConcurrentHashMap<>(); protected static JbootrpcConfig rpcConfig = Jboot.config(JbootrpcConfig.class); private boolean started = false; @Override public T serviceObtain(Class interfaceClass, JbootrpcReferenceConfig config) { - - T object = (T) objectCache.get(config); + String key = buildKey(interfaceClass, config); + T object = (T) objectCache.get(key); if (object == null) { synchronized (this) { - object = (T) objectCache.get(config); + object = (T) objectCache.get(key); if (object == null) { - // onStart 方法是在 app 启动完成后,Jboot 主动去调用的 // 但是,在某些场景可能存在没有等 app 启动完成就去获取 Service 的情况 // 此时,需要主动先调用下 onStart 方法 @@ -44,7 +43,7 @@ public abstract class JbootrpcBase implements Jbootrpc { object = onServiceCreate(interfaceClass, config); if (object != null) { - objectCache.put(config, object); + objectCache.put(key, object); } } } @@ -52,14 +51,14 @@ public abstract class JbootrpcBase implements Jbootrpc { return object; } + protected String buildKey(Class interfaceClass, JbootrpcReferenceConfig config) { + return interfaceClass.getName() + "@" + System.identityHashCode(config); + } + protected synchronized void invokeOnStartIfNecessary() { if (!started) { - synchronized (this) { - if (!started) { - onStart(); - setStarted(true); - } - } + onStart(); + setStarted(true); } } diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java index c3760aca..9e8577dd 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java @@ -28,8 +28,11 @@ public class JbootrpcConfig { public static final String TYPE_MOTAN = "motan"; public static final String TYPE_LOCAL = "local"; + + //使用的 RPC 类型 private String type; + //用于直连时的配置,直连一般只用于测试环境 //com.service.AAAService:127.0.0.1:8080,com.service.XXXService:127.0.0.1:8080 private Map urls; @@ -57,7 +60,6 @@ public class JbootrpcConfig { //本地自动暴露 @RPCBean 的 service private boolean autoExportEnable = true; - public String getType() { return type; } diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java index fd4ec265..40a3c05e 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java @@ -45,17 +45,19 @@ public class JbootrpcManager { private Jbootrpc jbootrpc; private JbootrpcConfig defaultConfig = Jboot.config(JbootrpcConfig.class); + public Jbootrpc getJbootrpc() { if (jbootrpc == null) { if (!defaultConfig.isConfigOk()) { - throw new JbootRpcException("jboot rpc config is error, please set up jboot.rpc.type config value"); + throw new JbootRpcException("Jboot RPC config is error, please set up \"jboot.rpc.type\" config value"); } - jbootrpc = createJbootrpc(defaultConfig.getType()); + jbootrpc = createJbootrpc(defaultConfig); } return jbootrpc; } + private static Class[] default_excludes = new Class[]{ JbootEventListener.class, JbootmqMessageListener.class, @@ -94,7 +96,7 @@ public class JbootrpcManager { RPCBean rpcBean = clazz.getAnnotation(RPCBean.class); Class[] inters = clazz.getInterfaces(); if (inters.length == 0) { - throw new JbootException("@RPCBean can not use for class \""+ ClassUtil.getUsefulClass(clazz).getName() +"\", because it has no interface."); + throw new JbootException("@RPCBean can not use for class \"" + ClassUtil.getUsefulClass(clazz).getName() + "\", because it has no interface."); } //对某些系统的类 进行排除,例如:Serializable 等 @@ -118,9 +120,12 @@ public class JbootrpcManager { } - public Jbootrpc createJbootrpc(String type) { + public Jbootrpc createJbootrpc(JbootrpcConfig config) { + if(!config.isConfigOk()){ + return null; + } - switch (type) { + switch (config.getType()) { case JbootrpcConfig.TYPE_DUBBO: return new JbootDubborpc(); case JbootrpcConfig.TYPE_MOTAN: @@ -128,7 +133,7 @@ public class JbootrpcManager { case JbootrpcConfig.TYPE_LOCAL: return new JbootLocalrpc(); default: - return JbootSpiLoader.load(Jbootrpc.class, type); + return JbootSpiLoader.load(Jbootrpc.class, config.getType()); } } -- Gitee From 14a6d3470e60553a7e45d4760621a29b06845025 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 1 Dec 2021 15:15:04 +0800 Subject: [PATCH 1474/1965] optimize --- pom.xml | 4 ++-- .../java/io/jboot/aop/JbootAopFactory.java | 4 ++-- .../components/cache/JbootCacheManager.java | 7 +++---- .../jboot/components/mq/JbootmqManager.java | 18 ++++------------- .../io/jboot/test/rpc/dubbonacos/readme.md | 20 ++++++++++++++++++- 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/pom.xml b/pom.xml index 76b261d9..49a053e4 100644 --- a/pom.xml +++ b/pom.xml @@ -47,9 +47,9 @@ 3.3 3.3.0 4.0.3 - 2.12.5 + 2.13.0 5.1.49 - 2.2.12.Final + 2.2.13.Final 1.7.32 2.57 1.2.78 diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index b15be315..5a160b9d 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -266,9 +266,9 @@ public class JbootAopFactory extends AopFactory { Jbootrpc jbootrpc = JbootrpcManager.me().getJbootrpc(); return jbootrpc.serviceObtain(fieldInjectedClass, referenceConfig); } catch (NullPointerException npe) { - LOG.error("Can not inject rpc service for \"" + field.getName() + "\" in " + ClassUtil.getUsefulClass(targetObject.getClass()) + ", because @RPCInject.check ==\"true\" and target is not available. \n" + rpcInject, npe); + LOG.error("Can not inject rpc service for \"" + field.getName() + "\" in class \"" + ClassUtil.getUsefulClass(targetObject.getClass()).getName() + "\", because @RPCInject.check ==\"true\" and target is not available. \n" + rpcInject, npe); } catch (Exception ex) { - LOG.error("Can not inject rpc service for \"" + field.getName() + "\" in " + ClassUtil.getUsefulClass(targetObject.getClass()) + " \n" + rpcInject, ex); + LOG.error("Can not inject rpc service for \"" + field.getName() + "\" in class \"" + ClassUtil.getUsefulClass(targetObject.getClass()).getName() + "\" \n" + rpcInject, ex); } return null; } diff --git a/src/main/java/io/jboot/components/cache/JbootCacheManager.java b/src/main/java/io/jboot/components/cache/JbootCacheManager.java index 4f869e5c..2848f05f 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheManager.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheManager.java @@ -16,7 +16,6 @@ package io.jboot.components.cache; import io.jboot.Jboot; -import io.jboot.utils.ConfigUtil; import io.jboot.components.cache.caffeine.CaffeineCacheImpl; import io.jboot.components.cache.caredis.JbootCaredisCacheImpl; import io.jboot.components.cache.ehcache.JbootEhcacheImpl; @@ -26,6 +25,7 @@ import io.jboot.components.cache.none.NoneCacheImpl; import io.jboot.components.cache.redis.JbootRedisCacheImpl; import io.jboot.core.spi.JbootSpiLoader; import io.jboot.exception.JbootIllegalConfigException; +import io.jboot.utils.ConfigUtil; import io.jboot.utils.StrUtil; import java.util.Map; @@ -40,14 +40,13 @@ public class JbootCacheManager { } private Map cacheMap = new ConcurrentHashMap<>(); - private JbootCacheConfig defaultConfig = Jboot.config(JbootCacheConfig.class); public static JbootCacheManager me() { return me; } public JbootCache getCache() { - return getCache(defaultConfig.getName()); + return getCache("default"); } public JbootCache getCache(String name) { @@ -64,7 +63,7 @@ public class JbootCacheManager { Map configModels = ConfigUtil.getConfigModels(JbootCacheConfig.class); JbootCacheConfig.TYPES.forEach(configModels::remove); - configModels.putIfAbsent("default", new JbootCacheConfig()); + configModels.putIfAbsent("default", Jboot.config(JbootCacheConfig.class)); if (!configModels.containsKey(name)) { throw new JbootIllegalConfigException("Please config \"jboot.cache." + name + ".type\" in your jboot.properties."); diff --git a/src/main/java/io/jboot/components/mq/JbootmqManager.java b/src/main/java/io/jboot/components/mq/JbootmqManager.java index b2417d0e..386e2ea6 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqManager.java +++ b/src/main/java/io/jboot/components/mq/JbootmqManager.java @@ -46,22 +46,10 @@ public class JbootmqManager { public Jbootmq getJbootmq() { - Jbootmq defaultMq = jbootmqMap.get("default"); - if (defaultMq == null) { - synchronized (this) { - defaultMq = jbootmqMap.get("default"); - if (defaultMq == null) { - JbootmqConfig config = Jboot.config(JbootmqConfig.class); - defaultMq = getJbootmq(config); - if (defaultMq != null) { - jbootmqMap.put("default", defaultMq); - } - } - } - } return getJbootmq("default"); } + public Jbootmq getJbootmq(String name) { Jbootmq mq = jbootmqMap.get(name); if (mq == null) { @@ -71,9 +59,12 @@ public class JbootmqManager { Map configModels = ConfigUtil.getConfigModels(JbootmqConfig.class); JbootmqConfig.TYPES.forEach(configModels::remove); + configModels.putIfAbsent("default", Jboot.config(JbootmqConfig.class)); + if (!configModels.containsKey(name)) { throw new JbootIllegalConfigException("Please config \"jboot.mq." + name + ".type\" in your jboot.properties."); } + mq = getJbootmq(configModels.get(name)); if (mq != null) { jbootmqMap.put(name, mq); @@ -81,7 +72,6 @@ public class JbootmqManager { } } } - return mq; } diff --git a/src/test/java/io/jboot/test/rpc/dubbonacos/readme.md b/src/test/java/io/jboot/test/rpc/dubbonacos/readme.md index 6f524738..6514ca2c 100644 --- a/src/test/java/io/jboot/test/rpc/dubbonacos/readme.md +++ b/src/test/java/io/jboot/test/rpc/dubbonacos/readme.md @@ -1,3 +1,21 @@ 在开始之前,先启动 nacos,启动建议使用 docker 的方式,更加方便。 -参考 https://nacos.io/zh-cn/docs/quick-start-docker.html \ No newline at end of file +Clone 项目 + +```shell +git clone https://github.com/nacos-group/nacos-docker.git +cd nacos-docker +``` + +单机模式 Derby + +```shell +docker-compose -f example/standalone-derby.yaml up +``` + + +访问Nacos 控制台 :[http://127.0.0.1:8848/nacos/ +](http://127.0.0.1:8848/nacos/) + + +更多参考 https://nacos.io/zh-cn/docs/quick-start-docker.html \ No newline at end of file -- Gitee From f2769e3e09820f3a74936757c60fb5925d56a63e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 1 Dec 2021 16:31:23 +0800 Subject: [PATCH 1475/1965] v3.12.1 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ba07c55e..52bc6fea 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.12.0 + 3.12.1 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 92ab82ef..106428f7 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.12.0 + 3.12.1 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 035b0013..21dbadb5 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.12.0 + 3.12.1 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index e278bf18..eb5c8efd 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.12.0 + 3.12.1 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.12.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.12.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 4cd7aca4..538a8168 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.12.0', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.12.1', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.12.0/jboot-3.12.0.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.12.1/jboot-3.12.1.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index f8245afd..4dc0f6ad 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.12.0"; + public static String VERSION = "3.12.1"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From b9d87cf44be77e4452b33ee57a95ff5e9098719e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 1 Dec 2021 16:31:33 +0800 Subject: [PATCH 1476/1965] v3.12.1 release (^.^)YYa!! --- changes.txt | 7 +++++++ pom.xml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 0f686295..4acd5c86 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.12.1: +优化:当 cglib 创建对象出错时,错误信息不明确的问题 +优化:Cache、MQ、Redis 的 Manager 在某些情况下可能出现 NPE 的问题 +优化:升级相关依赖到最新版本 + + + jboot v3.12.0: 新增:SPI 新增直接配置类名的功能 新增:MQ 新增支持多个实例配置的支持 diff --git a/pom.xml b/pom.xml index 49a053e4..5ca55314 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.12.1-SNAPSHOT + 3.12.1 jar jboot -- Gitee From 7380bc56e517b0274ba787b7646bdb8b14c59cb4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 5 Dec 2021 16:58:57 +0800 Subject: [PATCH 1477/1965] v3.12.2 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5ca55314..244ecee4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.12.1 + 3.12.2-SNAPSHOT jar jboot -- Gitee From 2660c094548e83b5bc97aacc6fed17b6cc6a4275 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 5 Dec 2021 17:00:11 +0800 Subject: [PATCH 1478/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E5=A4=84=E7=90=86=20404=20=E9=A1=B5=E9=9D=A2=E7=9A=84=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=94=AF=E6=8C=81=EF=BC=8C=E6=96=B9=E4=BE=BF=E4=B8=8E?= =?UTF-8?q?=E5=85=B6=E4=BB=96=20web=20=E6=A1=86=E6=9E=B6=E6=95=B4=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/app/JbootApplicationConfig.java | 9 ++++++++ .../jboot/web/handler/JbootActionHandler.java | 21 ++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/app/JbootApplicationConfig.java b/src/main/java/io/jboot/app/JbootApplicationConfig.java index 464bf217..118e6682 100644 --- a/src/main/java/io/jboot/app/JbootApplicationConfig.java +++ b/src/main/java/io/jboot/app/JbootApplicationConfig.java @@ -29,6 +29,7 @@ public class JbootApplicationConfig { private String bannerFile = "banner.txt"; private String jfinalConfig = "io.jboot.core.JbootCoreConfig"; private String listener = "*"; + private boolean handle404 = true; public String getMode() { @@ -79,6 +80,14 @@ public class JbootApplicationConfig { this.listener = listener; } + public boolean isHandle404() { + return handle404; + } + + public void setHandle404(boolean handle404) { + this.handle404 = handle404; + } + private static JbootApplicationConfig instance; public static JbootApplicationConfig get() { diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 2cb7169d..904df896 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -21,6 +21,7 @@ import com.jfinal.log.Log; import com.jfinal.render.IRenderFactory; import com.jfinal.render.Render; import com.jfinal.render.RenderException; +import io.jboot.app.JbootApplicationConfig; import io.jboot.components.valid.ValidErrorRender; import io.jboot.components.valid.ValidException; import io.jboot.components.valid.ValidUtil; @@ -40,6 +41,7 @@ public class JbootActionHandler extends ActionHandler { private static final Log LOG = Log.getLog(JbootActionHandler.class); + private static final JbootApplicationConfig appConfig = JbootApplicationConfig.get(); /** * 方便子类复写、从而可以实现 自定义 Action 的功能 @@ -95,6 +97,11 @@ public class JbootActionHandler extends ActionHandler { Action action = getAction(target, urlPara, request); if (action == null) { + if (!appConfig.isHandle404()) { + isHandled[0] = false; + return; + } + if (LOG.isWarnEnabled()) { String qs = request.getQueryString(); LOG.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs)); @@ -102,8 +109,6 @@ public class JbootActionHandler extends ActionHandler { renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); return; } - - Controller controller = null; try { controller = controllerFactory.getController(action.getControllerClass()); @@ -132,6 +137,10 @@ public class JbootActionHandler extends ActionHandler { LOG.error(qs == null ? target : target + "?" + qs, e); } } catch (ActionException e) { + if (e.getErrorCode() == 404 && !appConfig.isHandle404()) { + isHandled[0] = false; + return; + } handleActionException(target, request, response, action, e); } catch (ValidException e) { handleValidException(target, request, response, action, e); @@ -178,7 +187,13 @@ public class JbootActionHandler extends ActionHandler { /** - * 处理 Action(Controller的方法)执行错误 + * 处理错误信息 + * + * @param target + * @param request + * @param response + * @param action + * @param e */ protected void handleActionException(String target, HttpServletRequest request, HttpServletResponse response, Action action, ActionException e) { int errorCode = e.getErrorCode(); -- Gitee From c90236f6366ea4f0fbf1e7fe179389db65284bbf Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 5 Dec 2021 17:00:24 +0800 Subject: [PATCH 1479/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=20AttachmentManage?= =?UTF-8?q?r.java?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/web/attachment/AttachmentManager.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index a8c4bfed..67c41542 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -216,7 +216,7 @@ public class AttachmentManager { //优先从 默认的 container 去获取 File file = defaultContainer.getFile(relativePath); - if (file != null && file.exists()) { + if (file.exists()) { return file; } @@ -230,7 +230,9 @@ public class AttachmentManager { LOG.error("Get file error in container :" + container, ex); } } - return null; + + //文件不存在,也返回该文件 + return file; } /** -- Gitee From 1e1967d044c0c1c23be1d286076e2a99e0dbcfea Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 6 Dec 2021 11:01:11 +0800 Subject: [PATCH 1480/1965] add JbootAppListener.onConstantConfigBefore() method --- .../java/io/jboot/core/JbootCoreConfig.java | 6 ++--- .../jboot/core/listener/JbootAppListener.java | 24 ++++++++++--------- .../listener/JbootAppListenerManager.java | 11 +++++++++ .../LocalAttachmentContainerConfig.java | 8 +++---- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 0530b85d..8460d35d 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -21,7 +21,6 @@ import com.jfinal.config.*; import com.jfinal.core.Controller; import com.jfinal.core.Path; import com.jfinal.core.converter.TypeConverter; -import com.jfinal.json.JsonManager; import com.jfinal.kit.PathKit; import com.jfinal.kit.PropKit; import com.jfinal.plugin.activerecord.ActiveRecordPlugin; @@ -145,6 +144,8 @@ public class JbootCoreConfig extends JFinalConfig { @Override public void configConstant(Constants constants) { + JbootAppListenerManager.me().onConstantConfigBefore(constants); + constants.setRenderFactory(JbootRenderFactory.me()); constants.setDevMode(Jboot.isDevMode()); @@ -160,6 +161,7 @@ public class JbootCoreConfig extends JFinalConfig { constants.setCaptchaCache(new JbootCaptchaCache()); constants.setBaseUploadPath(LocalAttachmentContainerConfig.getInstance().buildUploadAbsolutePath()); + constants.setJsonDatePattern(DateUtil.datetimePattern); if (JbootWebConfig.getInstance().isPathVariableEnable()){ constants.setActionMapping(PathVariableActionMapping::new); @@ -372,8 +374,6 @@ public class JbootCoreConfig extends JFinalConfig { JbootAppListenerManager.me().onStartBefore(); - JsonManager.me().setDefaultDatePattern(DateUtil.dateMillisecondPattern); - // 初始化 Jboot 内置组件 JbootrpcManager.me().init(); JbootShiroManager.me().init(routeList); diff --git a/src/main/java/io/jboot/core/listener/JbootAppListener.java b/src/main/java/io/jboot/core/listener/JbootAppListener.java index 47b9c3ba..417f5a72 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListener.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListener.java @@ -25,26 +25,28 @@ import io.jboot.aop.jfinal.JfinalPlugins; public interface JbootAppListener { - public void onInit(); + default void onInit(){} - public void onConstantConfig(Constants constants); + default void onConstantConfigBefore(Constants constants){} - public void onRouteConfig(Routes routes); + default void onConstantConfig(Constants constants){} - public void onEngineConfig(Engine engine); + default void onRouteConfig(Routes routes){} - public void onPluginConfig(JfinalPlugins plugins); + default void onEngineConfig(Engine engine){} - public void onInterceptorConfig(Interceptors interceptors); + default void onPluginConfig(JfinalPlugins plugins){} - public void onHandlerConfig(JfinalHandlers handlers); + default void onInterceptorConfig(Interceptors interceptors){} - public void onStartBefore(); + default void onHandlerConfig(JfinalHandlers handlers){} - public void onStart(); + default void onStartBefore(){} - public void onStartFinish(); + default void onStart(){} - public void onStop(); + default void onStartFinish(){} + + default void onStop(){} } diff --git a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java index b8bfd9ea..af4d58e6 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java @@ -90,6 +90,17 @@ public class JbootAppListenerManager implements JbootAppListener { } } + @Override + public void onConstantConfigBefore(Constants constants) { + for (JbootAppListener listener : listeners) { + try { + listener.onConstantConfigBefore(constants); + } catch (Throwable ex) { + log.error(ex.toString(), ex); + } + } + } + @Override public void onConstantConfig(Constants constants) { for (JbootAppListener listener : listeners) { diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java index 925a5a48..8dd3bf2e 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java @@ -25,7 +25,7 @@ import java.io.File; * @author michael yang (fuhai999@gmail.com) */ @ConfigModel(prefix = "jboot.attachment") -public class LocalAttachmentContainerConfig { +public class LocalAttachmentContainerConfig { private String rootPath = PathKit.getWebRootPath(); private String targetPrefix = "/attachment"; @@ -46,11 +46,11 @@ public class LocalAttachmentContainerConfig { this.targetPrefix = targetPrefix; } - public static LocalAttachmentContainerConfig getInstance(){ + public static LocalAttachmentContainerConfig getInstance() { return Jboot.config(LocalAttachmentContainerConfig.class); } - public String buildUploadAbsolutePath(){ - return new File(rootPath,targetPrefix).getAbsolutePath(); + public String buildUploadAbsolutePath() { + return new File(rootPath, targetPrefix).getAbsolutePath(); } } -- Gitee From 2b2a333e84b8bc4ac5794098eb2486252b57a161 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 7 Dec 2021 10:50:08 +0800 Subject: [PATCH 1481/1965] v3.12.2 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 52bc6fea..c1a16a96 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.12.1 + 3.12.2 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 106428f7..d2395703 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.12.1 + 3.12.2 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 21dbadb5..0aefa1e3 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.12.1 + 3.12.2 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index eb5c8efd..fc734a44 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.12.1 + 3.12.2 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.12.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.12.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 538a8168..308298f5 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.12.1', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.12.2', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.12.1/jboot-3.12.1.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.12.2/jboot-3.12.2.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 4dc0f6ad..ad70498b 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.12.1"; + public static String VERSION = "3.12.2"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 0e4a593eece811eb82bfbe354e711afd2755145d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 7 Dec 2021 10:50:37 +0800 Subject: [PATCH 1482/1965] v3.12.2 release (^.^)YYa!! --- changes.txt | 8 ++++++++ pom.xml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 4acd5c86..14a4e2b0 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,11 @@ +jboot v3.12.2: +新增:是否处理 404 页面的配置支持,方便与其他 web 框架整合 +新增:JbootAppListener 新增 onConstantConfigBefore() 方法 +优化:AttachmentManager.java,使之 getFile 不返回 null +优化:JsonManager.me().setDefaultDatePattern 的默认配置 + + + jboot v3.12.1: 优化:当 cglib 创建对象出错时,错误信息不明确的问题 优化:Cache、MQ、Redis 的 Manager 在某些情况下可能出现 NPE 的问题 diff --git a/pom.xml b/pom.xml index 244ecee4..41a34716 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.12.2-SNAPSHOT + 3.12.2 jar jboot -- Gitee From aac3f45dd374681ad74ada58602a530482378a2a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 15 Dec 2021 20:09:11 +0800 Subject: [PATCH 1483/1965] v3.12.3 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 41a34716..42882bb1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.12.2 + 3.12.3-SNAPSHOT jar jboot -- Gitee From 10e83438a8f6e8f4c9f547d0ec252ddb62f40567 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 15 Dec 2021 20:09:31 +0800 Subject: [PATCH 1484/1965] add filter annotation --- src/main/java/io/jboot/aop/ValueFilter.java | 20 +++++++ .../io/jboot/aop/ValueFilterInterceptor.java | 57 +++++++++++++++++++ .../java/io/jboot/aop/annotation/Filter.java | 28 +++++++++ 3 files changed, 105 insertions(+) create mode 100644 src/main/java/io/jboot/aop/ValueFilter.java create mode 100644 src/main/java/io/jboot/aop/ValueFilterInterceptor.java create mode 100644 src/main/java/io/jboot/aop/annotation/Filter.java diff --git a/src/main/java/io/jboot/aop/ValueFilter.java b/src/main/java/io/jboot/aop/ValueFilter.java new file mode 100644 index 00000000..43023d8c --- /dev/null +++ b/src/main/java/io/jboot/aop/ValueFilter.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop; + +public interface ValueFilter { + Object filter(Object orignal); +} diff --git a/src/main/java/io/jboot/aop/ValueFilterInterceptor.java b/src/main/java/io/jboot/aop/ValueFilterInterceptor.java new file mode 100644 index 00000000..771bcdde --- /dev/null +++ b/src/main/java/io/jboot/aop/ValueFilterInterceptor.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop; + +import com.jfinal.aop.Aop; +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import io.jboot.aop.annotation.AutoLoad; +import io.jboot.aop.annotation.Filter; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; + +@AutoLoad +public class ValueFilterInterceptor implements Interceptor, InterceptorBuilder { + + @Override + public void intercept(Invocation inv) { + Parameter[] parameters = inv.getMethod().getParameters(); + for (int index = 0; index < parameters.length; index++) { + Filter filter = parameters[index].getAnnotation(Filter.class); + if (filter != null) { + Object orignal = inv.getArg(index); + ValueFilter vf = Aop.get(filter.value()); + inv.setArg(index, vf.filter(orignal)); + } + } + inv.invoke(); + } + + + @Override + public void build(Class targetClass, Method method, Interceptors interceptors) { + Parameter[] parameters = method.getParameters(); + if (parameters != null && parameters.length > 0) { + for (Parameter p : parameters) { + if (p.getAnnotation(Filter.class) != null) { + interceptors.addIfNotExist(this); + } + } + } + + } +} \ No newline at end of file diff --git a/src/main/java/io/jboot/aop/annotation/Filter.java b/src/main/java/io/jboot/aop/annotation/Filter.java new file mode 100644 index 00000000..0498793d --- /dev/null +++ b/src/main/java/io/jboot/aop/annotation/Filter.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop.annotation; + +import io.jboot.aop.ValueFilter; + +import java.lang.annotation.*; + + +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PARAMETER}) +public @interface Filter { + Class value(); +} -- Gitee From 37d843f2f73c5092549eddb3ce39aedfbf69bf18 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 15 Dec 2021 21:26:13 +0800 Subject: [PATCH 1485/1965] add @FilterBy() annotation --- src/main/java/io/jboot/aop/ValueFilter.java | 2 +- .../io/jboot/aop/ValueFilterInterceptor.java | 18 ++++++---- .../annotation/{Filter.java => FilterBy.java} | 4 +-- .../io/jboot/test/aop/FilterController.java | 35 +++++++++++++++++++ 4 files changed, 50 insertions(+), 9 deletions(-) rename src/main/java/io/jboot/aop/annotation/{Filter.java => FilterBy.java} (91%) create mode 100644 src/test/java/io/jboot/test/aop/FilterController.java diff --git a/src/main/java/io/jboot/aop/ValueFilter.java b/src/main/java/io/jboot/aop/ValueFilter.java index 43023d8c..946fff1a 100644 --- a/src/main/java/io/jboot/aop/ValueFilter.java +++ b/src/main/java/io/jboot/aop/ValueFilter.java @@ -16,5 +16,5 @@ package io.jboot.aop; public interface ValueFilter { - Object filter(Object orignal); + Object doFilter(Object orignal); } diff --git a/src/main/java/io/jboot/aop/ValueFilterInterceptor.java b/src/main/java/io/jboot/aop/ValueFilterInterceptor.java index 771bcdde..d4ea9609 100644 --- a/src/main/java/io/jboot/aop/ValueFilterInterceptor.java +++ b/src/main/java/io/jboot/aop/ValueFilterInterceptor.java @@ -19,7 +19,7 @@ import com.jfinal.aop.Aop; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import io.jboot.aop.annotation.AutoLoad; -import io.jboot.aop.annotation.Filter; +import io.jboot.aop.annotation.FilterBy; import java.lang.reflect.Method; import java.lang.reflect.Parameter; @@ -31,13 +31,20 @@ public class ValueFilterInterceptor implements Interceptor, InterceptorBuilder { public void intercept(Invocation inv) { Parameter[] parameters = inv.getMethod().getParameters(); for (int index = 0; index < parameters.length; index++) { - Filter filter = parameters[index].getAnnotation(Filter.class); + FilterBy filter = parameters[index].getAnnotation(FilterBy.class); if (filter != null) { Object orignal = inv.getArg(index); - ValueFilter vf = Aop.get(filter.value()); - inv.setArg(index, vf.filter(orignal)); + + Class[] classes = filter.value(); + for (Class aClass : classes) { + ValueFilter vf = Aop.get(aClass); + orignal = vf.doFilter(orignal); + } + + inv.setArg(index, orignal); } } + inv.invoke(); } @@ -47,11 +54,10 @@ public class ValueFilterInterceptor implements Interceptor, InterceptorBuilder { Parameter[] parameters = method.getParameters(); if (parameters != null && parameters.length > 0) { for (Parameter p : parameters) { - if (p.getAnnotation(Filter.class) != null) { + if (p.getAnnotation(FilterBy.class) != null) { interceptors.addIfNotExist(this); } } } - } } \ No newline at end of file diff --git a/src/main/java/io/jboot/aop/annotation/Filter.java b/src/main/java/io/jboot/aop/annotation/FilterBy.java similarity index 91% rename from src/main/java/io/jboot/aop/annotation/Filter.java rename to src/main/java/io/jboot/aop/annotation/FilterBy.java index 0498793d..02f9c3b0 100644 --- a/src/main/java/io/jboot/aop/annotation/Filter.java +++ b/src/main/java/io/jboot/aop/annotation/FilterBy.java @@ -23,6 +23,6 @@ import java.lang.annotation.*; @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) -public @interface Filter { - Class value(); +public @interface FilterBy { + Class[] value(); } diff --git a/src/test/java/io/jboot/test/aop/FilterController.java b/src/test/java/io/jboot/test/aop/FilterController.java new file mode 100644 index 00000000..b8c2f81f --- /dev/null +++ b/src/test/java/io/jboot/test/aop/FilterController.java @@ -0,0 +1,35 @@ +package io.jboot.test.aop; + +import com.jfinal.core.Controller; +import io.jboot.aop.ValueFilter; +import io.jboot.aop.annotation.FilterBy; +import io.jboot.web.controller.annotation.RequestMapping; + +@RequestMapping("/aop/filter") +public class FilterController extends Controller { + + public void test1(@FilterBy(Filter1.class) String orderBy) { + renderText(orderBy); + } + + public void test2(@FilterBy({Filter1.class, Filter2.class}) String orderBy) { + renderText(orderBy); + } + + + public static class Filter1 implements ValueFilter { + + @Override + public Object doFilter(Object orignal) { + return orignal + "filter1"; + } + } + + public static class Filter2 implements ValueFilter { + + @Override + public Object doFilter(Object orignal) { + return orignal + "filter2"; + } + } +} -- Gitee From 8b4f2f717bc21377928026260c1e1830c8e73f34 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 15 Dec 2021 21:41:36 +0800 Subject: [PATCH 1486/1965] add @FilterBy() annotation --- .../io/jboot/aop/DefaultValueInterceptor.java | 2 ++ .../LimiterInterceptorBuilder.java | 3 ++- .../io/jboot/test/aop/FilterController.java | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/aop/DefaultValueInterceptor.java b/src/main/java/io/jboot/aop/DefaultValueInterceptor.java index d5521816..f26dff3a 100644 --- a/src/main/java/io/jboot/aop/DefaultValueInterceptor.java +++ b/src/main/java/io/jboot/aop/DefaultValueInterceptor.java @@ -19,12 +19,14 @@ import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import io.jboot.aop.annotation.AutoLoad; import io.jboot.aop.annotation.DefaultValue; +import io.jboot.core.weight.Weight; import io.jboot.utils.ObjectUtil; import java.lang.reflect.Method; import java.lang.reflect.Parameter; @AutoLoad +@Weight(999) public class DefaultValueInterceptor implements Interceptor, InterceptorBuilder { @Override diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java index b19e8465..c60159b9 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java @@ -20,6 +20,7 @@ import io.jboot.aop.Interceptors; import io.jboot.aop.annotation.AutoLoad; import io.jboot.components.limiter.LimiterManager; import io.jboot.components.limiter.annotation.EnableLimit; +import io.jboot.core.weight.Weight; import java.lang.reflect.Method; @@ -27,6 +28,7 @@ import java.lang.reflect.Method; * @author michael yang (fuhai999@gmail.com) */ @AutoLoad +@Weight(-1) public class LimiterInterceptorBuilder implements InterceptorBuilder { private LimiterManager manager = LimiterManager.me(); @@ -42,7 +44,6 @@ public class LimiterInterceptorBuilder implements InterceptorBuilder { if (Util.hasAnnotation(method, EnableLimit.class)) { interceptors.add(LimiterInterceptor.class); } - } diff --git a/src/test/java/io/jboot/test/aop/FilterController.java b/src/test/java/io/jboot/test/aop/FilterController.java index b8c2f81f..4608eebd 100644 --- a/src/test/java/io/jboot/test/aop/FilterController.java +++ b/src/test/java/io/jboot/test/aop/FilterController.java @@ -2,6 +2,7 @@ package io.jboot.test.aop; import com.jfinal.core.Controller; import io.jboot.aop.ValueFilter; +import io.jboot.aop.annotation.DefaultValue; import io.jboot.aop.annotation.FilterBy; import io.jboot.web.controller.annotation.RequestMapping; @@ -17,6 +18,15 @@ public class FilterController extends Controller { } + public void test3(@FilterBy({Filter3.class}) String orderBy) { + renderText(orderBy); + } + + public void test4(@FilterBy({Filter3.class}) @DefaultValue("id desc") String orderBy) { + renderText(orderBy); + } + + public static class Filter1 implements ValueFilter { @Override @@ -32,4 +42,13 @@ public class FilterController extends Controller { return orignal + "filter2"; } } + + + public static class Filter3 implements ValueFilter { + + @Override + public Object doFilter(Object orignal) { + return null; + } + } } -- Gitee From 44d3279c667aa92c5a7eab6883b50022782b2f38 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 15 Dec 2021 21:46:40 +0800 Subject: [PATCH 1487/1965] v3.12.3 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c1a16a96..e7dbe49d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.12.2 + 3.12.3 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index d2395703..ca086324 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.12.2 + 3.12.3 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 0aefa1e3..823ef85c 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.12.2 + 3.12.3 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index fc734a44..bced9f0a 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.12.2 + 3.12.3 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.12.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.12.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 308298f5..916d6f72 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.12.2', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.12.3', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.12.2/jboot-3.12.2.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.12.3/jboot-3.12.3.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index ad70498b..db7aa401 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.12.2"; + public static String VERSION = "3.12.3"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 57c3a408e3eea8636622b1ec86b1b1328f45e3e1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 15 Dec 2021 21:46:55 +0800 Subject: [PATCH 1488/1965] v3.12.3 release (^.^)YYa!! --- changes.txt | 5 +++++ pom.xml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 14a4e2b0..b42d3dc7 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,8 @@ +jboot v3.12.3: +新增:@FilterBy() 注解,用于对某些参数进行内容过滤 + + + jboot v3.12.2: 新增:是否处理 404 页面的配置支持,方便与其他 web 框架整合 新增:JbootAppListener 新增 onConstantConfigBefore() 方法 diff --git a/pom.xml b/pom.xml index 42882bb1..c1d86541 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.12.3-SNAPSHOT + 3.12.3 jar jboot -- Gitee From 0288fdec24b52817a09adb928f6f0bbc3f2a660f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Dec 2021 09:33:29 +0800 Subject: [PATCH 1489/1965] v3.12.4 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e7dbe49d..7177f327 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.12.3 + 3.12.4 ``` diff --git a/changes.txt b/changes.txt index b42d3dc7..6d0e3fa3 100644 --- a/changes.txt +++ b/changes.txt @@ -1,4 +1,4 @@ -jboot v3.12.3: +jboot v3.12.4: 新增:@FilterBy() 注解,用于对某些参数进行内容过滤 diff --git a/doc/docs/install.md b/doc/docs/install.md index ca086324..451202d2 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.12.3 + 3.12.4 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 823ef85c..880ee704 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.12.3 + 3.12.4 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index bced9f0a..49e0056c 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.12.3 + 3.12.4 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.12.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.12.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 916d6f72..9ea17e0c 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.12.3', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.12.4', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.12.3/jboot-3.12.3.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.12.4/jboot-3.12.4.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/pom.xml b/pom.xml index c1d86541..afc8f348 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.12.3 + 3.12.4 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index db7aa401..10a8d243 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.12.3"; + public static String VERSION = "3.12.4"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 0e0d56ec9778c4d1988cbc4870c09d1814e68f24 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Dec 2021 20:46:03 +0800 Subject: [PATCH 1490/1965] v3.12.5 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index afc8f348..f726408a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.12.4 + 3.12.5-SNAPSHOT jar jboot -- Gitee From d88822e8063ccc58bb131612686298162e81835c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Dec 2021 20:46:37 +0800 Subject: [PATCH 1491/1965] fixed cache key build --- .../components/cache/interceptor/Utils.java | 125 +++++++++++++----- 1 file changed, 95 insertions(+), 30 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/interceptor/Utils.java b/src/main/java/io/jboot/components/cache/interceptor/Utils.java index 592c962b..390cf086 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/Utils.java +++ b/src/main/java/io/jboot/components/cache/interceptor/Utils.java @@ -19,16 +19,18 @@ import com.jfinal.template.Engine; import io.jboot.components.cache.AopCache; import io.jboot.components.cache.JbootAopCacheConfig; import io.jboot.components.cache.annotation.CacheEvict; +import io.jboot.db.model.Columns; import io.jboot.exception.JbootException; -import io.jboot.utils.AnnotationUtil; -import io.jboot.utils.ArrayUtil; -import io.jboot.utils.ClassUtil; -import io.jboot.utils.StrUtil; +import io.jboot.utils.*; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -67,49 +69,46 @@ class Utils { return renderKey(key, method, arguments); } - StringBuilder keyBuilder = new StringBuilder(clazz.getName()); - keyBuilder.append('#').append(method.getName()); + StringBuilder keyBuilder = new StringBuilder(clazz.getSimpleName()); + keyBuilder.append('.').append(method.getName()); if (ArrayUtil.isNullOrEmpty(arguments)) { - return keyBuilder.toString(); + return keyBuilder.append("()").toString(); } - Class[] paramTypes = method.getParameterTypes(); + Class[] paramTypes = method.getParameterTypes(); int index = 0; for (Object argument : arguments) { - String argString = converteToString(argument); - ensureArgumentNotNull(argString, method); - - if (index > 0){ - keyBuilder.append('-'); + String argString = converteToString(argument, method); + if (index == 0) { + keyBuilder.append("("); + } else { + keyBuilder.append(", "); } - keyBuilder.append(paramTypes[index++].getClass().getName()) + keyBuilder.append(paramTypes[index++].getSimpleName()) .append(':') .append(argString); + + if (index == arguments.length) { + keyBuilder.append(")"); + } } return keyBuilder.toString(); } private static String renderKey(String key, Method method, Object[] arguments) { - int indexOfStartFlag = key.indexOf("#("); - if (indexOfStartFlag > -1){ + int indexOfStartFlag = key.indexOf("#("); + if (indexOfStartFlag > -1) { int indexOfEndFlag = key.indexOf(")"); - if (indexOfEndFlag > indexOfStartFlag){ - return engineRender(key,method,arguments); + if (indexOfEndFlag > indexOfStartFlag) { + return engineRender(key, method, arguments); } } return key; } - public static void ensureArgumentNotNull(String argument, Method method) { - if (argument == null) { - throw new JbootException("not support empty key for annotation @Cacheable, @CacheEvict or @CachePut " + - "at method[" + ClassUtil.buildMethodString(method) + "], " + - "please config key properties in @Cacheable, @CacheEvict or @CachePut annotation."); - } - } public static void ensureCachenameAvailable(Method method, String cacheName) { if (StrUtil.isBlank(cacheName)) { @@ -119,10 +118,12 @@ class Utils { } - static boolean isPrimitive(Class clazz) { + static boolean isSupportClass(Class clazz) { return clazz == String.class || clazz == Integer.class || clazz == int.class + || clazz == Short.class + || clazz == short.class || clazz == Long.class || clazz == long.class || clazz == Double.class @@ -131,27 +132,91 @@ class Utils { || clazz == float.class || clazz == Boolean.class || clazz == boolean.class + || clazz == char.class || clazz == BigDecimal.class || clazz == BigInteger.class || clazz == java.util.Date.class || clazz == java.sql.Date.class || clazz == java.sql.Timestamp.class - || clazz == java.sql.Time.class; + || clazz == java.sql.Time.class + || clazz == LocalDate.class + || clazz == LocalDateTime.class + || clazz == LocalTime.class + || clazz.isArray() + || clazz.isAssignableFrom(Collection.class) + || clazz == Columns.class + ; } - static String converteToString(Object object) { + static String converteToString(Object object, Method method) { if (object == null) { return "null"; } - if (!isPrimitive(object.getClass())) { - return null; + + if (!isSupportClass(object.getClass())) { + String msg = "Unsupported empty key for annotation @Cacheable, @CacheEvict or @CachePut " + + "at method[" + ClassUtil.buildMethodString(method) + "], " + + "Please config key properties in the annotation."; + throw new IllegalArgumentException(msg); + } + + if (object.getClass().isArray()) { + StringBuilder ret = new StringBuilder(); + Object[] values = (Object[]) object; + int index = 0; + for (Object value : values) { + if (index == 0) { + ret.append('['); + } + ret.append(converteToString(value, method)); + if (++index != values.length) { + ret.append(','); + } else { + ret.append(']'); + } + } + return ret.toString(); + } + + if (object instanceof Collection) { + Collection c = (Collection) object; + StringBuilder ret = new StringBuilder(); + int index = 0; + for (Object o : c) { + if (index == 0) { + ret.append('['); + } + ret.append(converteToString(o, method)); + if (++index != c.size()) { + ret.append(','); + } else { + ret.append(']'); + } + } + return ret.toString(); } if (object instanceof java.util.Date) { return String.valueOf(((java.util.Date) object).getTime()); } + if (object instanceof LocalDateTime) { + return String.valueOf(DateUtil.toDate((LocalDateTime) object).getTime()); + } + + if (object instanceof LocalDate) { + return String.valueOf(DateUtil.toDate((LocalDate) object).getTime()); + } + + if (object instanceof LocalTime) { + return String.valueOf(DateUtil.toDate((LocalTime) object).getTime()); + } + + if (object instanceof Columns) { + return ((Columns) object).getCacheKey(); + } + return String.valueOf(object); } -- Gitee From 1c9f4bf5cf49a83bb131c5e3ebd5927a96f257fc Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Dec 2021 21:12:30 +0800 Subject: [PATCH 1492/1965] v3.12.5 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7177f327..489ce44f 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.12.4 + 3.12.5 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 451202d2..9bff06fa 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.12.4 + 3.12.5 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 880ee704..08aa9396 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.12.4 + 3.12.5 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 49e0056c..d7180bc3 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.12.4 + 3.12.5 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.12.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.12.5', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 9ea17e0c..6c9d978f 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.12.4', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.12.5', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.12.4/jboot-3.12.4.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.12.5/jboot-3.12.5.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 10a8d243..2fad5350 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.12.4"; + public static String VERSION = "3.12.5"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 931cdc2addf7c0f3c9a093526c0b094600b46c01 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 16 Dec 2021 21:12:35 +0800 Subject: [PATCH 1493/1965] v3.12.5 release (^.^)YYa!! --- changes.txt | 6 ++++++ pom.xml | 9 +++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/changes.txt b/changes.txt index 6d0e3fa3..96a435d1 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.12.5: +优化:为 @Cacheable() 等注解添的key构建加更多的数据类型支持,支持集合和数组 +优化:升级相关依赖到最新版本 + + + jboot v3.12.4: 新增:@FilterBy() 注解,用于对某些参数进行内容过滤 diff --git a/pom.xml b/pom.xml index f726408a..83bcde96 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.12.5-SNAPSHOT + 3.12.5 jar jboot @@ -49,13 +49,13 @@ 4.0.3 2.13.0 5.1.49 - 2.2.13.Final + 2.2.14.Final 1.7.32 2.57 1.2.78 31.0.1-jre 2.2.5 - 3.7.0 + 3.7.1 1.14.3 2.10.9.2 2.9.1 @@ -70,7 +70,7 @@ 3.13.2.Final 1.4.2 1.1.8 - 4.2.4 + 4.2.5 1.8 1.8 @@ -238,6 +238,7 @@ redis.clients jedis ${jedis.version} + provided -- Gitee From 8ce980d516a3150ada9940528405efc45e700d99 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 17 Dec 2021 14:05:57 +0800 Subject: [PATCH 1494/1965] v3.12.5 release (^.^)YYa!! --- changes.txt | 2 + .../components/cache/JbootAopCacheConfig.java | 10 +--- .../io/jboot/components/cache/JbootCache.java | 2 + .../components/cache/JbootCacheBase.java | 6 ++ .../components/cache/JbootCacheManager.java | 2 +- .../components/cache/none/NoneCacheImpl.java | 13 ++++ .../java/io/jboot/db/model/JbootModel.java | 10 +++- .../io/jboot/db/model/JbootModelConfig.java | 60 +++++++++++++++---- 8 files changed, 81 insertions(+), 24 deletions(-) diff --git a/changes.txt b/changes.txt index 96a435d1..4482ca8e 100644 --- a/changes.txt +++ b/changes.txt @@ -1,4 +1,6 @@ jboot v3.12.5: +新增:IdCache 默认的缓存前缀配置 +优化:AOP 缓存添加默认时间为 1 个小时 优化:为 @Cacheable() 等注解添的key构建加更多的数据类型支持,支持集合和数组 优化:升级相关依赖到最新版本 diff --git a/src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java b/src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java index 49bc6789..a8b67d8a 100644 --- a/src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java @@ -25,9 +25,8 @@ public class JbootAopCacheConfig { // AOP 缓存的默认有效时间,0为永久有效,单位秒, // 当 @Cacheable 和 @CachePut 注解不配置的时候默认用这个配置 - private int liveSeconds = 0; + private int liveSeconds = 60 * 60; //默认为 1 个小时 private String useCacheName = "default"; - private String defaultCacheNamePrefix; public int getLiveSeconds() { return liveSeconds; @@ -45,13 +44,6 @@ public class JbootAopCacheConfig { this.useCacheName = useCacheName; } - public String getDefaultCacheNamePrefix() { - return defaultCacheNamePrefix; - } - - public void setDefaultCacheNamePrefix(String defaultCacheNamePrefix) { - this.defaultCacheNamePrefix = defaultCacheNamePrefix; - } private static JbootAopCacheConfig me; diff --git a/src/main/java/io/jboot/components/cache/JbootCache.java b/src/main/java/io/jboot/components/cache/JbootCache.java index 95601637..50861ac5 100644 --- a/src/main/java/io/jboot/components/cache/JbootCache.java +++ b/src/main/java/io/jboot/components/cache/JbootCache.java @@ -26,6 +26,8 @@ public interface JbootCache extends com.jfinal.plugin.activerecord.cache.ICache public void removeCurrentCacheNamePrefix(); + public JbootCacheConfig getConfig(); + @Override public T get(String cacheName, Object key); diff --git a/src/main/java/io/jboot/components/cache/JbootCacheBase.java b/src/main/java/io/jboot/components/cache/JbootCacheBase.java index 467657ff..b6b183eb 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheBase.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheBase.java @@ -28,6 +28,11 @@ public abstract class JbootCacheBase implements JbootCache { private ThreadLocal CACHE_NAME_PREFIX_TL = new ThreadLocal<>(); + @Override + public JbootCacheConfig getConfig() { + return config; + } + @Override public JbootCache setCurrentCacheNamePrefix(String cacheNamePrefix) { if (StrUtil.isNotBlank(cacheNamePrefix)) { @@ -46,6 +51,7 @@ public abstract class JbootCacheBase implements JbootCache { protected String buildCacheName(String cacheName) { String cacheNamePrefix = CACHE_NAME_PREFIX_TL.get(); + if (StrUtil.isBlank(cacheNamePrefix)) { cacheNamePrefix = config.getDefaultCachePrefix(); } diff --git a/src/main/java/io/jboot/components/cache/JbootCacheManager.java b/src/main/java/io/jboot/components/cache/JbootCacheManager.java index 2848f05f..6631c52c 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheManager.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheManager.java @@ -98,7 +98,7 @@ public class JbootCacheManager { case JbootCacheConfig.TYPE_CAREDIS: return new JbootCaredisCacheImpl(config); case JbootCacheConfig.TYPE_NONE: - return new NoneCacheImpl(); + return new NoneCacheImpl(config); default: return JbootSpiLoader.load(JbootCache.class, config.getType(), config); } diff --git a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java index 13c1ccf7..ec8e8842 100644 --- a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java @@ -18,6 +18,7 @@ package io.jboot.components.cache.none; import com.jfinal.plugin.ehcache.IDataLoader; import io.jboot.components.cache.JbootCache; +import io.jboot.components.cache.JbootCacheConfig; import java.util.List; @@ -25,6 +26,13 @@ import java.util.List; * noneCache 存在的目的:方便通过配置文件的方式关闭缓存功能 */ public class NoneCacheImpl implements JbootCache { + + private JbootCacheConfig config; + + public NoneCacheImpl(JbootCacheConfig config) { + this.config = config; + } + @Override public JbootCache setCurrentCacheNamePrefix(String cacheNamePrefix) { return this; @@ -35,6 +43,11 @@ public class NoneCacheImpl implements JbootCache { } + @Override + public JbootCacheConfig getConfig() { + return config; + } + @Override public T get(String cacheName, Object key) { return null; diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index ccc1c00d..fd745ae5 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -359,7 +359,7 @@ public class JbootModel> extends Model { } try { - M m = config.getIdCache().get(_getTableName() + M m = config.getIdCache().get(buildIdCacheName(_getTableName()) , buildIdCacheKey(idValues) , () -> JbootModel.super.findByIds(idValues) , config.getIdCacheTime()); @@ -387,10 +387,10 @@ public class JbootModel> extends Model { protected void safeDeleteCache(Object... idValues) { try { - config.getIdCache().remove(_getTableName() + config.getIdCache().remove(buildIdCacheName(_getTableName()) , buildIdCacheKey(idValues)); } catch (Exception ex) { - LOG.error("Remove cache is error by name [" + _getTableName() + "] and key [" + buildIdCacheKey(idValues) + "]", ex); + LOG.error("Remove cache is error by name [" + buildIdCacheName(_getTableName()) + "] and key [" + buildIdCacheKey(idValues) + "]", ex); } } @@ -487,6 +487,10 @@ public class JbootModel> extends Model { } + protected String buildIdCacheName(String orginal) { + return config.buildCacheName(orginal); + } + protected String buildIdCacheKey(Object... idValues) { if (idValues == null || idValues.length == 0) { return null; diff --git a/src/main/java/io/jboot/db/model/JbootModelConfig.java b/src/main/java/io/jboot/db/model/JbootModelConfig.java index 1dd7ff7c..e0dc00a8 100644 --- a/src/main/java/io/jboot/db/model/JbootModelConfig.java +++ b/src/main/java/io/jboot/db/model/JbootModelConfig.java @@ -38,7 +38,7 @@ public class JbootModelConfig { /** * id 缓存的时间,默认为 1 个小时,单位:秒 */ - private int idCacheTime = 60 * 60 * 1; + private int idCacheTime = 60 * 60; /** * Model 过滤器,可以通过这个配置来防止 xss 等问题 @@ -66,6 +66,8 @@ public class JbootModelConfig { private String idCacheName = "default"; + private String idCacheCachePrefix; + public JbootModelConfig() { } @@ -141,20 +143,49 @@ public class JbootModelConfig { this.idCacheByCopyEnable = idCacheByCopyEnable; } + public String getIdCacheCachePrefix() { + return idCacheCachePrefix; + } + + public void setIdCacheCachePrefix(String idCacheCachePrefix) { + this.idCacheCachePrefix = idCacheCachePrefix; + } + + private String defaultIdCachePrefix; + + public String getIdCachePrefixOrDefault() { + if (StrUtil.isNotBlank(idCacheCachePrefix)) { + return idCacheCachePrefix; + } + + if (defaultIdCachePrefix == null) { + synchronized (this) { + if (defaultIdCachePrefix == null) { + defaultIdCachePrefix = JbootCacheManager.me().getCache().getConfig().getDefaultCachePrefix(); + if (defaultIdCachePrefix == null) { + defaultIdCachePrefix = ""; + } + } + } + } + return defaultIdCachePrefix; + } + public JbootModelConfig(String idCacheName) { this.idCacheName = idCacheName; } private JbootModelFilter filter; + public JbootModelFilter getFilter() { - if (filter == null){ - if (StrUtil.isNotBlank(filterClass)){ - synchronized (this){ - if (filter == null){ + if (filter == null) { + if (StrUtil.isNotBlank(filterClass)) { + synchronized (this) { + if (filter == null) { filter = ClassUtil.newInstance(filterClass); } } - }else { + } else { filter = JbootModelFilter.DEFAULT; } } @@ -167,15 +198,16 @@ public class JbootModelConfig { private PrimarykeyValueGenerator primarykeyValueGenerator; + public PrimarykeyValueGenerator getPrimarykeyValueGenerator() { - if (primarykeyValueGenerator == null){ - if (StrUtil.isNotBlank(primarykeyValueGeneratorClass)){ - synchronized (this){ - if (primarykeyValueGenerator == null){ + if (primarykeyValueGenerator == null) { + if (StrUtil.isNotBlank(primarykeyValueGeneratorClass)) { + synchronized (this) { + if (primarykeyValueGenerator == null) { primarykeyValueGenerator = ClassUtil.newInstance(primarykeyValueGeneratorClass); } } - }else { + } else { primarykeyValueGenerator = PrimarykeyValueGenerator.DEFAULT; } } @@ -209,4 +241,10 @@ public class JbootModelConfig { public void setIdCache(JbootCache idCache) { this.idCache = idCache; } + + public String buildCacheName(String oraginalName) { + return getIdCachePrefixOrDefault() + ":" + oraginalName; + } + + } -- Gitee From b40fca5905eae27ac194d2f97939da9ffdc4f031 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 22 Dec 2021 11:02:57 +0800 Subject: [PATCH 1495/1965] v3.13.0 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 83bcde96..414d6ca2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.12.5 + 3.13.0-SNAPSHOT jar jboot -- Gitee From a9a1511b5f3fb403aa4d5f319fd34f57c531a2e8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 22 Dec 2021 11:03:19 +0800 Subject: [PATCH 1496/1965] add JbootMqMessageInfo --- .../components/mq/JbootMqMessageInfo.java | 14 +++++++ .../io/jboot/components/mq/JbootmqBase.java | 14 +++---- .../components/mq/JbootmqMessageListener.java | 17 +++++++- .../mq/aliyunmq/AliyunmqMessageInfo.java | 36 +++++++++++++++++ .../mq/aliyunmq/JbootAliyunmqImpl.java | 22 ++++++----- .../components/mq/local/JbootLocalmqImpl.java | 4 +- .../mq/local/LocalmqMessageInfo.java | 13 +++++++ .../components/mq/qpidmq/JbootQpidmqImpl.java | 19 +++++---- .../mq/qpidmq/QpidmqMessageInfo.java | 17 ++++++++ .../mq/rabbitmq/JbootRabbitmqImpl.java | 3 +- .../mq/rabbitmq/RabbitmqMessageInfo.java | 39 +++++++++++++++++++ .../mq/redismq/JbootRedismqImpl.java | 4 +- .../mq/redismq/RedismqMessageInfo.java | 13 +++++++ .../mq/rocketmq/JbootRocketmqImpl.java | 20 +++++----- .../mq/rocketmq/RokectmqMessageInfo.java | 38 ++++++++++++++++++ 15 files changed, 229 insertions(+), 44 deletions(-) create mode 100644 src/main/java/io/jboot/components/mq/JbootMqMessageInfo.java create mode 100644 src/main/java/io/jboot/components/mq/aliyunmq/AliyunmqMessageInfo.java create mode 100644 src/main/java/io/jboot/components/mq/local/LocalmqMessageInfo.java create mode 100644 src/main/java/io/jboot/components/mq/qpidmq/QpidmqMessageInfo.java create mode 100644 src/main/java/io/jboot/components/mq/rabbitmq/RabbitmqMessageInfo.java create mode 100644 src/main/java/io/jboot/components/mq/redismq/RedismqMessageInfo.java create mode 100644 src/main/java/io/jboot/components/mq/rocketmq/RokectmqMessageInfo.java diff --git a/src/main/java/io/jboot/components/mq/JbootMqMessageInfo.java b/src/main/java/io/jboot/components/mq/JbootMqMessageInfo.java new file mode 100644 index 00000000..196a79ab --- /dev/null +++ b/src/main/java/io/jboot/components/mq/JbootMqMessageInfo.java @@ -0,0 +1,14 @@ +package io.jboot.components.mq; + +public class JbootMqMessageInfo { + + final Jbootmq mq; + + public JbootMqMessageInfo(Jbootmq mq) { + this.mq = mq; + } + + public Jbootmq getMq() { + return mq; + } +} diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index e16255a3..d725eb91 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -38,7 +38,7 @@ public abstract class JbootmqBase implements Jbootmq { private List globalListeners = new CopyOnWriteArrayList<>(); private Multimap channelListeners = ArrayListMultimap.create(); - protected final JbootmqConfig config ; + protected final JbootmqConfig config; protected Set channels = Sets.newHashSet(); protected Set syncRecevieMessageChannels = Sets.newHashSet(); @@ -107,10 +107,10 @@ public abstract class JbootmqBase implements Jbootmq { return channelListeners.get(channel); } - public void notifyListeners(String channel, Object message) { + public void notifyListeners(String channel, Object message, JbootMqMessageInfo messageInfo) { - boolean globalResult = notifyListeners(channel, message, globalListeners); - boolean channelResult = notifyListeners(channel, message, channelListeners.get(channel)); + boolean globalResult = notifyListeners(channel, message, messageInfo, globalListeners); + boolean channelResult = notifyListeners(channel, message, messageInfo, channelListeners.get(channel)); if (!globalResult && !channelResult) { LOG.error("Application has recevied mq message, But has no listener to process it. channel:" + @@ -119,7 +119,7 @@ public abstract class JbootmqBase implements Jbootmq { } - protected boolean notifyListeners(String channel, Object message, Collection listeners) { + protected boolean notifyListeners(String channel, Object message, JbootMqMessageInfo messageInfo, Collection listeners) { if (listeners == null || listeners.size() == 0) { return false; } @@ -127,7 +127,7 @@ public abstract class JbootmqBase implements Jbootmq { if (syncRecevieMessageChannels.contains(channel)) { for (JbootmqMessageListener listener : listeners) { try { - listener.onMessage(channel, message); + listener.onMessage(channel, message, messageInfo); } catch (Throwable ex) { LOG.warn("listener[" + listener.getClass().getName() + "] execute mq message is error. channel:" + channel + " message:" + message); @@ -136,7 +136,7 @@ public abstract class JbootmqBase implements Jbootmq { } else { for (JbootmqMessageListener listener : listeners) { threadPool.execute(() -> { - listener.onMessage(channel, message); + listener.onMessage(channel, message, messageInfo); }); } } diff --git a/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java b/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java index d57d85aa..ea5b7bdd 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java +++ b/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -18,9 +18,22 @@ package io.jboot.components.mq; public interface JbootmqMessageListener { + + /** + * @param channel + * @param message + * @param messageInfo + */ + default void onMessage(String channel, Object message, JbootMqMessageInfo messageInfo) { + this.onMessage(channel, message); + } + + /** * @param channel of topic * @param message topic message */ - void onMessage(String channel, Object message); + @Deprecated + default void onMessage(String channel, Object message) { + } } diff --git a/src/main/java/io/jboot/components/mq/aliyunmq/AliyunmqMessageInfo.java b/src/main/java/io/jboot/components/mq/aliyunmq/AliyunmqMessageInfo.java new file mode 100644 index 00000000..19886716 --- /dev/null +++ b/src/main/java/io/jboot/components/mq/aliyunmq/AliyunmqMessageInfo.java @@ -0,0 +1,36 @@ +package io.jboot.components.mq.aliyunmq; + +import com.aliyun.openservices.ons.api.Action; +import com.aliyun.openservices.ons.api.ConsumeContext; +import com.aliyun.openservices.ons.api.Message; +import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.Jbootmq; + +public class AliyunmqMessageInfo extends JbootMqMessageInfo { + + final Message orginalMessage; + final ConsumeContext context; + private Action returnAction = Action.CommitMessage; + + public AliyunmqMessageInfo(Jbootmq mq, Message orginalMessage, ConsumeContext context) { + super(mq); + this.orginalMessage = orginalMessage; + this.context = context; + } + + public Message getOrginalMessage() { + return orginalMessage; + } + + public ConsumeContext getContext() { + return context; + } + + public Action getReturnAction() { + return returnAction; + } + + public void setReturnAction(Action returnAction) { + this.returnAction = returnAction; + } +} diff --git a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java index bbeeab1d..992225df 100644 --- a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java +++ b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java @@ -56,27 +56,31 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { } - private void startQueueConsumer() { + public void startQueueConsumer() { Properties properties = createProperties(); consumer = ONSFactory.createConsumer(properties); for (String channel : channels) { consumer.subscribe(aliyunmqConfig.getBroadcastChannelPrefix() + channel, aliyunmqConfig.getSubscribeSubExpression(), (message, consumeContext) -> { - notifyListeners(channel, getSerializer().deserialize(message.getBody())); - return Action.CommitMessage; + AliyunmqMessageInfo aliyunmqMessageInfo = new AliyunmqMessageInfo(this, message, consumeContext); + notifyListeners(channel, getSerializer().deserialize(message.getBody()) + , aliyunmqMessageInfo); + return aliyunmqMessageInfo.getReturnAction(); }); } consumer.start(); } - private void startBroadCastConsumer() { + public void startBroadCastConsumer() { Properties properties = createProperties(); properties.put(PropertyKeyConst.MessageModel, PropertyValueConst.BROADCASTING); consumer = ONSFactory.createConsumer(properties); for (String channel : channels) { consumer.subscribe(channel, aliyunmqConfig.getSubscribeSubExpression(), (message, consumeContext) -> { - notifyListeners(channel, getSerializer().deserialize(message.getBody())); - return Action.CommitMessage; + AliyunmqMessageInfo aliyunmqMessageInfo = new AliyunmqMessageInfo(this, message, consumeContext); + notifyListeners(channel, getSerializer().deserialize(message.getBody()) + , aliyunmqMessageInfo); + return aliyunmqMessageInfo.getReturnAction(); }); } consumer.start(); @@ -109,7 +113,7 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { } - private Producer getProducer() { + public Producer getProducer() { if (producer == null) { synchronized (this) { if (producer == null) { @@ -121,14 +125,14 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { } - private void createProducer() { + public void createProducer() { Properties properties = createProperties(); producer = ONSFactory.createProducer(properties); producer.start(); } - protected Properties createProperties() { + public Properties createProperties() { Properties properties = new Properties(); properties.put(PropertyKeyConst.AccessKey, aliyunmqConfig.getAccessKey());//AccessKey 阿里云身份验证,在阿里云服务器管理控制台创建 diff --git a/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java b/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java index 3639cb59..b5c563de 100644 --- a/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java +++ b/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java @@ -35,11 +35,11 @@ public class JbootLocalmqImpl extends JbootmqBase { @Override public void enqueue(Object message, String toChannel) { - notifyListeners(toChannel, message); + notifyListeners(toChannel, message, new LocalmqMessageInfo(this)); } @Override public void publish(Object message, String toChannel) { - notifyListeners(toChannel, message); + notifyListeners(toChannel, message, new LocalmqMessageInfo(this)); } } diff --git a/src/main/java/io/jboot/components/mq/local/LocalmqMessageInfo.java b/src/main/java/io/jboot/components/mq/local/LocalmqMessageInfo.java new file mode 100644 index 00000000..ed33949c --- /dev/null +++ b/src/main/java/io/jboot/components/mq/local/LocalmqMessageInfo.java @@ -0,0 +1,13 @@ +package io.jboot.components.mq.local; + +import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.Jbootmq; + +public class LocalmqMessageInfo extends JbootMqMessageInfo { + + + public LocalmqMessageInfo(Jbootmq mq) { + super(mq); + } + +} diff --git a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java index ad502fd9..444880ea 100644 --- a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java +++ b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java @@ -42,10 +42,11 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { private Connection connection = null; private boolean serializerEnable = true; + private JbootQpidmqConfig qpidConfig = null; public JbootQpidmqImpl(JbootmqConfig config) { super(config); - JbootQpidmqConfig qpidConfig = null; + String typeName = config.getTypeName(); if (StrUtil.isNotBlank(typeName)) { Map configModels = ConfigUtil.getConfigModels(JbootQpidmqConfig.class); @@ -100,9 +101,9 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { producer.setTimeToLive(30000); Message sendMsg = null; - if (message instanceof Message){ + if (message instanceof Message) { sendMsg = (Message) message; - }else if (!serializerEnable) { + } else if (!serializerEnable) { sendMsg = session.createTextMessage((String) message); } else { byte[] data = getSerializer().serialize(message); @@ -118,9 +119,7 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { } } - private String getConnectionUrl() { - JbootQpidmqConfig qpidConfig = Jboot.config(JbootQpidmqConfig.class); - + public String getConnectionUrl() { StringBuffer url = new StringBuffer(); url.append("amqp://"); url.append(qpidConfig.getUsername()); @@ -146,7 +145,7 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { return url.toString(); } - private String getQueueAddr(String channel) { + public String getQueueAddr(String channel) { StringBuffer addr = new StringBuffer(); addr.append("ADDR:"); addr.append(channel); @@ -155,7 +154,7 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { return addr.toString(); } - private String getTopicAddr(String channel) { + public String getTopicAddr(String channel) { StringBuffer addr = new StringBuffer(); addr.append("ADDR:amq.topic/"); addr.append(channel); @@ -163,7 +162,7 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { return addr.toString(); } - private void startReceiveMsgThread() throws Exception { + public void startReceiveMsgThread() throws Exception { if (ArrayUtil.isNullOrEmpty(this.channels)) { return; } @@ -218,7 +217,7 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { } if (object != null) { - notifyListeners(channel, object); + notifyListeners(channel, object, new QpidmqMessageInfo(JbootQpidmqImpl.this, message)); } } } catch (Exception e) { diff --git a/src/main/java/io/jboot/components/mq/qpidmq/QpidmqMessageInfo.java b/src/main/java/io/jboot/components/mq/qpidmq/QpidmqMessageInfo.java new file mode 100644 index 00000000..97b4111c --- /dev/null +++ b/src/main/java/io/jboot/components/mq/qpidmq/QpidmqMessageInfo.java @@ -0,0 +1,17 @@ +package io.jboot.components.mq.qpidmq; + +import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.Jbootmq; + +import javax.jms.Message; + +public class QpidmqMessageInfo extends JbootMqMessageInfo { + + + final Message orignalMessage; + + public QpidmqMessageInfo(Jbootmq mq, Message orignalMessage) { + super(mq); + this.orignalMessage = orignalMessage; + } +} diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index d0466c20..e0fb798f 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -105,10 +105,9 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { Object o = getSerializer().deserialize(body); - notifyListeners(orginaChannelName, o); + notifyListeners(orginaChannelName, o, new RabbitmqMessageInfo(JbootRabbitmqImpl.this, channel, consumerTag, envelope, properties)); } }); - } catch (IOException e) { e.printStackTrace(); } diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/RabbitmqMessageInfo.java b/src/main/java/io/jboot/components/mq/rabbitmq/RabbitmqMessageInfo.java new file mode 100644 index 00000000..a36b05c0 --- /dev/null +++ b/src/main/java/io/jboot/components/mq/rabbitmq/RabbitmqMessageInfo.java @@ -0,0 +1,39 @@ +package io.jboot.components.mq.rabbitmq; + +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Envelope; +import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.Jbootmq; + +public class RabbitmqMessageInfo extends JbootMqMessageInfo { + + final Channel channel; + final String consumerTag; + final Envelope envelope; + final AMQP.BasicProperties properties; + + public RabbitmqMessageInfo(Jbootmq mq, Channel channel, String consumerTag, Envelope envelope, AMQP.BasicProperties properties) { + super(mq); + this.channel = channel; + this.consumerTag = consumerTag; + this.envelope = envelope; + this.properties = properties; + } + + public Channel getChannel() { + return channel; + } + + public String getConsumerTag() { + return consumerTag; + } + + public Envelope getEnvelope() { + return envelope; + } + + public AMQP.BasicProperties getProperties() { + return properties; + } +} diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java index 6cb191d3..f766015d 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java @@ -72,7 +72,7 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { redis.subscribe(new BinaryJedisPubSub() { @Override public void onMessage(byte[] channel, byte[] message) { - notifyListeners(redis.bytesToKey(channel), getSerializer().deserialize(message)); + notifyListeners(redis.bytesToKey(channel), getSerializer().deserialize(message), new RedismqMessageInfo(JbootRedismqImpl.this)); } }, redis.keysToBytesArray(channels)); @@ -109,7 +109,7 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { for (String channel : this.channels) { Object data = redis.lpop(channel); if (data != null) { - notifyListeners(channel, data); + notifyListeners(channel, data, new RedismqMessageInfo(JbootRedismqImpl.this)); } } } diff --git a/src/main/java/io/jboot/components/mq/redismq/RedismqMessageInfo.java b/src/main/java/io/jboot/components/mq/redismq/RedismqMessageInfo.java new file mode 100644 index 00000000..321e5f24 --- /dev/null +++ b/src/main/java/io/jboot/components/mq/redismq/RedismqMessageInfo.java @@ -0,0 +1,13 @@ +package io.jboot.components.mq.redismq; + +import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.Jbootmq; + +public class RedismqMessageInfo extends JbootMqMessageInfo { + + + public RedismqMessageInfo(Jbootmq mq) { + super(mq); + } + +} diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java index c1b20df3..df964a62 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java @@ -17,14 +17,13 @@ package io.jboot.components.mq.rocketmq; import com.jfinal.log.Log; import io.jboot.Jboot; -import io.jboot.utils.ConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; import io.jboot.components.mq.JbootmqConfig; import io.jboot.exception.JbootIllegalConfigException; +import io.jboot.utils.ConfigUtil; import io.jboot.utils.StrUtil; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; -import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.producer.DefaultMQProducer; @@ -92,13 +91,14 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { // 注册回调实现类来处理从broker拉取回来的消息 consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> { - if (msgs != null) { + RokectmqMessageInfo rokectMqMessageInfo = new RokectmqMessageInfo(this, msgs, context); + if (msgs != null && !msgs.isEmpty()) { for (MessageExt messageExt : msgs) { - notifyListeners(messageExt.getTopic(), getSerializer().deserialize(messageExt.getBody())); + notifyListeners(messageExt.getTopic(), getSerializer().deserialize(messageExt.getBody()), rokectMqMessageInfo); } } - // 标记该消息已经被成功消费 - return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; + + return rokectMqMessageInfo.getReturnStatus(); }); @@ -131,14 +131,14 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { final int len = rocketmqConfig.getBroadcastChannelPrefix().length(); consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> { - if (msgs != null) { + RokectmqMessageInfo rokectMqMessageInfo = new RokectmqMessageInfo(this, msgs, context); + if (msgs != null && !msgs.isEmpty()) { for (MessageExt messageExt : msgs) { String topic = messageExt.getTopic(); - notifyListeners(topic.substring(len), getSerializer().deserialize(messageExt.getBody())); + notifyListeners(topic.substring(len), getSerializer().deserialize(messageExt.getBody()), rokectMqMessageInfo); } } - // 标记该消息已经被成功消费 - return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; + return rokectMqMessageInfo.getReturnStatus(); }); consumer.start(); diff --git a/src/main/java/io/jboot/components/mq/rocketmq/RokectmqMessageInfo.java b/src/main/java/io/jboot/components/mq/rocketmq/RokectmqMessageInfo.java new file mode 100644 index 00000000..4fe9be04 --- /dev/null +++ b/src/main/java/io/jboot/components/mq/rocketmq/RokectmqMessageInfo.java @@ -0,0 +1,38 @@ +package io.jboot.components.mq.rocketmq; + +import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.Jbootmq; +import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; +import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; +import org.apache.rocketmq.common.message.MessageExt; + +import java.util.List; + +public class RokectmqMessageInfo extends JbootMqMessageInfo { + + private ConsumeConcurrentlyStatus returnStatus = ConsumeConcurrentlyStatus.CONSUME_SUCCESS; + private final List msgs; + private final ConsumeConcurrentlyContext context; + + public RokectmqMessageInfo(Jbootmq jbootmq, List msgs, ConsumeConcurrentlyContext context) { + super(jbootmq); + this.msgs = msgs; + this.context = context; + } + + public ConsumeConcurrentlyStatus getReturnStatus() { + return returnStatus; + } + + public void setReturnStatus(ConsumeConcurrentlyStatus returnStatus) { + this.returnStatus = returnStatus; + } + + public List getMsgs() { + return msgs; + } + + public ConsumeConcurrentlyContext getContext() { + return context; + } +} -- Gitee From 663188e44423660ff6f7f21531d5e1035078624e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 22 Dec 2021 11:24:33 +0800 Subject: [PATCH 1497/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=20404=20=20403=204?= =?UTF-8?q?01=20=E8=BE=93=E5=87=BA=20info=20=E4=BF=A1=E6=81=AF=EF=BC=8C?= =?UTF-8?q?=E8=80=8C=E4=B8=8D=E6=98=AF=20warn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/web/handler/JbootActionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 904df896..25bacbdc 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -214,7 +214,7 @@ public class JbootActionHandler extends ActionHandler { if (LOG.isWarnEnabled()) { String qs = request.getQueryString(); msg = msg + (qs == null ? target : target + "?" + qs); - LOG.warn(msg, e); + LOG.info(msg, e); } } else { if (LOG.isErrorEnabled()) { -- Gitee From 2350a242f55b75668170584a0f99f5432e551da3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 22 Dec 2021 11:44:25 +0800 Subject: [PATCH 1498/1965] add JbootMqMessageInfo --- .../components/mq/rabbitmq/JbootRabbitmqImpl.java | 6 +++--- .../jboot/components/mq/redismq/JbootRedismqImpl.java | 2 +- .../components/mq/rocketmq/JbootRocketmqImpl.java | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index e0fb798f..069f2343 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -98,7 +98,7 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { } - private void bindChannel(Channel channel, String name, String orginaChannelName) { + public void bindChannel(Channel channel, String name, String orginaChannelName) { if (channel != null) { try { channel.basicConsume(name, true, new DefaultConsumer(channel) { @@ -115,7 +115,7 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { } - private synchronized Channel getChannel(String toChannel, boolean queueMode) { + public synchronized Channel getChannel(String toChannel, boolean queueMode) { Channel channel = channelMap.get(toChannel + queueMode); if (channel == null) { @@ -146,7 +146,7 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { return channel; } - private synchronized String buildBroadcastChannelName(String channel) { + public String buildBroadcastChannelName(String channel) { return rabbitmqConfig.getBroadcastChannelPrefix() + channel; } diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java index f766015d..e3b0224f 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java @@ -105,7 +105,7 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { } } - private void doExecuteDequeue() { + public void doExecuteDequeue() { for (String channel : this.channels) { Object data = redis.lpop(channel); if (data != null) { diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java index df964a62..a8882f9e 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java @@ -69,7 +69,7 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { } - private void startQueueConsumer() throws MQClientException { + public void startQueueConsumer() throws MQClientException { // 实例化消费者 DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(rocketmqConfig.getConsumerGroup()); @@ -107,7 +107,7 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { } - private void startBroadcastConsumer() throws MQClientException { + public void startBroadcastConsumer() throws MQClientException { // 实例化消费者 DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(rocketmqConfig.getBroadcastChannelPrefix() + rocketmqConfig.getConsumerGroup()); @@ -157,7 +157,7 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { } - private void trySendMessage(Object message, String topic, int tryTimes) { + public void trySendMessage(Object message, String topic, int tryTimes) { if (tryTimes < 3) { try { Message sendMsg = null; @@ -183,7 +183,7 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { } - protected MQProducer getMQProducer() throws MQClientException { + public MQProducer getMQProducer() throws MQClientException { if (mqProducer == null) { synchronized (this) { if (mqProducer == null) { @@ -195,7 +195,7 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { } - protected void createMqProducer() throws MQClientException { + public void createMqProducer() throws MQClientException { DefaultMQProducer producer = new DefaultMQProducer(rocketmqConfig.getProducerGroup()); producer.setNamesrvAddr(rocketmqConfig.getNamesrvAddr()); -- Gitee From 1d5789a413948875e45836e7234b5d570f05428a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 22 Dec 2021 12:31:44 +0800 Subject: [PATCH 1499/1965] add JbootMqMessageInfo --- .../mq/rabbitmq/JbootRabbitmqConfig.java | 20 +++++++++++++++++++ .../mq/rabbitmq/JbootRabbitmqImpl.java | 6 +++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java index de1a73c8..c3f424b7 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java @@ -30,6 +30,10 @@ public class JbootRabbitmqConfig { private String virtualHost; private String broadcastChannelPrefix = "broadcast-"; + private String broadcastChannelRoutingKey = ""; + + //若配置为 false,则需要在 OnMessage 里,调用 RabbitmqMessageInfo.getChannel().baseAck(或者baseNack)进行消费(或者标识消费失败) + private boolean autoAck = true; public String getUsername() { @@ -79,4 +83,20 @@ public class JbootRabbitmqConfig { public void setBroadcastChannelPrefix(String broadcastChannelPrefix) { this.broadcastChannelPrefix = broadcastChannelPrefix; } + + public String getBroadcastChannelRoutingKey() { + return broadcastChannelRoutingKey; + } + + public void setBroadcastChannelRoutingKey(String broadcastChannelRoutingKey) { + this.broadcastChannelRoutingKey = broadcastChannelRoutingKey; + } + + public boolean isAutoAck() { + return autoAck; + } + + public void setAutoAck(boolean autoAck) { + this.autoAck = autoAck; + } } diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index 069f2343..930cf90d 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -101,7 +101,7 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { public void bindChannel(Channel channel, String name, String orginaChannelName) { if (channel != null) { try { - channel.basicConsume(name, true, new DefaultConsumer(channel) { + channel.basicConsume(name, rabbitmqConfig.isAutoAck(), new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { Object o = getSerializer().deserialize(body); @@ -122,7 +122,7 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { try { channel = connection.createChannel(); - //队列模式,值需要创建定义 队列就可以了,不需要定义交换机 + //队列模式,只需要创建 队列就可以了,不需要定义交换机 if (queueMode) { channel.queueDeclare(toChannel, true, false, false, null); } @@ -131,7 +131,7 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { else { channel.queueDeclare(buildBroadcastChannelName(toChannel), false, false, true, null); channel.exchangeDeclare(toChannel, BuiltinExchangeType.FANOUT, true); - channel.queueBind(buildBroadcastChannelName(toChannel), toChannel, ""); + channel.queueBind(buildBroadcastChannelName(toChannel), toChannel, rabbitmqConfig.getBroadcastChannelRoutingKey()); } } catch (Exception ex) { -- Gitee From 1efbe4d33d48dfa07afa53e40f952388989898fe Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 22 Dec 2021 12:44:10 +0800 Subject: [PATCH 1500/1965] add JbootMqMessageInfo --- .../mq/aliyunmq/JbootAliyunmqImpl.java | 14 +++++-- .../mq/rocketmq/JbootRocketmqImpl.java | 39 ++++++++----------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java index 992225df..8f7698ee 100644 --- a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java +++ b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java @@ -16,12 +16,13 @@ package io.jboot.components.mq.aliyunmq; import com.aliyun.openservices.ons.api.*; +import com.jfinal.log.Log; import io.jboot.Jboot; -import io.jboot.utils.ConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; import io.jboot.components.mq.JbootmqConfig; import io.jboot.exception.JbootIllegalConfigException; +import io.jboot.utils.ConfigUtil; import io.jboot.utils.StrUtil; import java.util.Map; @@ -29,6 +30,7 @@ import java.util.Properties; public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { + private static final Log LOG = Log.getLog(JbootAliyunmqImpl.class); private Producer producer; private Consumer consumer; @@ -96,7 +98,10 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { byte[] bytes = getSerializer().serialize(message); sendMsg = new Message(toChannel, "*", bytes); } - getProducer().send(sendMsg); + SendResult result = getProducer().send(sendMsg); + if (result == null) { + LOG.warn("Rockect mq send message fail!!!"); + } } @@ -109,7 +114,10 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { byte[] bytes = getSerializer().serialize(message); sendMsg = new Message(aliyunmqConfig.getBroadcastChannelPrefix() + toChannel, "*", bytes); } - getProducer().send(sendMsg); + SendResult result = getProducer().send(sendMsg); + if (result == null) { + LOG.warn("Rockect mq send message fail!!!"); + } } diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java index a8882f9e..20e20c71 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java @@ -147,38 +147,33 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { @Override public void enqueue(Object message, String toChannel) { - trySendMessage(message, toChannel, 1); + doSendMessage(message, toChannel); } @Override public void publish(Object message, String toChannel) { - trySendMessage(message, rocketmqConfig.getBroadcastChannelPrefix() + toChannel, 1); + doSendMessage(message, rocketmqConfig.getBroadcastChannelPrefix() + toChannel); } - public void trySendMessage(Object message, String topic, int tryTimes) { - if (tryTimes < 3) { - try { - Message sendMsg = null; - if (message instanceof Message) { - sendMsg = (Message) message; - } else { - sendMsg = new Message(topic, getSerializer().serialize(message)); - } + public void doSendMessage(Object message, String topic) { + try { + Message sendMsg = null; + if (message instanceof Message) { + sendMsg = (Message) message; + } else { + sendMsg = new Message(topic, getSerializer().serialize(message)); + } - SendResult result = getMQProducer().send(sendMsg); - // if (result.getSendStatus() != SendStatus.SEND_OK) { - // 只要不等于 null 就是发送成功 - if (result == null) { - trySendMessage(message, topic, ++tryTimes); - } - } catch (Exception e) { - trySendMessage(message, topic, ++tryTimes); - LOG.error(e.toString(), e); + SendResult result = getMQProducer().send(sendMsg); + // if (result.getSendStatus() != SendStatus.SEND_OK) { + // 只要不等于 null 就是发送成功 + if (result == null) { + LOG.warn("Rockect mq send message fail!!!"); } - } else { - LOG.error("Rocketmq publish not success!"); + } catch (Exception e) { + LOG.error(e.toString(), e); } } -- Gitee From 88b0cc1007c5c9228954fde2c2ef102530d28db8 Mon Sep 17 00:00:00 2001 From: whpat124 <124653847@qq.com> Date: Thu, 23 Dec 2021 00:06:10 +0000 Subject: [PATCH 1501/1965] =?UTF-8?q?update=20src/main/java/io/jboot/compo?= =?UTF-8?q?nents/mq/rabbitmq/JbootRabbitmqImpl.java.=20=E8=BF=99=E9=87=8C?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E5=BA=94=E8=AF=A5=E5=8A=A0=E4=B8=8A+=20queue?= =?UTF-8?q?Mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index 930cf90d..0c6ce9e5 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -139,7 +139,7 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { } if (channel != null) { - channelMap.put(toChannel, channel); + channelMap.put(toChannel + queueMode, channel); } } -- Gitee From 7c76f4606a0047d4dc2e6b668da0fbf010bdeb9a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 23 Dec 2021 19:08:08 +0800 Subject: [PATCH 1502/1965] add JbootMqMessageInfo --- .../components/mq/JbootmqMessageListener.java | 4 ++-- .../jboot/test/mq/rabbit/RabbitMqReceiver1.java | 10 ++++++++-- .../jboot/test/mq/rabbit/RabbitMqReceiver2.java | 9 +++++++-- .../jboot/test/mq/rabbit/RabbitMqReceiver3.java | 9 +++++++-- .../io/jboot/test/mq/redis/RedisMqReceiver1.java | 16 ++++++++++++---- .../io/jboot/test/mq/redis/RedisMqReceiver2.java | 16 ++++++++++++---- .../test/mq/rocketmq/RocketmqReceiver1.java | 16 ++++++++++++---- .../test/mq/rocketmq/RocketmqReceiver2.java | 16 ++++++++++++---- 8 files changed, 72 insertions(+), 24 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java b/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java index ea5b7bdd..dbe5dd6b 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java +++ b/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java @@ -22,9 +22,9 @@ public interface JbootmqMessageListener { /** * @param channel * @param message - * @param messageInfo + * @param info */ - default void onMessage(String channel, Object message, JbootMqMessageInfo messageInfo) { + default void onMessage(String channel, Object message, JbootMqMessageInfo info) { this.onMessage(channel, message); } diff --git a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java index 241a0e4f..7f7e902a 100644 --- a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java +++ b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java @@ -3,6 +3,8 @@ package io.jboot.test.mq.rabbit; import io.jboot.Jboot; import io.jboot.app.JbootApplication; +import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.JbootmqMessageListener; /** * 开始之前 先通过通过 docker 把 rabbitmq 运行起来 @@ -36,10 +38,14 @@ public class RabbitMqReceiver1 { // 只监听 myChannel 这个通道 - Jboot.getMq().addMessageListener((channel, message) -> { - System.out.println("Receive msg: " + message + ", from channel: " + channel); + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } },"channel1"); + Jboot.getMq().startListening(); System.out.println("RabbitMqReceiver1 started."); diff --git a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java index cd74e8d9..326da73c 100644 --- a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java +++ b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java @@ -3,6 +3,8 @@ package io.jboot.test.mq.rabbit; import io.jboot.Jboot; import io.jboot.app.JbootApplication; +import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.JbootmqMessageListener; /** * 开始之前 先通过通过 docker 把 rabbitmq 运行起来 @@ -36,8 +38,11 @@ public class RabbitMqReceiver2 { // 只监听 myChannel 这个通道 - Jboot.getMq().addMessageListener((channel, message) -> { - System.out.println("Receive msg: " + message + ", from channel: " + channel); + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } },"channel2"); diff --git a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver3.java b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver3.java index c2637ada..f01d9f42 100644 --- a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver3.java +++ b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver3.java @@ -3,6 +3,8 @@ package io.jboot.test.mq.rabbit; import io.jboot.Jboot; import io.jboot.app.JbootApplication; +import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.JbootmqMessageListener; /** * 开始之前 先通过通过 docker 把 rabbitmq 运行起来 @@ -34,8 +36,11 @@ public class RabbitMqReceiver3 { JbootApplication.run(args); //添加监听,不指定通道,则监听所有通道 - Jboot.getMq().addMessageListener((channel, message) -> { - System.out.println("Receive msg: " + message + ", from channel: " + channel); + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } }); diff --git a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java index 3127c0a6..edf70113 100644 --- a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java +++ b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java @@ -3,6 +3,8 @@ package io.jboot.test.mq.redis; import io.jboot.Jboot; import io.jboot.app.JbootApplication; +import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.JbootmqMessageListener; public class RedisMqReceiver1 { @@ -20,13 +22,19 @@ public class RedisMqReceiver1 { JbootApplication.run(args); //添加监听 - Jboot.getMq().addMessageListener((channel, message) -> { - System.out.println("listener1 receive msg : " + message + ", from channel : " + channel); + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } }); // 只监听 myChannel 这个通道 - Jboot.getMq().addMessageListener((channel, message) -> { - System.out.println("listener2 receive msg : " + message + ", from channel : " + channel); + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } },"myChannel"); Jboot.getMq().startListening(); diff --git a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java index ccba717c..403304ba 100644 --- a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java +++ b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java @@ -3,6 +3,8 @@ package io.jboot.test.mq.redis; import io.jboot.Jboot; import io.jboot.app.JbootApplication; +import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.JbootmqMessageListener; public class RedisMqReceiver2 { @@ -20,13 +22,19 @@ public class RedisMqReceiver2 { JbootApplication.run(args); //添加监听 - Jboot.getMq().addMessageListener((channel, message) -> { - System.out.println("listener1 receive msg : " + message + ", from channel : " + channel); + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } }); // 只监听 myChannel 这个通道 - Jboot.getMq().addMessageListener((channel, message) -> { - System.out.println("listener2 receive msg : " + message + ", from channel : " + channel); + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } },"myChannel"); Jboot.getMq().startListening(); diff --git a/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver1.java b/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver1.java index fe423399..5abc68a1 100644 --- a/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver1.java +++ b/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver1.java @@ -3,6 +3,8 @@ package io.jboot.test.mq.rocketmq; import io.jboot.Jboot; import io.jboot.app.JbootApplication; +import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.JbootmqMessageListener; public class RocketmqReceiver1 { @@ -20,13 +22,19 @@ public class RocketmqReceiver1 { JbootApplication.run(args); //添加监听 - Jboot.getMq().addMessageListener((channel, message) -> { - System.out.println("listener1 receive msg : " + message + ", from channel : " + channel); + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } }); // 只监听 myChannel 这个通道 - Jboot.getMq().addMessageListener((channel, message) -> { - System.out.println("listener2 receive msg : " + message + ", from channel : " + channel); + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } },"myChannel"); Jboot.getMq().startListening(); diff --git a/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver2.java b/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver2.java index a306e0f8..152cf0c3 100644 --- a/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver2.java +++ b/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver2.java @@ -3,6 +3,8 @@ package io.jboot.test.mq.rocketmq; import io.jboot.Jboot; import io.jboot.app.JbootApplication; +import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.JbootmqMessageListener; public class RocketmqReceiver2 { @@ -20,13 +22,19 @@ public class RocketmqReceiver2 { JbootApplication.run(args); //添加监听 - Jboot.getMq().addMessageListener((channel, message) -> { - System.out.println("listener1 receive msg : " + message + ", from channel : " + channel); + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } }); // 只监听 myChannel 这个通道 - Jboot.getMq().addMessageListener((channel, message) -> { - System.out.println("listener2 receive msg : " + message + ", from channel : " + channel); + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } },"myChannel"); Jboot.getMq().startListening(); -- Gitee From 047b8f7ece33f954f0f524c762da639389b33e2a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 23 Dec 2021 19:08:23 +0800 Subject: [PATCH 1503/1965] optimize MockMvc.java --- src/main/java/io/jboot/test/MockMvc.java | 76 +++++++++++++++++++----- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/jboot/test/MockMvc.java b/src/main/java/io/jboot/test/MockMvc.java index 20f1e4dc..9fad70a3 100644 --- a/src/main/java/io/jboot/test/MockMvc.java +++ b/src/main/java/io/jboot/test/MockMvc.java @@ -20,6 +20,7 @@ import io.jboot.test.web.MockHttpServletResponse; import io.jboot.test.web.MockServletInputStream; import io.jboot.utils.StrUtil; +import javax.servlet.ServletInputStream; import javax.servlet.http.Cookie; import java.util.*; import java.util.function.Consumer; @@ -51,6 +52,15 @@ public class MockMvc { return holdCookies; } + public String getCookieValue(String name) { + for (Cookie holdCookie : holdCookies) { + if (holdCookie.getName().equals(name)) { + return holdCookie.getValue(); + } + } + return null; + } + public void setHoldCookies(Set holdCookies) { this.holdCookies = holdCookies; } @@ -71,17 +81,27 @@ public class MockMvc { this.requestFinishedListener = requestFinishedListener; } + public MockMvcResult get(String target) { - return get(target, null); + return get(target, null, null, null); } public MockMvcResult get(String target, Map paras) { - return get(target, paras, null); + return get(target, paras, null, null); + } + + + public MockMvcResult get(String target, Map p, Set cookies) { + return get(target, p, null, cookies); } public MockMvcResult get(String target, Map p, Map headers) { + return get(target, p, headers, null); + } + + public MockMvcResult get(String target, Map p, Map headers, Set cookies) { MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("GET"); @@ -103,28 +123,48 @@ public class MockMvc { paras.forEach(request::addParameter); + if (cookies != null) { + request.setCookies(cookies); + } - return doStartMockRequest(request); + + return doMockRequest(request); } public MockMvcResult post(String target) { - return post(target, null, null, null); + return post(target, null, null, null, null); } public MockMvcResult post(String target, String postData) { - return post(target, null, null, postData); + return post(target, null, null, null, postData); } - public MockMvcResult post(String target, Map paras) { - return post(target, paras, null, null); + return post(target, paras, null, null, null); } public MockMvcResult post(String target, Map paras, String postData) { - return post(target, paras, null, postData); + return post(target, paras, null, null, postData); + } + + public MockMvcResult post(String target, Map paras, Map headers) { + return post(target, paras, headers, null, null); } public MockMvcResult post(String target, Map paras, Map headers, String postData) { + return post(target, paras, headers, null, postData); + } + + public MockMvcResult post(String target, Map paras, Map headers, Set cookies, String postData) { + MockServletInputStream inStream = null; + if (StrUtil.isNotBlank(postData)) { + inStream = new MockServletInputStream(postData); + } + return doPost(target, paras, headers, cookies, inStream); + } + + + public MockMvcResult doPost(String target, Map paras, Map headers, Set cookies, ServletInputStream inStream) { MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); @@ -148,28 +188,34 @@ public class MockMvc { paras.forEach(request::addParameter); } - if (StrUtil.isNotBlank(postData)) { - request.setInputStream(new MockServletInputStream(postData)); + if (inStream != null) { + request.setInputStream(inStream); } - return doStartMockRequest(request); - } + if (cookies != null) { + request.setCookies(cookies); + } - private MockMvcResult doStartMockRequest(MockHttpServletRequest request) { + return doMockRequest(request); + } + public MockMvcResult doMockRequest(MockHttpServletRequest request) { MockHttpServletResponse response = new MockHttpServletResponse(); try { if (requestStartListener != null) { requestStartListener.accept(request); } + Set cookies = new HashSet<>(Arrays.asList(request.getCookies())); + //开启 cookie 保持, 用户未设置自己的 cookie, 上次的 cookie 有值 - if (isHoldCookiesEnable() && request.getCookies().length == 0 && holdCookies.size() > 0) { - request.setCookies(holdCookies); + if (isHoldCookiesEnable()) { + cookies.addAll(holdCookies); } + request.setCookies(cookies); doSendRequest(request, response); } finally { -- Gitee From 2b61a235aab0da9b039093226341917a1d960c81 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 24 Dec 2021 11:36:59 +0800 Subject: [PATCH 1504/1965] optimize MockMvc.java --- src/main/java/io/jboot/test/MockMvc.java | 70 +++++++++++++++++++ .../test/web/MockHttpServletRequest.java | 28 ++++++-- .../io/jboot/test/web/MockServletContext.java | 3 +- 3 files changed, 93 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/test/MockMvc.java b/src/main/java/io/jboot/test/MockMvc.java index 9fad70a3..2c8d44a3 100644 --- a/src/main/java/io/jboot/test/MockMvc.java +++ b/src/main/java/io/jboot/test/MockMvc.java @@ -15,6 +15,7 @@ */ package io.jboot.test; +import io.jboot.components.http.HttpMimeTypes; import io.jboot.test.web.MockHttpServletRequest; import io.jboot.test.web.MockHttpServletResponse; import io.jboot.test.web.MockServletInputStream; @@ -22,6 +23,8 @@ import io.jboot.utils.StrUtil; import javax.servlet.ServletInputStream; import javax.servlet.http.Cookie; +import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.function.Consumer; @@ -164,6 +167,73 @@ public class MockMvc { } + public MockMvcResult upload(String target, Map paras, Map headers, Set cookies) { + + + String endFlag = "\r\n"; + String startFlag = "--"; + String boundary = "------" + StrUtil.uuid(); + + if (headers == null) { + headers = new HashMap<>(); + } + headers.put("Content-Type", "multipart/form-data; boundary=" + boundary); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + if (paras != null) { + paras.forEach((key, value) -> { + if (value instanceof File) { + File file = (File) value; + writeString(baos, startFlag + boundary + endFlag); + writeString(baos, "Content-Disposition: form-data; name=\"" + key + "\"; filename=\"" + file.getName() + "\""); + writeString(baos, endFlag + "Content-Type: " + HttpMimeTypes.getMimeType(file.getName())); + writeString(baos, endFlag + endFlag); + writeFile(baos, file); + writeString(baos, endFlag); + } else { + writeString(baos, startFlag + boundary + endFlag); + writeString(baos, "Content-Disposition: form-data; name=\"" + key + "\""); + writeString(baos, endFlag + endFlag); + writeString(baos, String.valueOf(value)); + writeString(baos, endFlag); + } + }); + } + + writeString(baos, startFlag + boundary + startFlag + endFlag); + return doPost(target, paras, headers, cookies, new MockServletInputStream(baos.toByteArray())); + } + + private void writeFile(ByteArrayOutputStream dos, File file) { + FileInputStream fStream = null; + try { + fStream = new FileInputStream(file); + byte[] buffer = new byte[2028]; + for (int len = 0; (len = fStream.read(buffer)) > 0; ) { + dos.write(buffer, 0, len); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (fStream != null) { + try { + fStream.close(); + } catch (IOException e) { + } + } + } + + } + + private void writeString(OutputStream dos, String s) { + try { + dos.write(s.getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public MockMvcResult doPost(String target, Map paras, Map headers, Set cookies, ServletInputStream inStream) { MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); diff --git a/src/main/java/io/jboot/test/web/MockHttpServletRequest.java b/src/main/java/io/jboot/test/web/MockHttpServletRequest.java index c3ca0c8e..f9a1bfef 100644 --- a/src/main/java/io/jboot/test/web/MockHttpServletRequest.java +++ b/src/main/java/io/jboot/test/web/MockHttpServletRequest.java @@ -81,15 +81,17 @@ public class MockHttpServletRequest extends HttpServletRequestWrapper { @Override public String getHeader(String name) { - return headers.get(name); + return headers.get(name.toLowerCase()); } public void setHeaders(Map headers) { - this.headers.putAll(headers); + if (headers != null) { + headers.forEach((s, s2) -> MockHttpServletRequest.this.headers.put(s.toLowerCase(), s2)); + } } public void addHeader(String name, Object value) { - headers.put(name, value.toString()); + headers.put(name.toLowerCase(), value.toString()); } @Override @@ -284,11 +286,23 @@ public class MockHttpServletRequest extends HttpServletRequestWrapper { @Override public int getContentLength() { String cl = this.getHeader("content-length"); - try { - return Integer.parseInt(cl); - } catch (NumberFormatException e) { - return 0; + if (cl != null) { + try { + return Integer.parseInt(cl); + } catch (NumberFormatException e) { + return 0; + } } + + if (inputStream != null) { + try { + return inputStream.available(); + } catch (IOException e) { + return 0; + } + } + + return 0; } @Override diff --git a/src/main/java/io/jboot/test/web/MockServletContext.java b/src/main/java/io/jboot/test/web/MockServletContext.java index 61037316..b79c15dc 100644 --- a/src/main/java/io/jboot/test/web/MockServletContext.java +++ b/src/main/java/io/jboot/test/web/MockServletContext.java @@ -16,6 +16,7 @@ package io.jboot.test.web; import com.jfinal.kit.PathKit; +import io.jboot.components.http.HttpMimeTypes; import io.jboot.test.MockExceptions; import javax.servlet.*; @@ -72,7 +73,7 @@ public class MockServletContext implements ServletContext { @Override public String getMimeType(String file) { - throw MockExceptions.unsupported; + return HttpMimeTypes.getMimeType(file); } @Override -- Gitee From c71b5e33fcf3a12ee7b5ce6387814aede4f9f182 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 24 Dec 2021 11:37:38 +0800 Subject: [PATCH 1505/1965] add http default mimeTypes --- .../jboot/components/http/HttpMimeTypes.java | 173 ++++++++++++++++++ .../components/http/jboot/JbootHttpImpl.java | 19 +- 2 files changed, 187 insertions(+), 5 deletions(-) create mode 100644 src/main/java/io/jboot/components/http/HttpMimeTypes.java diff --git a/src/main/java/io/jboot/components/http/HttpMimeTypes.java b/src/main/java/io/jboot/components/http/HttpMimeTypes.java new file mode 100644 index 00000000..d2d11deb --- /dev/null +++ b/src/main/java/io/jboot/components/http/HttpMimeTypes.java @@ -0,0 +1,173 @@ +package io.jboot.components.http; + +import java.util.HashMap; +import java.util.Map; + +public class HttpMimeTypes { + + private static final Map defaultMappings = new HashMap<>(200); + + static { + defaultMappings.put("txt", "text/plain"); + defaultMappings.put("css", "text/css"); + defaultMappings.put("html", "text/html"); + defaultMappings.put("htm", "text/html"); + defaultMappings.put("gif", "image/gif"); + defaultMappings.put("jpg", "image/jpeg"); + defaultMappings.put("jpe", "image/jpeg"); + defaultMappings.put("jpeg", "image/jpeg"); + defaultMappings.put("bmp", "image/bmp"); + defaultMappings.put("js", "application/javascript"); + defaultMappings.put("png", "image/png"); + defaultMappings.put("java", "text/plain"); + defaultMappings.put("body", "text/html"); + defaultMappings.put("rtx", "text/richtext"); + defaultMappings.put("tsv", "text/tab-separated-values"); + defaultMappings.put("etx", "text/x-setext"); + defaultMappings.put("json", "application/json"); + defaultMappings.put("class", "application/java"); + defaultMappings.put("csh", "application/x-csh"); + defaultMappings.put("sh", "application/x-sh"); + defaultMappings.put("tcl", "application/x-tcl"); + defaultMappings.put("tex", "application/x-tex"); + defaultMappings.put("texinfo", "application/x-texinfo"); + defaultMappings.put("texi", "application/x-texinfo"); + defaultMappings.put("t", "application/x-troff"); + defaultMappings.put("tr", "application/x-troff"); + defaultMappings.put("roff", "application/x-troff"); + defaultMappings.put("man", "application/x-troff-man"); + defaultMappings.put("me", "application/x-troff-me"); + defaultMappings.put("ms", "application/x-wais-source"); + defaultMappings.put("src", "application/x-wais-source"); + defaultMappings.put("zip", "application/zip"); + defaultMappings.put("bcpio", "application/x-bcpio"); + defaultMappings.put("cpio", "application/x-cpio"); + defaultMappings.put("gtar", "application/x-gtar"); + defaultMappings.put("shar", "application/x-shar"); + defaultMappings.put("sv4cpio", "application/x-sv4cpio"); + defaultMappings.put("sv4crc", "application/x-sv4crc"); + defaultMappings.put("tar", "application/x-tar"); + defaultMappings.put("ustar", "application/x-ustar"); + defaultMappings.put("dvi", "application/x-dvi"); + defaultMappings.put("hdf", "application/x-hdf"); + defaultMappings.put("latex", "application/x-latex"); + defaultMappings.put("bin", "application/octet-stream"); + defaultMappings.put("oda", "application/oda"); + defaultMappings.put("pdf", "application/pdf"); + defaultMappings.put("ps", "application/postscript"); + defaultMappings.put("eps", "application/postscript"); + defaultMappings.put("ai", "application/postscript"); + defaultMappings.put("rtf", "application/rtf"); + defaultMappings.put("nc", "application/x-netcdf"); + defaultMappings.put("cdf", "application/x-netcdf"); + defaultMappings.put("cer", "application/x-x509-ca-cert"); + defaultMappings.put("exe", "application/octet-stream"); + defaultMappings.put("gz", "application/x-gzip"); + defaultMappings.put("Z", "application/x-compress"); + defaultMappings.put("z", "application/x-compress"); + defaultMappings.put("hqx", "application/mac-binhex40"); + defaultMappings.put("mif", "application/x-mif"); + defaultMappings.put("ico", "image/x-icon"); + defaultMappings.put("ief", "image/ief"); + defaultMappings.put("tiff", "image/tiff"); + defaultMappings.put("tif", "image/tiff"); + defaultMappings.put("ras", "image/x-cmu-raster"); + defaultMappings.put("pnm", "image/x-portable-anymap"); + defaultMappings.put("pbm", "image/x-portable-bitmap"); + defaultMappings.put("pgm", "image/x-portable-graymap"); + defaultMappings.put("ppm", "image/x-portable-pixmap"); + defaultMappings.put("rgb", "image/x-rgb"); + defaultMappings.put("xbm", "image/x-xbitmap"); + defaultMappings.put("xpm", "image/x-xpixmap"); + defaultMappings.put("xwd", "image/x-xwindowdump"); + defaultMappings.put("au", "audio/basic"); + defaultMappings.put("snd", "audio/basic"); + defaultMappings.put("aif", "audio/x-aiff"); + defaultMappings.put("aiff", "audio/x-aiff"); + defaultMappings.put("aifc", "audio/x-aiff"); + defaultMappings.put("wav", "audio/x-wav"); + defaultMappings.put("mp3", "audio/mpeg"); + defaultMappings.put("mpeg", "video/mpeg"); + defaultMappings.put("mpg", "video/mpeg"); + defaultMappings.put("mpe", "video/mpeg"); + defaultMappings.put("qt", "video/quicktime"); + defaultMappings.put("mov", "video/quicktime"); + defaultMappings.put("avi", "video/x-msvideo"); + defaultMappings.put("movie", "video/x-sgi-movie"); + defaultMappings.put("avx", "video/x-rad-screenplay"); + defaultMappings.put("wrl", "x-world/x-vrml"); + defaultMappings.put("mpv2", "video/mpeg2"); + defaultMappings.put("jnlp", "application/x-java-jnlp-file"); + + defaultMappings.put("eot", "application/vnd.ms-fontobject"); + defaultMappings.put("woff", "application/font-woff"); + defaultMappings.put("woff2", "application/font-woff2"); + defaultMappings.put("ttf", "application/x-font-ttf"); + defaultMappings.put("otf", "application/x-font-opentype"); + defaultMappings.put("sfnt", "application/font-sfnt"); + + /* Add XML related MIMEs */ + + defaultMappings.put("xml", "application/xml"); + defaultMappings.put("xhtml", "application/xhtml+xml"); + defaultMappings.put("xsl", "application/xml"); + defaultMappings.put("svg", "image/svg+xml"); + defaultMappings.put("svgz", "image/svg+xml"); + defaultMappings.put("wbmp", "image/vnd.wap.wbmp"); + defaultMappings.put("wml", "text/vnd.wap.wml"); + defaultMappings.put("wmlc", "application/vnd.wap.wmlc"); + defaultMappings.put("wmls", "text/vnd.wap.wmlscript"); + defaultMappings.put("wmlscriptc", "application/vnd.wap.wmlscriptc"); + + /** + * 视频相关 + */ + defaultMappings.put("asf", "video/x-ms-asf"); + defaultMappings.put("asx", "video/x-ms-asf"); + defaultMappings.put("flv", "video/x-flv"); + defaultMappings.put("mp4", "video/mp4"); + defaultMappings.put("mps", "video/x-mpeg"); + defaultMappings.put("mpv", "video/mpg"); + defaultMappings.put("mpa", "video/x-mpg"); + defaultMappings.put("m4e", "video/mpeg4"); + defaultMappings.put("m2v", "video/x-mpeg"); + defaultMappings.put("wmv", "video/x-ms-wmv"); + defaultMappings.put("3gp", "video/3gpp"); + defaultMappings.put("ts", "video/MP2T"); + + + /** + * 音频相关 + */ + defaultMappings.put("mp2", "audio/mp2"); + defaultMappings.put("m3u", "audio/x-mpegurl"); + defaultMappings.put("m3u8", "audio/x-mpegurl"); + defaultMappings.put("mpga", "audio/rn-mpeg"); + defaultMappings.put("ra", "audio/vnd.rn-realaudio"); + defaultMappings.put("ram", "audio/x-pn-realaudio"); + defaultMappings.put("wax", "audio/x-ms-wax"); + defaultMappings.put("wma", "audio/x-ms-wma"); + + /** + * 文档相关 + */ + defaultMappings.put("doc", "application/msword"); + defaultMappings.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); + defaultMappings.put("xls", "application/vnd.ms-excel"); + defaultMappings.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + defaultMappings.put("pot", "application/vnd.ms-powerpoint"); + defaultMappings.put("ppt", "application/vnd.ms-powerpoint"); + defaultMappings.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"); + + } + + public static String getMimeType(String file) { + String lower = file.toLowerCase(); + int pos = lower.lastIndexOf('.'); + if (pos == -1) { + return null; //no extension + } + return defaultMappings.get(lower.substring(pos + 1)); + } + +} diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index 7db503ef..95ff1e36 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -15,6 +15,7 @@ */ package io.jboot.components.http.jboot; +import io.jboot.components.http.HttpMimeTypes; import io.jboot.components.http.JbootHttp; import io.jboot.components.http.JbootHttpRequest; import io.jboot.components.http.JbootHttpResponse; @@ -147,12 +148,11 @@ public class JbootHttpImpl implements JbootHttp { checkFileNormal(file); writeString(dos, request, startFlag + boundary + endFlag); writeString(dos, request, "Content-Disposition: form-data; name=\"" + entry.getKey() + "\"; filename=\"" + file.getName() + "\""); + writeString(dos, request, endFlag + "Content-Type: " + HttpMimeTypes.getMimeType(file.getName())); writeString(dos, request, endFlag + endFlag); - FileInputStream fStream = new FileInputStream(file); - byte[] buffer = new byte[2028]; - for (int len = 0; (len = fStream.read(buffer)) > 0; ) { - dos.write(buffer, 0, len); - } + + writeFile(dos, file); + writeString(dos, request, endFlag); } else { writeString(dos, request, startFlag + boundary + endFlag); @@ -171,6 +171,15 @@ public class JbootHttpImpl implements JbootHttp { dos.write(s.getBytes(request.getCharset())); } + private void writeFile(DataOutputStream dos, File file) throws IOException { + try (FileInputStream fStream = new FileInputStream(file)) { + byte[] buffer = new byte[2028]; + for (int len = 0; (len = fStream.read(buffer)) > 0; ) { + dos.write(buffer, 0, len); + } + } + } + private static void checkFileNormal(File file) { if (!file.exists()) { throw new JbootException("file not exists!!!!" + file); -- Gitee From 9b15e82fce05fad5f7f7210114cf4a46717bb934 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 24 Dec 2021 15:03:23 +0800 Subject: [PATCH 1506/1965] optimize... --- .../java/io/jboot/aop/InterceptorCache.java | 3 +- .../jboot/aop/cglib/JbootCglibCallback.java | 12 ++---- .../java/io/jboot/app/ApplicationUtil.java | 10 ++--- .../jboot/app/config/JbootConfigManager.java | 35 +++++---------- .../support/nacos/NacosConfigManager.java | 7 +-- .../io/jboot/components/cache/AopCache.java | 6 +-- .../components/cache/JbootCacheManager.java | 35 +++++++-------- .../cache/caredis/JbootCaredisCacheImpl.java | 17 ++++---- .../components/cache/j2cache/J2cacheImpl.java | 24 +++++------ .../gateway/JbootGatewayConfig.java | 43 ++++++++----------- .../gateway/JbootGatewayHealthChecker.java | 10 ++--- .../components/rpc/ReferenceConfigCache.java | 19 ++++---- .../valid/interceptor/SizeInterceptor.java | 1 + .../io/jboot/core/spi/JbootSpiLoader.java | 5 ++- .../java/io/jboot/db/model/JbootModel.java | 21 ++++----- .../io/jboot/db/model/JbootModelConfig.java | 22 +++------- .../counter/impl/JbootLocalCounter.java | 9 +--- .../objects/lock/impl/JbootLocalLock.java | 11 ++--- .../support/seata/JbootSeataManager.java | 26 +++++------ src/main/java/io/jboot/utils/FileUtil.java | 4 +- .../jboot/web/controller/JbootController.java | 6 +-- .../web/handler/JbootActionInvocation.java | 12 ++---- 22 files changed, 123 insertions(+), 215 deletions(-) diff --git a/src/main/java/io/jboot/aop/InterceptorCache.java b/src/main/java/io/jboot/aop/InterceptorCache.java index 0cc62e11..7cb28d83 100644 --- a/src/main/java/io/jboot/aop/InterceptorCache.java +++ b/src/main/java/io/jboot/aop/InterceptorCache.java @@ -54,7 +54,6 @@ public class InterceptorCache { } - public static class MethodKey { final int classHash; final int methodHash; @@ -80,7 +79,7 @@ public class InterceptorCache { @Override public boolean equals(Object methodKey) { MethodKey mk = (MethodKey) methodKey; - return classHash == mk.classHash && methodHash == mk.methodHash && paraHash == mk.paraHash; + return mk != null && classHash == mk.classHash && methodHash == mk.methodHash && paraHash == mk.paraHash; } @Override diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java index d1b06399..ca1eb759 100644 --- a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java @@ -46,16 +46,10 @@ public class JbootCglibCallback implements MethodInterceptor { InterceptorCache.MethodKey key = InterceptorCache.getMethodKey(targetClass, method); Interceptor[] inters = InterceptorCache.get(key); if (inters == null) { - synchronized (method) { - inters = InterceptorCache.get(key); - if (inters == null) { + inters = interManager.buildServiceMethodInterceptor(targetClass, method); + inters = builderManager.build(targetClass, method, inters); - inters = interManager.buildServiceMethodInterceptor(targetClass, method); - inters = builderManager.build(targetClass, method, inters); - - InterceptorCache.put(key, inters); - } - } + InterceptorCache.put(key, inters); } if (inters.length == 0) { diff --git a/src/main/java/io/jboot/app/ApplicationUtil.java b/src/main/java/io/jboot/app/ApplicationUtil.java index cc132033..7919742a 100644 --- a/src/main/java/io/jboot/app/ApplicationUtil.java +++ b/src/main/java/io/jboot/app/ApplicationUtil.java @@ -42,13 +42,10 @@ public class ApplicationUtil { } private static Boolean runInFatjar; + public static boolean runInFatjar() { - if (runInFatjar == null){ - synchronized (ApplicationUtil.class){ - if (runInFatjar == null){ - runInFatjar = buildRunInFatjar(); - } - } + if (runInFatjar == null) { + runInFatjar = buildRunInFatjar(); } return runInFatjar; } @@ -79,7 +76,6 @@ public class ApplicationUtil { } - static void printClassPath() { try { if (runInFatjar()) { diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index d51e9543..66b126e8 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -122,12 +122,9 @@ public class JbootConfigManager { Object configObject = configCache.get(clazz.getName() + prefix); if (configObject == null) { - synchronized (clazz) { - if (configObject == null) { - configObject = createConfigObject(clazz, prefix, file); - configCache.put(clazz.getName() + prefix, configObject); - } - } + Object obj = createConfigObject(clazz, prefix, file); + configCache.putIfAbsent(clazz.getName() + prefix, obj); + configObject = configCache.get(clazz.getName() + prefix); } return (T) configObject; @@ -190,7 +187,7 @@ public class JbootConfigManager { * @param * @return */ - public T createConfigObject(Class clazz, String prefix, String file) { + public synchronized T createConfigObject(Class clazz, String prefix, String file) { T configObject = JbootConfigKit.newInstance(clazz); for (Method setterMethod : JbootConfigKit.getClassSetMethods(clazz)) { String key = buildKey(prefix, setterMethod); @@ -339,13 +336,9 @@ public class JbootConfigManager { } - public void setRemoteProperty(String key, String value) { + public synchronized void setRemoteProperty(String key, String value) { if (remoteProperties == null) { - synchronized (this) { - if (remoteProperties == null) { - remoteProperties = new ConcurrentHashMap(); - } - } + remoteProperties = new ConcurrentHashMap(); } remoteProperties.put(key, value); } @@ -358,13 +351,9 @@ public class JbootConfigManager { } - public void setRemoteProperties(Map map) { + public synchronized void setRemoteProperties(Map map) { if (remoteProperties == null) { - synchronized (this) { - if (remoteProperties == null) { - remoteProperties = new ConcurrentHashMap(); - } - } + remoteProperties = new ConcurrentHashMap(); } remoteProperties.putAll(map); } @@ -501,12 +490,8 @@ public class JbootConfigManager { public boolean isDevMode() { if (devMode == null) { - synchronized (this) { - if (devMode == null) { - String appMode = getConfigValue("jboot.app.mode"); - devMode = (null == appMode || "".equals(appMode.trim()) || "dev".equalsIgnoreCase(appMode.trim())); - } - } + String appMode = getConfigValue("jboot.app.mode"); + devMode = (null == appMode || "".equals(appMode.trim()) || "dev".equalsIgnoreCase(appMode.trim())); } return devMode; } diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java index b46d8af2..bfc5e292 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java @@ -73,15 +73,16 @@ public class NacosConfigManager { /** * 接收到 nacos 服务器消息 + * * @param configManager * @param configInfo */ public void onReceiveConfigInfo(JbootConfigManager configManager, String configInfo) { Properties properties = str2Properties(configInfo); - if (contentProperties == null) { + if (contentProperties == null && properties != null) { contentProperties = properties; configManager.setRemoteProperties(properties); - } else { + } else if (contentProperties != null && properties != null) { for (Object key : properties.keySet()) { String newValue = properties.getProperty(key.toString()); String oldValue = contentProperties.getProperty(key.toString()); @@ -90,7 +91,7 @@ public class NacosConfigManager { contentProperties.put(key, newValue); configManager.setRemoteProperty(key.toString(), newValue); - configManager.notifyChangeListeners(key.toString(),newValue,oldValue); + configManager.notifyChangeListeners(key.toString(), newValue, oldValue); } } } diff --git a/src/main/java/io/jboot/components/cache/AopCache.java b/src/main/java/io/jboot/components/cache/AopCache.java index 894a3c8f..763864fc 100644 --- a/src/main/java/io/jboot/components/cache/AopCache.java +++ b/src/main/java/io/jboot/components/cache/AopCache.java @@ -22,11 +22,7 @@ public class AopCache { static JbootCache getAopCache() { if (aopCache == null) { - synchronized (AopCache.class) { - if (aopCache == null) { - aopCache = JbootCacheManager.me().getCache(JbootAopCacheConfig.getInstance().getUseCacheName()); - } - } + aopCache = JbootCacheManager.me().getCache(JbootAopCacheConfig.getInstance().getUseCacheName()); } return aopCache; } diff --git a/src/main/java/io/jboot/components/cache/JbootCacheManager.java b/src/main/java/io/jboot/components/cache/JbootCacheManager.java index 6631c52c..232e88ed 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheManager.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheManager.java @@ -57,32 +57,29 @@ public class JbootCacheManager { JbootCache cache = cacheMap.get(name); if (cache == null) { - synchronized (this) { - cache = cacheMap.get(name); - if (cache == null) { - Map configModels = ConfigUtil.getConfigModels(JbootCacheConfig.class); - JbootCacheConfig.TYPES.forEach(configModels::remove); - - configModels.putIfAbsent("default", Jboot.config(JbootCacheConfig.class)); - - if (!configModels.containsKey(name)) { - throw new JbootIllegalConfigException("Please config \"jboot.cache." + name + ".type\" in your jboot.properties."); - } - - JbootCacheConfig cacheConfig = configModels.get(name); - cache = buildCache(cacheConfig); - if (cache != null) { - cacheMap.put(name, cache); - } - } + Map configModels = ConfigUtil.getConfigModels(JbootCacheConfig.class); + JbootCacheConfig.TYPES.forEach(configModels::remove); + + configModels.putIfAbsent("default", Jboot.config(JbootCacheConfig.class)); + + if (!configModels.containsKey(name)) { + throw new JbootIllegalConfigException("Please config \"jboot.cache." + name + ".type\" in your jboot.properties."); } + + JbootCacheConfig cacheConfig = configModels.get(name); + JbootCache newCache = buildCache(cacheConfig); + if (newCache != null) { + cacheMap.putIfAbsent(name, newCache); + } + + cache = cacheMap.get(name); } return cache; } - private JbootCache buildCache(JbootCacheConfig config) { + private synchronized JbootCache buildCache(JbootCacheConfig config) { switch (config.getType()) { case JbootCacheConfig.TYPE_EHCACHE: diff --git a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java index a6da211e..7c8a1f68 100644 --- a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java @@ -207,17 +207,16 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { @Override public List getKeys(String cacheName) { List list = keysCache.getIfPresent(cacheName); + if (list != null) { + return list; + } + + list = redisCacheImpl.getKeys(cacheName); if (list == null) { - list = redisCacheImpl.getKeys(cacheName); - if (list == null) { - synchronized (cacheName.intern()) { - if (list == null) { - list = new ArrayList(); - } - } - } - keysCache.put(cacheName, list); + list = new ArrayList(); } + keysCache.put(cacheName, list); + return list; } diff --git a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java index 0ba3819d..6d324225 100644 --- a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java +++ b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java @@ -100,17 +100,15 @@ public class J2cacheImpl extends JbootCacheBase { private Method sendEvictCmdMethod; @Override - public void refresh(String cacheName, Object key) { + public synchronized void refresh(String cacheName, Object key) { cacheName = buildCacheName(cacheName); if (sendEvictCmdMethod == null) { - synchronized (this) { - if (sendEvictCmdMethod == null) { - sendEvictCmdMethod = getSendEvictCmdMethod(); - } - } + sendEvictCmdMethod = getSendEvictCmdMethod(); } try { - sendEvictCmdMethod.invoke(J2Cache.getChannel(), cacheName, key); + if (sendClearCmdMethod != null) { + sendEvictCmdMethod.invoke(J2Cache.getChannel(), cacheName, key); + } } catch (Exception e) { e.printStackTrace(); } @@ -120,17 +118,15 @@ public class J2cacheImpl extends JbootCacheBase { private Method sendClearCmdMethod; @Override - public void refresh(String cacheName) { + public synchronized void refresh(String cacheName) { cacheName = buildCacheName(cacheName); if (sendClearCmdMethod == null) { - synchronized (this) { - if (sendClearCmdMethod == null) { - sendClearCmdMethod = getSendClearCmdMethod(); - } - } + sendClearCmdMethod = getSendClearCmdMethod(); } try { - sendClearCmdMethod.invoke(J2Cache.getChannel(), cacheName); + if (sendClearCmdMethod != null) { + sendClearCmdMethod.invoke(J2Cache.getChannel(), cacheName); + } } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index bcfafe4e..ac8691af 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -316,20 +316,16 @@ public class JbootGatewayConfig implements Serializable { public GatewayInterceptor[] getGatewayInterceptors() { if (gatewayInterceptors == null) { - synchronized (this) { - if (gatewayInterceptors == null) { - if (interceptors == null || interceptors.length == 0) { - gatewayInterceptors = EMPTY_GATEWAY_INTERCEPTOR_ARRAY; - } else { - gatewayInterceptors = new GatewayInterceptor[interceptors.length]; - for (int i = 0; i < interceptors.length; i++) { - GatewayInterceptor interceptor = ClassUtil.newInstance(interceptors[i]); - if (interceptor == null) { - throw new NullPointerException("can not new instance by class:" + interceptors[i]); - } - gatewayInterceptors[i] = interceptor; - } + if (interceptors == null || interceptors.length == 0) { + gatewayInterceptors = EMPTY_GATEWAY_INTERCEPTOR_ARRAY; + } else { + gatewayInterceptors = new GatewayInterceptor[interceptors.length]; + for (int i = 0; i < interceptors.length; i++) { + GatewayInterceptor interceptor = ClassUtil.newInstance(interceptors[i]); + if (interceptor == null) { + throw new NullPointerException("can not new instance by class:" + interceptors[i]); } + gatewayInterceptors[i] = interceptor; } } } @@ -357,21 +353,16 @@ public class JbootGatewayConfig implements Serializable { return gatewayLoadBalanceStrategy; } - if (gatewayLoadBalanceStrategy == null) { - synchronized (this) { - if (gatewayLoadBalanceStrategy == null) { - if (StrUtil.isBlank(loadBalanceStrategy)) { - gatewayLoadBalanceStrategy = GatewayLoadBalanceStrategy.DEFAULT_STRATEGY; - } else { - GatewayLoadBalanceStrategy glbs = ClassUtil.newInstance(loadBalanceStrategy); - if (glbs == null) { - throw new NullPointerException("Can not new instance by class: " + loadBalanceStrategy); - } - gatewayLoadBalanceStrategy = glbs; - } - } + if (StrUtil.isBlank(loadBalanceStrategy)) { + gatewayLoadBalanceStrategy = GatewayLoadBalanceStrategy.DEFAULT_STRATEGY; + } else { + GatewayLoadBalanceStrategy glbs = ClassUtil.newInstance(loadBalanceStrategy); + if (glbs == null) { + throw new NullPointerException("Can not new instance by class: " + loadBalanceStrategy); } + gatewayLoadBalanceStrategy = glbs; } + return gatewayLoadBalanceStrategy; } diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayHealthChecker.java b/src/main/java/io/jboot/components/gateway/JbootGatewayHealthChecker.java index 3eb7d014..c5d08bac 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayHealthChecker.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayHealthChecker.java @@ -47,14 +47,10 @@ public class JbootGatewayHealthChecker implements Runnable { * 开始健康检查 * 多次执行,只会启动一次 */ - public void start() { + public synchronized void start() { if (fixedScheduler == null) { - synchronized (this) { - if (fixedScheduler == null) { - fixedScheduler = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("jboot-gateway-health-check")); - fixedScheduler.scheduleWithFixedDelay(this, fixedSchedulerInitialDelay, fixedSchedulerDelay, TimeUnit.SECONDS); - } - } + fixedScheduler = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("jboot-gateway-health-check")); + fixedScheduler.scheduleWithFixedDelay(this, fixedSchedulerInitialDelay, fixedSchedulerDelay, TimeUnit.SECONDS); } } diff --git a/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java b/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java index e982f65f..0841d8fd 100644 --- a/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java +++ b/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java @@ -17,27 +17,24 @@ package io.jboot.components.rpc; import io.jboot.components.rpc.annotation.RPCInject; -import java.io.Serializable; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class ReferenceConfigCache { - private static Map configs = new ConcurrentHashMap<>(); + private static Map configs = new ConcurrentHashMap<>(); public static JbootrpcReferenceConfig getReferenceConfig(RPCInject rpcInject) { int identityHashCode = System.identityHashCode(rpcInject); - JbootrpcReferenceConfig referenceConfig = (JbootrpcReferenceConfig) configs.get(identityHashCode); + JbootrpcReferenceConfig referenceConfig = configs.get(identityHashCode); if (referenceConfig == null) { - synchronized (rpcInject) { - referenceConfig = (JbootrpcReferenceConfig) configs.get(identityHashCode); - if (referenceConfig == null) { - referenceConfig = new JbootrpcReferenceConfig(); - RPCUtil.appendAnnotation(RPCInject.class, rpcInject, referenceConfig); - configs.put(identityHashCode, referenceConfig); - } - } + JbootrpcReferenceConfig config = new JbootrpcReferenceConfig(); + RPCUtil.appendAnnotation(RPCInject.class, rpcInject, config); + configs.putIfAbsent(identityHashCode, config); + + referenceConfig = configs.get(identityHashCode); } + return referenceConfig; } } diff --git a/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java index 73dc402a..3cfc03ee 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java @@ -42,6 +42,7 @@ public class SizeInterceptor implements Interceptor { + ", but current value is null at method: " + ClassUtil.buildMethodString(inv.getMethod()); Ret paras = Ret.by("max", size.max()).set("min", size.min()); ValidUtil.throwValidException(parameters[index].getName(), size.message(), paras, reason); + return; } int len = getObjectLen(validObject); diff --git a/src/main/java/io/jboot/core/spi/JbootSpiLoader.java b/src/main/java/io/jboot/core/spi/JbootSpiLoader.java index 5584a46a..63f2c4ae 100644 --- a/src/main/java/io/jboot/core/spi/JbootSpiLoader.java +++ b/src/main/java/io/jboot/core/spi/JbootSpiLoader.java @@ -22,6 +22,7 @@ import io.jboot.utils.StrUtil; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.ServiceLoader; /** @@ -93,11 +94,11 @@ public class JbootSpiLoader { for (Class c : classes) { JbootSpi spiConfig = c.getAnnotation(JbootSpi.class); - if (spiConfig != null && spiName.equals(AnnotationUtil.get(spiConfig.value()))) { + if (spiConfig != null && Objects.equals(spiName, AnnotationUtil.get(spiConfig.value()))) { return ClassUtil.newInstance(c); } //support config class name - else if (spiName.equals(c.getName())) { + else if (Objects.equals(spiName, c.getName())) { return ClassUtil.newInstance(c); } } diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index fd745ae5..b6a23f8d 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -220,20 +220,15 @@ public class JbootModel> extends Model { private M use(String configName, boolean validateExist) { M newDao = this.get(DATASOURCE_CACHE_PREFIX + configName); if (newDao == null) { - synchronized (configName.intern()) { - newDao = this.get(DATASOURCE_CACHE_PREFIX + configName); - if (newDao == null) { - newDao = this.copy()._setConfigName(configName); - if (newDao._getConfig() == null) { - if (validateExist) { - throw new JbootIllegalConfigException("the datasource \"" + configName + "\" not config well, please config it in jboot.properties."); - } else { - return null; - } - } else { - this.put(DATASOURCE_CACHE_PREFIX + configName, newDao); - } + newDao = this.copy()._setConfigName(configName); + if (newDao._getConfig() == null) { + if (validateExist) { + throw new JbootIllegalConfigException("the datasource \"" + configName + "\" not config well, please config it in jboot.properties."); + } else { + return null; } + } else { + this.put(DATASOURCE_CACHE_PREFIX + configName, newDao); } } return newDao; diff --git a/src/main/java/io/jboot/db/model/JbootModelConfig.java b/src/main/java/io/jboot/db/model/JbootModelConfig.java index e0dc00a8..5d207fe6 100644 --- a/src/main/java/io/jboot/db/model/JbootModelConfig.java +++ b/src/main/java/io/jboot/db/model/JbootModelConfig.java @@ -159,13 +159,9 @@ public class JbootModelConfig { } if (defaultIdCachePrefix == null) { - synchronized (this) { - if (defaultIdCachePrefix == null) { - defaultIdCachePrefix = JbootCacheManager.me().getCache().getConfig().getDefaultCachePrefix(); - if (defaultIdCachePrefix == null) { - defaultIdCachePrefix = ""; - } - } + defaultIdCachePrefix = JbootCacheManager.me().getCache().getConfig().getDefaultCachePrefix(); + if (defaultIdCachePrefix == null) { + defaultIdCachePrefix = ""; } } return defaultIdCachePrefix; @@ -180,11 +176,7 @@ public class JbootModelConfig { public JbootModelFilter getFilter() { if (filter == null) { if (StrUtil.isNotBlank(filterClass)) { - synchronized (this) { - if (filter == null) { - filter = ClassUtil.newInstance(filterClass); - } - } + filter = ClassUtil.newInstance(filterClass); } else { filter = JbootModelFilter.DEFAULT; } @@ -202,11 +194,7 @@ public class JbootModelConfig { public PrimarykeyValueGenerator getPrimarykeyValueGenerator() { if (primarykeyValueGenerator == null) { if (StrUtil.isNotBlank(primarykeyValueGeneratorClass)) { - synchronized (this) { - if (primarykeyValueGenerator == null) { - primarykeyValueGenerator = ClassUtil.newInstance(primarykeyValueGeneratorClass); - } - } + primarykeyValueGenerator = ClassUtil.newInstance(primarykeyValueGeneratorClass); } else { primarykeyValueGenerator = PrimarykeyValueGenerator.DEFAULT; } diff --git a/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java b/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java index a30b65ee..4754b91e 100644 --- a/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java +++ b/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java @@ -34,13 +34,8 @@ public class JbootLocalCounter implements JbootCounter { public JbootLocalCounter(String name) { atomicLong = atomicLongs.get(name); if (atomicLong == null) { - synchronized (JbootLocalCounter.class) { - atomicLong = atomicLongs.get(name); - if (atomicLong == null) { - atomicLong = new AtomicLong(); - atomicLongs.put(name, atomicLong); - } - } + AtomicLong newAl = new AtomicLong(); + atomicLongs.putIfAbsent(name, newAl); } } diff --git a/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java b/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java index bcd77932..523d9b28 100644 --- a/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java +++ b/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java @@ -36,13 +36,8 @@ public class JbootLocalLock implements JbootLock { public JbootLocalLock(String name) { lock = LOCKS.get(name); if (lock == null) { - synchronized (JbootLocalLock.class) { - lock = LOCKS.get(name); - if (lock == null) { - lock = new ReentrantLock(); - LOCKS.put(name, lock); - } - } + ReentrantLock newLock = new ReentrantLock(); + LOCKS.putIfAbsent(name, newLock); } } @@ -63,7 +58,7 @@ public class JbootLocalLock implements JbootLock { @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { - return lock.tryLock(time,unit); + return lock.tryLock(time, unit); } @Override diff --git a/src/main/java/io/jboot/support/seata/JbootSeataManager.java b/src/main/java/io/jboot/support/seata/JbootSeataManager.java index c3ed560c..a7ab8435 100644 --- a/src/main/java/io/jboot/support/seata/JbootSeataManager.java +++ b/src/main/java/io/jboot/support/seata/JbootSeataManager.java @@ -87,22 +87,18 @@ public class JbootSeataManager { public FailureHandler getFailureHandler() { if (handler == null) { - synchronized (this) { + String failureHandlerClassOrSpiName = config.getFailureHandler(); + if (StrUtil.isBlank(failureHandlerClassOrSpiName)) { + handler = new DefaultFailureHandlerImpl(); + } else { + if (failureHandlerClassOrSpiName.contains(".")) { + handler = ClassUtil.newInstance(failureHandlerClassOrSpiName); + } + if (handler == null) { + handler = JbootSpiLoader.load(FailureHandler.class, failureHandlerClassOrSpiName); + } if (handler == null) { - String failureHandlerClassOrSpiName = config.getFailureHandler(); - if (StrUtil.isBlank(failureHandlerClassOrSpiName)) { - handler = new DefaultFailureHandlerImpl(); - } else { - if (failureHandlerClassOrSpiName.contains(".")) { - handler = ClassUtil.newInstance(failureHandlerClassOrSpiName); - } - if (handler == null) { - handler = JbootSpiLoader.load(FailureHandler.class, failureHandlerClassOrSpiName); - } - if (handler == null) { - handler = new DefaultFailureHandlerImpl(); - } - } + handler = new DefaultFailureHandlerImpl(); } } } diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index 9ceaea95..6f95f4cc 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -67,7 +67,7 @@ public class FileUtil { } public static void writeString(File file, String string) { - if (!file.getParentFile().exists()){ + if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } FileOutputStream fos = null; @@ -82,7 +82,7 @@ public class FileUtil { } public static void close(Closeable... closeable) { - if (closeable != null || closeable.length != 0) { + if (closeable != null && closeable.length != 0) { for (Closeable c : closeable) { if (c != null) { try { diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 4eed47f7..540398bd 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -258,11 +258,7 @@ public class JbootController extends Controller { @NotAction public Map getJwtParas() { if (jwtParas == null) { - synchronized (this) { - if (jwtParas == null) { - jwtParas = JwtManager.me().parseJwtToken(this); - } - } + jwtParas = JwtManager.me().parseJwtToken(this); } return jwtParas; } diff --git a/src/main/java/io/jboot/web/handler/JbootActionInvocation.java b/src/main/java/io/jboot/web/handler/JbootActionInvocation.java index db628770..6d7fe667 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionInvocation.java +++ b/src/main/java/io/jboot/web/handler/JbootActionInvocation.java @@ -57,14 +57,9 @@ public class JbootActionInvocation extends Invocation { InterceptorCache.MethodKey key = InterceptorCache.getMethodKey(action.getControllerClass(), action.getMethod()); Interceptor[] inters = InterceptorCache.get(key); if (inters == null) { - synchronized (action) { - inters = InterceptorCache.get(key); - if (inters == null) { - inters = action.getInterceptors(); - inters = builderManager.build(action.getControllerClass(), action.getMethod(), inters); - InterceptorCache.put(key, inters); - } - } + inters = action.getInterceptors(); + inters = builderManager.build(action.getControllerClass(), action.getMethod(), inters); + InterceptorCache.put(key, inters); } return inters; @@ -94,7 +89,6 @@ public class JbootActionInvocation extends Invocation { } - @Override public Object getArg(int index) { if (index >= args.length) { -- Gitee From ecb44b7ac4d32500ac3ca2fbd34874bf90716cf5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Dec 2021 10:58:57 +0800 Subject: [PATCH 1507/1965] rename JbootMqMessageInfo to MessageContext --- .../java/io/jboot/components/mq/JbootmqBase.java | 12 ++++++------ .../jboot/components/mq/JbootmqMessageListener.java | 4 ++-- .../{JbootMqMessageInfo.java => MessageContext.java} | 4 ++-- ...qMessageInfo.java => AliyunmqMessageContext.java} | 6 +++--- .../components/mq/aliyunmq/JbootAliyunmqImpl.java | 8 ++++---- .../jboot/components/mq/local/JbootLocalmqImpl.java | 4 ++-- ...mqMessageInfo.java => LocalmqMessageContext.java} | 6 +++--- .../jboot/components/mq/qpidmq/JbootQpidmqImpl.java | 2 +- ...dmqMessageInfo.java => QpidmqMessageContext.java} | 6 +++--- .../components/mq/rabbitmq/JbootRabbitmqImpl.java | 2 +- ...qMessageInfo.java => RabbitmqMessageContext.java} | 6 +++--- .../components/mq/redismq/JbootRedismqImpl.java | 4 ++-- ...mqMessageInfo.java => RedismqMessageContext.java} | 6 +++--- .../components/mq/rocketmq/JbootRocketmqImpl.java | 8 ++++---- ...qMessageInfo.java => RokectmqMessageContext.java} | 6 +++--- .../io/jboot/test/mq/rabbit/RabbitMqReceiver1.java | 4 ++-- .../io/jboot/test/mq/rabbit/RabbitMqReceiver2.java | 4 ++-- .../io/jboot/test/mq/rabbit/RabbitMqReceiver3.java | 4 ++-- .../io/jboot/test/mq/redis/RedisMqReceiver1.java | 6 +++--- .../io/jboot/test/mq/redis/RedisMqReceiver2.java | 6 +++--- .../io/jboot/test/mq/rocketmq/RocketmqReceiver1.java | 6 +++--- .../io/jboot/test/mq/rocketmq/RocketmqReceiver2.java | 6 +++--- 22 files changed, 60 insertions(+), 60 deletions(-) rename src/main/java/io/jboot/components/mq/{JbootMqMessageInfo.java => MessageContext.java} (64%) rename src/main/java/io/jboot/components/mq/aliyunmq/{AliyunmqMessageInfo.java => AliyunmqMessageContext.java} (79%) rename src/main/java/io/jboot/components/mq/local/{LocalmqMessageInfo.java => LocalmqMessageContext.java} (41%) rename src/main/java/io/jboot/components/mq/qpidmq/{QpidmqMessageInfo.java => QpidmqMessageContext.java} (55%) rename src/main/java/io/jboot/components/mq/rabbitmq/{RabbitmqMessageInfo.java => RabbitmqMessageContext.java} (76%) rename src/main/java/io/jboot/components/mq/redismq/{RedismqMessageInfo.java => RedismqMessageContext.java} (41%) rename src/main/java/io/jboot/components/mq/rocketmq/{RokectmqMessageInfo.java => RokectmqMessageContext.java} (81%) diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index d725eb91..588116ae 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -107,10 +107,10 @@ public abstract class JbootmqBase implements Jbootmq { return channelListeners.get(channel); } - public void notifyListeners(String channel, Object message, JbootMqMessageInfo messageInfo) { + public void notifyListeners(String channel, Object message, MessageContext context) { - boolean globalResult = notifyListeners(channel, message, messageInfo, globalListeners); - boolean channelResult = notifyListeners(channel, message, messageInfo, channelListeners.get(channel)); + boolean globalResult = notifyListeners(channel, message, context, globalListeners); + boolean channelResult = notifyListeners(channel, message, context, channelListeners.get(channel)); if (!globalResult && !channelResult) { LOG.error("Application has recevied mq message, But has no listener to process it. channel:" + @@ -119,7 +119,7 @@ public abstract class JbootmqBase implements Jbootmq { } - protected boolean notifyListeners(String channel, Object message, JbootMqMessageInfo messageInfo, Collection listeners) { + protected boolean notifyListeners(String channel, Object message, MessageContext context, Collection listeners) { if (listeners == null || listeners.size() == 0) { return false; } @@ -127,7 +127,7 @@ public abstract class JbootmqBase implements Jbootmq { if (syncRecevieMessageChannels.contains(channel)) { for (JbootmqMessageListener listener : listeners) { try { - listener.onMessage(channel, message, messageInfo); + listener.onMessage(channel, message, context); } catch (Throwable ex) { LOG.warn("listener[" + listener.getClass().getName() + "] execute mq message is error. channel:" + channel + " message:" + message); @@ -136,7 +136,7 @@ public abstract class JbootmqBase implements Jbootmq { } else { for (JbootmqMessageListener listener : listeners) { threadPool.execute(() -> { - listener.onMessage(channel, message, messageInfo); + listener.onMessage(channel, message, context); }); } } diff --git a/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java b/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java index dbe5dd6b..d29084ef 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java +++ b/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java @@ -22,9 +22,9 @@ public interface JbootmqMessageListener { /** * @param channel * @param message - * @param info + * @param context */ - default void onMessage(String channel, Object message, JbootMqMessageInfo info) { + default void onMessage(String channel, Object message, MessageContext context) { this.onMessage(channel, message); } diff --git a/src/main/java/io/jboot/components/mq/JbootMqMessageInfo.java b/src/main/java/io/jboot/components/mq/MessageContext.java similarity index 64% rename from src/main/java/io/jboot/components/mq/JbootMqMessageInfo.java rename to src/main/java/io/jboot/components/mq/MessageContext.java index 196a79ab..64a7f4f3 100644 --- a/src/main/java/io/jboot/components/mq/JbootMqMessageInfo.java +++ b/src/main/java/io/jboot/components/mq/MessageContext.java @@ -1,10 +1,10 @@ package io.jboot.components.mq; -public class JbootMqMessageInfo { +public class MessageContext { final Jbootmq mq; - public JbootMqMessageInfo(Jbootmq mq) { + public MessageContext(Jbootmq mq) { this.mq = mq; } diff --git a/src/main/java/io/jboot/components/mq/aliyunmq/AliyunmqMessageInfo.java b/src/main/java/io/jboot/components/mq/aliyunmq/AliyunmqMessageContext.java similarity index 79% rename from src/main/java/io/jboot/components/mq/aliyunmq/AliyunmqMessageInfo.java rename to src/main/java/io/jboot/components/mq/aliyunmq/AliyunmqMessageContext.java index 19886716..efe7e57f 100644 --- a/src/main/java/io/jboot/components/mq/aliyunmq/AliyunmqMessageInfo.java +++ b/src/main/java/io/jboot/components/mq/aliyunmq/AliyunmqMessageContext.java @@ -3,16 +3,16 @@ package io.jboot.components.mq.aliyunmq; import com.aliyun.openservices.ons.api.Action; import com.aliyun.openservices.ons.api.ConsumeContext; import com.aliyun.openservices.ons.api.Message; -import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.MessageContext; import io.jboot.components.mq.Jbootmq; -public class AliyunmqMessageInfo extends JbootMqMessageInfo { +public class AliyunmqMessageContext extends MessageContext { final Message orginalMessage; final ConsumeContext context; private Action returnAction = Action.CommitMessage; - public AliyunmqMessageInfo(Jbootmq mq, Message orginalMessage, ConsumeContext context) { + public AliyunmqMessageContext(Jbootmq mq, Message orginalMessage, ConsumeContext context) { super(mq); this.orginalMessage = orginalMessage; this.context = context; diff --git a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java index 8f7698ee..9be68f16 100644 --- a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java +++ b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java @@ -63,10 +63,10 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { consumer = ONSFactory.createConsumer(properties); for (String channel : channels) { consumer.subscribe(aliyunmqConfig.getBroadcastChannelPrefix() + channel, aliyunmqConfig.getSubscribeSubExpression(), (message, consumeContext) -> { - AliyunmqMessageInfo aliyunmqMessageInfo = new AliyunmqMessageInfo(this, message, consumeContext); + AliyunmqMessageContext context = new AliyunmqMessageContext(this, message, consumeContext); notifyListeners(channel, getSerializer().deserialize(message.getBody()) - , aliyunmqMessageInfo); - return aliyunmqMessageInfo.getReturnAction(); + , context); + return context.getReturnAction(); }); } consumer.start(); @@ -79,7 +79,7 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { consumer = ONSFactory.createConsumer(properties); for (String channel : channels) { consumer.subscribe(channel, aliyunmqConfig.getSubscribeSubExpression(), (message, consumeContext) -> { - AliyunmqMessageInfo aliyunmqMessageInfo = new AliyunmqMessageInfo(this, message, consumeContext); + AliyunmqMessageContext aliyunmqMessageInfo = new AliyunmqMessageContext(this, message, consumeContext); notifyListeners(channel, getSerializer().deserialize(message.getBody()) , aliyunmqMessageInfo); return aliyunmqMessageInfo.getReturnAction(); diff --git a/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java b/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java index b5c563de..2059026d 100644 --- a/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java +++ b/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java @@ -35,11 +35,11 @@ public class JbootLocalmqImpl extends JbootmqBase { @Override public void enqueue(Object message, String toChannel) { - notifyListeners(toChannel, message, new LocalmqMessageInfo(this)); + notifyListeners(toChannel, message, new LocalmqMessageContext(this)); } @Override public void publish(Object message, String toChannel) { - notifyListeners(toChannel, message, new LocalmqMessageInfo(this)); + notifyListeners(toChannel, message, new LocalmqMessageContext(this)); } } diff --git a/src/main/java/io/jboot/components/mq/local/LocalmqMessageInfo.java b/src/main/java/io/jboot/components/mq/local/LocalmqMessageContext.java similarity index 41% rename from src/main/java/io/jboot/components/mq/local/LocalmqMessageInfo.java rename to src/main/java/io/jboot/components/mq/local/LocalmqMessageContext.java index ed33949c..f53412e9 100644 --- a/src/main/java/io/jboot/components/mq/local/LocalmqMessageInfo.java +++ b/src/main/java/io/jboot/components/mq/local/LocalmqMessageContext.java @@ -1,12 +1,12 @@ package io.jboot.components.mq.local; -import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.MessageContext; import io.jboot.components.mq.Jbootmq; -public class LocalmqMessageInfo extends JbootMqMessageInfo { +public class LocalmqMessageContext extends MessageContext { - public LocalmqMessageInfo(Jbootmq mq) { + public LocalmqMessageContext(Jbootmq mq) { super(mq); } diff --git a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java index 444880ea..b4988e4d 100644 --- a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java +++ b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java @@ -217,7 +217,7 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { } if (object != null) { - notifyListeners(channel, object, new QpidmqMessageInfo(JbootQpidmqImpl.this, message)); + notifyListeners(channel, object, new QpidmqMessageContext(JbootQpidmqImpl.this, message)); } } } catch (Exception e) { diff --git a/src/main/java/io/jboot/components/mq/qpidmq/QpidmqMessageInfo.java b/src/main/java/io/jboot/components/mq/qpidmq/QpidmqMessageContext.java similarity index 55% rename from src/main/java/io/jboot/components/mq/qpidmq/QpidmqMessageInfo.java rename to src/main/java/io/jboot/components/mq/qpidmq/QpidmqMessageContext.java index 97b4111c..205e7b30 100644 --- a/src/main/java/io/jboot/components/mq/qpidmq/QpidmqMessageInfo.java +++ b/src/main/java/io/jboot/components/mq/qpidmq/QpidmqMessageContext.java @@ -1,16 +1,16 @@ package io.jboot.components.mq.qpidmq; -import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.MessageContext; import io.jboot.components.mq.Jbootmq; import javax.jms.Message; -public class QpidmqMessageInfo extends JbootMqMessageInfo { +public class QpidmqMessageContext extends MessageContext { final Message orignalMessage; - public QpidmqMessageInfo(Jbootmq mq, Message orignalMessage) { + public QpidmqMessageContext(Jbootmq mq, Message orignalMessage) { super(mq); this.orignalMessage = orignalMessage; } diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index 0c6ce9e5..243d1efa 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -105,7 +105,7 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { Object o = getSerializer().deserialize(body); - notifyListeners(orginaChannelName, o, new RabbitmqMessageInfo(JbootRabbitmqImpl.this, channel, consumerTag, envelope, properties)); + notifyListeners(orginaChannelName, o, new RabbitmqMessageContext(JbootRabbitmqImpl.this, channel, consumerTag, envelope, properties)); } }); } catch (IOException e) { diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/RabbitmqMessageInfo.java b/src/main/java/io/jboot/components/mq/rabbitmq/RabbitmqMessageContext.java similarity index 76% rename from src/main/java/io/jboot/components/mq/rabbitmq/RabbitmqMessageInfo.java rename to src/main/java/io/jboot/components/mq/rabbitmq/RabbitmqMessageContext.java index a36b05c0..e07f82e2 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/RabbitmqMessageInfo.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/RabbitmqMessageContext.java @@ -3,17 +3,17 @@ package io.jboot.components.mq.rabbitmq; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Envelope; -import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.MessageContext; import io.jboot.components.mq.Jbootmq; -public class RabbitmqMessageInfo extends JbootMqMessageInfo { +public class RabbitmqMessageContext extends MessageContext { final Channel channel; final String consumerTag; final Envelope envelope; final AMQP.BasicProperties properties; - public RabbitmqMessageInfo(Jbootmq mq, Channel channel, String consumerTag, Envelope envelope, AMQP.BasicProperties properties) { + public RabbitmqMessageContext(Jbootmq mq, Channel channel, String consumerTag, Envelope envelope, AMQP.BasicProperties properties) { super(mq); this.channel = channel; this.consumerTag = consumerTag; diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java index e3b0224f..67e387b6 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java @@ -72,7 +72,7 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { redis.subscribe(new BinaryJedisPubSub() { @Override public void onMessage(byte[] channel, byte[] message) { - notifyListeners(redis.bytesToKey(channel), getSerializer().deserialize(message), new RedismqMessageInfo(JbootRedismqImpl.this)); + notifyListeners(redis.bytesToKey(channel), getSerializer().deserialize(message), new RedismqMessageContext(JbootRedismqImpl.this)); } }, redis.keysToBytesArray(channels)); @@ -109,7 +109,7 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { for (String channel : this.channels) { Object data = redis.lpop(channel); if (data != null) { - notifyListeners(channel, data, new RedismqMessageInfo(JbootRedismqImpl.this)); + notifyListeners(channel, data, new RedismqMessageContext(JbootRedismqImpl.this)); } } } diff --git a/src/main/java/io/jboot/components/mq/redismq/RedismqMessageInfo.java b/src/main/java/io/jboot/components/mq/redismq/RedismqMessageContext.java similarity index 41% rename from src/main/java/io/jboot/components/mq/redismq/RedismqMessageInfo.java rename to src/main/java/io/jboot/components/mq/redismq/RedismqMessageContext.java index 321e5f24..170500c2 100644 --- a/src/main/java/io/jboot/components/mq/redismq/RedismqMessageInfo.java +++ b/src/main/java/io/jboot/components/mq/redismq/RedismqMessageContext.java @@ -1,12 +1,12 @@ package io.jboot.components.mq.redismq; -import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.MessageContext; import io.jboot.components.mq.Jbootmq; -public class RedismqMessageInfo extends JbootMqMessageInfo { +public class RedismqMessageContext extends MessageContext { - public RedismqMessageInfo(Jbootmq mq) { + public RedismqMessageContext(Jbootmq mq) { super(mq); } diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java index 20e20c71..42789a78 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java @@ -91,14 +91,14 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { // 注册回调实现类来处理从broker拉取回来的消息 consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> { - RokectmqMessageInfo rokectMqMessageInfo = new RokectmqMessageInfo(this, msgs, context); + RokectmqMessageContext msgContext = new RokectmqMessageContext(this, msgs, context); if (msgs != null && !msgs.isEmpty()) { for (MessageExt messageExt : msgs) { - notifyListeners(messageExt.getTopic(), getSerializer().deserialize(messageExt.getBody()), rokectMqMessageInfo); + notifyListeners(messageExt.getTopic(), getSerializer().deserialize(messageExt.getBody()), msgContext); } } - return rokectMqMessageInfo.getReturnStatus(); + return msgContext.getReturnStatus(); }); @@ -131,7 +131,7 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { final int len = rocketmqConfig.getBroadcastChannelPrefix().length(); consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> { - RokectmqMessageInfo rokectMqMessageInfo = new RokectmqMessageInfo(this, msgs, context); + RokectmqMessageContext rokectMqMessageInfo = new RokectmqMessageContext(this, msgs, context); if (msgs != null && !msgs.isEmpty()) { for (MessageExt messageExt : msgs) { String topic = messageExt.getTopic(); diff --git a/src/main/java/io/jboot/components/mq/rocketmq/RokectmqMessageInfo.java b/src/main/java/io/jboot/components/mq/rocketmq/RokectmqMessageContext.java similarity index 81% rename from src/main/java/io/jboot/components/mq/rocketmq/RokectmqMessageInfo.java rename to src/main/java/io/jboot/components/mq/rocketmq/RokectmqMessageContext.java index 4fe9be04..64982294 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/RokectmqMessageInfo.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/RokectmqMessageContext.java @@ -1,6 +1,6 @@ package io.jboot.components.mq.rocketmq; -import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.MessageContext; import io.jboot.components.mq.Jbootmq; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; @@ -8,13 +8,13 @@ import org.apache.rocketmq.common.message.MessageExt; import java.util.List; -public class RokectmqMessageInfo extends JbootMqMessageInfo { +public class RokectmqMessageContext extends MessageContext { private ConsumeConcurrentlyStatus returnStatus = ConsumeConcurrentlyStatus.CONSUME_SUCCESS; private final List msgs; private final ConsumeConcurrentlyContext context; - public RokectmqMessageInfo(Jbootmq jbootmq, List msgs, ConsumeConcurrentlyContext context) { + public RokectmqMessageContext(Jbootmq jbootmq, List msgs, ConsumeConcurrentlyContext context) { super(jbootmq); this.msgs = msgs; this.context = context; diff --git a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java index 7f7e902a..066fe9ff 100644 --- a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java +++ b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java @@ -3,7 +3,7 @@ package io.jboot.test.mq.rabbit; import io.jboot.Jboot; import io.jboot.app.JbootApplication; -import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.MessageContext; import io.jboot.components.mq.JbootmqMessageListener; /** @@ -40,7 +40,7 @@ public class RabbitMqReceiver1 { // 只监听 myChannel 这个通道 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @Override - public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + public void onMessage(String channel, Object message, MessageContext context) { System.out.println("Receive msg: " + message + ", from channel: " + channel); } },"channel1"); diff --git a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java index 326da73c..c6fb0a1d 100644 --- a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java +++ b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java @@ -3,7 +3,7 @@ package io.jboot.test.mq.rabbit; import io.jboot.Jboot; import io.jboot.app.JbootApplication; -import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.MessageContext; import io.jboot.components.mq.JbootmqMessageListener; /** @@ -40,7 +40,7 @@ public class RabbitMqReceiver2 { // 只监听 myChannel 这个通道 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @Override - public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + public void onMessage(String channel, Object message, MessageContext context) { System.out.println("Receive msg: " + message + ", from channel: " + channel); } },"channel2"); diff --git a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver3.java b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver3.java index f01d9f42..fc2ca4c7 100644 --- a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver3.java +++ b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver3.java @@ -3,7 +3,7 @@ package io.jboot.test.mq.rabbit; import io.jboot.Jboot; import io.jboot.app.JbootApplication; -import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.MessageContext; import io.jboot.components.mq.JbootmqMessageListener; /** @@ -38,7 +38,7 @@ public class RabbitMqReceiver3 { //添加监听,不指定通道,则监听所有通道 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @Override - public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + public void onMessage(String channel, Object message, MessageContext context) { System.out.println("Receive msg: " + message + ", from channel: " + channel); } }); diff --git a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java index edf70113..ee652310 100644 --- a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java +++ b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java @@ -3,7 +3,7 @@ package io.jboot.test.mq.redis; import io.jboot.Jboot; import io.jboot.app.JbootApplication; -import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.MessageContext; import io.jboot.components.mq.JbootmqMessageListener; public class RedisMqReceiver1 { @@ -24,7 +24,7 @@ public class RedisMqReceiver1 { //添加监听 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @Override - public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + public void onMessage(String channel, Object message, MessageContext context) { System.out.println("Receive msg: " + message + ", from channel: " + channel); } }); @@ -32,7 +32,7 @@ public class RedisMqReceiver1 { // 只监听 myChannel 这个通道 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @Override - public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + public void onMessage(String channel, Object message, MessageContext context) { System.out.println("Receive msg: " + message + ", from channel: " + channel); } },"myChannel"); diff --git a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java index 403304ba..1b77c202 100644 --- a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java +++ b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java @@ -3,7 +3,7 @@ package io.jboot.test.mq.redis; import io.jboot.Jboot; import io.jboot.app.JbootApplication; -import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.MessageContext; import io.jboot.components.mq.JbootmqMessageListener; public class RedisMqReceiver2 { @@ -24,7 +24,7 @@ public class RedisMqReceiver2 { //添加监听 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @Override - public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + public void onMessage(String channel, Object message, MessageContext context) { System.out.println("Receive msg: " + message + ", from channel: " + channel); } }); @@ -32,7 +32,7 @@ public class RedisMqReceiver2 { // 只监听 myChannel 这个通道 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @Override - public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + public void onMessage(String channel, Object message, MessageContext context) { System.out.println("Receive msg: " + message + ", from channel: " + channel); } },"myChannel"); diff --git a/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver1.java b/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver1.java index 5abc68a1..b1e167dc 100644 --- a/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver1.java +++ b/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver1.java @@ -3,7 +3,7 @@ package io.jboot.test.mq.rocketmq; import io.jboot.Jboot; import io.jboot.app.JbootApplication; -import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.MessageContext; import io.jboot.components.mq.JbootmqMessageListener; public class RocketmqReceiver1 { @@ -24,7 +24,7 @@ public class RocketmqReceiver1 { //添加监听 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @Override - public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + public void onMessage(String channel, Object message, MessageContext context) { System.out.println("Receive msg: " + message + ", from channel: " + channel); } }); @@ -32,7 +32,7 @@ public class RocketmqReceiver1 { // 只监听 myChannel 这个通道 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @Override - public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + public void onMessage(String channel, Object message, MessageContext context) { System.out.println("Receive msg: " + message + ", from channel: " + channel); } },"myChannel"); diff --git a/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver2.java b/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver2.java index 152cf0c3..ffe1ee9b 100644 --- a/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver2.java +++ b/src/test/java/io/jboot/test/mq/rocketmq/RocketmqReceiver2.java @@ -3,7 +3,7 @@ package io.jboot.test.mq.rocketmq; import io.jboot.Jboot; import io.jboot.app.JbootApplication; -import io.jboot.components.mq.JbootMqMessageInfo; +import io.jboot.components.mq.MessageContext; import io.jboot.components.mq.JbootmqMessageListener; public class RocketmqReceiver2 { @@ -24,7 +24,7 @@ public class RocketmqReceiver2 { //添加监听 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @Override - public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + public void onMessage(String channel, Object message, MessageContext context) { System.out.println("Receive msg: " + message + ", from channel: " + channel); } }); @@ -32,7 +32,7 @@ public class RocketmqReceiver2 { // 只监听 myChannel 这个通道 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @Override - public void onMessage(String channel, Object message, JbootMqMessageInfo info) { + public void onMessage(String channel, Object message, MessageContext context) { System.out.println("Receive msg: " + message + ", from channel: " + channel); } },"myChannel"); -- Gitee From dd781c4ef012cae3629ac4649d7a007ada5e6eef Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Dec 2021 18:38:50 +0800 Subject: [PATCH 1508/1965] optimize... --- src/main/java/io/jboot/app/PathKitExt.java | 32 ++++++++-------- .../cache/redis/JbootRedisCacheImpl.java | 2 +- .../io/jboot/components/mq/JbootmqBase.java | 17 +++++++-- .../mq/qpidmq/JbootQpidmqConfig.java | 12 +++++- .../components/mq/qpidmq/JbootQpidmqImpl.java | 2 +- .../mq/rabbitmq/JbootRabbitmqConfig.java | 11 +++++- .../mq/redismq/JbootRedismqImpl.java | 2 +- src/main/java/io/jboot/utils/ClassUtil.java | 38 +++++++------------ 8 files changed, 65 insertions(+), 51 deletions(-) diff --git a/src/main/java/io/jboot/app/PathKitExt.java b/src/main/java/io/jboot/app/PathKitExt.java index 12d358c3..ec690310 100644 --- a/src/main/java/io/jboot/app/PathKitExt.java +++ b/src/main/java/io/jboot/app/PathKitExt.java @@ -10,23 +10,23 @@ import java.util.List; */ public class PathKitExt { - private static String locationPath = null; // 定位路径 + private static String locationPath = null; // 定位路径 private static String rootClassPath = null; private static String webRootPath = null; /** * 1:获取 PathKitExt 类文件所处 jar 包文件所在的目录,注意在 "非部署" 环境中获取到的 - * 通常是 maven 本地库中的某个目录,因为在开发时项目所依赖的 jar 包在 maven 本地库中 - * 这种情况不能使用 - * + * 通常是 maven 本地库中的某个目录,因为在开发时项目所依赖的 jar 包在 maven 本地库中 + * 这种情况不能使用 + *

        * 2:PathKitExt 自身在开发时,也就是未打成 jar 包时,获取到的是 APP_BASE/target/classes - * 这种情况多数不必关心,因为 PathKitExt 在使用时必定处于 jar 包之中 - * + * 这种情况多数不必关心,因为 PathKitExt 在使用时必定处于 jar 包之中 + *

        * 3:获取到的 locationPath 目录用于生成部署时的 config 目录,该值只会在 "部署" 环境下被获取 - * 也用于生成 webRootPath、rootClassPath,这两个值也只会在 "部署" 时被获取 - * 这样就兼容了部署与非部署两种场景 - * + * 也用于生成 webRootPath、rootClassPath,这两个值也只会在 "部署" 时被获取 + * 这样就兼容了部署与非部署两种场景 + *

        * 注意:该路径尾部的 "/" 或 "\\" 已被去除 */ public static String getLocationPath() { @@ -45,7 +45,7 @@ public class PathKitExt { path = file.getParent(); } - path = removeSlashEnd(path); // 去除尾部 '/' 或 '\' 字符 + path = removeSlashEnd(path); // 去除尾部 '/' 或 '\' 字符 locationPath = path; return locationPath; @@ -85,7 +85,7 @@ public class PathKitExt { for (String dir : classPathDirs) { if (dir != null) { dir = removeSlashEnd(dir.trim()); - if (dir.endsWith("classes")) { + if (dir != null && dir.endsWith("classes")) { return dir; } } @@ -96,9 +96,9 @@ public class PathKitExt { /** * 1:开发环境 path 会以 classes 结尾 - * + *

        * 2:打包以后的部署环境不会以 classes 结尾,约定一个合理的项目打包结构 - * 暂时约定 APP_BASE/config 为 rootClassPath,因为要读取外部配置文件 + * 暂时约定 APP_BASE/config 为 rootClassPath,因为要读取外部配置文件 */ private static String processRootClassPath(String path) { if (path.endsWith("classes")) { @@ -113,7 +113,7 @@ public class PathKitExt { } public static String removeSlashEnd(String path) { - if (path != null && path.endsWith(File.separator)) { + if (path.endsWith(File.separator)) { return path.substring(0, path.length() - 1); } else { return path; @@ -165,7 +165,7 @@ public class PathKitExt { private static String[] buildClassPathDirs() { List list = new ArrayList<>(); String[] classPathArray = System.getProperty("java.class.path").split(File.pathSeparator); - for(String classPath : classPathArray) { + for (String classPath : classPathArray) { classPath = classPath.trim(); if (classPath.startsWith("./")) { @@ -176,7 +176,7 @@ public class PathKitExt { if (file.exists() && file.isDirectory()) { // if (!classPath.endsWith("/") && !classPath.endsWith("\\")) { if (!classPath.endsWith(File.separator)) { - classPath = classPath + File.separator; // append postfix char "/" + classPath = classPath + File.separator; // append postfix char "/" } list.add(classPath); diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java index 43cb294b..415476b2 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java @@ -35,7 +35,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { private JbootRedis redis; private JbootRedisCacheConfig cacheConfig; - private static String redisCacheNamesKey = "jboot_cache_names"; + private String redisCacheNamesKey = "jboot_cache_names"; private String globalKeyPrefix = ""; diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index 588116ae..83e456b0 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -18,6 +18,7 @@ package io.jboot.components.mq; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; +import com.jfinal.kit.LogKit; import com.jfinal.log.Log; import io.jboot.Jboot; import io.jboot.components.serializer.JbootSerializer; @@ -157,11 +158,11 @@ public abstract class JbootmqBase implements Jbootmq { } - protected boolean isStartListen = false; + protected boolean isStarted = false; @Override public boolean startListening() { - if (isStartListen) { + if (isStarted) { throw new JbootException("jboot mq is started before."); } @@ -169,8 +170,16 @@ public abstract class JbootmqBase implements Jbootmq { throw new JbootException("mq channels is null or empty, please config channels"); } - onStartListening(); - isStartListen = true; + try { + isStarted = true; + onStartListening(); + } catch (Exception ex) { + LogKit.error("start mq fail!"); + isStarted = false; + return false; + } + + return true; } diff --git a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqConfig.java b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqConfig.java index 4cf6e2bd..6b1f35ac 100644 --- a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqConfig.java +++ b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqConfig.java @@ -19,10 +19,18 @@ import io.jboot.app.config.annotation.ConfigModel; @ConfigModel(prefix = "jboot.mq.qpid") public class JbootQpidmqConfig { - private String username = "admin"; - private String password = "admin"; + /** + * 默认账号 admin + */ + private String username; + + /** + * 默认密码 admin + */ + private String password; private String host = "127.0.0.1:5672"; + private String virtualHost; private boolean serializerEnable = true; diff --git a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java index b4988e4d..6eaf95d2 100644 --- a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java +++ b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java @@ -196,7 +196,7 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { @Override public void run() { try { - for (; ; ) { + while (isStarted) { Message message = consumer.receive(); if (message == null) { continue; diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java index c3f424b7..0fe112d3 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java @@ -22,8 +22,15 @@ import io.jboot.app.config.annotation.ConfigModel; public class JbootRabbitmqConfig { - private String username = "guest"; - private String password = "guest"; + /** + * 默认 username 为 guest + */ + private String username; + + /** + * 默认密码为 guest + */ + private String password; private String host = "127.0.0.1"; private int port = 5672; diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java index 67e387b6..e73c1dee 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java @@ -95,7 +95,7 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { @Override public void run() { - for (; ; ) { + while (isStarted) { try { doExecuteDequeue(); Thread.sleep(1); diff --git a/src/main/java/io/jboot/utils/ClassUtil.java b/src/main/java/io/jboot/utils/ClassUtil.java index fa442563..91c7f7e2 100644 --- a/src/main/java/io/jboot/utils/ClassUtil.java +++ b/src/main/java/io/jboot/utils/ClassUtil.java @@ -54,40 +54,30 @@ public class ClassUtil { * @param * @return */ - public static T singleton(Class clazz, boolean createByAop) { + public static synchronized T singleton(Class clazz, boolean createByAop) { Object ret = singletons.get(clazz); if (ret == null) { - synchronized (clazz) { - ret = singletons.get(clazz); - if (ret == null) { - ret = newInstance(clazz, createByAop); - if (ret != null) { - singletons.put(clazz, ret); - } else { - LOG.error("cannot new newInstance!!!!"); - } - } + ret = newInstance(clazz, createByAop); + if (ret != null) { + singletons.put(clazz, ret); + } else { + LOG.error("cannot new newInstance!!!!"); } } return (T) ret; } - public static T singleton(Class clazz, boolean createByAop, boolean inject) { + public static synchronized T singleton(Class clazz, boolean createByAop, boolean inject) { Object ret = singletons.get(clazz); if (ret == null) { - synchronized (clazz) { - ret = singletons.get(clazz); - if (ret == null) { - ret = newInstance(clazz, createByAop); - if (ret != null) { - if (inject && !createByAop) { - Aop.inject(ret); - } - singletons.put(clazz, ret); - } else { - LOG.error("cannot new newInstance!!!!"); - } + ret = newInstance(clazz, createByAop); + if (ret != null) { + if (inject && !createByAop) { + Aop.inject(ret); } + singletons.put(clazz, ret); + } else { + LOG.error("cannot new newInstance!!!!"); } } return (T) ret; -- Gitee From b0276e503df5d8fb83ccd4c88457c8660396fe6d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Dec 2021 18:47:58 +0800 Subject: [PATCH 1509/1965] optimize... --- .../io/jboot/components/cache/j2cache/J2cacheImpl.java | 9 ++++++--- src/main/java/io/jboot/web/validate/Regex.java | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java index 6d324225..8fc07302 100644 --- a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java +++ b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java @@ -15,6 +15,7 @@ */ package io.jboot.components.cache.j2cache; +import com.jfinal.log.Log; import com.jfinal.plugin.ehcache.IDataLoader; import io.jboot.components.cache.JbootCacheBase; import io.jboot.components.cache.JbootCacheConfig; @@ -35,6 +36,8 @@ import java.util.stream.Collectors; */ public class J2cacheImpl extends JbootCacheBase { + private static final Log LOG = Log.getLog(J2cacheImpl.class); + public J2cacheImpl(JbootCacheConfig config) { super(config); } @@ -106,11 +109,11 @@ public class J2cacheImpl extends JbootCacheBase { sendEvictCmdMethod = getSendEvictCmdMethod(); } try { - if (sendClearCmdMethod != null) { + if (sendEvictCmdMethod != null) { sendEvictCmdMethod.invoke(J2Cache.getChannel(), cacheName, key); } } catch (Exception e) { - e.printStackTrace(); + LOG.error("refresh error!", e); } } @@ -128,7 +131,7 @@ public class J2cacheImpl extends JbootCacheBase { sendClearCmdMethod.invoke(J2Cache.getChannel(), cacheName); } } catch (Exception e) { - e.printStackTrace(); + LOG.error("refresh error!", e); } } diff --git a/src/main/java/io/jboot/web/validate/Regex.java b/src/main/java/io/jboot/web/validate/Regex.java index c9ae6da9..2c6d300e 100644 --- a/src/main/java/io/jboot/web/validate/Regex.java +++ b/src/main/java/io/jboot/web/validate/Regex.java @@ -59,7 +59,7 @@ public class Regex { /** * 密码长度 6~20 位数,字母、数字和下划线 */ - public static final String PASSWORD ="^[a-zA-Z0-9_]\\w{5,19}$"; + public static final String CIPHER ="^[a-zA-Z0-9_]\\w{5,19}$"; /** @@ -172,8 +172,8 @@ public class Regex { - System.out.println("\"123\".matches(PASSWORD) ---> " + ("123".matches(PASSWORD))); - System.out.println("\"123456\".matches(PASSWORD) ---> " + ("123456".matches(PASSWORD))); + System.out.println("\"123\".matches(PASSWORD) ---> " + ("123".matches(CIPHER))); + System.out.println("\"123456\".matches(PASSWORD) ---> " + ("123456".matches(CIPHER))); System.out.println(); -- Gitee From bc496ff75ae250aa4265ca0d497c663348dedd0c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Dec 2021 18:58:47 +0800 Subject: [PATCH 1510/1965] optimize... --- src/main/java/io/jboot/components/mq/Jbootmq.java | 2 ++ src/main/java/io/jboot/components/mq/JbootmqBase.java | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/main/java/io/jboot/components/mq/Jbootmq.java b/src/main/java/io/jboot/components/mq/Jbootmq.java index e64a98f9..1943c7db 100644 --- a/src/main/java/io/jboot/components/mq/Jbootmq.java +++ b/src/main/java/io/jboot/components/mq/Jbootmq.java @@ -38,4 +38,6 @@ public interface Jbootmq { public boolean startListening(); + public JbootmqConfig getConfig(); + } diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index 83e456b0..bcd63506 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -186,4 +186,9 @@ public abstract class JbootmqBase implements Jbootmq { protected abstract void onStartListening(); + + @Override + public JbootmqConfig getConfig() { + return config; + } } -- Gitee From f03bc0c7dd389cf36d981d40b3f7ddcdd47b3b3f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Dec 2021 19:49:32 +0800 Subject: [PATCH 1511/1965] optimize... --- src/main/java/io/jboot/test/MockMvcResult.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/test/MockMvcResult.java b/src/main/java/io/jboot/test/MockMvcResult.java index af574c2e..2c57a202 100644 --- a/src/main/java/io/jboot/test/MockMvcResult.java +++ b/src/main/java/io/jboot/test/MockMvcResult.java @@ -19,9 +19,8 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import io.jboot.test.web.MockHttpServletResponse; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import javax.servlet.http.Cookie; +import java.util.*; public class MockMvcResult { @@ -62,6 +61,19 @@ public class MockMvcResult { return headers; } + public Set getCookies() { + return response.getCookies(); + } + + public String getCookie(String name) { + for (Cookie cookie : response.getCookies()) { + if (Objects.equals(name, cookie.getName())) { + return cookie.getValue(); + } + } + return null; + } + public MockHttpServletResponse getResponse() { return response; -- Gitee From a5173f447c7e24295ba8667b250489e0180872dd Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Dec 2021 20:06:17 +0800 Subject: [PATCH 1512/1965] v3.13.0 release (^.^)YYa!! --- .../support/nacos/NacosConfigManager.java | 30 +++++++++++-------- .../mq/rabbitmq/JbootRabbitmqConfig.java | 2 +- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java index bfc5e292..20d1416b 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java @@ -79,22 +79,26 @@ public class NacosConfigManager { */ public void onReceiveConfigInfo(JbootConfigManager configManager, String configInfo) { Properties properties = str2Properties(configInfo); - if (contentProperties == null && properties != null) { - contentProperties = properties; - configManager.setRemoteProperties(properties); - } else if (contentProperties != null && properties != null) { - for (Object key : properties.keySet()) { - String newValue = properties.getProperty(key.toString()); - String oldValue = contentProperties.getProperty(key.toString()); - - if (!Objects.equals(newValue, oldValue)) { - contentProperties.put(key, newValue); - configManager.setRemoteProperty(key.toString(), newValue); - - configManager.notifyChangeListeners(key.toString(), newValue, oldValue); + if (properties != null) { + if (contentProperties == null) { + contentProperties = properties; + configManager.setRemoteProperties(properties); + } else { + for (Object key : properties.keySet()) { + String newValue = properties.getProperty(key.toString()); + String oldValue = contentProperties.getProperty(key.toString()); + + if (!Objects.equals(newValue, oldValue)) { + contentProperties.put(key, newValue); + configManager.setRemoteProperty(key.toString(), newValue); + + configManager.notifyChangeListeners(key.toString(), newValue, oldValue); + } } } } + + } diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java index 0fe112d3..f279765d 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java @@ -39,7 +39,7 @@ public class JbootRabbitmqConfig { private String broadcastChannelPrefix = "broadcast-"; private String broadcastChannelRoutingKey = ""; - //若配置为 false,则需要在 OnMessage 里,调用 RabbitmqMessageInfo.getChannel().baseAck(或者baseNack)进行消费(或者标识消费失败) + //若配置为 false,则需要在 OnMessage 里,调用 MessageContext.getChannel().baseAck(或者baseNack)进行消费(或者标识消费失败) private boolean autoAck = true; -- Gitee From 216120c6769e500ba1838152af72b6b9d7d41d16 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Dec 2021 20:08:06 +0800 Subject: [PATCH 1513/1965] v3.13.0 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 489ce44f..181858af 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.12.5 + 3.13.0 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 9bff06fa..c3d31e31 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.12.5 + 3.13.0 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 08aa9396..7a7f302e 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.12.5 + 3.13.0 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index d7180bc3..32d561a9 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.12.5 + 3.13.0 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.12.5', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.13.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 6c9d978f..fe200a19 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.12.5', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.13.0', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.12.5/jboot-3.12.5.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.0/jboot-3.13.0.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 2fad5350..1c900c8b 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.12.5"; + public static String VERSION = "3.13.0"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From cb39d2feab78229d819b86d07bcb6dedc64eadd7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 26 Dec 2021 20:08:14 +0800 Subject: [PATCH 1514/1965] v3.13.0 release (^.^)YYa!! --- changes.txt | 9 +++++++++ pom.xml | 10 +++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/changes.txt b/changes.txt index 4482ca8e..2a3bf33b 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,12 @@ +jboot v3.13.0: +新增:Jbootmq 的监听器(Listener)新增 MessageContext 参数,支持更多的消息配置 +新增:MockMvc 新增 upload 方法,用于对上传文件的测试 +优化:404 403 401 等错误输出 info 信息,而不是 warn +优化:开放更多的 JbootMqImpl 方法 +优化:优化 Http 工具类,上传文件的时候添加 ContentType 信息 + + + jboot v3.12.5: 新增:IdCache 默认的缓存前缀配置 优化:AOP 缓存添加默认时间为 1 个小时 diff --git a/pom.xml b/pom.xml index 414d6ca2..862608c0 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.0-SNAPSHOT + 3.13.0 jar jboot @@ -47,12 +47,12 @@ 3.3 3.3.0 4.0.3 - 2.13.0 + 2.13.1 5.1.49 2.2.14.Final 1.7.32 2.57 - 1.2.78 + 1.2.79 31.0.1-jre 2.2.5 3.7.1 @@ -70,7 +70,7 @@ 3.13.2.Final 1.4.2 1.1.8 - 4.2.5 + 4.2.7 1.8 1.8 @@ -276,7 +276,7 @@ com.alibaba druid - 1.2.6 + 1.2.8 provided -- Gitee From 3b4c3391c1d00c7951eb823e15cfed43a526122a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 27 Dec 2021 11:39:50 +0800 Subject: [PATCH 1515/1965] v3.13.1 snapshot --- pom.xml | 2 +- .../test/web/MockHttpServletRequest.java | 162 ++++++++++++++++-- 2 files changed, 148 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index 862608c0..ac90c6a2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.0 + 3.13.1-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/test/web/MockHttpServletRequest.java b/src/main/java/io/jboot/test/web/MockHttpServletRequest.java index f9a1bfef..744739a2 100644 --- a/src/main/java/io/jboot/test/web/MockHttpServletRequest.java +++ b/src/main/java/io/jboot/test/web/MockHttpServletRequest.java @@ -15,13 +15,12 @@ */ package io.jboot.test.web; -import io.jboot.test.MockProxy; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import com.jfinal.kit.LogKit; import io.jboot.utils.StrUtil; -import javax.servlet.DispatcherType; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletInputStream; +import javax.servlet.*; import javax.servlet.http.*; import java.io.BufferedReader; import java.io.IOException; @@ -29,7 +28,7 @@ import java.io.InputStreamReader; import java.security.Principal; import java.util.*; -public class MockHttpServletRequest extends HttpServletRequestWrapper { +public class MockHttpServletRequest implements HttpServletRequest { protected String contextPath; protected String method = "GET"; @@ -41,6 +40,13 @@ public class MockHttpServletRequest extends HttpServletRequestWrapper { protected String characterEncoding = "UTF-8"; protected String protocol = "HTTP/1.1"; + private String remoteAddr = "127.0.0.1"; + private String remoteHost = "localhost"; + private int remotePort = 80; + private String localName = "localhost"; + private String localAddr = "127.0.0.1"; + private int localPort = 80; + protected String remoteUser; protected String authType; protected Principal userPrincipal; @@ -61,9 +67,18 @@ public class MockHttpServletRequest extends HttpServletRequestWrapper { protected Set cookies = new HashSet<>(); protected LinkedList locales = new LinkedList<>(); + private boolean requestedSessionIdValid = true; + + private boolean requestedSessionIdFromCookie = true; + + private boolean requestedSessionIdFromURL = false; + + private final Set userRoles = new HashSet<>(); + private final Multimap parts = LinkedHashMultimap.create(); + public MockHttpServletRequest() { - super(MockProxy.create(HttpServletRequest.class)); +// super(MockProxy.create(HttpServletRequest.class)); } @Override @@ -135,6 +150,16 @@ public class MockHttpServletRequest extends HttpServletRequestWrapper { return remoteUser; } + + public void addUserRole(String role) { + this.userRoles.add(role); + } + + @Override + public boolean isUserInRole(String role) { + return userRoles.contains(role); + } + public void setRemoteUser(String remoteUser) { this.remoteUser = remoteUser; } @@ -473,6 +498,21 @@ public class MockHttpServletRequest extends HttpServletRequestWrapper { return servletContext; } + @Override + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { + return null; + } + + @Override + public boolean isAsyncStarted() { + return false; + } + @Override public String getServerName() { return "localhost"; @@ -488,34 +528,74 @@ public class MockHttpServletRequest extends HttpServletRequestWrapper { return new BufferedReader(new InputStreamReader(getInputStream())); } + + public void setRemotePort(int remotePort) { + this.remotePort = remotePort; + } + @Override public int getRemotePort() { - return 0; + return remotePort; + } + + public void setRemoteAddr(String remoteAddr) { + this.remoteAddr = remoteAddr; } @Override public String getRemoteAddr() { - return "127.0.0.1"; + return remoteAddr; + } + + public void setRemoteHost(String remoteHost) { + this.remoteHost = remoteHost; } @Override public String getRemoteHost() { - return "localhost"; + return remoteHost; } @Override public boolean isRequestedSessionIdFromURL() { + return requestedSessionIdFromURL; + } + + @Override + public boolean isRequestedSessionIdFromUrl() { + return requestedSessionIdFromURL; + } + + public void setRequestedSessionIdFromURL(boolean requestedSessionIdFromURL) { + this.requestedSessionIdFromURL = requestedSessionIdFromURL; + } + + @Override + public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { return false; } + @Override + public void login(String username, String password) throws ServletException { + LogKit.error("Unsupport login method!"); + } + + public void setRequestedSessionIdFromCookie(boolean requestedSessionIdFromCookie) { + this.requestedSessionIdFromCookie = requestedSessionIdFromCookie; + } + @Override public boolean isRequestedSessionIdFromCookie() { - return true; + return requestedSessionIdFromCookie; + } + + public void setRequestedSessionIdValid(boolean requestedSessionIdValid) { + this.requestedSessionIdValid = requestedSessionIdValid; } @Override public boolean isRequestedSessionIdValid() { - return true; + return requestedSessionIdValid; } @Override @@ -528,19 +608,31 @@ public class MockHttpServletRequest extends HttpServletRequestWrapper { return this.locales.getFirst(); } + public void setLocalPort(int localPort) { + this.localPort = localPort; + } + @Override public int getLocalPort() { - return 80; + return localPort; + } + + public void setLocalAddr(String localAddr) { + this.localAddr = localAddr; } @Override public String getLocalAddr() { - return "127.0.0.1"; + return localAddr; + } + + public void setLocalName(String localName) { + this.localName = localName; } @Override public String getLocalName() { - return "localhost"; + return localName; } @Override @@ -548,11 +640,26 @@ public class MockHttpServletRequest extends HttpServletRequestWrapper { return false; } + @Override + public AsyncContext getAsyncContext() { + return null; + } + @Override public boolean isSecure() { return false; } + @Override + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + @Override + public String getRealPath(String path) { + return this.servletContext.getRealPath(path); + } + @Override public long getDateHeader(String name) { return Long.valueOf(getHeader(name)); @@ -587,6 +694,31 @@ public class MockHttpServletRequest extends HttpServletRequestWrapper { this.authType = null; } + public void addPart(Part part) { + this.parts.put(part.getName(), part); + } + + @Override + public Part getPart(String name) throws IOException, ServletException { + final Collection parts = this.parts.get(name); + for (Part part : parts) { + return part; + } + return null; + } + + @Override + public Collection getParts() throws IOException, ServletException { + List result = new LinkedList<>(this.parts.values()); + return result; + } + + @Override + public T upgrade(Class handlerClass) throws IOException, ServletException { + LogKit.error("Unsupport upgrade method!"); + return null; + } + public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } -- Gitee From 132feea53865c729eb1a8f2d96c1107f97c1e331 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 27 Dec 2021 11:46:53 +0800 Subject: [PATCH 1516/1965] v3.13.1 snapshot --- src/main/java/io/jboot/test/MockMvc.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/test/MockMvc.java b/src/main/java/io/jboot/test/MockMvc.java index 2c8d44a3..4b3e4133 100644 --- a/src/main/java/io/jboot/test/MockMvc.java +++ b/src/main/java/io/jboot/test/MockMvc.java @@ -278,13 +278,15 @@ public class MockMvc { requestStartListener.accept(request); } - Set cookies = new HashSet<>(Arrays.asList(request.getCookies())); + Set cookies = new HashSet<>(); //开启 cookie 保持, 用户未设置自己的 cookie, 上次的 cookie 有值 if (isHoldCookiesEnable()) { cookies.addAll(holdCookies); } + cookies.addAll(Arrays.asList(request.getCookies())); + request.setCookies(cookies); doSendRequest(request, response); -- Gitee From efe144439e9fa4ddf50968923836e603723c3148 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 27 Dec 2021 12:14:00 +0800 Subject: [PATCH 1517/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=9A=84=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/test/MockApp.java | 3 +++ src/main/java/io/jboot/test/MockMvc.java | 12 ++++++---- src/main/java/io/jboot/test/TestConfig.java | 2 ++ .../java/io/jboot/utils/ClassScanner.java | 23 ++++++++----------- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/jboot/test/MockApp.java b/src/main/java/io/jboot/test/MockApp.java index 27194df6..e1d3f535 100644 --- a/src/main/java/io/jboot/test/MockApp.java +++ b/src/main/java/io/jboot/test/MockApp.java @@ -83,6 +83,9 @@ class MockApp { if (testConfig != null) { JbootConfigManager.parseArgs(testConfig.launchArgs()); JbootConfigManager.me().setDevMode(testConfig.devMode()); + ClassScanner.setPrintScannerInfoEnable(testConfig.printScannerInfo()); + } else { + ClassScanner.setPrintScannerInfoEnable(false); } doInitJFinalPathKit(testConfig); diff --git a/src/main/java/io/jboot/test/MockMvc.java b/src/main/java/io/jboot/test/MockMvc.java index 4b3e4133..4437122d 100644 --- a/src/main/java/io/jboot/test/MockMvc.java +++ b/src/main/java/io/jboot/test/MockMvc.java @@ -285,7 +285,9 @@ public class MockMvc { cookies.addAll(holdCookies); } - cookies.addAll(Arrays.asList(request.getCookies())); + for (Cookie cookie : request.getCookies()) { + doSetCookie(cookie, cookies); + } request.setCookies(cookies); doSendRequest(request, response); @@ -296,17 +298,17 @@ public class MockMvc { } if (isHoldCookiesEnable() && response.getCookies().size() > 0) { - response.getCookies().forEach(this::doSetCookie); + response.getCookies().forEach(cookie -> doSetCookie(cookie, holdCookies)); } } return new MockMvcResult(response); } - public void doSetCookie(Cookie newCookie) { - holdCookies.removeIf(cookie -> Objects.equals(cookie.getName(), newCookie.getName())); + public void doSetCookie(Cookie newCookie, Set toCookies) { + toCookies.removeIf(cookie -> Objects.equals(cookie.getName(), newCookie.getName())); if (StrUtil.isNotEmpty(newCookie.getValue())) { - holdCookies.add(newCookie); + toCookies.add(newCookie); } } diff --git a/src/main/java/io/jboot/test/TestConfig.java b/src/main/java/io/jboot/test/TestConfig.java index efb389d3..42665a4f 100644 --- a/src/main/java/io/jboot/test/TestConfig.java +++ b/src/main/java/io/jboot/test/TestConfig.java @@ -34,6 +34,8 @@ public @interface TestConfig { //配置 appMode boolean devMode() default true; + boolean printScannerInfo() default false; + //配置启动参数 String[] launchArgs() default ""; diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 35e9a105..31dbb0e6 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -39,17 +39,15 @@ public class ClassScanner { public static final Set scanClasses = new HashSet<>(); public static final Set excludeClasses = new HashSet<>(); + private static boolean printScannerInfoEnable = JbootConfigManager.me().isDevMode(); -// public static final Set excludeFolderInjar = new HashSet<>(); -// -// static { -// excludeFolderInjar.add("webapp"); -// excludeFolderInjar.add("template"); -// excludeFolderInjar.add("assets"); -// excludeFolderInjar.add("META-INF"); -// excludeFolderInjar.add("schemaorg_apache_xmlbeans"); -// excludeFolderInjar.add("rest-management-private-classpath"); -// } + public static boolean isPrintScannerInfoEnable() { + return printScannerInfoEnable; + } + + public static void setPrintScannerInfoEnable(boolean printScannerInfoEnable) { + ClassScanner.printScannerInfoEnable = printScannerInfoEnable; + } public static void addScanJarPrefix(String prefix) { @@ -556,7 +554,6 @@ public class ClassScanner { } - private static void initAppClasses() { Set jarPaths = new HashSet<>(); @@ -586,7 +583,7 @@ public class ClassScanner { continue; } - if (JbootConfigManager.me().isDevMode()) { + if (isPrintScannerInfoEnable()) { System.out.println("Jboot Scan ClassPath: " + classPath); } @@ -606,7 +603,7 @@ public class ClassScanner { continue; } - if (JbootConfigManager.me().isDevMode()) { + if (isPrintScannerInfoEnable()) { System.out.println("Jboot Scan Jar: " + jarPath); } -- Gitee From 4641f269a1a9bbacff406f51626db4bd77ff4b7c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 27 Dec 2021 17:44:25 +0800 Subject: [PATCH 1518/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=9A=84=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/test/MockMvcResult.java | 6 +++++- .../jboot/test/web/MockHttpServletResponse.java | 15 +++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/test/MockMvcResult.java b/src/main/java/io/jboot/test/MockMvcResult.java index 2c57a202..459b29e4 100644 --- a/src/main/java/io/jboot/test/MockMvcResult.java +++ b/src/main/java/io/jboot/test/MockMvcResult.java @@ -25,6 +25,7 @@ import java.util.*; public class MockMvcResult { final MockHttpServletResponse response; + private JSONObject jsonObject; public MockMvcResult(MockHttpServletResponse response) { this.response = response; @@ -35,7 +36,10 @@ public class MockMvcResult { } public JSONObject getContentAsJSONObject() { - return JSON.parseObject(getContent()); + if (jsonObject == null) { + jsonObject = JSON.parseObject(getContent()); + } + return jsonObject; } public String getContentType() { diff --git a/src/main/java/io/jboot/test/web/MockHttpServletResponse.java b/src/main/java/io/jboot/test/web/MockHttpServletResponse.java index d6fe548e..f6b492d2 100644 --- a/src/main/java/io/jboot/test/web/MockHttpServletResponse.java +++ b/src/main/java/io/jboot/test/web/MockHttpServletResponse.java @@ -28,12 +28,12 @@ import java.util.*; public class MockHttpServletResponse extends HttpServletResponseWrapper { + protected String contentString; protected ByteArrayOutputStream stream = new ByteArrayOutputStream(); protected PrintWriter writer; protected Map headers = new HashMap<>(); protected Set cookies = new HashSet<>(); - ; protected int status = 200; protected String statusMessage = "OK"; @@ -210,12 +210,15 @@ public class MockHttpServletResponse extends HttpServletResponseWrapper { } public String getContentString() { - try { - getWriter().flush(); - return stream.toString(characterEncoding); - } catch (IOException e) { - throw new RuntimeException(e); + if (contentString == null) { + try { + getWriter().flush(); + contentString = stream.toString(characterEncoding); + } catch (IOException e) { + throw new RuntimeException(e); + } } + return contentString; } @Override -- Gitee From 435d18190878599523ea94621fa25cb16f4e9f72 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 27 Dec 2021 21:42:06 +0800 Subject: [PATCH 1519/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BD=91=E5=85=B3?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/gateway/GatewayInvocation.java | 30 +++++++++---------- .../gateway/JbootGatewayHandler.java | 21 ++++++------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java index 43c71c99..3304bef3 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java +++ b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java @@ -78,22 +78,20 @@ public class GatewayInvocation { //启用 Sentinel 限流 if (config.isSentinelEnable()) { new GatewaySentinelProcesser().process(proxy, proxyUrl, config, request, response, skipExceptionRender); - return; - } - - - //未启用 Sentinel 的情况 - proxy.sendRequest(proxyUrl, request, response); - - - Exception exception = proxy.getException(); - if (exception != null && !skipExceptionRender) { - if (exception instanceof ConnectException) { - Ret connectionError = Ret.fail().set("errorCode", 2).set("message", "Can not connect to target server: " + proxyUrl); - renderError(exception, connectionError, config, request, response); - } else { - Ret ret = Ret.fail().set("errorCode", 9).set("message", exception.getMessage()); - renderError(exception, ret, config, request, response); + }else { + + //未启用 Sentinel 的情况 + proxy.sendRequest(proxyUrl, request, response); + + Exception exception = proxy.getException(); + if (exception != null && !skipExceptionRender) { + if (exception instanceof ConnectException) { + Ret connectionError = Ret.fail().set("errorCode", 2).set("message", "Can not connect to target server: " + proxyUrl); + renderError(exception, connectionError, config, request, response); + } else { + Ret ret = Ret.fail().set("errorCode", 9).set("message", exception.getMessage()); + renderError(exception, ret, config, request, response); + } } } diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java index 26e48276..1b95f1e2 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java @@ -31,22 +31,23 @@ public class JbootGatewayHandler extends Handler { @Override public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { JbootGatewayConfig config = JbootGatewayManager.me().matchingConfig(request); - if (config == null) { + if (config != null) { + try { + new GatewayInvocation(config, request, response).invoke(); + } finally { + isHandled[0] = true; + flushBuffer(response); + } + } else { next.handle(target, request, response, isHandled); - return; - } - - try { - new GatewayInvocation(config, request, response).invoke(); - } finally { - isHandled[0] = true; - flushBuffer(response); } } private static void flushBuffer(HttpServletResponse resp) { try { - resp.flushBuffer(); + if (!resp.isCommitted()) { + resp.flushBuffer(); + } } catch (IOException e) { LogKit.error(e.toString(), e); } -- Gitee From 88d581d53f7995d4864ef0da45582bdbc997224e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 27 Dec 2021 21:50:53 +0800 Subject: [PATCH 1520/1965] optimize... --- src/main/java/io/jboot/utils/AntPathMatcher.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/io/jboot/utils/AntPathMatcher.java b/src/main/java/io/jboot/utils/AntPathMatcher.java index c777c050..c82d0124 100644 --- a/src/main/java/io/jboot/utils/AntPathMatcher.java +++ b/src/main/java/io/jboot/utils/AntPathMatcher.java @@ -425,6 +425,9 @@ public class AntPathMatcher { */ public String extractPathWithinPattern(String pattern, String path) { + if (pattern == null){ + return ""; + } String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true); String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true); StringBuilder builder = new StringBuilder(); -- Gitee From 1548d6fab84d3d65b91faed41fc2ee911ea4ba0f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 28 Dec 2021 16:52:55 +0800 Subject: [PATCH 1521/1965] =?UTF-8?q?JbootModelConfig=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=20idCacheCachePrefix=20=E9=85=8D=E7=BD=AE=EF=BC=8C=E5=90=A6?= =?UTF-8?q?=E5=88=99=E4=BC=9A=E9=80=A0=E6=88=90=E9=87=8D=E5=A4=8D=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/db/model/JbootModel.java | 2 +- .../io/jboot/db/model/JbootModelConfig.java | 28 ------------------- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index b6a23f8d..50fb8ad2 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -483,7 +483,7 @@ public class JbootModel> extends Model { protected String buildIdCacheName(String orginal) { - return config.buildCacheName(orginal); + return orginal; } protected String buildIdCacheKey(Object... idValues) { diff --git a/src/main/java/io/jboot/db/model/JbootModelConfig.java b/src/main/java/io/jboot/db/model/JbootModelConfig.java index 5d207fe6..33327366 100644 --- a/src/main/java/io/jboot/db/model/JbootModelConfig.java +++ b/src/main/java/io/jboot/db/model/JbootModelConfig.java @@ -66,7 +66,6 @@ public class JbootModelConfig { private String idCacheName = "default"; - private String idCacheCachePrefix; public JbootModelConfig() { } @@ -143,29 +142,6 @@ public class JbootModelConfig { this.idCacheByCopyEnable = idCacheByCopyEnable; } - public String getIdCacheCachePrefix() { - return idCacheCachePrefix; - } - - public void setIdCacheCachePrefix(String idCacheCachePrefix) { - this.idCacheCachePrefix = idCacheCachePrefix; - } - - private String defaultIdCachePrefix; - - public String getIdCachePrefixOrDefault() { - if (StrUtil.isNotBlank(idCacheCachePrefix)) { - return idCacheCachePrefix; - } - - if (defaultIdCachePrefix == null) { - defaultIdCachePrefix = JbootCacheManager.me().getCache().getConfig().getDefaultCachePrefix(); - if (defaultIdCachePrefix == null) { - defaultIdCachePrefix = ""; - } - } - return defaultIdCachePrefix; - } public JbootModelConfig(String idCacheName) { this.idCacheName = idCacheName; @@ -230,9 +206,5 @@ public class JbootModelConfig { this.idCache = idCache; } - public String buildCacheName(String oraginalName) { - return getIdCachePrefixOrDefault() + ":" + oraginalName; - } - } -- Gitee From cd326ef446e5755f1e3c23f26449bc2c7d28ee0f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 28 Dec 2021 16:53:24 +0800 Subject: [PATCH 1522/1965] =?UTF-8?q?JbootDb=20=E6=96=B0=E5=A2=9E=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20Columns=20=E7=9A=84=20findFirst=20=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/JbootDb.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/io/jboot/db/JbootDb.java b/src/main/java/io/jboot/db/JbootDb.java index c52fd066..4009d9eb 100644 --- a/src/main/java/io/jboot/db/JbootDb.java +++ b/src/main/java/io/jboot/db/JbootDb.java @@ -70,6 +70,17 @@ public class JbootDb extends Db { } + public static Record findFirst(String tableName, Columns columns) { + return findFirst(tableName, columns, null); + } + + + public static Record findFirst(String tableName, Columns columns, String orderBy) { + final List records = use().find(tableName, columns, orderBy, 1); + return records != null && !records.isEmpty() ? records.get(0) : null; + } + + public static int delete(String tableName, Columns columns) { return use().delete(tableName, columns); } -- Gitee From 3d8131f89a4b0493ec883afafea2e0d1b189edee Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 29 Dec 2021 15:28:10 +0800 Subject: [PATCH 1523/1965] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96.?= =?UTF-8?q?..?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cache/redis/JbootRedisCacheImpl.java | 13 ++- .../gateway/JbootGatewayHandler.java | 19 +-- .../schedule/JbootDistributedRunnable.java | 7 +- .../java/io/jboot/db/model/JbootModel.java | 26 +++-- .../jboot/support/redis/JbootRedisLock.java | 7 +- .../redis/jedis/JbootJedisClusterImpl.java | 13 +-- .../support/redis/jedis/JbootJedisImpl.java | 13 +-- .../sentinel/datasource/FileDataSource.java | 7 +- src/main/java/io/jboot/test/MockApp.java | 2 +- .../java/io/jboot/test/MockMvcResult.java | 7 +- src/main/java/io/jboot/utils/CacheUtil.java | 2 +- src/main/java/io/jboot/utils/FileUtil.java | 66 +++++------ src/main/java/io/jboot/utils/QuietlyUtil.java | 47 ++++++++ src/main/java/io/jboot/utils/ReflectUtil.java | 109 +++++++++++++++++- .../web/handler/JbootActionReporter.java | 4 +- .../test/cache/redis/RedisCacheTester.java | 5 +- .../java/io/jboot/test/jfinal/JfinalEnum.java | 4 - .../java/io/jboot/test/utils/ReflectBean.java | 37 ++++++ .../io/jboot/test/utils/ReflectUtilTest.java | 33 ++++++ 19 files changed, 302 insertions(+), 119 deletions(-) create mode 100644 src/main/java/io/jboot/utils/QuietlyUtil.java create mode 100644 src/test/java/io/jboot/test/utils/ReflectBean.java create mode 100644 src/test/java/io/jboot/test/utils/ReflectUtilTest.java diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java index 415476b2..10aeef98 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java @@ -47,6 +47,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { redisCacheNamesKey = globalKeyPrefix + redisCacheNamesKey; } + if (cacheConfig.isConfigOk()) { redis = JbootRedisManager.me().getRedis(cacheConfig); } else { @@ -67,17 +68,17 @@ public class JbootRedisCacheImpl extends JbootCacheBase { @Override public void put(String cacheName, Object key, Object value) { if (value == null) { - // if value is null : java.lang.NullPointerException: null at redis.clients.jedis.Protocol.sendCommand(Protocol.java:99) + remove(cacheName, key); return; } redis.set(buildKey(cacheName, key), value); - redis.sadd(redisCacheNamesKey, cacheName); + redis.sadd(buildCacheName(redisCacheNamesKey), cacheName); } @Override public void put(String cacheName, Object key, Object value, int liveSeconds) { if (value == null) { - // if value is null : java.lang.NullPointerException: null at redis.clients.jedis.Protocol.sendCommand(Protocol.java:99) + remove(cacheName, key); return; } if (liveSeconds <= 0) { @@ -86,7 +87,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { } redis.setex(buildKey(cacheName, key), liveSeconds, value); - redis.sadd(redisCacheNamesKey, cacheName); + redis.sadd(buildCacheName(redisCacheNamesKey), cacheName); } @@ -115,7 +116,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { } } while (continueState); - redis.srem(redisCacheNamesKey, cacheName); + redis.srem(buildCacheName(redisCacheNamesKey), cacheName); } @@ -178,7 +179,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { @Override public List getNames() { - Set set = redis.smembers(redisCacheNamesKey); + Set set = redis.smembers(buildCacheName(redisCacheNamesKey)); return set == null ? null : new ArrayList(set); } diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java index 1b95f1e2..c9cf4f09 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java @@ -16,11 +16,9 @@ package io.jboot.components.gateway; import com.jfinal.handler.Handler; -import com.jfinal.kit.LogKit; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; /** * @author michael yang (fuhai999@gmail.com) @@ -32,24 +30,11 @@ public class JbootGatewayHandler extends Handler { public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { JbootGatewayConfig config = JbootGatewayManager.me().matchingConfig(request); if (config != null) { - try { - new GatewayInvocation(config, request, response).invoke(); - } finally { - isHandled[0] = true; - flushBuffer(response); - } + isHandled[0] = true; + new GatewayInvocation(config, request, response).invoke(); } else { next.handle(target, request, response, isHandled); } } - private static void flushBuffer(HttpServletResponse resp) { - try { - if (!resp.isCommitted()) { - resp.flushBuffer(); - } - } catch (IOException e) { - LogKit.error(e.toString(), e); - } - } } diff --git a/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java b/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java index ac6575d6..7a31c81e 100644 --- a/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java +++ b/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java @@ -18,6 +18,7 @@ package io.jboot.components.schedule; import com.jfinal.log.Log; import io.jboot.Jboot; import io.jboot.support.redis.JbootRedis; +import io.jboot.utils.QuietlyUtil; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) @@ -169,11 +170,7 @@ public class JbootDistributedRunnable implements Runnable { millis = 1000; } - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - e.printStackTrace(); - } + QuietlyUtil.quietlySleep(millis); } //for override diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 50fb8ad2..22c42050 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -26,7 +26,6 @@ import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; -import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Connection; @@ -818,9 +817,9 @@ public class JbootModel> extends Model { return get(_getPrimaryKey()); } - public T[] _getIdValues(Class clazz) { + public Object[] _getIdValues() { String[] pkeys = _getPrimaryKeys(); - T[] values = (T[]) Array.newInstance(clazz, pkeys.length); + Object[] values = new Object[pkeys.length]; int i = 0; for (String key : pkeys) { @@ -890,7 +889,7 @@ public class JbootModel> extends Model { @Override public boolean equals(Object o) { - if (o == null || !(o instanceof JbootModel)) { + if (o instanceof JbootModel) { return false; } @@ -904,11 +903,22 @@ public class JbootModel> extends Model { } + @Override + public int hashCode() { + //可能model在rpc的Controller层,没有映射到数据库 + if (_getTable(false) == null) { + return Objects.hash(_getAttrValues()); + } + + final Object[] idValues = _getIdValues(); + return idValues.length > 0 ? Objects.hash(idValues) : Objects.hash(_getAttrValues()); + } + public M preventXssAttack() { String[] attrNames = _getAttrNames(); for (String attrName : attrNames) { Object value = get(attrName); - if (value == null || !(value instanceof String)) { + if (!(value instanceof String)) { continue; } @@ -922,7 +932,7 @@ public class JbootModel> extends Model { String[] attrNames = _getAttrNames(); for (String attrName : attrNames) { Object value = get(attrName); - if (value == null || !(value instanceof String)) { + if (!(value instanceof String)) { continue; } @@ -934,9 +944,7 @@ public class JbootModel> extends Model { } } - if (isIgnoreAttr) { - continue; - } else { + if (!isIgnoreAttr) { set(attrName, StrUtil.escapeHtml((String) value)); } } diff --git a/src/main/java/io/jboot/support/redis/JbootRedisLock.java b/src/main/java/io/jboot/support/redis/JbootRedisLock.java index b4537832..8f9c79e7 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedisLock.java +++ b/src/main/java/io/jboot/support/redis/JbootRedisLock.java @@ -16,6 +16,7 @@ package io.jboot.support.redis; import io.jboot.Jboot; +import io.jboot.utils.QuietlyUtil; /** * Created by michael. @@ -149,11 +150,7 @@ public class JbootRedisLock { if (timeout > 0) { timeout -= 100; - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } + QuietlyUtil.quietlySleep(100); } } while (timeout > 0); diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java index 2a8ccdf9..d40957d1 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java @@ -20,6 +20,7 @@ import io.jboot.exception.JbootException; import io.jboot.support.redis.JbootRedisBase; import io.jboot.support.redis.JbootRedisConfig; import io.jboot.support.redis.RedisScanResult; +import io.jboot.utils.QuietlyUtil; import io.jboot.utils.StrUtil; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.*; @@ -1182,11 +1183,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { break; } catch (JedisConnectionException e) { LOG.error("failed connect to redis, reconnect it.", e); - try { - Thread.sleep(1000); - } catch (InterruptedException ie) { - break; - } + QuietlyUtil.quietlySleep(1000); } } } @@ -1218,11 +1215,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { break; } catch (Throwable e) { LOG.error("failed connect to redis, reconnect it.", e); - try { - Thread.sleep(1000); - } catch (InterruptedException ie) { - break; - } + QuietlyUtil.quietlySleep(1000); } } } diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java index 2449f076..3fdbbcf4 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java @@ -20,6 +20,7 @@ import io.jboot.exception.JbootIllegalConfigException; import io.jboot.support.redis.JbootRedisBase; import io.jboot.support.redis.JbootRedisConfig; import io.jboot.support.redis.RedisScanResult; +import io.jboot.utils.QuietlyUtil; import io.jboot.utils.StrUtil; import redis.clients.jedis.*; import redis.clients.jedis.exceptions.JedisConnectionException; @@ -1495,11 +1496,7 @@ public class JbootJedisImpl extends JbootRedisBase { break; } catch (JedisConnectionException e) { LOG.error("Failed connect to redis, reconnect it.", e); - try { - Thread.sleep(1000); - } catch (InterruptedException ie) { - break; - } + QuietlyUtil.quietlySleep(1000); } finally { returnResource(jedis); } @@ -1538,11 +1535,7 @@ public class JbootJedisImpl extends JbootRedisBase { break; } catch (Throwable e) { LOG.error("Failed connect to redis, reconnect it.", e); - try { - Thread.sleep(1000); - } catch (InterruptedException ie) { - break; - } + QuietlyUtil.quietlySleep(1000); } finally { if (jedis != null) { returnResource(jedis); diff --git a/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java b/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java index ad425069..0c0f75a8 100644 --- a/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java +++ b/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java @@ -19,6 +19,7 @@ import com.alibaba.csp.sentinel.datasource.AbstractDataSource; import com.alibaba.csp.sentinel.datasource.Converter; import com.jfinal.kit.LogKit; import io.jboot.utils.FileUtil; +import io.jboot.utils.QuietlyUtil; import java.io.File; @@ -50,11 +51,7 @@ public class FileDataSource extends AbstractDataSource { } catch (Exception ex) { LogKit.error(ex.toString(), ex); } - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } + QuietlyUtil.quietlySleep(5000); } }, "jboot-sentinel-file-reader").start(); } diff --git a/src/main/java/io/jboot/test/MockApp.java b/src/main/java/io/jboot/test/MockApp.java index e1d3f535..8af8389e 100644 --- a/src/main/java/io/jboot/test/MockApp.java +++ b/src/main/java/io/jboot/test/MockApp.java @@ -108,7 +108,7 @@ class MockApp { } filter.init(new MockFilterConfig()); - config = ReflectUtil.getFieldValue(JFinalFilter.class, "jfinalConfig", filter); + config = ReflectUtil.getFieldValue(filter, "jfinalConfig"); } catch (ServletException e) { e.printStackTrace(); } diff --git a/src/main/java/io/jboot/test/MockMvcResult.java b/src/main/java/io/jboot/test/MockMvcResult.java index 459b29e4..4c3cb476 100644 --- a/src/main/java/io/jboot/test/MockMvcResult.java +++ b/src/main/java/io/jboot/test/MockMvcResult.java @@ -120,9 +120,10 @@ public class MockMvcResult { @Override public String toString() { StringBuilder builder = new StringBuilder(); - builder.append("Headers:").append("\n").append(getHeaders()).append("\n\n"); - builder.append("Status:").append("\n").append(getStatus()).append("\n\n"); - builder.append("Content:").append("\n").append(getContent()).append("\n\n"); + builder.append("Response:\n"); + builder.append("Headers >>> ").append(getHeaders()).append("\n"); + builder.append("Status >>> ").append(getStatus()).append("\n"); + builder.append("Content >>> ").append(getContent()).append("\n"); return builder.toString(); } } diff --git a/src/main/java/io/jboot/utils/CacheUtil.java b/src/main/java/io/jboot/utils/CacheUtil.java index 3af1a526..9790a5de 100644 --- a/src/main/java/io/jboot/utils/CacheUtil.java +++ b/src/main/java/io/jboot/utils/CacheUtil.java @@ -25,7 +25,7 @@ import java.util.List; /** * Usage: * 1、CacheUtil.get("cacheName","key") - * 2、CacheUtil.use("redis").get("cacheName","key") + * 2、CacheUtil.use("default").get("cacheName","key") */ public class CacheUtil { diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index 6f95f4cc..696df6cf 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -82,17 +82,7 @@ public class FileUtil { } public static void close(Closeable... closeable) { - if (closeable != null && closeable.length != 0) { - for (Closeable c : closeable) { - if (c != null) { - try { - c.close(); - } catch (IOException e) { - LogKit.error(e.toString(), e); - } - } - } - } + QuietlyUtil.quietlyClose(closeable); } @@ -108,34 +98,40 @@ public class FileUtil { public static void unzip(String zipFilePath, String targetPath, boolean safeUnzip) throws IOException { + targetPath = getCanonicalPath(new File(targetPath)); ZipFile zipFile = new ZipFile(zipFilePath); try { Enumeration entryEnum = zipFile.entries(); - if (null != entryEnum) { - while (entryEnum.hasMoreElements()) { - OutputStream os = null; - InputStream is = null; - try { - ZipEntry zipEntry = (ZipEntry) entryEnum.nextElement(); - if (!zipEntry.isDirectory()) { - if (safeUnzip && isNotSafeFile(zipEntry.getName())) { - continue; - } - File targetFile = new File(targetPath + File.separator + zipEntry.getName()); - if (!targetFile.getParentFile().exists()) { - targetFile.getParentFile().mkdirs(); - } - os = new BufferedOutputStream(new FileOutputStream(targetFile)); - is = zipFile.getInputStream(zipEntry); - byte[] buffer = new byte[4096]; - int readLen = 0; - while ((readLen = is.read(buffer, 0, 4096)) > 0) { - os.write(buffer, 0, readLen); - } + while (entryEnum.hasMoreElements()) { + OutputStream os = null; + InputStream is = null; + try { + ZipEntry zipEntry = (ZipEntry) entryEnum.nextElement(); + if (!zipEntry.isDirectory()) { + if (safeUnzip && isNotSafeFile(zipEntry.getName())) { + //Unsafe + continue; + } + + File targetFile = new File(targetPath + File.separator + zipEntry.getName()); + if (safeUnzip && !getCanonicalPath(targetFile).startsWith(targetPath)) { + //Unsafe + continue; + } + + if (!targetFile.getParentFile().exists()) { + targetFile.getParentFile().mkdirs(); + } + os = new BufferedOutputStream(new FileOutputStream(targetFile)); + is = zipFile.getInputStream(zipEntry); + byte[] buffer = new byte[4096]; + int readLen = 0; + while ((readLen = is.read(buffer, 0, 4096)) > 0) { + os.write(buffer, 0, readLen); } - } finally { - close(is, os); } + } finally { + close(is, os); } } } finally { @@ -145,7 +141,7 @@ public class FileUtil { private static boolean isNotSafeFile(String name) { name = name.toLowerCase(); - return name.contains("..") || name.endsWith(".jsp") || name.endsWith(".jspx"); + return name.endsWith(".jsp") || name.endsWith(".jspx"); } diff --git a/src/main/java/io/jboot/utils/QuietlyUtil.java b/src/main/java/io/jboot/utils/QuietlyUtil.java new file mode 100644 index 00000000..fe66039b --- /dev/null +++ b/src/main/java/io/jboot/utils/QuietlyUtil.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.utils; + +import com.jfinal.kit.LogKit; + +import java.io.Closeable; +import java.io.IOException; + +public class QuietlyUtil { + + public static void quietlyClose(Closeable... closeables) { + if (closeables != null) { + for (Closeable closeable : closeables) { + if (closeable != null) { + try { + closeable.close(); + } catch (IOException e) { + LogKit.error(e.toString(), e); + } + } + } + } + } + + public static void quietlySleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + LogKit.error(e.toString(), e); + Thread.currentThread().interrupt(); + } + } +} diff --git a/src/main/java/io/jboot/utils/ReflectUtil.java b/src/main/java/io/jboot/utils/ReflectUtil.java index 3e055c40..430121a3 100644 --- a/src/main/java/io/jboot/utils/ReflectUtil.java +++ b/src/main/java/io/jboot/utils/ReflectUtil.java @@ -15,7 +15,10 @@ */ package io.jboot.utils; +import com.jfinal.kit.LogKit; + import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.LinkedList; @@ -31,7 +34,11 @@ public class ReflectUtil { return getFieldValue(dClass, fieldName, null); } - public static T getFieldValue(Class dClass, String fieldName, Object from) { + public static T getFieldValue(Object getFrom, String fieldName) { + return getFieldValue(getFrom.getClass(), fieldName, getFrom); + } + + private static T getFieldValue(Class dClass, String fieldName, Object getFrom) { try { if (StrUtil.isBlank(fieldName)) { throw new IllegalArgumentException("fieldName must not be null or empty."); @@ -40,13 +47,64 @@ public class ReflectUtil { if (field == null) { throw new NoSuchFieldException(fieldName); } - field.setAccessible(true); - return (T) field.get(from); - } catch (NoSuchFieldException | IllegalAccessException e) { + + return getFileValue(getFrom, field); + + } catch (NoSuchFieldException e) { throw new RuntimeException(e); } } + private static T getFileValue(Object getFrom, Field field) { + final boolean accessible = field.isAccessible(); + try { + field.setAccessible(true); + return (T) field.get(getFrom); + } catch (IllegalAccessException e) { + LogKit.error(e.toString(), e); + } finally { + field.setAccessible(accessible); + } + return null; + } + + + public static void setStaticFieldValue(Class dClass, String fieldName, Object value) { + setFieldValue(dClass, null, fieldName, value); + } + + + public static void setFieldValue(Object setTo, String fieldName, Object value) { + setFieldValue(setTo.getClass(), setTo, fieldName, value); + } + + + private static void setFieldValue(Class dClass, Object setTo, String fieldName, Object value) { + setFieldValue(dClass, setTo, f -> f.getName().equals(fieldName), value); + } + + + private static void setFieldValue(Class dClass, Object setTo, Predicate filter, Object value) { + Field field = searchField(dClass, filter); + if (field == null) { + throw new IllegalArgumentException("No such field"); + } + + setFieldValue(setTo, value, field); + } + + public static void setFieldValue(Object setTo, Object value, Field field) { + final boolean accessible = field.isAccessible(); + try { + field.setAccessible(true); + field.set(setTo, value); + } catch (IllegalAccessException e) { + LogKit.error(e.toString(), e); + } finally { + field.setAccessible(accessible); + } + } + public static Field searchField(Class dClass, Predicate filter) { if (dClass == null) { @@ -131,4 +189,47 @@ public class ReflectUtil { doSearchMethodList(dClass.getSuperclass(), filter, searchToList); } + + + public static T invokeStaticMethod(Class dClass, String methodName, Object... args) { + return invokeStaticMethod(dClass, m -> m.getName().equals(methodName), args); + } + + + public static T invokeStaticMethod(Class dClass, Predicate filter, Object... args) { + Method method = searchMethod(dClass, filter); + if (method == null) { + throw new IllegalArgumentException("No such method."); + } + return invokeMethod(null, method, args); + } + + + public static T invokeMethod(Object obj, String methodName, Object... args) { + return invokeMethod(obj, m -> m.getName().equals(methodName), args); + } + + + public static T invokeMethod(Object obj, Predicate filter, Object... args) { + Method method = searchMethod(obj.getClass(), filter); + if (method == null) { + throw new IllegalArgumentException("No such method."); + } + return invokeMethod(obj, method, args); + } + + + public static T invokeMethod(Object obj, Method method, Object... args) { + final boolean accessible = method.isAccessible(); + try { + method.setAccessible(true); + return (T) method.invoke(obj, args); + } catch (IllegalAccessException | InvocationTargetException e) { + LogKit.error(e.toString(), e); + } finally { + method.setAccessible(accessible); + } + return null; + } + } diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index fc8d7ce8..8a9ab0a0 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -255,10 +255,10 @@ public class JbootActionReporter { } sb.append("Render : ").append(text); } else if (render instanceof FileRender) { - File file = ReflectUtil.getFieldValue(FileRender.class, "file", render); + File file = ReflectUtil.getFieldValue(render, "file"); sb.append("Render : ").append(file); } else if (render instanceof RedirectRender) { - String url = ReflectUtil.getFieldValue(RedirectRender.class, "url", render); + String url = ReflectUtil.getFieldValue(render, "url"); sb.append("Redirect : ").append(url); } else if (render instanceof NullRender) { sb.append("Render : null"); diff --git a/src/test/java/io/jboot/test/cache/redis/RedisCacheTester.java b/src/test/java/io/jboot/test/cache/redis/RedisCacheTester.java index 0b8b9dc3..05d60eb9 100644 --- a/src/test/java/io/jboot/test/cache/redis/RedisCacheTester.java +++ b/src/test/java/io/jboot/test/cache/redis/RedisCacheTester.java @@ -33,9 +33,10 @@ public class RedisCacheTester { @Test public void testGet() { - Jboot.getCache().put("cachename", "key", "value"); + Jboot.getCache().put("cachename", "key", "value1111"); String value = Jboot.getCache().get("cachename", "key"); - Assert.assertTrue("value".equals(value)); + System.out.println("testGet:" + value); + Assert.assertTrue("value1111".equals(value)); } diff --git a/src/test/java/io/jboot/test/jfinal/JfinalEnum.java b/src/test/java/io/jboot/test/jfinal/JfinalEnum.java index 7e69eff6..310c8771 100644 --- a/src/test/java/io/jboot/test/jfinal/JfinalEnum.java +++ b/src/test/java/io/jboot/test/jfinal/JfinalEnum.java @@ -29,14 +29,10 @@ public enum JfinalEnum { return value; } - public void setValue(int value) { - this.value = value; - } public String getText() { return text; } - public void setText(String text) { this.text = text; } diff --git a/src/test/java/io/jboot/test/utils/ReflectBean.java b/src/test/java/io/jboot/test/utils/ReflectBean.java new file mode 100644 index 00000000..a6fb27ab --- /dev/null +++ b/src/test/java/io/jboot/test/utils/ReflectBean.java @@ -0,0 +1,37 @@ +package io.jboot.test.utils; + +public class ReflectBean { + + private static String someStaticValue = "staticvalue"; + private String id; + private int age; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + private void doSomeThing(){ + System.out.println("doSomeThing invoked!!"); + } + + @Override + public String toString() { + return "ReflectBean{" + + "id='" + id + '\'' + + ", age=" + age + + ", someStaticValue=" + someStaticValue + + '}'; + } +} diff --git a/src/test/java/io/jboot/test/utils/ReflectUtilTest.java b/src/test/java/io/jboot/test/utils/ReflectUtilTest.java new file mode 100644 index 00000000..828523a0 --- /dev/null +++ b/src/test/java/io/jboot/test/utils/ReflectUtilTest.java @@ -0,0 +1,33 @@ +package io.jboot.test.utils; + +import io.jboot.utils.ReflectUtil; +import org.junit.Assert; +import org.junit.Test; + +public class ReflectUtilTest { + + + @Test + public void testSetFieldValue(){ + ReflectBean bean = new ReflectBean(); + bean.setAge(18); + bean.setId("myid"); + + Assert.assertEquals((int)ReflectUtil.getFieldValue(bean,"age"),18); + Assert.assertEquals(ReflectUtil.getFieldValue(bean,"id"),"myid"); + Assert.assertEquals(ReflectUtil.getStaticFieldValue(ReflectBean.class,"someStaticValue"),"staticvalue"); + + ReflectUtil.setFieldValue(bean,"id","123"); + ReflectUtil.setFieldValue(bean,"age",45); + Assert.assertEquals("123",bean.getId()); + Assert.assertEquals(45,bean.getAge()); + + ReflectUtil.invokeMethod(bean,"doSomeThing"); + + System.out.println(bean); + + ReflectUtil.setStaticFieldValue(ReflectBean.class,"someStaticValue","vvvv1111"); + + System.out.println(bean); + } +} -- Gitee From 5a81c21261cb1e721eae34e3794bf803eec6d178 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 29 Dec 2021 15:34:24 +0800 Subject: [PATCH 1524/1965] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96.?= =?UTF-8?q?..?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/JbootModel.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 22c42050..65d224c3 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -889,7 +889,7 @@ public class JbootModel> extends Model { @Override public boolean equals(Object o) { - if (o instanceof JbootModel) { + if (!(o instanceof JbootModel)) { return false; } @@ -898,8 +898,8 @@ public class JbootModel> extends Model { return this == o; } - Object id = ((JbootModel) o)._getIdValue(); - return id != null && id.equals(_getIdValue()); + Object id = ((JbootModel) o)._getIdValue(); + return id != null && id.equals(this._getIdValue()); } -- Gitee From 71e4f0c7b9bac97a541469de85ea902b8dae1051 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 29 Dec 2021 16:01:31 +0800 Subject: [PATCH 1525/1965] v3.13.1 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 181858af..9dbbc92d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.13.0 + 3.13.1 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index c3d31e31..c057252e 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.13.0 + 3.13.1 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 7a7f302e..e5cf7cfa 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.13.0 + 3.13.1 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 32d561a9..a69c66d8 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.13.0 + 3.13.1 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.13.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.13.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index fe200a19..2a04ecb7 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.13.0', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.13.1', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.0/jboot-3.13.0.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.1/jboot-3.13.1.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 1c900c8b..8d10d890 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.13.0"; + public static String VERSION = "3.13.1"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 83d5a0d36ba7a8fac29526ac24b428424bb083c3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 29 Dec 2021 16:04:43 +0800 Subject: [PATCH 1526/1965] v3.13.1 release (^.^)YYa!! --- changes.txt | 13 +++++++++++++ pom.xml | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 2a3bf33b..f0ffa775 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,16 @@ +jboot v3.13.1: +新增:JbootDb 新增支持 Columns 的 findFirst 方法 +新增:QuietlyUtil.java 工具类 +新增:ReflectUtil.java 的若干方法,反射调用更加简单 +优化:Redis 缓存,存入 null 时,直接对 key 进行移除 +优化:MockHttpServletRequest 直接实现 HttpServletRequest 接口,而非 HttpServletRequestWrapper 继承 +优化:@TestConfig() 注解新增 printScannerInfo 参数,默认为 false +优化:MockMvcResult.java 提高性能 +优化:优化网关代码,使代码更加清晰 +修复:移除 JbootModelConfig.idCacheCachePrefix 配置,否则会造成重复配置的情况 + + + jboot v3.13.0: 新增:Jbootmq 的监听器(Listener)新增 MessageContext 参数,支持更多的消息配置 新增:MockMvc 新增 upload 方法,用于对上传文件的测试 diff --git a/pom.xml b/pom.xml index ac90c6a2..1a5d1b58 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.1-SNAPSHOT + 3.13.1 jar jboot -- Gitee From 4d3ae940ec4e96cb54d943eff6c8c65449351fdf Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 30 Dec 2021 17:45:49 +0800 Subject: [PATCH 1527/1965] v3.13.2 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1a5d1b58..3ae54199 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.1 + 3.13.2-SNAPSHOT jar jboot -- Gitee From 7d88bcb6cb7dbb80c20841f3b4127aff5c2aaa55 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 1 Jan 2022 20:05:12 +0800 Subject: [PATCH 1528/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20javassist=20?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=90=86=E6=A8=A1=E5=BC=8F=EF=BC=8Ccglib=20?= =?UTF-8?q?=E5=9C=A8=20jdk17=20=E4=B8=8B=E6=97=A0=E6=B3=95=E6=AD=A3?= =?UTF-8?q?=E5=B8=B8=E4=BB=A3=E7=90=86=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/aop/JbootAopFactory.java | 10 ++- .../aop/cglib/JbootCglibProxyFactory.java | 2 +- .../aop/javassist/JbootJavassistHandler.java | 82 +++++++++++++++++++ .../javassist/JbootJavassistProxyFactory.java | 71 ++++++++++++++++ .../io/jboot/app/JbootApplicationConfig.java | 10 +++ src/main/java/io/jboot/test/MockApp.java | 4 + .../io/jboot/test/MockMethodInterceptor.java | 42 +++++++++- src/main/java/io/jboot/utils/ClassUtil.java | 12 ++- src/test/java/io/jboot/test/HelloWorld.java | 4 +- 9 files changed, 231 insertions(+), 6 deletions(-) create mode 100644 src/main/java/io/jboot/aop/javassist/JbootJavassistHandler.java create mode 100644 src/main/java/io/jboot/aop/javassist/JbootJavassistProxyFactory.java diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 5a160b9d..e289cbc8 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -24,6 +24,8 @@ import com.jfinal.proxy.Proxy; import com.jfinal.proxy.ProxyManager; import io.jboot.aop.annotation.*; import io.jboot.aop.cglib.JbootCglibProxyFactory; +import io.jboot.aop.javassist.JbootJavassistProxyFactory; +import io.jboot.app.JbootApplicationConfig; import io.jboot.app.config.JbootConfigKit; import io.jboot.app.config.JbootConfigManager; import io.jboot.app.config.annotation.ConfigModel; @@ -83,7 +85,13 @@ public class JbootAopFactory extends AopFactory { private JbootAopFactory() { - ProxyManager.me().setProxyFactory(new JbootCglibProxyFactory()); + + if ("javassist".equalsIgnoreCase(JbootApplicationConfig.get().getProxy())) { + ProxyManager.me().setProxyFactory(new JbootJavassistProxyFactory()); + } else { + ProxyManager.me().setProxyFactory(new JbootCglibProxyFactory()); + } + setInjectSuperClass(true); initBeanMapping(); } diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java b/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java index 48bcfa3e..45cff5e2 100644 --- a/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java @@ -49,7 +49,7 @@ public class JbootCglibProxyFactory extends ProxyFactory { public T get(Class target) { try { return (T) net.sf.cglib.proxy.Enhancer.create(target, methodInterceptor.apply(target)); - } catch (Exception e) { + } catch (Throwable e) { //原始的错误,无法给出哪个类无法被创建,错误信息不够友好 throw new RuntimeException("Can not create object for class:\"" + target.getName() + "\", cause:" + e.getMessage(), e); } diff --git a/src/main/java/io/jboot/aop/javassist/JbootJavassistHandler.java b/src/main/java/io/jboot/aop/javassist/JbootJavassistHandler.java new file mode 100644 index 00000000..e1361ec7 --- /dev/null +++ b/src/main/java/io/jboot/aop/javassist/JbootJavassistHandler.java @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop.javassist; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.InterceptorManager; +import com.jfinal.aop.Invocation; +import io.jboot.aop.InterceptorBuilderManager; +import io.jboot.aop.InterceptorCache; +import io.jboot.utils.ClassUtil; +import javassist.util.proxy.MethodHandler; + +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Set; + + +public class JbootJavassistHandler implements MethodHandler { + + private static final Set excludedMethodName = buildExcludedMethodName(); + private static final InterceptorManager interManager = InterceptorManager.me(); + private static final InterceptorBuilderManager builderManager = InterceptorBuilderManager.me(); + + + @Override + public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { + + if (excludedMethodName.contains(proceed.getName())) { + return proceed.invoke(self, args); + } + + Class targetClass = ClassUtil.getUsefulClass(self.getClass()); + + InterceptorCache.MethodKey key = InterceptorCache.getMethodKey(targetClass, proceed); + Interceptor[] inters = InterceptorCache.get(key); + if (inters == null) { + inters = interManager.buildServiceMethodInterceptor(targetClass, proceed); + inters = builderManager.build(targetClass, proceed, inters); + + InterceptorCache.put(key, inters); + } + + if (inters.length == 0) { + return proceed.invoke(self, args); + } else { + Invocation invocation = new Invocation(self, proceed, inters, + x -> proceed.invoke(self, x), args); + invocation.invoke(); + return invocation.getReturnValue(); + } + } + + + private static Set buildExcludedMethodName() { + Set excludedMethodName = new HashSet(64, 0.25F); + Method[] methods = Object.class.getDeclaredMethods(); + for (Method m : methods) { + excludedMethodName.add(m.getName()); + } + // getClass() registerNatives() can not be enhanced + // excludedMethodName.remove("getClass"); + // excludedMethodName.remove("registerNatives"); + return excludedMethodName; + } + + +} + + diff --git a/src/main/java/io/jboot/aop/javassist/JbootJavassistProxyFactory.java b/src/main/java/io/jboot/aop/javassist/JbootJavassistProxyFactory.java new file mode 100644 index 00000000..870e8c1c --- /dev/null +++ b/src/main/java/io/jboot/aop/javassist/JbootJavassistProxyFactory.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop.javassist; + +import com.jfinal.kit.LogKit; +import com.jfinal.proxy.ProxyFactory; +import javassist.util.proxy.MethodHandler; +import javassist.util.proxy.ProxyObject; + +import java.util.function.Function; + +/** + * JbootCglibProxyFactory 用于扩展 cglib 的代理模式 + * + *

        + * 配置方法:
        + * public void configConstant(Constants me) {
        + *     ProxyManager.me().setProxyFactory(new JbootJavassistProxyFactory());
        + * }
        + * 
        + */ +public class JbootJavassistProxyFactory extends ProxyFactory { + + /** + * 方便在单元测试的时候,可以对任意 class 进行 Mock + */ + private static Function, MethodHandler> methodInterceptor = aClass -> new JbootJavassistHandler(); + + public static Function, MethodHandler> getMethodInterceptor() { + return methodInterceptor; + } + + public static void setMethodInterceptor(Function, MethodHandler> methodInterceptor) { + JbootJavassistProxyFactory.methodInterceptor = methodInterceptor; + } + + @Override + public T get(Class target) { + javassist.util.proxy.ProxyFactory factory = new javassist.util.proxy.ProxyFactory(); + factory.setSuperclass(target); + final Class proxyClass = factory.createClass(); + + T proxyObject = null; + try { + proxyObject = (T) proxyClass.newInstance(); + ((ProxyObject) proxyObject).setHandler(methodInterceptor.apply(target)); + } catch (Throwable e) { + LogKit.error(e.toString(), e); + } + + return proxyObject; + } + + +} + + + diff --git a/src/main/java/io/jboot/app/JbootApplicationConfig.java b/src/main/java/io/jboot/app/JbootApplicationConfig.java index 118e6682..5cff6ab5 100644 --- a/src/main/java/io/jboot/app/JbootApplicationConfig.java +++ b/src/main/java/io/jboot/app/JbootApplicationConfig.java @@ -30,6 +30,7 @@ public class JbootApplicationConfig { private String jfinalConfig = "io.jboot.core.JbootCoreConfig"; private String listener = "*"; private boolean handle404 = true; + private String proxy = "cglib"; public String getMode() { @@ -88,6 +89,14 @@ public class JbootApplicationConfig { this.handle404 = handle404; } + public String getProxy() { + return proxy; + } + + public void setProxy(String proxy) { + this.proxy = proxy; + } + private static JbootApplicationConfig instance; public static JbootApplicationConfig get() { @@ -103,6 +112,7 @@ public class JbootApplicationConfig { " name='" + name + '\'' + ", mode='" + mode + '\'' + ", version='" + version + '\'' + + ", proxy='" + proxy + '\'' + ", config='" + jfinalConfig + '\'' + ", listener='" + listener + '\'' + " }"; diff --git a/src/main/java/io/jboot/test/MockApp.java b/src/main/java/io/jboot/test/MockApp.java index 8af8389e..88dafab7 100644 --- a/src/main/java/io/jboot/test/MockApp.java +++ b/src/main/java/io/jboot/test/MockApp.java @@ -20,6 +20,7 @@ import com.jfinal.core.JFinalFilter; import com.jfinal.kit.PathKit; import io.jboot.aop.JbootAopFactory; import io.jboot.aop.cglib.JbootCglibProxyFactory; +import io.jboot.aop.javassist.JbootJavassistProxyFactory; import io.jboot.app.PathKitExt; import io.jboot.app.config.JbootConfigManager; import io.jboot.test.web.MockFilterChain; @@ -92,7 +93,10 @@ class MockApp { boolean autoMockInterface = testConfig != null && testConfig.autoMockInterface(); + + //support cglib and javassist JbootCglibProxyFactory.setMethodInterceptor(aClass -> new MockMethodInterceptor(autoMockInterface)); + JbootJavassistProxyFactory.setMethodInterceptor(aClass -> new MockMethodInterceptor(autoMockInterface)); List mockMethodInfos = getMockMethodInfoList(testClass); if (!mockMethodInfos.isEmpty()) { diff --git a/src/main/java/io/jboot/test/MockMethodInterceptor.java b/src/main/java/io/jboot/test/MockMethodInterceptor.java index 7a2b43b0..d8c9562d 100644 --- a/src/main/java/io/jboot/test/MockMethodInterceptor.java +++ b/src/main/java/io/jboot/test/MockMethodInterceptor.java @@ -18,8 +18,10 @@ package io.jboot.test; import com.jfinal.kit.LogKit; import io.jboot.aop.InterceptorCache; import io.jboot.aop.cglib.JbootCglibCallback; +import io.jboot.aop.javassist.JbootJavassistHandler; import io.jboot.service.JbootServiceBase; import io.jboot.utils.ClassUtil; +import javassist.util.proxy.MethodHandler; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; @@ -29,7 +31,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -class MockMethodInterceptor extends JbootCglibCallback { +class MockMethodInterceptor extends JbootCglibCallback implements MethodHandler { private static final Map METHOD_INFO_CACHE = new HashMap<>(); @@ -95,4 +97,42 @@ class MockMethodInterceptor extends JbootCglibCallback { } } + + + private static final JbootJavassistHandler orginalHandler = new JbootJavassistHandler(); + + @Override + public Object invoke(Object target, Method thisMethod, Method method, Object[] args) throws Throwable { + Class targetClass = ClassUtil.getUsefulClass(target.getClass()); + + //对于接口而且没有实现类的情况,target 是一个 Object 类 + if (targetClass == Object.class && method.getDeclaringClass() != Object.class) { + targetClass = method.getDeclaringClass(); + } + + InterceptorCache.MethodKey methodKey = InterceptorCache.getMethodKey(targetClass, method); + + if (METHOD_INFO_CACHE.containsKey(methodKey)) { + MockMethodInfo methodInfo = METHOD_INFO_CACHE.get(methodKey); + return methodInfo.invokeMock(target, args); + } + + if (autoMockInterface && Modifier.isInterface(targetClass.getModifiers())) { + if (!("toString".equals(method.getName()) && args.length == 0)) { + LogKit.warn("Return null for Mock Method: \"" + ClassUtil.buildMethodString(method) + "\", " + + "Because the class \"" + targetClass.getName() + "\" is an interface and has no any implementation classes."); + } + return null; + } + + try { + return orginalHandler.invoke(target, thisMethod, method, args); + } catch (Exception ex) { + if ("initDao".equals(method.getName()) && JbootServiceBase.class == method.getDeclaringClass()) { + return null; + } else { + throw ex; + } + } + } } diff --git a/src/main/java/io/jboot/utils/ClassUtil.java b/src/main/java/io/jboot/utils/ClassUtil.java index 91c7f7e2..98f49614 100644 --- a/src/main/java/io/jboot/utils/ClassUtil.java +++ b/src/main/java/io/jboot/utils/ClassUtil.java @@ -255,11 +255,21 @@ public class ClassUtil { private static final String ENHANCER_BY = "$$EnhancerBy"; + private static final String JAVASSIST_BY = "_$$_"; public static Class getUsefulClass(Class clazz) { //ControllerTest$ServiceTest$$EnhancerByGuice$$40471411#hello //com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158 - return clazz.getName().indexOf(ENHANCER_BY) == -1 ? clazz : clazz.getSuperclass(); + //io.jboot.test.app.TestAppListener_$$_jvstb9f_0 by:javassist + + final String name = clazz.getName(); + if (name.contains(ENHANCER_BY) || name.contains(JAVASSIST_BY)){ + return clazz.getSuperclass(); + } + + return clazz; + +// return clazz.getName().indexOf(ENHANCER_BY) == -1 ? clazz : clazz.getSuperclass(); } diff --git a/src/test/java/io/jboot/test/HelloWorld.java b/src/test/java/io/jboot/test/HelloWorld.java index d9548135..c07b2925 100644 --- a/src/test/java/io/jboot/test/HelloWorld.java +++ b/src/test/java/io/jboot/test/HelloWorld.java @@ -56,8 +56,8 @@ public class HelloWorld extends JbootController { public void intercept(Invocation inv) { System.out.println("MyInterceptor.intercept"); // throw new NullPointerException(""); - inv.getController().renderText("aaa"); -// inv.invoke(); +// inv.getController().renderText("aaa"); + inv.invoke(); } } -- Gitee From e7f646d185c3cea15e6dddc72d9eb595b4b938a2 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 3 Jan 2022 16:57:46 +0800 Subject: [PATCH 1529/1965] =?UTF-8?q?fixed=20=E5=9C=A8=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=89=8D=E7=BC=80=E7=9A=84=E5=9C=BA=E6=99=AF=E4=B8=8B=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20redis=20cache=20removeAll()=20=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E6=AD=A3=E7=A1=AE=E7=A7=BB=E9=99=A4=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/components/cache/redis/JbootRedisCacheImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java index 10aeef98..342219a2 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java @@ -103,7 +103,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { int scanCount = 1000; boolean continueState = true; do { - RedisScanResult redisScanResult = redis.scan(cacheName + ":*", cursor, scanCount); + RedisScanResult redisScanResult = redis.scan(buildCacheName(cacheName) + ":*", cursor, scanCount); List scanKeys = redisScanResult.getResults(); cursor = redisScanResult.getCursor(); @@ -186,6 +186,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { @Override public List getKeys(String cacheName) { + cacheName = buildCacheName(cacheName); List keys = new ArrayList<>(); String cursor = "0"; int scanCount = 1000; -- Gitee From ce15c2e7ccb74bd257b4f4cf844c50f4527c7974 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 3 Jan 2022 16:58:10 +0800 Subject: [PATCH 1530/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20javassist=20?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=90=86=E6=A8=A1=E5=BC=8F=EF=BC=8Ccglib=20?= =?UTF-8?q?=E5=9C=A8=20jdk17=20=E4=B8=8B=E6=97=A0=E6=B3=95=E6=AD=A3?= =?UTF-8?q?=E5=B8=B8=E4=BB=A3=E7=90=86=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/aop/cglib/JbootCglibCallback.java | 4 ++-- .../aop/javassist/JbootJavassistHandler.java | 22 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java index ca1eb759..ed6f7597 100644 --- a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java @@ -20,7 +20,6 @@ import com.jfinal.aop.InterceptorManager; import com.jfinal.aop.Invocation; import io.jboot.aop.InterceptorBuilderManager; import io.jboot.aop.InterceptorCache; -import io.jboot.utils.ClassUtil; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; @@ -41,7 +40,8 @@ public class JbootCglibCallback implements MethodInterceptor { return methodProxy.invokeSuper(target, args); } - Class targetClass = ClassUtil.getUsefulClass(target.getClass()); + Class targetClass = target.getClass().getSuperclass(); + //ClassUtil.getUsefulClass(target.getClass()); InterceptorCache.MethodKey key = InterceptorCache.getMethodKey(targetClass, method); Interceptor[] inters = InterceptorCache.get(key); diff --git a/src/main/java/io/jboot/aop/javassist/JbootJavassistHandler.java b/src/main/java/io/jboot/aop/javassist/JbootJavassistHandler.java index e1361ec7..be9873ae 100644 --- a/src/main/java/io/jboot/aop/javassist/JbootJavassistHandler.java +++ b/src/main/java/io/jboot/aop/javassist/JbootJavassistHandler.java @@ -20,7 +20,6 @@ import com.jfinal.aop.InterceptorManager; import com.jfinal.aop.Invocation; import io.jboot.aop.InterceptorBuilderManager; import io.jboot.aop.InterceptorCache; -import io.jboot.utils.ClassUtil; import javassist.util.proxy.MethodHandler; import java.lang.reflect.Method; @@ -36,28 +35,29 @@ public class JbootJavassistHandler implements MethodHandler { @Override - public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { + public Object invoke(Object self, Method originalMethod, Method proxyMethod, Object[] args) throws Throwable { - if (excludedMethodName.contains(proceed.getName())) { - return proceed.invoke(self, args); + if (excludedMethodName.contains(originalMethod.getName())) { + return proxyMethod.invoke(self, args); } - Class targetClass = ClassUtil.getUsefulClass(self.getClass()); + Class targetClass = self.getClass().getSuperclass(); + //ClassUtil.getUsefulClass(self.getClass()); - InterceptorCache.MethodKey key = InterceptorCache.getMethodKey(targetClass, proceed); + InterceptorCache.MethodKey key = InterceptorCache.getMethodKey(targetClass, originalMethod); Interceptor[] inters = InterceptorCache.get(key); if (inters == null) { - inters = interManager.buildServiceMethodInterceptor(targetClass, proceed); - inters = builderManager.build(targetClass, proceed, inters); + inters = interManager.buildServiceMethodInterceptor(targetClass, originalMethod); + inters = builderManager.build(targetClass, originalMethod, inters); InterceptorCache.put(key, inters); } if (inters.length == 0) { - return proceed.invoke(self, args); + return proxyMethod.invoke(self, args); } else { - Invocation invocation = new Invocation(self, proceed, inters, - x -> proceed.invoke(self, x), args); + Invocation invocation = new Invocation(self, originalMethod, inters, + x -> proxyMethod.invoke(self, x), args); invocation.invoke(); return invocation.getReturnValue(); } -- Gitee From eccc8df21badfec4c3c6e987b632ee4b977373e4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 4 Jan 2022 00:21:05 +0800 Subject: [PATCH 1531/1965] fixed javassist lazy load --- .../java/io/jboot/aop/JbootAopFactory.java | 7 ++-- .../java/io/jboot/aop/JbootLazyLoader.java | 26 +++--------- .../io/jboot/aop/JbootLazyLoaderFactory.java | 38 +++++++++++++++++ .../jboot/aop/cglib/JbootCglibLazyLoader.java | 41 +++++++++++++++++++ .../javassist/JbootJavassistProxyFactory.java | 1 + 5 files changed, 88 insertions(+), 25 deletions(-) create mode 100644 src/main/java/io/jboot/aop/JbootLazyLoaderFactory.java create mode 100644 src/main/java/io/jboot/aop/cglib/JbootCglibLazyLoader.java diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index e289cbc8..cd8b7f31 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -41,7 +41,6 @@ import io.jboot.exception.JbootException; import io.jboot.service.JbootServiceBase; import io.jboot.utils.*; import io.jboot.web.controller.JbootController; -import net.sf.cglib.proxy.Enhancer; import javax.annotation.PostConstruct; import java.io.Serializable; @@ -186,12 +185,12 @@ public class JbootAopFactory extends AopFactory { } - protected Object createFieldObjectLazy(Object targetObject, Field field) { - return Enhancer.create(field.getType(), new JbootLazyLoader(targetObject, field)); + protected Object createFieldObjectLazy(Object targetObject, Field field) throws ReflectiveOperationException { + return JbootLazyLoaderFactory.me().getLoader().loadLazyObject(targetObject,field); } - protected Object createFieldObjectNormal(Object targetObject, Field field) throws ReflectiveOperationException { + public Object createFieldObjectNormal(Object targetObject, Field field) throws ReflectiveOperationException { Inject inject = field.getAnnotation(Inject.class); if (inject != null) { Bean bean = field.getAnnotation(Bean.class); diff --git a/src/main/java/io/jboot/aop/JbootLazyLoader.java b/src/main/java/io/jboot/aop/JbootLazyLoader.java index 2b31f0c1..f42a5ac9 100644 --- a/src/main/java/io/jboot/aop/JbootLazyLoader.java +++ b/src/main/java/io/jboot/aop/JbootLazyLoader.java @@ -1,3 +1,6 @@ +package io.jboot.aop; + +import java.lang.reflect.Field; /** * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). *

        @@ -13,26 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.jboot.aop; - -import net.sf.cglib.proxy.LazyLoader; - -import java.lang.reflect.Field; - -public class JbootLazyLoader implements LazyLoader { - - private static JbootAopFactory factory = JbootAopFactory.me(); - - private Object targetObject; - private Field field; - - public JbootLazyLoader(Object targetObject, Field field) { - this.targetObject = targetObject; - this.field = field; - } +public interface JbootLazyLoader { - @Override - public Object loadObject() throws Exception { - return factory.createFieldObjectNormal(targetObject, field); - } + Object loadLazyObject(Object targetObject, Field field) throws ReflectiveOperationException; } diff --git a/src/main/java/io/jboot/aop/JbootLazyLoaderFactory.java b/src/main/java/io/jboot/aop/JbootLazyLoaderFactory.java new file mode 100644 index 00000000..9808ccb5 --- /dev/null +++ b/src/main/java/io/jboot/aop/JbootLazyLoaderFactory.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop; + +import io.jboot.aop.cglib.JbootCglibLazyLoader; +import io.jboot.app.JbootApplicationConfig; +import net.sf.cglib.proxy.Enhancer; + +public class JbootLazyLoaderFactory { + + private static final JbootLazyLoaderFactory me = new JbootLazyLoaderFactory(); + + public static JbootLazyLoaderFactory me() { + return me; + } + + public JbootLazyLoader getLoader() { + if ("javassist".equalsIgnoreCase(JbootApplicationConfig.get().getProxy())) { + //javassist 直接返回 createFieldObjectNormal 的内容,而非 lazy Object + return (targetObject, field) -> JbootAopFactory.me().createFieldObjectNormal(targetObject, field); + } else { + return (targetObject, field) -> Enhancer.create(field.getType(), new JbootCglibLazyLoader(targetObject, field)); + } + } +} diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibLazyLoader.java b/src/main/java/io/jboot/aop/cglib/JbootCglibLazyLoader.java new file mode 100644 index 00000000..af8b2166 --- /dev/null +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibLazyLoader.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.aop.cglib; + +import io.jboot.aop.JbootAopFactory; +import net.sf.cglib.proxy.LazyLoader; + +import java.lang.reflect.Field; + +public class JbootCglibLazyLoader implements LazyLoader { + + private static JbootAopFactory factory = JbootAopFactory.me(); + + private Object targetObject; + private Field field; + + public JbootCglibLazyLoader(Object targetObject, Field field) { + this.targetObject = targetObject; + this.field = field; + } + + @Override + public Object loadObject() throws Exception { + return factory.createFieldObjectNormal(targetObject, field); + } + + +} diff --git a/src/main/java/io/jboot/aop/javassist/JbootJavassistProxyFactory.java b/src/main/java/io/jboot/aop/javassist/JbootJavassistProxyFactory.java index 870e8c1c..752e2050 100644 --- a/src/main/java/io/jboot/aop/javassist/JbootJavassistProxyFactory.java +++ b/src/main/java/io/jboot/aop/javassist/JbootJavassistProxyFactory.java @@ -53,6 +53,7 @@ public class JbootJavassistProxyFactory extends ProxyFactory { factory.setSuperclass(target); final Class proxyClass = factory.createClass(); + T proxyObject = null; try { proxyObject = (T) proxyClass.newInstance(); -- Gitee From c9a7282f90e8d2d97c92ad83edccb5474f89ead5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 4 Jan 2022 00:22:42 +0800 Subject: [PATCH 1532/1965] fixed fastsjon serializer --- .../serializer/FastJsonSerializer.java | 54 ++------ .../components/serializer/FstSerializer.java | 2 +- .../serializer/JacksonSerializer.java | 118 ++++++++++++++++++ .../serializer/JbootSerializerConfig.java | 1 + .../serializer/JbootSerializerManager.java | 2 + 5 files changed, 134 insertions(+), 43 deletions(-) create mode 100644 src/main/java/io/jboot/components/serializer/JacksonSerializer.java diff --git a/src/main/java/io/jboot/components/serializer/FastJsonSerializer.java b/src/main/java/io/jboot/components/serializer/FastJsonSerializer.java index 6fa9d08b..bd5b7267 100644 --- a/src/main/java/io/jboot/components/serializer/FastJsonSerializer.java +++ b/src/main/java/io/jboot/components/serializer/FastJsonSerializer.java @@ -16,11 +16,10 @@ package io.jboot.components.serializer; import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.parser.Feature; +import com.alibaba.fastjson.serializer.SerializerFeature; import com.jfinal.log.Log; -import java.io.Serializable; - public class FastJsonSerializer implements JbootSerializer { @@ -31,9 +30,13 @@ public class FastJsonSerializer implements JbootSerializer { if (obj == null) { return null; } - FastJsonCacheObject object = new FastJsonCacheObject(obj.getClass(), obj); - String string = JSON.toJSONString(object); - return string.getBytes(); + + return JSON.toJSONBytes(obj + , SerializerFeature.WriteClassName + , SerializerFeature.SkipTransientField + , SerializerFeature.IgnoreErrorGetter +// , SerializerFeature.IgnoreNonFieldGetter + ); } @Override @@ -41,47 +44,14 @@ public class FastJsonSerializer implements JbootSerializer { if (bytes == null || bytes.length == 0) { return null; } - String json = new String(bytes); - JSONObject jsonObject = null; - Class clazz = null; + try { - jsonObject = JSON.parseObject(json); - clazz = Class.forName(jsonObject.getString("clazz")); + return JSON.parse(bytes, Feature.SupportAutoType); } catch (Exception e) { LOG.error(e.toString(), e); - return null; - } - return jsonObject.getObject("object", clazz); - } - - - public static class FastJsonCacheObject implements Serializable { - private Class clazz; - private Object object; - - public FastJsonCacheObject() { } - public FastJsonCacheObject(Class clazz, Object object) { - this.clazz = clazz; - this.object = object; - } - - public Class getClazz() { - return clazz; - } - - public void setClazz(Class clazz) { - this.clazz = clazz; - } - - public Object getObject() { - return object; - } - - public void setObject(Object object) { - this.object = object; - } + return null; } diff --git a/src/main/java/io/jboot/components/serializer/FstSerializer.java b/src/main/java/io/jboot/components/serializer/FstSerializer.java index e799f499..1fc306c5 100644 --- a/src/main/java/io/jboot/components/serializer/FstSerializer.java +++ b/src/main/java/io/jboot/components/serializer/FstSerializer.java @@ -41,7 +41,7 @@ public class FstSerializer implements JbootSerializer { try { return fst.asObject(bytes); } catch (Exception ex) { - LOG.error("FstSerializer deserialize error!"); + LOG.error("FstSerializer deserialize error!", ex); } return null; } diff --git a/src/main/java/io/jboot/components/serializer/JacksonSerializer.java b/src/main/java/io/jboot/components/serializer/JacksonSerializer.java new file mode 100644 index 00000000..3d07dd1d --- /dev/null +++ b/src/main/java/io/jboot/components/serializer/JacksonSerializer.java @@ -0,0 +1,118 @@ +///** +// * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). +// *

        +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// *

        +// * http://www.apache.org/licenses/LICENSE-2.0 +// *

        +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +//package io.jboot.components.serializer; +// +//import com.fasterxml.jackson.annotation.JsonInclude; +//import com.fasterxml.jackson.core.JsonProcessingException; +//import com.fasterxml.jackson.databind.DeserializationFeature; +//import com.fasterxml.jackson.databind.JavaType; +//import com.fasterxml.jackson.databind.ObjectMapper; +//import com.fasterxml.jackson.databind.type.TypeFactory; +//import com.jfinal.log.Log; +// +//import java.io.Serializable; +//import java.lang.reflect.ParameterizedType; +//import java.lang.reflect.Type; +//import java.util.Collection; +// +// +//public class JacksonSerializer implements JbootSerializer { +// +// private static final Log LOG = Log.getLog(JacksonSerializer.class); +// +// public final static ObjectMapper MAPPER; +// +// static { +// MAPPER = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL); +// MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); +// } +// +// +// @Override +// public byte[] serialize(Object obj) { +// if (obj == null) { +// return null; +// } +// final TypeFactory typeFactory = MAPPER.getTypeFactory(); +// +// try { +// JavaType type =typeFactory.constructType(obj.getClass()); +// if (type.isContainerType()) { +// type = typeFactory.constructCollectionType((Class) type.getRawClass(),getActualType(obj,0)); +// } +// +// return MAPPER.writeValueAsBytes(new CachedObject(type, MAPPER.writeValueAsString(obj))); +// } catch (JsonProcessingException e) { +// LOG.error(e.toString(), e); +// } +// return null; +// } +// +// public static Class getActualType(Object o, int index) { +// Type clazz = o.getClass().getGenericSuperclass(); +// ParameterizedType pt = (ParameterizedType) clazz; +// return (Class) pt.getActualTypeArguments()[index]; +// } +// +// @Override +// public Object deserialize(byte[] bytes) { +// if (bytes == null || bytes.length == 0) { +// return null; +// } +// try { +// CachedObject cachedObject = MAPPER.readValue(bytes, CachedObject.class); +// return MAPPER.readValue(cachedObject.getData(), cachedObject.getType()); +// } catch (Exception e) { +// LOG.error(e.toString(), e); +// } +// +// return null; +// } +// +// +// public static class CachedObject implements Serializable { +// private JavaType type; +// private String data; +// +// public CachedObject() { +// } +// +// public CachedObject(JavaType type, String data) { +// this.type = type; +// this.data = data; +// } +// +// public JavaType getType() { +// return type; +// } +// +// public void setType(JavaType type) { +// this.type = type; +// } +// +// public String getData() { +// return data; +// } +// +// public void setData(String data) { +// this.data = data; +// } +// } +// +// +// +// +//} diff --git a/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java b/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java index 65ad077d..ad1ea75b 100644 --- a/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java +++ b/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java @@ -23,6 +23,7 @@ public class JbootSerializerConfig { public static final String FST = "fst"; public static final String FASTJSON = "fastjson"; + public static final String JACKSON = "jackson"; public static final String KRYO = "kryo"; public String type = FST; diff --git a/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java b/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java index 0e2ea564..19a4e98a 100644 --- a/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java +++ b/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java @@ -84,6 +84,8 @@ public class JbootSerializerManager { return new FstSerializer(); case JbootSerializerConfig.FASTJSON: return new FastJsonSerializer(); +// case JbootSerializerConfig.JACKSON: +// return new JacksonSerializer(); default: return JbootSpiLoader.load(JbootSerializer.class, serializerName); } -- Gitee From dc8dca42d75548ce698dbca6692ca7e6f1fa8a0b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 4 Jan 2022 00:23:08 +0800 Subject: [PATCH 1533/1965] fixed SharedEnumObject in jdk17 --- .../jboot/web/directive/SharedEnumObject.java | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/web/directive/SharedEnumObject.java b/src/main/java/io/jboot/web/directive/SharedEnumObject.java index b44e9dc2..ff79a25a 100644 --- a/src/main/java/io/jboot/web/directive/SharedEnumObject.java +++ b/src/main/java/io/jboot/web/directive/SharedEnumObject.java @@ -17,7 +17,10 @@ package io.jboot.web.directive; import com.jfinal.kit.LogKit; import com.jfinal.template.expr.ast.MethodKeyBuilder; -import javassist.*; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import javassist.NotFoundException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -148,7 +151,7 @@ public class SharedEnumObject extends LinkedHashMap { CtClass objectCtClass = pool.getCtClass(Object.class.getName()); CtClass supperClass = pool.get(SharedEnumObject.class.getName()); - CtClass newClass = pool.makeClass(SharedEnumObject.class.getName() + "." + enumClass.getSimpleName()); + CtClass newClass = pool.makeClass(SharedEnumObject.class.getName() + "_$$_" + enumClass.getSimpleName()); newClass.setSuperclass(supperClass); newClass.setModifiers(Modifier.PUBLIC); @@ -173,7 +176,20 @@ public class SharedEnumObject extends LinkedHashMap { } } - SharedEnumObject ret = (SharedEnumObject) newClass.toClass().newInstance(); + SharedEnumObject ret; + String javaVersion = System.getProperty("java.version"); + if (javaVersion != null && javaVersion.startsWith("1.")) { + //jdk 1.x + ret = (SharedEnumObject) newClass.toClass().newInstance(); + } else if (javaVersion != null && javaVersion.startsWith("17")) { + //jdk 17 + // toClass() must add neighbor class in jdk17 + // neighbor: A class belonging to the same package that this class belongs to + ret = (SharedEnumObject) newClass.toClass(SharedEnumObject.class).newInstance(); + } else { + //other + ret = (SharedEnumObject) newClass.toClass().newInstance(); + } ret.init(enumClass, enumStaticMethods); return ret; } catch (Exception e) { -- Gitee From ea09d4e07feaef3cbd8afbf41c54629c6383f3ca Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 4 Jan 2022 00:24:27 +0800 Subject: [PATCH 1534/1965] fixed ... --- pom.xml | 1 + src/main/java/io/jboot/app/JbootApplicationConfig.java | 2 +- src/main/java/io/jboot/utils/TypeDef.java | 8 ++++---- src/test/java/io/jboot/test/aop/LazyController.java | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 3ae54199..fd2b1434 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,7 @@ 5.1.49 2.2.14.Final 1.7.32 + 2.57 1.2.79 31.0.1-jre diff --git a/src/main/java/io/jboot/app/JbootApplicationConfig.java b/src/main/java/io/jboot/app/JbootApplicationConfig.java index 5cff6ab5..004c41fe 100644 --- a/src/main/java/io/jboot/app/JbootApplicationConfig.java +++ b/src/main/java/io/jboot/app/JbootApplicationConfig.java @@ -30,7 +30,7 @@ public class JbootApplicationConfig { private String jfinalConfig = "io.jboot.core.JbootCoreConfig"; private String listener = "*"; private boolean handle404 = true; - private String proxy = "cglib"; + private String proxy = "cglib"; //cglib or javassist public String getMode() { diff --git a/src/main/java/io/jboot/utils/TypeDef.java b/src/main/java/io/jboot/utils/TypeDef.java index 8d470874..1efe6893 100644 --- a/src/main/java/io/jboot/utils/TypeDef.java +++ b/src/main/java/io/jboot/utils/TypeDef.java @@ -21,7 +21,7 @@ public class TypeDef { protected Type type; - protected Class defClass; + protected Class defClass; protected TypeDef() { Type superClass = getClass().getGenericSuperclass(); @@ -33,10 +33,10 @@ public class TypeDef { if (type instanceof ParameterizedType) { ParameterizedType paraType = (ParameterizedType) type; this.type = paraType; - this.defClass = (Class) paraType.getRawType(); + this.defClass = (Class) paraType.getRawType(); } else { this.type = type; - this.defClass = (Class) type; + this.defClass = (Class) type; } } @@ -46,7 +46,7 @@ public class TypeDef { } - public Class getDefClass() { + public Class getDefClass() { return defClass; } diff --git a/src/test/java/io/jboot/test/aop/LazyController.java b/src/test/java/io/jboot/test/aop/LazyController.java index 3f329525..f79675be 100644 --- a/src/test/java/io/jboot/test/aop/LazyController.java +++ b/src/test/java/io/jboot/test/aop/LazyController.java @@ -18,7 +18,7 @@ public class LazyController extends Controller { public void index1() { System.out.println(""); // System.out.println("userService ---->" + userService); -// System.out.println("bService ---->" + bService); + System.out.println("bService ---->" + bService); renderText("index1"); } -- Gitee From d0d4ed4be2a00ab8c984acaaec593994d373aeb5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 4 Jan 2022 10:59:26 +0800 Subject: [PATCH 1535/1965] v3.13.2 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9dbbc92d..33fbf7ba 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.13.1 + 3.13.2 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index c057252e..5a28898a 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.13.1 + 3.13.2 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index e5cf7cfa..03d07754 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.13.1 + 3.13.2 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index a69c66d8..a3a9f867 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.13.1 + 3.13.2 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.13.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.13.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 2a04ecb7..8145ffbd 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.13.1', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.13.2', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.1/jboot-3.13.1.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.2/jboot-3.13.2.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 8d10d890..35ebe7dd 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.13.1"; + public static String VERSION = "3.13.2"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 504996606a8ab8b6cd6efe7a198b75fc3439055b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 4 Jan 2022 10:59:30 +0800 Subject: [PATCH 1536/1965] v3.13.2 release (^.^)YYa!! --- changes.txt | 8 ++++++++ pom.xml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index f0ffa775..4078bb2b 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,11 @@ +jboot v3.13.2: +新增:AOP 新增 javassist 的实现,方便在 jdk17 去掉 cglib +修复:在配置前缀的场景下,修复 redis 缓存 removeAll() 无法正确移除数据的问题 +修复:SharedEnumObject 在 jdk17 下无法正常运行的问题 +修复:Fastjson 对某些数据无法正确序列化的问题,升级 fastjson 到最新版本 + + + jboot v3.13.1: 新增:JbootDb 新增支持 Columns 的 findFirst 方法 新增:QuietlyUtil.java 工具类 diff --git a/pom.xml b/pom.xml index fd2b1434..a647eb1d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.2-SNAPSHOT + 3.13.2 jar jboot -- Gitee From 589abe1b22935079ef19ff348dec58bd6e5428da Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 12 Jan 2022 12:21:59 +0800 Subject: [PATCH 1537/1965] v3.13.3 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a647eb1d..2613b87e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.2 + 3.13.3-SNAPSHOT jar jboot -- Gitee From 78b9b8dfc6619d72d361b8700f5509a750ed9d9f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 12 Jan 2022 12:22:38 +0800 Subject: [PATCH 1538/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E9=80=9A=E8=BF=87=E5=90=AF=E5=8A=A8=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=90=8D=E7=A7=B0=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/app/config/JbootConfigKit.java | 4 ++-- .../jboot/app/config/JbootConfigManager.java | 19 ++++++++++++------- .../java/io/jboot/app/config/JbootProp.java | 15 ++------------- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigKit.java b/src/main/java/io/jboot/app/config/JbootConfigKit.java index 409903a6..0a5d80ba 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigKit.java +++ b/src/main/java/io/jboot/app/config/JbootConfigKit.java @@ -158,10 +158,10 @@ public class JbootConfigKit { } - static Properties readExternalProperties() { + static Properties readExternalProperties(String fileName) { String currentJarFilePath = JbootConfigKit.class.getProtectionDomain().getCodeSource().getLocation().getFile(); File fileDir = new File(currentJarFilePath).getParentFile(); - File externalProperties = new File(fileDir, "jboot.properties"); + File externalProperties = new File(fileDir, fileName+".properties"); if (externalProperties.exists()) { return new JbootProp(externalProperties).getProperties(); } diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 66b126e8..0f6de3fe 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -65,17 +65,20 @@ public class JbootConfigManager { private void init() { + String fileName = getOriginalConfigValue(null, "jboot_properties_name"); + if (fileName == null || fileName.length() == 0){ + fileName = "jboot"; + } - mainProperties = new JbootProp("jboot.properties").getProperties(); + mainProperties = new JbootProp(fileName + ".properties").getProperties(); String mode = getConfigValue("jboot.app.mode"); - if (JbootConfigKit.isNotBlank(mode)) { - String modePropertiesName = "jboot-" + mode + ".properties"; + String modePropertiesName = fileName + "-" + mode + ".properties"; mainProperties.putAll(new JbootProp(modePropertiesName).getProperties()); } - Properties externalProperties = JbootConfigKit.readExternalProperties(); + Properties externalProperties = JbootConfigKit.readExternalProperties(fileName); if (externalProperties != null && !externalProperties.isEmpty()) { mainProperties.putAll(externalProperties); } @@ -288,9 +291,11 @@ public class JbootConfigManager { } //user properties - value = (String) properties.get(key); - if (JbootConfigKit.isNotBlank(value)) { - return value.trim(); + if (properties != null) { + value = (String) properties.get(key); + if (JbootConfigKit.isNotBlank(value)) { + return value.trim(); + } } return null; diff --git a/src/main/java/io/jboot/app/config/JbootProp.java b/src/main/java/io/jboot/app/config/JbootProp.java index 5d5b9b1b..6fd23b7a 100644 --- a/src/main/java/io/jboot/app/config/JbootProp.java +++ b/src/main/java/io/jboot/app/config/JbootProp.java @@ -50,21 +50,10 @@ class JbootProp { public JbootProp(File file) { properties = new Properties(); - InputStream inputStream = null; - try { - inputStream = new FileInputStream(file); - if (inputStream != null) { - properties.load(new InputStreamReader(inputStream, DEFAULT_ENCODING)); - } + try (InputStream inputStream = new FileInputStream(file)) { + properties.load(new InputStreamReader(inputStream, DEFAULT_ENCODING)); } catch (Exception e) { System.err.println("Warning: Can not load properties file: " + file); - } finally { - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException e) { - } - } } } -- Gitee From c85a1da8d14dde0dda031cddc42efc9b43592097 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 20 Jan 2022 12:02:25 +0800 Subject: [PATCH 1539/1965] optimize --- src/main/java/io/jboot/db/driver/DriverClassNames.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/driver/DriverClassNames.java b/src/main/java/io/jboot/db/driver/DriverClassNames.java index ad2f2247..a8ab12b2 100644 --- a/src/main/java/io/jboot/db/driver/DriverClassNames.java +++ b/src/main/java/io/jboot/db/driver/DriverClassNames.java @@ -27,7 +27,7 @@ public class DriverClassNames { private static final Map driverClassNames = new HashMap<>(); static { - driverClassNames.put(DataSourceConfig.TYPE_MYSQL, new String[]{"com.mysql.jdbc.Driver", "com.mysql.cj.jdbc.Driver"}); + driverClassNames.put(DataSourceConfig.TYPE_MYSQL, new String[]{"com.mysql.cj.jdbc.Driver", "com.mysql.jdbc.Driver"}); driverClassNames.put(DataSourceConfig.TYPE_ORACLE, new String[]{"oracle.jdbc.driver.OracleDriver", "oracle.jdbc.OracleDriver"}); driverClassNames.put(DataSourceConfig.TYPE_SQLSERVER, new String[]{"com.microsoft.sqlserver.jdbc.SQLServerDriver"}); driverClassNames.put(DataSourceConfig.TYPE_SQLITE, new String[]{"org.sqlite.JDBC"}); -- Gitee From 8a750fd94d5fbb0255042fceaeaec5f4cddb281e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 20 Jan 2022 17:16:22 +0800 Subject: [PATCH 1540/1965] optimize --- .../io/jboot/components/mq/JbootmqBase.java | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index bcd63506..445e3570 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -15,9 +15,6 @@ */ package io.jboot.components.mq; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; import com.jfinal.kit.LogKit; import com.jfinal.log.Log; import io.jboot.Jboot; @@ -27,9 +24,7 @@ import io.jboot.exception.JbootException; import io.jboot.utils.NamedThreadFactory; import io.jboot.utils.StrUtil; -import java.util.Collection; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.concurrent.*; @@ -38,13 +33,13 @@ public abstract class JbootmqBase implements Jbootmq { private static final Log LOG = Log.getLog(JbootmqBase.class); private List globalListeners = new CopyOnWriteArrayList<>(); - private Multimap channelListeners = ArrayListMultimap.create(); - protected final JbootmqConfig config; + private Map> channelListeners = new ConcurrentHashMap<>(); - protected Set channels = Sets.newHashSet(); - protected Set syncRecevieMessageChannels = Sets.newHashSet(); + protected Set channels = new HashSet<>(); + protected Set syncRecevieMessageChannels = new HashSet<>(); protected JbootSerializer serializer; + protected final JbootmqConfig config; private final ExecutorService threadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, @@ -75,24 +70,35 @@ public abstract class JbootmqBase implements Jbootmq { public void addMessageListener(JbootmqMessageListener listener, String forChannel) { String[] forChannels = forChannel.split(","); for (String channel : forChannels) { - if (StrUtil.isBlank(channel)) { - continue; - } - channelListeners.put(channel.trim(), listener); + addChannelListener(channel, listener); + } + } + + private synchronized void addChannelListener(String channel, JbootmqMessageListener listener) { + if (StrUtil.isNotBlank(channel)) { + channel = channel.trim(); + List listeners = channelListeners.getOrDefault(channel, new CopyOnWriteArrayList<>()); + listeners.add(listener); + + channelListeners.put(channel, listeners); } } @Override public void removeListener(JbootmqMessageListener listener) { globalListeners.remove(listener); - for (String channel : channelListeners.keySet()) { - channelListeners.remove(channel, listener); + for (List listeners : channelListeners.values()) { + listeners.remove(listener); } } @Override public void removeAllListeners() { globalListeners.clear(); + + for (List listeners : channelListeners.values()) { + listeners.clear(); + } channelListeners.clear(); } -- Gitee From 31229b1a0112df626bfd70afcbf91702d314e056 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 20 Jan 2022 17:23:53 +0800 Subject: [PATCH 1541/1965] optimize --- .../java/io/jboot/web/directive/SharedEnumObject.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/web/directive/SharedEnumObject.java b/src/main/java/io/jboot/web/directive/SharedEnumObject.java index ff79a25a..a235e6c2 100644 --- a/src/main/java/io/jboot/web/directive/SharedEnumObject.java +++ b/src/main/java/io/jboot/web/directive/SharedEnumObject.java @@ -179,16 +179,13 @@ public class SharedEnumObject extends LinkedHashMap { SharedEnumObject ret; String javaVersion = System.getProperty("java.version"); if (javaVersion != null && javaVersion.startsWith("1.")) { - //jdk 1.x + // jdk 1.x ret = (SharedEnumObject) newClass.toClass().newInstance(); - } else if (javaVersion != null && javaVersion.startsWith("17")) { - //jdk 17 + } else { + // jdk 17 // toClass() must add neighbor class in jdk17 // neighbor: A class belonging to the same package that this class belongs to ret = (SharedEnumObject) newClass.toClass(SharedEnumObject.class).newInstance(); - } else { - //other - ret = (SharedEnumObject) newClass.toClass().newInstance(); } ret.init(enumClass, enumStaticMethods); return ret; -- Gitee From 935e56944a4c516e0b3a686b7c8a17478d0606b7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 23 Jan 2022 17:23:16 +0800 Subject: [PATCH 1542/1965] optimize --- .../io/jboot/components/mq/JbootmqBase.java | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index 445e3570..c79421ae 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -19,7 +19,6 @@ import com.jfinal.kit.LogKit; import com.jfinal.log.Log; import io.jboot.Jboot; import io.jboot.components.serializer.JbootSerializer; -import io.jboot.components.serializer.JbootSerializerManager; import io.jboot.exception.JbootException; import io.jboot.utils.NamedThreadFactory; import io.jboot.utils.StrUtil; @@ -66,24 +65,27 @@ public abstract class JbootmqBase implements Jbootmq { globalListeners.add(listener); } + @Override public void addMessageListener(JbootmqMessageListener listener, String forChannel) { String[] forChannels = forChannel.split(","); for (String channel : forChannels) { - addChannelListener(channel, listener); + if (StrUtil.isNotBlank(channel)) { + addChannelListener(channel.trim(), listener); + } } } private synchronized void addChannelListener(String channel, JbootmqMessageListener listener) { - if (StrUtil.isNotBlank(channel)) { - channel = channel.trim(); - List listeners = channelListeners.getOrDefault(channel, new CopyOnWriteArrayList<>()); - listeners.add(listener); - + List listeners = channelListeners.get(channel); + if (listeners == null) { + listeners = new CopyOnWriteArrayList<>(); channelListeners.put(channel, listeners); } + listeners.add(listener); } + @Override public void removeListener(JbootmqMessageListener listener) { globalListeners.remove(listener); @@ -95,10 +97,7 @@ public abstract class JbootmqBase implements Jbootmq { @Override public void removeAllListeners() { globalListeners.clear(); - - for (List listeners : channelListeners.values()) { - listeners.clear(); - } + channelListeners.forEach((s, list) -> list.clear()); channelListeners.clear(); } @@ -120,7 +119,7 @@ public abstract class JbootmqBase implements Jbootmq { boolean channelResult = notifyListeners(channel, message, context, channelListeners.get(channel)); if (!globalResult && !channelResult) { - LOG.error("Application has recevied mq message, But has no listener to process it. channel:" + + LOG.warn("Jboot has recevied mq message, But it has no listener to process. channel:" + channel + " message:" + message); } } @@ -154,11 +153,9 @@ public abstract class JbootmqBase implements Jbootmq { public JbootSerializer getSerializer() { if (serializer == null) { - if (StrUtil.isBlank(config.getSerializer())) { - serializer = Jboot.getSerializer(); - } else { - serializer = JbootSerializerManager.me().getSerializer(config.getSerializer()); - } + serializer = StrUtil.isNotBlank(config.getSerializer()) + ? Jboot.getSerializer(config.getSerializer()) + : Jboot.getSerializer(); } return serializer; } @@ -169,18 +166,18 @@ public abstract class JbootmqBase implements Jbootmq { @Override public boolean startListening() { if (isStarted) { - throw new JbootException("jboot mq is started before."); + throw new JbootException("Jboot MQ has started."); } if (channels == null || channels.isEmpty()) { - throw new JbootException("mq channels is null or empty, please config channels"); + throw new JbootException("Jboot MQ's channels is null or empty, please config channels"); } try { isStarted = true; onStartListening(); } catch (Exception ex) { - LogKit.error("start mq fail!"); + LogKit.error("Jboot MQ start fail!"); isStarted = false; return false; } -- Gitee From 8b74add61cbef7f438892ba21af21b9d67ba5604 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 23 Jan 2022 17:37:23 +0800 Subject: [PATCH 1543/1965] =?UTF-8?q?add:=20=E6=B7=BB=E5=8A=A0=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=E5=8F=AF=E4=BB=A5=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E8=87=AA=E5=B7=B1=E7=9A=84=E6=96=87=E4=BB=B6=E5=90=8D=E5=92=8C?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/app/config/JbootConfigManager.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 0f6de3fe..fb28b66c 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -65,12 +65,19 @@ public class JbootConfigManager { private void init() { - String fileName = getOriginalConfigValue(null, "jboot_properties_name"); - if (fileName == null || fileName.length() == 0){ + String fileName = getConfigValue(null, "jboot_properties_name"); + if (fileName == null || fileName.length() == 0) { fileName = "jboot"; } - mainProperties = new JbootProp(fileName + ".properties").getProperties(); + String pathName = getConfigValue(null, "jboot_properties_path"); + if (pathName == null || pathName.trim().length() == 0) { + mainProperties = new JbootProp(fileName + ".properties").getProperties(); + } + //指定路径的场景 + else { + mainProperties = new JbootProp(new File(pathName, fileName + ".properties")).getProperties(); + } String mode = getConfigValue("jboot.app.mode"); if (JbootConfigKit.isNotBlank(mode)) { -- Gitee From cc4e2d85f216aba2e71b89256cea91179500e92c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 23 Jan 2022 17:51:46 +0800 Subject: [PATCH 1544/1965] =?UTF-8?q?add:=20=E6=B7=BB=E5=8A=A0=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=E5=8F=AF=E4=BB=A5=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E8=87=AA=E5=B7=B1=E7=9A=84=E6=96=87=E4=BB=B6=E5=90=8D=E5=92=8C?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/app/config/JbootConfigManager.java | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index fb28b66c..28d710d0 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -71,29 +71,40 @@ public class JbootConfigManager { } String pathName = getConfigValue(null, "jboot_properties_path"); - if (pathName == null || pathName.trim().length() == 0) { - mainProperties = new JbootProp(fileName + ".properties").getProperties(); - } - //指定路径的场景 - else { - mainProperties = new JbootProp(new File(pathName, fileName + ".properties")).getProperties(); - } + mainProperties = getProperties(pathName, fileName + ".properties"); + String mode = getConfigValue("jboot.app.mode"); if (JbootConfigKit.isNotBlank(mode)) { + //开始加载 mode properties + //并全部添加覆盖掉掉 main properties + String modePropertiesName = fileName + "-" + mode + ".properties"; - mainProperties.putAll(new JbootProp(modePropertiesName).getProperties()); + Properties modeProperties = getProperties(pathName, modePropertiesName); + + mainProperties.putAll(modeProperties); } + Properties externalProperties = JbootConfigKit.readExternalProperties(fileName); if (externalProperties != null && !externalProperties.isEmpty()) { mainProperties.putAll(externalProperties); } + NacosConfigManager.me().init(this); ApolloConfigManager.me().init(this); } + + private Properties getProperties(String pathName, String name) { + if (pathName != null && pathName.trim().length() > 0) { + return new JbootProp(new File(pathName, name)).getProperties(); + } else { + return new JbootProp(name).getProperties(); + } + } + public JbootConfigDecryptor getDecryptor() { return decryptor; } -- Gitee From 5d97e99be19c3623a2d452caaa602cbd9f128c6a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 26 Jan 2022 10:58:29 +0800 Subject: [PATCH 1545/1965] v3.13.3 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- src/main/java/io/jboot/components/mq/JbootmqBase.java | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 33fbf7ba..c6f1225b 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.13.2 + 3.13.3 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 5a28898a..1af35223 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.13.2 + 3.13.3 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 03d07754..e6a5f6ea 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.13.2 + 3.13.3 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index a3a9f867..95a93234 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.13.2 + 3.13.3 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.13.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.13.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 8145ffbd..ceca8a3f 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.13.2', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.13.3', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.2/jboot-3.13.2.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.3/jboot-3.13.3.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 35ebe7dd..e2fa7211 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.13.2"; + public static String VERSION = "3.13.3"; public static final String ATTR_REQUEST = "REQUEST"; diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index c79421ae..ba0e197e 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -170,14 +170,14 @@ public abstract class JbootmqBase implements Jbootmq { } if (channels == null || channels.isEmpty()) { - throw new JbootException("Jboot MQ's channels is null or empty, please config channels"); + throw new JbootException("Jboot MQ's channels is null or empty, Please config channels."); } try { isStarted = true; onStartListening(); } catch (Exception ex) { - LogKit.error("Jboot MQ start fail!"); + LogKit.error("Jboot MQ start fail!", ex); isStarted = false; return false; } -- Gitee From 6dde7ab79cf423da436df756ab34ecba04c43e52 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 26 Jan 2022 10:58:59 +0800 Subject: [PATCH 1546/1965] v3.13.3 release (^.^)YYa!! --- changes.txt | 8 ++++++++ pom.xml | 12 ++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/changes.txt b/changes.txt index 4078bb2b..e57315c5 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,11 @@ +jboot v3.13.3: +新增:配置文件可以指定自己的文件名和路径的支持 +优化:JbootmqBase,使之代码更加简洁 +优化:Mysql 驱动,默认优先使用 MySql8 驱动 +优化:升级相关依赖到最新版本 + + + jboot v3.13.2: 新增:AOP 新增 javassist 的实现,方便在 jdk17 去掉 cglib 修复:在配置前缀的场景下,修复 redis 缓存 removeAll() 无法正确移除数据的问题 diff --git a/pom.xml b/pom.xml index 2613b87e..0a69d583 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.3-SNAPSHOT + 3.13.3 jar jboot @@ -50,7 +50,7 @@ 2.13.1 5.1.49 2.2.14.Final - 1.7.32 + 1.7.33 2.57 1.2.79 @@ -59,15 +59,15 @@ 3.7.1 1.14.3 2.10.9.2 - 2.9.1 + 2.9.3 3.12.0 2.11.0 1.15 4.5.12 4.4.13 4.1.52.Final - 1.8.2 - 2.7.13 + 1.8.3 + 2.7.15 3.13.2.Final 1.4.2 1.1.8 @@ -381,7 +381,7 @@ com.alibaba.nacos nacos-client - 2.0.3 + 2.0.4 provided -- Gitee From 4f3f663a0267f17e1ff2f634a7ad213a03daa00c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 6 Feb 2022 17:12:46 +0800 Subject: [PATCH 1547/1965] v3.13.4 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 +- doc/jbootadmin/start.md | 4 +- src/main/java/io/jboot/JbootConsts.java | 2 +- .../web/attachment/AttachmentManager.java | 38 ++++++++++++++----- 6 files changed, 35 insertions(+), 17 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 1af35223..8c42ab25 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.13.3 + 3.13.4 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index e6a5f6ea..6b3251b9 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.13.3 + 3.13.4 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 95a93234..21ae055f 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.13.3 + 3.13.4 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.13.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.13.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index ceca8a3f..a7df61a2 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.13.3', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.13.4', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.3/jboot-3.13.3.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.4/jboot-3.13.4.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index e2fa7211..515ed391 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.13.3"; + public static String VERSION = "3.13.4"; public static final String ATTR_REQUEST = "REQUEST"; diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index 67c41542..b327731b 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -49,6 +49,7 @@ public class AttachmentManager { AttachmentManager manager = managers.get(name); if (manager == null) { synchronized (AttachmentManager.class) { + manager = managers.get(name); if (manager == null) { manager = new AttachmentManager(name); managers.put(name, manager); @@ -213,26 +214,43 @@ public class AttachmentManager { * @return */ public File getFile(String relativePath) { + //优先从 默认的 container 去获取 + return getFile(relativePath, true); + } + + + /** + * 通过相对路径获取文件 + * + * @param relativePath + * @param localFirst + * @return + */ + public File getFile(String relativePath, boolean localFirst) { //优先从 默认的 container 去获取 - File file = defaultContainer.getFile(relativePath); - if (file.exists()) { - return file; + if (localFirst) { + File localFile = defaultContainer.getFile(relativePath); + if (localFile.exists()) { + return localFile; + } } for (AttachmentContainer container : containers) { - try { - file = container.getFile(relativePath); - if (file != null && file.exists()) { - return file; + if (container != defaultContainer) { + try { + File file = container.getFile(relativePath); + if (file != null && file.exists()) { + return file; + } + } catch (Exception ex) { + LOG.error("Get file error in container: " + container, ex); } - } catch (Exception ex) { - LOG.error("Get file error in container :" + container, ex); } } //文件不存在,也返回该文件 - return file; + return defaultContainer.getFile(relativePath); } /** -- Gitee From a56efb725b9616bad94fddb02d9e89fbc6495780 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 6 Feb 2022 17:12:56 +0800 Subject: [PATCH 1548/1965] v3.13.4 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ pom.xml | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c6f1225b..de47394e 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.13.3 + 3.13.4 ``` diff --git a/changes.txt b/changes.txt index e57315c5..115a68f3 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.13.4: +新增:AttachmentManager 新增 getFile(path,localFirst) 方法 +优化:升级相关依赖到最新版本 + + + jboot v3.13.3: 新增:配置文件可以指定自己的文件名和路径的支持 优化:JbootmqBase,使之代码更加简洁 diff --git a/pom.xml b/pom.xml index 0a69d583..41e655e0 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.3 + 3.13.4 jar jboot @@ -50,7 +50,7 @@ 2.13.1 5.1.49 2.2.14.Final - 1.7.33 + 1.7.35 2.57 1.2.79 -- Gitee From 7c97717be20f50160e6c3ab94011c60101967f67 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 8 Feb 2022 12:22:58 +0800 Subject: [PATCH 1549/1965] v3.13.5 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 41e655e0..80382c85 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.4 + 3.13.5-SNAPSHOT jar jboot -- Gitee From 2895fa85d2569d276e3ec3cc75c91abb47ec4334 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 8 Feb 2022 12:24:17 +0800 Subject: [PATCH 1550/1965] add: CookieUtil add "defaultPath" and "defaultDomain" config --- src/main/java/io/jboot/utils/CookieUtil.java | 34 ++++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/utils/CookieUtil.java b/src/main/java/io/jboot/utils/CookieUtil.java index 241a50ee..473304c5 100644 --- a/src/main/java/io/jboot/utils/CookieUtil.java +++ b/src/main/java/io/jboot/utils/CookieUtil.java @@ -42,7 +42,13 @@ public class CookieUtil { private static String COOKIE_ENCRYPT_KEY = JbootWebConfig.getInstance().getCookieEncryptKey(); // 2 days(单位:秒) - private static int COOKIE_MAX_AGE = JbootWebConfig.getInstance().getCookieMaxAge(); + private static int COOKIE_MAX_AGE = JbootWebConfig.getInstance().getCookieMaxAge(); + + // 默认的路径, null 默认路径 "/" + private static String defaultPath = null; + + // 默认的域名, null 为当前域名 + private static String defaultDomain = null; /** @@ -64,22 +70,38 @@ public class CookieUtil { } + public static String getDefaultPath() { + return defaultPath; + } + + public static void setDefaultPath(String defaultPath) { + CookieUtil.defaultPath = defaultPath; + } + + public static String getDefaultDomain() { + return defaultDomain; + } + + public static void setDefaultDomain(String defaultDomain) { + CookieUtil.defaultDomain = defaultDomain; + } + public static void put(Controller ctr, String key, Object value) { - put(ctr, key, value, COOKIE_MAX_AGE, null, null, COOKIE_ENCRYPT_KEY); + put(ctr, key, value, COOKIE_MAX_AGE, defaultPath, defaultDomain, COOKIE_ENCRYPT_KEY); } public static void put(Controller ctr, String key, Object value, String secretKey) { - put(ctr, key, value, COOKIE_MAX_AGE, null, null, secretKey); + put(ctr, key, value, COOKIE_MAX_AGE, defaultPath, defaultDomain, secretKey); } public static void put(Controller ctr, String key, String value, int maxAgeInSeconds) { - put(ctr, key, value, maxAgeInSeconds, null, null, COOKIE_ENCRYPT_KEY); + put(ctr, key, value, maxAgeInSeconds, defaultPath, defaultDomain, COOKIE_ENCRYPT_KEY); } public static void put(Controller ctr, String key, String value, int maxAgeInSeconds, String secretKey) { - put(ctr, key, value, maxAgeInSeconds, null, null, secretKey); + put(ctr, key, value, maxAgeInSeconds, defaultPath, defaultDomain, secretKey); } @@ -152,7 +174,7 @@ public class CookieUtil { } String[] cookieStrings = cookieValue.split(COOKIE_SEPARATOR); - if (cookieStrings == null || cookieStrings.length != 4) { + if (cookieStrings.length != 4) { return null; } -- Gitee From 34c3230cb97361548f89d3b329447c1757390202 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 8 Feb 2022 12:44:26 +0800 Subject: [PATCH 1551/1965] optimize... --- src/main/java/io/jboot/utils/RequestUtil.java | 10 ++++------ .../java/io/jboot/test/controller/IndexController.java | 5 +++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/utils/RequestUtil.java b/src/main/java/io/jboot/utils/RequestUtil.java index 21518d9b..2d5fc165 100644 --- a/src/main/java/io/jboot/utils/RequestUtil.java +++ b/src/main/java/io/jboot/utils/RequestUtil.java @@ -162,12 +162,10 @@ public class RequestUtil { public static String getBaseUrl(HttpServletRequest request) { int port = request.getServerPort(); - StringBuilder defaultDomain = new StringBuilder(request.getScheme()); - defaultDomain.append("://") - .append(request.getServerName()) - .append(port == 80 ? "" : ":" + port) - .append(request.getContextPath()); - return defaultDomain.toString(); + return request.getScheme() + "://" + + request.getServerName() + + (port == 80 || port == 443 ? "" : ":" + port) + + request.getContextPath(); } diff --git a/src/test/java/io/jboot/test/controller/IndexController.java b/src/test/java/io/jboot/test/controller/IndexController.java index c1dcb7ee..b83a4c94 100644 --- a/src/test/java/io/jboot/test/controller/IndexController.java +++ b/src/test/java/io/jboot/test/controller/IndexController.java @@ -1,6 +1,7 @@ package io.jboot.test.controller; import com.jfinal.kit.PathKit; +import io.jboot.utils.RequestUtil; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.GetRequest; import io.jboot.web.controller.annotation.PostRequest; @@ -51,6 +52,10 @@ public class IndexController extends JbootController { renderText("get or post ok"); } + public void baseUrl(){ + renderText(RequestUtil.getBaseUrl()); + } + } -- Gitee From 9a9776ebe8457dcb4f38b8b3461c4c69cc173b89 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 12 Feb 2022 18:30:07 +0800 Subject: [PATCH 1552/1965] optimize... --- src/main/java/io/jboot/utils/RequestUtil.java | 2 +- src/test/java/io/jboot/test/controller/IndexController.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/utils/RequestUtil.java b/src/main/java/io/jboot/utils/RequestUtil.java index 2d5fc165..13b2abac 100644 --- a/src/main/java/io/jboot/utils/RequestUtil.java +++ b/src/main/java/io/jboot/utils/RequestUtil.java @@ -183,7 +183,7 @@ public class RequestUtil { public static String getCurrentUrl(HttpServletRequest request) { String queryString = request.getQueryString(); - String url = getBaseUrl(request) + request.getRequestURI(); + String url = getBaseUrl(request) + request.getServletPath(); if (StrUtil.isNotBlank(queryString)) { url = url.concat("?").concat(queryString); } diff --git a/src/test/java/io/jboot/test/controller/IndexController.java b/src/test/java/io/jboot/test/controller/IndexController.java index b83a4c94..79e018fb 100644 --- a/src/test/java/io/jboot/test/controller/IndexController.java +++ b/src/test/java/io/jboot/test/controller/IndexController.java @@ -57,5 +57,10 @@ public class IndexController extends JbootController { } + public void currentUrl(){ + renderText(RequestUtil.getCurrentUrl()); + } + + } -- Gitee From 060a83e4a4d0dd6c9e086ea5918dff20ff347a07 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 13 Feb 2022 11:33:30 +0800 Subject: [PATCH 1553/1965] add: Columns.addToFirst() method --- src/main/java/io/jboot/db/model/Columns.java | 40 ++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index b626060c..f52b9e7e 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -110,6 +110,29 @@ public class Columns implements Serializable { } + + + /** + * add new column in Columns + * + * @param column + */ + public Columns addToFirst(Column column) { + + //do not add null value column + if (column.hasPara() && column.getValue() == null) { + return this; + } + + if (this.cols == null) { + this.cols = new LinkedList<>(); + } + + this.cols.add(0, column); + return this; + } + + /** * add Columns * @@ -121,6 +144,23 @@ public class Columns implements Serializable { } + /** + * add Columns To First + * + * @param columns + * @return + */ + public Columns addToFirst(Columns columns) { + if (columns != null && !columns.isEmpty()) { + for (Column column : columns.getList()) { + addToFirst(column); + } + } + return this; + } + + + /** * equals * -- Gitee From 6e97e9b10a850262b17611338149293e9f464c32 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 13 Feb 2022 14:02:38 +0800 Subject: [PATCH 1554/1965] add: JbootDirectiveBase.getParaToString() methods --- .../directive/base/JbootDirectiveBase.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java b/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java index 5e7d648e..194d532b 100644 --- a/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java +++ b/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java @@ -91,6 +91,38 @@ public abstract class JbootDirectiveBase extends Directive { return (T) (data == null ? defaultValue : data); } + public String getParaToString(String key, Scope scope) { + Object object = getPara(key, scope, null); + if (object == null || object instanceof String) { + return (String) object; + } + String objStr = object.toString(); + return StrUtil.isBlank(objStr) ? null : objStr; + } + + + public String getParaToString(String key, Scope scope, String defaultValue) { + String v = getParaToString(key, scope); + return v == null ? defaultValue : v; + } + + + public String getParaToString(int index, Scope scope) { + Object object = getPara(index, scope, null); + if (object == null || object instanceof String) { + return (String) object; + } + String objStr = object.toString(); + return StrUtil.isBlank(objStr) ? null : objStr; + } + + + public String getParaToString(int index, Scope scope, String defaultValue) { + String v = getParaToString(index, scope); + return v == null ? defaultValue : v; + } + + public Integer getParaToInt(String key, Scope scope) { Object object = getPara(key, scope, null); if (object == null || object instanceof Integer) { -- Gitee From b0c263ac47f74f814451a83f7a281704faa3a88a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Feb 2022 09:41:10 +0800 Subject: [PATCH 1555/1965] upgrade dependency --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 80382c85..926ff447 100644 --- a/pom.xml +++ b/pom.xml @@ -41,16 +41,16 @@ - 4.9.17 - 2020.4 + 4.9.18 + 2022.2 2.8 3.3 3.3.0 4.0.3 2.13.1 5.1.49 - 2.2.14.Final - 1.7.35 + 2.2.16.Final + 1.7.36 2.57 1.2.79 @@ -71,7 +71,7 @@ 3.13.2.Final 1.4.2 1.1.8 - 4.2.7 + 4.2.8 1.8 1.8 -- Gitee From aac9cfe399b66e059bcacbb48dc2b2e8937a9698 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Feb 2022 12:35:40 +0800 Subject: [PATCH 1556/1965] =?UTF-8?q?HttpUtil=20=E6=B7=BB=E5=8A=A0=20http?= =?UTF-8?q?=20=E4=BB=A3=E7=90=86=E7=9A=84=E9=85=8D=E7=BD=AE=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/components/http/HttpProxyInfo.java | 103 ++++++++++++++++++ .../components/http/JbootHttpConfig.java | 55 +++++++++- .../components/http/JbootHttpRequest.java | 17 +++ .../components/http/jboot/JbootHttpImpl.java | 8 +- .../components/http/okhttp/OKHttpImpl.java | 8 +- .../io/jboot/test/http/HttpUtilTester.java | 7 ++ 6 files changed, 193 insertions(+), 5 deletions(-) create mode 100644 src/main/java/io/jboot/components/http/HttpProxyInfo.java diff --git a/src/main/java/io/jboot/components/http/HttpProxyInfo.java b/src/main/java/io/jboot/components/http/HttpProxyInfo.java new file mode 100644 index 00000000..cd39fe7b --- /dev/null +++ b/src/main/java/io/jboot/components/http/HttpProxyInfo.java @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.http; + +import io.jboot.utils.StrUtil; + +import java.io.Serializable; +import java.net.Authenticator; +import java.net.InetSocketAddress; +import java.net.PasswordAuthentication; +import java.net.Proxy; + +import static java.net.Proxy.Type.HTTP; + +public class HttpProxyInfo implements Serializable { + + private String proxyHost; + private Integer proxyPort; + private String proxyUser; + private String proxyPassword; + + private Proxy proxy; + + public HttpProxyInfo() { + } + + public HttpProxyInfo(String proxyHost, Integer proxyPort) { + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + } + + public HttpProxyInfo(String proxyHost, Integer proxyPort, String proxyUser, String proxyPassword) { + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + this.proxyUser = proxyUser; + this.proxyPassword = proxyPassword; + setAuthenticator(); + } + + public void setAuthenticator() { + if (StrUtil.isNotBlank(proxyUser) && StrUtil.isNotBlank(proxyPassword)) { + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(proxyUser, proxyPassword.toCharArray()); + } + }); + } + } + + public String getProxyHost() { + return proxyHost; + } + + public void setProxyHost(String proxyHost) { + this.proxyHost = proxyHost; + } + + public Integer getProxyPort() { + return proxyPort; + } + + public void setProxyPort(Integer proxyPort) { + this.proxyPort = proxyPort; + } + + public String getProxyUser() { + return proxyUser; + } + + public void setProxyUser(String proxyUser) { + this.proxyUser = proxyUser; + } + + public String getProxyPassword() { + return proxyPassword; + } + + public void setProxyPassword(String proxyPassword) { + this.proxyPassword = proxyPassword; + } + + + public Proxy getProxy() { + if (proxy == null) { + proxy = new Proxy(HTTP, new InetSocketAddress(getProxyHost(), getProxyPort())); + } + return proxy; + } +} diff --git a/src/main/java/io/jboot/components/http/JbootHttpConfig.java b/src/main/java/io/jboot/components/http/JbootHttpConfig.java index da6310b1..b9343dee 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpConfig.java +++ b/src/main/java/io/jboot/components/http/JbootHttpConfig.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -16,6 +16,7 @@ package io.jboot.components.http; import io.jboot.app.config.annotation.ConfigModel; +import io.jboot.utils.StrUtil; @ConfigModel(prefix = "jboot.http") @@ -25,12 +26,18 @@ public class JbootHttpConfig { public static final String TYPE_OKHTTP = "okhttp"; public String type = TYPE_DEFAULT; + private String certPath; private String certPass; + private int readTimeOut = JbootHttpRequest.READ_TIME_OUT; private int connectTimeOut = JbootHttpRequest.CONNECT_TIME_OUT; private String contentType = JbootHttpRequest.CONTENT_TYPE_URL_ENCODED; + private String proxyHost; + private Integer proxyPort; + private String proxyUser; + private String proxyPassword; public String getType() { @@ -80,4 +87,50 @@ public class JbootHttpConfig { public void setContentType(String contentType) { this.contentType = contentType; } + + public String getProxyHost() { + return proxyHost; + } + + public void setProxyHost(String proxyHost) { + this.proxyHost = proxyHost; + } + + public Integer getProxyPort() { + return proxyPort; + } + + public void setProxyPort(Integer proxyPort) { + this.proxyPort = proxyPort; + } + + public String getProxyUser() { + return proxyUser; + } + + public void setProxyUser(String proxyUser) { + this.proxyUser = proxyUser; + } + + public String getProxyPassword() { + return proxyPassword; + } + + public void setProxyPassword(String proxyPassword) { + this.proxyPassword = proxyPassword; + } + + private HttpProxyInfo httpProxyInfo; + + public HttpProxyInfo getHttpProxyInfo() { + if (httpProxyInfo != null) { + return httpProxyInfo; + } + if (StrUtil.isNotBlank(proxyHost) && proxyPort != null && proxyPort > 0) { + httpProxyInfo = new HttpProxyInfo(proxyHost, proxyPort, proxyUser, proxyPassword); + } + return httpProxyInfo; + } + + } diff --git a/src/main/java/io/jboot/components/http/JbootHttpRequest.java b/src/main/java/io/jboot/components/http/JbootHttpRequest.java index 2aab755f..50b621fa 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpRequest.java +++ b/src/main/java/io/jboot/components/http/JbootHttpRequest.java @@ -22,6 +22,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; +import java.net.Proxy; import java.util.LinkedHashMap; import java.util.Map; @@ -79,6 +80,9 @@ public class JbootHttpRequest { // 自定义 sslContext private SSLContext sslContext; + // 自定义 http 代理 + private HttpProxyInfo httpProxyInfo; + public static JbootHttpRequest create(String url) { return new JbootHttpRequest(url); @@ -116,6 +120,7 @@ public class JbootHttpRequest { this.readTimeOut = config.getReadTimeOut(); this.connectTimeOut = config.getConnectTimeOut(); this.contentType = config.getContentType(); + this.httpProxyInfo = config.getHttpProxyInfo(); } public JbootHttpRequest(String url) { @@ -374,4 +379,16 @@ public class JbootHttpRequest { public void setSslContext(SSLContext sslContext) { this.sslContext = sslContext; } + + public HttpProxyInfo getHttpProxyInfo() { + return httpProxyInfo; + } + + public void setHttpProxyInfo(HttpProxyInfo httpProxyInfo) { + this.httpProxyInfo = httpProxyInfo; + } + + public Proxy getProxy() { + return httpProxyInfo != null ? httpProxyInfo.getProxy() : null; + } } diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index 95ff1e36..3edf10fb 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -212,7 +212,7 @@ public class JbootHttpImpl implements JbootHttp { } private static HttpURLConnection getConnection(JbootHttpRequest request) throws Exception { - if (request.isPostRequest() == false) { + if (!request.isPostRequest()) { request.initGetUrl(); } return request.isHttps() ? getHttpsConnection(request) : getHttpConnection(request); @@ -220,13 +220,15 @@ public class JbootHttpImpl implements JbootHttp { private static HttpURLConnection getHttpConnection(JbootHttpRequest request) throws Exception { URL url = new URL(request.getRequestUrl()); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + HttpURLConnection conn = (HttpURLConnection) (request.getProxy() != null + ? url.openConnection(request.getProxy()) : url.openConnection()); return conn; } private static HttpsURLConnection getHttpsConnection(JbootHttpRequest request) throws Exception { URL url = new URL(request.getRequestUrl()); - HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); + HttpsURLConnection conn = (HttpsURLConnection) (request.getProxy() != null + ? url.openConnection(request.getProxy()) : url.openConnection()); //自定义 sslContext if (request.getSslContext() != null) { diff --git a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java index 0fc79da3..3edbe4b0 100644 --- a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java +++ b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java @@ -130,7 +130,13 @@ public class OKHttpImpl implements JbootHttp { return getHttpsClient(request); } - return new OkHttpClient(); + + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + if (request.getProxy() != null) { + builder.proxy(request.getProxy()); + } + + return builder.build(); } public OkHttpClient getHttpsClient(JbootHttpRequest request) throws Exception { diff --git a/src/test/java/io/jboot/test/http/HttpUtilTester.java b/src/test/java/io/jboot/test/http/HttpUtilTester.java index dc2ab4a0..948cf7f2 100644 --- a/src/test/java/io/jboot/test/http/HttpUtilTester.java +++ b/src/test/java/io/jboot/test/http/HttpUtilTester.java @@ -1,6 +1,7 @@ package io.jboot.test.http; import com.jfinal.kit.JsonKit; +import io.jboot.components.http.HttpProxyInfo; import io.jboot.components.http.JbootHttpManager; import io.jboot.components.http.JbootHttpRequest; import io.jboot.components.http.JbootHttpResponse; @@ -17,6 +18,8 @@ public class HttpUtilTester { public static void main(String[] args) { JbootHttpRequest request = JbootHttpRequest.create("https://www.baidu.com", null, JbootHttpRequest.METHOD_GET); + request.setHttpProxyInfo(new HttpProxyInfo("127.0.0.1",8080)); + JbootHttpResponse response = JbootHttpManager.me().getJbootHttp().handle(request); // System.out.println(response.getHeaders().get("Location").get(0)); @@ -32,7 +35,11 @@ public class HttpUtilTester { map.put("cc",null); map.put("你好","xx"); System.out.println(StrUtil.mapToQueryString(map)); + + + } + } \ No newline at end of file -- Gitee From 1817dfcda1e0a9f3d124921e1d73ecd7b9636620 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Feb 2022 14:30:27 +0800 Subject: [PATCH 1557/1965] v3.13.5 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 8c42ab25..6257113f 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.13.4 + 3.13.5 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 6b3251b9..b28682d7 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.13.4 + 3.13.5 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 21ae055f..ac333286 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.13.4 + 3.13.5 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.13.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.13.5', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index a7df61a2..10c289c6 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.13.4', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.13.5', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.4/jboot-3.13.4.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.5/jboot-3.13.5.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 515ed391..eb82933a 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.13.4"; + public static String VERSION = "3.13.5"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From aa7f324f4ca74c1b343b9fd3715344dfa4f71122 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 14 Feb 2022 14:30:31 +0800 Subject: [PATCH 1558/1965] v3.13.5 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 10 ++++++++++ pom.xml | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index de47394e..2b5648b1 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.13.4 + 3.13.5 ``` diff --git a/changes.txt b/changes.txt index 115a68f3..49a57250 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,13 @@ +jboot v3.13.5: +新增:CookieUtil 添加 "defaultPath" 和 "defaultDomain" 的配置 +新增:HttpUtil 添加 http 代理的配置支持 +新增:Columns.addToFirst() 方法 +新增:JbootDirectiveBase.getParaToString() +优化:RequestUtil.java +优化:升级 JFinal 到最新版本 + + + jboot v3.13.4: 新增:AttachmentManager 新增 getFile(path,localFirst) 方法 优化:升级相关依赖到最新版本 diff --git a/pom.xml b/pom.xml index 926ff447..b9c40a36 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.5-SNAPSHOT + 3.13.5 jar jboot -- Gitee From 6c6503e42c577453cf18983297632a2161060c60 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 18 Feb 2022 12:46:01 +0800 Subject: [PATCH 1559/1965] v3.13.6 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b9c40a36..210ce2cd 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.5 + 3.13.6-SNAPSHOT jar jboot -- Gitee From 089963c6f0352121933853d01d444cc3567d33dd Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 18 Feb 2022 12:47:21 +0800 Subject: [PATCH 1560/1965] =?UTF-8?q?feature:=20Controller=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E8=BF=94=E5=9B=9E=20xxx.html=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=A8=A1=E6=9D=BF=E5=BC=95=E6=93=8E=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/render/JbootReturnValueRender.java | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index 98f17c01..d32bdfd1 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -17,8 +17,11 @@ package io.jboot.web.render; import com.jfinal.core.Action; import com.jfinal.kit.JsonKit; -import com.jfinal.render.*; +import com.jfinal.render.IRenderFactory; +import com.jfinal.render.Render; +import com.jfinal.render.RenderManager; import io.jboot.utils.DateUtil; +import io.jboot.utils.StrUtil; import io.jboot.web.ResponseEntity; import javax.servlet.http.HttpServletRequest; @@ -34,9 +37,10 @@ import java.util.Date; */ public class JbootReturnValueRender extends Render { - private Action action; - private Object value; - private Render render; + protected IRenderFactory factory = RenderManager.me().getRenderFactory(); + protected Action action; + protected Object value; + protected Render render; public JbootReturnValueRender(Action action, Object returnValue) { @@ -50,20 +54,41 @@ public class JbootReturnValueRender extends Render { this.value = returnValue; } + initRealRender(); + } + + + protected void initRealRender() { if (this.value == null) { - this.render = new NullRender(); + this.render = factory.getNullRender(); } else if (this.value instanceof ResponseEntity) { this.render = new JbootResponseEntityRender((ResponseEntity) value); } else if (this.value instanceof String) { - this.render = new TextRender((String) value); + String newVal = ((String) value).toLowerCase(); + if (newVal.endsWith(".html")) { + this.render = factory.getTemplateRender((String) value); + } else if (newVal.startsWith("error") && newVal.length() > 8) { + String trim = newVal.substring(5).trim(); + if (trim.startsWith(":")) { + String errorCodeStr = trim.substring(1).trim(); + if (StrUtil.isNumeric(errorCodeStr)) { + this.render = factory.getErrorRender(Integer.parseInt(errorCodeStr)); + } + } + if (this.render == null) { + this.render = factory.getTextRender((String) value); + } + } else { + this.render = factory.getTextRender((String) value); + } } else if (this.value instanceof Date) { - this.render = new TextRender(DateUtil.toDateTimeString((Date) value)); + this.render = factory.getTextRender(DateUtil.toDateTimeString((Date) value)); } else if (this.value instanceof File) { - this.render = new FileRender((File) value); + this.render = factory.getFileRender((File) value); } else if (this.value instanceof Render) { this.render = (Render) value; } else { - this.render = new JsonRender(JsonKit.toJson(value)); + this.render = factory.getJsonRender(JsonKit.toJson(value)); } } @@ -86,7 +111,7 @@ public class JbootReturnValueRender extends Render { } - private boolean isBaseType(Object value) { + protected boolean isBaseType(Object value) { Class c = value.getClass(); return c == String.class || c == char.class || c == Integer.class || c == int.class @@ -97,4 +122,6 @@ public class JbootReturnValueRender extends Render { || c == Short.class || c == short.class || c == BigDecimal.class || c == BigInteger.class; } + + } -- Gitee From d13798781ca994d03fc436eccc19e0d31b2d96d9 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 18 Feb 2022 15:56:53 +0800 Subject: [PATCH 1561/1965] optimize... --- .../components/http/JbootHttpManager.java | 22 +++++++++---------- .../components/http/JbootHttpResponse.java | 18 ++++++++------- .../components/http/jboot/JbootHttpImpl.java | 17 +++++++------- src/main/java/io/jboot/test/TestConfig.java | 1 + src/main/java/io/jboot/utils/ClassUtil.java | 16 +++++++------- .../java/io/jboot/utils/CollectionUtil.java | 2 +- src/main/java/io/jboot/utils/ReflectUtil.java | 2 +- 7 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/main/java/io/jboot/components/http/JbootHttpManager.java b/src/main/java/io/jboot/components/http/JbootHttpManager.java index 567c0a0f..a1d72185 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpManager.java +++ b/src/main/java/io/jboot/components/http/JbootHttpManager.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -19,25 +19,22 @@ import io.jboot.Jboot; import io.jboot.components.http.jboot.JbootHttpImpl; import io.jboot.components.http.okhttp.OKHttpImpl; import io.jboot.core.spi.JbootSpiLoader; -import io.jboot.utils.ClassUtil; public class JbootHttpManager { - private static JbootHttpManager manager; + private static JbootHttpManager me = new JbootHttpManager(); + private JbootHttp jbootHttp; + private JbootHttpConfig httpConfig; public static JbootHttpManager me() { - if (manager == null) { - manager = ClassUtil.singleton(JbootHttpManager.class); - manager.httpConfig = Jboot.config(JbootHttpConfig.class); - - } - return manager; + return me; } + private JbootHttpManager() { + httpConfig = Jboot.config(JbootHttpConfig.class); + } - private JbootHttp jbootHttp; - private JbootHttpConfig httpConfig; public JbootHttp getJbootHttp() { if (jbootHttp == null) { @@ -46,12 +43,13 @@ public class JbootHttpManager { return jbootHttp; } + public JbootHttpConfig getHttpConfig() { return httpConfig; } - private JbootHttp buildJbootHttp() { + private JbootHttp buildJbootHttp() { switch (httpConfig.getType()) { case JbootHttpConfig.TYPE_DEFAULT: return new JbootHttpImpl(); diff --git a/src/main/java/io/jboot/components/http/JbootHttpResponse.java b/src/main/java/io/jboot/components/http/JbootHttpResponse.java index 6e8bcbd0..98359758 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpResponse.java +++ b/src/main/java/io/jboot/components/http/JbootHttpResponse.java @@ -21,8 +21,9 @@ import java.io.*; import java.util.List; import java.util.Map; -public class JbootHttpResponse { - private static final Log log = Log.getLog(JbootHttpResponse.class); +public class JbootHttpResponse implements Closeable { + + private static final Log LOG = Log.getLog(JbootHttpResponse.class); private String content; private OutputStream contentStream; @@ -37,12 +38,12 @@ public class JbootHttpResponse { } public JbootHttpResponse(File file) { - if (!file.getParentFile().exists()) { - file.getParentFile().mkdirs(); + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) { + LOG.error("Can not mkdirs for: " + file.getParentFile()); } - if (file.exists()) { - file.delete(); + if (file.exists() && !file.delete()) { + LOG.error("Can not delete file: " + file); } try { @@ -86,7 +87,7 @@ public class JbootHttpResponse { contentStream.write(buffer, 0, len); } } catch (Throwable throwable) { - log.error(throwable.toString(), throwable); + LOG.error(throwable.toString(), throwable); setError(throwable); } } @@ -94,13 +95,14 @@ public class JbootHttpResponse { /** * 结束response和释放资源 */ + @Override public void close() { if (contentStream != null) { try { contentStream.flush(); contentStream.close(); } catch (IOException e) { - e.printStackTrace(); + LOG.error(e.toString(), e); } } } diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index 3edf10fb..bc562450 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -15,12 +15,14 @@ */ package io.jboot.components.http.jboot; +import com.jfinal.log.Log; import io.jboot.components.http.HttpMimeTypes; import io.jboot.components.http.JbootHttp; import io.jboot.components.http.JbootHttpRequest; import io.jboot.components.http.JbootHttpResponse; import io.jboot.exception.JbootException; import io.jboot.utils.ArrayUtil; +import io.jboot.utils.QuietlyUtil; import io.jboot.utils.StrUtil; import javax.net.ssl.*; @@ -37,6 +39,8 @@ import java.util.zip.GZIPInputStream; public class JbootHttpImpl implements JbootHttp { + private static final Log LOG = Log.getLog(JbootHttpImpl.class); + @Override public JbootHttpResponse handle(JbootHttpRequest request) { @@ -105,20 +109,15 @@ public class JbootHttpImpl implements JbootHttp { } catch (Throwable ex) { response.setError(ex); + LOG.error(ex.toString(), ex); } finally { - response.close(); - if (connection != null) { connection.disconnect(); } - if (inStream != null) { - try { - inStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } + + QuietlyUtil.quietlyClose(inStream, response); + } } diff --git a/src/main/java/io/jboot/test/TestConfig.java b/src/main/java/io/jboot/test/TestConfig.java index 42665a4f..b3383eb0 100644 --- a/src/main/java/io/jboot/test/TestConfig.java +++ b/src/main/java/io/jboot/test/TestConfig.java @@ -34,6 +34,7 @@ public @interface TestConfig { //配置 appMode boolean devMode() default true; + //配置启动是否打印 class 扫描信息 boolean printScannerInfo() default false; //配置启动参数 diff --git a/src/main/java/io/jboot/utils/ClassUtil.java b/src/main/java/io/jboot/utils/ClassUtil.java index 98f49614..476aa6c3 100644 --- a/src/main/java/io/jboot/utils/ClassUtil.java +++ b/src/main/java/io/jboot/utils/ClassUtil.java @@ -31,7 +31,8 @@ import java.util.concurrent.ConcurrentHashMap; public class ClassUtil { private static Log LOG = Log.getLog(ClassUtil.class); - private static final Map singletons = new ConcurrentHashMap<>(); + + private static final Map, Object> singletons = new ConcurrentHashMap<>(); /** @@ -135,7 +136,7 @@ public class ClassUtil { public static T newInstance(Class clazz, boolean createByAop, Object... paras) { try { - Class[] classes = new Class[paras.length]; + Class[] classes = new Class[paras.length]; for (int i = 0; i < paras.length; i++) { Object data = paras[i]; if (data == null) { @@ -173,17 +174,16 @@ public class ClassUtil { Method method = getStaticConstruct(staticConstruct.value(), clazz); if (method == null) { - throw new JbootException("can not new instance by static constrauct for class : " + clazz); + throw new JbootException("Can not new instance by static constrauct for class: " + clazz); } try { return (T) method.invoke(null, null); } catch (Exception e) { - LOG.error("can not invoke method:" + method.getName() - + " in class : " - + clazz + "\n" - + e.toString(), e); + LOG.error("Can not invoke method:" + method.getName() + + " in class : "+ clazz + "\n" + + e, e); if (e instanceof RuntimeException) { throw (RuntimeException) e; @@ -194,7 +194,7 @@ public class ClassUtil { } - private static Method getStaticConstruct(String name, Class clazz) { + private static Method getStaticConstruct(String name, Class clazz) { Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { if (Modifier.isStatic(method.getModifiers()) diff --git a/src/main/java/io/jboot/utils/CollectionUtil.java b/src/main/java/io/jboot/utils/CollectionUtil.java index adf70dfe..3f588234 100644 --- a/src/main/java/io/jboot/utils/CollectionUtil.java +++ b/src/main/java/io/jboot/utils/CollectionUtil.java @@ -39,7 +39,7 @@ public class CollectionUtil { return map; } - public static boolean isEmpty(Collection collection){ + public static boolean isEmpty(Collection collection){ return collection == null || collection.isEmpty(); } } diff --git a/src/main/java/io/jboot/utils/ReflectUtil.java b/src/main/java/io/jboot/utils/ReflectUtil.java index 430121a3..ad0f037d 100644 --- a/src/main/java/io/jboot/utils/ReflectUtil.java +++ b/src/main/java/io/jboot/utils/ReflectUtil.java @@ -56,7 +56,7 @@ public class ReflectUtil { } private static T getFileValue(Object getFrom, Field field) { - final boolean accessible = field.isAccessible(); + boolean accessible = field.isAccessible(); try { field.setAccessible(true); return (T) field.get(getFrom); -- Gitee From 24f756b0caa2d698fb83da6ea048f68110bdc4d0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 18 Feb 2022 16:02:41 +0800 Subject: [PATCH 1562/1965] update Copyright --- src/main/java/io/jboot/Jboot.java | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- src/main/java/io/jboot/aop/DefaultValueInterceptor.java | 2 +- src/main/java/io/jboot/aop/InterceptorBuilder.java | 2 +- src/main/java/io/jboot/aop/InterceptorBuilderManager.java | 2 +- src/main/java/io/jboot/aop/InterceptorCache.java | 2 +- src/main/java/io/jboot/aop/Interceptors.java | 2 +- src/main/java/io/jboot/aop/JbootAopFactory.java | 2 +- src/main/java/io/jboot/aop/JbootLazyLoader.java | 2 +- src/main/java/io/jboot/aop/JbootLazyLoaderFactory.java | 2 +- src/main/java/io/jboot/aop/ValueFilter.java | 2 +- src/main/java/io/jboot/aop/ValueFilterInterceptor.java | 2 +- src/main/java/io/jboot/aop/annotation/AutoLoad.java | 2 +- src/main/java/io/jboot/aop/annotation/Bean.java | 2 +- src/main/java/io/jboot/aop/annotation/BeanExclude.java | 2 +- src/main/java/io/jboot/aop/annotation/ConfigValue.java | 2 +- src/main/java/io/jboot/aop/annotation/Configuration.java | 2 +- src/main/java/io/jboot/aop/annotation/DefaultValue.java | 2 +- src/main/java/io/jboot/aop/annotation/FilterBy.java | 2 +- src/main/java/io/jboot/aop/annotation/Lazy.java | 2 +- src/main/java/io/jboot/aop/annotation/StaticConstruct.java | 2 +- src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java | 2 +- src/main/java/io/jboot/aop/cglib/JbootCglibLazyLoader.java | 2 +- src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java | 2 +- src/main/java/io/jboot/aop/javassist/JbootJavassistHandler.java | 2 +- .../java/io/jboot/aop/javassist/JbootJavassistProxyFactory.java | 2 +- src/main/java/io/jboot/aop/jfinal/JfinalHandlers.java | 2 +- src/main/java/io/jboot/aop/jfinal/JfinalPlugins.java | 2 +- src/main/java/io/jboot/apidoc/ApiDocConfig.java | 2 +- src/main/java/io/jboot/apidoc/ApiDocManager.java | 2 +- src/main/java/io/jboot/apidoc/ApiDocRender.java | 2 +- src/main/java/io/jboot/apidoc/ApiDocUtil.java | 2 +- src/main/java/io/jboot/apidoc/ApiDocument.java | 2 +- src/main/java/io/jboot/apidoc/ApiJsonGenerator.java | 2 +- src/main/java/io/jboot/apidoc/ApiMockBuilder.java | 2 +- src/main/java/io/jboot/apidoc/ApiMockBuilders.java | 2 +- src/main/java/io/jboot/apidoc/ApiOperation.java | 2 +- src/main/java/io/jboot/apidoc/ApiParameter.java | 2 +- src/main/java/io/jboot/apidoc/ApiResponse.java | 2 +- src/main/java/io/jboot/apidoc/ContentType.java | 2 +- src/main/java/io/jboot/apidoc/HttpMethod.java | 2 +- src/main/java/io/jboot/apidoc/annotation/Api.java | 2 +- src/main/java/io/jboot/apidoc/annotation/ApiOper.java | 2 +- src/main/java/io/jboot/apidoc/annotation/ApiPara.java | 2 +- src/main/java/io/jboot/apidoc/annotation/ApiParas.java | 2 +- src/main/java/io/jboot/apidoc/annotation/ApiResp.java | 2 +- src/main/java/io/jboot/apidoc/annotation/ApiResps.java | 2 +- src/main/java/io/jboot/app/ApplicationUtil.java | 2 +- src/main/java/io/jboot/app/Banner.java | 2 +- src/main/java/io/jboot/app/HttpContentTypes.java | 2 +- src/main/java/io/jboot/app/JbootApplication.java | 2 +- src/main/java/io/jboot/app/JbootApplicationConfig.java | 2 +- src/main/java/io/jboot/app/JbootResourceLoader.java | 2 +- src/main/java/io/jboot/app/JbootSimpleApplication.java | 2 +- src/main/java/io/jboot/app/JbootWebBuilderConfiger.java | 2 +- src/main/java/io/jboot/app/config/ConfigPara.java | 2 +- .../java/io/jboot/app/config/JbootConfigChangeListener.java | 2 +- src/main/java/io/jboot/app/config/JbootConfigDecryptor.java | 2 +- src/main/java/io/jboot/app/config/JbootConfigKit.java | 2 +- src/main/java/io/jboot/app/config/JbootConfigManager.java | 2 +- src/main/java/io/jboot/app/config/JbootProp.java | 2 +- src/main/java/io/jboot/app/config/annotation/ConfigModel.java | 2 +- .../io/jboot/app/config/support/apollo/ApolloConfigManager.java | 2 +- .../io/jboot/app/config/support/apollo/ApolloServerConfig.java | 2 +- .../io/jboot/app/config/support/nacos/NacosConfigIniter.java | 2 +- .../io/jboot/app/config/support/nacos/NacosConfigManager.java | 2 +- .../io/jboot/app/config/support/nacos/NacosServerConfig.java | 2 +- src/main/java/io/jboot/app/undertow/JbootHotSwapResolver.java | 2 +- src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java | 2 +- src/main/java/io/jboot/app/undertow/JbootUndertowServer.java | 2 +- src/main/java/io/jboot/codegen/CodeGenHelpler.java | 2 +- src/main/java/io/jboot/codegen/GenTester.java | 2 +- .../java/io/jboot/codegen/model/JbootBaseModelGenerator.java | 2 +- src/main/java/io/jboot/codegen/model/JbootModelGenerator.java | 2 +- src/main/java/io/jboot/codegen/model/ModeGenTester.java | 2 +- .../io/jboot/codegen/service/JbootServiceImplGenerator.java | 2 +- .../jboot/codegen/service/JbootServiceInterfaceGenerator.java | 2 +- src/main/java/io/jboot/codegen/service/ServiceGenTester.java | 2 +- src/main/java/io/jboot/components/cache/CacheTime.java | 2 +- .../java/io/jboot/components/cache/JbootAopCacheConfig.java | 2 +- src/main/java/io/jboot/components/cache/JbootCache.java | 2 +- src/main/java/io/jboot/components/cache/JbootCacheBase.java | 2 +- src/main/java/io/jboot/components/cache/JbootCacheConfig.java | 2 +- src/main/java/io/jboot/components/cache/JbootCacheManager.java | 2 +- .../java/io/jboot/components/cache/annotation/CacheEvict.java | 2 +- .../java/io/jboot/components/cache/annotation/CachePut.java | 2 +- .../java/io/jboot/components/cache/annotation/Cacheable.java | 2 +- .../java/io/jboot/components/cache/annotation/CachesEvict.java | 2 +- .../jboot/components/cache/caffeine/CaffeineCacheBuilder.java | 2 +- .../io/jboot/components/cache/caffeine/CaffeineCacheImpl.java | 2 +- .../io/jboot/components/cache/caffeine/CaffeineCacheObject.java | 2 +- .../components/cache/caffeine/DefaultCaffeineCacheBuilder.java | 2 +- .../jboot/components/cache/caredis/JbootCaredisCacheImpl.java | 2 +- .../io/jboot/components/cache/caredis/JbootCaredisMessage.java | 2 +- .../io/jboot/components/cache/ehcache/JbootEhCacheConfig.java | 2 +- .../io/jboot/components/cache/ehcache/JbootEhcacheImpl.java | 2 +- .../jboot/components/cache/ehredis/JbootEhredisCacheImpl.java | 2 +- .../io/jboot/components/cache/ehredis/JbootEhredisMessage.java | 2 +- .../components/cache/interceptor/CacheEvictInterceptor.java | 2 +- .../components/cache/interceptor/CacheInterceptorBuilder.java | 2 +- .../jboot/components/cache/interceptor/CachePutInterceptor.java | 2 +- .../components/cache/interceptor/CacheableInterceptor.java | 2 +- .../components/cache/interceptor/CachesEvictInterceptor.java | 2 +- src/main/java/io/jboot/components/cache/interceptor/Utils.java | 2 +- .../java/io/jboot/components/cache/j2cache/J2cacheImpl.java | 2 +- src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java | 2 +- .../io/jboot/components/cache/redis/JbootRedisCacheConfig.java | 2 +- .../io/jboot/components/cache/redis/JbootRedisCacheImpl.java | 2 +- src/main/java/io/jboot/components/event/JbootEvent.java | 2 +- src/main/java/io/jboot/components/event/JbootEventListener.java | 2 +- src/main/java/io/jboot/components/event/JbootEventManager.java | 2 +- .../java/io/jboot/components/event/annotation/EventConfig.java | 2 +- .../java/io/jboot/components/gateway/GatewayErrorRender.java | 2 +- src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java | 2 +- .../java/io/jboot/components/gateway/GatewayInterceptor.java | 2 +- .../java/io/jboot/components/gateway/GatewayInvocation.java | 2 +- .../io/jboot/components/gateway/GatewayLoadBalanceStrategy.java | 2 +- .../io/jboot/components/gateway/GatewaySentinelProcesser.java | 2 +- .../java/io/jboot/components/gateway/JbootGatewayConfig.java | 2 +- .../java/io/jboot/components/gateway/JbootGatewayHandler.java | 2 +- .../io/jboot/components/gateway/JbootGatewayHealthChecker.java | 2 +- .../java/io/jboot/components/gateway/JbootGatewayManager.java | 2 +- .../io/jboot/components/gateway/discovery/GatewayDiscovery.java | 2 +- .../components/gateway/discovery/GatewayDiscoveryConfig.java | 2 +- .../components/gateway/discovery/GatewayDiscoveryListener.java | 2 +- .../components/gateway/discovery/GatewayDiscoveryManager.java | 2 +- .../io/jboot/components/gateway/discovery/GatewayInstance.java | 2 +- .../components/gateway/discovery/GatewayInstanceConfig.java | 2 +- .../components/gateway/discovery/NacosGatewayDiscovery.java | 2 +- src/main/java/io/jboot/components/http/HttpProxyInfo.java | 2 +- src/main/java/io/jboot/components/http/JbootHttp.java | 2 +- src/main/java/io/jboot/components/http/JbootHttpConfig.java | 2 +- src/main/java/io/jboot/components/http/JbootHttpManager.java | 2 +- src/main/java/io/jboot/components/http/JbootHttpRequest.java | 2 +- src/main/java/io/jboot/components/http/JbootHttpResponse.java | 2 +- src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java | 2 +- src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java | 2 +- src/main/java/io/jboot/components/limiter/LimitConfig.java | 2 +- .../io/jboot/components/limiter/LimitFallbackProcesser.java | 2 +- .../jboot/components/limiter/LimitFallbackProcesserDefault.java | 2 +- src/main/java/io/jboot/components/limiter/LimitInfo.java | 2 +- src/main/java/io/jboot/components/limiter/LimitScope.java | 2 +- src/main/java/io/jboot/components/limiter/LimitType.java | 2 +- src/main/java/io/jboot/components/limiter/LimiterManager.java | 2 +- .../io/jboot/components/limiter/annotation/EnableLimit.java | 2 +- .../components/limiter/interceptor/BaseLimiterInterceptor.java | 2 +- .../limiter/interceptor/LimiterGlobalInterceptor.java | 2 +- .../components/limiter/interceptor/LimiterInterceptor.java | 2 +- .../limiter/interceptor/LimiterInterceptorBuilder.java | 2 +- .../io/jboot/components/limiter/redis/RedisRateLimitUtil.java | 2 +- src/main/java/io/jboot/components/mq/Jbootmq.java | 2 +- src/main/java/io/jboot/components/mq/JbootmqBase.java | 2 +- src/main/java/io/jboot/components/mq/JbootmqConfig.java | 2 +- src/main/java/io/jboot/components/mq/JbootmqManager.java | 2 +- .../java/io/jboot/components/mq/JbootmqMessageListener.java | 2 +- .../java/io/jboot/components/mq/activemq/JbootActivemq.java | 2 +- .../io/jboot/components/mq/aliyunmq/JbootAliyunmqConfig.java | 2 +- .../java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java | 2 +- .../java/io/jboot/components/mq/local/JbootLocalmqImpl.java | 2 +- .../java/io/jboot/components/mq/qpidmq/JbootQpidmqConfig.java | 2 +- .../java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java | 2 +- .../io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java | 2 +- .../java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java | 2 +- .../java/io/jboot/components/mq/redismq/JbootRedismqConfig.java | 2 +- .../java/io/jboot/components/mq/redismq/JbootRedismqImpl.java | 2 +- .../io/jboot/components/mq/rocketmq/JbootRocketmqConfig.java | 2 +- .../java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java | 2 +- src/main/java/io/jboot/components/rpc/Jbootrpc.java | 2 +- src/main/java/io/jboot/components/rpc/JbootrpcBase.java | 2 +- src/main/java/io/jboot/components/rpc/JbootrpcConfig.java | 2 +- src/main/java/io/jboot/components/rpc/JbootrpcManager.java | 2 +- .../java/io/jboot/components/rpc/JbootrpcReferenceConfig.java | 2 +- .../java/io/jboot/components/rpc/JbootrpcServiceConfig.java | 2 +- src/main/java/io/jboot/components/rpc/RPCUtil.java | 2 +- src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java | 2 +- src/main/java/io/jboot/components/rpc/annotation/RPCBean.java | 2 +- src/main/java/io/jboot/components/rpc/annotation/RPCInject.java | 2 +- src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java | 2 +- src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java | 2 +- src/main/java/io/jboot/components/rpc/local/JbootLocalrpc.java | 2 +- src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java | 2 +- src/main/java/io/jboot/components/rpc/motan/MotanUtil.java | 2 +- src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java | 2 +- .../java/io/jboot/components/schedule/JbooScheduleConfig.java | 2 +- .../java/io/jboot/components/schedule/JbootCron4jPlugin.java | 2 +- .../io/jboot/components/schedule/JbootDistributedRunnable.java | 2 +- .../java/io/jboot/components/schedule/JbootScheduleManager.java | 2 +- src/main/java/io/jboot/components/schedule/annotation/Cron.java | 2 +- .../schedule/annotation/EnableDistributedRunnable.java | 2 +- .../io/jboot/components/schedule/annotation/FixedDelay.java | 2 +- .../java/io/jboot/components/schedule/annotation/FixedRate.java | 2 +- .../java/io/jboot/components/serializer/FastJsonSerializer.java | 2 +- src/main/java/io/jboot/components/serializer/FstSerializer.java | 2 +- .../java/io/jboot/components/serializer/JacksonSerializer.java | 2 +- .../java/io/jboot/components/serializer/JbootSerializer.java | 2 +- .../io/jboot/components/serializer/JbootSerializerConfig.java | 2 +- .../io/jboot/components/serializer/JbootSerializerManager.java | 2 +- .../java/io/jboot/components/serializer/KryoSerializer.java | 2 +- src/main/java/io/jboot/components/valid/ValidErrorRender.java | 2 +- src/main/java/io/jboot/components/valid/ValidException.java | 2 +- src/main/java/io/jboot/components/valid/ValidUtil.java | 2 +- .../components/valid/interceptor/DecimalMaxInterceptor.java | 2 +- .../components/valid/interceptor/DecimalMinInterceptor.java | 2 +- .../jboot/components/valid/interceptor/DigitsInterceptor.java | 2 +- .../io/jboot/components/valid/interceptor/EmailInterceptor.java | 2 +- .../io/jboot/components/valid/interceptor/MaxInterceptor.java | 2 +- .../io/jboot/components/valid/interceptor/MinInterceptor.java | 2 +- .../jboot/components/valid/interceptor/NegativeInterceptor.java | 2 +- .../components/valid/interceptor/NegativeOrZeroInterceptor.java | 2 +- .../jboot/components/valid/interceptor/NotBlankInterceptor.java | 2 +- .../jboot/components/valid/interceptor/NotEmptyInterceptor.java | 2 +- .../jboot/components/valid/interceptor/NotNullInterceptor.java | 2 +- .../jboot/components/valid/interceptor/PatternInterceptor.java | 2 +- .../jboot/components/valid/interceptor/PositiveInterceptor.java | 2 +- .../components/valid/interceptor/PositiveOrZeroInterceptor.java | 2 +- .../io/jboot/components/valid/interceptor/SimpleContext.java | 2 +- .../io/jboot/components/valid/interceptor/SizeInterceptor.java | 2 +- .../io/jboot/components/valid/interceptor/ValidInterceptor.java | 2 +- .../components/valid/interceptor/ValidInterceptorBuilder.java | 2 +- src/main/java/io/jboot/core/JbootCoreConfig.java | 2 +- src/main/java/io/jboot/core/listener/JbootAppListener.java | 2 +- src/main/java/io/jboot/core/listener/JbootAppListenerBase.java | 2 +- .../java/io/jboot/core/listener/JbootAppListenerManager.java | 2 +- src/main/java/io/jboot/core/log/JbootLogFactory.java | 2 +- src/main/java/io/jboot/core/log/JdkLogger.java | 2 +- src/main/java/io/jboot/core/log/Slf4jLogger.java | 2 +- src/main/java/io/jboot/core/log/Slf4jSimpleLogger.java | 2 +- src/main/java/io/jboot/core/spi/JbootSpi.java | 2 +- src/main/java/io/jboot/core/spi/JbootSpiLoader.java | 2 +- src/main/java/io/jboot/core/weight/Weight.java | 2 +- src/main/java/io/jboot/core/weight/WeightUtil.java | 2 +- src/main/java/io/jboot/db/ArpManager.java | 2 +- src/main/java/io/jboot/db/JbootDb.java | 2 +- src/main/java/io/jboot/db/SqlDebugger.java | 2 +- src/main/java/io/jboot/db/TableInfo.java | 2 +- src/main/java/io/jboot/db/TableInfoManager.java | 2 +- src/main/java/io/jboot/db/annotation/Table.java | 2 +- src/main/java/io/jboot/db/datasource/DataSourceBuilder.java | 2 +- src/main/java/io/jboot/db/datasource/DataSourceConfig.java | 2 +- .../java/io/jboot/db/datasource/DataSourceConfigManager.java | 2 +- src/main/java/io/jboot/db/datasource/DataSourceFactory.java | 2 +- .../java/io/jboot/db/datasource/DruidDataSourceFactory.java | 2 +- .../java/io/jboot/db/datasource/HikariDataSourceFactory.java | 2 +- src/main/java/io/jboot/db/dbpro/JbootDbPro.java | 2 +- src/main/java/io/jboot/db/dbpro/JbootDbProFactory.java | 2 +- src/main/java/io/jboot/db/dialect/JbootAnsiSqlDialect.java | 2 +- src/main/java/io/jboot/db/dialect/JbootClickHouseDialect.java | 2 +- src/main/java/io/jboot/db/dialect/JbootDialect.java | 2 +- src/main/java/io/jboot/db/dialect/JbootMysqlDialect.java | 2 +- src/main/java/io/jboot/db/dialect/JbootOracleDialect.java | 2 +- src/main/java/io/jboot/db/dialect/JbootPostgreSqlDialect.java | 2 +- src/main/java/io/jboot/db/dialect/JbootSqlServerDialect.java | 2 +- src/main/java/io/jboot/db/dialect/JbootSqlite3Dialect.java | 2 +- src/main/java/io/jboot/db/driver/DriverClassNames.java | 2 +- .../java/io/jboot/db/driver/NativeClickHouseConnection.java | 2 +- src/main/java/io/jboot/db/driver/NativeClickHouseDriver.java | 2 +- .../java/io/jboot/db/driver/OfficialClickHouseConnection.java | 2 +- src/main/java/io/jboot/db/driver/OfficialClickHouseDriver.java | 2 +- src/main/java/io/jboot/db/model/CPI.java | 2 +- src/main/java/io/jboot/db/model/Column.java | 2 +- src/main/java/io/jboot/db/model/Columns.java | 2 +- src/main/java/io/jboot/db/model/Group.java | 2 +- src/main/java/io/jboot/db/model/GroupBy.java | 2 +- src/main/java/io/jboot/db/model/Having.java | 2 +- src/main/java/io/jboot/db/model/JbootModel.java | 2 +- src/main/java/io/jboot/db/model/JbootModelConfig.java | 2 +- src/main/java/io/jboot/db/model/JbootModelExts.java | 2 +- src/main/java/io/jboot/db/model/JbootModelFilter.java | 2 +- src/main/java/io/jboot/db/model/JbootModelHintManager.java | 2 +- src/main/java/io/jboot/db/model/Join.java | 2 +- src/main/java/io/jboot/db/model/Joiner.java | 2 +- src/main/java/io/jboot/db/model/Or.java | 2 +- src/main/java/io/jboot/db/model/PrimarykeyValueGenerator.java | 2 +- src/main/java/io/jboot/db/model/SqlBuilder.java | 2 +- src/main/java/io/jboot/db/model/SqlPart.java | 2 +- src/main/java/io/jboot/db/model/Util.java | 2 +- src/main/java/io/jboot/db/record/JbootRecord.java | 2 +- src/main/java/io/jboot/db/record/JbootRecordBuilder.java | 2 +- src/main/java/io/jboot/db/tx/TxEnable.java | 2 +- src/main/java/io/jboot/db/tx/TxEnableInterceptor.java | 2 +- src/main/java/io/jboot/exception/JbootException.java | 2 +- src/main/java/io/jboot/exception/JbootExceptionHolder.java | 2 +- .../java/io/jboot/exception/JbootIllegalConfigException.java | 2 +- src/main/java/io/jboot/exception/JbootRpcException.java | 2 +- src/main/java/io/jboot/ext/MixedByteArrayOutputStream.java | 2 +- src/main/java/io/jboot/objects/counter/JbootCounter.java | 2 +- src/main/java/io/jboot/objects/counter/JbootCounterConfig.java | 2 +- src/main/java/io/jboot/objects/counter/JbootCounterManager.java | 2 +- .../java/io/jboot/objects/counter/impl/JbootLocalCounter.java | 2 +- .../java/io/jboot/objects/counter/impl/JbootRedisCounter.java | 2 +- src/main/java/io/jboot/objects/list/JbootList.java | 2 +- src/main/java/io/jboot/objects/lock/JbootLockConfig.java | 2 +- src/main/java/io/jboot/objects/lock/JbootLockManager.java | 2 +- src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java | 2 +- src/main/java/io/jboot/objects/lock/impl/JbootRedisLock.java | 2 +- src/main/java/io/jboot/objects/map/JbootMap.java | 2 +- src/main/java/io/jboot/objects/map/JbootMapManager.java | 2 +- src/main/java/io/jboot/objects/object/JbootObject.java | 2 +- src/main/java/io/jboot/service/JbootServiceBase.java | 2 +- src/main/java/io/jboot/service/JbootServiceJoiner.java | 2 +- src/main/java/io/jboot/service/JbootServiceJoinerImpl.java | 2 +- src/main/java/io/jboot/support/jwt/JwtConfig.java | 2 +- src/main/java/io/jboot/support/jwt/JwtInterceptor.java | 2 +- src/main/java/io/jboot/support/jwt/JwtInterceptorBuilder.java | 2 +- src/main/java/io/jboot/support/jwt/JwtManager.java | 2 +- .../support/metric/JbootHealthCheckServletContextListener.java | 2 +- src/main/java/io/jboot/support/metric/JbootMetricConfig.java | 2 +- src/main/java/io/jboot/support/metric/JbootMetricManager.java | 2 +- src/main/java/io/jboot/support/metric/JbootMetricReporter.java | 2 +- .../jboot/support/metric/JbootMetricServletContextListener.java | 2 +- src/main/java/io/jboot/support/metric/JvmCpuGaugeSet.java | 2 +- src/main/java/io/jboot/support/metric/JvmGcMetrics.java | 2 +- src/main/java/io/jboot/support/metric/JvmGcUtil.java | 2 +- src/main/java/io/jboot/support/metric/MetricServletHandler.java | 2 +- src/main/java/io/jboot/support/metric/ProcessFilesGaugeSet.java | 2 +- .../support/metric/annotation/EnableMetricConcurrency.java | 2 +- .../io/jboot/support/metric/annotation/EnableMetricCounter.java | 2 +- .../jboot/support/metric/annotation/EnableMetricHistogram.java | 2 +- .../io/jboot/support/metric/annotation/EnableMetricMeter.java | 2 +- .../io/jboot/support/metric/annotation/EnableMetricTimer.java | 2 +- .../metric/interceptor/MetricConcurrencyInterceptor.java | 2 +- .../support/metric/interceptor/MetricCounterInterceptor.java | 2 +- .../support/metric/interceptor/MetricHistogramInterceptor.java | 2 +- .../support/metric/interceptor/MetricInterceptorBuilder.java | 2 +- .../support/metric/interceptor/MetricMeterInterceptor.java | 2 +- .../support/metric/interceptor/MetricTimerInterceptor.java | 2 +- .../support/metric/reporter/console/JbootConsoleReporter.java | 2 +- .../java/io/jboot/support/metric/reporter/csv/CSVReporter.java | 2 +- .../metric/reporter/csv/JbootMetricCVRReporterConfig.java | 2 +- .../metric/reporter/elasticsearch/ElasticsearchReporter.java | 2 +- .../jboot/support/metric/reporter/ganglia/GangliaReporter.java | 2 +- .../support/metric/reporter/graphite/JbootGraphiteReporter.java | 2 +- .../reporter/graphite/JbootMetricGraphiteReporterConfig.java | 2 +- .../support/metric/reporter/influxdb/InfluxdbReporter.java | 2 +- .../reporter/influxdb/JbootMetricInfluxdbReporterConfig.java | 2 +- .../java/io/jboot/support/metric/reporter/jmx/JMXReporter.java | 2 +- .../support/metric/reporter/prometheus/PrometheusExports.java | 2 +- .../support/metric/reporter/prometheus/PrometheusReporter.java | 2 +- .../metric/reporter/prometheus/PrometheusReporterConfig.java | 2 +- .../jboot/support/metric/reporter/slf4j/JbootSlf4jReporter.java | 2 +- .../support/metric/request/AbstractInstrumentedFilter.java | 2 +- .../jboot/support/metric/request/JbootRequestMetricHandler.java | 2 +- src/main/java/io/jboot/support/redis/JbootRedis.java | 2 +- src/main/java/io/jboot/support/redis/JbootRedisBase.java | 2 +- src/main/java/io/jboot/support/redis/JbootRedisConfig.java | 2 +- src/main/java/io/jboot/support/redis/JbootRedisLock.java | 2 +- src/main/java/io/jboot/support/redis/JbootRedisManager.java | 2 +- .../io/jboot/support/redis/jedis/JbootJedisClusterImpl.java | 2 +- src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java | 2 +- .../java/io/jboot/support/redis/lettuce/JbootLettuceCodec.java | 2 +- .../java/io/jboot/support/redis/lettuce/JbootLettuceImpl.java | 2 +- .../java/io/jboot/support/redis/lettuce/LettuceException.java | 2 +- .../java/io/jboot/support/redis/redisson/JbootRedissonImpl.java | 2 +- src/main/java/io/jboot/support/seata/JbootSeataManager.java | 2 +- src/main/java/io/jboot/support/seata/SeataConfig.java | 2 +- .../io/jboot/support/seata/SeataGlobalTransactionManager.java | 2 +- .../java/io/jboot/support/seata/annotation/SeataGlobalLock.java | 2 +- .../support/seata/annotation/SeataGlobalTransactional.java | 2 +- .../support/seata/filter/TransactionPropagationFilter.java | 2 +- .../seata/interceptor/SeataGlobalInterceptorBuilder.java | 2 +- .../seata/interceptor/SeataGlobalTransactionHandler.java | 2 +- .../seata/interceptor/SeataGlobalTransactionalInterceptor.java | 2 +- .../io/jboot/support/seata/tcc/ActionInterceptorHandler.java | 2 +- .../java/io/jboot/support/seata/tcc/TccActionInterceptor.java | 2 +- .../java/io/jboot/support/seata/tcc/TccInterceptorBuilder.java | 2 +- .../java/io/jboot/support/sentinel/JbootSentinelBuilder.java | 2 +- .../java/io/jboot/support/sentinel/JbootSentinelManager.java | 2 +- src/main/java/io/jboot/support/sentinel/SentinelConfig.java | 2 +- src/main/java/io/jboot/support/sentinel/SentinelHandler.java | 2 +- .../java/io/jboot/support/sentinel/SentinelInterceptor.java | 2 +- .../io/jboot/support/sentinel/SentinelInterceptorBuilder.java | 2 +- src/main/java/io/jboot/support/sentinel/SentinelUtil.java | 2 +- .../support/sentinel/datasource/ApolloDatasourceConfig.java | 2 +- .../support/sentinel/datasource/ApolloDatasourceFactory.java | 2 +- .../io/jboot/support/sentinel/datasource/FileDataSource.java | 2 +- .../support/sentinel/datasource/NacosDatasourceConfig.java | 2 +- .../support/sentinel/datasource/NacosDatasourceFactory.java | 2 +- .../support/sentinel/datasource/RedisDatasourceConfig.java | 2 +- .../support/sentinel/datasource/RedisDatasourceFactory.java | 2 +- .../support/sentinel/datasource/SentinelDatasourceFactory.java | 2 +- .../support/sentinel/datasource/ZookeeperDatasourceConfig.java | 2 +- .../support/sentinel/datasource/ZookeeperDatasourceFactory.java | 2 +- src/main/java/io/jboot/support/shiro/JbootShiroConfig.java | 2 +- src/main/java/io/jboot/support/shiro/JbootShiroFilter.java | 2 +- src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java | 2 +- .../java/io/jboot/support/shiro/JbootShiroInvokeListener.java | 2 +- src/main/java/io/jboot/support/shiro/JbootShiroManager.java | 2 +- .../io/jboot/support/shiro/ShiroAuthorizeProcesserInvoker.java | 2 +- .../java/io/jboot/support/shiro/ShiroInterceptorBuilder.java | 2 +- src/main/java/io/jboot/support/shiro/cache/JbootShiroCache.java | 2 +- .../io/jboot/support/shiro/cache/JbootShiroCacheManager.java | 2 +- .../jboot/support/shiro/directives/JbootShiroDirectiveBase.java | 2 +- .../support/shiro/directives/ShiroAuthenticatedDirective.java | 2 +- .../io/jboot/support/shiro/directives/ShiroGuestDirective.java | 2 +- .../shiro/directives/ShiroHasAllPermissionDirective.java | 2 +- .../support/shiro/directives/ShiroHasAllRolesDirective.java | 2 +- .../shiro/directives/ShiroHasAnyPermissionDirective.java | 2 +- .../support/shiro/directives/ShiroHasAnyRolesDirective.java | 2 +- .../support/shiro/directives/ShiroHasPermissionDirective.java | 2 +- .../jboot/support/shiro/directives/ShiroHasRoleDirective.java | 2 +- .../support/shiro/directives/ShiroNoAuthenticatedDirective.java | 2 +- .../shiro/directives/ShiroNotHasPermissionDirective.java | 2 +- .../support/shiro/directives/ShiroNotHasRoleDirective.java | 2 +- .../jboot/support/shiro/directives/ShiroPrincipalDirective.java | 2 +- .../java/io/jboot/support/shiro/processer/AuthorizeResult.java | 2 +- .../jboot/support/shiro/processer/IShiroAuthorizeProcesser.java | 2 +- src/main/java/io/jboot/support/shiro/processer/ShiroClear.java | 2 +- .../shiro/processer/ShiroRequiresAuthenticationProcesser.java | 2 +- .../support/shiro/processer/ShiroRequiresGuestProcesser.java | 2 +- .../shiro/processer/ShiroRequiresPermissionsProcesser.java | 2 +- .../support/shiro/processer/ShiroRequiresRolesProcesser.java | 2 +- .../support/shiro/processer/ShiroRequiresUserProcesser.java | 2 +- .../io/jboot/support/swagger/ControllerReaderExtension.java | 2 +- src/main/java/io/jboot/support/swagger/JbootSwaggerConfig.java | 2 +- .../java/io/jboot/support/swagger/JbootSwaggerController.java | 2 +- src/main/java/io/jboot/support/swagger/JbootSwaggerManager.java | 2 +- src/main/java/io/jboot/support/swagger/Reader.java | 2 +- src/main/java/io/jboot/support/swagger/ReaderContext.java | 2 +- .../java/io/jboot/support/swagger/RefPropertySerializer.java | 2 +- src/main/java/io/jboot/support/swagger/SwaggerPath.java | 2 +- src/main/java/io/jboot/test/CPI.java | 2 +- src/main/java/io/jboot/test/MockApp.java | 2 +- src/main/java/io/jboot/test/MockClass.java | 2 +- src/main/java/io/jboot/test/MockClassInfo.java | 2 +- src/main/java/io/jboot/test/MockExceptions.java | 2 +- src/main/java/io/jboot/test/MockMethod.java | 2 +- src/main/java/io/jboot/test/MockMethodInfo.java | 2 +- src/main/java/io/jboot/test/MockMethodInterceptor.java | 2 +- src/main/java/io/jboot/test/MockMvc.java | 2 +- src/main/java/io/jboot/test/MockMvcAsserter.java | 2 +- src/main/java/io/jboot/test/MockMvcJsonAsserter.java | 2 +- src/main/java/io/jboot/test/MockMvcResult.java | 2 +- src/main/java/io/jboot/test/MockMvcTrueAsserter.java | 2 +- src/main/java/io/jboot/test/MockProxy.java | 2 +- src/main/java/io/jboot/test/TestConfig.java | 2 +- src/main/java/io/jboot/test/junit4/JbootRunner.java | 2 +- src/main/java/io/jboot/test/junit5/JbootExtension.java | 2 +- src/main/java/io/jboot/test/web/MockActionHandler.java | 2 +- src/main/java/io/jboot/test/web/MockCoreConfig.java | 2 +- src/main/java/io/jboot/test/web/MockFilterChain.java | 2 +- src/main/java/io/jboot/test/web/MockFilterConfig.java | 2 +- src/main/java/io/jboot/test/web/MockHttpServletRequest.java | 2 +- src/main/java/io/jboot/test/web/MockHttpServletResponse.java | 2 +- src/main/java/io/jboot/test/web/MockHttpSession.java | 2 +- src/main/java/io/jboot/test/web/MockJFinalFilter.java | 2 +- src/main/java/io/jboot/test/web/MockServletContext.java | 2 +- src/main/java/io/jboot/test/web/MockServletInputStream.java | 2 +- src/main/java/io/jboot/utils/ArrayUtil.java | 2 +- src/main/java/io/jboot/utils/CacheUtil.java | 2 +- src/main/java/io/jboot/utils/ClassScanner.java | 2 +- src/main/java/io/jboot/utils/ClassType.java | 2 +- src/main/java/io/jboot/utils/ClassUtil.java | 2 +- src/main/java/io/jboot/utils/CollectionUtil.java | 2 +- src/main/java/io/jboot/utils/ConfigUtil.java | 2 +- src/main/java/io/jboot/utils/CookieUtil.java | 2 +- src/main/java/io/jboot/utils/DateUtil.java | 2 +- src/main/java/io/jboot/utils/FileScanner.java | 2 +- src/main/java/io/jboot/utils/FileUtil.java | 2 +- src/main/java/io/jboot/utils/HttpUtil.java | 2 +- src/main/java/io/jboot/utils/ModelUtil.java | 2 +- src/main/java/io/jboot/utils/NamedThreadFactory.java | 2 +- src/main/java/io/jboot/utils/NamedThreadPools.java | 2 +- src/main/java/io/jboot/utils/ObjectFunc.java | 2 +- src/main/java/io/jboot/utils/ObjectUtil.java | 2 +- src/main/java/io/jboot/utils/QuietlyUtil.java | 2 +- src/main/java/io/jboot/utils/ReflectUtil.java | 2 +- src/main/java/io/jboot/utils/RequestUtil.java | 2 +- src/main/java/io/jboot/utils/StrUtil.java | 2 +- src/main/java/io/jboot/web/HttpStatus.java | 2 +- src/main/java/io/jboot/web/JbootWebConfig.java | 2 +- src/main/java/io/jboot/web/PathVariableActionMapping.java | 2 +- src/main/java/io/jboot/web/ResponseEntity.java | 2 +- src/main/java/io/jboot/web/attachment/AttachmentContainer.java | 2 +- src/main/java/io/jboot/web/attachment/AttachmentHandler.java | 2 +- src/main/java/io/jboot/web/attachment/AttachmentManager.java | 2 +- .../java/io/jboot/web/attachment/LocalAttachmentContainer.java | 2 +- .../io/jboot/web/attachment/LocalAttachmentContainerConfig.java | 2 +- .../java/io/jboot/web/controller/GetMappingInterceptor.java | 2 +- src/main/java/io/jboot/web/controller/JbootController.java | 2 +- .../java/io/jboot/web/controller/JbootControllerContext.java | 2 +- .../java/io/jboot/web/controller/JbootControllerManager.java | 2 +- .../java/io/jboot/web/controller/PostMappingInterceptor.java | 2 +- .../io/jboot/web/controller/RequestMethodLimitInterceptor.java | 2 +- .../web/controller/RequestMethodLimitInterceptorBuilder.java | 2 +- .../java/io/jboot/web/controller/annotation/DeleteRequest.java | 2 +- .../java/io/jboot/web/controller/annotation/GetMapping.java | 2 +- .../java/io/jboot/web/controller/annotation/GetRequest.java | 2 +- .../java/io/jboot/web/controller/annotation/PatchRequest.java | 2 +- .../java/io/jboot/web/controller/annotation/PostMapping.java | 2 +- .../java/io/jboot/web/controller/annotation/PostRequest.java | 2 +- .../java/io/jboot/web/controller/annotation/PutRequest.java | 2 +- .../java/io/jboot/web/controller/annotation/RequestMapping.java | 2 +- src/main/java/io/jboot/web/cors/CORSInterceptor.java | 2 +- src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java | 2 +- .../java/io/jboot/web/directive/JbootPaginateDirective.java | 2 +- src/main/java/io/jboot/web/directive/SharedEnumObject.java | 2 +- .../java/io/jboot/web/directive/annotation/JFinalDirective.java | 2 +- .../io/jboot/web/directive/annotation/JFinalSharedEnum.java | 2 +- .../io/jboot/web/directive/annotation/JFinalSharedMethod.java | 2 +- .../io/jboot/web/directive/annotation/JFinalSharedObject.java | 2 +- .../web/directive/annotation/JFinalSharedStaticMethod.java | 2 +- .../java/io/jboot/web/directive/base/JbootDirectiveBase.java | 2 +- .../java/io/jboot/web/directive/base/PaginateDirectiveBase.java | 2 +- src/main/java/io/jboot/web/handler/ConsoleColor.java | 2 +- src/main/java/io/jboot/web/handler/JbootActionHandler.java | 2 +- src/main/java/io/jboot/web/handler/JbootActionInvocation.java | 2 +- src/main/java/io/jboot/web/handler/JbootActionReporter.java | 2 +- .../io/jboot/web/handler/JbootActionReporterInvocation.java | 2 +- src/main/java/io/jboot/web/handler/JbootActionReporterUtil.java | 2 +- src/main/java/io/jboot/web/handler/JbootHandler.java | 2 +- .../java/io/jboot/web/handler/PathVariableActionHandler.java | 2 +- src/main/java/io/jboot/web/json/JbootJson.java | 2 +- src/main/java/io/jboot/web/json/JbootJsonConfig.java | 2 +- src/main/java/io/jboot/web/json/JsonBody.java | 2 +- src/main/java/io/jboot/web/json/JsonBodyParseInterceptor.java | 2 +- src/main/java/io/jboot/web/json/JsonIgnore.java | 2 +- src/main/java/io/jboot/web/render/JbootErrorRender.java | 2 +- src/main/java/io/jboot/web/render/JbootHtmlRender.java | 2 +- src/main/java/io/jboot/web/render/JbootJavascriptRender.java | 2 +- src/main/java/io/jboot/web/render/JbootJsonRender.java | 2 +- src/main/java/io/jboot/web/render/JbootRender.java | 2 +- src/main/java/io/jboot/web/render/JbootRenderFactory.java | 2 +- .../java/io/jboot/web/render/JbootResponseEntityRender.java | 2 +- src/main/java/io/jboot/web/render/JbootReturnValueRender.java | 2 +- src/main/java/io/jboot/web/render/JbootTemplateRender.java | 2 +- src/main/java/io/jboot/web/render/JbootTextRender.java | 2 +- src/main/java/io/jboot/web/render/JbootXmlRender.java | 2 +- src/main/java/io/jboot/web/render/cdn/CdnUtil.java | 2 +- src/main/java/io/jboot/web/render/cdn/JbootWebCdnConfig.java | 2 +- src/main/java/io/jboot/web/session/JbootHttpSession.java | 2 +- .../java/io/jboot/web/session/JbootServletRequestWrapper.java | 2 +- src/main/java/io/jboot/web/session/JbootSessionConfig.java | 2 +- src/main/java/io/jboot/web/validate/CaptchaValidate.java | 2 +- src/main/java/io/jboot/web/validate/EmptyValidate.java | 2 +- src/main/java/io/jboot/web/validate/Form.java | 2 +- src/main/java/io/jboot/web/validate/FormType.java | 2 +- src/main/java/io/jboot/web/validate/Regex.java | 2 +- src/main/java/io/jboot/web/validate/RegexForm.java | 2 +- src/main/java/io/jboot/web/validate/RegexValidate.java | 2 +- src/main/java/io/jboot/web/validate/ValidateRenderType.java | 2 +- .../web/validate/interceptor/CaptchaValidateInterceptor.java | 2 +- .../web/validate/interceptor/EmptyValidateInterceptor.java | 2 +- .../web/validate/interceptor/RegexValidateInterceptor.java | 2 +- .../web/validate/interceptor/ValidateInterceptorBuilder.java | 2 +- .../jboot/web/validate/interceptor/ValidateInterceptorUtil.java | 2 +- src/main/java/io/jboot/web/xss/XSSHandler.java | 2 +- .../java/io/jboot/web/xss/XSSHttpServletRequestWrapper.java | 2 +- src/main/java/io/jboot/wechat/JbootWechatConfig.java | 2 +- src/main/java/io/jboot/wechat/WechatApis.java | 2 +- .../java/io/jboot/wechat/controller/JbootWechatController.java | 2 +- .../io/jboot/wechat/interceptor/WechatApiConfigInterceptor.java | 2 +- .../java/io/jboot/wechat/interceptor/WechatUserInterceptor.java | 2 +- src/test/java/io/jboot/test/HelloInterceptor.java | 2 +- src/test/java/io/jboot/test/HelloWorld.java | 2 +- src/test/java/io/jboot/test/aop/AopConfiguration.java | 2 +- src/test/java/io/jboot/test/config/ConfigController.java | 2 +- src/test/java/io/jboot/test/config/TestConfigModel.java | 2 +- src/test/java/io/jboot/test/file/FileController.java | 2 +- src/test/java/io/jboot/test/other/ModelUtilTest.java | 2 +- src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java | 2 +- src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java | 2 +- src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java | 2 +- src/test/java/io/jboot/test/rpc/motan/MotanClient.java | 2 +- src/test/java/io/jboot/test/rpc/motan/MotanInterceptor.java | 2 +- src/test/java/io/jboot/test/rpc/motan/MotanServer.java | 2 +- src/test/java/io/jboot/test/sentinel/SentinelController.java | 2 +- 566 files changed, 566 insertions(+), 566 deletions(-) diff --git a/src/main/java/io/jboot/Jboot.java b/src/main/java/io/jboot/Jboot.java index b31bc4e8..6e2ea6d7 100644 --- a/src/main/java/io/jboot/Jboot.java +++ b/src/main/java/io/jboot/Jboot.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index eb82933a..46b201b3 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/DefaultValueInterceptor.java b/src/main/java/io/jboot/aop/DefaultValueInterceptor.java index f26dff3a..b2d5fd47 100644 --- a/src/main/java/io/jboot/aop/DefaultValueInterceptor.java +++ b/src/main/java/io/jboot/aop/DefaultValueInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/InterceptorBuilder.java b/src/main/java/io/jboot/aop/InterceptorBuilder.java index ac28cd7d..cf202754 100644 --- a/src/main/java/io/jboot/aop/InterceptorBuilder.java +++ b/src/main/java/io/jboot/aop/InterceptorBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java index 408ec958..83a777fa 100644 --- a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java +++ b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/InterceptorCache.java b/src/main/java/io/jboot/aop/InterceptorCache.java index 7cb28d83..79efd8f2 100644 --- a/src/main/java/io/jboot/aop/InterceptorCache.java +++ b/src/main/java/io/jboot/aop/InterceptorCache.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/Interceptors.java b/src/main/java/io/jboot/aop/Interceptors.java index 61fd6599..feaa9199 100644 --- a/src/main/java/io/jboot/aop/Interceptors.java +++ b/src/main/java/io/jboot/aop/Interceptors.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index cd8b7f31..a28014cb 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/JbootLazyLoader.java b/src/main/java/io/jboot/aop/JbootLazyLoader.java index f42a5ac9..1227de04 100644 --- a/src/main/java/io/jboot/aop/JbootLazyLoader.java +++ b/src/main/java/io/jboot/aop/JbootLazyLoader.java @@ -2,7 +2,7 @@ package io.jboot.aop; import java.lang.reflect.Field; /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/JbootLazyLoaderFactory.java b/src/main/java/io/jboot/aop/JbootLazyLoaderFactory.java index 9808ccb5..f1db0cce 100644 --- a/src/main/java/io/jboot/aop/JbootLazyLoaderFactory.java +++ b/src/main/java/io/jboot/aop/JbootLazyLoaderFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/ValueFilter.java b/src/main/java/io/jboot/aop/ValueFilter.java index 946fff1a..e8194016 100644 --- a/src/main/java/io/jboot/aop/ValueFilter.java +++ b/src/main/java/io/jboot/aop/ValueFilter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/ValueFilterInterceptor.java b/src/main/java/io/jboot/aop/ValueFilterInterceptor.java index d4ea9609..79ce1385 100644 --- a/src/main/java/io/jboot/aop/ValueFilterInterceptor.java +++ b/src/main/java/io/jboot/aop/ValueFilterInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/annotation/AutoLoad.java b/src/main/java/io/jboot/aop/annotation/AutoLoad.java index 6b968364..ae298c35 100644 --- a/src/main/java/io/jboot/aop/annotation/AutoLoad.java +++ b/src/main/java/io/jboot/aop/annotation/AutoLoad.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/annotation/Bean.java b/src/main/java/io/jboot/aop/annotation/Bean.java index 00ee3ae6..c9bde5bd 100644 --- a/src/main/java/io/jboot/aop/annotation/Bean.java +++ b/src/main/java/io/jboot/aop/annotation/Bean.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/annotation/BeanExclude.java b/src/main/java/io/jboot/aop/annotation/BeanExclude.java index 8b23eb7f..bb028e3b 100644 --- a/src/main/java/io/jboot/aop/annotation/BeanExclude.java +++ b/src/main/java/io/jboot/aop/annotation/BeanExclude.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/annotation/ConfigValue.java b/src/main/java/io/jboot/aop/annotation/ConfigValue.java index aa0261a2..45ba1f00 100644 --- a/src/main/java/io/jboot/aop/annotation/ConfigValue.java +++ b/src/main/java/io/jboot/aop/annotation/ConfigValue.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/annotation/Configuration.java b/src/main/java/io/jboot/aop/annotation/Configuration.java index 49bd37e2..bf96657d 100644 --- a/src/main/java/io/jboot/aop/annotation/Configuration.java +++ b/src/main/java/io/jboot/aop/annotation/Configuration.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/annotation/DefaultValue.java b/src/main/java/io/jboot/aop/annotation/DefaultValue.java index 192cbb06..209498a1 100644 --- a/src/main/java/io/jboot/aop/annotation/DefaultValue.java +++ b/src/main/java/io/jboot/aop/annotation/DefaultValue.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/annotation/FilterBy.java b/src/main/java/io/jboot/aop/annotation/FilterBy.java index 02f9c3b0..eb08fbce 100644 --- a/src/main/java/io/jboot/aop/annotation/FilterBy.java +++ b/src/main/java/io/jboot/aop/annotation/FilterBy.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/annotation/Lazy.java b/src/main/java/io/jboot/aop/annotation/Lazy.java index 4e0b2bc3..6eb59598 100644 --- a/src/main/java/io/jboot/aop/annotation/Lazy.java +++ b/src/main/java/io/jboot/aop/annotation/Lazy.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/annotation/StaticConstruct.java b/src/main/java/io/jboot/aop/annotation/StaticConstruct.java index 5353a406..800d05ee 100644 --- a/src/main/java/io/jboot/aop/annotation/StaticConstruct.java +++ b/src/main/java/io/jboot/aop/annotation/StaticConstruct.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java index ed6f7597..44530436 100644 --- a/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibCallback.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibLazyLoader.java b/src/main/java/io/jboot/aop/cglib/JbootCglibLazyLoader.java index af8b2166..55a4a1bd 100644 --- a/src/main/java/io/jboot/aop/cglib/JbootCglibLazyLoader.java +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibLazyLoader.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java b/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java index 45cff5e2..eb18aba0 100644 --- a/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java +++ b/src/main/java/io/jboot/aop/cglib/JbootCglibProxyFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/javassist/JbootJavassistHandler.java b/src/main/java/io/jboot/aop/javassist/JbootJavassistHandler.java index be9873ae..4bd7d53f 100644 --- a/src/main/java/io/jboot/aop/javassist/JbootJavassistHandler.java +++ b/src/main/java/io/jboot/aop/javassist/JbootJavassistHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/javassist/JbootJavassistProxyFactory.java b/src/main/java/io/jboot/aop/javassist/JbootJavassistProxyFactory.java index 752e2050..f9c69595 100644 --- a/src/main/java/io/jboot/aop/javassist/JbootJavassistProxyFactory.java +++ b/src/main/java/io/jboot/aop/javassist/JbootJavassistProxyFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/jfinal/JfinalHandlers.java b/src/main/java/io/jboot/aop/jfinal/JfinalHandlers.java index a9cf3f5a..5b8bc273 100644 --- a/src/main/java/io/jboot/aop/jfinal/JfinalHandlers.java +++ b/src/main/java/io/jboot/aop/jfinal/JfinalHandlers.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/aop/jfinal/JfinalPlugins.java b/src/main/java/io/jboot/aop/jfinal/JfinalPlugins.java index 52385b9e..8e55bd19 100644 --- a/src/main/java/io/jboot/aop/jfinal/JfinalPlugins.java +++ b/src/main/java/io/jboot/aop/jfinal/JfinalPlugins.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/ApiDocConfig.java b/src/main/java/io/jboot/apidoc/ApiDocConfig.java index 915cbe60..eeebf167 100644 --- a/src/main/java/io/jboot/apidoc/ApiDocConfig.java +++ b/src/main/java/io/jboot/apidoc/ApiDocConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/ApiDocManager.java b/src/main/java/io/jboot/apidoc/ApiDocManager.java index e643c2cb..44d8b9b7 100644 --- a/src/main/java/io/jboot/apidoc/ApiDocManager.java +++ b/src/main/java/io/jboot/apidoc/ApiDocManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/ApiDocRender.java b/src/main/java/io/jboot/apidoc/ApiDocRender.java index 07b734e2..1d171a3d 100644 --- a/src/main/java/io/jboot/apidoc/ApiDocRender.java +++ b/src/main/java/io/jboot/apidoc/ApiDocRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/ApiDocUtil.java b/src/main/java/io/jboot/apidoc/ApiDocUtil.java index 287e82a2..733ac18d 100644 --- a/src/main/java/io/jboot/apidoc/ApiDocUtil.java +++ b/src/main/java/io/jboot/apidoc/ApiDocUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/ApiDocument.java b/src/main/java/io/jboot/apidoc/ApiDocument.java index 2f331c90..c25f55ca 100644 --- a/src/main/java/io/jboot/apidoc/ApiDocument.java +++ b/src/main/java/io/jboot/apidoc/ApiDocument.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/ApiJsonGenerator.java b/src/main/java/io/jboot/apidoc/ApiJsonGenerator.java index bff8c134..a2ba83ae 100644 --- a/src/main/java/io/jboot/apidoc/ApiJsonGenerator.java +++ b/src/main/java/io/jboot/apidoc/ApiJsonGenerator.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/ApiMockBuilder.java b/src/main/java/io/jboot/apidoc/ApiMockBuilder.java index 8cd7aae3..5ae6b9b6 100644 --- a/src/main/java/io/jboot/apidoc/ApiMockBuilder.java +++ b/src/main/java/io/jboot/apidoc/ApiMockBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/ApiMockBuilders.java b/src/main/java/io/jboot/apidoc/ApiMockBuilders.java index cf958c65..3e856bb9 100644 --- a/src/main/java/io/jboot/apidoc/ApiMockBuilders.java +++ b/src/main/java/io/jboot/apidoc/ApiMockBuilders.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/ApiOperation.java b/src/main/java/io/jboot/apidoc/ApiOperation.java index f1fee2d2..a603239d 100644 --- a/src/main/java/io/jboot/apidoc/ApiOperation.java +++ b/src/main/java/io/jboot/apidoc/ApiOperation.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/ApiParameter.java b/src/main/java/io/jboot/apidoc/ApiParameter.java index 6093e21a..2591ca83 100644 --- a/src/main/java/io/jboot/apidoc/ApiParameter.java +++ b/src/main/java/io/jboot/apidoc/ApiParameter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/ApiResponse.java b/src/main/java/io/jboot/apidoc/ApiResponse.java index 5527becb..c95c5493 100644 --- a/src/main/java/io/jboot/apidoc/ApiResponse.java +++ b/src/main/java/io/jboot/apidoc/ApiResponse.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/ContentType.java b/src/main/java/io/jboot/apidoc/ContentType.java index 1abd9d64..69b02651 100644 --- a/src/main/java/io/jboot/apidoc/ContentType.java +++ b/src/main/java/io/jboot/apidoc/ContentType.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/HttpMethod.java b/src/main/java/io/jboot/apidoc/HttpMethod.java index 55bd36d7..7fbdec8a 100644 --- a/src/main/java/io/jboot/apidoc/HttpMethod.java +++ b/src/main/java/io/jboot/apidoc/HttpMethod.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/annotation/Api.java b/src/main/java/io/jboot/apidoc/annotation/Api.java index d11278a7..1d5c9826 100644 --- a/src/main/java/io/jboot/apidoc/annotation/Api.java +++ b/src/main/java/io/jboot/apidoc/annotation/Api.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/annotation/ApiOper.java b/src/main/java/io/jboot/apidoc/annotation/ApiOper.java index d7de5ef2..42935b10 100644 --- a/src/main/java/io/jboot/apidoc/annotation/ApiOper.java +++ b/src/main/java/io/jboot/apidoc/annotation/ApiOper.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/annotation/ApiPara.java b/src/main/java/io/jboot/apidoc/annotation/ApiPara.java index a5580cac..300e3403 100644 --- a/src/main/java/io/jboot/apidoc/annotation/ApiPara.java +++ b/src/main/java/io/jboot/apidoc/annotation/ApiPara.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/annotation/ApiParas.java b/src/main/java/io/jboot/apidoc/annotation/ApiParas.java index 3425b9a7..2c455d44 100644 --- a/src/main/java/io/jboot/apidoc/annotation/ApiParas.java +++ b/src/main/java/io/jboot/apidoc/annotation/ApiParas.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/annotation/ApiResp.java b/src/main/java/io/jboot/apidoc/annotation/ApiResp.java index 0744dd49..0f7fb09d 100644 --- a/src/main/java/io/jboot/apidoc/annotation/ApiResp.java +++ b/src/main/java/io/jboot/apidoc/annotation/ApiResp.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/apidoc/annotation/ApiResps.java b/src/main/java/io/jboot/apidoc/annotation/ApiResps.java index a7aa207b..b4a3eca3 100644 --- a/src/main/java/io/jboot/apidoc/annotation/ApiResps.java +++ b/src/main/java/io/jboot/apidoc/annotation/ApiResps.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/ApplicationUtil.java b/src/main/java/io/jboot/app/ApplicationUtil.java index 7919742a..9b10362a 100644 --- a/src/main/java/io/jboot/app/ApplicationUtil.java +++ b/src/main/java/io/jboot/app/ApplicationUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/Banner.java b/src/main/java/io/jboot/app/Banner.java index 43d8b973..2b13d654 100644 --- a/src/main/java/io/jboot/app/Banner.java +++ b/src/main/java/io/jboot/app/Banner.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/HttpContentTypes.java b/src/main/java/io/jboot/app/HttpContentTypes.java index 28dbdf51..6b94b360 100644 --- a/src/main/java/io/jboot/app/HttpContentTypes.java +++ b/src/main/java/io/jboot/app/HttpContentTypes.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/JbootApplication.java b/src/main/java/io/jboot/app/JbootApplication.java index 42064414..96e70f81 100644 --- a/src/main/java/io/jboot/app/JbootApplication.java +++ b/src/main/java/io/jboot/app/JbootApplication.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/JbootApplicationConfig.java b/src/main/java/io/jboot/app/JbootApplicationConfig.java index 004c41fe..5cea9f1b6 100644 --- a/src/main/java/io/jboot/app/JbootApplicationConfig.java +++ b/src/main/java/io/jboot/app/JbootApplicationConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/JbootResourceLoader.java b/src/main/java/io/jboot/app/JbootResourceLoader.java index 29a19f8f..a3b018b2 100644 --- a/src/main/java/io/jboot/app/JbootResourceLoader.java +++ b/src/main/java/io/jboot/app/JbootResourceLoader.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/JbootSimpleApplication.java b/src/main/java/io/jboot/app/JbootSimpleApplication.java index 8c6815c8..f5bacdd8 100644 --- a/src/main/java/io/jboot/app/JbootSimpleApplication.java +++ b/src/main/java/io/jboot/app/JbootSimpleApplication.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/JbootWebBuilderConfiger.java b/src/main/java/io/jboot/app/JbootWebBuilderConfiger.java index 67e99a14..2b999f3f 100644 --- a/src/main/java/io/jboot/app/JbootWebBuilderConfiger.java +++ b/src/main/java/io/jboot/app/JbootWebBuilderConfiger.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/config/ConfigPara.java b/src/main/java/io/jboot/app/config/ConfigPara.java index c4417b2f..9e5721cf 100644 --- a/src/main/java/io/jboot/app/config/ConfigPara.java +++ b/src/main/java/io/jboot/app/config/ConfigPara.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/config/JbootConfigChangeListener.java b/src/main/java/io/jboot/app/config/JbootConfigChangeListener.java index 4c5681aa..6d98aae9 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigChangeListener.java +++ b/src/main/java/io/jboot/app/config/JbootConfigChangeListener.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/config/JbootConfigDecryptor.java b/src/main/java/io/jboot/app/config/JbootConfigDecryptor.java index ef03ca3e..20592487 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigDecryptor.java +++ b/src/main/java/io/jboot/app/config/JbootConfigDecryptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/config/JbootConfigKit.java b/src/main/java/io/jboot/app/config/JbootConfigKit.java index 0a5d80ba..f2c08628 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigKit.java +++ b/src/main/java/io/jboot/app/config/JbootConfigKit.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 28d710d0..cf48f80f 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/config/JbootProp.java b/src/main/java/io/jboot/app/config/JbootProp.java index 6fd23b7a..4b98441d 100644 --- a/src/main/java/io/jboot/app/config/JbootProp.java +++ b/src/main/java/io/jboot/app/config/JbootProp.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/config/annotation/ConfigModel.java b/src/main/java/io/jboot/app/config/annotation/ConfigModel.java index c8347275..720f7e40 100644 --- a/src/main/java/io/jboot/app/config/annotation/ConfigModel.java +++ b/src/main/java/io/jboot/app/config/annotation/ConfigModel.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java b/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java index ae019677..37840d58 100644 --- a/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java b/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java index 2dd46e3e..c4f36456 100644 --- a/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java +++ b/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java index fe9b7d4d..99ab39fe 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java index 20d1416b..702c33dc 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java b/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java index 65cc9d73..39fc58a9 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosServerConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/undertow/JbootHotSwapResolver.java b/src/main/java/io/jboot/app/undertow/JbootHotSwapResolver.java index cb79eb19..cdb9dad6 100644 --- a/src/main/java/io/jboot/app/undertow/JbootHotSwapResolver.java +++ b/src/main/java/io/jboot/app/undertow/JbootHotSwapResolver.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java b/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java index 28623123..7c1ac109 100644 --- a/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java +++ b/src/main/java/io/jboot/app/undertow/JbootUndertowConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/app/undertow/JbootUndertowServer.java b/src/main/java/io/jboot/app/undertow/JbootUndertowServer.java index 0b01136a..623de19c 100644 --- a/src/main/java/io/jboot/app/undertow/JbootUndertowServer.java +++ b/src/main/java/io/jboot/app/undertow/JbootUndertowServer.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/codegen/CodeGenHelpler.java b/src/main/java/io/jboot/codegen/CodeGenHelpler.java index cc5d934a..98ab5b88 100644 --- a/src/main/java/io/jboot/codegen/CodeGenHelpler.java +++ b/src/main/java/io/jboot/codegen/CodeGenHelpler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/codegen/GenTester.java b/src/main/java/io/jboot/codegen/GenTester.java index bcc2d00f..4fab6994 100644 --- a/src/main/java/io/jboot/codegen/GenTester.java +++ b/src/main/java/io/jboot/codegen/GenTester.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/codegen/model/JbootBaseModelGenerator.java b/src/main/java/io/jboot/codegen/model/JbootBaseModelGenerator.java index e7493f92..f54d0bc2 100644 --- a/src/main/java/io/jboot/codegen/model/JbootBaseModelGenerator.java +++ b/src/main/java/io/jboot/codegen/model/JbootBaseModelGenerator.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/codegen/model/JbootModelGenerator.java b/src/main/java/io/jboot/codegen/model/JbootModelGenerator.java index 5aec8459..41a95958 100644 --- a/src/main/java/io/jboot/codegen/model/JbootModelGenerator.java +++ b/src/main/java/io/jboot/codegen/model/JbootModelGenerator.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/codegen/model/ModeGenTester.java b/src/main/java/io/jboot/codegen/model/ModeGenTester.java index deee3829..e1ab2ba9 100644 --- a/src/main/java/io/jboot/codegen/model/ModeGenTester.java +++ b/src/main/java/io/jboot/codegen/model/ModeGenTester.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java b/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java index 76b579e2..9bed6242 100644 --- a/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java +++ b/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/codegen/service/JbootServiceInterfaceGenerator.java b/src/main/java/io/jboot/codegen/service/JbootServiceInterfaceGenerator.java index 6864ebe8..87771e2f 100644 --- a/src/main/java/io/jboot/codegen/service/JbootServiceInterfaceGenerator.java +++ b/src/main/java/io/jboot/codegen/service/JbootServiceInterfaceGenerator.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/codegen/service/ServiceGenTester.java b/src/main/java/io/jboot/codegen/service/ServiceGenTester.java index faabaee8..aba88b8b 100644 --- a/src/main/java/io/jboot/codegen/service/ServiceGenTester.java +++ b/src/main/java/io/jboot/codegen/service/ServiceGenTester.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/CacheTime.java b/src/main/java/io/jboot/components/cache/CacheTime.java index c959c976..8a93723e 100644 --- a/src/main/java/io/jboot/components/cache/CacheTime.java +++ b/src/main/java/io/jboot/components/cache/CacheTime.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java b/src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java index a8b67d8a..fa9ae221 100644 --- a/src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/JbootCache.java b/src/main/java/io/jboot/components/cache/JbootCache.java index 50861ac5..f98d5867 100644 --- a/src/main/java/io/jboot/components/cache/JbootCache.java +++ b/src/main/java/io/jboot/components/cache/JbootCache.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/JbootCacheBase.java b/src/main/java/io/jboot/components/cache/JbootCacheBase.java index b6b183eb..11194d55 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheBase.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheBase.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java index a24d1888..de8a49fe 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/JbootCacheManager.java b/src/main/java/io/jboot/components/cache/JbootCacheManager.java index 232e88ed..866cabda 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheManager.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/annotation/CacheEvict.java b/src/main/java/io/jboot/components/cache/annotation/CacheEvict.java index 416358b9..78da5da5 100644 --- a/src/main/java/io/jboot/components/cache/annotation/CacheEvict.java +++ b/src/main/java/io/jboot/components/cache/annotation/CacheEvict.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/annotation/CachePut.java b/src/main/java/io/jboot/components/cache/annotation/CachePut.java index 3731ff24..35f7dd80 100644 --- a/src/main/java/io/jboot/components/cache/annotation/CachePut.java +++ b/src/main/java/io/jboot/components/cache/annotation/CachePut.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/annotation/Cacheable.java b/src/main/java/io/jboot/components/cache/annotation/Cacheable.java index 3aad4efd..63cc1115 100644 --- a/src/main/java/io/jboot/components/cache/annotation/Cacheable.java +++ b/src/main/java/io/jboot/components/cache/annotation/Cacheable.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/annotation/CachesEvict.java b/src/main/java/io/jboot/components/cache/annotation/CachesEvict.java index ec1d37a5..edc4d9dd 100644 --- a/src/main/java/io/jboot/components/cache/annotation/CachesEvict.java +++ b/src/main/java/io/jboot/components/cache/annotation/CachesEvict.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheBuilder.java b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheBuilder.java index 421fd81c..6a0db104 100644 --- a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheBuilder.java +++ b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java index dd97528a..703b2c4a 100644 --- a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheObject.java b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheObject.java index 723ecce6..3e11d8e0 100644 --- a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheObject.java +++ b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheObject.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/caffeine/DefaultCaffeineCacheBuilder.java b/src/main/java/io/jboot/components/cache/caffeine/DefaultCaffeineCacheBuilder.java index 25270e1e..51df59d2 100644 --- a/src/main/java/io/jboot/components/cache/caffeine/DefaultCaffeineCacheBuilder.java +++ b/src/main/java/io/jboot/components/cache/caffeine/DefaultCaffeineCacheBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java index 7c8a1f68..3f4af435 100644 --- a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisMessage.java b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisMessage.java index c1f3cef2..6422e578 100644 --- a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisMessage.java +++ b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisMessage.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/ehcache/JbootEhCacheConfig.java b/src/main/java/io/jboot/components/cache/ehcache/JbootEhCacheConfig.java index 8067dc8a..afa5a4c8 100644 --- a/src/main/java/io/jboot/components/cache/ehcache/JbootEhCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/ehcache/JbootEhCacheConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java index 25cc7721..6c405de0 100644 --- a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java index 9d886d8e..ffd38f5a 100644 --- a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisMessage.java b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisMessage.java index 8f41e046..1630b27c 100644 --- a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisMessage.java +++ b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisMessage.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheEvictInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheEvictInterceptor.java index 50889a35..0ea61090 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheEvictInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheEvictInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java b/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java index 62bf4aa5..da004a92 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java index 29f0eb6d..f828fe24 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java index 8a2ece5b..c25e13be 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/interceptor/CachesEvictInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CachesEvictInterceptor.java index fdcd2284..5d4c0928 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CachesEvictInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CachesEvictInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/interceptor/Utils.java b/src/main/java/io/jboot/components/cache/interceptor/Utils.java index 390cf086..d2d71647 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/Utils.java +++ b/src/main/java/io/jboot/components/cache/interceptor/Utils.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java index 8fc07302..7e13a9ed 100644 --- a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java +++ b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java index ec8e8842..4e3c94ef 100644 --- a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheConfig.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheConfig.java index 25913474..05ad4d43 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java index 342219a2..b56b6ff8 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/event/JbootEvent.java b/src/main/java/io/jboot/components/event/JbootEvent.java index 5569b375..960d68ff 100644 --- a/src/main/java/io/jboot/components/event/JbootEvent.java +++ b/src/main/java/io/jboot/components/event/JbootEvent.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/event/JbootEventListener.java b/src/main/java/io/jboot/components/event/JbootEventListener.java index 3751272a..3195c645 100644 --- a/src/main/java/io/jboot/components/event/JbootEventListener.java +++ b/src/main/java/io/jboot/components/event/JbootEventListener.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/event/JbootEventManager.java b/src/main/java/io/jboot/components/event/JbootEventManager.java index d350b182..1603331f 100644 --- a/src/main/java/io/jboot/components/event/JbootEventManager.java +++ b/src/main/java/io/jboot/components/event/JbootEventManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/event/annotation/EventConfig.java b/src/main/java/io/jboot/components/event/annotation/EventConfig.java index 2dc13955..9266631c 100644 --- a/src/main/java/io/jboot/components/event/annotation/EventConfig.java +++ b/src/main/java/io/jboot/components/event/annotation/EventConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/GatewayErrorRender.java b/src/main/java/io/jboot/components/gateway/GatewayErrorRender.java index 0312c94a..b4408320 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayErrorRender.java +++ b/src/main/java/io/jboot/components/gateway/GatewayErrorRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index f3e0923f..7b959cdc 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/GatewayInterceptor.java b/src/main/java/io/jboot/components/gateway/GatewayInterceptor.java index f6deacc1..289545cc 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayInterceptor.java +++ b/src/main/java/io/jboot/components/gateway/GatewayInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java index 3304bef3..b87abd83 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayInvocation.java +++ b/src/main/java/io/jboot/components/gateway/GatewayInvocation.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/GatewayLoadBalanceStrategy.java b/src/main/java/io/jboot/components/gateway/GatewayLoadBalanceStrategy.java index 8be6f30e..2f8b7142 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayLoadBalanceStrategy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayLoadBalanceStrategy.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java b/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java index 7c3baf30..d2f20d35 100644 --- a/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java +++ b/src/main/java/io/jboot/components/gateway/GatewaySentinelProcesser.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index ac8691af..502b395c 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java index c9cf4f09..9439b4e1 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayHealthChecker.java b/src/main/java/io/jboot/components/gateway/JbootGatewayHealthChecker.java index c5d08bac..6d2926d8 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayHealthChecker.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayHealthChecker.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java index 7c141b91..977cf19b 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscovery.java b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscovery.java index 8e139c3a..1389e827 100644 --- a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscovery.java +++ b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscovery.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryConfig.java b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryConfig.java index 2fb53c7e..201fe546 100644 --- a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryConfig.java +++ b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryListener.java b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryListener.java index 5eae6246..b53b37e7 100644 --- a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryListener.java +++ b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryListener.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryManager.java b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryManager.java index 498229f0..06cd87c6 100644 --- a/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryManager.java +++ b/src/main/java/io/jboot/components/gateway/discovery/GatewayDiscoveryManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/discovery/GatewayInstance.java b/src/main/java/io/jboot/components/gateway/discovery/GatewayInstance.java index 685cf831..1a809008 100644 --- a/src/main/java/io/jboot/components/gateway/discovery/GatewayInstance.java +++ b/src/main/java/io/jboot/components/gateway/discovery/GatewayInstance.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/discovery/GatewayInstanceConfig.java b/src/main/java/io/jboot/components/gateway/discovery/GatewayInstanceConfig.java index a72e30a3..285027a2 100644 --- a/src/main/java/io/jboot/components/gateway/discovery/GatewayInstanceConfig.java +++ b/src/main/java/io/jboot/components/gateway/discovery/GatewayInstanceConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/gateway/discovery/NacosGatewayDiscovery.java b/src/main/java/io/jboot/components/gateway/discovery/NacosGatewayDiscovery.java index f00b5386..3088dcbc 100644 --- a/src/main/java/io/jboot/components/gateway/discovery/NacosGatewayDiscovery.java +++ b/src/main/java/io/jboot/components/gateway/discovery/NacosGatewayDiscovery.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/http/HttpProxyInfo.java b/src/main/java/io/jboot/components/http/HttpProxyInfo.java index cd39fe7b..8d60fe7a 100644 --- a/src/main/java/io/jboot/components/http/HttpProxyInfo.java +++ b/src/main/java/io/jboot/components/http/HttpProxyInfo.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/http/JbootHttp.java b/src/main/java/io/jboot/components/http/JbootHttp.java index 94781bdc..9c5a67ec 100644 --- a/src/main/java/io/jboot/components/http/JbootHttp.java +++ b/src/main/java/io/jboot/components/http/JbootHttp.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/http/JbootHttpConfig.java b/src/main/java/io/jboot/components/http/JbootHttpConfig.java index b9343dee..d6a3d0ff 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpConfig.java +++ b/src/main/java/io/jboot/components/http/JbootHttpConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/http/JbootHttpManager.java b/src/main/java/io/jboot/components/http/JbootHttpManager.java index a1d72185..5bda73f2 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpManager.java +++ b/src/main/java/io/jboot/components/http/JbootHttpManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/http/JbootHttpRequest.java b/src/main/java/io/jboot/components/http/JbootHttpRequest.java index 50b621fa..889f124f 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpRequest.java +++ b/src/main/java/io/jboot/components/http/JbootHttpRequest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/http/JbootHttpResponse.java b/src/main/java/io/jboot/components/http/JbootHttpResponse.java index 98359758..8149f8ee 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpResponse.java +++ b/src/main/java/io/jboot/components/http/JbootHttpResponse.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index bc562450..57c53790 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java index 3edbe4b0..1b09e28d 100644 --- a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java +++ b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/limiter/LimitConfig.java b/src/main/java/io/jboot/components/limiter/LimitConfig.java index 0256c660..5a4b9f01 100644 --- a/src/main/java/io/jboot/components/limiter/LimitConfig.java +++ b/src/main/java/io/jboot/components/limiter/LimitConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/limiter/LimitFallbackProcesser.java b/src/main/java/io/jboot/components/limiter/LimitFallbackProcesser.java index eed151a7..fc7dc389 100644 --- a/src/main/java/io/jboot/components/limiter/LimitFallbackProcesser.java +++ b/src/main/java/io/jboot/components/limiter/LimitFallbackProcesser.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/limiter/LimitFallbackProcesserDefault.java b/src/main/java/io/jboot/components/limiter/LimitFallbackProcesserDefault.java index f9760b67..02a7bddb 100644 --- a/src/main/java/io/jboot/components/limiter/LimitFallbackProcesserDefault.java +++ b/src/main/java/io/jboot/components/limiter/LimitFallbackProcesserDefault.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/limiter/LimitInfo.java b/src/main/java/io/jboot/components/limiter/LimitInfo.java index 99e165d7..6de9ce89 100644 --- a/src/main/java/io/jboot/components/limiter/LimitInfo.java +++ b/src/main/java/io/jboot/components/limiter/LimitInfo.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/limiter/LimitScope.java b/src/main/java/io/jboot/components/limiter/LimitScope.java index ae8fed0c..2ce267f6 100644 --- a/src/main/java/io/jboot/components/limiter/LimitScope.java +++ b/src/main/java/io/jboot/components/limiter/LimitScope.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/limiter/LimitType.java b/src/main/java/io/jboot/components/limiter/LimitType.java index 6ce618d1..ceee39cc 100644 --- a/src/main/java/io/jboot/components/limiter/LimitType.java +++ b/src/main/java/io/jboot/components/limiter/LimitType.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/limiter/LimiterManager.java b/src/main/java/io/jboot/components/limiter/LimiterManager.java index 7e998d76..2369ee01 100644 --- a/src/main/java/io/jboot/components/limiter/LimiterManager.java +++ b/src/main/java/io/jboot/components/limiter/LimiterManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/limiter/annotation/EnableLimit.java b/src/main/java/io/jboot/components/limiter/annotation/EnableLimit.java index 248069a1..e8708fbf 100644 --- a/src/main/java/io/jboot/components/limiter/annotation/EnableLimit.java +++ b/src/main/java/io/jboot/components/limiter/annotation/EnableLimit.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java index 2ab9177d..cdb8de52 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/BaseLimiterInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java index 43e113bf..e1d01a47 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterGlobalInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java index 2c5a4660..ea9f3088 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java index c60159b9..9428b0bc 100644 --- a/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java +++ b/src/main/java/io/jboot/components/limiter/interceptor/LimiterInterceptorBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java b/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java index 2850d171..8e07a706 100644 --- a/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java +++ b/src/main/java/io/jboot/components/limiter/redis/RedisRateLimitUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/Jbootmq.java b/src/main/java/io/jboot/components/mq/Jbootmq.java index 1943c7db..eaa17c4d 100644 --- a/src/main/java/io/jboot/components/mq/Jbootmq.java +++ b/src/main/java/io/jboot/components/mq/Jbootmq.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index ba0e197e..23e13a45 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/JbootmqConfig.java b/src/main/java/io/jboot/components/mq/JbootmqConfig.java index 6fd4e72b..a11102f0 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqConfig.java +++ b/src/main/java/io/jboot/components/mq/JbootmqConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/JbootmqManager.java b/src/main/java/io/jboot/components/mq/JbootmqManager.java index 386e2ea6..4eaa886e 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqManager.java +++ b/src/main/java/io/jboot/components/mq/JbootmqManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java b/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java index d29084ef..0775047a 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java +++ b/src/main/java/io/jboot/components/mq/JbootmqMessageListener.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/activemq/JbootActivemq.java b/src/main/java/io/jboot/components/mq/activemq/JbootActivemq.java index a62ecadf..10496317 100644 --- a/src/main/java/io/jboot/components/mq/activemq/JbootActivemq.java +++ b/src/main/java/io/jboot/components/mq/activemq/JbootActivemq.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqConfig.java b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqConfig.java index a89b7e62..75e62455 100644 --- a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqConfig.java +++ b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java index 9be68f16..17e15efc 100644 --- a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java +++ b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java b/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java index 2059026d..8b4aab64 100644 --- a/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java +++ b/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqConfig.java b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqConfig.java index 6b1f35ac..921a8817 100644 --- a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqConfig.java +++ b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java index 6eaf95d2..95309472 100644 --- a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java +++ b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java index f279765d..6a907291 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index 243d1efa..0626c249 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqConfig.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqConfig.java index 1fbbe72d..cdca3591 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqConfig.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java index e73c1dee..d469bf66 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqConfig.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqConfig.java index 51d9749c..40d8bc58 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqConfig.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java index 42789a78..9e198d6b 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/Jbootrpc.java b/src/main/java/io/jboot/components/rpc/Jbootrpc.java index 0e1d697f..c642191d 100644 --- a/src/main/java/io/jboot/components/rpc/Jbootrpc.java +++ b/src/main/java/io/jboot/components/rpc/Jbootrpc.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java index 254d2c86..3d112b6b 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java index 9e8577dd..1df540cd 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java index 40a3c05e..5720b3c4 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcManager.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java index 42e05828..421adadb 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java index 8a06959b..e256944a 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcServiceConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/RPCUtil.java b/src/main/java/io/jboot/components/rpc/RPCUtil.java index a47a9b18..ed2c2589 100644 --- a/src/main/java/io/jboot/components/rpc/RPCUtil.java +++ b/src/main/java/io/jboot/components/rpc/RPCUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java b/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java index 0841d8fd..2b026425 100644 --- a/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java +++ b/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/annotation/RPCBean.java b/src/main/java/io/jboot/components/rpc/annotation/RPCBean.java index 9e81a184..6d597c53 100644 --- a/src/main/java/io/jboot/components/rpc/annotation/RPCBean.java +++ b/src/main/java/io/jboot/components/rpc/annotation/RPCBean.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/annotation/RPCInject.java b/src/main/java/io/jboot/components/rpc/annotation/RPCInject.java index d189d294..b95344e1 100644 --- a/src/main/java/io/jboot/components/rpc/annotation/RPCInject.java +++ b/src/main/java/io/jboot/components/rpc/annotation/RPCInject.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java index 45c9d685..66b0dfb8 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/DubboUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java index 4569411f..52ee88de 100644 --- a/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java +++ b/src/main/java/io/jboot/components/rpc/dubbo/JbootDubborpc.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/local/JbootLocalrpc.java b/src/main/java/io/jboot/components/rpc/local/JbootLocalrpc.java index 436fd1e6..6874ffb8 100644 --- a/src/main/java/io/jboot/components/rpc/local/JbootLocalrpc.java +++ b/src/main/java/io/jboot/components/rpc/local/JbootLocalrpc.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java index 5fb00754..21f899da 100644 --- a/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java +++ b/src/main/java/io/jboot/components/rpc/motan/JbootMotanrpc.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java index ead37e12..44b66242 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java b/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java index e44fa582..e3726cb1 100644 --- a/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java +++ b/src/main/java/io/jboot/components/rpc/motan/MotanrpcConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/schedule/JbooScheduleConfig.java b/src/main/java/io/jboot/components/schedule/JbooScheduleConfig.java index f548b89c..28c1d8d0 100644 --- a/src/main/java/io/jboot/components/schedule/JbooScheduleConfig.java +++ b/src/main/java/io/jboot/components/schedule/JbooScheduleConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/schedule/JbootCron4jPlugin.java b/src/main/java/io/jboot/components/schedule/JbootCron4jPlugin.java index 4b5aee1d..a06dac42 100644 --- a/src/main/java/io/jboot/components/schedule/JbootCron4jPlugin.java +++ b/src/main/java/io/jboot/components/schedule/JbootCron4jPlugin.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java b/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java index 7a31c81e..fae23a62 100644 --- a/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java +++ b/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java b/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java index 54d67d54..4cf4c001 100644 --- a/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java +++ b/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/schedule/annotation/Cron.java b/src/main/java/io/jboot/components/schedule/annotation/Cron.java index 7cc5b476..11b70cb4 100644 --- a/src/main/java/io/jboot/components/schedule/annotation/Cron.java +++ b/src/main/java/io/jboot/components/schedule/annotation/Cron.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/schedule/annotation/EnableDistributedRunnable.java b/src/main/java/io/jboot/components/schedule/annotation/EnableDistributedRunnable.java index e7726358..800f954a 100644 --- a/src/main/java/io/jboot/components/schedule/annotation/EnableDistributedRunnable.java +++ b/src/main/java/io/jboot/components/schedule/annotation/EnableDistributedRunnable.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/schedule/annotation/FixedDelay.java b/src/main/java/io/jboot/components/schedule/annotation/FixedDelay.java index f3a1ae6d..e7a8b1aa 100644 --- a/src/main/java/io/jboot/components/schedule/annotation/FixedDelay.java +++ b/src/main/java/io/jboot/components/schedule/annotation/FixedDelay.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/schedule/annotation/FixedRate.java b/src/main/java/io/jboot/components/schedule/annotation/FixedRate.java index ad9459da..92d1b63f 100644 --- a/src/main/java/io/jboot/components/schedule/annotation/FixedRate.java +++ b/src/main/java/io/jboot/components/schedule/annotation/FixedRate.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/serializer/FastJsonSerializer.java b/src/main/java/io/jboot/components/serializer/FastJsonSerializer.java index bd5b7267..9c3dfc71 100644 --- a/src/main/java/io/jboot/components/serializer/FastJsonSerializer.java +++ b/src/main/java/io/jboot/components/serializer/FastJsonSerializer.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/serializer/FstSerializer.java b/src/main/java/io/jboot/components/serializer/FstSerializer.java index 1fc306c5..a2556f42 100644 --- a/src/main/java/io/jboot/components/serializer/FstSerializer.java +++ b/src/main/java/io/jboot/components/serializer/FstSerializer.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/serializer/JacksonSerializer.java b/src/main/java/io/jboot/components/serializer/JacksonSerializer.java index 3d07dd1d..b3711c3b 100644 --- a/src/main/java/io/jboot/components/serializer/JacksonSerializer.java +++ b/src/main/java/io/jboot/components/serializer/JacksonSerializer.java @@ -1,5 +1,5 @@ ///** -// * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). +// * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). // *

        // * Licensed under the Apache License, Version 2.0 (the "License"); // * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/serializer/JbootSerializer.java b/src/main/java/io/jboot/components/serializer/JbootSerializer.java index d7d48a0f..efdf6153 100644 --- a/src/main/java/io/jboot/components/serializer/JbootSerializer.java +++ b/src/main/java/io/jboot/components/serializer/JbootSerializer.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java b/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java index ad1ea75b..49ce2b00 100644 --- a/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java +++ b/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java b/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java index 19a4e98a..9729853c 100644 --- a/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java +++ b/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/serializer/KryoSerializer.java b/src/main/java/io/jboot/components/serializer/KryoSerializer.java index af05c370..81421496 100644 --- a/src/main/java/io/jboot/components/serializer/KryoSerializer.java +++ b/src/main/java/io/jboot/components/serializer/KryoSerializer.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/ValidErrorRender.java b/src/main/java/io/jboot/components/valid/ValidErrorRender.java index 66f40ea1..eaaaa314 100644 --- a/src/main/java/io/jboot/components/valid/ValidErrorRender.java +++ b/src/main/java/io/jboot/components/valid/ValidErrorRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/ValidException.java b/src/main/java/io/jboot/components/valid/ValidException.java index edb3bb0f..57fff338 100644 --- a/src/main/java/io/jboot/components/valid/ValidException.java +++ b/src/main/java/io/jboot/components/valid/ValidException.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/ValidUtil.java b/src/main/java/io/jboot/components/valid/ValidUtil.java index e43ca99a..94b9563d 100644 --- a/src/main/java/io/jboot/components/valid/ValidUtil.java +++ b/src/main/java/io/jboot/components/valid/ValidUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/DecimalMaxInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/DecimalMaxInterceptor.java index c9bc4c6a..adc48551 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/DecimalMaxInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/DecimalMaxInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/DecimalMinInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/DecimalMinInterceptor.java index c124a557..32f210b0 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/DecimalMinInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/DecimalMinInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/DigitsInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/DigitsInterceptor.java index ebce2e47..07c6ec6f 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/DigitsInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/DigitsInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/EmailInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/EmailInterceptor.java index 9f206464..79b9ab0f 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/EmailInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/EmailInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/MaxInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/MaxInterceptor.java index 7c38c283..c3117bee 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/MaxInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/MaxInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/MinInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/MinInterceptor.java index 5d950b00..8904bdb3 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/MinInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/MinInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/NegativeInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/NegativeInterceptor.java index 588851ff..13af998d 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/NegativeInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/NegativeInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/NegativeOrZeroInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/NegativeOrZeroInterceptor.java index c6ba80ef..46ea3d06 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/NegativeOrZeroInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/NegativeOrZeroInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/NotBlankInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/NotBlankInterceptor.java index 9cfe0ae8..184a3384 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/NotBlankInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/NotBlankInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/NotEmptyInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/NotEmptyInterceptor.java index 821bc96a..bfeea9d1 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/NotEmptyInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/NotEmptyInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/NotNullInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/NotNullInterceptor.java index 6d2d6d46..60aba7b6 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/NotNullInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/NotNullInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/PatternInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/PatternInterceptor.java index d88f3252..e7e15436 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/PatternInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/PatternInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/PositiveInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/PositiveInterceptor.java index 5ee65f56..f55605cd 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/PositiveInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/PositiveInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/PositiveOrZeroInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/PositiveOrZeroInterceptor.java index acf37a25..d787c47e 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/PositiveOrZeroInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/PositiveOrZeroInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/SimpleContext.java b/src/main/java/io/jboot/components/valid/interceptor/SimpleContext.java index d63259ec..50ae5614 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/SimpleContext.java +++ b/src/main/java/io/jboot/components/valid/interceptor/SimpleContext.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java index 3cfc03ee..2fcdb3ae 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/ValidInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/ValidInterceptor.java index 338b9329..5b7718f3 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/ValidInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/ValidInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/components/valid/interceptor/ValidInterceptorBuilder.java b/src/main/java/io/jboot/components/valid/interceptor/ValidInterceptorBuilder.java index bb6dc431..0dd81c98 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/ValidInterceptorBuilder.java +++ b/src/main/java/io/jboot/components/valid/interceptor/ValidInterceptorBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 8460d35d..86f3c7ce 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/core/listener/JbootAppListener.java b/src/main/java/io/jboot/core/listener/JbootAppListener.java index 417f5a72..1a14be17 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListener.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListener.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/core/listener/JbootAppListenerBase.java b/src/main/java/io/jboot/core/listener/JbootAppListenerBase.java index 9ff991fb..533e9c57 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListenerBase.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListenerBase.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java index af4d58e6..0230bece 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/core/log/JbootLogFactory.java b/src/main/java/io/jboot/core/log/JbootLogFactory.java index 7f911bc7..6c427db1 100644 --- a/src/main/java/io/jboot/core/log/JbootLogFactory.java +++ b/src/main/java/io/jboot/core/log/JbootLogFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/core/log/JdkLogger.java b/src/main/java/io/jboot/core/log/JdkLogger.java index ff016124..880bba3f 100644 --- a/src/main/java/io/jboot/core/log/JdkLogger.java +++ b/src/main/java/io/jboot/core/log/JdkLogger.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/core/log/Slf4jLogger.java b/src/main/java/io/jboot/core/log/Slf4jLogger.java index d292182c..0bbf8e76 100644 --- a/src/main/java/io/jboot/core/log/Slf4jLogger.java +++ b/src/main/java/io/jboot/core/log/Slf4jLogger.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/core/log/Slf4jSimpleLogger.java b/src/main/java/io/jboot/core/log/Slf4jSimpleLogger.java index 08bae613..6f4c0554 100644 --- a/src/main/java/io/jboot/core/log/Slf4jSimpleLogger.java +++ b/src/main/java/io/jboot/core/log/Slf4jSimpleLogger.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/core/spi/JbootSpi.java b/src/main/java/io/jboot/core/spi/JbootSpi.java index 134b6740..d623b387 100644 --- a/src/main/java/io/jboot/core/spi/JbootSpi.java +++ b/src/main/java/io/jboot/core/spi/JbootSpi.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/core/spi/JbootSpiLoader.java b/src/main/java/io/jboot/core/spi/JbootSpiLoader.java index 63f2c4ae..edc47bdc 100644 --- a/src/main/java/io/jboot/core/spi/JbootSpiLoader.java +++ b/src/main/java/io/jboot/core/spi/JbootSpiLoader.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/core/weight/Weight.java b/src/main/java/io/jboot/core/weight/Weight.java index 2188222b..e3d258aa 100644 --- a/src/main/java/io/jboot/core/weight/Weight.java +++ b/src/main/java/io/jboot/core/weight/Weight.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/core/weight/WeightUtil.java b/src/main/java/io/jboot/core/weight/WeightUtil.java index db2e5e1f..38393eb5 100644 --- a/src/main/java/io/jboot/core/weight/WeightUtil.java +++ b/src/main/java/io/jboot/core/weight/WeightUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/ArpManager.java b/src/main/java/io/jboot/db/ArpManager.java index bed441c7..b1f9c9c1 100644 --- a/src/main/java/io/jboot/db/ArpManager.java +++ b/src/main/java/io/jboot/db/ArpManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/JbootDb.java b/src/main/java/io/jboot/db/JbootDb.java index 4009d9eb..1b59c525 100644 --- a/src/main/java/io/jboot/db/JbootDb.java +++ b/src/main/java/io/jboot/db/JbootDb.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/SqlDebugger.java b/src/main/java/io/jboot/db/SqlDebugger.java index 2a5f433e..31910c7d 100644 --- a/src/main/java/io/jboot/db/SqlDebugger.java +++ b/src/main/java/io/jboot/db/SqlDebugger.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/TableInfo.java b/src/main/java/io/jboot/db/TableInfo.java index 5c82899f..0b74ab4c 100644 --- a/src/main/java/io/jboot/db/TableInfo.java +++ b/src/main/java/io/jboot/db/TableInfo.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/TableInfoManager.java b/src/main/java/io/jboot/db/TableInfoManager.java index df5c15a1..8439a2ce 100644 --- a/src/main/java/io/jboot/db/TableInfoManager.java +++ b/src/main/java/io/jboot/db/TableInfoManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/annotation/Table.java b/src/main/java/io/jboot/db/annotation/Table.java index 40a72f9f..e4119876 100644 --- a/src/main/java/io/jboot/db/annotation/Table.java +++ b/src/main/java/io/jboot/db/annotation/Table.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/datasource/DataSourceBuilder.java b/src/main/java/io/jboot/db/datasource/DataSourceBuilder.java index aa34d523..25a6856b 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceBuilder.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java index 37b6c7a9..15cd8b9e 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java b/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java index 804b53a3..979dd8ca 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/datasource/DataSourceFactory.java b/src/main/java/io/jboot/db/datasource/DataSourceFactory.java index ad828fe8..0f3e99ef 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceFactory.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/datasource/DruidDataSourceFactory.java b/src/main/java/io/jboot/db/datasource/DruidDataSourceFactory.java index c85a8ff8..eeca3324 100644 --- a/src/main/java/io/jboot/db/datasource/DruidDataSourceFactory.java +++ b/src/main/java/io/jboot/db/datasource/DruidDataSourceFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/datasource/HikariDataSourceFactory.java b/src/main/java/io/jboot/db/datasource/HikariDataSourceFactory.java index 693bd41b..92f3b264 100644 --- a/src/main/java/io/jboot/db/datasource/HikariDataSourceFactory.java +++ b/src/main/java/io/jboot/db/datasource/HikariDataSourceFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java index 7e0aa9c3..ef9da445 100644 --- a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java +++ b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/dbpro/JbootDbProFactory.java b/src/main/java/io/jboot/db/dbpro/JbootDbProFactory.java index ee4cf5a3..e1c60ef3 100644 --- a/src/main/java/io/jboot/db/dbpro/JbootDbProFactory.java +++ b/src/main/java/io/jboot/db/dbpro/JbootDbProFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/dialect/JbootAnsiSqlDialect.java b/src/main/java/io/jboot/db/dialect/JbootAnsiSqlDialect.java index d40ce3ad..ec6a19dc 100644 --- a/src/main/java/io/jboot/db/dialect/JbootAnsiSqlDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootAnsiSqlDialect.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/dialect/JbootClickHouseDialect.java b/src/main/java/io/jboot/db/dialect/JbootClickHouseDialect.java index 41ac4228..9e4a2bd7 100644 --- a/src/main/java/io/jboot/db/dialect/JbootClickHouseDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootClickHouseDialect.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/dialect/JbootDialect.java b/src/main/java/io/jboot/db/dialect/JbootDialect.java index 771c66e7..b361fc63 100644 --- a/src/main/java/io/jboot/db/dialect/JbootDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootDialect.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/dialect/JbootMysqlDialect.java b/src/main/java/io/jboot/db/dialect/JbootMysqlDialect.java index bd783114..5b5def72 100644 --- a/src/main/java/io/jboot/db/dialect/JbootMysqlDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootMysqlDialect.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/dialect/JbootOracleDialect.java b/src/main/java/io/jboot/db/dialect/JbootOracleDialect.java index def16e21..940b21ff 100644 --- a/src/main/java/io/jboot/db/dialect/JbootOracleDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootOracleDialect.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/dialect/JbootPostgreSqlDialect.java b/src/main/java/io/jboot/db/dialect/JbootPostgreSqlDialect.java index 6997fb9c..dbec0a73 100644 --- a/src/main/java/io/jboot/db/dialect/JbootPostgreSqlDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootPostgreSqlDialect.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/dialect/JbootSqlServerDialect.java b/src/main/java/io/jboot/db/dialect/JbootSqlServerDialect.java index 4492d174..cec4d976 100644 --- a/src/main/java/io/jboot/db/dialect/JbootSqlServerDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootSqlServerDialect.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/dialect/JbootSqlite3Dialect.java b/src/main/java/io/jboot/db/dialect/JbootSqlite3Dialect.java index 5f1af6d7..b4f792b8 100644 --- a/src/main/java/io/jboot/db/dialect/JbootSqlite3Dialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootSqlite3Dialect.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/driver/DriverClassNames.java b/src/main/java/io/jboot/db/driver/DriverClassNames.java index a8ab12b2..c698890c 100644 --- a/src/main/java/io/jboot/db/driver/DriverClassNames.java +++ b/src/main/java/io/jboot/db/driver/DriverClassNames.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/driver/NativeClickHouseConnection.java b/src/main/java/io/jboot/db/driver/NativeClickHouseConnection.java index 34469e4d..27735b76 100644 --- a/src/main/java/io/jboot/db/driver/NativeClickHouseConnection.java +++ b/src/main/java/io/jboot/db/driver/NativeClickHouseConnection.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/driver/NativeClickHouseDriver.java b/src/main/java/io/jboot/db/driver/NativeClickHouseDriver.java index beb6fc4f..470e1d60 100644 --- a/src/main/java/io/jboot/db/driver/NativeClickHouseDriver.java +++ b/src/main/java/io/jboot/db/driver/NativeClickHouseDriver.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/driver/OfficialClickHouseConnection.java b/src/main/java/io/jboot/db/driver/OfficialClickHouseConnection.java index 12d1d5f3..9a002fdf 100644 --- a/src/main/java/io/jboot/db/driver/OfficialClickHouseConnection.java +++ b/src/main/java/io/jboot/db/driver/OfficialClickHouseConnection.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/driver/OfficialClickHouseDriver.java b/src/main/java/io/jboot/db/driver/OfficialClickHouseDriver.java index 9bd8e7e5..c6eb6005 100644 --- a/src/main/java/io/jboot/db/driver/OfficialClickHouseDriver.java +++ b/src/main/java/io/jboot/db/driver/OfficialClickHouseDriver.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/CPI.java b/src/main/java/io/jboot/db/model/CPI.java index 5d3fc563..148227bc 100644 --- a/src/main/java/io/jboot/db/model/CPI.java +++ b/src/main/java/io/jboot/db/model/CPI.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/Column.java b/src/main/java/io/jboot/db/model/Column.java index 3aa6110e..1ea16d7b 100644 --- a/src/main/java/io/jboot/db/model/Column.java +++ b/src/main/java/io/jboot/db/model/Column.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index f52b9e7e..39e22bce 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/Group.java b/src/main/java/io/jboot/db/model/Group.java index 1100f2b0..191f3350 100644 --- a/src/main/java/io/jboot/db/model/Group.java +++ b/src/main/java/io/jboot/db/model/Group.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/GroupBy.java b/src/main/java/io/jboot/db/model/GroupBy.java index fbf1df20..fe38309d 100644 --- a/src/main/java/io/jboot/db/model/GroupBy.java +++ b/src/main/java/io/jboot/db/model/GroupBy.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/Having.java b/src/main/java/io/jboot/db/model/Having.java index edf2359e..f1ce3f0d 100644 --- a/src/main/java/io/jboot/db/model/Having.java +++ b/src/main/java/io/jboot/db/model/Having.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 65d224c3..587b85a7 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/JbootModelConfig.java b/src/main/java/io/jboot/db/model/JbootModelConfig.java index 33327366..c470c04c 100644 --- a/src/main/java/io/jboot/db/model/JbootModelConfig.java +++ b/src/main/java/io/jboot/db/model/JbootModelConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/JbootModelExts.java b/src/main/java/io/jboot/db/model/JbootModelExts.java index 77bd5552..563745ec 100644 --- a/src/main/java/io/jboot/db/model/JbootModelExts.java +++ b/src/main/java/io/jboot/db/model/JbootModelExts.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/JbootModelFilter.java b/src/main/java/io/jboot/db/model/JbootModelFilter.java index 35883660..0bcc0db8 100644 --- a/src/main/java/io/jboot/db/model/JbootModelFilter.java +++ b/src/main/java/io/jboot/db/model/JbootModelFilter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/JbootModelHintManager.java b/src/main/java/io/jboot/db/model/JbootModelHintManager.java index 4f26526a..6f5a2509 100644 --- a/src/main/java/io/jboot/db/model/JbootModelHintManager.java +++ b/src/main/java/io/jboot/db/model/JbootModelHintManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/Join.java b/src/main/java/io/jboot/db/model/Join.java index c18b5483..d49f9ad7 100644 --- a/src/main/java/io/jboot/db/model/Join.java +++ b/src/main/java/io/jboot/db/model/Join.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/Joiner.java b/src/main/java/io/jboot/db/model/Joiner.java index 34482d01..db3422ec 100644 --- a/src/main/java/io/jboot/db/model/Joiner.java +++ b/src/main/java/io/jboot/db/model/Joiner.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/Or.java b/src/main/java/io/jboot/db/model/Or.java index afdb2b9f..2dbbf3ac 100644 --- a/src/main/java/io/jboot/db/model/Or.java +++ b/src/main/java/io/jboot/db/model/Or.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/PrimarykeyValueGenerator.java b/src/main/java/io/jboot/db/model/PrimarykeyValueGenerator.java index b41296c1..b7012825 100644 --- a/src/main/java/io/jboot/db/model/PrimarykeyValueGenerator.java +++ b/src/main/java/io/jboot/db/model/PrimarykeyValueGenerator.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index 66aab64e..0ffc38fc 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/SqlPart.java b/src/main/java/io/jboot/db/model/SqlPart.java index c8f836c9..67567d86 100644 --- a/src/main/java/io/jboot/db/model/SqlPart.java +++ b/src/main/java/io/jboot/db/model/SqlPart.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/model/Util.java b/src/main/java/io/jboot/db/model/Util.java index 9e6b1f20..b16cd3ec 100644 --- a/src/main/java/io/jboot/db/model/Util.java +++ b/src/main/java/io/jboot/db/model/Util.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/record/JbootRecord.java b/src/main/java/io/jboot/db/record/JbootRecord.java index c4764d33..e1c4dd7a 100644 --- a/src/main/java/io/jboot/db/record/JbootRecord.java +++ b/src/main/java/io/jboot/db/record/JbootRecord.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/record/JbootRecordBuilder.java b/src/main/java/io/jboot/db/record/JbootRecordBuilder.java index 4d8ba24e..01da6810 100644 --- a/src/main/java/io/jboot/db/record/JbootRecordBuilder.java +++ b/src/main/java/io/jboot/db/record/JbootRecordBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/tx/TxEnable.java b/src/main/java/io/jboot/db/tx/TxEnable.java index 17684ff9..082db0cd 100644 --- a/src/main/java/io/jboot/db/tx/TxEnable.java +++ b/src/main/java/io/jboot/db/tx/TxEnable.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/tx/TxEnableInterceptor.java b/src/main/java/io/jboot/db/tx/TxEnableInterceptor.java index a1fca002..bb94fb9d 100644 --- a/src/main/java/io/jboot/db/tx/TxEnableInterceptor.java +++ b/src/main/java/io/jboot/db/tx/TxEnableInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/exception/JbootException.java b/src/main/java/io/jboot/exception/JbootException.java index de862e2a..484a34ef 100644 --- a/src/main/java/io/jboot/exception/JbootException.java +++ b/src/main/java/io/jboot/exception/JbootException.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/exception/JbootExceptionHolder.java b/src/main/java/io/jboot/exception/JbootExceptionHolder.java index ca409679..76390b7d 100644 --- a/src/main/java/io/jboot/exception/JbootExceptionHolder.java +++ b/src/main/java/io/jboot/exception/JbootExceptionHolder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/exception/JbootIllegalConfigException.java b/src/main/java/io/jboot/exception/JbootIllegalConfigException.java index 7b1507e0..36572440 100644 --- a/src/main/java/io/jboot/exception/JbootIllegalConfigException.java +++ b/src/main/java/io/jboot/exception/JbootIllegalConfigException.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/exception/JbootRpcException.java b/src/main/java/io/jboot/exception/JbootRpcException.java index d5fa3692..14bb76e6 100644 --- a/src/main/java/io/jboot/exception/JbootRpcException.java +++ b/src/main/java/io/jboot/exception/JbootRpcException.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/ext/MixedByteArrayOutputStream.java b/src/main/java/io/jboot/ext/MixedByteArrayOutputStream.java index c55d2325..ff8983f6 100644 --- a/src/main/java/io/jboot/ext/MixedByteArrayOutputStream.java +++ b/src/main/java/io/jboot/ext/MixedByteArrayOutputStream.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/objects/counter/JbootCounter.java b/src/main/java/io/jboot/objects/counter/JbootCounter.java index e1dce912..d492d297 100644 --- a/src/main/java/io/jboot/objects/counter/JbootCounter.java +++ b/src/main/java/io/jboot/objects/counter/JbootCounter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/objects/counter/JbootCounterConfig.java b/src/main/java/io/jboot/objects/counter/JbootCounterConfig.java index 9bc22948..541df41b 100644 --- a/src/main/java/io/jboot/objects/counter/JbootCounterConfig.java +++ b/src/main/java/io/jboot/objects/counter/JbootCounterConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/objects/counter/JbootCounterManager.java b/src/main/java/io/jboot/objects/counter/JbootCounterManager.java index a655cb77..af02c19c 100644 --- a/src/main/java/io/jboot/objects/counter/JbootCounterManager.java +++ b/src/main/java/io/jboot/objects/counter/JbootCounterManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java b/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java index 4754b91e..dd51b893 100644 --- a/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java +++ b/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/objects/counter/impl/JbootRedisCounter.java b/src/main/java/io/jboot/objects/counter/impl/JbootRedisCounter.java index 35e49c05..d59eabba 100644 --- a/src/main/java/io/jboot/objects/counter/impl/JbootRedisCounter.java +++ b/src/main/java/io/jboot/objects/counter/impl/JbootRedisCounter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/objects/list/JbootList.java b/src/main/java/io/jboot/objects/list/JbootList.java index 5adb583f..1e3c1487 100644 --- a/src/main/java/io/jboot/objects/list/JbootList.java +++ b/src/main/java/io/jboot/objects/list/JbootList.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/objects/lock/JbootLockConfig.java b/src/main/java/io/jboot/objects/lock/JbootLockConfig.java index 781d1abf..cb914d82 100644 --- a/src/main/java/io/jboot/objects/lock/JbootLockConfig.java +++ b/src/main/java/io/jboot/objects/lock/JbootLockConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/objects/lock/JbootLockManager.java b/src/main/java/io/jboot/objects/lock/JbootLockManager.java index eec2e506..9fca2b39 100644 --- a/src/main/java/io/jboot/objects/lock/JbootLockManager.java +++ b/src/main/java/io/jboot/objects/lock/JbootLockManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java b/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java index 523d9b28..68ad5910 100644 --- a/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java +++ b/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/objects/lock/impl/JbootRedisLock.java b/src/main/java/io/jboot/objects/lock/impl/JbootRedisLock.java index cc6cdd95..76c52bb0 100644 --- a/src/main/java/io/jboot/objects/lock/impl/JbootRedisLock.java +++ b/src/main/java/io/jboot/objects/lock/impl/JbootRedisLock.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/objects/map/JbootMap.java b/src/main/java/io/jboot/objects/map/JbootMap.java index 8156f694..b7a2ec54 100644 --- a/src/main/java/io/jboot/objects/map/JbootMap.java +++ b/src/main/java/io/jboot/objects/map/JbootMap.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/objects/map/JbootMapManager.java b/src/main/java/io/jboot/objects/map/JbootMapManager.java index 832c45f3..82dc0c9d 100644 --- a/src/main/java/io/jboot/objects/map/JbootMapManager.java +++ b/src/main/java/io/jboot/objects/map/JbootMapManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/objects/object/JbootObject.java b/src/main/java/io/jboot/objects/object/JbootObject.java index 3a592336..4fffcb0e 100644 --- a/src/main/java/io/jboot/objects/object/JbootObject.java +++ b/src/main/java/io/jboot/objects/object/JbootObject.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/service/JbootServiceBase.java b/src/main/java/io/jboot/service/JbootServiceBase.java index a23c8f21..65fe4cc2 100644 --- a/src/main/java/io/jboot/service/JbootServiceBase.java +++ b/src/main/java/io/jboot/service/JbootServiceBase.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/service/JbootServiceJoiner.java b/src/main/java/io/jboot/service/JbootServiceJoiner.java index 7303b2a9..b083a743 100644 --- a/src/main/java/io/jboot/service/JbootServiceJoiner.java +++ b/src/main/java/io/jboot/service/JbootServiceJoiner.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java index db08e520..07cb53c5 100644 --- a/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java +++ b/src/main/java/io/jboot/service/JbootServiceJoinerImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/jwt/JwtConfig.java b/src/main/java/io/jboot/support/jwt/JwtConfig.java index fc05ced0..7d7cdfef 100644 --- a/src/main/java/io/jboot/support/jwt/JwtConfig.java +++ b/src/main/java/io/jboot/support/jwt/JwtConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/jwt/JwtInterceptor.java b/src/main/java/io/jboot/support/jwt/JwtInterceptor.java index c4932338..988d3f4a 100644 --- a/src/main/java/io/jboot/support/jwt/JwtInterceptor.java +++ b/src/main/java/io/jboot/support/jwt/JwtInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/jwt/JwtInterceptorBuilder.java b/src/main/java/io/jboot/support/jwt/JwtInterceptorBuilder.java index 9233f697..edee2f51 100644 --- a/src/main/java/io/jboot/support/jwt/JwtInterceptorBuilder.java +++ b/src/main/java/io/jboot/support/jwt/JwtInterceptorBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/jwt/JwtManager.java b/src/main/java/io/jboot/support/jwt/JwtManager.java index 2cf7e1a5..7fdd21f0 100644 --- a/src/main/java/io/jboot/support/jwt/JwtManager.java +++ b/src/main/java/io/jboot/support/jwt/JwtManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/JbootHealthCheckServletContextListener.java b/src/main/java/io/jboot/support/metric/JbootHealthCheckServletContextListener.java index 55f1e088..d4e5261a 100644 --- a/src/main/java/io/jboot/support/metric/JbootHealthCheckServletContextListener.java +++ b/src/main/java/io/jboot/support/metric/JbootHealthCheckServletContextListener.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/JbootMetricConfig.java b/src/main/java/io/jboot/support/metric/JbootMetricConfig.java index 84d0a83a..622baf87 100644 --- a/src/main/java/io/jboot/support/metric/JbootMetricConfig.java +++ b/src/main/java/io/jboot/support/metric/JbootMetricConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/JbootMetricManager.java b/src/main/java/io/jboot/support/metric/JbootMetricManager.java index 4af41066..14738093 100644 --- a/src/main/java/io/jboot/support/metric/JbootMetricManager.java +++ b/src/main/java/io/jboot/support/metric/JbootMetricManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/JbootMetricReporter.java b/src/main/java/io/jboot/support/metric/JbootMetricReporter.java index 7e7242f4..6a69afd6 100644 --- a/src/main/java/io/jboot/support/metric/JbootMetricReporter.java +++ b/src/main/java/io/jboot/support/metric/JbootMetricReporter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/JbootMetricServletContextListener.java b/src/main/java/io/jboot/support/metric/JbootMetricServletContextListener.java index 52a8496e..377dccfe 100644 --- a/src/main/java/io/jboot/support/metric/JbootMetricServletContextListener.java +++ b/src/main/java/io/jboot/support/metric/JbootMetricServletContextListener.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/JvmCpuGaugeSet.java b/src/main/java/io/jboot/support/metric/JvmCpuGaugeSet.java index 7b3a08be..df4d34c9 100644 --- a/src/main/java/io/jboot/support/metric/JvmCpuGaugeSet.java +++ b/src/main/java/io/jboot/support/metric/JvmCpuGaugeSet.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/JvmGcMetrics.java b/src/main/java/io/jboot/support/metric/JvmGcMetrics.java index b42dcb5e..0ab5f15a 100644 --- a/src/main/java/io/jboot/support/metric/JvmGcMetrics.java +++ b/src/main/java/io/jboot/support/metric/JvmGcMetrics.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/JvmGcUtil.java b/src/main/java/io/jboot/support/metric/JvmGcUtil.java index 25c55466..b1e0e097 100644 --- a/src/main/java/io/jboot/support/metric/JvmGcUtil.java +++ b/src/main/java/io/jboot/support/metric/JvmGcUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/MetricServletHandler.java b/src/main/java/io/jboot/support/metric/MetricServletHandler.java index 1a2d1e11..c1dc815f 100644 --- a/src/main/java/io/jboot/support/metric/MetricServletHandler.java +++ b/src/main/java/io/jboot/support/metric/MetricServletHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/ProcessFilesGaugeSet.java b/src/main/java/io/jboot/support/metric/ProcessFilesGaugeSet.java index 606413b5..78857dd7 100644 --- a/src/main/java/io/jboot/support/metric/ProcessFilesGaugeSet.java +++ b/src/main/java/io/jboot/support/metric/ProcessFilesGaugeSet.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/annotation/EnableMetricConcurrency.java b/src/main/java/io/jboot/support/metric/annotation/EnableMetricConcurrency.java index 94ce91db..4f03f047 100644 --- a/src/main/java/io/jboot/support/metric/annotation/EnableMetricConcurrency.java +++ b/src/main/java/io/jboot/support/metric/annotation/EnableMetricConcurrency.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/annotation/EnableMetricCounter.java b/src/main/java/io/jboot/support/metric/annotation/EnableMetricCounter.java index fd49b9b6..2502c4dd 100644 --- a/src/main/java/io/jboot/support/metric/annotation/EnableMetricCounter.java +++ b/src/main/java/io/jboot/support/metric/annotation/EnableMetricCounter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/annotation/EnableMetricHistogram.java b/src/main/java/io/jboot/support/metric/annotation/EnableMetricHistogram.java index 257c79fb..b47f3415 100644 --- a/src/main/java/io/jboot/support/metric/annotation/EnableMetricHistogram.java +++ b/src/main/java/io/jboot/support/metric/annotation/EnableMetricHistogram.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/annotation/EnableMetricMeter.java b/src/main/java/io/jboot/support/metric/annotation/EnableMetricMeter.java index 1560bf7d..21bd781b 100644 --- a/src/main/java/io/jboot/support/metric/annotation/EnableMetricMeter.java +++ b/src/main/java/io/jboot/support/metric/annotation/EnableMetricMeter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/annotation/EnableMetricTimer.java b/src/main/java/io/jboot/support/metric/annotation/EnableMetricTimer.java index b2cacd60..11e698ce 100644 --- a/src/main/java/io/jboot/support/metric/annotation/EnableMetricTimer.java +++ b/src/main/java/io/jboot/support/metric/annotation/EnableMetricTimer.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricConcurrencyInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricConcurrencyInterceptor.java index fcb66c95..39554da9 100644 --- a/src/main/java/io/jboot/support/metric/interceptor/MetricConcurrencyInterceptor.java +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricConcurrencyInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricCounterInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricCounterInterceptor.java index f313101e..6d9d5ea6 100644 --- a/src/main/java/io/jboot/support/metric/interceptor/MetricCounterInterceptor.java +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricCounterInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricHistogramInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricHistogramInterceptor.java index 18ab11c1..15236123 100644 --- a/src/main/java/io/jboot/support/metric/interceptor/MetricHistogramInterceptor.java +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricHistogramInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricInterceptorBuilder.java b/src/main/java/io/jboot/support/metric/interceptor/MetricInterceptorBuilder.java index aca3db00..d8d6239c 100644 --- a/src/main/java/io/jboot/support/metric/interceptor/MetricInterceptorBuilder.java +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricInterceptorBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricMeterInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricMeterInterceptor.java index a92ee1e6..11d7ac83 100644 --- a/src/main/java/io/jboot/support/metric/interceptor/MetricMeterInterceptor.java +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricMeterInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/interceptor/MetricTimerInterceptor.java b/src/main/java/io/jboot/support/metric/interceptor/MetricTimerInterceptor.java index df5e374c..a0082607 100644 --- a/src/main/java/io/jboot/support/metric/interceptor/MetricTimerInterceptor.java +++ b/src/main/java/io/jboot/support/metric/interceptor/MetricTimerInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/reporter/console/JbootConsoleReporter.java b/src/main/java/io/jboot/support/metric/reporter/console/JbootConsoleReporter.java index 5e7060c7..14791beb 100644 --- a/src/main/java/io/jboot/support/metric/reporter/console/JbootConsoleReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/console/JbootConsoleReporter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/reporter/csv/CSVReporter.java b/src/main/java/io/jboot/support/metric/reporter/csv/CSVReporter.java index 4ace943c..7a84e10f 100644 --- a/src/main/java/io/jboot/support/metric/reporter/csv/CSVReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/csv/CSVReporter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/reporter/csv/JbootMetricCVRReporterConfig.java b/src/main/java/io/jboot/support/metric/reporter/csv/JbootMetricCVRReporterConfig.java index fb05aa9a..f0edafb4 100644 --- a/src/main/java/io/jboot/support/metric/reporter/csv/JbootMetricCVRReporterConfig.java +++ b/src/main/java/io/jboot/support/metric/reporter/csv/JbootMetricCVRReporterConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/reporter/elasticsearch/ElasticsearchReporter.java b/src/main/java/io/jboot/support/metric/reporter/elasticsearch/ElasticsearchReporter.java index 9d5963f3..a8e096d9 100644 --- a/src/main/java/io/jboot/support/metric/reporter/elasticsearch/ElasticsearchReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/elasticsearch/ElasticsearchReporter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/reporter/ganglia/GangliaReporter.java b/src/main/java/io/jboot/support/metric/reporter/ganglia/GangliaReporter.java index 8e234739..00dfb450 100644 --- a/src/main/java/io/jboot/support/metric/reporter/ganglia/GangliaReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/ganglia/GangliaReporter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/reporter/graphite/JbootGraphiteReporter.java b/src/main/java/io/jboot/support/metric/reporter/graphite/JbootGraphiteReporter.java index e7ba6441..e98b5477 100644 --- a/src/main/java/io/jboot/support/metric/reporter/graphite/JbootGraphiteReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/graphite/JbootGraphiteReporter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/reporter/graphite/JbootMetricGraphiteReporterConfig.java b/src/main/java/io/jboot/support/metric/reporter/graphite/JbootMetricGraphiteReporterConfig.java index c355cfa9..59f55c84 100644 --- a/src/main/java/io/jboot/support/metric/reporter/graphite/JbootMetricGraphiteReporterConfig.java +++ b/src/main/java/io/jboot/support/metric/reporter/graphite/JbootMetricGraphiteReporterConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/reporter/influxdb/InfluxdbReporter.java b/src/main/java/io/jboot/support/metric/reporter/influxdb/InfluxdbReporter.java index 6a5273fb..fb4ccd97 100644 --- a/src/main/java/io/jboot/support/metric/reporter/influxdb/InfluxdbReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/influxdb/InfluxdbReporter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/reporter/influxdb/JbootMetricInfluxdbReporterConfig.java b/src/main/java/io/jboot/support/metric/reporter/influxdb/JbootMetricInfluxdbReporterConfig.java index fd11ab02..acd2b38e 100644 --- a/src/main/java/io/jboot/support/metric/reporter/influxdb/JbootMetricInfluxdbReporterConfig.java +++ b/src/main/java/io/jboot/support/metric/reporter/influxdb/JbootMetricInfluxdbReporterConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/reporter/jmx/JMXReporter.java b/src/main/java/io/jboot/support/metric/reporter/jmx/JMXReporter.java index 084c3684..ca0f662f 100644 --- a/src/main/java/io/jboot/support/metric/reporter/jmx/JMXReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/jmx/JMXReporter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/reporter/prometheus/PrometheusExports.java b/src/main/java/io/jboot/support/metric/reporter/prometheus/PrometheusExports.java index 17dc599b..4d435c2f 100644 --- a/src/main/java/io/jboot/support/metric/reporter/prometheus/PrometheusExports.java +++ b/src/main/java/io/jboot/support/metric/reporter/prometheus/PrometheusExports.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/reporter/prometheus/PrometheusReporter.java b/src/main/java/io/jboot/support/metric/reporter/prometheus/PrometheusReporter.java index 9b20153c..623b6353 100644 --- a/src/main/java/io/jboot/support/metric/reporter/prometheus/PrometheusReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/prometheus/PrometheusReporter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/reporter/prometheus/PrometheusReporterConfig.java b/src/main/java/io/jboot/support/metric/reporter/prometheus/PrometheusReporterConfig.java index 121e8ab4..3e15fe85 100644 --- a/src/main/java/io/jboot/support/metric/reporter/prometheus/PrometheusReporterConfig.java +++ b/src/main/java/io/jboot/support/metric/reporter/prometheus/PrometheusReporterConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/reporter/slf4j/JbootSlf4jReporter.java b/src/main/java/io/jboot/support/metric/reporter/slf4j/JbootSlf4jReporter.java index 0eefad40..3f4ef42b 100644 --- a/src/main/java/io/jboot/support/metric/reporter/slf4j/JbootSlf4jReporter.java +++ b/src/main/java/io/jboot/support/metric/reporter/slf4j/JbootSlf4jReporter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/request/AbstractInstrumentedFilter.java b/src/main/java/io/jboot/support/metric/request/AbstractInstrumentedFilter.java index e9a77813..ede358bf 100644 --- a/src/main/java/io/jboot/support/metric/request/AbstractInstrumentedFilter.java +++ b/src/main/java/io/jboot/support/metric/request/AbstractInstrumentedFilter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/metric/request/JbootRequestMetricHandler.java b/src/main/java/io/jboot/support/metric/request/JbootRequestMetricHandler.java index b076cb71..2b254380 100644 --- a/src/main/java/io/jboot/support/metric/request/JbootRequestMetricHandler.java +++ b/src/main/java/io/jboot/support/metric/request/JbootRequestMetricHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/redis/JbootRedis.java b/src/main/java/io/jboot/support/redis/JbootRedis.java index a6ec93f1..1c7cade8 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedis.java +++ b/src/main/java/io/jboot/support/redis/JbootRedis.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/redis/JbootRedisBase.java b/src/main/java/io/jboot/support/redis/JbootRedisBase.java index 919ce9c0..c2a55dbc 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedisBase.java +++ b/src/main/java/io/jboot/support/redis/JbootRedisBase.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/redis/JbootRedisConfig.java b/src/main/java/io/jboot/support/redis/JbootRedisConfig.java index ea8ee05a..58c15157 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedisConfig.java +++ b/src/main/java/io/jboot/support/redis/JbootRedisConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/redis/JbootRedisLock.java b/src/main/java/io/jboot/support/redis/JbootRedisLock.java index 8f9c79e7..533737a7 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedisLock.java +++ b/src/main/java/io/jboot/support/redis/JbootRedisLock.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/redis/JbootRedisManager.java b/src/main/java/io/jboot/support/redis/JbootRedisManager.java index 53aa6ea1..8c436671 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedisManager.java +++ b/src/main/java/io/jboot/support/redis/JbootRedisManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java index d40957d1..b3e1e38b 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java index 3fdbbcf4..8c3dbfab 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/redis/lettuce/JbootLettuceCodec.java b/src/main/java/io/jboot/support/redis/lettuce/JbootLettuceCodec.java index 24653c9e..9aefe8b9 100644 --- a/src/main/java/io/jboot/support/redis/lettuce/JbootLettuceCodec.java +++ b/src/main/java/io/jboot/support/redis/lettuce/JbootLettuceCodec.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/redis/lettuce/JbootLettuceImpl.java b/src/main/java/io/jboot/support/redis/lettuce/JbootLettuceImpl.java index 653c336f..260cc82b 100644 --- a/src/main/java/io/jboot/support/redis/lettuce/JbootLettuceImpl.java +++ b/src/main/java/io/jboot/support/redis/lettuce/JbootLettuceImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/redis/lettuce/LettuceException.java b/src/main/java/io/jboot/support/redis/lettuce/LettuceException.java index 9bd70566..fd0501a8 100644 --- a/src/main/java/io/jboot/support/redis/lettuce/LettuceException.java +++ b/src/main/java/io/jboot/support/redis/lettuce/LettuceException.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/redis/redisson/JbootRedissonImpl.java b/src/main/java/io/jboot/support/redis/redisson/JbootRedissonImpl.java index d7625904..aa75966d 100644 --- a/src/main/java/io/jboot/support/redis/redisson/JbootRedissonImpl.java +++ b/src/main/java/io/jboot/support/redis/redisson/JbootRedissonImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/seata/JbootSeataManager.java b/src/main/java/io/jboot/support/seata/JbootSeataManager.java index a7ab8435..72527ac7 100644 --- a/src/main/java/io/jboot/support/seata/JbootSeataManager.java +++ b/src/main/java/io/jboot/support/seata/JbootSeataManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/seata/SeataConfig.java b/src/main/java/io/jboot/support/seata/SeataConfig.java index ea45b120..d069f819 100644 --- a/src/main/java/io/jboot/support/seata/SeataConfig.java +++ b/src/main/java/io/jboot/support/seata/SeataConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/seata/SeataGlobalTransactionManager.java b/src/main/java/io/jboot/support/seata/SeataGlobalTransactionManager.java index d218fee2..2c203d8f 100644 --- a/src/main/java/io/jboot/support/seata/SeataGlobalTransactionManager.java +++ b/src/main/java/io/jboot/support/seata/SeataGlobalTransactionManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/seata/annotation/SeataGlobalLock.java b/src/main/java/io/jboot/support/seata/annotation/SeataGlobalLock.java index fa8652cd..fc29e295 100644 --- a/src/main/java/io/jboot/support/seata/annotation/SeataGlobalLock.java +++ b/src/main/java/io/jboot/support/seata/annotation/SeataGlobalLock.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/seata/annotation/SeataGlobalTransactional.java b/src/main/java/io/jboot/support/seata/annotation/SeataGlobalTransactional.java index 27433fea..14d2938f 100644 --- a/src/main/java/io/jboot/support/seata/annotation/SeataGlobalTransactional.java +++ b/src/main/java/io/jboot/support/seata/annotation/SeataGlobalTransactional.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/seata/filter/TransactionPropagationFilter.java b/src/main/java/io/jboot/support/seata/filter/TransactionPropagationFilter.java index 10436f02..1ae5ef29 100644 --- a/src/main/java/io/jboot/support/seata/filter/TransactionPropagationFilter.java +++ b/src/main/java/io/jboot/support/seata/filter/TransactionPropagationFilter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalInterceptorBuilder.java b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalInterceptorBuilder.java index e47c81ce..3d5f8a7b 100644 --- a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalInterceptorBuilder.java +++ b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalInterceptorBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionHandler.java b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionHandler.java index dffacf7a..08ff6872 100644 --- a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionHandler.java +++ b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java index 274f1a0f..1147b07b 100644 --- a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java +++ b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/seata/tcc/ActionInterceptorHandler.java b/src/main/java/io/jboot/support/seata/tcc/ActionInterceptorHandler.java index 89e55c90..8c16211e 100644 --- a/src/main/java/io/jboot/support/seata/tcc/ActionInterceptorHandler.java +++ b/src/main/java/io/jboot/support/seata/tcc/ActionInterceptorHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java b/src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java index e22bddc1..799930ce 100644 --- a/src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java +++ b/src/main/java/io/jboot/support/seata/tcc/TccActionInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/seata/tcc/TccInterceptorBuilder.java b/src/main/java/io/jboot/support/seata/tcc/TccInterceptorBuilder.java index a9a2a5ee..f4c8ed7f 100644 --- a/src/main/java/io/jboot/support/seata/tcc/TccInterceptorBuilder.java +++ b/src/main/java/io/jboot/support/seata/tcc/TccInterceptorBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/JbootSentinelBuilder.java b/src/main/java/io/jboot/support/sentinel/JbootSentinelBuilder.java index fedd5ed6..c18dea23 100644 --- a/src/main/java/io/jboot/support/sentinel/JbootSentinelBuilder.java +++ b/src/main/java/io/jboot/support/sentinel/JbootSentinelBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/JbootSentinelManager.java b/src/main/java/io/jboot/support/sentinel/JbootSentinelManager.java index f7e3e7a2..984ffe31 100644 --- a/src/main/java/io/jboot/support/sentinel/JbootSentinelManager.java +++ b/src/main/java/io/jboot/support/sentinel/JbootSentinelManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/SentinelConfig.java b/src/main/java/io/jboot/support/sentinel/SentinelConfig.java index 1f7cd9fa..817f1000 100644 --- a/src/main/java/io/jboot/support/sentinel/SentinelConfig.java +++ b/src/main/java/io/jboot/support/sentinel/SentinelConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/SentinelHandler.java b/src/main/java/io/jboot/support/sentinel/SentinelHandler.java index 78bc5cbe..776801c5 100755 --- a/src/main/java/io/jboot/support/sentinel/SentinelHandler.java +++ b/src/main/java/io/jboot/support/sentinel/SentinelHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java b/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java index 254ab964..f7c1cb21 100644 --- a/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java +++ b/src/main/java/io/jboot/support/sentinel/SentinelInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/SentinelInterceptorBuilder.java b/src/main/java/io/jboot/support/sentinel/SentinelInterceptorBuilder.java index 7249408e..26abf95c 100644 --- a/src/main/java/io/jboot/support/sentinel/SentinelInterceptorBuilder.java +++ b/src/main/java/io/jboot/support/sentinel/SentinelInterceptorBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/SentinelUtil.java b/src/main/java/io/jboot/support/sentinel/SentinelUtil.java index 2702da1b..6b85d7e1 100644 --- a/src/main/java/io/jboot/support/sentinel/SentinelUtil.java +++ b/src/main/java/io/jboot/support/sentinel/SentinelUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/datasource/ApolloDatasourceConfig.java b/src/main/java/io/jboot/support/sentinel/datasource/ApolloDatasourceConfig.java index c82f3288..33263e70 100644 --- a/src/main/java/io/jboot/support/sentinel/datasource/ApolloDatasourceConfig.java +++ b/src/main/java/io/jboot/support/sentinel/datasource/ApolloDatasourceConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/datasource/ApolloDatasourceFactory.java b/src/main/java/io/jboot/support/sentinel/datasource/ApolloDatasourceFactory.java index c8d3fb0b..68ca77fd 100644 --- a/src/main/java/io/jboot/support/sentinel/datasource/ApolloDatasourceFactory.java +++ b/src/main/java/io/jboot/support/sentinel/datasource/ApolloDatasourceFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java b/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java index 0c0f75a8..fb928bb8 100644 --- a/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java +++ b/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/datasource/NacosDatasourceConfig.java b/src/main/java/io/jboot/support/sentinel/datasource/NacosDatasourceConfig.java index c70d4dcf..6a51f3c6 100644 --- a/src/main/java/io/jboot/support/sentinel/datasource/NacosDatasourceConfig.java +++ b/src/main/java/io/jboot/support/sentinel/datasource/NacosDatasourceConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/datasource/NacosDatasourceFactory.java b/src/main/java/io/jboot/support/sentinel/datasource/NacosDatasourceFactory.java index 3e49e45b..08862b08 100644 --- a/src/main/java/io/jboot/support/sentinel/datasource/NacosDatasourceFactory.java +++ b/src/main/java/io/jboot/support/sentinel/datasource/NacosDatasourceFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/datasource/RedisDatasourceConfig.java b/src/main/java/io/jboot/support/sentinel/datasource/RedisDatasourceConfig.java index 342ccaa4..6475822d 100644 --- a/src/main/java/io/jboot/support/sentinel/datasource/RedisDatasourceConfig.java +++ b/src/main/java/io/jboot/support/sentinel/datasource/RedisDatasourceConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/datasource/RedisDatasourceFactory.java b/src/main/java/io/jboot/support/sentinel/datasource/RedisDatasourceFactory.java index 8860a182..42a7fde4 100644 --- a/src/main/java/io/jboot/support/sentinel/datasource/RedisDatasourceFactory.java +++ b/src/main/java/io/jboot/support/sentinel/datasource/RedisDatasourceFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/datasource/SentinelDatasourceFactory.java b/src/main/java/io/jboot/support/sentinel/datasource/SentinelDatasourceFactory.java index b91df114..19319e43 100644 --- a/src/main/java/io/jboot/support/sentinel/datasource/SentinelDatasourceFactory.java +++ b/src/main/java/io/jboot/support/sentinel/datasource/SentinelDatasourceFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/datasource/ZookeeperDatasourceConfig.java b/src/main/java/io/jboot/support/sentinel/datasource/ZookeeperDatasourceConfig.java index eb715067..fee206b6 100644 --- a/src/main/java/io/jboot/support/sentinel/datasource/ZookeeperDatasourceConfig.java +++ b/src/main/java/io/jboot/support/sentinel/datasource/ZookeeperDatasourceConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/sentinel/datasource/ZookeeperDatasourceFactory.java b/src/main/java/io/jboot/support/sentinel/datasource/ZookeeperDatasourceFactory.java index 576e8155..1691f5ff 100644 --- a/src/main/java/io/jboot/support/sentinel/datasource/ZookeeperDatasourceFactory.java +++ b/src/main/java/io/jboot/support/sentinel/datasource/ZookeeperDatasourceFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroConfig.java b/src/main/java/io/jboot/support/shiro/JbootShiroConfig.java index b0002a38..7d37043b 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroConfig.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroFilter.java b/src/main/java/io/jboot/support/shiro/JbootShiroFilter.java index d1875388..c2122585 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroFilter.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroFilter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java b/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java index e2e41c9c..ebc6c9f8 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroInvokeListener.java b/src/main/java/io/jboot/support/shiro/JbootShiroInvokeListener.java index c3b73d83..349863b5 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroInvokeListener.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroInvokeListener.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/JbootShiroManager.java b/src/main/java/io/jboot/support/shiro/JbootShiroManager.java index 4fb3f9f5..be4124bd 100644 --- a/src/main/java/io/jboot/support/shiro/JbootShiroManager.java +++ b/src/main/java/io/jboot/support/shiro/JbootShiroManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/ShiroAuthorizeProcesserInvoker.java b/src/main/java/io/jboot/support/shiro/ShiroAuthorizeProcesserInvoker.java index 48271fe5..665761a3 100644 --- a/src/main/java/io/jboot/support/shiro/ShiroAuthorizeProcesserInvoker.java +++ b/src/main/java/io/jboot/support/shiro/ShiroAuthorizeProcesserInvoker.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java b/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java index 56dacde2..0644a3fd 100644 --- a/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java +++ b/src/main/java/io/jboot/support/shiro/ShiroInterceptorBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/cache/JbootShiroCache.java b/src/main/java/io/jboot/support/shiro/cache/JbootShiroCache.java index cac5b039..613dc4ef 100755 --- a/src/main/java/io/jboot/support/shiro/cache/JbootShiroCache.java +++ b/src/main/java/io/jboot/support/shiro/cache/JbootShiroCache.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/cache/JbootShiroCacheManager.java b/src/main/java/io/jboot/support/shiro/cache/JbootShiroCacheManager.java index b1655882..a32bff47 100755 --- a/src/main/java/io/jboot/support/shiro/cache/JbootShiroCacheManager.java +++ b/src/main/java/io/jboot/support/shiro/cache/JbootShiroCacheManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/directives/JbootShiroDirectiveBase.java b/src/main/java/io/jboot/support/shiro/directives/JbootShiroDirectiveBase.java index f90c3b01..d21fef68 100644 --- a/src/main/java/io/jboot/support/shiro/directives/JbootShiroDirectiveBase.java +++ b/src/main/java/io/jboot/support/shiro/directives/JbootShiroDirectiveBase.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/directives/ShiroAuthenticatedDirective.java b/src/main/java/io/jboot/support/shiro/directives/ShiroAuthenticatedDirective.java index 0e136b98..291d4f69 100644 --- a/src/main/java/io/jboot/support/shiro/directives/ShiroAuthenticatedDirective.java +++ b/src/main/java/io/jboot/support/shiro/directives/ShiroAuthenticatedDirective.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/directives/ShiroGuestDirective.java b/src/main/java/io/jboot/support/shiro/directives/ShiroGuestDirective.java index 0f78df34..dc464cdf 100644 --- a/src/main/java/io/jboot/support/shiro/directives/ShiroGuestDirective.java +++ b/src/main/java/io/jboot/support/shiro/directives/ShiroGuestDirective.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/directives/ShiroHasAllPermissionDirective.java b/src/main/java/io/jboot/support/shiro/directives/ShiroHasAllPermissionDirective.java index b57a79d1..f2c19fb8 100644 --- a/src/main/java/io/jboot/support/shiro/directives/ShiroHasAllPermissionDirective.java +++ b/src/main/java/io/jboot/support/shiro/directives/ShiroHasAllPermissionDirective.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/directives/ShiroHasAllRolesDirective.java b/src/main/java/io/jboot/support/shiro/directives/ShiroHasAllRolesDirective.java index 4a853625..2040b225 100644 --- a/src/main/java/io/jboot/support/shiro/directives/ShiroHasAllRolesDirective.java +++ b/src/main/java/io/jboot/support/shiro/directives/ShiroHasAllRolesDirective.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/directives/ShiroHasAnyPermissionDirective.java b/src/main/java/io/jboot/support/shiro/directives/ShiroHasAnyPermissionDirective.java index f54d6e4e..020fd2c2 100644 --- a/src/main/java/io/jboot/support/shiro/directives/ShiroHasAnyPermissionDirective.java +++ b/src/main/java/io/jboot/support/shiro/directives/ShiroHasAnyPermissionDirective.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/directives/ShiroHasAnyRolesDirective.java b/src/main/java/io/jboot/support/shiro/directives/ShiroHasAnyRolesDirective.java index d3122faa..f2a9c460 100644 --- a/src/main/java/io/jboot/support/shiro/directives/ShiroHasAnyRolesDirective.java +++ b/src/main/java/io/jboot/support/shiro/directives/ShiroHasAnyRolesDirective.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/directives/ShiroHasPermissionDirective.java b/src/main/java/io/jboot/support/shiro/directives/ShiroHasPermissionDirective.java index 08a00264..047c97f9 100644 --- a/src/main/java/io/jboot/support/shiro/directives/ShiroHasPermissionDirective.java +++ b/src/main/java/io/jboot/support/shiro/directives/ShiroHasPermissionDirective.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/directives/ShiroHasRoleDirective.java b/src/main/java/io/jboot/support/shiro/directives/ShiroHasRoleDirective.java index f3e44c4c..aca55bc3 100644 --- a/src/main/java/io/jboot/support/shiro/directives/ShiroHasRoleDirective.java +++ b/src/main/java/io/jboot/support/shiro/directives/ShiroHasRoleDirective.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/directives/ShiroNoAuthenticatedDirective.java b/src/main/java/io/jboot/support/shiro/directives/ShiroNoAuthenticatedDirective.java index c4859828..8e07f373 100644 --- a/src/main/java/io/jboot/support/shiro/directives/ShiroNoAuthenticatedDirective.java +++ b/src/main/java/io/jboot/support/shiro/directives/ShiroNoAuthenticatedDirective.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/directives/ShiroNotHasPermissionDirective.java b/src/main/java/io/jboot/support/shiro/directives/ShiroNotHasPermissionDirective.java index 18bbb6a6..7b5dac03 100644 --- a/src/main/java/io/jboot/support/shiro/directives/ShiroNotHasPermissionDirective.java +++ b/src/main/java/io/jboot/support/shiro/directives/ShiroNotHasPermissionDirective.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/directives/ShiroNotHasRoleDirective.java b/src/main/java/io/jboot/support/shiro/directives/ShiroNotHasRoleDirective.java index aeeeb93c..7509e13a 100644 --- a/src/main/java/io/jboot/support/shiro/directives/ShiroNotHasRoleDirective.java +++ b/src/main/java/io/jboot/support/shiro/directives/ShiroNotHasRoleDirective.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/directives/ShiroPrincipalDirective.java b/src/main/java/io/jboot/support/shiro/directives/ShiroPrincipalDirective.java index 7e2db22c..8ee59b2c 100644 --- a/src/main/java/io/jboot/support/shiro/directives/ShiroPrincipalDirective.java +++ b/src/main/java/io/jboot/support/shiro/directives/ShiroPrincipalDirective.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/processer/AuthorizeResult.java b/src/main/java/io/jboot/support/shiro/processer/AuthorizeResult.java index ff8abd5b..adcd662a 100644 --- a/src/main/java/io/jboot/support/shiro/processer/AuthorizeResult.java +++ b/src/main/java/io/jboot/support/shiro/processer/AuthorizeResult.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/processer/IShiroAuthorizeProcesser.java b/src/main/java/io/jboot/support/shiro/processer/IShiroAuthorizeProcesser.java index 211897ec..a22951af 100644 --- a/src/main/java/io/jboot/support/shiro/processer/IShiroAuthorizeProcesser.java +++ b/src/main/java/io/jboot/support/shiro/processer/IShiroAuthorizeProcesser.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/processer/ShiroClear.java b/src/main/java/io/jboot/support/shiro/processer/ShiroClear.java index 26d2d68d..4bfa8cf5 100644 --- a/src/main/java/io/jboot/support/shiro/processer/ShiroClear.java +++ b/src/main/java/io/jboot/support/shiro/processer/ShiroClear.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresAuthenticationProcesser.java b/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresAuthenticationProcesser.java index deaaf5a3..99304ca6 100644 --- a/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresAuthenticationProcesser.java +++ b/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresAuthenticationProcesser.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresGuestProcesser.java b/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresGuestProcesser.java index 3c7e0c90..994cd679 100644 --- a/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresGuestProcesser.java +++ b/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresGuestProcesser.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresPermissionsProcesser.java b/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresPermissionsProcesser.java index 2c434a78..1496213b 100644 --- a/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresPermissionsProcesser.java +++ b/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresPermissionsProcesser.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresRolesProcesser.java b/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresRolesProcesser.java index 458b56c2..9b394df0 100644 --- a/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresRolesProcesser.java +++ b/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresRolesProcesser.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresUserProcesser.java b/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresUserProcesser.java index 5e62f212..1174604c 100644 --- a/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresUserProcesser.java +++ b/src/main/java/io/jboot/support/shiro/processer/ShiroRequiresUserProcesser.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/swagger/ControllerReaderExtension.java b/src/main/java/io/jboot/support/swagger/ControllerReaderExtension.java index a092e341..55fca002 100644 --- a/src/main/java/io/jboot/support/swagger/ControllerReaderExtension.java +++ b/src/main/java/io/jboot/support/swagger/ControllerReaderExtension.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/swagger/JbootSwaggerConfig.java b/src/main/java/io/jboot/support/swagger/JbootSwaggerConfig.java index 05bf1a62..82c564d2 100644 --- a/src/main/java/io/jboot/support/swagger/JbootSwaggerConfig.java +++ b/src/main/java/io/jboot/support/swagger/JbootSwaggerConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/swagger/JbootSwaggerController.java b/src/main/java/io/jboot/support/swagger/JbootSwaggerController.java index e7beca15..25054461 100644 --- a/src/main/java/io/jboot/support/swagger/JbootSwaggerController.java +++ b/src/main/java/io/jboot/support/swagger/JbootSwaggerController.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/swagger/JbootSwaggerManager.java b/src/main/java/io/jboot/support/swagger/JbootSwaggerManager.java index dbff298b..56e2f951 100644 --- a/src/main/java/io/jboot/support/swagger/JbootSwaggerManager.java +++ b/src/main/java/io/jboot/support/swagger/JbootSwaggerManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/swagger/Reader.java b/src/main/java/io/jboot/support/swagger/Reader.java index a1702aeb..bf3be3ee 100644 --- a/src/main/java/io/jboot/support/swagger/Reader.java +++ b/src/main/java/io/jboot/support/swagger/Reader.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/swagger/ReaderContext.java b/src/main/java/io/jboot/support/swagger/ReaderContext.java index 229ad474..b2b98869 100644 --- a/src/main/java/io/jboot/support/swagger/ReaderContext.java +++ b/src/main/java/io/jboot/support/swagger/ReaderContext.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/swagger/RefPropertySerializer.java b/src/main/java/io/jboot/support/swagger/RefPropertySerializer.java index 3cefacae..b132b98d 100644 --- a/src/main/java/io/jboot/support/swagger/RefPropertySerializer.java +++ b/src/main/java/io/jboot/support/swagger/RefPropertySerializer.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/swagger/SwaggerPath.java b/src/main/java/io/jboot/support/swagger/SwaggerPath.java index 1f6a7d4a..eae5a0f7 100644 --- a/src/main/java/io/jboot/support/swagger/SwaggerPath.java +++ b/src/main/java/io/jboot/support/swagger/SwaggerPath.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/CPI.java b/src/main/java/io/jboot/test/CPI.java index df0a17c6..847d0e82 100644 --- a/src/main/java/io/jboot/test/CPI.java +++ b/src/main/java/io/jboot/test/CPI.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/MockApp.java b/src/main/java/io/jboot/test/MockApp.java index 88dafab7..ef1d73dd 100644 --- a/src/main/java/io/jboot/test/MockApp.java +++ b/src/main/java/io/jboot/test/MockApp.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/MockClass.java b/src/main/java/io/jboot/test/MockClass.java index 105032b1..2b51b681 100644 --- a/src/main/java/io/jboot/test/MockClass.java +++ b/src/main/java/io/jboot/test/MockClass.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/MockClassInfo.java b/src/main/java/io/jboot/test/MockClassInfo.java index 0d7ee1a7..8932ff97 100644 --- a/src/main/java/io/jboot/test/MockClassInfo.java +++ b/src/main/java/io/jboot/test/MockClassInfo.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/MockExceptions.java b/src/main/java/io/jboot/test/MockExceptions.java index 93c2d32a..42bf44eb 100644 --- a/src/main/java/io/jboot/test/MockExceptions.java +++ b/src/main/java/io/jboot/test/MockExceptions.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/MockMethod.java b/src/main/java/io/jboot/test/MockMethod.java index 72fd5aec..f31cca3c 100644 --- a/src/main/java/io/jboot/test/MockMethod.java +++ b/src/main/java/io/jboot/test/MockMethod.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/MockMethodInfo.java b/src/main/java/io/jboot/test/MockMethodInfo.java index 407bd5fe..4784d81a 100644 --- a/src/main/java/io/jboot/test/MockMethodInfo.java +++ b/src/main/java/io/jboot/test/MockMethodInfo.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/MockMethodInterceptor.java b/src/main/java/io/jboot/test/MockMethodInterceptor.java index d8c9562d..64c3e4da 100644 --- a/src/main/java/io/jboot/test/MockMethodInterceptor.java +++ b/src/main/java/io/jboot/test/MockMethodInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/MockMvc.java b/src/main/java/io/jboot/test/MockMvc.java index 4437122d..eda908c1 100644 --- a/src/main/java/io/jboot/test/MockMvc.java +++ b/src/main/java/io/jboot/test/MockMvc.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/MockMvcAsserter.java b/src/main/java/io/jboot/test/MockMvcAsserter.java index 794f8f97..d2421bfe 100644 --- a/src/main/java/io/jboot/test/MockMvcAsserter.java +++ b/src/main/java/io/jboot/test/MockMvcAsserter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/MockMvcJsonAsserter.java b/src/main/java/io/jboot/test/MockMvcJsonAsserter.java index 092bee2f..6aa66299 100644 --- a/src/main/java/io/jboot/test/MockMvcJsonAsserter.java +++ b/src/main/java/io/jboot/test/MockMvcJsonAsserter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/MockMvcResult.java b/src/main/java/io/jboot/test/MockMvcResult.java index 4c3cb476..e7a1d51a 100644 --- a/src/main/java/io/jboot/test/MockMvcResult.java +++ b/src/main/java/io/jboot/test/MockMvcResult.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/MockMvcTrueAsserter.java b/src/main/java/io/jboot/test/MockMvcTrueAsserter.java index b0e178f1..0ea822f2 100644 --- a/src/main/java/io/jboot/test/MockMvcTrueAsserter.java +++ b/src/main/java/io/jboot/test/MockMvcTrueAsserter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/MockProxy.java b/src/main/java/io/jboot/test/MockProxy.java index f9b059f3..ac8d91bd 100644 --- a/src/main/java/io/jboot/test/MockProxy.java +++ b/src/main/java/io/jboot/test/MockProxy.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/TestConfig.java b/src/main/java/io/jboot/test/TestConfig.java index b3383eb0..e720a18e 100644 --- a/src/main/java/io/jboot/test/TestConfig.java +++ b/src/main/java/io/jboot/test/TestConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/junit4/JbootRunner.java b/src/main/java/io/jboot/test/junit4/JbootRunner.java index 63121d56..827ec809 100644 --- a/src/main/java/io/jboot/test/junit4/JbootRunner.java +++ b/src/main/java/io/jboot/test/junit4/JbootRunner.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/junit5/JbootExtension.java b/src/main/java/io/jboot/test/junit5/JbootExtension.java index 59bf7714..d1441fad 100644 --- a/src/main/java/io/jboot/test/junit5/JbootExtension.java +++ b/src/main/java/io/jboot/test/junit5/JbootExtension.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/web/MockActionHandler.java b/src/main/java/io/jboot/test/web/MockActionHandler.java index 6ef2738e..83cda363 100644 --- a/src/main/java/io/jboot/test/web/MockActionHandler.java +++ b/src/main/java/io/jboot/test/web/MockActionHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/web/MockCoreConfig.java b/src/main/java/io/jboot/test/web/MockCoreConfig.java index 144a9ffa..35890a84 100644 --- a/src/main/java/io/jboot/test/web/MockCoreConfig.java +++ b/src/main/java/io/jboot/test/web/MockCoreConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/web/MockFilterChain.java b/src/main/java/io/jboot/test/web/MockFilterChain.java index cf3c766c..55b30bd1 100644 --- a/src/main/java/io/jboot/test/web/MockFilterChain.java +++ b/src/main/java/io/jboot/test/web/MockFilterChain.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/web/MockFilterConfig.java b/src/main/java/io/jboot/test/web/MockFilterConfig.java index 79cad483..fd3e3b73 100644 --- a/src/main/java/io/jboot/test/web/MockFilterConfig.java +++ b/src/main/java/io/jboot/test/web/MockFilterConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/web/MockHttpServletRequest.java b/src/main/java/io/jboot/test/web/MockHttpServletRequest.java index 744739a2..744f5822 100644 --- a/src/main/java/io/jboot/test/web/MockHttpServletRequest.java +++ b/src/main/java/io/jboot/test/web/MockHttpServletRequest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/web/MockHttpServletResponse.java b/src/main/java/io/jboot/test/web/MockHttpServletResponse.java index f6b492d2..ec690b2a 100644 --- a/src/main/java/io/jboot/test/web/MockHttpServletResponse.java +++ b/src/main/java/io/jboot/test/web/MockHttpServletResponse.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/web/MockHttpSession.java b/src/main/java/io/jboot/test/web/MockHttpSession.java index 98fd9f5e..bbcdb297 100644 --- a/src/main/java/io/jboot/test/web/MockHttpSession.java +++ b/src/main/java/io/jboot/test/web/MockHttpSession.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/web/MockJFinalFilter.java b/src/main/java/io/jboot/test/web/MockJFinalFilter.java index f8718141..d4c0b79d 100644 --- a/src/main/java/io/jboot/test/web/MockJFinalFilter.java +++ b/src/main/java/io/jboot/test/web/MockJFinalFilter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/web/MockServletContext.java b/src/main/java/io/jboot/test/web/MockServletContext.java index b79c15dc..32f62425 100644 --- a/src/main/java/io/jboot/test/web/MockServletContext.java +++ b/src/main/java/io/jboot/test/web/MockServletContext.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/test/web/MockServletInputStream.java b/src/main/java/io/jboot/test/web/MockServletInputStream.java index 91767cc8..4330982e 100644 --- a/src/main/java/io/jboot/test/web/MockServletInputStream.java +++ b/src/main/java/io/jboot/test/web/MockServletInputStream.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/ArrayUtil.java b/src/main/java/io/jboot/utils/ArrayUtil.java index 08c96003..383b53ff 100644 --- a/src/main/java/io/jboot/utils/ArrayUtil.java +++ b/src/main/java/io/jboot/utils/ArrayUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/CacheUtil.java b/src/main/java/io/jboot/utils/CacheUtil.java index 9790a5de..2f2d2e81 100644 --- a/src/main/java/io/jboot/utils/CacheUtil.java +++ b/src/main/java/io/jboot/utils/CacheUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 31dbb0e6..a722d697 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/ClassType.java b/src/main/java/io/jboot/utils/ClassType.java index 4e281e41..ddcfa974 100644 --- a/src/main/java/io/jboot/utils/ClassType.java +++ b/src/main/java/io/jboot/utils/ClassType.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/ClassUtil.java b/src/main/java/io/jboot/utils/ClassUtil.java index 476aa6c3..160a545f 100644 --- a/src/main/java/io/jboot/utils/ClassUtil.java +++ b/src/main/java/io/jboot/utils/ClassUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/CollectionUtil.java b/src/main/java/io/jboot/utils/CollectionUtil.java index 3f588234..f1950c4b 100644 --- a/src/main/java/io/jboot/utils/CollectionUtil.java +++ b/src/main/java/io/jboot/utils/CollectionUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/ConfigUtil.java b/src/main/java/io/jboot/utils/ConfigUtil.java index fcaa3932..70e42adc 100644 --- a/src/main/java/io/jboot/utils/ConfigUtil.java +++ b/src/main/java/io/jboot/utils/ConfigUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/CookieUtil.java b/src/main/java/io/jboot/utils/CookieUtil.java index 473304c5..deea9ca7 100644 --- a/src/main/java/io/jboot/utils/CookieUtil.java +++ b/src/main/java/io/jboot/utils/CookieUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/DateUtil.java b/src/main/java/io/jboot/utils/DateUtil.java index 5016e9cb..fc365314 100644 --- a/src/main/java/io/jboot/utils/DateUtil.java +++ b/src/main/java/io/jboot/utils/DateUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/FileScanner.java b/src/main/java/io/jboot/utils/FileScanner.java index 830525d2..82ed5293 100644 --- a/src/main/java/io/jboot/utils/FileScanner.java +++ b/src/main/java/io/jboot/utils/FileScanner.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index 696df6cf..ceaec482 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/HttpUtil.java b/src/main/java/io/jboot/utils/HttpUtil.java index 21875e8e..6a9e1b08 100644 --- a/src/main/java/io/jboot/utils/HttpUtil.java +++ b/src/main/java/io/jboot/utils/HttpUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/ModelUtil.java b/src/main/java/io/jboot/utils/ModelUtil.java index c62c014f..9bf0c825 100644 --- a/src/main/java/io/jboot/utils/ModelUtil.java +++ b/src/main/java/io/jboot/utils/ModelUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/NamedThreadFactory.java b/src/main/java/io/jboot/utils/NamedThreadFactory.java index 6c0503b4..be819cdf 100644 --- a/src/main/java/io/jboot/utils/NamedThreadFactory.java +++ b/src/main/java/io/jboot/utils/NamedThreadFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/NamedThreadPools.java b/src/main/java/io/jboot/utils/NamedThreadPools.java index 9d048a0c..ce8ff773 100644 --- a/src/main/java/io/jboot/utils/NamedThreadPools.java +++ b/src/main/java/io/jboot/utils/NamedThreadPools.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/ObjectFunc.java b/src/main/java/io/jboot/utils/ObjectFunc.java index 9b3ba540..9a80a758 100644 --- a/src/main/java/io/jboot/utils/ObjectFunc.java +++ b/src/main/java/io/jboot/utils/ObjectFunc.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/ObjectUtil.java b/src/main/java/io/jboot/utils/ObjectUtil.java index 954a883e..87e749d6 100644 --- a/src/main/java/io/jboot/utils/ObjectUtil.java +++ b/src/main/java/io/jboot/utils/ObjectUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/QuietlyUtil.java b/src/main/java/io/jboot/utils/QuietlyUtil.java index fe66039b..aa37f67f 100644 --- a/src/main/java/io/jboot/utils/QuietlyUtil.java +++ b/src/main/java/io/jboot/utils/QuietlyUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/ReflectUtil.java b/src/main/java/io/jboot/utils/ReflectUtil.java index ad0f037d..e8b68df2 100644 --- a/src/main/java/io/jboot/utils/ReflectUtil.java +++ b/src/main/java/io/jboot/utils/ReflectUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/RequestUtil.java b/src/main/java/io/jboot/utils/RequestUtil.java index 13b2abac..d295fdb9 100644 --- a/src/main/java/io/jboot/utils/RequestUtil.java +++ b/src/main/java/io/jboot/utils/RequestUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/utils/StrUtil.java b/src/main/java/io/jboot/utils/StrUtil.java index 4c6063d2..b2bb87ba 100644 --- a/src/main/java/io/jboot/utils/StrUtil.java +++ b/src/main/java/io/jboot/utils/StrUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/HttpStatus.java b/src/main/java/io/jboot/web/HttpStatus.java index d6570ff5..569f98d8 100644 --- a/src/main/java/io/jboot/web/HttpStatus.java +++ b/src/main/java/io/jboot/web/HttpStatus.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/JbootWebConfig.java b/src/main/java/io/jboot/web/JbootWebConfig.java index 4a8f503f..0de4c247 100644 --- a/src/main/java/io/jboot/web/JbootWebConfig.java +++ b/src/main/java/io/jboot/web/JbootWebConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/PathVariableActionMapping.java b/src/main/java/io/jboot/web/PathVariableActionMapping.java index f0863b41..5d17efde 100644 --- a/src/main/java/io/jboot/web/PathVariableActionMapping.java +++ b/src/main/java/io/jboot/web/PathVariableActionMapping.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/ResponseEntity.java b/src/main/java/io/jboot/web/ResponseEntity.java index b5dee8a0..29921c55 100644 --- a/src/main/java/io/jboot/web/ResponseEntity.java +++ b/src/main/java/io/jboot/web/ResponseEntity.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/attachment/AttachmentContainer.java b/src/main/java/io/jboot/web/attachment/AttachmentContainer.java index f282eeaf..09a71dcb 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentContainer.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/attachment/AttachmentHandler.java b/src/main/java/io/jboot/web/attachment/AttachmentHandler.java index a9141c14..fa5456d1 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentHandler.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index b327731b..55f6d7d3 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java index 56ecd08b..b2ab1386 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainer.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java index 8dd3bf2e..578a0636 100644 --- a/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java +++ b/src/main/java/io/jboot/web/attachment/LocalAttachmentContainerConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/GetMappingInterceptor.java b/src/main/java/io/jboot/web/controller/GetMappingInterceptor.java index 8f592a65..927d74ff 100644 --- a/src/main/java/io/jboot/web/controller/GetMappingInterceptor.java +++ b/src/main/java/io/jboot/web/controller/GetMappingInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 540398bd..9eca3219 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/JbootControllerContext.java b/src/main/java/io/jboot/web/controller/JbootControllerContext.java index bb4104e8..d37dc841 100644 --- a/src/main/java/io/jboot/web/controller/JbootControllerContext.java +++ b/src/main/java/io/jboot/web/controller/JbootControllerContext.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/JbootControllerManager.java b/src/main/java/io/jboot/web/controller/JbootControllerManager.java index 0baa08fd..244c7388 100644 --- a/src/main/java/io/jboot/web/controller/JbootControllerManager.java +++ b/src/main/java/io/jboot/web/controller/JbootControllerManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/PostMappingInterceptor.java b/src/main/java/io/jboot/web/controller/PostMappingInterceptor.java index 4aab424d..6064e61e 100644 --- a/src/main/java/io/jboot/web/controller/PostMappingInterceptor.java +++ b/src/main/java/io/jboot/web/controller/PostMappingInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/RequestMethodLimitInterceptor.java b/src/main/java/io/jboot/web/controller/RequestMethodLimitInterceptor.java index 2a5576eb..369257d8 100644 --- a/src/main/java/io/jboot/web/controller/RequestMethodLimitInterceptor.java +++ b/src/main/java/io/jboot/web/controller/RequestMethodLimitInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/RequestMethodLimitInterceptorBuilder.java b/src/main/java/io/jboot/web/controller/RequestMethodLimitInterceptorBuilder.java index 7a842346..39335c61 100644 --- a/src/main/java/io/jboot/web/controller/RequestMethodLimitInterceptorBuilder.java +++ b/src/main/java/io/jboot/web/controller/RequestMethodLimitInterceptorBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/annotation/DeleteRequest.java b/src/main/java/io/jboot/web/controller/annotation/DeleteRequest.java index 53bd0699..9b2f0e38 100644 --- a/src/main/java/io/jboot/web/controller/annotation/DeleteRequest.java +++ b/src/main/java/io/jboot/web/controller/annotation/DeleteRequest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/annotation/GetMapping.java b/src/main/java/io/jboot/web/controller/annotation/GetMapping.java index b3c230f5..2b31c15f 100644 --- a/src/main/java/io/jboot/web/controller/annotation/GetMapping.java +++ b/src/main/java/io/jboot/web/controller/annotation/GetMapping.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/annotation/GetRequest.java b/src/main/java/io/jboot/web/controller/annotation/GetRequest.java index 53ca9a3c..546d00f1 100644 --- a/src/main/java/io/jboot/web/controller/annotation/GetRequest.java +++ b/src/main/java/io/jboot/web/controller/annotation/GetRequest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/annotation/PatchRequest.java b/src/main/java/io/jboot/web/controller/annotation/PatchRequest.java index 4424dd43..0f86b983 100644 --- a/src/main/java/io/jboot/web/controller/annotation/PatchRequest.java +++ b/src/main/java/io/jboot/web/controller/annotation/PatchRequest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/annotation/PostMapping.java b/src/main/java/io/jboot/web/controller/annotation/PostMapping.java index ac8a6c22..77398ecc 100644 --- a/src/main/java/io/jboot/web/controller/annotation/PostMapping.java +++ b/src/main/java/io/jboot/web/controller/annotation/PostMapping.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/annotation/PostRequest.java b/src/main/java/io/jboot/web/controller/annotation/PostRequest.java index d73c899c..4b1feca8 100644 --- a/src/main/java/io/jboot/web/controller/annotation/PostRequest.java +++ b/src/main/java/io/jboot/web/controller/annotation/PostRequest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/annotation/PutRequest.java b/src/main/java/io/jboot/web/controller/annotation/PutRequest.java index 124aa7db..2fe289f9 100644 --- a/src/main/java/io/jboot/web/controller/annotation/PutRequest.java +++ b/src/main/java/io/jboot/web/controller/annotation/PutRequest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/controller/annotation/RequestMapping.java b/src/main/java/io/jboot/web/controller/annotation/RequestMapping.java index 3903f045..3928a66b 100644 --- a/src/main/java/io/jboot/web/controller/annotation/RequestMapping.java +++ b/src/main/java/io/jboot/web/controller/annotation/RequestMapping.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/cors/CORSInterceptor.java b/src/main/java/io/jboot/web/cors/CORSInterceptor.java index 354e0dbf..478c14da 100644 --- a/src/main/java/io/jboot/web/cors/CORSInterceptor.java +++ b/src/main/java/io/jboot/web/cors/CORSInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java b/src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java index 0c705d81..0a1b5e1b 100644 --- a/src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java +++ b/src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/directive/JbootPaginateDirective.java b/src/main/java/io/jboot/web/directive/JbootPaginateDirective.java index 3a5c045d..099b83e7 100644 --- a/src/main/java/io/jboot/web/directive/JbootPaginateDirective.java +++ b/src/main/java/io/jboot/web/directive/JbootPaginateDirective.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/directive/SharedEnumObject.java b/src/main/java/io/jboot/web/directive/SharedEnumObject.java index a235e6c2..4884f1af 100644 --- a/src/main/java/io/jboot/web/directive/SharedEnumObject.java +++ b/src/main/java/io/jboot/web/directive/SharedEnumObject.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/directive/annotation/JFinalDirective.java b/src/main/java/io/jboot/web/directive/annotation/JFinalDirective.java index 4edfd559..b276dae9 100644 --- a/src/main/java/io/jboot/web/directive/annotation/JFinalDirective.java +++ b/src/main/java/io/jboot/web/directive/annotation/JFinalDirective.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/directive/annotation/JFinalSharedEnum.java b/src/main/java/io/jboot/web/directive/annotation/JFinalSharedEnum.java index 82ed1237..8308c422 100644 --- a/src/main/java/io/jboot/web/directive/annotation/JFinalSharedEnum.java +++ b/src/main/java/io/jboot/web/directive/annotation/JFinalSharedEnum.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/directive/annotation/JFinalSharedMethod.java b/src/main/java/io/jboot/web/directive/annotation/JFinalSharedMethod.java index f73acc60..b531b2a2 100644 --- a/src/main/java/io/jboot/web/directive/annotation/JFinalSharedMethod.java +++ b/src/main/java/io/jboot/web/directive/annotation/JFinalSharedMethod.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/directive/annotation/JFinalSharedObject.java b/src/main/java/io/jboot/web/directive/annotation/JFinalSharedObject.java index c20c1001..8b77a42a 100644 --- a/src/main/java/io/jboot/web/directive/annotation/JFinalSharedObject.java +++ b/src/main/java/io/jboot/web/directive/annotation/JFinalSharedObject.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/directive/annotation/JFinalSharedStaticMethod.java b/src/main/java/io/jboot/web/directive/annotation/JFinalSharedStaticMethod.java index 123fdc23..ce1847eb 100644 --- a/src/main/java/io/jboot/web/directive/annotation/JFinalSharedStaticMethod.java +++ b/src/main/java/io/jboot/web/directive/annotation/JFinalSharedStaticMethod.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java b/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java index 194d532b..65f3a161 100644 --- a/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java +++ b/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/directive/base/PaginateDirectiveBase.java b/src/main/java/io/jboot/web/directive/base/PaginateDirectiveBase.java index c0e7dc06..422488ed 100644 --- a/src/main/java/io/jboot/web/directive/base/PaginateDirectiveBase.java +++ b/src/main/java/io/jboot/web/directive/base/PaginateDirectiveBase.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/handler/ConsoleColor.java b/src/main/java/io/jboot/web/handler/ConsoleColor.java index 6380f26b..84f3ea64 100644 --- a/src/main/java/io/jboot/web/handler/ConsoleColor.java +++ b/src/main/java/io/jboot/web/handler/ConsoleColor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 25bacbdc..937ee914 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/handler/JbootActionInvocation.java b/src/main/java/io/jboot/web/handler/JbootActionInvocation.java index 6d7fe667..7e872ea7 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionInvocation.java +++ b/src/main/java/io/jboot/web/handler/JbootActionInvocation.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index 8a9ab0a0..5ea4179b 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporterInvocation.java b/src/main/java/io/jboot/web/handler/JbootActionReporterInvocation.java index c71ad853..15b9fc27 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporterInvocation.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporterInvocation.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporterUtil.java b/src/main/java/io/jboot/web/handler/JbootActionReporterUtil.java index 1cbd61aa..1916e309 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporterUtil.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporterUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/handler/JbootHandler.java b/src/main/java/io/jboot/web/handler/JbootHandler.java index 376e12a9..152d577a 100644 --- a/src/main/java/io/jboot/web/handler/JbootHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java b/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java index a079ac0d..2aeb9ee3 100644 --- a/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java +++ b/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/json/JbootJson.java b/src/main/java/io/jboot/web/json/JbootJson.java index 7b506773..421ef9c7 100644 --- a/src/main/java/io/jboot/web/json/JbootJson.java +++ b/src/main/java/io/jboot/web/json/JbootJson.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/json/JbootJsonConfig.java b/src/main/java/io/jboot/web/json/JbootJsonConfig.java index 697235bf..004b814b 100644 --- a/src/main/java/io/jboot/web/json/JbootJsonConfig.java +++ b/src/main/java/io/jboot/web/json/JbootJsonConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/json/JsonBody.java b/src/main/java/io/jboot/web/json/JsonBody.java index 4944db0b..efc9d73e 100644 --- a/src/main/java/io/jboot/web/json/JsonBody.java +++ b/src/main/java/io/jboot/web/json/JsonBody.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/json/JsonBodyParseInterceptor.java b/src/main/java/io/jboot/web/json/JsonBodyParseInterceptor.java index fce280d6..15587107 100644 --- a/src/main/java/io/jboot/web/json/JsonBodyParseInterceptor.java +++ b/src/main/java/io/jboot/web/json/JsonBodyParseInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/json/JsonIgnore.java b/src/main/java/io/jboot/web/json/JsonIgnore.java index c5b47fd9..944028e8 100644 --- a/src/main/java/io/jboot/web/json/JsonIgnore.java +++ b/src/main/java/io/jboot/web/json/JsonIgnore.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootErrorRender.java b/src/main/java/io/jboot/web/render/JbootErrorRender.java index 640ae0b5..fef1907d 100644 --- a/src/main/java/io/jboot/web/render/JbootErrorRender.java +++ b/src/main/java/io/jboot/web/render/JbootErrorRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootHtmlRender.java b/src/main/java/io/jboot/web/render/JbootHtmlRender.java index 23de7726..1595a913 100644 --- a/src/main/java/io/jboot/web/render/JbootHtmlRender.java +++ b/src/main/java/io/jboot/web/render/JbootHtmlRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootJavascriptRender.java b/src/main/java/io/jboot/web/render/JbootJavascriptRender.java index 41d3223a..799b026c 100644 --- a/src/main/java/io/jboot/web/render/JbootJavascriptRender.java +++ b/src/main/java/io/jboot/web/render/JbootJavascriptRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootJsonRender.java b/src/main/java/io/jboot/web/render/JbootJsonRender.java index c131136a..6bf71374 100644 --- a/src/main/java/io/jboot/web/render/JbootJsonRender.java +++ b/src/main/java/io/jboot/web/render/JbootJsonRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootRender.java b/src/main/java/io/jboot/web/render/JbootRender.java index 6e657091..31cc1472 100644 --- a/src/main/java/io/jboot/web/render/JbootRender.java +++ b/src/main/java/io/jboot/web/render/JbootRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootRenderFactory.java b/src/main/java/io/jboot/web/render/JbootRenderFactory.java index 6f6a55a7..27a7e9aa 100644 --- a/src/main/java/io/jboot/web/render/JbootRenderFactory.java +++ b/src/main/java/io/jboot/web/render/JbootRenderFactory.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java b/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java index 51a49368..332dd695 100644 --- a/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java +++ b/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index d32bdfd1..7f24ede0 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootTemplateRender.java b/src/main/java/io/jboot/web/render/JbootTemplateRender.java index 3459515b..65ed97f0 100644 --- a/src/main/java/io/jboot/web/render/JbootTemplateRender.java +++ b/src/main/java/io/jboot/web/render/JbootTemplateRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootTextRender.java b/src/main/java/io/jboot/web/render/JbootTextRender.java index a6331ee3..1e7510d2 100644 --- a/src/main/java/io/jboot/web/render/JbootTextRender.java +++ b/src/main/java/io/jboot/web/render/JbootTextRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/JbootXmlRender.java b/src/main/java/io/jboot/web/render/JbootXmlRender.java index cbbcc944..58b0e429 100644 --- a/src/main/java/io/jboot/web/render/JbootXmlRender.java +++ b/src/main/java/io/jboot/web/render/JbootXmlRender.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/cdn/CdnUtil.java b/src/main/java/io/jboot/web/render/cdn/CdnUtil.java index 223c8972..94d3ba45 100644 --- a/src/main/java/io/jboot/web/render/cdn/CdnUtil.java +++ b/src/main/java/io/jboot/web/render/cdn/CdnUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/render/cdn/JbootWebCdnConfig.java b/src/main/java/io/jboot/web/render/cdn/JbootWebCdnConfig.java index 6681f61f..f9e4f6d2 100644 --- a/src/main/java/io/jboot/web/render/cdn/JbootWebCdnConfig.java +++ b/src/main/java/io/jboot/web/render/cdn/JbootWebCdnConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/session/JbootHttpSession.java b/src/main/java/io/jboot/web/session/JbootHttpSession.java index a31e6ef0..8aebe54d 100644 --- a/src/main/java/io/jboot/web/session/JbootHttpSession.java +++ b/src/main/java/io/jboot/web/session/JbootHttpSession.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/session/JbootServletRequestWrapper.java b/src/main/java/io/jboot/web/session/JbootServletRequestWrapper.java index cd67f13f..6aab4b7c 100644 --- a/src/main/java/io/jboot/web/session/JbootServletRequestWrapper.java +++ b/src/main/java/io/jboot/web/session/JbootServletRequestWrapper.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/session/JbootSessionConfig.java b/src/main/java/io/jboot/web/session/JbootSessionConfig.java index b24b6e2d..58d20bbc 100644 --- a/src/main/java/io/jboot/web/session/JbootSessionConfig.java +++ b/src/main/java/io/jboot/web/session/JbootSessionConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/validate/CaptchaValidate.java b/src/main/java/io/jboot/web/validate/CaptchaValidate.java index ea0e0784..ae58c8fe 100644 --- a/src/main/java/io/jboot/web/validate/CaptchaValidate.java +++ b/src/main/java/io/jboot/web/validate/CaptchaValidate.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/validate/EmptyValidate.java b/src/main/java/io/jboot/web/validate/EmptyValidate.java index 9ef2a7d2..4bebc93a 100644 --- a/src/main/java/io/jboot/web/validate/EmptyValidate.java +++ b/src/main/java/io/jboot/web/validate/EmptyValidate.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/validate/Form.java b/src/main/java/io/jboot/web/validate/Form.java index f4374052..9727ab41 100644 --- a/src/main/java/io/jboot/web/validate/Form.java +++ b/src/main/java/io/jboot/web/validate/Form.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/validate/FormType.java b/src/main/java/io/jboot/web/validate/FormType.java index 1c48637c..ce996407 100644 --- a/src/main/java/io/jboot/web/validate/FormType.java +++ b/src/main/java/io/jboot/web/validate/FormType.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/validate/Regex.java b/src/main/java/io/jboot/web/validate/Regex.java index 2c6d300e..fd64fef8 100644 --- a/src/main/java/io/jboot/web/validate/Regex.java +++ b/src/main/java/io/jboot/web/validate/Regex.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/validate/RegexForm.java b/src/main/java/io/jboot/web/validate/RegexForm.java index a0ad0d81..2db227a5 100644 --- a/src/main/java/io/jboot/web/validate/RegexForm.java +++ b/src/main/java/io/jboot/web/validate/RegexForm.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/validate/RegexValidate.java b/src/main/java/io/jboot/web/validate/RegexValidate.java index 4a99a435..6bf4a3d7 100644 --- a/src/main/java/io/jboot/web/validate/RegexValidate.java +++ b/src/main/java/io/jboot/web/validate/RegexValidate.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/validate/ValidateRenderType.java b/src/main/java/io/jboot/web/validate/ValidateRenderType.java index da89d34e..90dc183b 100644 --- a/src/main/java/io/jboot/web/validate/ValidateRenderType.java +++ b/src/main/java/io/jboot/web/validate/ValidateRenderType.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/validate/interceptor/CaptchaValidateInterceptor.java b/src/main/java/io/jboot/web/validate/interceptor/CaptchaValidateInterceptor.java index ac81447c..df18e571 100644 --- a/src/main/java/io/jboot/web/validate/interceptor/CaptchaValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/interceptor/CaptchaValidateInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java b/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java index b1bb5421..4d1f0ea5 100644 --- a/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/validate/interceptor/RegexValidateInterceptor.java b/src/main/java/io/jboot/web/validate/interceptor/RegexValidateInterceptor.java index f5e162b0..036952aa 100644 --- a/src/main/java/io/jboot/web/validate/interceptor/RegexValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/interceptor/RegexValidateInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorBuilder.java b/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorBuilder.java index 31b4f559..722bf5e6 100644 --- a/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorBuilder.java +++ b/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorUtil.java b/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorUtil.java index b4bf86ea..f11e604c 100644 --- a/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorUtil.java +++ b/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorUtil.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/xss/XSSHandler.java b/src/main/java/io/jboot/web/xss/XSSHandler.java index a07c9060..a6e29442 100644 --- a/src/main/java/io/jboot/web/xss/XSSHandler.java +++ b/src/main/java/io/jboot/web/xss/XSSHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/web/xss/XSSHttpServletRequestWrapper.java b/src/main/java/io/jboot/web/xss/XSSHttpServletRequestWrapper.java index 3d3ac112..21f34100 100644 --- a/src/main/java/io/jboot/web/xss/XSSHttpServletRequestWrapper.java +++ b/src/main/java/io/jboot/web/xss/XSSHttpServletRequestWrapper.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/wechat/JbootWechatConfig.java b/src/main/java/io/jboot/wechat/JbootWechatConfig.java index 32774a65..aff4243b 100644 --- a/src/main/java/io/jboot/wechat/JbootWechatConfig.java +++ b/src/main/java/io/jboot/wechat/JbootWechatConfig.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/wechat/WechatApis.java b/src/main/java/io/jboot/wechat/WechatApis.java index 20696ff2..4c8b3b6d 100644 --- a/src/main/java/io/jboot/wechat/WechatApis.java +++ b/src/main/java/io/jboot/wechat/WechatApis.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/wechat/controller/JbootWechatController.java b/src/main/java/io/jboot/wechat/controller/JbootWechatController.java index 65be10e7..aa8da69a 100644 --- a/src/main/java/io/jboot/wechat/controller/JbootWechatController.java +++ b/src/main/java/io/jboot/wechat/controller/JbootWechatController.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/wechat/interceptor/WechatApiConfigInterceptor.java b/src/main/java/io/jboot/wechat/interceptor/WechatApiConfigInterceptor.java index 615da6f3..4672c8ff 100644 --- a/src/main/java/io/jboot/wechat/interceptor/WechatApiConfigInterceptor.java +++ b/src/main/java/io/jboot/wechat/interceptor/WechatApiConfigInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/wechat/interceptor/WechatUserInterceptor.java b/src/main/java/io/jboot/wechat/interceptor/WechatUserInterceptor.java index 02315f69..4a9266e7 100644 --- a/src/main/java/io/jboot/wechat/interceptor/WechatUserInterceptor.java +++ b/src/main/java/io/jboot/wechat/interceptor/WechatUserInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/HelloInterceptor.java b/src/test/java/io/jboot/test/HelloInterceptor.java index df543a68..801a20c8 100644 --- a/src/test/java/io/jboot/test/HelloInterceptor.java +++ b/src/test/java/io/jboot/test/HelloInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/HelloWorld.java b/src/test/java/io/jboot/test/HelloWorld.java index c07b2925..19825449 100644 --- a/src/test/java/io/jboot/test/HelloWorld.java +++ b/src/test/java/io/jboot/test/HelloWorld.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/aop/AopConfiguration.java b/src/test/java/io/jboot/test/aop/AopConfiguration.java index 828c2007..c255a29c 100644 --- a/src/test/java/io/jboot/test/aop/AopConfiguration.java +++ b/src/test/java/io/jboot/test/aop/AopConfiguration.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/config/ConfigController.java b/src/test/java/io/jboot/test/config/ConfigController.java index 16f98c0c..ab82c15f 100644 --- a/src/test/java/io/jboot/test/config/ConfigController.java +++ b/src/test/java/io/jboot/test/config/ConfigController.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/config/TestConfigModel.java b/src/test/java/io/jboot/test/config/TestConfigModel.java index 2e195047..63ab0916 100644 --- a/src/test/java/io/jboot/test/config/TestConfigModel.java +++ b/src/test/java/io/jboot/test/config/TestConfigModel.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/file/FileController.java b/src/test/java/io/jboot/test/file/FileController.java index 4b536eb9..93d8364c 100644 --- a/src/test/java/io/jboot/test/file/FileController.java +++ b/src/test/java/io/jboot/test/file/FileController.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/other/ModelUtilTest.java b/src/test/java/io/jboot/test/other/ModelUtilTest.java index b31b0304..65f5127d 100644 --- a/src/test/java/io/jboot/test/other/ModelUtilTest.java +++ b/src/test/java/io/jboot/test/other/ModelUtilTest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java index 1520eb55..b6b433d7 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboClient.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java index 78499110..0a55dc81 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboInterceptor.java @@ -1,5 +1,5 @@ ///** -// * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). +// * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). // *

        // * Licensed under the Apache License, Version 2.0 (the "License"); // * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java b/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java index b784c337..2a74cd5b 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java +++ b/src/test/java/io/jboot/test/rpc/dubbo/DubboServer.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/rpc/motan/MotanClient.java b/src/test/java/io/jboot/test/rpc/motan/MotanClient.java index 42e672ed..07c91cfa 100644 --- a/src/test/java/io/jboot/test/rpc/motan/MotanClient.java +++ b/src/test/java/io/jboot/test/rpc/motan/MotanClient.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/rpc/motan/MotanInterceptor.java b/src/test/java/io/jboot/test/rpc/motan/MotanInterceptor.java index 5a38078f..ef891054 100644 --- a/src/test/java/io/jboot/test/rpc/motan/MotanInterceptor.java +++ b/src/test/java/io/jboot/test/rpc/motan/MotanInterceptor.java @@ -1,5 +1,5 @@ ///** -// * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). +// * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). // *

        // * Licensed under the Apache License, Version 2.0 (the "License"); // * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/rpc/motan/MotanServer.java b/src/test/java/io/jboot/test/rpc/motan/MotanServer.java index daa95832..4891d373 100644 --- a/src/test/java/io/jboot/test/rpc/motan/MotanServer.java +++ b/src/test/java/io/jboot/test/rpc/motan/MotanServer.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/io/jboot/test/sentinel/SentinelController.java b/src/test/java/io/jboot/test/sentinel/SentinelController.java index fd1ea6c9..00a358cf 100644 --- a/src/test/java/io/jboot/test/sentinel/SentinelController.java +++ b/src/test/java/io/jboot/test/sentinel/SentinelController.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2021, Michael Yang 杨福海 (fuhai999@gmail.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. -- Gitee From bc40db26fe9544731e93175506775d7ea9b1243b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 18 Feb 2022 19:53:44 +0800 Subject: [PATCH 1563/1965] =?UTF-8?q?feature:=20Controller=20=E5=BC=BA?= =?UTF-8?q?=E5=8C=96=20String=20=E8=BF=94=E5=9B=9E=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/web/handler/JbootActionHandler.java | 34 ++++++++++- .../handler/PathVariableActionHandler.java | 23 ++++++-- .../jboot/web/render/JbootRenderFactory.java | 2 +- .../web/render/JbootResponseEntityRender.java | 16 ++++- .../web/render/JbootReturnValueRender.java | 44 ++++++++++++-- .../test/controller/IndexController.java | 59 +++++++++++++++++++ 6 files changed, 162 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 937ee914..c94fb172 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -29,6 +29,7 @@ import io.jboot.utils.ClassUtil; import io.jboot.web.controller.JbootControllerContext; import io.jboot.web.render.JbootErrorRender; import io.jboot.web.render.JbootRenderFactory; +import io.jboot.web.render.JbootReturnValueRender; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -172,9 +173,19 @@ public class JbootActionHandler extends ActionHandler { handle(actionUrl, request, response, isHandled); } } else { - if (render == null && void.class != action.getMethod().getReturnType() && renderManager.getRenderFactory() instanceof JbootRenderFactory) { - JbootRenderFactory jbootRenderFactory = (JbootRenderFactory) renderManager.getRenderFactory(); - render = jbootRenderFactory.getReturnValueRender(action, invocation.getReturnValue()); + if (render == null && void.class != action.getMethod().getReturnType() + && renderManager.getRenderFactory() instanceof JbootRenderFactory) { + + JbootRenderFactory factory = (JbootRenderFactory) renderManager.getRenderFactory(); + JbootReturnValueRender returnValueRender = factory.getReturnValueRender(action, invocation.getReturnValue()); + + String forwardTo = returnValueRender.getForwardTo(); + if (forwardTo != null) { + handle(getRealForwrdTo(forwardTo, target, action), request, response, isHandled); + return; + } else { + render = returnValueRender; + } } if (render == null) { @@ -185,6 +196,23 @@ public class JbootActionHandler extends ActionHandler { } } + public String getRealForwrdTo(String forwardTo, String currentTarget, Action action) { + if ("".equals(forwardTo)) { + throw new IllegalArgumentException(ClassUtil.buildMethodString(action.getMethod()) + ": The forward key can not be blank."); + } + + if (forwardTo.startsWith("/")) { + return forwardTo; + } + + + if (forwardTo.startsWith("./")) { + return currentTarget.substring(0, currentTarget.lastIndexOf("/")) + forwardTo.substring(1); + } + + return "/" + forwardTo; + } + /** * 处理错误信息 diff --git a/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java b/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java index 2aeb9ee3..e3e43ba4 100644 --- a/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java +++ b/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java @@ -24,6 +24,7 @@ import com.jfinal.render.RenderException; import io.jboot.components.valid.ValidException; import io.jboot.web.controller.JbootControllerContext; import io.jboot.web.render.JbootRenderFactory; +import io.jboot.web.render.JbootReturnValueRender; import io.jboot.web.session.JbootServletRequestWrapper; import javax.servlet.http.HttpServletRequest; @@ -72,11 +73,11 @@ public class PathVariableActionHandler extends JbootActionHandler { controller = controllerFactory.getController(action.getControllerClass()); //controller.init(request, response, urlPara[0]); //存在封装的路径参数 - if(urlPara[1] != null){ + if (urlPara[1] != null) { Map params = Splitter.on("&").withKeyValueSeparator("=").split(urlPara[1]); PathVariableWrappedRequest wrappedRequest = new PathVariableWrappedRequest(request, response, params); CPI._init_(controller, action, wrappedRequest, response, urlPara[0]); - }else { + } else { CPI._init_(controller, action, request, response, urlPara[0]); } JbootControllerContext.hold(controller); @@ -132,9 +133,18 @@ public class PathVariableActionHandler extends JbootActionHandler { handle(actionUrl, request, response, isHandled); } } else { - if (render == null && void.class != action.getMethod().getReturnType() && renderManager.getRenderFactory() instanceof JbootRenderFactory) { - JbootRenderFactory jbootRenderFactory = (JbootRenderFactory) renderManager.getRenderFactory(); - render = jbootRenderFactory.getReturnValueRender(action, invocation.getReturnValue()); + if (render == null && void.class != action.getMethod().getReturnType() + && renderManager.getRenderFactory() instanceof JbootRenderFactory) { + + JbootRenderFactory factory = (JbootRenderFactory) renderManager.getRenderFactory(); + JbootReturnValueRender returnValueRender = factory.getReturnValueRender(action, invocation.getReturnValue()); + String forwardTo = returnValueRender.getForwardTo(); + if (forwardTo != null) { + handle(getRealForwrdTo(forwardTo, target, action), request, response, isHandled); + return; + } else { + render = returnValueRender; + } } if (render == null) { @@ -148,9 +158,10 @@ public class PathVariableActionHandler extends JbootActionHandler { /** * 请求包装类用于将路径变量的URL中的额外参数加入request中 */ - private class PathVariableWrappedRequest extends JbootServletRequestWrapper{ + private class PathVariableWrappedRequest extends JbootServletRequestWrapper { private final Map modifiableParameters; private Map allParameters = null; + public PathVariableWrappedRequest(HttpServletRequest request, HttpServletResponse response, Map params) { super(request, response); diff --git a/src/main/java/io/jboot/web/render/JbootRenderFactory.java b/src/main/java/io/jboot/web/render/JbootRenderFactory.java index 27a7e9aa..0251e0b0 100644 --- a/src/main/java/io/jboot/web/render/JbootRenderFactory.java +++ b/src/main/java/io/jboot/web/render/JbootRenderFactory.java @@ -132,7 +132,7 @@ public class JbootRenderFactory extends RenderFactory { return new JbootCaptchaRender(); } - public Render getReturnValueRender(Action action, Object returnValue) { + public JbootReturnValueRender getReturnValueRender(Action action, Object returnValue) { return new JbootReturnValueRender(action, returnValue); } diff --git a/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java b/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java index 332dd695..a8f90968 100644 --- a/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java +++ b/src/main/java/io/jboot/web/render/JbootResponseEntityRender.java @@ -18,10 +18,12 @@ package io.jboot.web.render; import com.jfinal.kit.JsonKit; import com.jfinal.render.Render; import com.jfinal.render.RenderException; +import io.jboot.utils.DateUtil; import io.jboot.web.ResponseEntity; import java.io.IOException; import java.io.PrintWriter; +import java.util.Date; import java.util.Map; /** @@ -53,9 +55,19 @@ public class JbootResponseEntityRender extends Render { } } - String jsonText = responseEntity.getBody() == null ? "" : JsonKit.toJson(responseEntity.getBody()); + Object body = responseEntity.getBody(); + String bodyString = null; + if (body == null) { + bodyString = ""; + } else if (body instanceof String) { + bodyString = (String) body; + } else if (body instanceof Date) { + bodyString = DateUtil.toDateTimeString((Date) body); + } else { + JsonKit.toJson(body); + } writer = response.getWriter(); - writer.write(jsonText); + writer.write(bodyString); // writer.flush(); } catch (IOException e) { throw new RenderException(e); diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index 7f24ede0..e9db94f9 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -42,6 +42,8 @@ public class JbootReturnValueRender extends Render { protected Object value; protected Render render; + protected String forwardTo; + public JbootReturnValueRender(Action action, Object returnValue) { this.action = action; @@ -65,9 +67,14 @@ public class JbootReturnValueRender extends Render { this.render = new JbootResponseEntityRender((ResponseEntity) value); } else if (this.value instanceof String) { String newVal = ((String) value).toLowerCase(); - if (newVal.endsWith(".html")) { + + //template + if (newVal.endsWith(".html") && !newVal.contains(":")) { this.render = factory.getTemplateRender((String) value); - } else if (newVal.startsWith("error") && newVal.length() > 8) { + } + + //error + else if (newVal.startsWith("error") && newVal.length() > 8) { String trim = newVal.substring(5).trim(); if (trim.startsWith(":")) { String errorCodeStr = trim.substring(1).trim(); @@ -78,7 +85,34 @@ public class JbootReturnValueRender extends Render { if (this.render == null) { this.render = factory.getTextRender((String) value); } - } else { + } + + //forward + else if (newVal.startsWith("forward")) { + String trim = newVal.substring(7).trim(); + if (trim.startsWith(":")) { + this.forwardTo = trim.substring(1).trim(); + } else { + this.render = factory.getTextRender((String) value); + } + } + + //redirect + else if (newVal.startsWith("redirect")) { + String trim = newVal.substring(8).trim(); + if (trim.startsWith(":")) { + String redirectTo = trim.substring(1).trim(); + if (StrUtil.isNotBlank(redirectTo)) { + this.render = factory.getRedirectRender(redirectTo); + } + } + if (this.render == null) { + this.render = factory.getTextRender((String) value); + } + } + + //text + else { this.render = factory.getTextRender((String) value); } } else if (this.value instanceof Date) { @@ -123,5 +157,7 @@ public class JbootReturnValueRender extends Render { || c == BigDecimal.class || c == BigInteger.class; } - + public String getForwardTo() { + return forwardTo; + } } diff --git a/src/test/java/io/jboot/test/controller/IndexController.java b/src/test/java/io/jboot/test/controller/IndexController.java index 79e018fb..ca22e9df 100644 --- a/src/test/java/io/jboot/test/controller/IndexController.java +++ b/src/test/java/io/jboot/test/controller/IndexController.java @@ -2,6 +2,7 @@ package io.jboot.test.controller; import com.jfinal.kit.PathKit; import io.jboot.utils.RequestUtil; +import io.jboot.web.ResponseEntity; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.GetRequest; import io.jboot.web.controller.annotation.PostRequest; @@ -14,6 +15,7 @@ public class IndexController extends JbootController { render("/index.html"); } + public void classPath() { renderText(PathKit.getRootClassPath()); } @@ -61,6 +63,63 @@ public class IndexController extends JbootController { renderText(RequestUtil.getCurrentUrl()); } + public String r1(){ + return "text...."; + } + + public String r2(){ + return "error:404"; + } + + + public String r3(){ + return "error : 404"; + } + + public String r4(){ + return "error:500"; + } + + + public String r5(){ + return "error : 500"; + } + + + public String r6(){ + return "index.html"; + } + + public ResponseEntity r7(){ + return ResponseEntity.ok().body("aaa"); + } + + + + public String r8(){ + return "redirect : r1"; + } + + + public String r9(){ + return "redirect:r2"; + } + + public String r10(){ + return "forward : r3"; + } + + + public String r11(){ + return "forward:r4"; + } + + public String r12(){ + return "forward:./r4"; + } + public String r13(){ + return "forward: "; + } } -- Gitee From a4119fd0998195b7c0dc7fffffa5a112ea2d2dea Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 18 Feb 2022 19:57:47 +0800 Subject: [PATCH 1564/1965] v3.13.6 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 6257113f..2e1bd1fa 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.13.5 + 3.13.6 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index b28682d7..ea2e5309 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.13.5 + 3.13.6 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index ac333286..966ace15 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.13.5 + 3.13.6 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.13.5', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.13.6', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 10c289c6..27feb368 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.13.5', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.13.6', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.5/jboot-3.13.5.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.6/jboot-3.13.6.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 46b201b3..d4039cc4 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.13.5"; + public static String VERSION = "3.13.6"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From f900e11739ab4b43dbaa2e3c3aee3e73a5b110e3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 18 Feb 2022 19:57:52 +0800 Subject: [PATCH 1565/1965] v3.13.6 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ pom.xml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2b5648b1..efdb5e37 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.13.5 + 3.13.6 ``` diff --git a/changes.txt b/changes.txt index 49a57250..252a53c7 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.13.6: +优化:强化 Controller 返回值,自动匹配相应的 render +优化:优化 JbootHttpImpl 和 JbootHttpResponse 代码,添加必要的日志输出 +优化:更新 Copyright + + + jboot v3.13.5: 新增:CookieUtil 添加 "defaultPath" 和 "defaultDomain" 的配置 新增:HttpUtil 添加 http 代理的配置支持 diff --git a/pom.xml b/pom.xml index 210ce2cd..8dd0d53a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.6-SNAPSHOT + 3.13.6 jar jboot -- Gitee From 8a789cb53b618fcbe01fbdf219c3d3215e514aec Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 21 Feb 2022 09:24:47 +0800 Subject: [PATCH 1566/1965] v3.13.7 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8dd0d53a..cef6bdb5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.6 + 3.13.7-SNAPSHOT jar jboot -- Gitee From 0f3beb7ef92c146c010a9d5801a3acb67a2b283b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 21 Feb 2022 10:05:55 +0800 Subject: [PATCH 1567/1965] =?UTF-8?q?fixed=EF=BC=9A=E4=BF=AE=E5=A4=8D=20Co?= =?UTF-8?q?ntroller=20=E8=BF=94=E5=9B=9E=E5=80=BC=E6=9C=89=E5=A4=A7?= =?UTF-8?q?=E5=86=99=E5=8F=AF=E8=83=BD=E4=B8=8D=E6=AD=A3=E7=A1=AE=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/render/JbootReturnValueRender.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index e9db94f9..9b8b322c 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -68,14 +68,10 @@ public class JbootReturnValueRender extends Render { } else if (this.value instanceof String) { String newVal = ((String) value).toLowerCase(); - //template - if (newVal.endsWith(".html") && !newVal.contains(":")) { - this.render = factory.getTemplateRender((String) value); - } //error - else if (newVal.startsWith("error") && newVal.length() > 8) { - String trim = newVal.substring(5).trim(); + if (newVal.startsWith("error") && newVal.length() > 8) { + String trim = ((String) value).substring(5).trim(); if (trim.startsWith(":")) { String errorCodeStr = trim.substring(1).trim(); if (StrUtil.isNumeric(errorCodeStr)) { @@ -89,7 +85,7 @@ public class JbootReturnValueRender extends Render { //forward else if (newVal.startsWith("forward")) { - String trim = newVal.substring(7).trim(); + String trim = ((String) value).substring(7).trim(); if (trim.startsWith(":")) { this.forwardTo = trim.substring(1).trim(); } else { @@ -99,7 +95,7 @@ public class JbootReturnValueRender extends Render { //redirect else if (newVal.startsWith("redirect")) { - String trim = newVal.substring(8).trim(); + String trim = ((String) value).substring(8).trim(); if (trim.startsWith(":")) { String redirectTo = trim.substring(1).trim(); if (StrUtil.isNotBlank(redirectTo)) { @@ -111,6 +107,11 @@ public class JbootReturnValueRender extends Render { } } + //template + else if (newVal.endsWith(".html")) { + this.render = factory.getTemplateRender((String) value); + } + //text else { this.render = factory.getTextRender((String) value); -- Gitee From 2d72f9bad19301acfe99e0222aafffb3792d7a6f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 21 Feb 2022 10:38:16 +0800 Subject: [PATCH 1568/1965] feature: mq add stopListening() method. close #I4UELR --- .../java/io/jboot/components/mq/Jbootmq.java | 2 + .../io/jboot/components/mq/JbootmqBase.java | 23 +++++++++ .../components/mq/activemq/JbootActivemq.java | 5 ++ .../mq/aliyunmq/JbootAliyunmqImpl.java | 8 ++++ .../components/mq/local/JbootLocalmqImpl.java | 5 ++ .../components/mq/qpidmq/JbootQpidmqImpl.java | 15 +++++- .../mq/rabbitmq/JbootRabbitmqImpl.java | 5 ++ .../mq/redismq/JbootRedismqImpl.java | 23 ++++++--- .../mq/rocketmq/JbootRocketmqImpl.java | 47 ++++++++++++------- 9 files changed, 108 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/Jbootmq.java b/src/main/java/io/jboot/components/mq/Jbootmq.java index eaa17c4d..4d50d35f 100644 --- a/src/main/java/io/jboot/components/mq/Jbootmq.java +++ b/src/main/java/io/jboot/components/mq/Jbootmq.java @@ -38,6 +38,8 @@ public interface Jbootmq { public boolean startListening(); + public boolean stopListening(); + public JbootmqConfig getConfig(); } diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index 23e13a45..9bc62267 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -186,9 +186,32 @@ public abstract class JbootmqBase implements Jbootmq { return true; } + @Override + public boolean stopListening() { + if (!isStarted) { + throw new JbootException("Jboot MQ has stoped."); + } + + try { + isStarted = false; + onStopListening(); + } catch (Exception ex) { + LogKit.error("Jboot MQ stop fail!", ex); + isStarted = true; + return false; + } + + return true; + } + + public boolean isStarted() { + return isStarted; + } protected abstract void onStartListening(); + protected abstract void onStopListening(); + @Override public JbootmqConfig getConfig() { diff --git a/src/main/java/io/jboot/components/mq/activemq/JbootActivemq.java b/src/main/java/io/jboot/components/mq/activemq/JbootActivemq.java index 10496317..a512e138 100644 --- a/src/main/java/io/jboot/components/mq/activemq/JbootActivemq.java +++ b/src/main/java/io/jboot/components/mq/activemq/JbootActivemq.java @@ -40,4 +40,9 @@ public class JbootActivemq extends JbootmqBase implements Jbootmq { protected void onStartListening() { } + + @Override + protected void onStopListening() { + + } } diff --git a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java index 17e15efc..f9081677 100644 --- a/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java +++ b/src/main/java/io/jboot/components/mq/aliyunmq/JbootAliyunmqImpl.java @@ -57,6 +57,14 @@ public class JbootAliyunmqImpl extends JbootmqBase implements Jbootmq { startBroadCastConsumer(); } + @Override + protected void onStopListening() { + if (consumer != null) { + consumer.shutdown(); + consumer = null; + } + } + public void startQueueConsumer() { Properties properties = createProperties(); diff --git a/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java b/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java index 8b4aab64..369f6596 100644 --- a/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java +++ b/src/main/java/io/jboot/components/mq/local/JbootLocalmqImpl.java @@ -33,6 +33,11 @@ public class JbootLocalmqImpl extends JbootmqBase { //do nothing } + @Override + protected void onStopListening() { + //do nothing + } + @Override public void enqueue(Object message, String toChannel) { notifyListeners(toChannel, message, new LocalmqMessageContext(this)); diff --git a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java index 95309472..746faca0 100644 --- a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java +++ b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java @@ -44,6 +44,9 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { private boolean serializerEnable = true; private JbootQpidmqConfig qpidConfig = null; + private Thread queueThread; + private Thread topicThread; + public JbootQpidmqImpl(JbootmqConfig config) { super(config); @@ -80,6 +83,12 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { } } + @Override + protected void onStopListening() { + queueThread.interrupt(); + topicThread.interrupt(); + } + @Override public void enqueue(Object message, String toChannel) { @@ -173,12 +182,14 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { String queueAddr = getQueueAddr(channel); Destination queue = new AMQAnyDestination(queueAddr); MessageConsumer queueConsumer = session.createConsumer(queue); - new Thread(new ReceiveMsgThread(queueConsumer, channel, serializerEnable)).start(); + queueThread = new Thread(new ReceiveMsgThread(queueConsumer, channel, serializerEnable)); + queueThread.start(); String topicAddr = getTopicAddr(channel); Destination topic = new AMQAnyDestination(topicAddr); MessageConsumer topicConsumer = session.createConsumer(topic); - new Thread(new ReceiveMsgThread(topicConsumer, channel, serializerEnable)).start(); + topicThread = new Thread(new ReceiveMsgThread(topicConsumer, channel, serializerEnable)); + topicThread.start(); } } diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index 0626c249..c3818418 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -97,6 +97,11 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { } } + @Override + protected void onStopListening() { + connection.abort(); + } + public void bindChannel(Channel channel, String name, String orginaChannelName) { if (channel != null) { diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java index d469bf66..f814e149 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java @@ -17,13 +17,13 @@ package io.jboot.components.mq.redismq; import com.jfinal.log.Log; import io.jboot.Jboot; -import io.jboot.utils.ConfigUtil; import io.jboot.components.mq.Jbootmq; import io.jboot.components.mq.JbootmqBase; import io.jboot.components.mq.JbootmqConfig; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.support.redis.JbootRedis; import io.jboot.support.redis.JbootRedisManager; +import io.jboot.utils.ConfigUtil; import io.jboot.utils.StrUtil; import redis.clients.jedis.BinaryJedisPubSub; @@ -36,6 +36,7 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { private JbootRedis redis; private Thread dequeueThread; + private BinaryJedisPubSub jedisPubSub; public JbootRedismqImpl(JbootmqConfig config) { super(config); @@ -68,18 +69,28 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { protected void onStartListening() { String[] channels = this.channels.toArray(new String[]{}); - - redis.subscribe(new BinaryJedisPubSub() { + jedisPubSub = new BinaryJedisPubSub() { @Override public void onMessage(byte[] channel, byte[] message) { - notifyListeners(redis.bytesToKey(channel), getSerializer().deserialize(message), new RedismqMessageContext(JbootRedismqImpl.this)); + notifyListeners(redis.bytesToKey(channel), getSerializer().deserialize(message) + , new RedismqMessageContext(JbootRedismqImpl.this)); } - }, redis.keysToBytesArray(channels)); + }; + + redis.subscribe(jedisPubSub, redis.keysToBytesArray(channels)); dequeueThread = new Thread(this, "redis-dequeue-thread"); dequeueThread.start(); } + @Override + protected void onStopListening() { + if (jedisPubSub != null) { + jedisPubSub.unsubscribe(); + } + dequeueThread.interrupt(); + } + @Override public void enqueue(Object message, String toChannel) { @@ -99,7 +110,7 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { try { doExecuteDequeue(); Thread.sleep(1); - } catch (Throwable ex) { + } catch (Exception ex) { LOG.error(ex.toString(), ex); } } diff --git a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java index 9e198d6b..5d0ed087 100644 --- a/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rocketmq/JbootRocketmqImpl.java @@ -42,6 +42,8 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { private static final Log LOG = Log.getLog(JbootRocketmqImpl.class); private JbootRocketmqConfig rocketmqConfig; private MQProducer mqProducer; + private DefaultMQPushConsumer queueConsumer; + private DefaultMQPushConsumer broadcastConsumer; public JbootRocketmqImpl(JbootmqConfig config) { super(config); @@ -68,29 +70,40 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { } } + @Override + protected void onStopListening() { + if (queueConsumer != null) { + queueConsumer.shutdown(); + } + + if (broadcastConsumer != null) { + broadcastConsumer.shutdown(); + } + } + public void startQueueConsumer() throws MQClientException { // 实例化消费者 - DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(rocketmqConfig.getConsumerGroup()); + queueConsumer = new DefaultMQPushConsumer(rocketmqConfig.getConsumerGroup()); // 设置NameServer的地址 - consumer.setNamesrvAddr(rocketmqConfig.getNamesrvAddr()); - consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); + queueConsumer.setNamesrvAddr(rocketmqConfig.getNamesrvAddr()); + queueConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); if (StrUtil.isNotBlank(rocketmqConfig.getNamespace())) { - consumer.setNamespace(rocketmqConfig.getNamespace()); + queueConsumer.setNamespace(rocketmqConfig.getNamespace()); } if (rocketmqConfig.getConsumeMessageBatchMaxSize() != null) { - consumer.setConsumeMessageBatchMaxSize(rocketmqConfig.getConsumeMessageBatchMaxSize()); + queueConsumer.setConsumeMessageBatchMaxSize(rocketmqConfig.getConsumeMessageBatchMaxSize()); } for (String channel : channels) { - consumer.subscribe(channel, rocketmqConfig.getSubscribeSubExpression()); + queueConsumer.subscribe(channel, rocketmqConfig.getSubscribeSubExpression()); } // 注册回调实现类来处理从broker拉取回来的消息 - consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> { + queueConsumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> { RokectmqMessageContext msgContext = new RokectmqMessageContext(this, msgs, context); if (msgs != null && !msgs.isEmpty()) { for (MessageExt messageExt : msgs) { @@ -102,35 +115,35 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { }); - consumer.start(); + queueConsumer.start(); } public void startBroadcastConsumer() throws MQClientException { // 实例化消费者 - DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(rocketmqConfig.getBroadcastChannelPrefix() + rocketmqConfig.getConsumerGroup()); + broadcastConsumer = new DefaultMQPushConsumer(rocketmqConfig.getBroadcastChannelPrefix() + rocketmqConfig.getConsumerGroup()); // 设置NameServer的地址 - consumer.setNamesrvAddr(rocketmqConfig.getNamesrvAddr()); + broadcastConsumer.setNamesrvAddr(rocketmqConfig.getNamesrvAddr()); if (StrUtil.isNotBlank(rocketmqConfig.getNamespace())) { - consumer.setNamespace(rocketmqConfig.getNamespace()); + broadcastConsumer.setNamespace(rocketmqConfig.getNamespace()); } - consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); - consumer.setMessageModel(MessageModel.BROADCASTING); + broadcastConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); + broadcastConsumer.setMessageModel(MessageModel.BROADCASTING); if (rocketmqConfig.getConsumeMessageBatchMaxSize() != null) { - consumer.setConsumeMessageBatchMaxSize(rocketmqConfig.getConsumeMessageBatchMaxSize()); + broadcastConsumer.setConsumeMessageBatchMaxSize(rocketmqConfig.getConsumeMessageBatchMaxSize()); } for (String channel : channels) { - consumer.subscribe(rocketmqConfig.getBroadcastChannelPrefix() + channel, rocketmqConfig.getSubscribeSubExpression()); + broadcastConsumer.subscribe(rocketmqConfig.getBroadcastChannelPrefix() + channel, rocketmqConfig.getSubscribeSubExpression()); } final int len = rocketmqConfig.getBroadcastChannelPrefix().length(); - consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> { + broadcastConsumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> { RokectmqMessageContext rokectMqMessageInfo = new RokectmqMessageContext(this, msgs, context); if (msgs != null && !msgs.isEmpty()) { for (MessageExt messageExt : msgs) { @@ -141,7 +154,7 @@ public class JbootRocketmqImpl extends JbootmqBase implements Jbootmq { return rokectMqMessageInfo.getReturnStatus(); }); - consumer.start(); + broadcastConsumer.start(); } -- Gitee From eb1667cf3a6120db5c49a817a03418ea86545d9e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 21 Feb 2022 11:21:39 +0800 Subject: [PATCH 1569/1965] =?UTF-8?q?fixed=EF=BC=9A=E4=BF=AE=E5=A4=8D=20Co?= =?UTF-8?q?ntroller=20=E8=BF=94=E5=9B=9E=E5=80=BC=E6=9C=89=E5=A4=A7?= =?UTF-8?q?=E5=86=99=E5=8F=AF=E8=83=BD=E4=B8=8D=E6=AD=A3=E7=A1=AE=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/test/controller/IndexController.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/java/io/jboot/test/controller/IndexController.java b/src/test/java/io/jboot/test/controller/IndexController.java index ca22e9df..970e368b 100644 --- a/src/test/java/io/jboot/test/controller/IndexController.java +++ b/src/test/java/io/jboot/test/controller/IndexController.java @@ -122,4 +122,11 @@ public class IndexController extends JbootController { public String r13(){ return "forward: "; } + + public String r14(){ + return "forward: classPath"; + } + public String r15(){ + return "redirect: classPath"; + } } -- Gitee From 90339828073aadb0ceeba1f2b4fb5937383f4afd Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 21 Feb 2022 11:32:28 +0800 Subject: [PATCH 1570/1965] =?UTF-8?q?fixed=EF=BC=9A=E4=BF=AE=E5=A4=8D=20Co?= =?UTF-8?q?ntroller=20=E8=BF=94=E5=9B=9E=E5=80=BC=E6=9C=89=E5=A4=A7?= =?UTF-8?q?=E5=86=99=E5=8F=AF=E8=83=BD=E4=B8=8D=E6=AD=A3=E7=A1=AE=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/web/render/JbootReturnValueRender.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index 9b8b322c..475444cc 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -68,9 +68,13 @@ public class JbootReturnValueRender extends Render { } else if (this.value instanceof String) { String newVal = ((String) value).toLowerCase(); + //template + if (newVal.endsWith(".html") && !newVal.contains(":")) { + this.render = factory.getTemplateRender((String) value); + } //error - if (newVal.startsWith("error") && newVal.length() > 8) { + else if (newVal.startsWith("error") && newVal.length() > 8) { String trim = ((String) value).substring(5).trim(); if (trim.startsWith(":")) { String errorCodeStr = trim.substring(1).trim(); @@ -107,11 +111,6 @@ public class JbootReturnValueRender extends Render { } } - //template - else if (newVal.endsWith(".html")) { - this.render = factory.getTemplateRender((String) value); - } - //text else { this.render = factory.getTextRender((String) value); -- Gitee From 3f41e2635c78d9b8de1515ce279f6459e0502125 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 21 Feb 2022 11:35:20 +0800 Subject: [PATCH 1571/1965] v3.13.7 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 2e1bd1fa..c59625a2 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.13.6 + 3.13.7 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index ea2e5309..edb92ce0 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.13.6 + 3.13.7 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 966ace15..f679a132 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.13.6 + 3.13.7 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.13.6', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.13.7', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 27feb368..3acb83d6 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.13.6', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.13.7', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.6/jboot-3.13.6.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.7/jboot-3.13.7.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index d4039cc4..527bb9aa 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.13.6"; + public static String VERSION = "3.13.7"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From b14dfb42a1c09729cf7e32a82b407d2f6922b282 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 21 Feb 2022 11:35:25 +0800 Subject: [PATCH 1572/1965] v3.13.7 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ pom.xml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index efdb5e37..e5c54999 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.13.6 + 3.13.7 ``` diff --git a/changes.txt b/changes.txt index 252a53c7..d6acbb52 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.13.7: +新增: MQ 新增 stopListening() 方法,可用于定制化关闭 MQ +修复: Controller 返回值有大写可能不正确的问题 + + + jboot v3.13.6: 优化:强化 Controller 返回值,自动匹配相应的 render 优化:优化 JbootHttpImpl 和 JbootHttpResponse 代码,添加必要的日志输出 diff --git a/pom.xml b/pom.xml index cef6bdb5..226ac640 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.7-SNAPSHOT + 3.13.7 jar jboot -- Gitee From e1436a6efef51f3f3884b307d09f3a7930f2978c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 22 Feb 2022 10:26:07 +0800 Subject: [PATCH 1573/1965] v3.13.8 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- .../jboot/components/schedule/JbootCron4jPlugin.java | 5 ++--- src/main/java/io/jboot/test/MockApp.java | 12 +++++++++++- 7 files changed, 20 insertions(+), 11 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index c59625a2..8adc1c03 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.13.7 + 3.13.8 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index edb92ce0..4c4c87fe 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.13.7 + 3.13.8 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index f679a132..9cb8bace 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.13.7 + 3.13.8 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.13.7', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.13.8', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 3acb83d6..adb163c6 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.13.7', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.13.8', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.7/jboot-3.13.7.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.8/jboot-3.13.8.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 527bb9aa..636c73e9 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.13.7"; + public static String VERSION = "3.13.8"; public static final String ATTR_REQUEST = "REQUEST"; diff --git a/src/main/java/io/jboot/components/schedule/JbootCron4jPlugin.java b/src/main/java/io/jboot/components/schedule/JbootCron4jPlugin.java index a06dac42..9f311ca6 100644 --- a/src/main/java/io/jboot/components/schedule/JbootCron4jPlugin.java +++ b/src/main/java/io/jboot/components/schedule/JbootCron4jPlugin.java @@ -150,9 +150,8 @@ public class JbootCron4jPlugin implements IPlugin { @Override public boolean stop() { - for (JbootCron4jPlugin.TaskInfo taskInfo : taskInfoList) { - taskInfo.stop(); - } + taskInfoList.forEach(TaskInfo::stop); + taskInfoList.clear(); return true; } diff --git a/src/main/java/io/jboot/test/MockApp.java b/src/main/java/io/jboot/test/MockApp.java index ef1d73dd..20a784ac 100644 --- a/src/main/java/io/jboot/test/MockApp.java +++ b/src/main/java/io/jboot/test/MockApp.java @@ -17,6 +17,7 @@ package io.jboot.test; import com.jfinal.config.JFinalConfig; import com.jfinal.core.JFinalFilter; +import com.jfinal.kit.LogKit; import com.jfinal.kit.PathKit; import io.jboot.aop.JbootAopFactory; import io.jboot.aop.cglib.JbootCglibProxyFactory; @@ -52,6 +53,8 @@ class MockApp { private Object testInstance; + private boolean isInit = false; + private MockApp() { filter = new MockJFinalFilter(); @@ -78,6 +81,13 @@ class MockApp { } void start(Class testClass) { + if (!isInit) { + init(testClass); + isInit = true; + } + } + + private void init(Class testClass) { try { TestConfig testConfig = testClass.getAnnotation(TestConfig.class); @@ -114,7 +124,7 @@ class MockApp { filter.init(new MockFilterConfig()); config = ReflectUtil.getFieldValue(filter, "jfinalConfig"); } catch (ServletException e) { - e.printStackTrace(); + LogKit.error(e.toString(), e); } } -- Gitee From c241320cc46ed7bb3b876fc827b9aea401df0719 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 22 Feb 2022 10:26:34 +0800 Subject: [PATCH 1574/1965] v3.13.8 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 8 +++++++- pom.xml | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e5c54999..949bad2c 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.13.7 + 3.13.8 ``` diff --git a/changes.txt b/changes.txt index d6acbb52..2ada1bff 100644 --- a/changes.txt +++ b/changes.txt @@ -1,5 +1,11 @@ +jboot v3.13.8: +修复:Junit 代码覆盖率测试可能出现多次启动的问题 +修复:JbootCron4jPlugin 停止后未移除已经停止任务的问题 + + + jboot v3.13.7: -新增: MQ 新增 stopListening() 方法,可用于定制化关闭 MQ +新增:MQ 新增 stopListening() 方法,可用于定制化关闭 MQ 修复: Controller 返回值有大写可能不正确的问题 diff --git a/pom.xml b/pom.xml index 226ac640..66c1c996 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.7 + 3.13.8 jar jboot -- Gitee From 7ff45fb4e76f2e866954ac018c1363aedb80689b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 22 Feb 2022 12:13:00 +0800 Subject: [PATCH 1575/1965] v3.13.8 release (^.^)YYa!! --- README.md | 2 +- doc/deploy_to_alioss.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 949bad2c..ca7fdd1c 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ public class Helloworld extends JbootController { ## 帮助文档 -- 文档请访问:[www.jboot.io](http://www.jboot.io) +- 文档请访问:[www.jboot.com.cn](http://www.jboot.com.cn) - Demos 请访问:[这里](./src/test/java/io/jboot/test) diff --git a/doc/deploy_to_alioss.sh b/doc/deploy_to_alioss.sh index e3aced3c..aac1c6f4 100755 --- a/doc/deploy_to_alioss.sh +++ b/doc/deploy_to_alioss.sh @@ -6,5 +6,5 @@ set -e # build yarn build -ossutil rm oss://jboot-doc-site-hk/ -rf -ossutil cp -rf .vuepress/dist oss://jboot-doc-site-hk/ \ No newline at end of file +ossutil rm oss://jboot-doc-site/ -rf +ossutil cp -rf .vuepress/dist oss://jboot-doc-site/ \ No newline at end of file -- Gitee From a10b88e3760d63fc5bf73e5a14ba900040b01f5d Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 25 Feb 2022 08:53:30 +0800 Subject: [PATCH 1576/1965] optimize FastJsonSerializer.java --- .../serializer/FastJsonSerializer.java | 11 +++++++++-- .../jboot/test/controller/IndexController.java | 16 ++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/components/serializer/FastJsonSerializer.java b/src/main/java/io/jboot/components/serializer/FastJsonSerializer.java index 9c3dfc71..4de321ec 100644 --- a/src/main/java/io/jboot/components/serializer/FastJsonSerializer.java +++ b/src/main/java/io/jboot/components/serializer/FastJsonSerializer.java @@ -16,8 +16,9 @@ package io.jboot.components.serializer; import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.parser.Feature; +import com.alibaba.fastjson.parser.ParserConfig; import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.util.IOUtils; import com.jfinal.log.Log; @@ -25,6 +26,11 @@ public class FastJsonSerializer implements JbootSerializer { private static final Log LOG = Log.getLog(FastJsonSerializer.class); + private final static ParserConfig autoTypeSupportConfig = new ParserConfig(); + static { + autoTypeSupportConfig.setAutoTypeSupport(true); + } + @Override public byte[] serialize(Object obj) { if (obj == null) { @@ -46,7 +52,8 @@ public class FastJsonSerializer implements JbootSerializer { } try { - return JSON.parse(bytes, Feature.SupportAutoType); +// return JSON.parse(bytes, Feature.SupportAutoType); + return JSON.parse(new String(bytes, IOUtils.UTF8), autoTypeSupportConfig); } catch (Exception e) { LOG.error(e.toString(), e); } diff --git a/src/test/java/io/jboot/test/controller/IndexController.java b/src/test/java/io/jboot/test/controller/IndexController.java index 970e368b..43dacf4d 100644 --- a/src/test/java/io/jboot/test/controller/IndexController.java +++ b/src/test/java/io/jboot/test/controller/IndexController.java @@ -1,6 +1,7 @@ package io.jboot.test.controller; import com.jfinal.kit.PathKit; +import io.jboot.test.db.model.User; import io.jboot.utils.RequestUtil; import io.jboot.web.ResponseEntity; import io.jboot.web.controller.JbootController; @@ -8,11 +9,11 @@ import io.jboot.web.controller.annotation.GetRequest; import io.jboot.web.controller.annotation.PostRequest; import io.jboot.web.controller.annotation.RequestMapping; -@RequestMapping("/") +@RequestMapping(value = "/") public class IndexController extends JbootController { public void index() { - render("/index.html"); + render("index.html"); } @@ -21,7 +22,7 @@ public class IndexController extends JbootController { } public void error500(){ - + render("/xxx/xx/x/xxx/index.html"); } public void csv(){ @@ -64,11 +65,12 @@ public class IndexController extends JbootController { } public String r1(){ +// render("xxxx"); return "text...."; } public String r2(){ - return "error:404"; + return "error : 404"; } @@ -129,4 +131,10 @@ public class IndexController extends JbootController { public String r15(){ return "redirect: classPath"; } + + public Object r16(){ + User user = new User(); + user.put("aaa",123); + return user; + } } -- Gitee From 234a896e957db861112f164243b6267994ee890b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 25 Feb 2022 10:55:18 +0800 Subject: [PATCH 1577/1965] rename TxEnable.java to Transactional.java --- .../annotation/Transactional.java} | 64 ++++++++++- .../TransactionalInterceptor.java} | 58 +++++++--- .../transactional/TransactionalManager.java | 100 ++++++++++++++++++ .../java/io/jboot/test/db/tx/TestService.java | 11 +- 4 files changed, 212 insertions(+), 21 deletions(-) rename src/main/java/io/jboot/{db/tx/TxEnable.java => aop/annotation/Transactional.java} (41%) rename src/main/java/io/jboot/db/{tx/TxEnableInterceptor.java => transactional/TransactionalInterceptor.java} (50%) create mode 100644 src/main/java/io/jboot/db/transactional/TransactionalManager.java diff --git a/src/main/java/io/jboot/db/tx/TxEnable.java b/src/main/java/io/jboot/aop/annotation/Transactional.java similarity index 41% rename from src/main/java/io/jboot/db/tx/TxEnable.java rename to src/main/java/io/jboot/aop/annotation/Transactional.java index 082db0cd..a698dfa6 100644 --- a/src/main/java/io/jboot/db/tx/TxEnable.java +++ b/src/main/java/io/jboot/aop/annotation/Transactional.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.jboot.db.tx; +package io.jboot.aop.annotation; import java.lang.annotation.*; @@ -23,12 +23,72 @@ import java.lang.annotation.*; @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) -public @interface TxEnable { +public @interface Transactional { + /** + * 使用哪个数据源 + * + * @return + */ String config() default ""; + /** + * 事务隔离级别 + * + * @return + */ int transactionLevel() default -1; + /** + * return false 的时候,是否进行回滚 + * + * @return + */ + boolean rollbackForFalse() default false; + + /** + * return ret.fail 的时候,是否进行回滚 + * + * @return + */ + boolean rollbackForRetFail() default false; + + + /** + * 返回 null 的时候,是否进行回滚 + * + * @return + */ + boolean rollbackForNull() default false; + + + /** + * 配置允许哪些异常不回滚 + * + * @return + */ + Class[] noRollbackFor() default {}; + + /** + * 是否在新的线程里执行 + * + * @return + */ boolean inNewThread() default false; + /** + * 使用哪个线程池来运行线程,需要在启动的时候,通过 TransactionalManager 来配置线程池及其名称 + * + * @return + */ + String threadPoolName() default ""; + + /** + * 是否以阻塞的方式运行线程,这个配置只有在返回值 void 情况下配置生效 + * 又返回的,默认都是阻塞运行线程 + * + * @return + */ + boolean threadWithBlocked() default false; + } \ No newline at end of file diff --git a/src/main/java/io/jboot/db/tx/TxEnableInterceptor.java b/src/main/java/io/jboot/db/transactional/TransactionalInterceptor.java similarity index 50% rename from src/main/java/io/jboot/db/tx/TxEnableInterceptor.java rename to src/main/java/io/jboot/db/transactional/TransactionalInterceptor.java index bb94fb9d..92fbf3a9 100644 --- a/src/main/java/io/jboot/db/tx/TxEnableInterceptor.java +++ b/src/main/java/io/jboot/db/transactional/TransactionalInterceptor.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.jboot.db.tx; +package io.jboot.db.transactional; import com.jfinal.aop.Interceptor; @@ -24,10 +24,13 @@ import com.jfinal.plugin.activerecord.*; import io.jboot.aop.InterceptorBuilder; import io.jboot.aop.Interceptors; import io.jboot.aop.annotation.AutoLoad; +import io.jboot.aop.annotation.Transactional; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.StrUtil; import java.lang.reflect.Method; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; /** * 缓存操作的拦截器 @@ -35,26 +38,38 @@ import java.lang.reflect.Method; * @author michael yang */ @AutoLoad -public class TxEnableInterceptor implements Interceptor, InterceptorBuilder { +public class TransactionalInterceptor implements Interceptor, InterceptorBuilder { @Override public void intercept(Invocation inv) { - TxEnable txEnable = inv.getMethod().getAnnotation(TxEnable.class); - String configName = AnnotationUtil.get(txEnable.config()); + Transactional transactional = inv.getMethod().getAnnotation(Transactional.class); + String configName = AnnotationUtil.get(transactional.config()); DbPro dbPro = StrUtil.isBlank(configName) ? Db.use() : Db.use(configName); Config config = StrUtil.isBlank(configName) ? DbKit.getConfig() : DbKit.getConfig(configName); - int transactionLevel = txEnable.transactionLevel(); + int transactionLevel = transactional.transactionLevel(); if (transactionLevel == -1) { transactionLevel = config.getTransactionLevel(); } - IAtom runnable = () -> { - inv.invoke(); + IAtom runnable = () -> { + try { + inv.invoke(); + } catch (Throwable ex) { + for (Class forClass : transactional.noRollbackFor()) { + if (ex.getClass().isAssignableFrom(forClass)) { + LogKit.error(ex.toString(), ex); + + //允许事务提交 + return true; + } + } + throw ex; + } //没有返回值的方法,只要没有异常就是提交事务 if (inv.getMethod().getReturnType() == void.class) { @@ -63,24 +78,31 @@ public class TxEnableInterceptor implements Interceptor, InterceptorBuilder { Object result = inv.getReturnValue(); - if (result == null) { + if (result == null && transactional.rollbackForNull()) { return false; } - if (result instanceof Boolean) { - return (boolean) result; + if (result instanceof Boolean && !(Boolean) result && transactional.rollbackForFalse()) { + return false; } - if (result instanceof Ret) { - return ((Ret) result).isOk(); + if (result instanceof Ret && ((Ret) result).isFail() && transactional.rollbackForRetFail()) { + return false; } return true; }; - if (txEnable.inNewThread()) { + + if (transactional.inNewThread()) { try { - dbPro.txInNewThread(transactionLevel, runnable).get(); + Future future = txInNewThread(inv, transactional.threadPoolName(), dbPro, transactionLevel, runnable); + + //有返回值的场景下,需要等待返回值 + //或者没有返回值,但是配置了 @Transacional(threadWithBlocked=ture) 的时候 + if (inv.getMethod().getReturnType() != void.class || transactional.threadWithBlocked()) { + Boolean success = future.get(); + } } catch (Exception e) { LogKit.error(e.toString(), e); } @@ -91,9 +113,15 @@ public class TxEnableInterceptor implements Interceptor, InterceptorBuilder { } + public Future txInNewThread(Invocation inv, String name, DbPro dbPro, int transactionLevel, IAtom atom) { + Callable callable = () -> dbPro.tx(transactionLevel, atom); + return TransactionalManager.me().execute(inv, name, callable); + } + + @Override public void build(Class targetClass, Method method, Interceptors interceptors) { - if (Util.hasAnnotation(method, TxEnable.class)) { + if (Util.hasAnnotation(method, Transactional.class)) { interceptors.add(this); } } diff --git a/src/main/java/io/jboot/db/transactional/TransactionalManager.java b/src/main/java/io/jboot/db/transactional/TransactionalManager.java new file mode 100644 index 00000000..e5bf8b06 --- /dev/null +++ b/src/main/java/io/jboot/db/transactional/TransactionalManager.java @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.db.transactional; + +import com.jfinal.aop.Invocation; +import io.jboot.utils.ClassUtil; +import io.jboot.utils.NamedThreadFactory; +import io.jboot.utils.StrUtil; + +import java.util.Map; +import java.util.concurrent.*; + +public class TransactionalManager { + + private static TransactionalManager instance = new TransactionalManager(); + + public static TransactionalManager me() { + return instance; + } + + + private Map executorServiceMap = new ConcurrentHashMap<>(); + private ThreadFactory threadFactory = new NamedThreadFactory("Transactional", true); + + //当未配置线程池名称的时候,是否使用 threadFactory 来执行 + private boolean runDefaultWithoutConfigName = false; + + + public void addExecutorService(String name, ExecutorService service) { + executorServiceMap.put(name, service); + } + + public ExecutorService getExecutorService(String name) { + return executorServiceMap.get(name); + } + + public void removeExecutorService(String name) { + executorServiceMap.remove(name); + } + + public Map getExecutorServiceMap() { + return executorServiceMap; + } + + public ThreadFactory getThreadFactory() { + return threadFactory; + } + + public void setThreadFactory(ThreadFactory threadFactory) { + this.threadFactory = threadFactory; + } + + public boolean isRunDefaultWithoutConfigName() { + return runDefaultWithoutConfigName; + } + + public void setRunDefaultWithoutConfigName(boolean runDefaultWithoutConfigName) { + this.runDefaultWithoutConfigName = runDefaultWithoutConfigName; + } + + public Future execute(Invocation inv, String byName, Callable callable) { + if (StrUtil.isBlank(byName)) { + FutureTask task = new FutureTask<>(callable); + threadFactory.newThread(task).start(); + return task; + } + + + ExecutorService executorService = executorServiceMap.get(byName); + if (executorService != null) { + return executorService.submit(callable); + } + + if (!runDefaultWithoutConfigName) { + throw new IllegalStateException("Can not find threadPoolName: \"" + byName + "\" for @Transactional() in method: " + + ClassUtil.buildMethodString(inv.getMethod()) + + ".\n Please invoke TransactionalManager.me().addExecutorService() to configure transactional threadPool on application started."); + } + + + FutureTask task = new FutureTask<>(callable); + threadFactory.newThread(task).start(); + return task; + } + + +} diff --git a/src/test/java/io/jboot/test/db/tx/TestService.java b/src/test/java/io/jboot/test/db/tx/TestService.java index c76094d8..87fea134 100644 --- a/src/test/java/io/jboot/test/db/tx/TestService.java +++ b/src/test/java/io/jboot/test/db/tx/TestService.java @@ -1,23 +1,26 @@ package io.jboot.test.db.tx; import com.jfinal.kit.Ret; -import io.jboot.db.tx.TxEnable; +import io.jboot.aop.annotation.Transactional; public class TestService { - @TxEnable + @Transactional public void test1() { + System.out.println("currentThread------->>>>" + Thread.currentThread().getName()); System.out.println("test1"); } - @TxEnable + @Transactional(rollbackForFalse = true) public boolean test2() { + System.out.println("currentThread------->>>>" + Thread.currentThread().getName()); System.out.println("test1"); return false; } - @TxEnable(inNewThread = true) + @Transactional(inNewThread = true,threadPoolName = "aaa") public Ret test3() { + System.out.println("currentThread------->>>>" + Thread.currentThread().getName()); return Ret.ok(); } } -- Gitee From 1c52ea9263d1b9d605e884cbc995189218ed18d8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 25 Feb 2022 11:51:13 +0800 Subject: [PATCH 1578/1965] =?UTF-8?q?=E4=B8=BA=20RabbitMQ=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=9B=B4=E5=A4=9A=E7=9A=84=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mq/rabbitmq/JbootRabbitmqConfig.java | 75 +++++++++++++++++++ .../mq/rabbitmq/JbootRabbitmqImpl.java | 27 +++++-- 2 files changed, 95 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java index 6a907291..cf7e040f 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java @@ -42,6 +42,17 @@ public class JbootRabbitmqConfig { //若配置为 false,则需要在 OnMessage 里,调用 MessageContext.getChannel().baseAck(或者baseNack)进行消费(或者标识消费失败) private boolean autoAck = true; + private boolean queueDeclareDurable = true; + private boolean queueDeclareExclusive = false; + private boolean queueDeclareAutoDelete = false; + + + private String broadcastExchangeDeclareExchangeType = "fanout"; + private boolean broadcastExchangeDeclareDurable = false; + + private boolean broadcastQueueDeclareDurable = false; + private boolean broadcastQueueDeclareExclusive = false; + private boolean broadcastQueueDeclareAutoDelete = true; public String getUsername() { return username; @@ -106,4 +117,68 @@ public class JbootRabbitmqConfig { public void setAutoAck(boolean autoAck) { this.autoAck = autoAck; } + + public boolean isQueueDeclareDurable() { + return queueDeclareDurable; + } + + public void setQueueDeclareDurable(boolean queueDeclareDurable) { + this.queueDeclareDurable = queueDeclareDurable; + } + + public boolean isQueueDeclareExclusive() { + return queueDeclareExclusive; + } + + public void setQueueDeclareExclusive(boolean queueDeclareExclusive) { + this.queueDeclareExclusive = queueDeclareExclusive; + } + + public boolean isQueueDeclareAutoDelete() { + return queueDeclareAutoDelete; + } + + public void setQueueDeclareAutoDelete(boolean queueDeclareAutoDelete) { + this.queueDeclareAutoDelete = queueDeclareAutoDelete; + } + + public String getBroadcastExchangeDeclareExchangeType() { + return broadcastExchangeDeclareExchangeType; + } + + public void setBroadcastExchangeDeclareExchangeType(String broadcastExchangeDeclareExchangeType) { + this.broadcastExchangeDeclareExchangeType = broadcastExchangeDeclareExchangeType; + } + + public boolean isBroadcastExchangeDeclareDurable() { + return broadcastExchangeDeclareDurable; + } + + public void setBroadcastExchangeDeclareDurable(boolean broadcastExchangeDeclareDurable) { + this.broadcastExchangeDeclareDurable = broadcastExchangeDeclareDurable; + } + + public boolean isBroadcastQueueDeclareDurable() { + return broadcastQueueDeclareDurable; + } + + public void setBroadcastQueueDeclareDurable(boolean broadcastQueueDeclareDurable) { + this.broadcastQueueDeclareDurable = broadcastQueueDeclareDurable; + } + + public boolean isBroadcastQueueDeclareExclusive() { + return broadcastQueueDeclareExclusive; + } + + public void setBroadcastQueueDeclareExclusive(boolean broadcastQueueDeclareExclusive) { + this.broadcastQueueDeclareExclusive = broadcastQueueDeclareExclusive; + } + + public boolean isBroadcastQueueDeclareAutoDelete() { + return broadcastQueueDeclareAutoDelete; + } + + public void setBroadcastQueueDeclareAutoDelete(boolean broadcastQueueDeclareAutoDelete) { + this.broadcastQueueDeclareAutoDelete = broadcastQueueDeclareAutoDelete; + } } diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index c3818418..15ffe4b0 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -121,7 +121,6 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { public synchronized Channel getChannel(String toChannel, boolean queueMode) { - Channel channel = channelMap.get(toChannel + queueMode); if (channel == null) { try { @@ -129,13 +128,29 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { //队列模式,只需要创建 队列就可以了,不需要定义交换机 if (queueMode) { - channel.queueDeclare(toChannel, true, false, false, null); + channel.queueDeclare(toChannel + , rabbitmqConfig.isQueueDeclareDurable() + , rabbitmqConfig.isQueueDeclareExclusive() + , rabbitmqConfig.isQueueDeclareAutoDelete() + , null); } //广播模式,需要定义交换机,发送者直接把消息发送到交换机里 else { - channel.queueDeclare(buildBroadcastChannelName(toChannel), false, false, true, null); - channel.exchangeDeclare(toChannel, BuiltinExchangeType.FANOUT, true); + channel.queueDeclare(buildBroadcastChannelName(toChannel) + , rabbitmqConfig.isBroadcastQueueDeclareDurable() + , rabbitmqConfig.isBroadcastQueueDeclareExclusive() + , rabbitmqConfig.isBroadcastQueueDeclareAutoDelete() + , null); + + + BuiltinExchangeType exchangeType = BuiltinExchangeType.FANOUT; + for (BuiltinExchangeType type : BuiltinExchangeType.values()) { + if (type.getType().equals(rabbitmqConfig.getBroadcastExchangeDeclareExchangeType())) { + exchangeType = type; + } + } + channel.exchangeDeclare(toChannel, exchangeType, rabbitmqConfig.isBroadcastExchangeDeclareDurable()); channel.queueBind(buildBroadcastChannelName(toChannel), toChannel, rabbitmqConfig.getBroadcastChannelRoutingKey()); } @@ -143,9 +158,7 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { throw new JbootException("Can not create rabbit mq channel.", ex); } - if (channel != null) { - channelMap.put(toChannel + queueMode, channel); - } + channelMap.put(toChannel + queueMode, channel); } return channel; -- Gitee From 5c5d3e8a40585a68dd67cff7fb2f91208a4d2da0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 25 Feb 2022 12:02:38 +0800 Subject: [PATCH 1579/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9A=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E6=A3=80=E6=B5=8B=E6=98=AF=E5=90=A6=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=20jfinal-wexin=20=E5=B9=B6=E8=87=AA=E5=8A=A8=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=20JbootAccessTokenCache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cache/support/JbootAccessTokenCache.java | 2 +- .../cache/support/JbootCaptchaCache.java | 2 +- .../cache/support/JbootTokenCache.java | 2 +- src/main/java/io/jboot/core/JbootCoreConfig.java | 16 ++++++++++------ 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/support/JbootAccessTokenCache.java b/src/main/java/io/jboot/components/cache/support/JbootAccessTokenCache.java index faf2d3f2..5f1f7213 100644 --- a/src/main/java/io/jboot/components/cache/support/JbootAccessTokenCache.java +++ b/src/main/java/io/jboot/components/cache/support/JbootAccessTokenCache.java @@ -6,7 +6,7 @@ import io.jboot.components.cache.CacheTime; public class JbootAccessTokenCache implements IAccessTokenCache { - static final String CACHE_NAME = "__jboot_wechat_access_tokens"; + static final String CACHE_NAME = "wechat_access_tokens"; @Override public String get(String key) { diff --git a/src/main/java/io/jboot/components/cache/support/JbootCaptchaCache.java b/src/main/java/io/jboot/components/cache/support/JbootCaptchaCache.java index 686f0019..d2e5ce76 100644 --- a/src/main/java/io/jboot/components/cache/support/JbootCaptchaCache.java +++ b/src/main/java/io/jboot/components/cache/support/JbootCaptchaCache.java @@ -7,7 +7,7 @@ import io.jboot.utils.StrUtil; public class JbootCaptchaCache implements ICaptchaCache { - public static final String CACHE_NAME = "__jboot_captcha"; + public static final String CACHE_NAME = "jboot_captchas"; @Override public void put(Captcha captcha) { diff --git a/src/main/java/io/jboot/components/cache/support/JbootTokenCache.java b/src/main/java/io/jboot/components/cache/support/JbootTokenCache.java index 8cae9e9d..72e7c1f0 100644 --- a/src/main/java/io/jboot/components/cache/support/JbootTokenCache.java +++ b/src/main/java/io/jboot/components/cache/support/JbootTokenCache.java @@ -8,7 +8,7 @@ import java.util.List; public class JbootTokenCache implements ITokenCache { - static final String CACHE_NAME = "__jboot_token"; + static final String CACHE_NAME = "jboot_tokens"; @Override public void put(Token token) { diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 86f3c7ce..dd99ba39 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -26,11 +26,13 @@ import com.jfinal.kit.PropKit; import com.jfinal.plugin.activerecord.ActiveRecordPlugin; import com.jfinal.template.Directive; import com.jfinal.template.Engine; +import com.jfinal.weixin.sdk.api.ApiConfigKit; import io.jboot.Jboot; import io.jboot.aop.JbootAopFactory; import io.jboot.aop.jfinal.JfinalHandlers; import io.jboot.aop.jfinal.JfinalPlugins; import io.jboot.app.ApplicationUtil; +import io.jboot.components.cache.support.JbootAccessTokenCache; import io.jboot.components.cache.support.JbootCaptchaCache; import io.jboot.components.cache.support.JbootTokenCache; import io.jboot.components.gateway.JbootGatewayHandler; @@ -163,12 +165,16 @@ public class JbootCoreConfig extends JFinalConfig { constants.setBaseUploadPath(LocalAttachmentContainerConfig.getInstance().buildUploadAbsolutePath()); constants.setJsonDatePattern(DateUtil.datetimePattern); - if (JbootWebConfig.getInstance().isPathVariableEnable()){ + if (JbootWebConfig.getInstance().isPathVariableEnable()) { constants.setActionMapping(PathVariableActionMapping::new); - }else { + } else { constants.setActionMapping(JbootActionMapping::new); } + if (ClassUtil.hasClass("com.jfinal.weixin.sdk.api.ApiConfigKit")) { + ApiConfigKit.setAccessTokenCache(new JbootAccessTokenCache()); + } + JbootAppListenerManager.me().onConstantConfig(constants); } @@ -360,9 +366,9 @@ public class JbootCoreConfig extends JFinalConfig { //若用户自己没配置 ActionHandler,默认使用 JbootActionHandler if (handlers.getActionHandler() == null) { - if (JbootWebConfig.getInstance().isPathVariableEnable()){ + if (JbootWebConfig.getInstance().isPathVariableEnable()) { handlers.setActionHandler(new PathVariableActionHandler()); - }else { + } else { handlers.setActionHandler(new JbootActionHandler()); } } @@ -400,8 +406,6 @@ public class JbootCoreConfig extends JFinalConfig { JbootScheduleManager.me().stop(); JbootSeataManager.me().stop(); JbootrpcManager.me().stop(); - - } -- Gitee From 31f1e58f05edf79502659f716583da9fc1815ec6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 25 Feb 2022 12:03:24 +0800 Subject: [PATCH 1580/1965] v3.14.0 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 66c1c996..f0d80c2c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.13.8 + 3.14.0-SNAPSHOT jar jboot -- Gitee From f93e295ae781d96f011add6d8e959753812ab161 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 25 Feb 2022 15:23:56 +0800 Subject: [PATCH 1581/1965] remove JacksonSerializer.java --- .../serializer/JacksonSerializer.java | 118 ------------------ .../serializer/JbootSerializerConfig.java | 1 - .../serializer/JbootSerializerManager.java | 10 -- 3 files changed, 129 deletions(-) delete mode 100644 src/main/java/io/jboot/components/serializer/JacksonSerializer.java diff --git a/src/main/java/io/jboot/components/serializer/JacksonSerializer.java b/src/main/java/io/jboot/components/serializer/JacksonSerializer.java deleted file mode 100644 index b3711c3b..00000000 --- a/src/main/java/io/jboot/components/serializer/JacksonSerializer.java +++ /dev/null @@ -1,118 +0,0 @@ -///** -// * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). -// *

        -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// *

        -// * http://www.apache.org/licenses/LICENSE-2.0 -// *

        -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ -//package io.jboot.components.serializer; -// -//import com.fasterxml.jackson.annotation.JsonInclude; -//import com.fasterxml.jackson.core.JsonProcessingException; -//import com.fasterxml.jackson.databind.DeserializationFeature; -//import com.fasterxml.jackson.databind.JavaType; -//import com.fasterxml.jackson.databind.ObjectMapper; -//import com.fasterxml.jackson.databind.type.TypeFactory; -//import com.jfinal.log.Log; -// -//import java.io.Serializable; -//import java.lang.reflect.ParameterizedType; -//import java.lang.reflect.Type; -//import java.util.Collection; -// -// -//public class JacksonSerializer implements JbootSerializer { -// -// private static final Log LOG = Log.getLog(JacksonSerializer.class); -// -// public final static ObjectMapper MAPPER; -// -// static { -// MAPPER = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL); -// MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); -// } -// -// -// @Override -// public byte[] serialize(Object obj) { -// if (obj == null) { -// return null; -// } -// final TypeFactory typeFactory = MAPPER.getTypeFactory(); -// -// try { -// JavaType type =typeFactory.constructType(obj.getClass()); -// if (type.isContainerType()) { -// type = typeFactory.constructCollectionType((Class) type.getRawClass(),getActualType(obj,0)); -// } -// -// return MAPPER.writeValueAsBytes(new CachedObject(type, MAPPER.writeValueAsString(obj))); -// } catch (JsonProcessingException e) { -// LOG.error(e.toString(), e); -// } -// return null; -// } -// -// public static Class getActualType(Object o, int index) { -// Type clazz = o.getClass().getGenericSuperclass(); -// ParameterizedType pt = (ParameterizedType) clazz; -// return (Class) pt.getActualTypeArguments()[index]; -// } -// -// @Override -// public Object deserialize(byte[] bytes) { -// if (bytes == null || bytes.length == 0) { -// return null; -// } -// try { -// CachedObject cachedObject = MAPPER.readValue(bytes, CachedObject.class); -// return MAPPER.readValue(cachedObject.getData(), cachedObject.getType()); -// } catch (Exception e) { -// LOG.error(e.toString(), e); -// } -// -// return null; -// } -// -// -// public static class CachedObject implements Serializable { -// private JavaType type; -// private String data; -// -// public CachedObject() { -// } -// -// public CachedObject(JavaType type, String data) { -// this.type = type; -// this.data = data; -// } -// -// public JavaType getType() { -// return type; -// } -// -// public void setType(JavaType type) { -// this.type = type; -// } -// -// public String getData() { -// return data; -// } -// -// public void setData(String data) { -// this.data = data; -// } -// } -// -// -// -// -//} diff --git a/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java b/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java index 49ce2b00..1ae23252 100644 --- a/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java +++ b/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java @@ -23,7 +23,6 @@ public class JbootSerializerConfig { public static final String FST = "fst"; public static final String FASTJSON = "fastjson"; - public static final String JACKSON = "jackson"; public static final String KRYO = "kryo"; public String type = FST; diff --git a/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java b/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java index 9729853c..2ff5ee1c 100644 --- a/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java +++ b/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java @@ -64,18 +64,10 @@ public class JbootSerializerManager { } public JbootSerializer buildSerializer(String serializerName) { - if (serializerName == null) { throw new NullPointerException("SerializerName must not be null"); } - //可能是某个类名 - if (serializerName.contains(".")) { - JbootSerializer serializer = ClassUtil.newInstance(serializerName, false); - if (serializer == null) { - throw new JbootIllegalConfigException("can not new instance serializer by class: " + serializerName); - } - } switch (serializerName.toLowerCase()) { case JbootSerializerConfig.KRYO: @@ -84,8 +76,6 @@ public class JbootSerializerManager { return new FstSerializer(); case JbootSerializerConfig.FASTJSON: return new FastJsonSerializer(); -// case JbootSerializerConfig.JACKSON: -// return new JacksonSerializer(); default: return JbootSpiLoader.load(JbootSerializer.class, serializerName); } -- Gitee From 5e0b36666b21331b9fbc907d216132218a7805c3 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 25 Feb 2022 17:12:38 +0800 Subject: [PATCH 1582/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9A=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E6=A3=80=E6=B5=8B=E6=98=AF=E5=90=A6=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=20jfinal-wexin=20=E5=B9=B6=E8=87=AA=E5=8A=A8=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=20JbootAccessTokenCache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simples/websocket/pom.xml | 2 +- .../java/io/jboot/core/JbootCoreConfig.java | 11 ++++---- .../java/io/jboot/wechat/WechatSupport.java | 26 +++++++++++++++++++ .../WechatApiConfigInterceptor.java | 3 +-- 4 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 src/main/java/io/jboot/wechat/WechatSupport.java diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index a6f161d4..ab9dca9f 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.10.2 + 3.14.0 diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index dd99ba39..11a45872 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -26,13 +26,11 @@ import com.jfinal.kit.PropKit; import com.jfinal.plugin.activerecord.ActiveRecordPlugin; import com.jfinal.template.Directive; import com.jfinal.template.Engine; -import com.jfinal.weixin.sdk.api.ApiConfigKit; import io.jboot.Jboot; import io.jboot.aop.JbootAopFactory; import io.jboot.aop.jfinal.JfinalHandlers; import io.jboot.aop.jfinal.JfinalPlugins; import io.jboot.app.ApplicationUtil; -import io.jboot.components.cache.support.JbootAccessTokenCache; import io.jboot.components.cache.support.JbootCaptchaCache; import io.jboot.components.cache.support.JbootTokenCache; import io.jboot.components.gateway.JbootGatewayHandler; @@ -73,6 +71,7 @@ import io.jboot.web.handler.PathVariableActionHandler; import io.jboot.web.json.JbootJson; import io.jboot.web.render.JbootRenderFactory; import io.jboot.web.xss.XSSHandler; +import io.jboot.wechat.WechatSupport; import java.io.File; import java.util.ArrayList; @@ -171,10 +170,6 @@ public class JbootCoreConfig extends JFinalConfig { constants.setActionMapping(JbootActionMapping::new); } - if (ClassUtil.hasClass("com.jfinal.weixin.sdk.api.ApiConfigKit")) { - ApiConfigKit.setAccessTokenCache(new JbootAccessTokenCache()); - } - JbootAppListenerManager.me().onConstantConfig(constants); } @@ -389,6 +384,10 @@ public class JbootCoreConfig extends JFinalConfig { JbootSeataManager.me().init(); JbootSentinelManager.me().init(); + if (ClassUtil.hasClass("com.jfinal.weixin.sdk.api.ApiConfigKit")) { + new WechatSupport().autoSupport(); + } + JbootAppListenerManager.me().onStart(); //自定义参数转换方法 diff --git a/src/main/java/io/jboot/wechat/WechatSupport.java b/src/main/java/io/jboot/wechat/WechatSupport.java new file mode 100644 index 00000000..e32aa9ba --- /dev/null +++ b/src/main/java/io/jboot/wechat/WechatSupport.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.wechat; + +import com.jfinal.weixin.sdk.api.ApiConfigKit; +import io.jboot.components.cache.support.JbootAccessTokenCache; + +public class WechatSupport { + + public void autoSupport() { + ApiConfigKit.setAccessTokenCache(new JbootAccessTokenCache()); + } +} diff --git a/src/main/java/io/jboot/wechat/interceptor/WechatApiConfigInterceptor.java b/src/main/java/io/jboot/wechat/interceptor/WechatApiConfigInterceptor.java index 4672c8ff..f42b3391 100644 --- a/src/main/java/io/jboot/wechat/interceptor/WechatApiConfigInterceptor.java +++ b/src/main/java/io/jboot/wechat/interceptor/WechatApiConfigInterceptor.java @@ -30,10 +30,9 @@ public class WechatApiConfigInterceptor implements Interceptor { ApiConfig config = controller.getApiConfig(); if (config == null) { - inv.getController().renderText("error : cannot get apiconfig,please config jboot.properties"); + inv.getController().renderText("Error: Can not get apiconfig, please config jboot.properties"); return; } - ApiConfigKit.setThreadLocalAppId(config.getAppId()); inv.invoke(); } finally { -- Gitee From caa20484008f088e94087b0a6f1f6a9a34c14e68 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 26 Feb 2022 16:10:13 +0800 Subject: [PATCH 1583/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A@Cacheable?= =?UTF-8?q?()=20=E7=AD=89=E7=B3=BB=E5=88=97=E7=BC=93=E5=AD=98=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E5=AF=B9=20Controller=20=E7=9A=84=E6=94=AF=E6=8C=81.?= =?UTF-8?q?=20close=20#I3V6RF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/components/cache/ActionCache.java | 137 +++++++++ .../components/cache/ActionCacheConfig.java | 57 ++++ .../io/jboot/components/cache/AopCache.java | 14 +- ...opCacheConfig.java => AopCacheConfig.java} | 8 +- .../interceptor/CacheEvictInterceptor.java | 7 +- .../interceptor/CachePutInterceptor.java | 45 ++- .../interceptor/CacheableInterceptor.java | 73 ++++- .../interceptor/CachesEvictInterceptor.java | 13 +- .../cache/interceptor/ParaDirective.java | 58 ++++ .../components/cache/interceptor/Utils.java | 59 ++-- .../web/cached/CacheSupportResponseProxy.java | 254 ++++++++++++++++ .../jboot/web/cached/CacheSupportWriter.java | 270 ++++++++++++++++++ .../io/jboot/web/cached/CachedContent.java | 83 ++++++ .../jboot/web/handler/JbootActionHandler.java | 33 ++- .../handler/PathVariableActionHandler.java | 94 +++--- .../test/controller/CacheController.java | 62 ++++ 16 files changed, 1155 insertions(+), 112 deletions(-) create mode 100644 src/main/java/io/jboot/components/cache/ActionCache.java create mode 100644 src/main/java/io/jboot/components/cache/ActionCacheConfig.java rename src/main/java/io/jboot/components/cache/{JbootAopCacheConfig.java => AopCacheConfig.java} (87%) create mode 100644 src/main/java/io/jboot/components/cache/interceptor/ParaDirective.java create mode 100644 src/main/java/io/jboot/web/cached/CacheSupportResponseProxy.java create mode 100644 src/main/java/io/jboot/web/cached/CacheSupportWriter.java create mode 100644 src/main/java/io/jboot/web/cached/CachedContent.java create mode 100644 src/test/java/io/jboot/test/controller/CacheController.java diff --git a/src/main/java/io/jboot/components/cache/ActionCache.java b/src/main/java/io/jboot/components/cache/ActionCache.java new file mode 100644 index 00000000..25b5a4b0 --- /dev/null +++ b/src/main/java/io/jboot/components/cache/ActionCache.java @@ -0,0 +1,137 @@ +package io.jboot.components.cache; + + +import com.jfinal.log.Log; +import com.jfinal.plugin.ehcache.IDataLoader; + +import java.util.List; + +public class ActionCache { + + private static final Log LOG = Log.getLog(ActionCache.class); + + private static JbootCache actionCache; + + public static JbootCache setCurrentPrefix(String cacheNamePrefix) { + return getActionCache().setCurrentCacheNamePrefix(cacheNamePrefix); + } + + public static void clearCurrentPrefix() { + getActionCache().removeCurrentCacheNamePrefix(); + } + + static JbootCache getActionCache() { + if (actionCache == null) { + actionCache = JbootCacheManager.me().getCache(AopCacheConfig.getInstance().getUseCacheName()); + } + return actionCache; + } + + public static void setActionCache(JbootCache actionCache) { + ActionCache.actionCache = actionCache; + } + + + public static void put(String cacheName, Object key, Object value) { + try { + getActionCache().put(cacheName, key, value); + } catch (Exception ex) { + LOG.error(ex.toString(), ex); + } + } + + public static void put(String cacheName, Object key, Object value, int liveSeconds) { + try { + getActionCache().put(cacheName, key, value, liveSeconds); + } catch (Exception ex) { + LOG.error(ex.toString(), ex); + } + } + + public static List getKeys(String cacheName) { + try { + return getActionCache().getKeys(cacheName); + } catch (Exception ex) { + LOG.error(ex.toString(), ex); + } + return null; + } + + public static void remove(String cacheName, Object key) { + try { + getActionCache().remove(cacheName, key); + } catch (Exception ex) { + LOG.error(ex.toString(), ex); + } + } + + public static void removeAll(String cacheName) { + try { + getActionCache().removeAll(cacheName); + } catch (Exception ex) { + LOG.error(ex.toString(), ex); + } + } + + public static T get(String cacheName, Object key) { + try { + return getActionCache().get(cacheName, key); + } catch (Exception ex) { + LOG.error(ex.toString(), ex); + remove(cacheName, key); + } + return null; + } + + + public static T get(String cacheName, Object key, IDataLoader dataLoader) { + try { + return getActionCache().get(cacheName, key, dataLoader); + } catch (Exception ex) { + LOG.error(ex.toString(), ex); + remove(cacheName, key); + } + return null; + } + + + public static T get(String cacheName, Object key, IDataLoader dataLoader, int liveSeconds) { + try { + return getActionCache().get(cacheName, key, dataLoader, liveSeconds); + } catch (Exception ex) { + LOG.error(ex.toString(), ex); + remove(cacheName, key); + } + return null; + } + + + public static Integer getTtl(String cacheName, Object key) { + try { + return getActionCache().getTtl(cacheName, key); + } catch (Exception ex) { + LOG.error(ex.toString(), ex); + } + return null; + } + + + public static void setTtl(String cacheName, Object key, int seconds) { + try { + getActionCache().setTtl(cacheName, key, seconds); + } catch (Exception ex) { + LOG.error(ex.toString(), ex); + } + } + + private static final ActionCacheConfig CONFIG = ActionCacheConfig.getInstance(); + + public static void putDataToCache(String cacheName, String cacheKey, Object data, int liveSeconds) { + liveSeconds = liveSeconds > 0 ? liveSeconds : CONFIG.getLiveSeconds(); + if (liveSeconds > 0) { + put(cacheName, cacheKey, data, liveSeconds); + } else { + put(cacheName, cacheKey, data); + } + } +} diff --git a/src/main/java/io/jboot/components/cache/ActionCacheConfig.java b/src/main/java/io/jboot/components/cache/ActionCacheConfig.java new file mode 100644 index 00000000..896afdc2 --- /dev/null +++ b/src/main/java/io/jboot/components/cache/ActionCacheConfig.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.cache; + + +import io.jboot.app.config.JbootConfigManager; +import io.jboot.app.config.annotation.ConfigModel; + + +@ConfigModel(prefix = "jboot.action.cache") +public class ActionCacheConfig { + + // AOP 缓存的默认有效时间,0为永久有效,单位秒, + // 当 @Cacheable 和 @CachePut 注解不配置的时候默认用这个配置 + private int liveSeconds = 60 * 60; //默认为 1 个小时 + private String useCacheName = "default"; + + public int getLiveSeconds() { + return liveSeconds; + } + + public void setLiveSeconds(int liveSeconds) { + this.liveSeconds = liveSeconds; + } + + public String getUseCacheName() { + return useCacheName; + } + + public void setUseCacheName(String useCacheName) { + this.useCacheName = useCacheName; + } + + + private static ActionCacheConfig me; + + public static ActionCacheConfig getInstance() { + if (me == null) { + me = JbootConfigManager.me().get(ActionCacheConfig.class); + } + return me; + } + +} diff --git a/src/main/java/io/jboot/components/cache/AopCache.java b/src/main/java/io/jboot/components/cache/AopCache.java index 763864fc..089c4dbf 100644 --- a/src/main/java/io/jboot/components/cache/AopCache.java +++ b/src/main/java/io/jboot/components/cache/AopCache.java @@ -22,7 +22,7 @@ public class AopCache { static JbootCache getAopCache() { if (aopCache == null) { - aopCache = JbootCacheManager.me().getCache(JbootAopCacheConfig.getInstance().getUseCacheName()); + aopCache = JbootCacheManager.me().getCache(AopCacheConfig.getInstance().getUseCacheName()); } return aopCache; } @@ -123,4 +123,16 @@ public class AopCache { LOG.error(ex.toString(), ex); } } + + + private static final AopCacheConfig CONFIG = AopCacheConfig.getInstance(); + + public static void putDataToCache(String cacheName, String cacheKey, Object data, int liveSeconds) { + liveSeconds = liveSeconds > 0 ? liveSeconds : CONFIG.getLiveSeconds(); + if (liveSeconds > 0) { + put(cacheName, cacheKey, data, liveSeconds); + } else { + put(cacheName, cacheKey, data); + } + } } diff --git a/src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java b/src/main/java/io/jboot/components/cache/AopCacheConfig.java similarity index 87% rename from src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java rename to src/main/java/io/jboot/components/cache/AopCacheConfig.java index fa9ae221..44c80aca 100644 --- a/src/main/java/io/jboot/components/cache/JbootAopCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/AopCacheConfig.java @@ -21,7 +21,7 @@ import io.jboot.app.config.annotation.ConfigModel; @ConfigModel(prefix = "jboot.aop.cache") -public class JbootAopCacheConfig { +public class AopCacheConfig { // AOP 缓存的默认有效时间,0为永久有效,单位秒, // 当 @Cacheable 和 @CachePut 注解不配置的时候默认用这个配置 @@ -45,11 +45,11 @@ public class JbootAopCacheConfig { } - private static JbootAopCacheConfig me; + private static AopCacheConfig me; - public static JbootAopCacheConfig getInstance() { + public static AopCacheConfig getInstance() { if (me == null) { - me = JbootConfigManager.me().get(JbootAopCacheConfig.class); + me = JbootConfigManager.me().get(AopCacheConfig.class); } return me; } diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheEvictInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheEvictInterceptor.java index 0ea61090..52b6d696 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheEvictInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheEvictInterceptor.java @@ -31,23 +31,22 @@ public class CacheEvictInterceptor implements Interceptor { public void intercept(Invocation inv) { Method method = inv.getMethod(); - CacheEvict cacheEvict = method.getAnnotation(CacheEvict.class); if (cacheEvict == null) { inv.invoke(); return; } - Class targetClass = inv.getTarget().getClass(); + Class targetClass = inv.getTarget().getClass(); if (cacheEvict.beforeInvocation()) { - Utils.doCacheEvict(inv.getArgs(), targetClass, method, cacheEvict); + Utils.removeCache(inv.getArgs(), targetClass, method, cacheEvict, inv.isActionInvocation()); } inv.invoke(); if (!cacheEvict.beforeInvocation()) { - Utils.doCacheEvict(inv.getArgs(), targetClass, method, cacheEvict); + Utils.removeCache(inv.getArgs(), targetClass, method, cacheEvict, inv.isActionInvocation()); } } } diff --git a/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java index f828fe24..8058889c 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java @@ -18,8 +18,12 @@ package io.jboot.components.cache.interceptor; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; +import com.jfinal.core.CPI; +import com.jfinal.core.Controller; +import io.jboot.components.cache.AopCache; import io.jboot.components.cache.annotation.CachePut; import io.jboot.utils.AnnotationUtil; +import io.jboot.web.cached.CacheSupportResponseProxy; import java.lang.reflect.Method; @@ -39,6 +43,41 @@ public class CachePutInterceptor implements Interceptor { return; } + if (inv.isActionInvocation()) { + forController(inv, method, cachePut); + } else { + forService(inv, method, cachePut); + } + } + + + private void forController(Invocation inv, Method method, CachePut cachePut) { + + String unless = AnnotationUtil.get(cachePut.unless()); + if (Utils.isUnless(unless, method, inv.getArgs())) { + return; + } + + Class targetClass = inv.getTarget().getClass(); + String cacheName = AnnotationUtil.get(cachePut.name()); + Utils.ensureCacheNameNotBlank(method, cacheName); + String cacheKey = Utils.buildCacheKey(AnnotationUtil.get(cachePut.key()), targetClass, method, inv.getArgs()); + + Controller controller = inv.getController(); + + CacheSupportResponseProxy responseProxy = new CacheSupportResponseProxy(controller.getResponse()); + responseProxy.setCacheName(cacheName); + responseProxy.setCacheKey(cacheKey); + responseProxy.setCacheLiveSeconds(cachePut.liveSeconds()); + + //让 Controller 持有缓存的 responseProxy + CPI._init_(controller, CPI.getAction(controller), controller.getRequest(), responseProxy, controller.getPara()); + + } + + + + private void forService(Invocation inv, Method method, CachePut cachePut) { Object data = inv.getReturnValue(); String unless = AnnotationUtil.get(cachePut.unless()); @@ -46,12 +85,12 @@ public class CachePutInterceptor implements Interceptor { return; } - Class targetClass = inv.getTarget().getClass(); + Class targetClass = inv.getTarget().getClass(); String cacheName = AnnotationUtil.get(cachePut.name()); - Utils.ensureCachenameAvailable(method, cacheName); + Utils.ensureCacheNameNotBlank(method, cacheName); String cacheKey = Utils.buildCacheKey(AnnotationUtil.get(cachePut.key()), targetClass, method, inv.getArgs()); - Utils.putDataToCache(cacheName, cacheKey, data, cachePut.liveSeconds()); + AopCache.putDataToCache(cacheName, cacheKey, data, cachePut.liveSeconds()); } diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java index c25e13be..dffc21ec 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java @@ -18,8 +18,10 @@ package io.jboot.components.cache.interceptor; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; +import com.jfinal.core.CPI; +import com.jfinal.core.Controller; import com.jfinal.plugin.activerecord.Page; -import io.jboot.Jboot; +import io.jboot.components.cache.ActionCache; import io.jboot.components.cache.AopCache; import io.jboot.components.cache.annotation.Cacheable; import io.jboot.db.model.JbootModel; @@ -27,9 +29,13 @@ import io.jboot.exception.JbootException; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.ClassUtil; import io.jboot.utils.ModelUtil; +import io.jboot.web.cached.CacheSupportResponseProxy; +import io.jboot.web.cached.CachedContent; +import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -40,7 +46,6 @@ import java.util.Set; public class CacheableInterceptor implements Interceptor { private static final String NULL_VALUE = "NULL_VALUE"; - private static final boolean devMode = Jboot.isDevMode(); @Override public void intercept(Invocation inv) { @@ -52,15 +57,69 @@ public class CacheableInterceptor implements Interceptor { return; } + if (inv.isActionInvocation()) { + forController(inv, method, cacheable); + } else { + forService(inv, method, cacheable); + } + } + + + private void forController(Invocation inv, Method method, Cacheable cacheable) { + + Class targetClass = inv.getTarget().getClass(); + String cacheName = AnnotationUtil.get(cacheable.name()); + Utils.ensureCacheNameNotBlank(method, cacheName); + String cacheKey = Utils.buildCacheKey(AnnotationUtil.get(cacheable.key()), targetClass, method, inv.getArgs()); + + Controller controller = inv.getController(); + + CachedContent cachedContent = ActionCache.get(cacheName, cacheKey); + if (cachedContent != null) { + writeCachedContent(controller, cachedContent); + return; + } + + + CacheSupportResponseProxy responseProxy = new CacheSupportResponseProxy(controller.getResponse()); + responseProxy.setCacheName(cacheName); + responseProxy.setCacheKey(cacheKey); + responseProxy.setCacheLiveSeconds(cacheable.liveSeconds()); + + //让 Controller 持有缓存的 responseProxy + CPI._init_(controller, CPI.getAction(controller), controller.getRequest(), responseProxy, controller.getPara()); + + inv.invoke(); + + } + + + private void writeCachedContent(Controller controller, CachedContent cachedContent) { + HttpServletResponse response = controller.getResponse(); + Map headers = cachedContent.getHeaders(); + if (headers != null && !headers.isEmpty()) { + headers.forEach(response::addHeader); + } + controller.render(cachedContent.createRender()); + } + + /** + * Service 层的 Cacheable 使用 + * + * @param inv + * @param method + * @param cacheable + */ + private void forService(Invocation inv, Method method, Cacheable cacheable) { String unlessString = AnnotationUtil.get(cacheable.unless()); if (Utils.isUnless(unlessString, method, inv.getArgs())) { inv.invoke(); return; } - Class targetClass = inv.getTarget().getClass(); + Class targetClass = inv.getTarget().getClass(); String cacheName = AnnotationUtil.get(cacheable.name()); - Utils.ensureCachenameAvailable(method, cacheName); + Utils.ensureCacheNameNotBlank(method, cacheName); String cacheKey = Utils.buildCacheKey(AnnotationUtil.get(cacheable.key()), targetClass, method, inv.getArgs()); Object data = AopCache.get(cacheName, cacheKey); @@ -77,7 +136,7 @@ public class CacheableInterceptor implements Interceptor { data = inv.getReturnValue(); if (data != null) { - Utils.putDataToCache(cacheName, cacheKey, data, cacheable.liveSeconds()); + AopCache.putDataToCache(cacheName, cacheKey, data, cacheable.liveSeconds()); //当启用返回 copy 值的时候,返回的内容应该是一个进行copy之后的值 if (cacheable.returnCopyEnable()) { @@ -85,7 +144,7 @@ public class CacheableInterceptor implements Interceptor { } } else if (cacheable.nullCacheEnable()) { - Utils.putDataToCache(cacheName, cacheKey, NULL_VALUE, cacheable.liveSeconds()); + AopCache.putDataToCache(cacheName, cacheKey, NULL_VALUE, cacheable.liveSeconds()); } } } @@ -110,7 +169,7 @@ public class CacheableInterceptor implements Interceptor { private JbootException newException(Exception ex, Invocation inv, Object data) { - String msg = "can not copy data for type [" + data.getClass().getName() + "] in method :" + String msg = "Can not copy data for type [" + data.getClass().getName() + "] in method :" + ClassUtil.buildMethodString(inv.getMethod()) + " , can not use @Cacheable(returnCopyEnable=true) annotation"; diff --git a/src/main/java/io/jboot/components/cache/interceptor/CachesEvictInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CachesEvictInterceptor.java index 5d4c0928..08c8552d 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CachesEvictInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CachesEvictInterceptor.java @@ -62,20 +62,21 @@ public class CachesEvictInterceptor implements Interceptor { } } - Class targetClass = inv.getTarget().getClass(); + Class targetClass = inv.getTarget().getClass(); try { - doCachesEvict(inv.getArgs(), targetClass, method, beforeInvocations); + doCachesEvict(inv.getArgs(), targetClass, method, beforeInvocations, inv.isActionInvocation()); inv.invoke(); } finally { - doCachesEvict(inv.getArgs(), targetClass, method, afterInvocations); + doCachesEvict(inv.getArgs(), targetClass, method, afterInvocations, inv.isActionInvocation()); } } private void doCachesEvict(Object[] arguments - , Class targetClass + , Class targetClass , Method method - , List cacheEvicts) { + , List cacheEvicts + , boolean isAction) { if (cacheEvicts == null || cacheEvicts.isEmpty()) { return; @@ -83,7 +84,7 @@ public class CachesEvictInterceptor implements Interceptor { for (CacheEvict evict : cacheEvicts) { try { - Utils.doCacheEvict(arguments, targetClass, method, evict); + Utils.removeCache(arguments, targetClass, method, evict, isAction); } catch (Exception ex) { LOG.error(ex.toString(), ex); } diff --git a/src/main/java/io/jboot/components/cache/interceptor/ParaDirective.java b/src/main/java/io/jboot/components/cache/interceptor/ParaDirective.java new file mode 100644 index 00000000..3c570d09 --- /dev/null +++ b/src/main/java/io/jboot/components/cache/interceptor/ParaDirective.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.cache.interceptor; + +import com.jfinal.core.Controller; +import com.jfinal.kit.LogKit; +import com.jfinal.template.Env; +import com.jfinal.template.io.Writer; +import com.jfinal.template.stat.Scope; +import io.jboot.utils.StrUtil; +import io.jboot.web.controller.JbootControllerContext; +import io.jboot.web.directive.base.JbootDirectiveBase; + +import java.io.IOException; + +public class ParaDirective extends JbootDirectiveBase { + + @Override + public void onRender(Env env, Scope scope, Writer writer) { + + Controller controller = JbootControllerContext.get(); + if (controller == null) { + throw new IllegalStateException("#para(...) directive only use for controller." + getLocation()); + } + + String key = getPara(0, scope); + String defaultValue = getPara(1, scope); + + if (StrUtil.isBlank(key)) { + throw new IllegalArgumentException("#para(...) argument must not be empty." + getLocation()); + } + + String value = controller.getPara(key); + if (StrUtil.isBlank(value)) { + value = StrUtil.isNotBlank(defaultValue) ? defaultValue : ""; + } + + try { + writer.write(value); + } catch (IOException e) { + LogKit.error(e.toString(), e); + } + } +} + diff --git a/src/main/java/io/jboot/components/cache/interceptor/Utils.java b/src/main/java/io/jboot/components/cache/interceptor/Utils.java index d2d71647..b33a2ffe 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/Utils.java +++ b/src/main/java/io/jboot/components/cache/interceptor/Utils.java @@ -16,8 +16,8 @@ package io.jboot.components.cache.interceptor; import com.jfinal.template.Engine; +import io.jboot.components.cache.ActionCache; import io.jboot.components.cache.AopCache; -import io.jboot.components.cache.JbootAopCacheConfig; import io.jboot.components.cache.annotation.CacheEvict; import io.jboot.db.model.Columns; import io.jboot.exception.JbootException; @@ -38,6 +38,10 @@ class Utils { static final Engine ENGINE = new Engine("JbootCacheRender"); + static { + ENGINE.addDirective("para", ParaDirective.class); + } + /** * use jfinal engine render text * @@ -47,7 +51,7 @@ class Utils { * @return */ static String engineRender(String template, Method method, Object[] arguments) { - Map datas = new HashMap(); + Map datas = new HashMap<>(); int x = 0; for (Parameter p : method.getParameters()) { if (!p.isNamePresent()) { @@ -61,8 +65,7 @@ class Utils { } - static String buildCacheKey(String key, Class clazz, Method method, Object[] arguments) { - + static String buildCacheKey(String key, Class clazz, Method method, Object[] arguments) { clazz = ClassUtil.getUsefulClass(clazz); if (StrUtil.isNotBlank(key)) { @@ -79,7 +82,7 @@ class Utils { Class[] paramTypes = method.getParameterTypes(); int index = 0; for (Object argument : arguments) { - String argString = converteToString(argument, method); + String argString = convertToString(argument, method); if (index == 0) { keyBuilder.append("("); } else { @@ -98,7 +101,7 @@ class Utils { } private static String renderKey(String key, Method method, Object[] arguments) { - int indexOfStartFlag = key.indexOf("#("); + int indexOfStartFlag = key.indexOf("#"); if (indexOfStartFlag > -1) { int indexOfEndFlag = key.indexOf(")"); if (indexOfEndFlag > indexOfStartFlag) { @@ -110,9 +113,9 @@ class Utils { } - public static void ensureCachenameAvailable(Method method, String cacheName) { + public static void ensureCacheNameNotBlank(Method method, String cacheName) { if (StrUtil.isBlank(cacheName)) { - throw new JbootException(String.format("CacheEvict.name() must not empty in method [%s].", + throw new JbootException(String.format("Cache name must not empty or blank in method: " + ClassUtil.buildMethodString(method))); } } @@ -149,7 +152,7 @@ class Utils { } - static String converteToString(Object object, Method method) { + static String convertToString(Object object, Method method) { if (object == null) { return "null"; } @@ -169,7 +172,7 @@ class Utils { if (index == 0) { ret.append('['); } - ret.append(converteToString(value, method)); + ret.append(convertToString(value, method)); if (++index != values.length) { ret.append(','); } else { @@ -180,14 +183,14 @@ class Utils { } if (object instanceof Collection) { - Collection c = (Collection) object; + Collection c = (Collection) object; StringBuilder ret = new StringBuilder(); int index = 0; for (Object o : c) { if (index == 0) { ret.append('['); } - ret.append(converteToString(o, method)); + ret.append(convertToString(o, method)); if (++index != c.size()) { ret.append(','); } else { @@ -227,16 +230,12 @@ class Utils { return false; } - String template = new StringBuilder("#(") - .append(unlessString) - .append(")") - .toString(); - + String template = "#(" + unlessString + ")"; return "true".equals(engineRender(template, method, arguments)); } - static void doCacheEvict(Object[] arguments, Class targetClass, Method method, CacheEvict evict) { + static void removeCache(Object[] arguments, Class targetClass, Method method, CacheEvict evict, boolean isAction) { String unless = AnnotationUtil.get(evict.unless()); if (Utils.isUnless(unless, method, arguments)) { return; @@ -250,23 +249,21 @@ class Utils { String cacheKey = AnnotationUtil.get(evict.key()); - if (StrUtil.isBlank(cacheKey) || "*".equals(cacheKey)) { - AopCache.removeAll(cacheName); + if (StrUtil.isBlank(cacheKey) || "*".equals(cacheKey.trim())) { + if (isAction) { + ActionCache.removeAll(cacheName); + } else { + AopCache.removeAll(cacheName); + } } else { cacheKey = Utils.buildCacheKey(cacheKey, targetClass, method, arguments); - AopCache.remove(cacheName, cacheKey); + if (isAction) { + ActionCache.remove(cacheName, cacheKey); + } else { + AopCache.remove(cacheName, cacheKey); + } } } - private static final JbootAopCacheConfig CONFIG = JbootAopCacheConfig.getInstance(); - - static void putDataToCache(String cacheName, String cacheKey, Object data, int liveSeconds) { - liveSeconds = liveSeconds > 0 ? liveSeconds : CONFIG.getLiveSeconds(); - if (liveSeconds > 0) { - AopCache.put(cacheName, cacheKey, data, liveSeconds); - } else { - AopCache.put(cacheName, cacheKey, data); - } - } } diff --git a/src/main/java/io/jboot/web/cached/CacheSupportResponseProxy.java b/src/main/java/io/jboot/web/cached/CacheSupportResponseProxy.java new file mode 100644 index 00000000..3a1d6e74 --- /dev/null +++ b/src/main/java/io/jboot/web/cached/CacheSupportResponseProxy.java @@ -0,0 +1,254 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.cached; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collection; +import java.util.Locale; + +public class CacheSupportResponseProxy implements HttpServletResponse { + + final private HttpServletResponse proxy; + private CacheSupportWriter cacheWriter; + private String cacheName; + private String cacheKey; + private int cacheLiveSeconds; + + + public CacheSupportResponseProxy(HttpServletResponse proxy) { + this.proxy = proxy; + } + + @Override + public void addCookie(Cookie cookie) { + proxy.addCookie(cookie); + } + + @Override + public boolean containsHeader(String name) { + return proxy.containsHeader(name); + } + + @Override + public String encodeURL(String url) { + return proxy.encodeURL(url); + } + + @Override + public String encodeRedirectURL(String url) { + return proxy.encodeRedirectURL(url); + } + + @Override + public String encodeUrl(String url) { + return proxy.encodeUrl(url); + } + + @Override + public String encodeRedirectUrl(String url) { + return proxy.encodeRedirectUrl(url); + } + + @Override + public void sendError(int sc, String msg) throws IOException { + proxy.sendError(sc, msg); + } + + @Override + public void sendError(int sc) throws IOException { + proxy.sendError(sc); + } + + @Override + public void sendRedirect(String location) throws IOException { + proxy.sendRedirect(location); + } + + @Override + public void setDateHeader(String name, long date) { + proxy.setDateHeader(name, date); + } + + @Override + public void addDateHeader(String name, long date) { + proxy.addDateHeader(name, date); + } + + @Override + public void setHeader(String name, String value) { + proxy.setHeader(name, value); + } + + @Override + public void addHeader(String name, String value) { + proxy.addHeader(name, value); + } + + @Override + public void setIntHeader(String name, int value) { + proxy.setIntHeader(name, value); + } + + @Override + public void addIntHeader(String name, int value) { + proxy.addIntHeader(name, value); + } + + @Override + public void setStatus(int sc) { + proxy.setStatus(sc); + } + + @Override + public void setStatus(int sc, String sm) { + proxy.setStatus(sc, sm); + } + + @Override + public int getStatus() { + return proxy.getStatus(); + } + + @Override + public String getHeader(String name) { + return proxy.getHeader(name); + } + + @Override + public Collection getHeaders(String name) { + return proxy.getHeaders(name); + } + + @Override + public Collection getHeaderNames() { + return proxy.getHeaderNames(); + } + + @Override + public String getCharacterEncoding() { + return proxy.getCharacterEncoding(); + } + + @Override + public String getContentType() { + return proxy.getContentType(); + } + + @Override + public ServletOutputStream getOutputStream() throws IOException { + return proxy.getOutputStream(); + } + + @Override + public PrintWriter getWriter() throws IOException { + if (cacheWriter == null) { + cacheWriter = new CacheSupportWriter(proxy.getWriter()); + } + return cacheWriter; + } + + @Override + public void setCharacterEncoding(String charset) { + proxy.setCharacterEncoding(charset); + } + + @Override + public void setContentLength(int len) { + proxy.setContentLength(len); + } + + @Override + public void setContentLengthLong(long len) { + proxy.setContentLengthLong(len); + } + + @Override + public void setContentType(String type) { + proxy.setContentType(type); + } + + @Override + public void setBufferSize(int size) { + proxy.setBufferSize(size); + } + + @Override + public int getBufferSize() { + return proxy.getBufferSize(); + } + + @Override + public void flushBuffer() throws IOException { + proxy.flushBuffer(); + } + + @Override + public void resetBuffer() { + proxy.resetBuffer(); + } + + @Override + public boolean isCommitted() { + return proxy.isCommitted(); + } + + @Override + public void reset() { + proxy.reset(); + } + + @Override + public void setLocale(Locale loc) { + proxy.setLocale(loc); + } + + @Override + public Locale getLocale() { + return proxy.getLocale(); + } + + public String getResponseString() { + return cacheWriter != null ? cacheWriter.getWriterString() : null; + } + + public String getCacheName() { + return cacheName; + } + + public void setCacheName(String cacheName) { + this.cacheName = cacheName; + } + + public String getCacheKey() { + return cacheKey; + } + + public void setCacheKey(String cacheKey) { + this.cacheKey = cacheKey; + } + + public int getCacheLiveSeconds() { + return cacheLiveSeconds; + } + + public void setCacheLiveSeconds(int cacheLiveSeconds) { + this.cacheLiveSeconds = cacheLiveSeconds; + } +} diff --git a/src/main/java/io/jboot/web/cached/CacheSupportWriter.java b/src/main/java/io/jboot/web/cached/CacheSupportWriter.java new file mode 100644 index 00000000..9740e8ea --- /dev/null +++ b/src/main/java/io/jboot/web/cached/CacheSupportWriter.java @@ -0,0 +1,270 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.cached; + +import java.io.*; +import java.util.Formatter; +import java.util.Locale; + +public class CacheSupportWriter extends PrintWriter { + + final PrintWriter proxy; + final StringWriter cached; + + private Formatter formatter; + + public CacheSupportWriter(PrintWriter proxy) { + super(proxy); + this.proxy = proxy; + this.cached = new StringWriter(); + } + + @Override + public void flush() { + proxy.flush(); + cached.flush(); + } + + @Override + public void close() { + proxy.close(); + try { + cached.close(); + } catch (IOException e) { + } + } + + @Override + public boolean checkError() { + return proxy.checkError(); + } + + + @Override + public void write(int c) { + proxy.write(c); + cached.write(c); + } + + @Override + public void write(char[] buf, int off, int len) { + proxy.write(buf, off, len); + cached.write(buf, off, len); + } + + @Override + public void write(char[] buf) { + proxy.write(buf); + try { + cached.write(buf); + } catch (IOException e) { + } + } + + @Override + public void write(String s, int off, int len) { + proxy.write(s, off, len); + cached.write(s, off, len); + } + + @Override + public void write(String s) { + proxy.write(s); + cached.write(s); + } + + @Override + public void print(boolean b) { + proxy.print(b); + cached.write(b ? "true" : "false"); + } + + @Override + public void print(char c) { + proxy.print(c); + cached.write(c); + } + + @Override + public void print(int i) { + proxy.print(i); + cached.write(i); + } + + @Override + public void print(long l) { + proxy.print(l); + cached.write(String.valueOf(l)); + } + + @Override + public void print(float f) { + proxy.print(f); + cached.write(String.valueOf(f)); + } + + @Override + public void print(double d) { + proxy.print(d); + cached.write(String.valueOf(d)); + } + + @Override + public void print(char[] s) { + proxy.print(s); + try { + cached.write(s); + } catch (IOException e) { + } + } + + @Override + public void print(String s) { + if (s == null) { + s = "null"; + } + proxy.print(s); + cached.write(s); + } + + @Override + public void print(Object obj) { + proxy.print(obj); + cached.write(String.valueOf(obj)); + } + + @Override + public void println() { + proxy.println(); + cached.write("\n"); + } + + @Override + public void println(boolean x) { + proxy.println(x); + cached.write(x + "\n"); + } + + @Override + public void println(char x) { + proxy.println(x); + cached.write(x + "\n"); + } + + @Override + public void println(int x) { + proxy.println(x); + cached.write(x + "\n"); + } + + @Override + public void println(long x) { + proxy.println(x); + cached.write(x + "\n"); + } + + @Override + public void println(float x) { + proxy.println(x); + cached.write(x + "\n"); + } + + @Override + public void println(double x) { + proxy.println(x); + cached.write(x + "\n"); + } + + @Override + public void println(char[] x) { + proxy.println(x); + cached.write(x + "\n"); + } + + @Override + public void println(String x) { + proxy.println(x); + cached.write(x + "\n"); + } + + @Override + public void println(Object x) { + proxy.println(x); + cached.write(x + "\n"); + } + + @Override + public PrintWriter printf(String format, Object... args) { +// proxy.printf(format, args); + format(format, args); + return this; + } + + @Override + public PrintWriter printf(Locale l, String format, Object... args) { +// proxy.printf(l, format, args); + format(l, format, args); + return this; + } + + @Override + public PrintWriter format(String format, Object... args) { + proxy.format(format, args); + + if ((formatter == null) + || (formatter.locale() != Locale.getDefault())) { + formatter = new Formatter(cached); + } + formatter.format(Locale.getDefault(), format, args); + return this; + } + + @Override + public PrintWriter format(Locale l, String format, Object... args) { + proxy.format(l, format, args); + + if ((formatter == null) || (formatter.locale() != l)) { + formatter = new Formatter(cached, l); + } + formatter.format(l, format, args); + return this; + } + + @Override + public PrintWriter append(CharSequence csq) { + proxy.append(csq); + cached.append(csq); + return this; + } + + @Override + public PrintWriter append(CharSequence csq, int start, int end) { + proxy.append(csq, start, end); + cached.append(csq, start, end); + return this; + } + + @Override + public PrintWriter append(char c) { + proxy.append(c); + cached.append(c); + return this; + } + + public String getWriterString() { + return cached.toString(); + } +} diff --git a/src/main/java/io/jboot/web/cached/CachedContent.java b/src/main/java/io/jboot/web/cached/CachedContent.java new file mode 100644 index 00000000..bea4238c --- /dev/null +++ b/src/main/java/io/jboot/web/cached/CachedContent.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.cached; + +import com.jfinal.render.Render; +import com.jfinal.render.TextRender; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class CachedContent implements Serializable { + + private String content; + private String contentType; + private Map headers; + + public CachedContent() { + } + + public static CachedContent fromResponseProxy(CacheSupportResponseProxy responseProxy) { + CachedContent cachedContent = new CachedContent(); + cachedContent.setContentType(responseProxy.getContentType()); + cachedContent.setContent(responseProxy.getResponseString()); + + Collection headerNames = responseProxy.getHeaderNames(); + if (headerNames != null) { + headerNames.forEach(s -> cachedContent.addHeader(s, responseProxy.getHeader(s))); + } + return cachedContent; + } + + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public void addHeader(String key, String value) { + if (this.headers == null) { + this.headers = new HashMap<>(); + } + this.headers.put(key, value); + } + + + public Render createRender() { + return new TextRender(getContent(), getContentType()); + } +} diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index c94fb172..009b4002 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -22,10 +22,13 @@ import com.jfinal.render.IRenderFactory; import com.jfinal.render.Render; import com.jfinal.render.RenderException; import io.jboot.app.JbootApplicationConfig; +import io.jboot.components.cache.ActionCache; import io.jboot.components.valid.ValidErrorRender; import io.jboot.components.valid.ValidException; import io.jboot.components.valid.ValidUtil; import io.jboot.utils.ClassUtil; +import io.jboot.web.cached.CacheSupportResponseProxy; +import io.jboot.web.cached.CachedContent; import io.jboot.web.controller.JbootControllerContext; import io.jboot.web.render.JbootErrorRender; import io.jboot.web.render.JbootRenderFactory; @@ -124,14 +127,16 @@ public class JbootActionHandler extends ActionHandler { if (JbootActionReporter.isReportEnable()) { long time = System.currentTimeMillis(); try { - doStartRender(target, request, response, isHandled, action, controller, invocation); + doStartRender(target, action, controller, invocation, isHandled); } finally { JbootActionReporter.report(target, controller, action, invocation, time); } } else { - doStartRender(target, request, response, isHandled, action, controller, invocation); + doStartRender(target, action, controller, invocation, isHandled); } + doAfterRender(action, controller); + } catch (RenderException e) { if (LOG.isErrorEnabled()) { String qs = request.getQueryString(); @@ -153,14 +158,22 @@ public class JbootActionHandler extends ActionHandler { } } + protected void doAfterRender(Action action, Controller controller) { + // Controller 缓存的支持,必须在 render() 执行之后,才能通过 response 获取缓存信息 + if (controller.getResponse() instanceof CacheSupportResponseProxy) { + CacheSupportResponseProxy responseProxy = (CacheSupportResponseProxy) controller.getResponse(); + CachedContent cachedContent = CachedContent.fromResponseProxy(responseProxy); + ActionCache.putDataToCache(responseProxy.getCacheName(), responseProxy.getCacheKey(), + cachedContent, responseProxy.getCacheLiveSeconds()); + } + } - private void doStartRender(String target - , HttpServletRequest request - , HttpServletResponse response - , boolean[] isHandled + + protected void doStartRender(String target , Action action , Controller controller - , Invocation invocation) { + , Invocation invocation + , boolean[] isHandled) { invocation.invoke(); @@ -170,7 +183,7 @@ public class JbootActionHandler extends ActionHandler { if (target.equals(actionUrl)) { throw new RuntimeException("The forward action url is the same as before."); } else { - handle(actionUrl, request, response, isHandled); + handle(actionUrl, controller.getRequest(), controller.getResponse(), isHandled); } } else { if (render == null && void.class != action.getMethod().getReturnType() @@ -181,7 +194,7 @@ public class JbootActionHandler extends ActionHandler { String forwardTo = returnValueRender.getForwardTo(); if (forwardTo != null) { - handle(getRealForwrdTo(forwardTo, target, action), request, response, isHandled); + handle(getRealForwrdTo(forwardTo, target, action), controller.getRequest(), controller.getResponse(), isHandled); return; } else { render = returnValueRender; @@ -192,7 +205,7 @@ public class JbootActionHandler extends ActionHandler { render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); } - render.setContext(request, response, action.getViewPath()).render(); + render.setContext(controller.getRequest(), controller.getResponse(), action.getViewPath()).render(); } } diff --git a/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java b/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java index e3e43ba4..cafe3128 100644 --- a/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java +++ b/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java @@ -17,14 +17,14 @@ package io.jboot.web.handler; import com.google.common.base.Splitter; import com.jfinal.aop.Invocation; -import com.jfinal.core.*; +import com.jfinal.core.Action; +import com.jfinal.core.ActionException; +import com.jfinal.core.CPI; +import com.jfinal.core.Controller; import com.jfinal.log.Log; -import com.jfinal.render.Render; import com.jfinal.render.RenderException; import io.jboot.components.valid.ValidException; import io.jboot.web.controller.JbootControllerContext; -import io.jboot.web.render.JbootRenderFactory; -import io.jboot.web.render.JbootReturnValueRender; import io.jboot.web.session.JbootServletRequestWrapper; import javax.servlet.http.HttpServletRequest; @@ -88,14 +88,16 @@ public class PathVariableActionHandler extends JbootActionHandler { if (JbootActionReporter.isReportEnable()) { long time = System.currentTimeMillis(); try { - doStartRender(target, request, response, isHandled, action, controller, invocation); + doStartRender(target, action, controller, invocation, isHandled); } finally { JbootActionReporter.report(target, controller, action, invocation, time); } } else { - doStartRender(target, request, response, isHandled, action, controller, invocation); + doStartRender(target, action, controller, invocation, isHandled); } + doAfterRender(action, controller); + } catch (RenderException e) { if (LOG.isErrorEnabled()) { String qs = request.getQueryString(); @@ -114,46 +116,46 @@ public class PathVariableActionHandler extends JbootActionHandler { } - private void doStartRender(String target - , HttpServletRequest request - , HttpServletResponse response - , boolean[] isHandled - , Action action - , Controller controller - , Invocation invocation) { - - invocation.invoke(); - - Render render = controller.getRender(); - if (render instanceof ForwardActionRender) { - String actionUrl = ((ForwardActionRender) render).getActionUrl(); - if (target.equals(actionUrl)) { - throw new RuntimeException("The forward action url is the same as before."); - } else { - handle(actionUrl, request, response, isHandled); - } - } else { - if (render == null && void.class != action.getMethod().getReturnType() - && renderManager.getRenderFactory() instanceof JbootRenderFactory) { - - JbootRenderFactory factory = (JbootRenderFactory) renderManager.getRenderFactory(); - JbootReturnValueRender returnValueRender = factory.getReturnValueRender(action, invocation.getReturnValue()); - String forwardTo = returnValueRender.getForwardTo(); - if (forwardTo != null) { - handle(getRealForwrdTo(forwardTo, target, action), request, response, isHandled); - return; - } else { - render = returnValueRender; - } - } - - if (render == null) { - render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); - } - - render.setContext(request, response, action.getViewPath()).render(); - } - } +// private void doStartRender(String target +// , HttpServletRequest request +// , HttpServletResponse response +// , boolean[] isHandled +// , Action action +// , Controller controller +// , Invocation invocation) { +// +// invocation.invoke(); +// +// Render render = controller.getRender(); +// if (render instanceof ForwardActionRender) { +// String actionUrl = ((ForwardActionRender) render).getActionUrl(); +// if (target.equals(actionUrl)) { +// throw new RuntimeException("The forward action url is the same as before."); +// } else { +// handle(actionUrl, request, response, isHandled); +// } +// } else { +// if (render == null && void.class != action.getMethod().getReturnType() +// && renderManager.getRenderFactory() instanceof JbootRenderFactory) { +// +// JbootRenderFactory factory = (JbootRenderFactory) renderManager.getRenderFactory(); +// JbootReturnValueRender returnValueRender = factory.getReturnValueRender(action, invocation.getReturnValue()); +// String forwardTo = returnValueRender.getForwardTo(); +// if (forwardTo != null) { +// handle(getRealForwrdTo(forwardTo, target, action), request, response, isHandled); +// return; +// } else { +// render = returnValueRender; +// } +// } +// +// if (render == null) { +// render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); +// } +// +// render.setContext(request, response, action.getViewPath()).render(); +// } +// } /** * 请求包装类用于将路径变量的URL中的额外参数加入request中 diff --git a/src/test/java/io/jboot/test/controller/CacheController.java b/src/test/java/io/jboot/test/controller/CacheController.java new file mode 100644 index 00000000..9d08e49b --- /dev/null +++ b/src/test/java/io/jboot/test/controller/CacheController.java @@ -0,0 +1,62 @@ +package io.jboot.test.controller; + +import io.jboot.components.cache.annotation.CacheEvict; +import io.jboot.components.cache.annotation.Cacheable; +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; + +import java.util.HashMap; +import java.util.Map; + +@RequestMapping(value = "/cache") +public class CacheController extends JbootController { + + + @Cacheable(name = "aaa", liveSeconds = 10) + public void index() { + System.out.println("index() invoked!!!!!!!!!"); + renderText("index"); + } + + + @Cacheable(name = "aaa", liveSeconds = 10) + public void json() { + System.out.println("json() invoked!!!!!!!!!"); + Map data = new HashMap<>(); + data.put("age", 1); + data.put("name", "张三"); + data.put("sex", 1); + renderJson(data); + } + + + @Cacheable(name = "aaa", liveSeconds = 10) + public void html() { + System.out.println("html() invoked!!!!!!!!!"); + renderText("/index.html"); + } + + + @Cacheable(name = "aaa", liveSeconds = 10) + public void xml() { + System.out.println("xml() invoked!!!!!!!!!"); + String xml = "\n" + + " \n" + + " \n" + + " 1348831860\n" + + " \n" + + " \n" + + " 1234567890123456\n" + + ""; + + renderText(xml,"xml"); + } + + + @CacheEvict(name = "aaa") + public void removeAll(){ + renderText("ok"); + } + + +} -- Gitee From 5244c4a45bceae5bed841e6a7edfdfd1956323f5 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 26 Feb 2022 16:17:07 +0800 Subject: [PATCH 1584/1965] optimize JbootProp.java --- src/main/java/io/jboot/app/config/JbootProp.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/jboot/app/config/JbootProp.java b/src/main/java/io/jboot/app/config/JbootProp.java index 4b98441d..8f5f5bbe 100644 --- a/src/main/java/io/jboot/app/config/JbootProp.java +++ b/src/main/java/io/jboot/app/config/JbootProp.java @@ -34,6 +34,8 @@ class JbootProp { inputStream = JbootConfigKit.getClassLoader().getResourceAsStream(fileName); if (inputStream != null) { properties.load(new InputStreamReader(inputStream, encoding)); + } else { + System.err.println("Warning: Can not load properties file in classpath, file name: " + fileName); } } catch (Exception e) { System.err.println("Warning: Can not load properties file in classpath, file name: " + fileName); -- Gitee From 3181bb550042205ef5b19a2a288726b83a9bd109 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 26 Feb 2022 16:45:52 +0800 Subject: [PATCH 1585/1965] optimize JbootProp.java --- .../java/io/jboot/app/config/JbootProp.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/main/java/io/jboot/app/config/JbootProp.java b/src/main/java/io/jboot/app/config/JbootProp.java index 8f5f5bbe..dc530ed4 100644 --- a/src/main/java/io/jboot/app/config/JbootProp.java +++ b/src/main/java/io/jboot/app/config/JbootProp.java @@ -16,6 +16,7 @@ package io.jboot.app.config; import java.io.*; +import java.net.URL; import java.util.Properties; @@ -32,6 +33,23 @@ class JbootProp { InputStream inputStream = null; try { inputStream = JbootConfigKit.getClassLoader().getResourceAsStream(fileName); + + // 当系统未编译的时候,开发环境下的 resources 目录下的 jboot.properties 文件不会自动被 copy 到 target/classes 目录下 + // 此时,需要主动去探测 resources 目录的文件 + if (inputStream == null) { + URL resourceURL = JbootProp.class.getResource("/"); + if (resourceURL != null) { + String classPath = resourceURL.toURI().getPath(); + if (removeSlashEnd(classPath).endsWith("classes")) { + File resourcesDir = new File(classPath, "../../src/main/resources"); + File propFile = new File(resourcesDir, fileName); + if (propFile.exists() && propFile.isFile()) { + inputStream = new FileInputStream(propFile); + } + } + } + } + if (inputStream != null) { properties.load(new InputStreamReader(inputStream, encoding)); } else { @@ -50,6 +68,15 @@ class JbootProp { } + private static String removeSlashEnd(String path) { + if (path != null && path.endsWith(File.separator)) { + return path.substring(0, path.length() - 1); + } else { + return path; + } + } + + public JbootProp(File file) { properties = new Properties(); try (InputStream inputStream = new FileInputStream(file)) { @@ -59,6 +86,7 @@ class JbootProp { } } + public Properties getProperties() { return properties; } -- Gitee From 28edb9b42ce8ce84fd6f3028235b91d830dfad38 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sat, 26 Feb 2022 17:05:15 +0800 Subject: [PATCH 1586/1965] optimize @Transactional() annotation --- src/main/java/io/jboot/aop/annotation/Transactional.java | 5 +++-- .../jboot/db/transactional/TransactionalInterceptor.java | 7 ++++--- .../io/jboot/db/transactional/TransactionalManager.java | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/aop/annotation/Transactional.java b/src/main/java/io/jboot/aop/annotation/Transactional.java index a698dfa6..1dcade0e 100644 --- a/src/main/java/io/jboot/aop/annotation/Transactional.java +++ b/src/main/java/io/jboot/aop/annotation/Transactional.java @@ -70,7 +70,8 @@ public @interface Transactional { Class[] noRollbackFor() default {}; /** - * 是否在新的线程里执行 + * 是否在新的线程里执行,在 Controller 的 Action 方法下配置无效 + * 在 Controller 里,不能以新的线程在运行 * * @return */ @@ -85,7 +86,7 @@ public @interface Transactional { /** * 是否以阻塞的方式运行线程,这个配置只有在返回值 void 情况下配置生效 - * 又返回的,默认都是阻塞运行线程 + * 有返回值的,此配置无效,默认都是阻塞运行线程的方式运行 * * @return */ diff --git a/src/main/java/io/jboot/db/transactional/TransactionalInterceptor.java b/src/main/java/io/jboot/db/transactional/TransactionalInterceptor.java index 92fbf3a9..e02e9691 100644 --- a/src/main/java/io/jboot/db/transactional/TransactionalInterceptor.java +++ b/src/main/java/io/jboot/db/transactional/TransactionalInterceptor.java @@ -94,13 +94,14 @@ public class TransactionalInterceptor implements Interceptor, InterceptorBuilder }; - if (transactional.inNewThread()) { + if (!inv.isActionInvocation() && transactional.inNewThread()) { try { Future future = txInNewThread(inv, transactional.threadPoolName(), dbPro, transactionLevel, runnable); //有返回值的场景下,需要等待返回值 //或者没有返回值,但是配置了 @Transacional(threadWithBlocked=ture) 的时候 - if (inv.getMethod().getReturnType() != void.class || transactional.threadWithBlocked()) { + if (inv.getMethod().getReturnType() != void.class + || transactional.threadWithBlocked()) { Boolean success = future.get(); } } catch (Exception e) { @@ -115,7 +116,7 @@ public class TransactionalInterceptor implements Interceptor, InterceptorBuilder public Future txInNewThread(Invocation inv, String name, DbPro dbPro, int transactionLevel, IAtom atom) { Callable callable = () -> dbPro.tx(transactionLevel, atom); - return TransactionalManager.me().execute(inv, name, callable); + return TransactionalManager.me().execute(name, callable, inv); } diff --git a/src/main/java/io/jboot/db/transactional/TransactionalManager.java b/src/main/java/io/jboot/db/transactional/TransactionalManager.java index e5bf8b06..2647e950 100644 --- a/src/main/java/io/jboot/db/transactional/TransactionalManager.java +++ b/src/main/java/io/jboot/db/transactional/TransactionalManager.java @@ -71,7 +71,7 @@ public class TransactionalManager { this.runDefaultWithoutConfigName = runDefaultWithoutConfigName; } - public Future execute(Invocation inv, String byName, Callable callable) { + public Future execute(String byName, Callable callable, Invocation inv) { if (StrUtil.isBlank(byName)) { FutureTask task = new FutureTask<>(callable); threadFactory.newThread(task).start(); -- Gitee From 7bb05dbe4090965b31a11fd857a70def391bf491 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 27 Feb 2022 12:04:03 +0800 Subject: [PATCH 1587/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A@Cacheable?= =?UTF-8?q?()=20=E7=AD=89=E7=B3=BB=E5=88=97=E7=BC=93=E5=AD=98=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E5=AF=B9=20Controller=20=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/cache/interceptor/CPI.java | 26 +++++++++++++++++++ .../interceptor/CacheableInterceptor.java | 5 ++++ .../cache/interceptor/ParaDirective.java | 13 ++++++++++ .../components/cache/interceptor/Utils.java | 12 +++++---- .../test/controller/CacheController.java | 15 +++++++++-- 5 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 src/main/java/io/jboot/components/cache/interceptor/CPI.java diff --git a/src/main/java/io/jboot/components/cache/interceptor/CPI.java b/src/main/java/io/jboot/components/cache/interceptor/CPI.java new file mode 100644 index 00000000..0b5b1c6a --- /dev/null +++ b/src/main/java/io/jboot/components/cache/interceptor/CPI.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.cache.interceptor; + +import com.jfinal.template.Engine; + +public class CPI { + + public static Engine getCacheRenderEngine(){ + return Utils.ENGINE; + } + +} diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java index dffc21ec..374ea825 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java @@ -66,6 +66,11 @@ public class CacheableInterceptor implements Interceptor { private void forController(Invocation inv, Method method, Cacheable cacheable) { + String unlessString = AnnotationUtil.get(cacheable.unless()); + if (Utils.isUnless(unlessString, method, inv.getArgs())) { + inv.invoke(); + return; + } Class targetClass = inv.getTarget().getClass(); String cacheName = AnnotationUtil.get(cacheable.name()); diff --git a/src/main/java/io/jboot/components/cache/interceptor/ParaDirective.java b/src/main/java/io/jboot/components/cache/interceptor/ParaDirective.java index 3c570d09..c755a392 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/ParaDirective.java +++ b/src/main/java/io/jboot/components/cache/interceptor/ParaDirective.java @@ -54,5 +54,18 @@ public class ParaDirective extends JbootDirectiveBase { LogKit.error(e.toString(), e); } } + + public static String para(String key) { + return para(key, null); + } + + public static String para(String key, String defaultValue) { + Controller controller = JbootControllerContext.get(); + if (controller == null) { + throw new IllegalStateException("para(...) method only use for controller." ); + } + + return controller.get(key, defaultValue); + } } diff --git a/src/main/java/io/jboot/components/cache/interceptor/Utils.java b/src/main/java/io/jboot/components/cache/interceptor/Utils.java index b33a2ffe..7942db1e 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/Utils.java +++ b/src/main/java/io/jboot/components/cache/interceptor/Utils.java @@ -36,10 +36,11 @@ import java.util.Map; class Utils { - static final Engine ENGINE = new Engine("JbootCacheRender"); + static final Engine ENGINE = new Engine("JbootCacheRenderEngine"); static { ENGINE.addDirective("para", ParaDirective.class); + ENGINE.addSharedStaticMethod(ParaDirective.class); } /** @@ -62,7 +63,6 @@ class Utils { } return ENGINE.getTemplateByString(template).renderToString(datas); - } static String buildCacheKey(String key, Class clazz, Method method, Object[] arguments) { @@ -225,13 +225,15 @@ class Utils { static boolean isUnless(String unlessString, Method method, Object[] arguments) { - if (StrUtil.isBlank(unlessString)) { return false; } - String template = "#(" + unlessString + ")"; - return "true".equals(engineRender(template, method, arguments)); +// if (!unlessString.contains("#")) { + unlessString = "#(" + unlessString + ")"; +// } + + return "true".equals(engineRender(unlessString, method, arguments)); } diff --git a/src/test/java/io/jboot/test/controller/CacheController.java b/src/test/java/io/jboot/test/controller/CacheController.java index 9d08e49b..fcfaa40d 100644 --- a/src/test/java/io/jboot/test/controller/CacheController.java +++ b/src/test/java/io/jboot/test/controller/CacheController.java @@ -30,6 +30,17 @@ public class CacheController extends JbootController { } + @Cacheable(name = "aaa", liveSeconds = 10, unless = "para('unless')=='nocache'") + public void json2() { + System.out.println("json2() invoked!!!!!!!!!"); + Map data = new HashMap<>(); + data.put("age", 1); + data.put("name", "张三"); + data.put("sex", 1); + renderJson(data); + } + + @Cacheable(name = "aaa", liveSeconds = 10) public void html() { System.out.println("html() invoked!!!!!!!!!"); @@ -49,12 +60,12 @@ public class CacheController extends JbootController { " 1234567890123456\n" + ""; - renderText(xml,"xml"); + renderText(xml, "xml"); } @CacheEvict(name = "aaa") - public void removeAll(){ + public void removeAll() { renderText("ok"); } -- Gitee From c49e5548299cc9dfa37253cde0c2bfb4b2b29be4 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 27 Feb 2022 12:22:14 +0800 Subject: [PATCH 1588/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A@Cacheable?= =?UTF-8?q?()=20=E7=AD=89=E7=B3=BB=E5=88=97=E7=BC=93=E5=AD=98=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E5=AF=B9=20Controller=20=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cache/interceptor/ParaDirective.java | 18 ++++++++++++++---- .../jboot/test/controller/CacheController.java | 10 ++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/interceptor/ParaDirective.java b/src/main/java/io/jboot/components/cache/interceptor/ParaDirective.java index c755a392..aadaf6b2 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/ParaDirective.java +++ b/src/main/java/io/jboot/components/cache/interceptor/ParaDirective.java @@ -55,17 +55,27 @@ public class ParaDirective extends JbootDirectiveBase { } } - public static String para(String key) { + public static Object para(String key) { return para(key, null); } - public static String para(String key, String defaultValue) { + public static Object para(String key, Object defaultValue) { Controller controller = JbootControllerContext.get(); if (controller == null) { - throw new IllegalStateException("para(...) method only use for controller." ); + throw new IllegalStateException("para(...) method only use for controller."); } - return controller.get(key, defaultValue); + String value = controller.get(key); + + if (StrUtil.isNumeric(value)) { + return Long.valueOf(value); + } + + if (StrUtil.isDecimal(value)) { + return Double.parseDouble(value); + } + + return StrUtil.isNotBlank(value) ? value : defaultValue; } } diff --git a/src/test/java/io/jboot/test/controller/CacheController.java b/src/test/java/io/jboot/test/controller/CacheController.java index fcfaa40d..7301e5a9 100644 --- a/src/test/java/io/jboot/test/controller/CacheController.java +++ b/src/test/java/io/jboot/test/controller/CacheController.java @@ -40,6 +40,16 @@ public class CacheController extends JbootController { renderJson(data); } + @Cacheable(name = "aaa", liveSeconds = 10, unless = "para('type')==1") + public void json3() { + System.out.println("json2() invoked!!!!!!!!!"); + Map data = new HashMap<>(); + data.put("age", 1); + data.put("name", "张三"); + data.put("sex", 1); + renderJson(data); + } + @Cacheable(name = "aaa", liveSeconds = 10) public void html() { -- Gitee From d5fbcb5e58d3743da1206f1a9c867e763298da70 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 27 Feb 2022 12:50:17 +0800 Subject: [PATCH 1589/1965] update docs --- doc/docs/db.md | 418 +++++++++++++++++++++++++------------- doc/docs/mvc.md | 136 ++++++++++++- simples/websocket/pom.xml | 2 +- 3 files changed, 412 insertions(+), 144 deletions(-) diff --git a/doc/docs/db.md b/doc/docs/db.md index f296d4b8..ebdaa62c 100644 --- a/doc/docs/db.md +++ b/doc/docs/db.md @@ -26,6 +26,68 @@ Jboot 的数据库是依赖 JFinal 的 ORM 做基本的数据库操作,同时 - Apache Sharding-Sphere 文档:http://shardingsphere.io/document/current/cn/overview/ - Seata 的帮助文档:https://github.com/seata/seata/wiki/Home_Chinese +## 配置 + +基本配置 + +```properties +jboot.datasource.type=mysql +jboot.datasource.url=jdbc:mysql://127.0.0.1:3306/dbname +jboot.datasource.user=root +jboot.datasource.password=123456 +``` + +更多配置 +```properties +# 数据源名称 +jboot.datasource.name +# 数据源类型,支持 mysql oracle sqlserver sqlite ansisql postgresql clickhouse 等 +jboot.datasource.type +# 链接url +jboot.datasource.url +# 数据库登录账号 +jboot.datasource.user +#数据库登录密码 +jboot.datasource.password +#数据源驱动,一般只需要配置 type,程序自动会寻找适合的驱动 +jboot.datasource.driverClassName = com.mysql.jdbc.Driver +jboot.datasource.connectionInitSql +jboot.datasource.poolName +jboot.datasource.cachePrepStmts = true +jboot.datasource.prepStmtCacheSize = 500 +jboot.datasource.prepStmtCacheSqlLimit = 2048 +jboot.datasource.maximumPoolSize = 10 +jboot.datasource.maxLifetime +jboot.datasource.idleTimeout +jboot.datasource.minimumIdle = 0 + +#sql 模板文件存放路径 +jboot.datasource.sqlTemplatePath +# sql 模板文件的名称,多个模板文件使用英文都还隔开 +jboot.datasource.sqlTemplate + +#数据源创建的工厂类 DataSourceFactory +jboot.datasource.factory +#分布式分库分表的 sharding 配置文件 +jboot.datasource.shardingConfigYaml +#jfinal 的 dbPro 的创建工厂类,该类需要实现 IDbProFactory 接口 +jboot.datasource.dbProFactory +#jfinal ActiveRecordPlugin 插件里需要配置的 containerFactory,填写类名 +jboot.datasource.containerFactory +#jfinal 默认配置的事务隔离级别 +jboot.datasource.transactionLevel +# 此数据源包含哪些表 +jboot.datasource.table +# 该数据源排除哪些表 +jboot.datasource.exTable +# 数据方言的 class +jboot.datasource.dialectClass +# 自定义的 activeRecordPlugin 类,该类需要继承 ActiveRecordPlugin +jboot.datasource.activeRecordPluginClass +# 是否需要添加到映射,当不添加映射的时候,只能通过 model.use("xxx").save()这种方式去调用该数据源 +jboot.datasource.needAddMapping = true +``` + ## 基本增删改查 @@ -40,9 +102,9 @@ JFinal 操作数据库,提供了两种方式对数据库进行操作,他们 参考 JFinal 的文档:https://jfinal.com/doc/5-5 -### Model 映射方式 +### 使用 @Table 注解来玩喷子和 Model 映射 -Model是 MVC 模式中的 M 部分。以下是 Model 定义示例代码: +Model 是 MVC 模式中的 M 部分。以下是 Model 定义示例代码: ```java @Table(tableName = "user", primaryKey = "id") @@ -72,7 +134,7 @@ public abstract class BaseUser> extends JbootModel impl 需要注意的是: - 以上的 `User` 和 `BaseUser` 都是通过代码生成器自动生成的,无需手写。 -- 多次执行代码生成器,`User` 代码不会被覆盖,但是 `BaseUser` 会被重新覆盖,因此,请不要在 `BaseUser` 手写任何代码。 +- 多次执行代码生成器,`User` 代码不会被覆盖,但是 `BaseUser` 会被重新覆盖,因此,请不要在 `BaseUser` 手写任何代码。 一般情况下,在正式的项目里,代码分层还需要 `Service` 层来对业务逻辑进行处理。 @@ -126,18 +188,6 @@ CREATE TABLE `user` ( `email` varchar(64) DEFAULT NULL COMMENT '邮件', `mobile` varchar(32) DEFAULT NULL COMMENT '手机电话', `gender` varchar(16) DEFAULT NULL COMMENT '性别', - `signature` varchar(2048) DEFAULT NULL COMMENT '签名', - `birthday` datetime DEFAULT NULL COMMENT '生日', - `company` varchar(256) DEFAULT NULL COMMENT '公司', - `occupation` varchar(256) DEFAULT NULL COMMENT '职位、职业', - `address` varchar(256) DEFAULT NULL COMMENT '地址', - `zipcode` varchar(128) DEFAULT NULL COMMENT '邮政编码', - `site` varchar(256) DEFAULT NULL COMMENT '个人网址', - `graduateschool` varchar(256) DEFAULT NULL COMMENT '毕业学校', - `education` varchar(256) DEFAULT NULL COMMENT '学历', - `avatar` varchar(256) DEFAULT NULL COMMENT '头像', - `idcardtype` varchar(128) DEFAULT NULL COMMENT '证件类型:身份证 护照 军官证等', - `idcard` varchar(128) DEFAULT NULL COMMENT '证件号码', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表,保存用户信息。'; ``` @@ -177,8 +227,6 @@ public List findListBy(int userAge,String articleTitle){ 在 Jboot 中,提供了 Join 系列方法,我们在 Service 层可以直接使用 Join 进行 一对一、一对多、多对一、多对对 的查询操作。 - - 假设存在这样的关系:一篇文章只有一个作者,一个作者可以写多篇文章,一篇文章可以归属多个文章分类、一个文章分类有可以包含多篇文章。 @@ -303,13 +351,17 @@ public class ArticleService extends JbootServiceBase

        { @Inject private CategoryService categoryService; - public List
        findListWithAuthorAndCategories(){ + /** + * 查询出文章列表,并自动查询其归属的多个分类,已经文章的作者 + * @return + */ + public List
        findListWithAuthorAndCategories(){ List
        articles = DAO.findAll(); - // 查询出每篇文章的作者 + // 查询出每篇文章的作者,关系字段的字段名称 authorService.join(articles,"author_id"); - // 查询文章的所属分类 + // 查询文章的所属分类,三个参数分别是:中间表名称,article表对应的字段,分类表对应的字段 categoryService.joinManyByTable(articles,"article_category","article_id","category_id"); return articles; @@ -322,86 +374,86 @@ ArticleService 输出的 Json 内容如下: ```json [ { - "author":{ - "nickname":"孙悟空", - "id":1, - "email":"swk@gmail.com" - }, - "categoryList":[ - { - "description":"文章分类描述111", - "id":1, - "title":"文章分类1" - }, - { - "description":"文章分类描述222", - "id":2, - "title":"文章分类2" - } - ], "id":1, "authorId":1, "title":"文章1", - "content":"内容111" - }, - { + "content":"内容111", "author":{ - "nickname":"孙悟空", - "id":1, - "email":"swk@gmail.com" + "nickname":"孙悟空", + "id":1, + "email":"swk@gmail.com" }, "categoryList":[ - { - "description":"文章分类描述222", - "id":2, - "title":"文章分类2" - } - ], + { + "description":"文章分类描述111", + "id":1, + "title":"文章分类1" + }, + { + "description":"文章分类描述222", + "id":2, + "title":"文章分类2" + } + ] + }, + { "id":2, "authorId":1, "title":"文章2", - "content":"内容2222" - }, - { + "content":"内容2222", "author":{ - "nickname":"猪八戒", - "id":2, - "email":"zbj@gmail.com" + "nickname":"孙悟空", + "id":1, + "email":"swk@gmail.com" }, "categoryList":[ - { - "description":"文章分类描述111", - "id":1, - "title":"文章分类1" - }, - { - "description":"文章分类描述222", - "id":2, - "title":"文章分类2" - } - ], + { + "description":"文章分类描述222", + "id":2, + "title":"文章分类2" + } + ] + }, + { "id":3, "authorId":2, "title":"文章3", - "content":"内容333" - }, - { + "content":"内容333", "author":{ - "nickname":"猪八戒", - "id":2, - "email":"zbj@gmail.com" + "nickname":"猪八戒", + "id":2, + "email":"zbj@gmail.com" }, "categoryList":[ - { - "description":"文章分类描述111", - "id":1, - "title":"文章分类1" - } - ], + { + "description":"文章分类描述111", + "id":1, + "title":"文章分类1" + }, + { + "description":"文章分类描述222", + "id":2, + "title":"文章分类2" + } + ] + }, + { "id":4, "authorId":2, "title":"文章4", - "content":"内容444" + "content":"内容444", + "author":{ + "nickname":"猪八戒", + "id":2, + "email":"zbj@gmail.com" + }, + "categoryList":[ + { + "description":"文章分类描述111", + "id":1, + "title":"文章分类1" + } + ] } ] ``` @@ -420,7 +472,7 @@ public class AuthorService extends JbootServiceBase { public List findListWithArticles(){ List authors = DAO.findAll(); - //查询每个作者的所有文章 + //查询每个作者的所有文章,第二个参数是关系字段的名称 articleService.joinMany(authors,"author_id"); return authors; @@ -433,42 +485,42 @@ AuthorService 输出的 Json 内容如下: ```json [ { - "articleList":[ - { - "id":1, - "authorId":1, - "title":"文章1", - "content":"内容111" - }, - { - "id":2, - "authorId":1, - "title":"文章2", - "content":"内容2222" - } - ], - "nickname":"孙悟空", "id":1, - "email":"swk@gmail.com" + "nickname":"孙悟空", + "email":"swk@gmail.com", + "articleList":[ + { + "id":1, + "authorId":1, + "title":"文章1", + "content":"内容111" + }, + { + "id":2, + "authorId":1, + "title":"文章2", + "content":"内容2222" + } + ] }, { - "articleList":[ - { - "id":3, - "authorId":2, - "title":"文章3", - "content":"内容333" - }, - { - "id":4, - "authorId":2, - "title":"文章4", - "content":"内容444" - } - ], - "nickname":"猪八戒", "id":2, - "email":"zbj@gmail.com" + "nickname":"猪八戒", + "email":"zbj@gmail.com", + "articleList":[ + { + "id":3, + "authorId":2, + "title":"文章3", + "content":"内容333" + }, + { + "id":4, + "authorId":2, + "title":"文章4", + "content":"内容444" + } + ] } ] ``` @@ -487,7 +539,7 @@ public class CategoryService extends JbootServiceBase { public List findListWithArticles(){ List categories = DAO.findAll(); - //查询每个分类的所有文章 + //查询每个分类的所有文章 articleService.joinManyByTable(categories,"article_category","category_id","article_id"); return categories; @@ -630,38 +682,122 @@ company.save(); 代码中不再需要 `use("a1")` 指定数据源,因为 `company` 表只有一个数据源。 -更多关于 datasource 的配置如下: +## 事务以及 @Transactional 注解 + +在 JFinal 中提供了一种使用 Db.tx(...) 的操作方式,具体参考:https://jfinal.com/doc/5-7 ,同时,Jboot 也提供了一个用于进行事务管理的注解 +@Transactional 。具体用法如下: +实例1:出现异常时回滚 +```java +@Transactional +public void test1() { + //your code + throw new RuntimeException("..."); +} ``` -jboot.datasource.name //数据源名称 -jboot.datasource.type //数据源类型 -jboot.datasource.url //数据源URL地址 -jboot.datasource.user -jboot.datasource.password -jboot.datasource.driverClassName = com.mysql.jdbc.Driver -jboot.datasource.connectionInitSql -jboot.datasource.poolName -jboot.datasource.cachePrepStmts = true -jboot.datasource.prepStmtCacheSize = 500 -jboot.datasource.prepStmtCacheSqlLimit = 2048 -jboot.datasource.maximumPoolSize = 10 -jboot.datasource.maxLifetime -jboot.datasource.idleTimeout -jboot.datasource.minimumIdle = 0 -jboot.datasource.sqlTemplatePath // sql 模板存放路径,用到 jfinal sql独立文件的时候 -jboot.datasource.sqlTemplate -jboot.datasource.factory -jboot.datasource.shardingConfigYaml //分库分表的配置文件 -jboot.datasource.dbProFactory -jboot.datasource.containerFactory -jboot.datasource.transactionLevel -jboot.datasource.table //此数据源包含哪些表 -jboot.datasource.exTable //该数据源排除哪些表 -jboot.datasource.dialectClass -jboot.datasource.activeRecordPluginClass -jboot.datasource.needAddMapping = true //是否需要添加到映射,当不添加映射的时候,只能通过 model.use("xxx").save()这种方式去调用该数据源 + +实例2:返回值为 false 时回滚 +```java +@Transactional(rollbackForFalse=true) +public boolean test2() { + //your code + return false; +} ``` +实例3:返回值为 Ret.fail() 时回滚 +```java +@Transactional(rollbackForRetFail=true) +public Ret test3() { + //your code + return Ret.fail("message..."); +} +``` + +实例4:返回值为 null 时回滚 +```java +@Transactional(rollbackForNull=true) +public Ret test3() { + //your code + return null; +} +``` + +@Transactional 的更多配置: +```java +public @interface Transactional { + + /** + * 使用哪个数据源 + * + * @return + */ + String config() default ""; + + /** + * 事务隔离级别 + * + * @return + */ + int transactionLevel() default -1; + + /** + * return false 的时候,是否进行回滚 + * + * @return + */ + boolean rollbackForFalse() default false; + + /** + * return ret.fail 的时候,是否进行回滚 + * + * @return + */ + boolean rollbackForRetFail() default false; + + + /** + * 返回 null 的时候,是否进行回滚 + * + * @return + */ + boolean rollbackForNull() default false; + + + /** + * 配置允许哪些异常不回滚 + * + * @return + */ + Class[] noRollbackFor() default {}; + + /** + * 是否在新的线程里执行,在 Controller 的 Action 方法下配置无效 + * 在 Controller 里,不能以新的线程在运行 + * + * @return + */ + boolean inNewThread() default false; + + /** + * 使用哪个线程池来运行线程,需要在启动的时候,通过 TransactionalManager 来配置线程池及其名称 + * + * @return + */ + String threadPoolName() default ""; + + /** + * 是否以阻塞的方式运行线程,这个配置只有在返回值 void 情况下配置生效 + * 有返回值的,此配置无效,默认都是阻塞运行线程的方式运行 + * + * @return + */ + boolean threadWithBlocked() default false; + +} +``` + + ## 读写分离 diff --git a/doc/docs/mvc.md b/doc/docs/mvc.md index 546f07d3..904c7283 100644 --- a/doc/docs/mvc.md +++ b/doc/docs/mvc.md @@ -24,9 +24,141 @@ JFinal 的相关文档: [https://www.jfinal.com/doc/3-1](https://www.jfinal.co Controller 是 JFinal 的核心类之一,是 MVC 设计模式中的控制器。基于 Jboot 开发的控制器需要继承 Controller。Controller 也是定义 Action 方法的地点,一个 Controller 可以包含多个 Action 。 - 另外,JbootController 扩展了 JFinal 的 Controller 类,增加了 Jwt、FlashMessage 和 其他一些实用的方法。 +## Jboot 增强的 Controller 功能 - 建议基于 Jboot 开发的应用,都继承至 JbootController。(备注:只是建议、而非必须。) +#### 增强功能1:缓存功能 + +例如: +```java +@Cacheable(name = "aaa", liveSeconds = 10) +public void json() { + + System.out.println("json() invoked!!!!!!!!!"); + + Map data = new HashMap<>(); + data.put("age", 1); + data.put("name", "张三"); + data.put("sex", 1); + + renderJson(data); +} +``` +以上的代码中,在 10 秒钟内多次访问的时候,只有第一次访问到了 json() 方法,其余的都直接使用第一次的结果输出给浏览器。其中,缓存的名称为 +"aaa",key 为 `class.method()` 格式。我们可以通过 `@Cacheable(name = "aaa", key="xxx")` 来指定缓存的key值。 + +其中 key 里的内容还可以使用 `#(方法参数名称)` 和 `@para('http参数名称')` 的方式来定义 key。 + +例如: +```java +@Cacheable(name = "aaa", key="#para('mykey')", liveSeconds = 10) +public void json() { + + System.out.println("json() invoked!!!!!!!!!"); + + Map data = new HashMap<>(); + data.put("age", 1); + data.put("name", "张三"); + data.put("sex", 1); + + renderJson(data); +} +``` +当访问 `http://127.0.0.1:8080/json?mykey=thekey` 的时候,该缓存的 key 的内容为:"thekey"。 + +但是在某些极端场景下,我们可能是不需要缓存的,配置如下: + +```java + @Cacheable(name = "aaa", liveSeconds = 10, unless = "para('type')==1") + public void json2() { + System.out.println("json2() invoked!!!!!!!!!"); + Map data = new HashMap<>(); + data.put("age", 1); + data.put("name", "张三"); + data.put("sex", 1); + renderJson(data); + } +``` + +当访问 `http://127.0.0.1:8080/json2?type=1` 的时候,永远不会命中缓存。 + +#### 增强功能2:返回值自动渲染功能 + +例如: + +```java +@RequestMapping("/") +public MyController extends Controller{ + + //自动使用 html 来渲染 + public String test1(){ + return "test1.html"; + } + + //自动使用文本来渲染 + public String test2(){ + return "test2..."; + } + + //渲染 404 错误 + public String test3(){ + return "error: 404"; + } + + //渲染 500 错误 + public String test4(){ + return "error: 500"; + } + + //自动 redirect 跳转 + public String test5(){ + return "redirect: /to/your/path"; + } + + //自动 forward action 重定向 + public String test6(){ + return "forward: /to/your/path"; + } + + //自动进行文件下载 + public File test7(){ + return new File('/file/path'); + } + + //渲染 json 内容 + public Object test8(){ + Map map = new HashMap<>(); + map.put("key","value...."); + return map; + } + + //使用自动的 render 渲染 + public Object test9(){ + Render render = new TextRender("some text..."); + return render; + } + +} +``` + +#### 增强功能3:XSS 功能防护功能 + +在 jboot 中,只需要我们在 jboot.properties 添加如下配置,并能开启全局 xss 防护功能: + +```properties +jboot.web.escapeParasEnable = true +``` +所以的 http 请求都会自动进行 escape 编码,防止 xss 攻击。若想在某些极端场景下获取原始内容,只需要我们在 Controller 里通过 +getOriginalPara('key'); 既可以获得原始的内容,此时,需要自己进行 XSS 内容过滤或者对内容进行安全编码。 + +#### 增强功能4:更加强大的验证器功能 + +详情:http://www.jboot.com.cn/docs/validator.html + +#### 更多的增强功能 + +Jboot 还对 web 模块做了许多其他的增强,比如 1、分布式 session 的支持; 2、json 增强,前端传入 json 内容可以直接注入到 model 或者 bean; +3、更多的模板指令,比如前端的分页指令等。4、@EnableCORS 对跨域的支持。 5、更加方便的枚举类在 模板 里的使用。6、提供 @GetRequest、@PostRequest +对 Controller 方法的限制 等等等等。 ## Action diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index ab9dca9f..128a84ee 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.14.0 + 3.14.0-SNAPSHOT -- Gitee From d3257015ed20e97072259eb99aa850c60d9d18f1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 27 Feb 2022 15:39:39 +0800 Subject: [PATCH 1590/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9A=20?= =?UTF-8?q?=E5=88=86=E5=B8=83=E5=BC=8F=E4=BB=BB=E5=8A=A1=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=20@EnableDistributedRunnable=EF=BC=8C=E5=B9=B6=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=87=AA=E5=AE=9A=E4=B9=89=E7=9A=84=20redisKey=20?= =?UTF-8?q?=E5=92=8C=20key=20=E6=8C=81=E6=9C=89=E6=97=B6=E9=97=B4=EF=BC=8C?= =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=85=B6=E7=A8=B3=E5=AE=9A=E6=80=A7=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../schedule/JbootDistributedRunnable.java | 178 +++++++++++------- .../schedule/JbootScheduleManager.java | 21 ++- .../annotation/EnableDistributedRunnable.java | 14 ++ .../components/serializer/FstSerializer.java | 2 +- .../io/jboot/test/distributed/Runable1.java | 17 ++ .../io/jboot/test/distributed/Runable2.java | 17 ++ .../io/jboot/test/distributed/Runable3.java | 17 ++ 7 files changed, 191 insertions(+), 75 deletions(-) create mode 100644 src/test/java/io/jboot/test/distributed/Runable1.java create mode 100644 src/test/java/io/jboot/test/distributed/Runable2.java create mode 100644 src/test/java/io/jboot/test/distributed/Runable3.java diff --git a/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java b/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java index fae23a62..302512d6 100644 --- a/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java +++ b/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java @@ -15,17 +15,20 @@ */ package io.jboot.components.schedule; +import com.jfinal.kit.LogKit; import com.jfinal.log.Log; import io.jboot.Jboot; import io.jboot.support.redis.JbootRedis; +import io.jboot.utils.ClassUtil; import io.jboot.utils.QuietlyUtil; +import io.jboot.utils.StrUtil; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 * @Title: 分布式任务 * @Description: 在分布式应用中,处理分布式应用,基于redis。 - * + *

        * 特点: * 1、简单,无需依赖数据库。 * 2、高可用,不存在单点故障 @@ -37,113 +40,153 @@ public class JbootDistributedRunnable implements Runnable { private static final Log LOG = Log.getLog(JbootDistributedRunnable.class); private JbootRedis redis; - private int expire = 50 * 1000; // 单位毫秒 + + // 锁的过期时间,单位毫秒,默认 1 分钟, + // 因此,配置的定时任务,每次执行任务的时间间隔,必须大于 1 分钟以上 + private int expire = 60 * 1000; private String key; private Runnable runnable; - public JbootDistributedRunnable() { - - this.redis = Jboot.getRedis(); - this.key = "jbootRunnable:" + this.getClass().getName(); + public JbootDistributedRunnable(Runnable runnable, String key, int expireSeconds) { + this.runnable = runnable; - if (redis == null) { - LOG.warn("redis is null, " + - "can not use @EnableDistributedRunnable in your Class[" + this.getClass().getName() + "], " + - "or config redis info in jboot.properties"); + if (StrUtil.isNotBlank(key)) { + this.key = key; + } else { + this.key = "jboot-distributed-key:" + ClassUtil.getUsefulClass(runnable.getClass()).getName(); } - } - - public JbootDistributedRunnable(Runnable runnable) { - this.runnable = runnable; - this.key = "jbootRunnable:" + runnable.getClass().getName(); - this.redis = Jboot.getRedis(); - if (redis == null) { - LOG.warn("redis is null, " + - "can not use @EnableDistributedRunnable in your Class[" + runnable.getClass().getName() + "], " + - "or config redis info in jboot.properties"); + if (expireSeconds > 0) { + this.expire = expireSeconds * 1000; } - } - public JbootDistributedRunnable(Runnable runnable, int expire) { - this.expire = (expire - 1) * 1000; - this.runnable = runnable; - this.key = "jbootRunnable:" + runnable.getClass().getName(); this.redis = Jboot.getRedis(); + if (redis == null) { - LOG.warn("redis is null, " + - "can not use @EnableDistributedRunnable in your Class[" + runnable.getClass().getName() + "], " + - "or config redis info in jboot.properties"); + LOG.warn("Redis is null, Can not use @EnableDistributedRunnable in your class: " + + ClassUtil.getUsefulClass(runnable.getClass()).getName() + + ", Please config redis info in jboot.properties"); } + } @Override public void run() { - if (redis == null) { + // 当未配置 redis 的时候,默认使用本地任务的方式进行执行 + runnable.run(); return; } - Long result = null; + + Long setTimeMillis = System.currentTimeMillis(); + + //要设置的值 + String setValue = setTimeMillis + ":" + StrUtil.uuid(); + + + boolean locked = false; for (int i = 0; i < 5; i++) { - Long setTimeMillis = System.currentTimeMillis(); - result = redis.setnx(key, setTimeMillis); + //setnx: 只在键 key 不存在的情况下, 将键 key 的值设置为 value, 若键 key 已经存在, 则 SETNX 命令不做任何动作。 + //result: 设置成功,返回 1,设置失败,返回 0 + Long result = redis.setnx(key, setValue); - //error + //error 一般不会出现这种情况,除非是网络异常等原因 if (result == null) { - quietSleep(); + quietlySleep(); + continue; + } + + + //setnx success 设置成功 + if (result == 1) { + String value = redis.get(key); + + //在分布式的场景下,可能自己设置成功后,又被其他节点删除重新设置的情况 + //需要判断是否是当前节点(或者线程)设置的 + if (setValue.equals(value)) { + locked = true; + break; + } + // 可能被其他节点删除,或重置了 + else { + quietlySleep(); + continue; + } } - //setnx fail + //setnx fail,可能已经被其他 server 优先设置了,也有可能是 自己的server 在上一次任务里设置了 else if (result == 0) { - Long saveTimeMillis = redis.get(key); - if (saveTimeMillis == null) { + + String value = null; + + try { + value = redis.get(key); + } catch (Exception ex) { + LogKit.logNothing(ex); + } + + + //获取不到,一般不会出现这种情况,除非是网络异常等原因 + //或者是使用了已经存在的 key,但是此 key 已经有其他序列化方式的值导致异常 + if (value == null) { reset(); + quietlySleep(); + continue; } - long ttl = System.currentTimeMillis() - saveTimeMillis; - if (ttl > expire) { - //防止死锁 + + String[] split = value.split(":"); + + //被其他节点,或者手动操作 redis 的方式给设置了这个key值 + if (split.length != 2) { reset(); + continue; } - // 休息 2 秒钟,重新去抢,因为可能别的应用执行失败了 - quietSleep(); + //获取设置的时间 + long savedTimeMillis = 0; - } + try { + savedTimeMillis = Long.parseLong(split[0]); + } catch (NumberFormatException ex) { + LogKit.logNothing(ex); + } + + //redis 存储的内容有问题,可能是被手动设置 redis 的方式设置了这个 key 值 + if (savedTimeMillis == 0) { + reset(); + continue; + } - //set success - else if (result == 1) { - break; + if ((System.currentTimeMillis() - savedTimeMillis) > expire) { + //若设置锁的时间以及过期了 + //说明是上一次任务配置的,此时需要删除这个过期的 key,然后重新去抢 + reset(); + } + // 若锁没有过期,休息后重新去抢,因为抢走的线程可能会重新释放 + else { + quietlySleep(); + } } + } //抢了5次都抢不到,证明已经被别的应用抢走了 - if (result == null || result == 0) { + if (!locked) { return; } try { - - if (runnable != null) { - runnable.run(); - } else { - boolean runSuccess = execute(); - - //run()执行失败,让别的分布式应用APP去执行 - //如果run()执行的时间很长(超过30秒),那么别的分布式应用可能也抢不到了,只能等待下次轮休 - //作用:故障转移 - if (!runSuccess) { - reset(); - } - } + runnable.run(); } // 如果 run() 执行异常,让别的分布式应用APP去执行 + // 但如果 run() 执行的时间很长(超过30秒),而且失败了,那么别的分布式应用可能也抢不到了,只能等待下次任务 // 作用:故障转移 catch (Throwable ex) { LOG.error(ex.toString(), ex); @@ -153,14 +196,18 @@ public class JbootDistributedRunnable implements Runnable { /** - * 重置分布式的key + * 重置分布式的 key */ private void reset() { - redis.del(key); + try { + redis.del(key); + } catch (Exception ex) { + LogKit.logNothing(ex); + } } - public void quietSleep() { + public void quietlySleep() { int millis = 2000; if (this.expire <= 2000) { millis = 100; @@ -173,10 +220,5 @@ public class JbootDistributedRunnable implements Runnable { QuietlyUtil.quietlySleep(millis); } - //for override - public boolean execute() { - return true; - } - } diff --git a/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java b/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java index 4cf4c001..d1d384f9 100644 --- a/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java +++ b/src/main/java/io/jboot/components/schedule/JbootScheduleManager.java @@ -87,9 +87,11 @@ public class JbootScheduleManager { FixedDelay fixedDelayJob = runnableClass.getAnnotation(FixedDelay.class); if (fixedDelayJob != null) { Runnable runnable = ClassUtil.newInstance(runnableClass); - Runnable executeRunnable = runnableClass.getAnnotation(EnableDistributedRunnable.class) == null + + EnableDistributedRunnable enableDistributedRunnable = runnableClass.getAnnotation(EnableDistributedRunnable.class); + Runnable executeRunnable = enableDistributedRunnable == null ? runnable - : new JbootDistributedRunnable(runnable, fixedDelayJob.period()); + : new JbootDistributedRunnable(runnable, AnnotationUtil.get(enableDistributedRunnable.redisKey()), enableDistributedRunnable.expireSeconds()); // ScheduledThreadPoolExecutor 线程池在遇到未捕获的异常时会终止调度 // JbootSafeRunnable 可以捕捉业务代码异常,防止线程池意外终止调度 @@ -107,9 +109,13 @@ public class JbootScheduleManager { FixedRate fixedRateJob = runnableClass.getAnnotation(FixedRate.class); if (fixedRateJob != null) { Runnable runnable = ClassUtil.newInstance(runnableClass); - Runnable executeRunnable = runnableClass.getAnnotation(EnableDistributedRunnable.class) == null + + EnableDistributedRunnable enableDistributedRunnable = runnableClass.getAnnotation(EnableDistributedRunnable.class); + Runnable executeRunnable = enableDistributedRunnable == null ? runnable - : new JbootDistributedRunnable(runnable, fixedRateJob.period()); + : new JbootDistributedRunnable(runnable, AnnotationUtil.get(enableDistributedRunnable.redisKey()), enableDistributedRunnable.expireSeconds()); + + executeRunnable = new JbootSafeRunnable(executeRunnable); try { scheduleRunnableCache.put(runnableClass, executeRunnable); @@ -126,9 +132,12 @@ public class JbootScheduleManager { if (cron != null) { String value = AnnotationUtil.get(cron.value()); Runnable runnable = ClassUtil.newInstance(runnableClass); - Runnable executeRunnable = runnableClass.getAnnotation(EnableDistributedRunnable.class) == null + + EnableDistributedRunnable enableDistributedRunnable = runnableClass.getAnnotation(EnableDistributedRunnable.class); + Runnable executeRunnable = enableDistributedRunnable == null ? runnable - : new JbootDistributedRunnable(runnable); + : new JbootDistributedRunnable(runnable, AnnotationUtil.get(enableDistributedRunnable.redisKey()), enableDistributedRunnable.expireSeconds()); + scheduleRunnableCache.put(runnableClass, executeRunnable); cron4jPlugin.addTask(value, executeRunnable, cron.daemon()); } diff --git a/src/main/java/io/jboot/components/schedule/annotation/EnableDistributedRunnable.java b/src/main/java/io/jboot/components/schedule/annotation/EnableDistributedRunnable.java index 800f954a..596f51f0 100644 --- a/src/main/java/io/jboot/components/schedule/annotation/EnableDistributedRunnable.java +++ b/src/main/java/io/jboot/components/schedule/annotation/EnableDistributedRunnable.java @@ -21,4 +21,18 @@ import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface EnableDistributedRunnable { + + /** + * 分布式任务用的 redisKey,不配置默认使用 Runnable 实现类的类名 + * + * @return + */ + String redisKey() default ""; + + /** + * redisKey 持有时间,不配置默认为 1 分钟 + * + * @return + */ + int expireSeconds() default 0; } \ No newline at end of file diff --git a/src/main/java/io/jboot/components/serializer/FstSerializer.java b/src/main/java/io/jboot/components/serializer/FstSerializer.java index a2556f42..eb1bb8b3 100644 --- a/src/main/java/io/jboot/components/serializer/FstSerializer.java +++ b/src/main/java/io/jboot/components/serializer/FstSerializer.java @@ -41,7 +41,7 @@ public class FstSerializer implements JbootSerializer { try { return fst.asObject(bytes); } catch (Exception ex) { - LOG.error("FstSerializer deserialize error!", ex); + LOG.error("FstSerializer deserialize fail!", ex); } return null; } diff --git a/src/test/java/io/jboot/test/distributed/Runable1.java b/src/test/java/io/jboot/test/distributed/Runable1.java new file mode 100644 index 00000000..d2a41138 --- /dev/null +++ b/src/test/java/io/jboot/test/distributed/Runable1.java @@ -0,0 +1,17 @@ +package io.jboot.test.distributed; + +import io.jboot.components.schedule.annotation.EnableDistributedRunnable; +import io.jboot.components.schedule.annotation.FixedDelay; + +@FixedDelay(period = 30) +@EnableDistributedRunnable(redisKey = "aaa",expireSeconds = 30) +public class Runable1 implements Runnable { + int i =0; + @Override + public void run() { + if (i++ >= 3){ + throw new IllegalStateException("messss..."); + } + System.err.println("Runable1.running.........."+this); + } +} diff --git a/src/test/java/io/jboot/test/distributed/Runable2.java b/src/test/java/io/jboot/test/distributed/Runable2.java new file mode 100644 index 00000000..3e03bbad --- /dev/null +++ b/src/test/java/io/jboot/test/distributed/Runable2.java @@ -0,0 +1,17 @@ +package io.jboot.test.distributed; + +import io.jboot.components.schedule.annotation.EnableDistributedRunnable; +import io.jboot.components.schedule.annotation.FixedDelay; + +@FixedDelay(period = 30) +@EnableDistributedRunnable(redisKey = "aaa",expireSeconds = 30) +public class Runable2 implements Runnable { + int i =0; + @Override + public void run() { + if (i++ >= 3){ + throw new IllegalStateException("messss..."); + } + System.err.println("Runable2.running.........."+this); + } +} diff --git a/src/test/java/io/jboot/test/distributed/Runable3.java b/src/test/java/io/jboot/test/distributed/Runable3.java new file mode 100644 index 00000000..79499b82 --- /dev/null +++ b/src/test/java/io/jboot/test/distributed/Runable3.java @@ -0,0 +1,17 @@ +package io.jboot.test.distributed; + +import io.jboot.components.schedule.annotation.EnableDistributedRunnable; +import io.jboot.components.schedule.annotation.FixedDelay; + +@FixedDelay(period = 30) +@EnableDistributedRunnable(redisKey = "aaa",expireSeconds = 30) +public class Runable3 implements Runnable { + int i =0; + @Override + public void run() { + if (i++ >= 3){ + throw new IllegalStateException("messss..."); + } + System.err.println("Runable3.running.........."+this); + } +} -- Gitee From a917042202fbc2500b41664a80aade3c79923a23 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 27 Feb 2022 16:45:23 +0800 Subject: [PATCH 1591/1965] optimize... --- .../jboot/support/redis/JbootRedisLock.java | 61 +++++++++++-------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/src/main/java/io/jboot/support/redis/JbootRedisLock.java b/src/main/java/io/jboot/support/redis/JbootRedisLock.java index 533737a7..293376b7 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedisLock.java +++ b/src/main/java/io/jboot/support/redis/JbootRedisLock.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -119,40 +119,49 @@ public class JbootRedisLock { */ public boolean acquire() { long timeout = timeoutMsecs; - do { - long expires = System.currentTimeMillis() + expireMsecs + 1; - - Long result = redis.setnx(lockName, expires); - if (result != null && result == 1) { - // lock acquired - locked = true; - return true; - } + try { + //超时时间 + long expiredTime = System.currentTimeMillis() + expireMsecs + 1; - Long savedValue = redis.get(lockName); - if (savedValue != null && savedValue < System.currentTimeMillis()) { - // 判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的 - // lock is expired + Long result = redis.setnx(lockName, expiredTime); - Long oldValue = redis.getSet(lockName, expires); - //获取上一个锁到期时间,并设置现在的锁到期时间, - //只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的 + if (result == null) { + continue; + } - if (oldValue != null && oldValue.equals(savedValue)) { - //如果这个时候,多个线程恰好都到了这里 - //只有一个线程的设置值和当前值相同,他才有权利获取锁 - //lock acquired + // lock acquired + if (result == 1) { locked = true; return true; } - } - if (timeout > 0) { - timeout -= 100; - QuietlyUtil.quietlySleep(100); - } + //之前的保存时间 + Long savedValue = redis.get(lockName); + //这个锁已经过期了,此时可以去设置新的key + if (savedValue != null && savedValue < System.currentTimeMillis()) { + + + //获取上一个锁到期时间,并设置现在的锁到期时间, + //只有一获个线程才能取上一个线上的设置时间,因为jedis.getSet是同步的 + Long oldValue = redis.getSet(lockName, expiredTime); + + + if (oldValue != null && oldValue.equals(savedValue)) { + //如果这个时候,多个线程恰好都到了这里 + //只有一个线程的设置值和当前值相同,他才有权利获取锁 + //lock acquired + locked = true; + return true; + } + } + } finally { + if (timeout > 0) { + timeout -= 100; + QuietlyUtil.quietlySleep(100); + } + } } while (timeout > 0); return false; -- Gitee From 22dc5538378a165aa288291a105bc864a6ea85c7 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 27 Feb 2022 16:56:51 +0800 Subject: [PATCH 1592/1965] v3.14.0 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- src/main/java/io/jboot/JbootConsts.java | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 8adc1c03..66f9a8cf 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.13.8 + 3.14.0 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 4c4c87fe..c2ee9926 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.13.8 + 3.14.0 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 9cb8bace..dd74fc6c 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.13.8 + 3.14.0 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.13.8', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.14.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index adb163c6..58e466fe 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.13.8', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.14.0', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.13.8/jboot-3.13.8.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.0/jboot-3.14.0.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 636c73e9..b184dd6b 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.13.8"; + public static String VERSION = "3.14.0"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From cef43c442569010f09292b60e80540473fa8be13 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 27 Feb 2022 16:56:55 +0800 Subject: [PATCH 1593/1965] v3.14.0 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 13 +++++++++++++ pom.xml | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ca7fdd1c..38432a32 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.13.8 + 3.14.0 ``` diff --git a/changes.txt b/changes.txt index 2ada1bff..385ffe9d 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,16 @@ +jboot v3.14.0: +新增:@Cacheable() 等系列缓存注解对 controller 的支持 +优化:优化 fastjson 序列化的功能,使用 config 而非 features +优化:重命名注解 @TxEnable() 修改为 @Transactional(),并为 @Transactional() 添加更多的配置功能 +优化:为 RabbitMQ 添加更多的自定义配置 +优化:自动检测是否依赖 jfinal-wexin 并自动配置 JbootAccessTokenCache +优化:删除 JacksonSerializer 等无用的代码文件 +优化:优化配置文件的的自动探测功能,防止在某些新手未编译直接运行找不到配置文件的问题 +优化:分布式任务注解 @EnableDistributedRunnable,并添加自定义的 redisKey 和 key 持有时间配置功能。 +文档:完善关于 Controller 和 数据库的相关文档 + + + jboot v3.13.8: 修复:Junit 代码覆盖率测试可能出现多次启动的问题 修复:JbootCron4jPlugin 停止后未移除已经停止任务的问题 diff --git a/pom.xml b/pom.xml index f0d80c2c..b1fe0d28 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.0-SNAPSHOT + 3.14.0 jar jboot -- Gitee From 44668e86be77fe140b07564e2691cfff3c30ebb8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 27 Feb 2022 16:59:00 +0800 Subject: [PATCH 1594/1965] v3.14.0 release (^.^)YYa!! --- simples/websocket/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 128a84ee..ab9dca9f 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.14.0-SNAPSHOT + 3.14.0 -- Gitee From e1425f79e54286369a47a2307802a50a48d53386 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 1 Mar 2022 12:34:28 +0800 Subject: [PATCH 1595/1965] v3.14.1 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b1fe0d28..9a8fcc95 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.0 + 3.14.1-SNAPSHOT jar jboot -- Gitee From abd10bf5479d7dc1e1a097d397b446c2c90315b1 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 1 Mar 2022 12:34:41 +0800 Subject: [PATCH 1596/1965] v3.14.1 snapshot --- .../java/io/jboot/web/json/JbootJson.java | 8 +++--- .../io/jboot/web/json/JbootJsonConfig.java | 14 ++++++++++ .../java/io/jboot/test/db/model/User.java | 5 ++++ .../jboot/test/json/JsonRenderController.java | 26 +++++++++++++++++++ 4 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 src/test/java/io/jboot/test/json/JsonRenderController.java diff --git a/src/main/java/io/jboot/web/json/JbootJson.java b/src/main/java/io/jboot/web/json/JbootJson.java index 421ef9c7..a9284bab 100644 --- a/src/main/java/io/jboot/web/json/JbootJson.java +++ b/src/main/java/io/jboot/web/json/JbootJson.java @@ -114,7 +114,7 @@ public class JbootJson extends JFinalJson { String originalField = wrapper.originalFields.get(i); toMap.remove(originalField); - Object value = invokeMethod(wrapper.methods.get(i), bean); + Object value = invokeGetterMethod(wrapper.getterMethods.get(i), bean); String field = wrapper.fields.get(i); toMap.put(field, value); } @@ -125,7 +125,7 @@ public class JbootJson extends JFinalJson { protected void optimizeMapAttrs(Map map) { } - protected Object invokeMethod(Method method, Object bean) { + protected Object invokeGetterMethod(Method method, Object bean) { try { return method.invoke(bean); } catch (Exception ex) { @@ -147,7 +147,7 @@ public class JbootJson extends JFinalJson { private static boolean hasFastJson = ClassUtil.hasClass("com.alibaba.fastjson.JSON"); private List fields = new LinkedList<>(); - private List methods = new LinkedList<>(); + private List getterMethods = new LinkedList<>(); private List originalFields = new LinkedList<>(); //需要忽略的字段 @@ -173,7 +173,7 @@ public class JbootJson extends JFinalJson { } else { originalFields.add(attrName); fields.add(getDefineName(method, attrName)); - methods.add(method); + getterMethods.add(method); } } diff --git a/src/main/java/io/jboot/web/json/JbootJsonConfig.java b/src/main/java/io/jboot/web/json/JbootJsonConfig.java index 004b814b..738c10c5 100644 --- a/src/main/java/io/jboot/web/json/JbootJsonConfig.java +++ b/src/main/java/io/jboot/web/json/JbootJsonConfig.java @@ -24,12 +24,26 @@ import io.jboot.app.config.annotation.ConfigModel; @ConfigModel(prefix = "jboot.json") public class JbootJsonConfig { + //model 和 record 是否自动转为驼峰格式 + //当配置此项为 false 的时候,需要配置 skipBeanGetters 为 true,否则出现个别相同数据库字段输出两次的情况 private boolean camelCaseJsonStyleEnable = true; + + //是否把所有的信息都转为驼峰格式,包括 map private boolean camelCaseToLowerCaseAnyway = false; + + //是否跳过 null 值 private boolean skipNullValueField = true; + + //是否跳过 model attrs,而只有使用 getter 来生成 private boolean skipModelAttrs = false; + + //是否跳过 getter 方法 private boolean skipBeanGetters = false; + + //时间格式化 private String timestampPattern; + + //转换深度,防止且套引用导致死循环转换 private int depth = 16; public boolean isCamelCaseJsonStyleEnable() { diff --git a/src/test/java/io/jboot/test/db/model/User.java b/src/test/java/io/jboot/test/db/model/User.java index ed84ae7e..b8eb16c3 100644 --- a/src/test/java/io/jboot/test/db/model/User.java +++ b/src/test/java/io/jboot/test/db/model/User.java @@ -18,4 +18,9 @@ public class User extends JbootModel { public String getId(){ return "111"; } + + + public Integer getUserId(){ + return (Integer) get("user_id"); + } } diff --git a/src/test/java/io/jboot/test/json/JsonRenderController.java b/src/test/java/io/jboot/test/json/JsonRenderController.java new file mode 100644 index 00000000..7515ad8d --- /dev/null +++ b/src/test/java/io/jboot/test/json/JsonRenderController.java @@ -0,0 +1,26 @@ +package io.jboot.test.json; + +import com.jfinal.core.Controller; +import io.jboot.app.JbootApplication; +import io.jboot.test.db.model.User; +import io.jboot.web.controller.annotation.RequestMapping; + +@RequestMapping("/json") +public class JsonRenderController extends Controller { + + public static void main(String[] args) { + JbootApplication.setBootArg("jboot.json.camelCaseJsonStyleEnable", "false"); + JbootApplication.setBootArg("jboot.json.skipBeanGetters", "true"); + JbootApplication.run(args); + } + + public Object test(){ + + User user = new User(); + user.put("user_id",1); + user.put("type","aaa"); + user.put("type_id",100); + + return user; + } +} -- Gitee From 2195290d366c72bc67c133b1eb631dfdd4fffd60 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 1 Mar 2022 12:35:05 +0800 Subject: [PATCH 1597/1965] v3.14.1 snapshot --- .../java/io/jboot/app/config/JbootConfigManager.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index cf48f80f..0e10c0ad 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -464,7 +464,14 @@ public class JbootConfigManager { if (argMap == null) { argMap = new HashMap<>(); } - argMap.put(key, value.toString()); + if (key == null) { + return; + } + if (value == null || value.toString().trim().length() == 0) { + argMap.remove(key.trim()); + } else { + argMap.put(key.trim(), value.toString().trim()); + } } public static void setBootProperties(Properties properties) { -- Gitee From 3812245fe42a3762fd343390afb085855b86b202 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 2 Mar 2022 09:26:53 +0800 Subject: [PATCH 1598/1965] fixed @Cacheput() annotation for controller --- .../components/cache/interceptor/CachePutInterceptor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java index 8058889c..2bd37ce5 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java @@ -34,18 +34,19 @@ public class CachePutInterceptor implements Interceptor { @Override public void intercept(Invocation inv) { - //先执行,之后再保存数据 - inv.invoke(); Method method = inv.getMethod(); CachePut cachePut = method.getAnnotation(CachePut.class); if (cachePut == null) { + inv.invoke(); return; } if (inv.isActionInvocation()) { forController(inv, method, cachePut); + inv.invoke(); } else { + inv.invoke(); forService(inv, method, cachePut); } } @@ -76,7 +77,6 @@ public class CachePutInterceptor implements Interceptor { } - private void forService(Invocation inv, Method method, CachePut cachePut) { Object data = inv.getReturnValue(); -- Gitee From eb107b15b4a686b44c271d75845f81efaab66b6e Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Wed, 2 Mar 2022 14:42:40 +0800 Subject: [PATCH 1599/1965] fixed JbootProp.java --- .../io/jboot/app/config/JbootConfigKit.java | 6 --- .../java/io/jboot/app/config/JbootProp.java | 37 +++++++++++++++++-- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigKit.java b/src/main/java/io/jboot/app/config/JbootConfigKit.java index f2c08628..9a2b7d7a 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigKit.java +++ b/src/main/java/io/jboot/app/config/JbootConfigKit.java @@ -169,12 +169,6 @@ public class JbootConfigKit { } - public static ClassLoader getClassLoader() { - ClassLoader ret = Thread.currentThread().getContextClassLoader(); - return ret != null ? ret : JbootConfigKit.class.getClassLoader(); - } - - public static Object convert(Class convertClass, String s, Type genericType) { if (convertClass == String.class || s == null || convertClass == Object.class) { diff --git a/src/main/java/io/jboot/app/config/JbootProp.java b/src/main/java/io/jboot/app/config/JbootProp.java index dc530ed4..499fdf31 100644 --- a/src/main/java/io/jboot/app/config/JbootProp.java +++ b/src/main/java/io/jboot/app/config/JbootProp.java @@ -15,6 +15,8 @@ */ package io.jboot.app.config; +import com.jfinal.kit.LogKit; + import java.io.*; import java.net.URL; import java.util.Properties; @@ -32,7 +34,11 @@ class JbootProp { properties = new Properties(); InputStream inputStream = null; try { - inputStream = JbootConfigKit.getClassLoader().getResourceAsStream(fileName); + inputStream = getResourceAsStreamByCurrentThread(fileName); + + if (inputStream == null) { + inputStream = getResourceAsStreamByClassloader(fileName); + } // 当系统未编译的时候,开发环境下的 resources 目录下的 jboot.properties 文件不会自动被 copy 到 target/classes 目录下 // 此时,需要主动去探测 resources 目录的文件 @@ -40,12 +46,23 @@ class JbootProp { URL resourceURL = JbootProp.class.getResource("/"); if (resourceURL != null) { String classPath = resourceURL.toURI().getPath(); + if (removeSlashEnd(classPath).endsWith("classes")) { - File resourcesDir = new File(classPath, "../../src/main/resources"); - File propFile = new File(resourcesDir, fileName); + + //from classes path + File propFile = new File(classPath, fileName); if (propFile.exists() && propFile.isFile()) { inputStream = new FileInputStream(propFile); } + + //from resources path + else { + File resourcesDir = new File(classPath, "../../src/main/resources"); + propFile = new File(resourcesDir, fileName); + if (propFile.exists() && propFile.isFile()) { + inputStream = new FileInputStream(propFile); + } + } } } } @@ -62,14 +79,26 @@ class JbootProp { try { inputStream.close(); } catch (IOException e) { + LogKit.logNothing(e); } } } } + private InputStream getResourceAsStreamByCurrentThread(String fileName) { + ClassLoader ret = Thread.currentThread().getContextClassLoader(); + return ret != null ? ret.getResourceAsStream(fileName) : null; + } + + + private InputStream getResourceAsStreamByClassloader(String fileName) { + ClassLoader ret = JbootProp.class.getClassLoader(); + return ret != null ? ret.getResourceAsStream(fileName) : null; + } + private static String removeSlashEnd(String path) { - if (path != null && path.endsWith(File.separator)) { + if (path != null && (path.endsWith("/") || path.endsWith("\\"))) { return path.substring(0, path.length() - 1); } else { return path; -- Gitee From 83001a836c6fdb55b26f6c4093ec2bda4fdbfde0 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 3 Mar 2022 16:05:41 +0800 Subject: [PATCH 1600/1965] remove mockHandler --- .../io/jboot/test/web/MockActionHandler.java | 49 ------------------- .../io/jboot/test/web/MockCoreConfig.java | 32 ------------ .../io/jboot/test/web/MockFilterConfig.java | 5 +- 3 files changed, 3 insertions(+), 83 deletions(-) delete mode 100644 src/main/java/io/jboot/test/web/MockActionHandler.java delete mode 100644 src/main/java/io/jboot/test/web/MockCoreConfig.java diff --git a/src/main/java/io/jboot/test/web/MockActionHandler.java b/src/main/java/io/jboot/test/web/MockActionHandler.java deleted file mode 100644 index 83cda363..00000000 --- a/src/main/java/io/jboot/test/web/MockActionHandler.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.test.web; - -import com.jfinal.core.Action; -import com.jfinal.core.ActionException; -import io.jboot.utils.StrUtil; -import io.jboot.web.handler.JbootActionHandler; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -public class MockActionHandler extends JbootActionHandler { - - @Override - protected void handleActionException(String target, HttpServletRequest request, HttpServletResponse response, Action action, ActionException e) { - int errorCode = e.getErrorCode(); - String msg = null; - if (errorCode == 404) { - msg = "404 Not Found."; - } else if (errorCode == 400) { - msg = "400 Bad Request. "; - } else if (errorCode == 401) { - msg = "401 Unauthorized. "; - } else if (errorCode == 403) { - msg = "403 Forbidden. "; - } - throw new AssertionError(msg, e); - } - - - @Override - protected void handleException(String target, HttpServletRequest request, HttpServletResponse response, Action action, Exception e) { - throw new AssertionError("500 error: " + (StrUtil.isNotBlank(e.getMessage()) ? e.getMessage() : e.getClass().getSimpleName()), e); - } -} diff --git a/src/main/java/io/jboot/test/web/MockCoreConfig.java b/src/main/java/io/jboot/test/web/MockCoreConfig.java deleted file mode 100644 index 35890a84..00000000 --- a/src/main/java/io/jboot/test/web/MockCoreConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.test.web; - -import com.jfinal.config.Handlers; -import io.jboot.core.JbootCoreConfig; -import io.jboot.web.handler.JbootActionHandler; - -public class MockCoreConfig extends JbootCoreConfig { - - @Override - public void configHandler(Handlers handlers) { - super.configHandler(handlers); - - if (handlers.getActionHandler().getClass() == JbootActionHandler.class){ - handlers.setActionHandler(new MockActionHandler()); - } - } -} diff --git a/src/main/java/io/jboot/test/web/MockFilterConfig.java b/src/main/java/io/jboot/test/web/MockFilterConfig.java index fd3e3b73..a9da3655 100644 --- a/src/main/java/io/jboot/test/web/MockFilterConfig.java +++ b/src/main/java/io/jboot/test/web/MockFilterConfig.java @@ -15,6 +15,8 @@ */ package io.jboot.test.web; +import io.jboot.core.JbootCoreConfig; + import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import java.util.Collections; @@ -28,10 +30,9 @@ public class MockFilterConfig implements FilterConfig { protected ServletContext servletContext = MockServletContext.DEFAULT; public MockFilterConfig() { - initParameters.put("configClass", MockCoreConfig.class.getName()); + initParameters.put("configClass", JbootCoreConfig.class.getName()); } - @Override public String getFilterName() { return "mockFilter"; -- Gitee From b9cd6e1a46ccd5a68a8237eff61d58da92a5fd8a Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 3 Mar 2022 17:11:54 +0800 Subject: [PATCH 1601/1965] optimize DAO.findFirstByColumn() --- src/main/java/io/jboot/db/model/Column.java | 9 +++++++++ src/main/java/io/jboot/db/model/JbootModel.java | 6 ++++++ src/main/java/io/jboot/db/model/Util.java | 4 ++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Column.java b/src/main/java/io/jboot/db/model/Column.java index 1ea16d7b..a0a24216 100644 --- a/src/main/java/io/jboot/db/model/Column.java +++ b/src/main/java/io/jboot/db/model/Column.java @@ -16,6 +16,8 @@ package io.jboot.db.model; +import io.jboot.utils.StrUtil; + import java.io.Serializable; public class Column implements Serializable { @@ -96,4 +98,11 @@ public class Column implements Serializable { && !LOGIC_IS_NOT_NULL.equals(getLogic()); } + public boolean checkAvailable() { + if (StrUtil.isBlank(getName())) { + return false; + } + return !hasPara() || getValue() != null; + } + } diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 587b85a7..75022ab1 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -523,11 +523,17 @@ public class JbootModel> extends Model { } public M findFirstByColumn(Column column) { + if (column == null || !column.checkAvailable()) { + throw new IllegalArgumentException("Column or value must not be null."); + } return findFirstByColumns(Columns.create(column)); } public M findFirstByColumn(Column column, String orderBy) { + if (column == null || !column.checkAvailable()) { + throw new IllegalArgumentException("Column or value must not be null."); + } return findFirstByColumns(Columns.create(column), orderBy); } diff --git a/src/main/java/io/jboot/db/model/Util.java b/src/main/java/io/jboot/db/model/Util.java index b16cd3ec..5bd120a2 100644 --- a/src/main/java/io/jboot/db/model/Util.java +++ b/src/main/java/io/jboot/db/model/Util.java @@ -155,7 +155,7 @@ class Util { if (columns.isUseSafeMode()) { for (Object obj : paras) { if (obj == null) { - throw new NullPointerException("column \"" + name + "\" para is null, Columns must has not null para value in safeMode."); + throw new NullPointerException("Column \"" + name + "\" para is null, Columns must has not null para value in safeMode."); } } } @@ -171,7 +171,7 @@ class Util { } } - static void checkNullParas(Columns columns, Collection collection) { + static void checkNullParas(Columns columns, Collection collection) { if (columns.isUseSafeMode()) { if (CollectionUtil.isEmpty(collection)) { throw new NullPointerException("Columns must has not empty collection in safeMode."); -- Gitee From b4ded113d0e2498bdac9a15665738e0e6c2964ec Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 3 Mar 2022 17:17:40 +0800 Subject: [PATCH 1602/1965] set rabbitmq.queueDeclareDurable defalut false --- .../io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java index cf7e040f..488f2c07 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java @@ -42,7 +42,7 @@ public class JbootRabbitmqConfig { //若配置为 false,则需要在 OnMessage 里,调用 MessageContext.getChannel().baseAck(或者baseNack)进行消费(或者标识消费失败) private boolean autoAck = true; - private boolean queueDeclareDurable = true; + private boolean queueDeclareDurable = false; private boolean queueDeclareExclusive = false; private boolean queueDeclareAutoDelete = false; -- Gitee From 9df352f9f0c4f46e738909ac78af59d7a537b965 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 4 Mar 2022 20:01:56 +0800 Subject: [PATCH 1603/1965] optimize method name --- .../io/jboot/components/gateway/GatewayHttpProxy.java | 4 +++- .../io/jboot/components/http/jboot/JbootHttpImpl.java | 2 +- .../components/schedule/JbootDistributedRunnable.java | 2 +- .../java/io/jboot/support/redis/JbootRedisLock.java | 2 +- .../support/redis/jedis/JbootJedisClusterImpl.java | 4 ++-- .../io/jboot/support/redis/jedis/JbootJedisImpl.java | 4 ++-- .../support/sentinel/datasource/FileDataSource.java | 2 +- src/main/java/io/jboot/utils/DateUtil.java | 10 +++++----- src/main/java/io/jboot/utils/FileUtil.java | 2 +- src/main/java/io/jboot/utils/QuietlyUtil.java | 4 ++-- 10 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java index 7b959cdc..c1e1d54b 100644 --- a/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java +++ b/src/main/java/io/jboot/components/gateway/GatewayHttpProxy.java @@ -15,6 +15,7 @@ */ package io.jboot.components.gateway; +import com.jfinal.kit.LogKit; import com.jfinal.log.Log; import io.jboot.exception.JbootException; import io.jboot.utils.StrUtil; @@ -160,12 +161,13 @@ public class GatewayHttpProxy { } - protected static void quetlyClose(Closeable... closeables) { + protected void quetlyClose(Closeable... closeables) { for (Closeable closeable : closeables) { if (closeable != null) { try { closeable.close(); } catch (IOException e) { + LogKit.logNothing(e); } } } diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index 57c53790..c1eda6fa 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -116,7 +116,7 @@ public class JbootHttpImpl implements JbootHttp { connection.disconnect(); } - QuietlyUtil.quietlyClose(inStream, response); + QuietlyUtil.closeQuietly(inStream, response); } } diff --git a/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java b/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java index 302512d6..d5d7a756 100644 --- a/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java +++ b/src/main/java/io/jboot/components/schedule/JbootDistributedRunnable.java @@ -217,7 +217,7 @@ public class JbootDistributedRunnable implements Runnable { millis = 1000; } - QuietlyUtil.quietlySleep(millis); + QuietlyUtil.sleepQuietly(millis); } diff --git a/src/main/java/io/jboot/support/redis/JbootRedisLock.java b/src/main/java/io/jboot/support/redis/JbootRedisLock.java index 293376b7..0641aa0a 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedisLock.java +++ b/src/main/java/io/jboot/support/redis/JbootRedisLock.java @@ -159,7 +159,7 @@ public class JbootRedisLock { } finally { if (timeout > 0) { timeout -= 100; - QuietlyUtil.quietlySleep(100); + QuietlyUtil.sleepQuietly(100); } } } while (timeout > 0); diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java index b3e1e38b..dd6386f1 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java @@ -1183,7 +1183,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { break; } catch (JedisConnectionException e) { LOG.error("failed connect to redis, reconnect it.", e); - QuietlyUtil.quietlySleep(1000); + QuietlyUtil.sleepQuietly(1000); } } } @@ -1215,7 +1215,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { break; } catch (Throwable e) { LOG.error("failed connect to redis, reconnect it.", e); - QuietlyUtil.quietlySleep(1000); + QuietlyUtil.sleepQuietly(1000); } } } diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java index 8c3dbfab..2ff9631d 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java @@ -1496,7 +1496,7 @@ public class JbootJedisImpl extends JbootRedisBase { break; } catch (JedisConnectionException e) { LOG.error("Failed connect to redis, reconnect it.", e); - QuietlyUtil.quietlySleep(1000); + QuietlyUtil.sleepQuietly(1000); } finally { returnResource(jedis); } @@ -1535,7 +1535,7 @@ public class JbootJedisImpl extends JbootRedisBase { break; } catch (Throwable e) { LOG.error("Failed connect to redis, reconnect it.", e); - QuietlyUtil.quietlySleep(1000); + QuietlyUtil.sleepQuietly(1000); } finally { if (jedis != null) { returnResource(jedis); diff --git a/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java b/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java index fb928bb8..3139e784 100644 --- a/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java +++ b/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java @@ -51,7 +51,7 @@ public class FileDataSource extends AbstractDataSource { } catch (Exception ex) { LogKit.error(ex.toString(), ex); } - QuietlyUtil.quietlySleep(5000); + QuietlyUtil.sleepQuietly(5000); } }, "jboot-sentinel-file-reader").start(); } diff --git a/src/main/java/io/jboot/utils/DateUtil.java b/src/main/java/io/jboot/utils/DateUtil.java index fc365314..9477e9e6 100644 --- a/src/main/java/io/jboot/utils/DateUtil.java +++ b/src/main/java/io/jboot/utils/DateUtil.java @@ -453,13 +453,13 @@ public class DateUtil { public static Date getStartOfThisQuarter() { Calendar cal = Calendar.getInstance(); int currentMonth = cal.get(Calendar.MONTH) + 1; - if (currentMonth >= 1 && currentMonth <= 3) { + if (currentMonth <= 3) { cal.set(Calendar.MONTH, 0); - } else if (currentMonth >= 4 && currentMonth <= 6) { + } else if (currentMonth <= 6) { cal.set(Calendar.MONTH, 3); - } else if (currentMonth >= 7 && currentMonth <= 9) { + } else if (currentMonth <= 9) { cal.set(Calendar.MONTH, 6); - } else if (currentMonth >= 10 && currentMonth <= 12) { + } else if (currentMonth <= 12) { cal.set(Calendar.MONTH, 9); } cal.set(Calendar.DATE, 0); @@ -494,7 +494,7 @@ public class DateUtil { */ public static Date getStartOfQuarter(int quarterNumber) { if (quarterNumber < 1 || quarterNumber > 4) { - throw new IllegalArgumentException("quarterNumber must 1,2,3,4"); + throw new IllegalArgumentException("quarterNumber must equals 1,2,3,4"); } Calendar cal = Calendar.getInstance(); if (quarterNumber == 1) { diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index ceaec482..0b0b7538 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -82,7 +82,7 @@ public class FileUtil { } public static void close(Closeable... closeable) { - QuietlyUtil.quietlyClose(closeable); + QuietlyUtil.closeQuietly(closeable); } diff --git a/src/main/java/io/jboot/utils/QuietlyUtil.java b/src/main/java/io/jboot/utils/QuietlyUtil.java index aa37f67f..268d6963 100644 --- a/src/main/java/io/jboot/utils/QuietlyUtil.java +++ b/src/main/java/io/jboot/utils/QuietlyUtil.java @@ -22,7 +22,7 @@ import java.io.IOException; public class QuietlyUtil { - public static void quietlyClose(Closeable... closeables) { + public static void closeQuietly(Closeable... closeables) { if (closeables != null) { for (Closeable closeable : closeables) { if (closeable != null) { @@ -36,7 +36,7 @@ public class QuietlyUtil { } } - public static void quietlySleep(long millis) { + public static void sleepQuietly(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { -- Gitee From 6ce670ca9116d45b9e9048b16f3f65ff98866ddc Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 4 Mar 2022 20:03:07 +0800 Subject: [PATCH 1604/1965] feature: use @Table(datasource="xxx") config datasource --- src/main/java/io/jboot/db/ArpManager.java | 53 +++++++------ src/main/java/io/jboot/db/TableInfo.java | 78 +++++++++++++++++-- .../java/io/jboot/db/TableInfoManager.java | 44 ++++++----- .../java/io/jboot/db/annotation/Table.java | 2 + .../jboot/db/datasource/DataSourceConfig.java | 35 +++++++++ 5 files changed, 164 insertions(+), 48 deletions(-) diff --git a/src/main/java/io/jboot/db/ArpManager.java b/src/main/java/io/jboot/db/ArpManager.java index b1f9c9c1..2be0b2d5 100644 --- a/src/main/java/io/jboot/db/ArpManager.java +++ b/src/main/java/io/jboot/db/ArpManager.java @@ -15,7 +15,10 @@ */ package io.jboot.db; -import com.jfinal.plugin.activerecord.*; +import com.jfinal.plugin.activerecord.ActiveRecordPlugin; +import com.jfinal.plugin.activerecord.CaseInsensitiveContainerFactory; +import com.jfinal.plugin.activerecord.IDbProFactory; +import com.jfinal.plugin.activerecord.Model; import com.jfinal.plugin.activerecord.dialect.Dialect; import io.jboot.Jboot; import io.jboot.components.cache.JbootCache; @@ -27,7 +30,6 @@ import io.jboot.db.dialect.*; import io.jboot.db.record.JbootRecordBuilder; import io.jboot.exception.JbootException; import io.jboot.exception.JbootIllegalConfigException; -import io.jboot.utils.ArrayUtil; import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; @@ -79,13 +81,38 @@ public class ArpManager { } private void createRecordPlugin(Map mergeDatasourceConfigs) { + Map dataSourceConfigMap = new HashMap<>(); + for (Map.Entry entry : mergeDatasourceConfigs.entrySet()) { DataSourceConfig datasourceConfig = entry.getValue(); if (datasourceConfig.isConfigOk()) { ActiveRecordPlugin activeRecordPlugin = createRecordPlugin(datasourceConfig); + + dataSourceConfigMap.put(System.identityHashCode(activeRecordPlugin), datasourceConfig); activeRecordPlugins.add(activeRecordPlugin); } } + + + for (ActiveRecordPlugin activeRecordPlugin : activeRecordPlugins) { + DataSourceConfig dataSourceConfig = dataSourceConfigMap.get(System.identityHashCode(activeRecordPlugin)); + + //获得该数据源匹配的表 + List tableInfos = dataSourceConfig.getTableInfos(); + + if (tableInfos != null) { + for (TableInfo table : tableInfos) { + String tableName = StrUtil.isNotBlank(dataSourceConfig.getTablePrefix()) ? dataSourceConfig.getTablePrefix() + table.getTableName() : table.getTableName(); + if (StrUtil.isNotBlank(table.getPrimaryKey())) { + activeRecordPlugin.addMapping(tableName, table.getPrimaryKey(), (Class>) table.getModelClass()); + } else { + activeRecordPlugin.addMapping(tableName, (Class>) table.getModelClass()); + } + } + } + } + + } @@ -99,7 +126,6 @@ public class ArpManager { ActiveRecordPlugin activeRecordPlugin = newRecordPlugin(config); - if (StrUtil.isNotBlank(config.getDbProFactory())) { IDbProFactory dbProFactory = Objects.requireNonNull(ClassUtil.newInstance(config.getDbProFactory()), "can not create dbProfactory by class : " + config.getDbProFactory()); @@ -131,30 +157,13 @@ public class ArpManager { activeRecordPlugin.getConfig().getDialect().setRecordBuilder(new JbootRecordBuilder()); /** - * 不需要添加映射的直接返回 - * * 在一个表有多个数据源的情况下,应该只需要添加一个映射就可以了 * 添加映射:默认为该 model 的数据源 * 不添加映射:通过 model.use("xxx").save() 这种方式去调用该数据源 * 不添加映射使用从场景一般是:读写分离时,用于读取只读数据库的数据 */ - if (!config.isNeedAddMapping()) { - return activeRecordPlugin; - } - - //获得该数据源匹配的表 - List tableInfos = TableInfoManager.me().getMatchTablesInfos(config); - if (ArrayUtil.isNullOrEmpty(tableInfos)) { - return activeRecordPlugin; - } - - for (TableInfo table : tableInfos) { - String tableName = StrUtil.isNotBlank(config.getTablePrefix()) ? config.getTablePrefix() + table.getTableName() : table.getTableName(); - if (StrUtil.isNotBlank(table.getPrimaryKey())) { - activeRecordPlugin.addMapping(tableName, table.getPrimaryKey(), (Class>) table.getModelClass()); - } else { - activeRecordPlugin.addMapping(tableName, (Class>) table.getModelClass()); - } + if (config.isNeedAddMapping()) { + TableInfoManager.me().initConfigMappingTables(config); } return activeRecordPlugin; diff --git a/src/main/java/io/jboot/db/TableInfo.java b/src/main/java/io/jboot/db/TableInfo.java index 0b74ab4c..47673852 100644 --- a/src/main/java/io/jboot/db/TableInfo.java +++ b/src/main/java/io/jboot/db/TableInfo.java @@ -16,6 +16,13 @@ package io.jboot.db; import com.jfinal.plugin.activerecord.Model; +import io.jboot.db.datasource.DataSourceConfig; +import io.jboot.utils.StrUtil; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) @@ -26,7 +33,10 @@ public class TableInfo { private String tableName; private String primaryKey; private Class modelClass; - private String datasources; + private String datasource; + private Set datasourceNames; + + private List attachedDatasources; public String getTableName() { @@ -53,11 +63,69 @@ public class TableInfo { this.modelClass = modelClass; } - public String getDatasources() { - return datasources; + public String getDatasource() { + return datasource; + } + + public void setDatasource(String datasource) { + this.datasource = datasource; + } + + public Set getDatasourceNames() { + if (datasourceNames == null) { + datasourceNames = StrUtil.isNotBlank(datasource) + ? StrUtil.splitToSetByComma(datasource) + : new HashSet<>(); + } + return datasourceNames; + } + + + /** + * 添加这个表存绑定的数据源 + * + * @param dataSourceConfig + * @param fromDesignated 是否是通过 jboot.datasource.table 或者 @table(datasource="xxx") 来指定的 + */ + public boolean addAttachedDatasource(DataSourceConfig dataSourceConfig, boolean fromDesignated) { + if (this.attachedDatasources == null) { + this.attachedDatasources = new ArrayList<>(); + } + + //未指定数据源 + if (!fromDesignated) { + for (DataSourceConfigWrapper dataSourceConfigWrapper : this.attachedDatasources) { + //若已经存在了指定的数据源,再无法添加未指定的数据源 + if (dataSourceConfigWrapper.fromDesignated) { + return false; + } + } + } + + this.attachedDatasources.add(new DataSourceConfigWrapper(dataSourceConfig, fromDesignated)); + + // 通过配置指定的,那么需要移除哪些未指定的默认数据源 + if (fromDesignated) { + for (DataSourceConfigWrapper dataSourceConfigWrapper : attachedDatasources) { + if (!dataSourceConfigWrapper.fromDesignated) { + dataSourceConfigWrapper.dataSourceConfig.removeTableInfo(this); + } + } + attachedDatasources.removeIf(dataSourceConfigWrapper -> !dataSourceConfigWrapper.fromDesignated); + } + + return true; } - public void setDatasources(String datasources) { - this.datasources = datasources; + + public static class DataSourceConfigWrapper { + private final DataSourceConfig dataSourceConfig; + private final boolean fromDesignated; + + public DataSourceConfigWrapper(DataSourceConfig dataSourceConfig, boolean fromDesignated) { + this.dataSourceConfig = dataSourceConfig; + this.fromDesignated = fromDesignated; + } } + } diff --git a/src/main/java/io/jboot/db/TableInfoManager.java b/src/main/java/io/jboot/db/TableInfoManager.java index 8439a2ce..7a7bbaad 100644 --- a/src/main/java/io/jboot/db/TableInfoManager.java +++ b/src/main/java/io/jboot/db/TableInfoManager.java @@ -24,9 +24,7 @@ import io.jboot.utils.ArrayUtil; import io.jboot.utils.ClassScanner; import io.jboot.utils.StrUtil; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; +import java.util.*; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) @@ -45,46 +43,49 @@ public class TableInfoManager { /** - * 获取 某数据源 下匹配的表 + * 初始化该数据下的 tableInfos 对象,其用来存储该数据源下有哪些表 * * @param dataSourceConfig - * @return 该数据源下所有的表 */ - public List getMatchTablesInfos(DataSourceConfig dataSourceConfig) { + public void initConfigMappingTables(DataSourceConfig dataSourceConfig) { + // 该数据源下配置的所有表 Set configTables = StrUtil.isNotBlank(dataSourceConfig.getTable()) - ? StrUtil.splitToSet(dataSourceConfig.getTable(), ",") + ? StrUtil.splitToSetByComma(dataSourceConfig.getTable()) : null; + // 该数据源下排除的所有表 Set configExTables = StrUtil.isNotBlank(dataSourceConfig.getExTable()) - ? StrUtil.splitToSet(dataSourceConfig.getExTable(), ",") + ? StrUtil.splitToSetByComma(dataSourceConfig.getExTable()) : null; - List matchList = new ArrayList<>(); + //所有的表信息 + List allTableInfos = getAllTableInfos(); - for (TableInfo tableInfo : getAllTableInfos()) { - //说明该表已经被指定到 datasource 了 - if (tableInfo.getDatasources() != null) { + for (TableInfo tableInfo : allTableInfos) { + + // 排除配置 jboot.datasource.extable 包含了这个表 + if (configExTables != null && configExTables.contains(tableInfo.getTableName())) { continue; } - // 如果 datasource.table 已经配置了, - // 就只用这个配置的,不是这个配置的都排除 - if (configTables != null && !configTables.contains(tableInfo.getTableName())) { - continue; + if (configTables != null && configTables.contains(tableInfo.getTableName())) { + dataSourceConfig.addTableInfo(tableInfo, true); } - //被指定排除的表进行排除了 - if (configExTables != null && configExTables.contains(tableInfo.getTableName())) { + if (tableInfo.getDatasourceNames().contains(dataSourceConfig.getName())) { + dataSourceConfig.addTableInfo(tableInfo, true); + } + + // 注解 @Table 指定了数据源,而且当前数据源未匹配 + if (!tableInfo.getDatasourceNames().isEmpty()) { continue; } - tableInfo.setDatasources(dataSourceConfig.getName()); - matchList.add(tableInfo); + dataSourceConfig.addTableInfo(tableInfo, false); } - return matchList; } private List getAllTableInfos() { @@ -133,6 +134,7 @@ public class TableInfoManager { tableInfo.setModelClass(clazz); tableInfo.setPrimaryKey(AnnotationUtil.get(tb.primaryKey())); tableInfo.setTableName(AnnotationUtil.get(tb.tableName())); + tableInfo.setDatasource(AnnotationUtil.get(tb.datasource())); tableInfoList.add(tableInfo); } diff --git a/src/main/java/io/jboot/db/annotation/Table.java b/src/main/java/io/jboot/db/annotation/Table.java index e4119876..85932d66 100644 --- a/src/main/java/io/jboot/db/annotation/Table.java +++ b/src/main/java/io/jboot/db/annotation/Table.java @@ -26,4 +26,6 @@ public @interface Table { String primaryKey() default ""; + String datasource() default ""; + } \ No newline at end of file diff --git a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java index 15cd8b9e..701b3d76 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java @@ -16,9 +16,13 @@ package io.jboot.db.datasource; import com.jfinal.plugin.activerecord.DbKit; +import io.jboot.db.TableInfo; import io.jboot.db.driver.DriverClassNames; import io.jboot.utils.StrUtil; +import java.util.ArrayList; +import java.util.List; + public class DataSourceConfig { public static final String NAME_DEFAULT = DbKit.MAIN_CONFIG_NAME; @@ -88,6 +92,7 @@ public class DataSourceConfig { */ private boolean needAddMapping = true; + public String getName() { return name; } @@ -419,4 +424,34 @@ public class DataSourceConfig { public void setTestOnReturn(boolean testOnReturn) { this.testOnReturn = testOnReturn; } + + + private List tableInfos; + + /** + * 添加表信息 + * + * @param tableInfo 表信息 + * @param fromDesignated 是否是通过 jboot.datasource.table 或者 @table(datasource="xxx") 来指定的 + */ + public void addTableInfo(TableInfo tableInfo, boolean fromDesignated) { + if (tableInfos == null) { + tableInfos = new ArrayList<>(); + } + + if (!tableInfos.contains(tableInfo) && tableInfo.addAttachedDatasource(this, fromDesignated)) { + tableInfos.add(tableInfo); + } + } + + public void removeTableInfo(TableInfo tableInfo) { + if (tableInfos != null) { + tableInfos.remove(tableInfo); + } + } + + + public List getTableInfos() { + return tableInfos; + } } -- Gitee From 80ff3cc1d5b9b990d6cffb1938feafb8d732e3fd Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 4 Mar 2022 21:04:37 +0800 Subject: [PATCH 1605/1965] feature: use @Table(datasource="xxx") config datasource --- src/main/java/io/jboot/db/ArpManager.java | 41 +++++-------------- .../datasource/DataSourceConfigManager.java | 17 +++----- 2 files changed, 17 insertions(+), 41 deletions(-) diff --git a/src/main/java/io/jboot/db/ArpManager.java b/src/main/java/io/jboot/db/ArpManager.java index 2be0b2d5..6949e1ab 100644 --- a/src/main/java/io/jboot/db/ArpManager.java +++ b/src/main/java/io/jboot/db/ArpManager.java @@ -57,50 +57,32 @@ public class ArpManager { } private ArpManager() { - - Map allDatasourceConfigs = DataSourceConfigManager.me().getDatasourceConfigs(); - - // 包含了指定表配置的数据源 - Map hasTableDatasourceConfigs = new HashMap<>(); - - - Iterator> it = allDatasourceConfigs.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = it.next(); - if (StrUtil.isNotBlank(entry.getValue().getTable())) { - hasTableDatasourceConfigs.put(entry.getKey(), entry.getValue()); - it.remove(); - } - } - - // 优先创建有指定表的数据源的 activeRecordPlugin - // 表一旦附着到 activeRecordPlugin, 就不会被其他 activeRecordPlugin 包含了 - createRecordPlugin(hasTableDatasourceConfigs); - createRecordPlugin(allDatasourceConfigs); - + Map datasourceConfigs = DataSourceConfigManager.me().getDatasourceConfigs(); + createRecordPlugin(datasourceConfigs); } - private void createRecordPlugin(Map mergeDatasourceConfigs) { - Map dataSourceConfigMap = new HashMap<>(); + private void createRecordPlugin(Map allConfigs) { + + Map arpDatasourceConfigs = new HashMap<>(); - for (Map.Entry entry : mergeDatasourceConfigs.entrySet()) { + for (Map.Entry entry : allConfigs.entrySet()) { DataSourceConfig datasourceConfig = entry.getValue(); if (datasourceConfig.isConfigOk()) { ActiveRecordPlugin activeRecordPlugin = createRecordPlugin(datasourceConfig); - dataSourceConfigMap.put(System.identityHashCode(activeRecordPlugin), datasourceConfig); + arpDatasourceConfigs.put(System.identityHashCode(activeRecordPlugin), datasourceConfig); activeRecordPlugins.add(activeRecordPlugin); } } + // 添加 activeRecordPlugin 的表映射 for (ActiveRecordPlugin activeRecordPlugin : activeRecordPlugins) { - DataSourceConfig dataSourceConfig = dataSourceConfigMap.get(System.identityHashCode(activeRecordPlugin)); + DataSourceConfig dataSourceConfig = arpDatasourceConfigs.get(System.identityHashCode(activeRecordPlugin)); //获得该数据源匹配的表 List tableInfos = dataSourceConfig.getTableInfos(); - - if (tableInfos != null) { + if (tableInfos != null && !tableInfos.isEmpty()) { for (TableInfo table : tableInfos) { String tableName = StrUtil.isNotBlank(dataSourceConfig.getTablePrefix()) ? dataSourceConfig.getTablePrefix() + table.getTableName() : table.getTableName(); if (StrUtil.isNotBlank(table.getPrimaryKey())) { @@ -112,7 +94,6 @@ public class ArpManager { } } - } @@ -128,7 +109,7 @@ public class ArpManager { if (StrUtil.isNotBlank(config.getDbProFactory())) { IDbProFactory dbProFactory = Objects.requireNonNull(ClassUtil.newInstance(config.getDbProFactory()), - "can not create dbProfactory by class : " + config.getDbProFactory()); + "Can not create dbProfactory by class : " + config.getDbProFactory()); activeRecordPlugin.setDbProFactory(dbProFactory); } else { activeRecordPlugin.setDbProFactory(new JbootDbProFactory()); diff --git a/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java b/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java index 979dd8ca..9ee89910 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java @@ -15,36 +15,31 @@ */ package io.jboot.db.datasource; -import com.google.common.collect.Maps; import io.jboot.utils.ConfigUtil; import io.jboot.utils.StrUtil; +import java.util.HashMap; import java.util.Map; public class DataSourceConfigManager { - private static final String DATASOURCE_PREFIX = "jboot.datasource."; - - private static DataSourceConfigManager manager = new DataSourceConfigManager(); public static DataSourceConfigManager me() { return manager; } - private Map datasourceConfigs = Maps.newHashMap(); + private Map datasourceConfigs = new HashMap<>(); private DataSourceConfigManager() { - - Map configMap = ConfigUtil.getConfigModels(DataSourceConfig.class,"jboot.datasource"); - - for (Map.Entry entry : configMap.entrySet()){ + Map configMap = ConfigUtil.getConfigModels(DataSourceConfig.class, "jboot.datasource"); + for (Map.Entry entry : configMap.entrySet()) { DataSourceConfig config = entry.getValue(); //默认数据源 - if (entry.getKey().equals("default") && StrUtil.isBlank(config.getName())){ + if ("default".equals(entry.getKey()) && StrUtil.isBlank(config.getName())) { config.setName(DataSourceConfig.NAME_DEFAULT); - }else if (StrUtil.isBlank(config.getName())){ + } else if (StrUtil.isBlank(config.getName())) { config.setName(entry.getKey()); } -- Gitee From a49ee6983074c08d7c2ee94ecde65ca18e0353b8 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 6 Mar 2022 10:52:40 +0800 Subject: [PATCH 1606/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=20ValidErrorRender?= =?UTF-8?q?.java=EF=BC=8C=E8=BE=93=E5=87=BA=E7=9A=84=20message=20=E4=B8=8D?= =?UTF-8?q?=E5=B8=A6=E6=9C=89=20fieldName=20=E4=BF=A1=E6=81=AF=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/valid/ValidErrorRender.java | 7 ++++--- .../components/valid/ValidException.java | 21 ++++++++++++++++++- .../io/jboot/components/valid/ValidUtil.java | 6 +----- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/components/valid/ValidErrorRender.java b/src/main/java/io/jboot/components/valid/ValidErrorRender.java index eaaaa314..a21e4897 100644 --- a/src/main/java/io/jboot/components/valid/ValidErrorRender.java +++ b/src/main/java/io/jboot/components/valid/ValidErrorRender.java @@ -36,7 +36,7 @@ public class ValidErrorRender extends Render { protected static final String html_footer = "


        " + poweredBy + ""; protected int errorCode = ValidUtil.getErrorCode(); - protected ValidException validException; + protected final ValidException validException; public ValidErrorRender(ValidException validException) { this.validException = validException; @@ -45,8 +45,7 @@ public class ValidErrorRender extends Render { @Override public void render() { try { - boolean needRenderJson = RequestUtil.isJsonContentType(request) || RequestUtil.isAjaxRequest(request); - if (needRenderJson) { + if (RequestUtil.isJsonContentType(request) || RequestUtil.isAjaxRequest(request)) { response.setStatus(200); response.setContentType(jsonContentType); response.getWriter().write(getErrorJson()); @@ -62,6 +61,7 @@ public class ValidErrorRender extends Render { public String getErrorHtml() { StringBuilder html = new StringBuilder(html_header); + html.append(validException.getFieldName() == null ? "" : (validException.getFieldName() + ": ")); html.append(validException.getMessage()).append("
        "); return html.append(html_footer).toString(); } @@ -71,6 +71,7 @@ public class ValidErrorRender extends Render { ret.set("throwable", validException.getClass().getName() + ": " + this.validException.getMessage()); ret.set("message", validException.getMessage()); ret.set("errorMessage", validException.getReason()); + ret.set("field", validException.getFieldName()); return JsonKit.toJson(ret); } } diff --git a/src/main/java/io/jboot/components/valid/ValidException.java b/src/main/java/io/jboot/components/valid/ValidException.java index 57fff338..72faea1f 100644 --- a/src/main/java/io/jboot/components/valid/ValidException.java +++ b/src/main/java/io/jboot/components/valid/ValidException.java @@ -19,22 +19,41 @@ public class ValidException extends RuntimeException { private String message; private String reason; + private String fieldName; public ValidException() { } - public ValidException(String message, String reason) { + public ValidException(String message, String reason, String fieldName) { this.message = message; this.reason = reason; + this.fieldName = fieldName; } + @Override public String getMessage() { return message; } + public void setMessage(String message) { + this.message = message; + } + public String getReason() { return reason; } + + public void setReason(String reason) { + this.reason = reason; + } + + public String getFieldName() { + return fieldName; + } + + public void setFieldName(String fieldName) { + this.fieldName = fieldName; + } } diff --git a/src/main/java/io/jboot/components/valid/ValidUtil.java b/src/main/java/io/jboot/components/valid/ValidUtil.java index 94b9563d..a39748a0 100644 --- a/src/main/java/io/jboot/components/valid/ValidUtil.java +++ b/src/main/java/io/jboot/components/valid/ValidUtil.java @@ -70,10 +70,6 @@ public class ValidUtil { message = Validation.buildDefaultValidatorFactory().getMessageInterpolator().interpolate(message, new SimpleContext(paras)); } - if (fieldName != null) { - message = fieldName + message; - } - - throw new ValidException(message, reason); + throw new ValidException(message, reason, fieldName); } } -- Gitee From 165db51742566c610ce24b868ef28546fec7781f Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 6 Mar 2022 11:26:02 +0800 Subject: [PATCH 1607/1965] =?UTF-8?q?feature:=20=E6=96=B0=E5=A2=9E=20jboo.?= =?UTF-8?q?app.listenerPackage=20=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/app/JbootApplicationConfig.java | 10 +++ .../listener/JbootAppListenerManager.java | 72 ++++++++++++------- 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/jboot/app/JbootApplicationConfig.java b/src/main/java/io/jboot/app/JbootApplicationConfig.java index 5cea9f1b6..644ce4ac 100644 --- a/src/main/java/io/jboot/app/JbootApplicationConfig.java +++ b/src/main/java/io/jboot/app/JbootApplicationConfig.java @@ -29,6 +29,7 @@ public class JbootApplicationConfig { private String bannerFile = "banner.txt"; private String jfinalConfig = "io.jboot.core.JbootCoreConfig"; private String listener = "*"; + private String listenerPackage = "*"; private boolean handle404 = true; private String proxy = "cglib"; //cglib or javassist @@ -81,6 +82,14 @@ public class JbootApplicationConfig { this.listener = listener; } + public String getListenerPackage() { + return listenerPackage; + } + + public void setListenerPackage(String listenerPackage) { + this.listenerPackage = listenerPackage; + } + public boolean isHandle404() { return handle404; } @@ -115,6 +124,7 @@ public class JbootApplicationConfig { ", proxy='" + proxy + '\'' + ", config='" + jfinalConfig + '\'' + ", listener='" + listener + '\'' + + ", listenerPackage='" + listenerPackage + '\'' + " }"; } } diff --git a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java index 0230bece..eb77d416 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java @@ -18,7 +18,6 @@ package io.jboot.core.listener; import com.jfinal.config.Constants; import com.jfinal.config.Interceptors; import com.jfinal.config.Routes; -import com.jfinal.kit.LogKit; import com.jfinal.log.Log; import com.jfinal.template.Engine; import io.jboot.aop.jfinal.JfinalHandlers; @@ -30,15 +29,16 @@ import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.function.Predicate; public class JbootAppListenerManager implements JbootAppListener { - private static Log log = Log.getLog(JbootAppListenerManager.class); + private static final Log LOG = Log.getLog(JbootAppListenerManager.class); private static JbootAppListenerManager me = new JbootAppListenerManager(); - public static JbootAppListenerManager me() { return me; } @@ -50,24 +50,32 @@ public class JbootAppListenerManager implements JbootAppListener { String listener = JbootApplicationConfig.get().getListener(); + String listenerPackage = JbootApplicationConfig.get().getListenerPackage(); + Set packages = StrUtil.isNotBlank(listenerPackage) && !"*".equals(listenerPackage.trim()) + ? StrUtil.splitToSet(listenerPackage, ";") : new HashSet<>(); + if (StrUtil.isBlank(listener) || "*".equals(listener.trim())) { List> allListeners = ClassScanner.scanSubClass(JbootAppListener.class, true); allListeners.removeIf((Predicate>) c -> c == JbootAppListenerManager.class || c == JbootAppListenerBase.class); - allListeners.forEach(c -> { - JbootAppListener l = ClassUtil.newInstance(c); - if (l != null) { - listeners.add(l); + allListeners.forEach(clazz -> { + if (isMatchedPackage(packages, clazz.getCanonicalName())) { + JbootAppListener appListener = ClassUtil.newInstance(clazz); + if (appListener != null) { + listeners.add(appListener); + } } }); } else { - StrUtil.splitToSet(listener, ";").forEach(c -> { - JbootAppListener l = ClassUtil.newInstance(c); - if (l != null) { - listeners.add(l); - } else { - LogKit.warn("Can not create JbootAppListener by class: " + c); + StrUtil.splitToSet(listener, ";").forEach(className -> { + if (isMatchedPackage(packages, className)) { + JbootAppListener appListener = ClassUtil.newInstance(className); + if (appListener != null) { + listeners.add(appListener); + } else { + LOG.warn("Can not create JbootAppListener by class: " + className); + } } }); } @@ -75,6 +83,20 @@ public class JbootAppListenerManager implements JbootAppListener { WeightUtil.sort(listeners); } + private boolean isMatchedPackage(Set packages, String className) { + //matched all + if (packages == null || packages.isEmpty()) { + return true; + } + + for (String packageString : packages) { + if (className.startsWith(packageString)) { + return true; + } + } + return false; + } + public List getListeners() { return listeners; } @@ -85,7 +107,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onInit(); } catch (Throwable ex) { - log.error(ex.toString(), ex); + LOG.error(ex.toString(), ex); } } } @@ -96,7 +118,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onConstantConfigBefore(constants); } catch (Throwable ex) { - log.error(ex.toString(), ex); + LOG.error(ex.toString(), ex); } } } @@ -107,7 +129,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onConstantConfig(constants); } catch (Throwable ex) { - log.error(ex.toString(), ex); + LOG.error(ex.toString(), ex); } } } @@ -118,7 +140,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onRouteConfig(routes); } catch (Throwable ex) { - log.error(ex.toString(), ex); + LOG.error(ex.toString(), ex); } } } @@ -129,7 +151,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onEngineConfig(engine); } catch (Throwable ex) { - log.error(ex.toString(), ex); + LOG.error(ex.toString(), ex); } } } @@ -140,7 +162,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onPluginConfig(plugins); } catch (Throwable ex) { - log.error(ex.toString(), ex); + LOG.error(ex.toString(), ex); } } } @@ -151,7 +173,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onInterceptorConfig(interceptors); } catch (Throwable ex) { - log.error(ex.toString(), ex); + LOG.error(ex.toString(), ex); } } } @@ -163,7 +185,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onHandlerConfig(handlers); } catch (Throwable ex) { - log.error(ex.toString(), ex); + LOG.error(ex.toString(), ex); } } } @@ -174,7 +196,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onStartBefore(); } catch (Throwable ex) { - log.error(ex.toString(), ex); + LOG.error(ex.toString(), ex); } } } @@ -185,7 +207,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onStart(); } catch (Throwable ex) { - log.error(ex.toString(), ex); + LOG.error(ex.toString(), ex); } } } @@ -196,7 +218,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onStartFinish(); } catch (Throwable ex) { - log.error(ex.toString(), ex); + LOG.error(ex.toString(), ex); } } } @@ -207,7 +229,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onStop(); } catch (Throwable ex) { - log.error(ex.toString(), ex); + LOG.error(ex.toString(), ex); } } } -- Gitee From a9f4a940e0fb230bce59ed7d4b2d4fa6615c0347 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 6 Mar 2022 11:55:42 +0800 Subject: [PATCH 1608/1965] feature: use @Table(datasource="xxx") config datasource --- src/main/java/io/jboot/db/ArpManager.java | 6 ++++-- src/main/java/io/jboot/db/TableInfo.java | 8 ++++---- src/main/java/io/jboot/db/TableInfoManager.java | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/db/ArpManager.java b/src/main/java/io/jboot/db/ArpManager.java index 6949e1ab..a7a98d5f 100644 --- a/src/main/java/io/jboot/db/ArpManager.java +++ b/src/main/java/io/jboot/db/ArpManager.java @@ -68,6 +68,9 @@ public class ArpManager { for (Map.Entry entry : allConfigs.entrySet()) { DataSourceConfig datasourceConfig = entry.getValue(); if (datasourceConfig.isConfigOk()) { + + // 执行 createRecordPlugin(...) 的时候,会同时完善 DataSourceConfig 里绑定的表数据 + // createRecordPlugin完毕后,就可以通过 dataSourceConfig.getTableInfos() 去获取该数据源有哪些表 ActiveRecordPlugin activeRecordPlugin = createRecordPlugin(datasourceConfig); arpDatasourceConfigs.put(System.identityHashCode(activeRecordPlugin), datasourceConfig); @@ -76,11 +79,10 @@ public class ArpManager { } - // 添加 activeRecordPlugin 的表映射 + // 为 activeRecordPlugin 添加 jfinal 的表映射 for (ActiveRecordPlugin activeRecordPlugin : activeRecordPlugins) { DataSourceConfig dataSourceConfig = arpDatasourceConfigs.get(System.identityHashCode(activeRecordPlugin)); - //获得该数据源匹配的表 List tableInfos = dataSourceConfig.getTableInfos(); if (tableInfos != null && !tableInfos.isEmpty()) { for (TableInfo table : tableInfos) { diff --git a/src/main/java/io/jboot/db/TableInfo.java b/src/main/java/io/jboot/db/TableInfo.java index 47673852..d96dfb0b 100644 --- a/src/main/java/io/jboot/db/TableInfo.java +++ b/src/main/java/io/jboot/db/TableInfo.java @@ -92,10 +92,10 @@ public class TableInfo { this.attachedDatasources = new ArrayList<>(); } - //未指定数据源 - if (!fromDesignated) { + // 若未指定数据源,且已经存在了指定数据源的 datasource + // 则不能再添加该数据源 + if (!fromDesignated && !this.attachedDatasources.isEmpty()) { for (DataSourceConfigWrapper dataSourceConfigWrapper : this.attachedDatasources) { - //若已经存在了指定的数据源,再无法添加未指定的数据源 if (dataSourceConfigWrapper.fromDesignated) { return false; } @@ -104,7 +104,7 @@ public class TableInfo { this.attachedDatasources.add(new DataSourceConfigWrapper(dataSourceConfig, fromDesignated)); - // 通过配置指定的,那么需要移除哪些未指定的默认数据源 + // 若数据源配置了指定的表(亦或者表配置了指定的数据源),那么需要移除哪些未指定的默认数据源 if (fromDesignated) { for (DataSourceConfigWrapper dataSourceConfigWrapper : attachedDatasources) { if (!dataSourceConfigWrapper.fromDesignated) { diff --git a/src/main/java/io/jboot/db/TableInfoManager.java b/src/main/java/io/jboot/db/TableInfoManager.java index 7a7bbaad..066bd837 100644 --- a/src/main/java/io/jboot/db/TableInfoManager.java +++ b/src/main/java/io/jboot/db/TableInfoManager.java @@ -78,7 +78,7 @@ public class TableInfoManager { dataSourceConfig.addTableInfo(tableInfo, true); } - // 注解 @Table 指定了数据源,而且当前数据源未匹配 + // 注解 @Table(datasource="xxxx") 指定了数据源,而且当前数据源未匹配 if (!tableInfo.getDatasourceNames().isEmpty()) { continue; } -- Gitee From 9db62197a7cc48f56886e9752be7fa97a8867844 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 7 Mar 2022 08:48:46 +0800 Subject: [PATCH 1609/1965] v3.14.1 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- src/main/java/io/jboot/support/redis/RedisScanResult.java | 1 - 7 files changed, 8 insertions(+), 9 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 66f9a8cf..eed3bd8f 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.14.0 + 3.14.1 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index c2ee9926..16285283 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.14.0 + 3.14.1 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index dd74fc6c..4b852ac9 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.14.0 + 3.14.1 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.14.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.14.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 58e466fe..a0733919 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.14.0', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.14.1', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.0/jboot-3.14.0.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.1/jboot-3.14.1.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index ab9dca9f..13a82c32 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.14.0 + 3.14.1 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index b184dd6b..13c65002 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.14.0"; + public static String VERSION = "3.14.1"; public static final String ATTR_REQUEST = "REQUEST"; diff --git a/src/main/java/io/jboot/support/redis/RedisScanResult.java b/src/main/java/io/jboot/support/redis/RedisScanResult.java index e99250a7..a7d02598 100644 --- a/src/main/java/io/jboot/support/redis/RedisScanResult.java +++ b/src/main/java/io/jboot/support/redis/RedisScanResult.java @@ -1,6 +1,5 @@ package io.jboot.support.redis; -import redis.clients.jedis.ScanParams; import java.io.Serializable; import java.util.List; -- Gitee From 5ec69726d7e5b1c5720f2ead781dcccdcdf65f7c Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 7 Mar 2022 08:48:57 +0800 Subject: [PATCH 1610/1965] v3.14.1 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 16 ++++++++++++++++ pom.xml | 12 ++++++------ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 38432a32..42f11a0e 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.14.0 + 3.14.1 ``` diff --git a/changes.txt b/changes.txt index 385ffe9d..cb399d48 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,19 @@ +jboot v3.14.1: +新增:注解 @Table(datasource="xxx") 新增对数据源的配置支持 +新增:添加 jboo.app.listenerPackage 配置支持,用于只扫描哪些包的 listener +优化:完善 JbootJson 更多的代码注释 +优化:优化 JbootConfigManager.java 代码,对添加 key 进行 trim() +优化:进一步完善 jboot.properties 文件存放位置的探测 +优化:单元测试移除 mockHandler +优化:DAO.findFirstByColumn(),当传入 null 应抛出错误,防止查询出错误的数据 +优化:rabbitmq.queueDeclareDurable 的配置默认设置为 false +优化:对 QuietlyUtil 的方法进行重命名优化 +优化:ValidErrorRender 输出的 message 不带有 fieldName 信息 +优化:升级 Jfinal 等依赖到最新版本 +修复:@Cacheput() 注解在 Controller 上无效的问题 + + + jboot v3.14.0: 新增:@Cacheable() 等系列缓存注解对 controller 的支持 优化:优化 fastjson 序列化的功能,使用 config 而非 features diff --git a/pom.xml b/pom.xml index 9a8fcc95..5ee5c00d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.1-SNAPSHOT + 3.14.1 jar jboot @@ -41,10 +41,10 @@ - 4.9.18 + 4.9.21 2022.2 2.8 - 3.3 + 3.4 3.3.0 4.0.3 2.13.1 @@ -54,9 +54,9 @@ 2.57 1.2.79 - 31.0.1-jre + 31.1-jre 2.2.5 - 3.7.1 + 3.8.0 1.14.3 2.10.9.2 2.9.3 @@ -728,7 +728,7 @@ net.oschina.j2cache j2cache-core - 2.8.2-release + 2.8.4-release provided -- Gitee From 6951233fd1385b2ad0ab093452935dadb8fa1392 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 7 Mar 2022 09:01:27 +0800 Subject: [PATCH 1611/1965] v3.14.1 release (^.^)YYa!! --- src/main/java/io/jboot/app/JbootApplicationConfig.java | 2 +- src/main/java/io/jboot/app/config/JbootProp.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/app/JbootApplicationConfig.java b/src/main/java/io/jboot/app/JbootApplicationConfig.java index 644ce4ac..1bc94bf5 100644 --- a/src/main/java/io/jboot/app/JbootApplicationConfig.java +++ b/src/main/java/io/jboot/app/JbootApplicationConfig.java @@ -122,7 +122,7 @@ public class JbootApplicationConfig { ", mode='" + mode + '\'' + ", version='" + version + '\'' + ", proxy='" + proxy + '\'' + - ", config='" + jfinalConfig + '\'' + +// ", config='" + jfinalConfig + '\'' + ", listener='" + listener + '\'' + ", listenerPackage='" + listenerPackage + '\'' + " }"; diff --git a/src/main/java/io/jboot/app/config/JbootProp.java b/src/main/java/io/jboot/app/config/JbootProp.java index 499fdf31..1ab53f41 100644 --- a/src/main/java/io/jboot/app/config/JbootProp.java +++ b/src/main/java/io/jboot/app/config/JbootProp.java @@ -69,7 +69,7 @@ class JbootProp { if (inputStream != null) { properties.load(new InputStreamReader(inputStream, encoding)); - } else { + } else if (!fileName.contains("-")) { System.err.println("Warning: Can not load properties file in classpath, file name: " + fileName); } } catch (Exception e) { -- Gitee From 6e11cd01d5afef7da1b80d7c61e34a5d8951d624 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 11 Mar 2022 15:58:06 +0800 Subject: [PATCH 1612/1965] v3.14.2 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5ee5c00d..ebdeab31 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.1 + 3.14.2-SNAPSHOT jar jboot -- Gitee From 28a8f6346ecc51f8aca5795df3f7e417c6df5138 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 11 Mar 2022 16:17:10 +0800 Subject: [PATCH 1613/1965] add Model.CPI methods --- src/main/java/io/jboot/db/model/CPI.java | 49 +++++++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/db/model/CPI.java b/src/main/java/io/jboot/db/model/CPI.java index 148227bc..d97b96c7 100644 --- a/src/main/java/io/jboot/db/model/CPI.java +++ b/src/main/java/io/jboot/db/model/CPI.java @@ -15,9 +15,54 @@ */ package io.jboot.db.model; +import com.jfinal.plugin.activerecord.Config; +import io.jboot.db.dialect.JbootDialect; + public class CPI { - public static boolean hasAnyJoinEffective(JbootModel model){ - return model.hasAnyJoinEffective(); + public static boolean hasAnyJoinEffective(JbootModel dao) { + return dao.hasAnyJoinEffective(); + } + + + public static boolean hasColumn(JbootModel dao, String columnLabel) { + return dao._hasColumn(columnLabel); + } + + + public static JbootDialect getJbootDialect(JbootModel dao) { + return dao._getDialect(); } + + + public static Config getModelConfig(JbootModel dao) { + return dao._getConfig(); + } + + + public static M loadByCache(JbootModel dao, Object... idValues) { + return (M) dao.loadByCache(idValues); + } + + + public static void safeDeleteCache(JbootModel dao, Object... idValues) { + dao.safeDeleteCache(idValues); + } + + + public static Class safeDeleteCache(JbootModel dao) { + return dao._getPrimaryType(); + } + + + public static String buildIdCacheName(JbootModel dao, String name) { + return dao.buildIdCacheName(name); + } + + + public static String buildIdCacheKey(JbootModel dao, Object... idValues) { + return dao.buildIdCacheKey(idValues); + } + + } -- Gitee From 6230d3a131961304b6befe2d319b28bb53357aac Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 11 Mar 2022 17:05:33 +0800 Subject: [PATCH 1614/1965] Optimize ValidUtil.java --- src/main/java/io/jboot/components/valid/ValidUtil.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/components/valid/ValidUtil.java b/src/main/java/io/jboot/components/valid/ValidUtil.java index a39748a0..2394a0f1 100644 --- a/src/main/java/io/jboot/components/valid/ValidUtil.java +++ b/src/main/java/io/jboot/components/valid/ValidUtil.java @@ -66,10 +66,16 @@ public class ValidUtil { public static void throwValidException(String fieldName, String message, Ret paras, String reason) { - if (message != null) { - message = Validation.buildDefaultValidatorFactory().getMessageInterpolator().interpolate(message, new SimpleContext(paras)); + if (isParaMessage(message)) { + message = fieldName + " " + Validation.buildDefaultValidatorFactory().getMessageInterpolator() + .interpolate(message, new SimpleContext(paras)); } throw new ValidException(message, reason, fieldName); } + + + private static boolean isParaMessage(String message) { + return message != null && message.startsWith("{") && message.endsWith("}"); + } } -- Gitee From 85e8cce1aab9b0f1c1c40f1f4494b6e7e8d92bda Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 11 Mar 2022 17:27:51 +0800 Subject: [PATCH 1615/1965] Optimize ... --- .../components/valid/ValidErrorRender.java | 4 ++-- .../components/valid/ValidException.java | 12 +++++------ src/main/java/io/jboot/utils/ArrayUtil.java | 4 ++-- .../interceptor/EmptyValidateInterceptor.java | 20 +++++++++---------- .../interceptor/ValidateInterceptorUtil.java | 6 ++---- .../test/validate/ValidateController.java | 2 +- 6 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/jboot/components/valid/ValidErrorRender.java b/src/main/java/io/jboot/components/valid/ValidErrorRender.java index a21e4897..2bd1a062 100644 --- a/src/main/java/io/jboot/components/valid/ValidErrorRender.java +++ b/src/main/java/io/jboot/components/valid/ValidErrorRender.java @@ -61,7 +61,7 @@ public class ValidErrorRender extends Render { public String getErrorHtml() { StringBuilder html = new StringBuilder(html_header); - html.append(validException.getFieldName() == null ? "" : (validException.getFieldName() + ": ")); + html.append(validException.getFormName() == null ? "" : (validException.getFormName() + ": ")); html.append(validException.getMessage()).append("
        "); return html.append(html_footer).toString(); } @@ -71,7 +71,7 @@ public class ValidErrorRender extends Render { ret.set("throwable", validException.getClass().getName() + ": " + this.validException.getMessage()); ret.set("message", validException.getMessage()); ret.set("errorMessage", validException.getReason()); - ret.set("field", validException.getFieldName()); + ret.set("formName", validException.getFormName()); return JsonKit.toJson(ret); } } diff --git a/src/main/java/io/jboot/components/valid/ValidException.java b/src/main/java/io/jboot/components/valid/ValidException.java index 72faea1f..9221f687 100644 --- a/src/main/java/io/jboot/components/valid/ValidException.java +++ b/src/main/java/io/jboot/components/valid/ValidException.java @@ -19,7 +19,7 @@ public class ValidException extends RuntimeException { private String message; private String reason; - private String fieldName; + private String formName; public ValidException() { } @@ -28,7 +28,7 @@ public class ValidException extends RuntimeException { public ValidException(String message, String reason, String fieldName) { this.message = message; this.reason = reason; - this.fieldName = fieldName; + this.formName = fieldName; } @@ -49,11 +49,11 @@ public class ValidException extends RuntimeException { this.reason = reason; } - public String getFieldName() { - return fieldName; + public String getFormName() { + return formName; } - public void setFieldName(String fieldName) { - this.fieldName = fieldName; + public void setFormName(String formName) { + this.formName = formName; } } diff --git a/src/main/java/io/jboot/utils/ArrayUtil.java b/src/main/java/io/jboot/utils/ArrayUtil.java index 383b53ff..a5df3ac5 100644 --- a/src/main/java/io/jboot/utils/ArrayUtil.java +++ b/src/main/java/io/jboot/utils/ArrayUtil.java @@ -23,7 +23,7 @@ import java.util.Objects; public class ArrayUtil { - public static boolean isNotEmpty(Collection list) { + public static boolean isNotEmpty(Collection list) { return list != null && list.size() > 0; } @@ -35,7 +35,7 @@ public class ArrayUtil { return objects != null && objects.length > 0; } - public static boolean isNullOrEmpty(Collection list) { + public static boolean isNullOrEmpty(Collection list) { return list == null || list.size() == 0; } diff --git a/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java b/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java index 4d1f0ea5..159e366c 100644 --- a/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java @@ -65,40 +65,40 @@ public class EmptyValidateInterceptor implements Interceptor { } - for (Form form : forms) { - String formName = AnnotationUtil.get(form.name()); - String formType = AnnotationUtil.get(form.type()); + for (Form formAnnotation : forms) { + String formName = AnnotationUtil.get(formAnnotation.name()); + String formType = AnnotationUtil.get(formAnnotation.type()); if (StrUtil.isBlank(formName)) { throw new IllegalArgumentException("@Form.value must not be empty in " + inv.getController().getClass().getName() + "." + inv.getMethodName()); } - String value = null; + String paraValue = null; if (FormType.FORM_DATA.equalsIgnoreCase(formType)) { - value = inv.getController().getPara(formName); + paraValue = inv.getController().getPara(formName); } else if (FormType.RAW_DATA.equalsIgnoreCase(formType)) { try { JSONObject json = JSON.parseObject(inv.getController().getRawData()); if (json != null) { Object tmp = JSONPath.eval(json, "$." + formName); if (tmp != null) { - value = tmp.toString(); + paraValue = tmp.toString(); } } } catch (Exception e) { - value = null; + paraValue = null; } } else { throw new IllegalArgumentException("@EmptyValidate not support form type : " + formType + ", " + "see : io.jboot.web.controller.validate.FormType"); } - if (value == null || value.trim().length() == 0) { + if (paraValue == null || paraValue.trim().length() == 0) { ValidateInterceptorUtil.renderValidException(inv.getController() , AnnotationUtil.get(emptyParaValidate.renderType()) , formName - , AnnotationUtil.get(form.message()) + , AnnotationUtil.get(formAnnotation.message()) , AnnotationUtil.get(emptyParaValidate.redirectUrl()) , AnnotationUtil.get(emptyParaValidate.htmlPath()) - , form.errorCode() + , formAnnotation.errorCode() ); return false; } diff --git a/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorUtil.java b/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorUtil.java index f11e604c..99e02bdd 100644 --- a/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorUtil.java +++ b/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorUtil.java @@ -56,7 +56,7 @@ public class ValidateInterceptorUtil { static void renderValidException(Controller controller, String renderType, String formName, String message, String redirectUrl, String htmlPath, int errorCode) { - String reason = StrUtil.isNotBlank(message) ? (formName + " validate failed: " + message) : (formName + " validate failed!"); + String reason = StrUtil.isNotBlank(message) ? (formName + " validate failed: " + message) : (formName + " validate failed."); switch (renderType) { case ValidateRenderType.DEFAULT: if (RequestUtil.isAjaxRequest(controller.getRequest()) @@ -91,15 +91,13 @@ public class ValidateInterceptorUtil { controller.renderText(message); break; default: - throw new IllegalArgumentException("can not process render : " + renderType); + throw new IllegalArgumentException("Can not process render : " + renderType); } } public static interface ValidExceptionRetBuilder{ - ValidExceptionRetBuilder DEFAULT_BUILDER = ret -> ret; - Ret build(Ret baseRet); } diff --git a/src/test/java/io/jboot/test/validate/ValidateController.java b/src/test/java/io/jboot/test/validate/ValidateController.java index 5dd177a7..a78ee562 100644 --- a/src/test/java/io/jboot/test/validate/ValidateController.java +++ b/src/test/java/io/jboot/test/validate/ValidateController.java @@ -62,7 +62,7 @@ public class ValidateController extends Controller { } - public void test10(@Max(10) int para1) { + public void test10(@Max(value = 10,message = "错误的 para1") int para1) { renderText("test10"); } -- Gitee From 1cb7b29daa1d5007ba48e60d7e6d8f52b47551e6 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 13 Mar 2022 10:09:05 +0800 Subject: [PATCH 1616/1965] =?UTF-8?q?ClassScanner.java=20=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=85=B3=E9=97=AD=20Class=20=E6=89=AB=E6=8F=8F?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E5=9C=A8=E6=8E=A7=E5=88=B6=E5=8F=B0=E8=BE=93?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/utils/ClassScanner.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index a722d697..7b5de0b6 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -39,7 +39,9 @@ public class ClassScanner { public static final Set scanClasses = new HashSet<>(); public static final Set excludeClasses = new HashSet<>(); - private static boolean printScannerInfoEnable = JbootConfigManager.me().isDevMode(); + + // 默认关闭扫描信息的控制台输出 + private static boolean printScannerInfoEnable = false; public static boolean isPrintScannerInfoEnable() { return printScannerInfoEnable; -- Gitee From a51d65bdd53645b5cf95f9fd2fb41396974e7a56 Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Sun, 13 Mar 2022 12:21:26 +0800 Subject: [PATCH 1617/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9A=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E7=BC=93=E5=AD=98=E6=8B=A6=E6=88=AA=E5=99=A8=E7=9A=84?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E6=9D=83=E9=87=8D=E4=B8=BA=20100=EF=BC=8C?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E6=83=85=E5=86=B5=E4=B8=8B=E6=8B=A6=E6=88=AA?= =?UTF-8?q?=E5=99=A8=E5=9C=A8=E6=9C=80=E5=90=8E=E6=89=A7=E8=A1=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/aop/Interceptors.java | 10 ++++++++-- .../interceptor/CacheInterceptorBuilder.java | 19 +++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/jboot/aop/Interceptors.java b/src/main/java/io/jboot/aop/Interceptors.java index feaa9199..740f6243 100644 --- a/src/main/java/io/jboot/aop/Interceptors.java +++ b/src/main/java/io/jboot/aop/Interceptors.java @@ -209,8 +209,10 @@ public class Interceptors { public List toList() { + // 排序:weight 越小越靠前 warppers.sort(Comparator.comparingInt(InterceptorWarpper::getWeight)); - return warppers.stream().map(InterceptorWarpper::getInterceptor).collect(Collectors.toList()); + return warppers.stream().map(InterceptorWarpper::getInterceptor) + .collect(Collectors.toList()); } @@ -218,7 +220,9 @@ public class Interceptors { if (warppers == null || warppers.size() == 0) { return InterceptorManager.NULL_INTERS; } else { + // 排序:weight 越小越靠前 warppers.sort(Comparator.comparingInt(InterceptorWarpper::getWeight)); + Interceptor[] inters = new Interceptor[warppers.size()]; for (int i = 0; i < warppers.size(); i++) { inters[i] = warppers.get(i).getInterceptor(); @@ -261,9 +265,11 @@ public class Interceptors { return ClassUtil.singleton(interceptorClass, false, true); } + public List getWarppers() { + return warppers; + } static class InterceptorWarpper { - private Interceptor interceptor; private int weight; diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java b/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java index da004a92..8ee0a521 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheInterceptorBuilder.java @@ -31,23 +31,26 @@ import java.lang.reflect.Method; @AutoLoad public class CacheInterceptorBuilder implements InterceptorBuilder { + // 缓存拦截器的权重 + // 其他拦截器器的默认权重是 1+ + public static final int INTERCEPTOR_WEIGHT = 100; @Override public void build(Class targetClass, Method method, Interceptors interceptors) { - if (Util.hasAnnotation(method,CacheEvict.class)) { - interceptors.add(CacheEvictInterceptor.class); + if (Util.hasAnnotation(method, CacheEvict.class)) { + interceptors.add(CacheEvictInterceptor.class, INTERCEPTOR_WEIGHT); } - if (Util.hasAnnotation(method,Cacheable.class)) { - interceptors.add(CacheableInterceptor.class); + if (Util.hasAnnotation(method, Cacheable.class)) { + interceptors.add(CacheableInterceptor.class, INTERCEPTOR_WEIGHT); } - if (Util.hasAnnotation(method,CachePut.class)) { - interceptors.add(CachePutInterceptor.class); + if (Util.hasAnnotation(method, CachePut.class)) { + interceptors.add(CachePutInterceptor.class, INTERCEPTOR_WEIGHT); } - if (Util.hasAnnotation(method,CachesEvict.class)) { - interceptors.add(CachesEvictInterceptor.class); + if (Util.hasAnnotation(method, CachesEvict.class)) { + interceptors.add(CachesEvictInterceptor.class, INTERCEPTOR_WEIGHT); } } -- Gitee From 8d0b2be91f5f9d3301a224bd9465b1541ae2c29a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 13 Mar 2022 16:54:15 +0800 Subject: [PATCH 1618/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9A=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E7=BC=93=E5=AD=98=E6=8B=A6=E6=88=AA=E5=99=A8=E7=9A=84?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E6=9D=83=E9=87=8D=E4=B8=BA=20100=EF=BC=8C?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E6=83=85=E5=86=B5=E4=B8=8B=E6=8B=A6=E6=88=AA?= =?UTF-8?q?=E5=99=A8=E5=9C=A8=E6=9C=80=E5=90=8E=E6=89=A7=E8=A1=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/aop/InterceptorBuilderManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java index 83a777fa..65f6e48f 100644 --- a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java +++ b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java @@ -94,7 +94,7 @@ public class InterceptorBuilderManager{ - public Interceptor[] build(Class targetClass, Method method, Interceptor[] inters) { + public Interceptor[] build(Class targetClass, Method method, Interceptor[] inters) { if (interceptorBuilders != null && interceptorBuilders.size() > 0) { Interceptors interceptors = new Interceptors(inters); for (InterceptorBuilder builder : interceptorBuilders) { -- Gitee From aeeb91181e23ca1b3a303dee436262e3ac7ab70e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 15 Mar 2022 10:25:15 +0800 Subject: [PATCH 1619/1965] add JbootModel.findByIdWithoutCache() method --- .../java/io/jboot/db/model/JbootModel.java | 51 +++++++++++-------- .../io/jboot/db/model/JbootModelFilter.java | 5 +- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 75022ab1..463aea1d 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -330,6 +330,20 @@ public class JbootModel> extends Model { return idCacheEnable ? loadByCache(idValue) : super.findById(idValue); } + /** + * 直接查询数据库,不走缓存 + * + * @param idValue + * @return + */ + public M findByIdWithoutCache(Object idValue) { + if (idValue == null) { + return null; + } + return super.findById(idValue); + } + + @Override public M findByIds(Object... idValues) { if (idValues == null) { @@ -341,17 +355,24 @@ public class JbootModel> extends Model { return idCacheEnable ? loadByCache(idValues) : super.findByIds(idValues); } - protected M loadByCache(Object... idValues) { - - //临时关闭 id 缓存的情况 - if (JbootModelHintManager.me().isClosedIdCache(getClass())) { - try { - return JbootModel.super.findByIds(idValues); - } finally { - JbootModelHintManager.me().clearIdCacheFlag(); - } + /** + * 直接查询数据库,不走缓存 + * + * @param idValues + * @return + */ + public M findByIdsWithoutCache(Object... idValues) { + if (idValues == null) { + return null; } + if (idValues.length != _getPrimaryKeys().length) { + throw new IllegalArgumentException("idValues.length != _getPrimaryKeys().length"); + } + return super.findByIds(idValues); + } + + protected M loadByCache(Object... idValues) { try { M m = config.getIdCache().get(buildIdCacheName(_getTableName()) , buildIdCacheKey(idValues) @@ -367,18 +388,6 @@ public class JbootModel> extends Model { } - /** - * 临时关闭 id 缓存,关闭后通过 findById 执行后又会开启了 - * 一般情况下的使用方法是 DAO.closeIdCacheTemporary().findById(...) - * - * @return - */ - public M closeIdCacheTemporary() { - JbootModelHintManager.me().closeIdCache(getClass()); - return (M) this; - } - - protected void safeDeleteCache(Object... idValues) { try { config.getIdCache().remove(buildIdCacheName(_getTableName()) diff --git a/src/main/java/io/jboot/db/model/JbootModelFilter.java b/src/main/java/io/jboot/db/model/JbootModelFilter.java index 0bcc0db8..c9bb5b82 100644 --- a/src/main/java/io/jboot/db/model/JbootModelFilter.java +++ b/src/main/java/io/jboot/db/model/JbootModelFilter.java @@ -21,7 +21,8 @@ package io.jboot.db.model; */ public interface JbootModelFilter { - static JbootModelFilter DEFAULT = (model, filterBy) -> {}; + JbootModelFilter DEFAULT = (model, filterBy) -> { + }; - public void filter(JbootModel model,int filterBy); + void filter(JbootModel model, int filterBy); } -- Gitee From 25da7859f41176f86c452c66c9b93831888d1e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 15 Mar 2022 10:25:26 +0800 Subject: [PATCH 1620/1965] remove JbootModelHintManager.java --- .../jboot/db/model/JbootModelHintManager.java | 51 ------------------- 1 file changed, 51 deletions(-) delete mode 100644 src/main/java/io/jboot/db/model/JbootModelHintManager.java diff --git a/src/main/java/io/jboot/db/model/JbootModelHintManager.java b/src/main/java/io/jboot/db/model/JbootModelHintManager.java deleted file mode 100644 index 6f5a2509..00000000 --- a/src/main/java/io/jboot/db/model/JbootModelHintManager.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.db.model; - - -import java.util.HashSet; -import java.util.Set; - -public class JbootModelHintManager { - - private static final ThreadLocal> ID_CACHE_HOLDER = new ThreadLocal<>(); - private static JbootModelHintManager me = new JbootModelHintManager(); - - public static JbootModelHintManager me() { - return me; - } - - public void closeIdCache(Class clazz) { - Set flags = ID_CACHE_HOLDER.get(); - if (flags == null) { - flags = new HashSet<>(); - } - flags.add(clazz); - ID_CACHE_HOLDER.set(flags); - } - - - public boolean isClosedIdCache(Class clazz) { - Set result = ID_CACHE_HOLDER.get(); - return result != null && result.contains(clazz); - } - - - public void clearIdCacheFlag() { - ID_CACHE_HOLDER.remove(); - } - -} -- Gitee From 081dc3cbf1941709c59fabdf8188c4ad8f791880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 15 Mar 2022 11:19:27 +0800 Subject: [PATCH 1621/1965] =?UTF-8?q?Nacos=20=E8=BF=9C=E7=A8=8B=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E4=B8=AD=E5=BF=83=E6=B7=BB=E5=8A=A0=E5=A4=9A=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...niter.java => NacosConfigInitializer.java} | 4 ++-- .../support/nacos/NacosConfigManager.java | 18 ++++++++++++---- src/main/java/io/jboot/utils/ConfigUtil.java | 21 +++++++++++++++---- 3 files changed, 33 insertions(+), 10 deletions(-) rename src/main/java/io/jboot/app/config/support/nacos/{NacosConfigIniter.java => NacosConfigInitializer.java} (93%) diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigInitializer.java similarity index 93% rename from src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java rename to src/main/java/io/jboot/app/config/support/nacos/NacosConfigInitializer.java index 99ab39fe..58d8396c 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigIniter.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigInitializer.java @@ -25,13 +25,13 @@ import java.util.concurrent.Executor; * @author michael yang (fuhai999@gmail.com) * @Date: 2020/2/9 */ -public class NacosConfigIniter { +public class NacosConfigInitializer { private NacosConfigManager manager; private JbootConfigManager configManager; - public NacosConfigIniter(NacosConfigManager manager, JbootConfigManager configManager) { + public NacosConfigInitializer(NacosConfigManager manager, JbootConfigManager configManager) { this.manager = manager; this.configManager = configManager; } diff --git a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java index 702c33dc..03185c98 100644 --- a/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/nacos/NacosConfigManager.java @@ -17,11 +17,14 @@ package io.jboot.app.config.support.nacos; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; +import com.jfinal.log.Log; import io.jboot.app.config.JbootConfigKit; import io.jboot.app.config.JbootConfigManager; +import io.jboot.utils.ConfigUtil; import java.io.IOException; import java.io.StringReader; +import java.util.Map; import java.util.Objects; import java.util.Properties; @@ -31,6 +34,7 @@ import java.util.Properties; */ public class NacosConfigManager { + private static final Log LOG = Log.getLog(NacosConfigManager.class); private static final NacosConfigManager ME = new NacosConfigManager(); public static NacosConfigManager me() { @@ -44,9 +48,14 @@ public class NacosConfigManager { * 初始化 nacos 配置监听 */ public void init(JbootConfigManager configManager) { + Map configModels = ConfigUtil.getConfigModels(configManager, NacosServerConfig.class); + configModels.forEach((s, nacosConfig) -> initConfig(configManager, nacosConfig)); + } - NacosServerConfig nacosServerConfig = configManager.get(NacosServerConfig.class); - if (!nacosServerConfig.isEnable() || !nacosServerConfig.isConfigOk()) { + private void initConfig(JbootConfigManager configManager, NacosServerConfig nacosServerConfig) { + if (nacosServerConfig == null + || !nacosServerConfig.isEnable() + || !nacosServerConfig.isConfigOk()) { return; } @@ -63,10 +72,11 @@ public class NacosConfigManager { } } - new NacosConfigIniter(this, configManager).initListener(configService, nacosServerConfig); + new NacosConfigInitializer(this, configManager).initListener(configService, nacosServerConfig); } catch (Exception e) { - e.printStackTrace(); + + LOG.error(e.toString(), e); } } diff --git a/src/main/java/io/jboot/utils/ConfigUtil.java b/src/main/java/io/jboot/utils/ConfigUtil.java index 70e42adc..eed5406b 100644 --- a/src/main/java/io/jboot/utils/ConfigUtil.java +++ b/src/main/java/io/jboot/utils/ConfigUtil.java @@ -39,8 +39,21 @@ public class ConfigUtil { } - public static Map getConfigModels(Class tClass, String prefix) { + return getConfigModels(JbootConfigManager.me(), tClass, prefix); + } + + + public static Map getConfigModels(JbootConfigManager configManager, Class tClass) { + ConfigModel configModel = tClass.getAnnotation(ConfigModel.class); + if (configModel == null) { + throw new IllegalStateException("The Class \"" + tClass.getName() + "\" is not have @ConfigModel annotation"); + } + return getConfigModels(configManager, tClass, configModel.prefix()); + } + + + public static Map getConfigModels(JbootConfigManager configManager, Class tClass, String prefix) { Map objMap = new HashMap<>(); boolean initDefault = false; @@ -50,7 +63,7 @@ public class ConfigUtil { String objPrefix = prefix + "."; int pointCount = StringUtils.countMatches(prefix, '.'); - Properties prop = JbootConfigManager.me().getProperties(); + Properties prop = configManager.getProperties(); for (Map.Entry entry : prop.entrySet()) { if (entry.getKey() == null || JbootConfigKit.isBlank(entry.getKey().toString())) { @@ -67,7 +80,7 @@ public class ConfigUtil { //初始化默认的配置 if (!initDefault && key.startsWith(prefix) && StringUtils.countMatches(key, '.') == pointCount + 1) { initDefault = true; - T defaultObj = JbootConfigManager.me().get(tClass, prefix, null); + T defaultObj = configManager.get(tClass, prefix, null); objMap.put("default", defaultObj); } @@ -81,7 +94,7 @@ public class ConfigUtil { } for (String name : objNames) { - T obj = JbootConfigManager.me().get(tClass, objPrefix + name, null); + T obj = configManager.get(tClass, objPrefix + name, null); objMap.put(name, obj); } -- Gitee From 216ec627ce16a0f9d99393c09bb68f6dfc2c1831 Mon Sep 17 00:00:00 2001 From: xujianxie Date: Thu, 17 Mar 2022 05:35:19 +0000 Subject: [PATCH 1622/1965] add src/main/java/io/jboot/db/dialect/InformixDialect.java. --- .../io/jboot/db/dialect/InformixDialect.java | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 src/main/java/io/jboot/db/dialect/InformixDialect.java diff --git a/src/main/java/io/jboot/db/dialect/InformixDialect.java b/src/main/java/io/jboot/db/dialect/InformixDialect.java new file mode 100644 index 00000000..1e4c9bd3 --- /dev/null +++ b/src/main/java/io/jboot/db/dialect/InformixDialect.java @@ -0,0 +1,185 @@ +package io.jboot.db.dialet; +import com.jfinal.plugin.activerecord.Record; +import com.jfinal.plugin.activerecord.Table; +import com.jfinal.plugin.activerecord.dialect.Dialect; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.Set; +public class InformixDialect extends Dialect { + public String forTableBuilderDoBuild(String tableName) { + return "select * from " + tableName + " where 1 = 2"; + } + + public void forModelSave(Table table, Map attrs, StringBuilder sql, List paras) { + sql.append("insert into ").append(table.getName()).append('('); + StringBuilder temp = new StringBuilder(") values("); + for (Map.Entry e: attrs.entrySet()) { + String colName = e.getKey(); + if (table.hasColumnLabel(colName)) { + if (paras.size() > 0) { + sql.append(", "); + temp.append(", "); + } + sql.append(colName); + temp.append('?'); + paras.add(e.getValue()); + } + } + sql.append(temp.toString()).append(')'); + } + + public String forModelDeleteById(Table table) { + String[] pKeys = table.getPrimaryKey(); + StringBuilder sql = new StringBuilder(45); + sql.append("delete from "); + sql.append(table.getName()); + sql.append(" where "); + for (int i=0; i 0) { + sql.append(" and "); + } + sql.append(pKeys[i]).append(" = ?"); + } + return sql.toString(); + } + + public void forModelUpdate(Table table, Map attrs, Set modifyFlag, StringBuilder sql, List paras) { + sql.append("update ").append(table.getName()).append(" set "); + String[] pKeys = table.getPrimaryKey(); + for (Map.Entry e : attrs.entrySet()) { + String colName = e.getKey(); + if (modifyFlag.contains(colName) && !isPrimaryKey(colName, pKeys) && table.hasColumnLabel(colName)) { + if (paras.size() > 0) { + sql.append(", "); + } + sql.append(colName).append(" = ? "); + paras.add(e.getValue()); + } + } + sql.append(" where "); + for (int i=0; i 0) { + sql.append(" and "); + } + sql.append(pKeys[i]).append(" = ?"); + paras.add(attrs.get(pKeys[i])); + } + } + + public String forModelFindById(Table table, String columns) { + StringBuilder sql = new StringBuilder("select ").append(columns).append(" from "); + sql.append(table.getName()); + sql.append(" where "); + String[] pKeys = table.getPrimaryKey(); + for (int i=0; i 0) { + sql.append(" and "); + } + sql.append(pKeys[i]).append(" = ?"); + } + return sql.toString(); + } + + public String forDbFindById(String tableName, String[] pKeys) { + tableName = tableName.trim(); + trimPrimaryKeys(pKeys); + + StringBuilder sql = new StringBuilder("select * from ").append(tableName).append(" where "); + for (int i=0; i 0) { + sql.append(" and "); + } + sql.append(pKeys[i]).append(" = ?"); + } + return sql.toString(); + } + + public String forDbDeleteById(String tableName, String[] pKeys) { + tableName = tableName.trim(); + trimPrimaryKeys(pKeys); + + StringBuilder sql = new StringBuilder("delete from ").append(tableName).append(" where "); + for (int i=0; i 0) { + sql.append(" and "); + } + sql.append(pKeys[i]).append(" = ?"); + } + return sql.toString(); + } + + public void forDbSave(String tableName, String[] pKeys, Record record, StringBuilder sql, List paras) { + tableName = tableName.trim(); + trimPrimaryKeys(pKeys); + + sql.append("insert into "); + sql.append(tableName).append('('); + StringBuilder temp = new StringBuilder(); + temp.append(") values("); + + for (Map.Entry e: record.getColumns().entrySet()) { + if (paras.size() > 0) { + sql.append(", "); + temp.append(", "); + } + sql.append(e.getKey()); + temp.append('?'); + paras.add(e.getValue()); + } + sql.append(temp.toString()).append(')'); + } + + public void forDbUpdate(String tableName, String[] pKeys, Object[] ids, Record record, StringBuilder sql, List paras) { + tableName = tableName.trim(); + trimPrimaryKeys(pKeys); + + sql.append("update ").append(tableName).append(" set "); + for (Map.Entry e: record.getColumns().entrySet()) { + String colName = e.getKey(); + if (!isPrimaryKey(colName, pKeys)) { + if (paras.size() > 0) { + sql.append(", "); + } + sql.append(colName).append(" = ? "); + paras.add(e.getValue()); + } + } + sql.append(" where "); + for (int i=0; i 0) { + sql.append(" and "); + } + sql.append(pKeys[i]).append(" = ?"); + paras.add(ids[i]); + } + } + + /** + * sql.replaceFirst("(?i)select", "") 正则中带有 "(?i)" 前缀,指定在匹配时不区分大小写 + */ + public String forPaginate(int pageNumber, int pageSize, StringBuilder findSql) { + int end = pageNumber * pageSize; + if (end <= 0) { + end = pageSize; + } + int begin = (pageNumber - 1) * pageSize; + if (begin < 0) { + begin = 0; + } + StringBuilder ret = new StringBuilder(); + ret.append(String.format("select skip %s first %s ", begin+"",pageSize+"")); + ret.append(findSql.toString().replaceFirst("(?i)select", "")); + return ret.toString(); + } + + public void fillStatement(PreparedStatement pst, List paras) throws SQLException { + fillStatementHandleDateType(pst, paras); + } + + public void fillStatement(PreparedStatement pst, Object... paras) throws SQLException { + fillStatementHandleDateType(pst, paras); + } +} \ No newline at end of file -- Gitee From 1bbdcfe3b62d6e6af826a2c5255b28a3685135f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 17 Mar 2022 14:30:12 +0800 Subject: [PATCH 1623/1965] Add: Informix dialect --- src/main/java/io/jboot/db/ArpManager.java | 3 + .../jboot/db/datasource/DataSourceConfig.java | 1 + ...Dialect.java => JbootInformixDialect.java} | 451 +++++++++++------- 3 files changed, 271 insertions(+), 184 deletions(-) rename src/main/java/io/jboot/db/dialect/{InformixDialect.java => JbootInformixDialect.java} (59%) diff --git a/src/main/java/io/jboot/db/ArpManager.java b/src/main/java/io/jboot/db/ArpManager.java index a7a98d5f..c75e3ab1 100644 --- a/src/main/java/io/jboot/db/ArpManager.java +++ b/src/main/java/io/jboot/db/ArpManager.java @@ -245,6 +245,9 @@ public class ArpManager { case DataSourceConfig.TYPE_CLICKHOUSE: activeRecordPlugin.setDialect(new JbootClickHouseDialect()); break; + case DataSourceConfig.TYPE_INFORMIX: + activeRecordPlugin.setDialect(new JbootInformixDialect()); + break; default: throw new JbootIllegalConfigException("only support datasource type : mysql、orcale、sqlserver、sqlite、ansisql、postgresql and clickhouse, please check your jboot.properties. "); } diff --git a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java index 701b3d76..a633021c 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java @@ -34,6 +34,7 @@ public class DataSourceConfig { public static final String TYPE_ANSISQL = "ansisql"; public static final String TYPE_POSTGRESQL = "postgresql"; public static final String TYPE_CLICKHOUSE = "clickhouse"; + public static final String TYPE_INFORMIX = "informix"; private String name; diff --git a/src/main/java/io/jboot/db/dialect/InformixDialect.java b/src/main/java/io/jboot/db/dialect/JbootInformixDialect.java similarity index 59% rename from src/main/java/io/jboot/db/dialect/InformixDialect.java rename to src/main/java/io/jboot/db/dialect/JbootInformixDialect.java index 1e4c9bd3..69868c44 100644 --- a/src/main/java/io/jboot/db/dialect/InformixDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootInformixDialect.java @@ -1,185 +1,268 @@ -package io.jboot.db.dialet; -import com.jfinal.plugin.activerecord.Record; -import com.jfinal.plugin.activerecord.Table; -import com.jfinal.plugin.activerecord.dialect.Dialect; - -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.List; -import java.util.Map; -import java.util.Set; -public class InformixDialect extends Dialect { - public String forTableBuilderDoBuild(String tableName) { - return "select * from " + tableName + " where 1 = 2"; - } - - public void forModelSave(Table table, Map attrs, StringBuilder sql, List paras) { - sql.append("insert into ").append(table.getName()).append('('); - StringBuilder temp = new StringBuilder(") values("); - for (Map.Entry e: attrs.entrySet()) { - String colName = e.getKey(); - if (table.hasColumnLabel(colName)) { - if (paras.size() > 0) { - sql.append(", "); - temp.append(", "); - } - sql.append(colName); - temp.append('?'); - paras.add(e.getValue()); - } - } - sql.append(temp.toString()).append(')'); - } - - public String forModelDeleteById(Table table) { - String[] pKeys = table.getPrimaryKey(); - StringBuilder sql = new StringBuilder(45); - sql.append("delete from "); - sql.append(table.getName()); - sql.append(" where "); - for (int i=0; i 0) { - sql.append(" and "); - } - sql.append(pKeys[i]).append(" = ?"); - } - return sql.toString(); - } - - public void forModelUpdate(Table table, Map attrs, Set modifyFlag, StringBuilder sql, List paras) { - sql.append("update ").append(table.getName()).append(" set "); - String[] pKeys = table.getPrimaryKey(); - for (Map.Entry e : attrs.entrySet()) { - String colName = e.getKey(); - if (modifyFlag.contains(colName) && !isPrimaryKey(colName, pKeys) && table.hasColumnLabel(colName)) { - if (paras.size() > 0) { - sql.append(", "); - } - sql.append(colName).append(" = ? "); - paras.add(e.getValue()); - } - } - sql.append(" where "); - for (int i=0; i 0) { - sql.append(" and "); - } - sql.append(pKeys[i]).append(" = ?"); - paras.add(attrs.get(pKeys[i])); - } - } - - public String forModelFindById(Table table, String columns) { - StringBuilder sql = new StringBuilder("select ").append(columns).append(" from "); - sql.append(table.getName()); - sql.append(" where "); - String[] pKeys = table.getPrimaryKey(); - for (int i=0; i 0) { - sql.append(" and "); - } - sql.append(pKeys[i]).append(" = ?"); - } - return sql.toString(); - } - - public String forDbFindById(String tableName, String[] pKeys) { - tableName = tableName.trim(); - trimPrimaryKeys(pKeys); - - StringBuilder sql = new StringBuilder("select * from ").append(tableName).append(" where "); - for (int i=0; i 0) { - sql.append(" and "); - } - sql.append(pKeys[i]).append(" = ?"); - } - return sql.toString(); - } - - public String forDbDeleteById(String tableName, String[] pKeys) { - tableName = tableName.trim(); - trimPrimaryKeys(pKeys); - - StringBuilder sql = new StringBuilder("delete from ").append(tableName).append(" where "); - for (int i=0; i 0) { - sql.append(" and "); - } - sql.append(pKeys[i]).append(" = ?"); - } - return sql.toString(); - } - - public void forDbSave(String tableName, String[] pKeys, Record record, StringBuilder sql, List paras) { - tableName = tableName.trim(); - trimPrimaryKeys(pKeys); - - sql.append("insert into "); - sql.append(tableName).append('('); - StringBuilder temp = new StringBuilder(); - temp.append(") values("); - - for (Map.Entry e: record.getColumns().entrySet()) { - if (paras.size() > 0) { - sql.append(", "); - temp.append(", "); - } - sql.append(e.getKey()); - temp.append('?'); - paras.add(e.getValue()); - } - sql.append(temp.toString()).append(')'); - } - - public void forDbUpdate(String tableName, String[] pKeys, Object[] ids, Record record, StringBuilder sql, List paras) { - tableName = tableName.trim(); - trimPrimaryKeys(pKeys); - - sql.append("update ").append(tableName).append(" set "); - for (Map.Entry e: record.getColumns().entrySet()) { - String colName = e.getKey(); - if (!isPrimaryKey(colName, pKeys)) { - if (paras.size() > 0) { - sql.append(", "); - } - sql.append(colName).append(" = ? "); - paras.add(e.getValue()); - } - } - sql.append(" where "); - for (int i=0; i 0) { - sql.append(" and "); - } - sql.append(pKeys[i]).append(" = ?"); - paras.add(ids[i]); - } - } - - /** - * sql.replaceFirst("(?i)select", "") 正则中带有 "(?i)" 前缀,指定在匹配时不区分大小写 - */ - public String forPaginate(int pageNumber, int pageSize, StringBuilder findSql) { - int end = pageNumber * pageSize; - if (end <= 0) { - end = pageSize; - } - int begin = (pageNumber - 1) * pageSize; - if (begin < 0) { - begin = 0; - } - StringBuilder ret = new StringBuilder(); - ret.append(String.format("select skip %s first %s ", begin+"",pageSize+"")); - ret.append(findSql.toString().replaceFirst("(?i)select", "")); - return ret.toString(); - } - - public void fillStatement(PreparedStatement pst, List paras) throws SQLException { - fillStatementHandleDateType(pst, paras); - } - - public void fillStatement(PreparedStatement pst, Object... paras) throws SQLException { - fillStatementHandleDateType(pst, paras); - } +package io.jboot.db.dialect; + +import com.jfinal.plugin.activerecord.Record; +import com.jfinal.plugin.activerecord.Table; +import com.jfinal.plugin.activerecord.dialect.Dialect; +import io.jboot.db.model.Column; +import io.jboot.db.model.Join; +import io.jboot.db.model.SqlBuilder; +import io.jboot.exception.JbootException; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class JbootInformixDialect extends Dialect implements JbootDialect { + + + @Override + public String forTableBuilderDoBuild(String tableName) { + return "select * from " + tableName + " where 1 = 2"; + } + + @Override + public void forModelSave(Table table, Map attrs, StringBuilder sql, List paras) { + sql.append("insert into ").append(table.getName()).append('('); + StringBuilder temp = new StringBuilder(") values("); + for (Map.Entry e : attrs.entrySet()) { + String colName = e.getKey(); + if (table.hasColumnLabel(colName)) { + if (paras.size() > 0) { + sql.append(", "); + temp.append(", "); + } + sql.append(colName); + temp.append('?'); + paras.add(e.getValue()); + } + } + sql.append(temp).append(')'); + } + + @Override + public String forModelDeleteById(Table table) { + String[] pKeys = table.getPrimaryKey(); + StringBuilder sql = new StringBuilder(45); + sql.append("delete from "); + sql.append(table.getName()); + sql.append(" where "); + for (int i = 0; i < pKeys.length; i++) { + if (i > 0) { + sql.append(" and "); + } + sql.append(pKeys[i]).append(" = ?"); + } + return sql.toString(); + } + + @Override + public void forModelUpdate(Table table, Map attrs, Set modifyFlag, StringBuilder sql, List paras) { + sql.append("update ").append(table.getName()).append(" set "); + String[] pKeys = table.getPrimaryKey(); + for (Map.Entry e : attrs.entrySet()) { + String colName = e.getKey(); + if (modifyFlag.contains(colName) && !isPrimaryKey(colName, pKeys) && table.hasColumnLabel(colName)) { + if (paras.size() > 0) { + sql.append(", "); + } + sql.append(colName).append(" = ? "); + paras.add(e.getValue()); + } + } + sql.append(" where "); + for (int i = 0; i < pKeys.length; i++) { + if (i > 0) { + sql.append(" and "); + } + sql.append(pKeys[i]).append(" = ?"); + paras.add(attrs.get(pKeys[i])); + } + } + + @Override + public String forModelFindById(Table table, String columns) { + StringBuilder sql = new StringBuilder("select ").append(columns).append(" from "); + sql.append(table.getName()); + sql.append(" where "); + String[] pKeys = table.getPrimaryKey(); + for (int i = 0; i < pKeys.length; i++) { + if (i > 0) { + sql.append(" and "); + } + sql.append(pKeys[i]).append(" = ?"); + } + return sql.toString(); + } + + @Override + public String forDbFindById(String tableName, String[] pKeys) { + tableName = tableName.trim(); + trimPrimaryKeys(pKeys); + + StringBuilder sql = new StringBuilder("select * from ").append(tableName).append(" where "); + for (int i = 0; i < pKeys.length; i++) { + if (i > 0) { + sql.append(" and "); + } + sql.append(pKeys[i]).append(" = ?"); + } + return sql.toString(); + } + + @Override + public String forDbDeleteById(String tableName, String[] pKeys) { + tableName = tableName.trim(); + trimPrimaryKeys(pKeys); + + StringBuilder sql = new StringBuilder("delete from ").append(tableName).append(" where "); + for (int i = 0; i < pKeys.length; i++) { + if (i > 0) { + sql.append(" and "); + } + sql.append(pKeys[i]).append(" = ?"); + } + return sql.toString(); + } + + @Override + public void forDbSave(String tableName, String[] pKeys, Record record, StringBuilder sql, List paras) { + tableName = tableName.trim(); + trimPrimaryKeys(pKeys); + + sql.append("insert into "); + sql.append(tableName).append('('); + StringBuilder temp = new StringBuilder(); + temp.append(") values("); + + for (Map.Entry e : record.getColumns().entrySet()) { + if (paras.size() > 0) { + sql.append(", "); + temp.append(", "); + } + sql.append(e.getKey()); + temp.append('?'); + paras.add(e.getValue()); + } + sql.append(temp).append(')'); + } + + @Override + public void forDbUpdate(String tableName, String[] pKeys, Object[] ids, Record record, StringBuilder sql, List paras) { + tableName = tableName.trim(); + trimPrimaryKeys(pKeys); + + sql.append("update ").append(tableName).append(" set "); + for (Map.Entry e : record.getColumns().entrySet()) { + String colName = e.getKey(); + if (!isPrimaryKey(colName, pKeys)) { + if (paras.size() > 0) { + sql.append(", "); + } + sql.append(colName).append(" = ? "); + paras.add(e.getValue()); + } + } + sql.append(" where "); + for (int i = 0; i < pKeys.length; i++) { + if (i > 0) { + sql.append(" and "); + } + sql.append(pKeys[i]).append(" = ?"); + paras.add(ids[i]); + } + } + + /** + * sql.replaceFirst("(?i)select", "") 正则中带有 "(?i)" 前缀,指定在匹配时不区分大小写 + */ + @Override + public String forPaginate(int pageNumber, int pageSize, StringBuilder findSql) { +// int end = pageNumber * pageSize; +// if (end <= 0) { +// end = pageSize; +// } + + int begin = (pageNumber - 1) * pageSize; + if (begin < 0) { + begin = 0; + } + +// StringBuilder ret = new StringBuilder(); +// ret.append(String.format("select skip %s first %s ", begin + "", pageSize + "")); +// ret.append(findSql.toString().replaceFirst("(?i)select", "")); + + StringBuilder ret = new StringBuilder("select skip "); + ret.append(begin).append(" first ").append(pageSize); + ret.append(findSql, 6, findSql.length()); + return ret.toString(); + } + + @Override + public void fillStatement(PreparedStatement pst, List paras) throws SQLException { + fillStatementHandleDateType(pst, paras); + } + + @Override + public void fillStatement(PreparedStatement pst, Object... paras) throws SQLException { + fillStatementHandleDateType(pst, paras); + } + + + //for jbootDialect ----------------- + @Override + public String forFindByColumns(String alias, List joins, String table, String loadColumns, List columns, String orderBy, Object limit) { + StringBuilder sqlBuilder = SqlBuilder.forFindByColumns(alias, joins, table, loadColumns, columns, orderBy, ' '); + if (limit == null) { + return sqlBuilder.toString(); + } + + if (limit instanceof Number) { + StringBuilder ret = new StringBuilder("select first "); + ret.append(limit).append(" "); + ret.append(sqlBuilder, 6, sqlBuilder.length()); + return ret.toString(); + } else if (limit instanceof String && limit.toString().contains(",")) { + String[] startAndEnd = limit.toString().split(","); + String start = startAndEnd[0]; + String size = startAndEnd[1]; + + StringBuilder ret = new StringBuilder("select skip "); + ret.append(start).append(" first ").append(size); + ret.append(sqlBuilder, 6, sqlBuilder.length()); + return ret.toString(); + } else { + throw new JbootException("sql limit is error!,limit must is Number of String like \"0,10\""); + } + + } + + @Override + public String forFindCountByColumns(String alias, List joins, String table, String loadColumns, List columns) { + return SqlBuilder.forFindCountByColumns(alias, joins, table, loadColumns, columns, ' '); + } + + @Override + public String forDeleteByColumns(String alias, List joins, String table, List columns) { + return SqlBuilder.forDeleteByColumns(alias, joins, table, columns, ' '); + } + + @Override + public String forPaginateSelect(String loadColumns) { + return "select " + loadColumns; + } + + + @Override + public String forPaginateFrom(String alias, List joins, String table, List columns, String orderBy) { + return SqlBuilder.forPaginateFrom(alias, joins, table, columns, orderBy, ' '); + } + + @Override + public String forPaginateTotalRow(String select, String sqlExceptSelect, Object ext) { + String distinctSql = SqlBuilder.forPaginateDistinctTotalRow(select, sqlExceptSelect, ext); + return distinctSql != null ? distinctSql : super.forPaginateTotalRow(select, sqlExceptSelect, ext); + } + } \ No newline at end of file -- Gitee From fc7bde2087be158095ea372ed9bf0253905c3052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 17 Mar 2022 14:47:30 +0800 Subject: [PATCH 1624/1965] Add: Informix dialect --- .../app/config/support/apollo/ApolloConfigManager.java | 5 ++--- .../app/config/support/apollo/ApolloServerConfig.java | 10 +++++----- src/main/java/io/jboot/db/driver/DriverClassNames.java | 1 + 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java b/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java index 37840d58..5bc1ae10 100644 --- a/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java +++ b/src/main/java/io/jboot/app/config/support/apollo/ApolloConfigManager.java @@ -61,7 +61,6 @@ public class ApolloConfigManager { properties.put(name,value); } configManager.setRemoteProperties(properties); - } config.addChangeListener(changeEvent -> { @@ -81,8 +80,8 @@ public class ApolloConfigManager { private Config getDefaultConfig(JbootConfigManager configManager) { ApolloServerConfig apolloServerConfig = configManager.get(ApolloServerConfig.class); - if (JbootConfigKit.isNotBlank(apolloServerConfig.getDefaultNamespace())) { - return ConfigService.getConfig(apolloServerConfig.getDefaultNamespace()); + if (JbootConfigKit.isNotBlank(apolloServerConfig.getNamespace())) { + return ConfigService.getConfig(apolloServerConfig.getNamespace()); } else { return ConfigService.getAppConfig(); } diff --git a/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java b/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java index c4f36456..dc0bdfd3 100644 --- a/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java +++ b/src/main/java/io/jboot/app/config/support/apollo/ApolloServerConfig.java @@ -24,7 +24,7 @@ public class ApolloServerConfig { private boolean enable = false; private String meta; private String appId; - private String defaultNamespace; + private String namespace; public boolean isEnable() { @@ -51,12 +51,12 @@ public class ApolloServerConfig { this.meta = meta; } - public String getDefaultNamespace() { - return defaultNamespace; + public String getNamespace() { + return namespace; } - public void setDefaultNamespace(String defaultNamespace) { - this.defaultNamespace = defaultNamespace; + public void setNamespace(String namespace) { + this.namespace = namespace; } public boolean isConfigOk() { diff --git a/src/main/java/io/jboot/db/driver/DriverClassNames.java b/src/main/java/io/jboot/db/driver/DriverClassNames.java index c698890c..0086d8b5 100644 --- a/src/main/java/io/jboot/db/driver/DriverClassNames.java +++ b/src/main/java/io/jboot/db/driver/DriverClassNames.java @@ -33,6 +33,7 @@ public class DriverClassNames { driverClassNames.put(DataSourceConfig.TYPE_SQLITE, new String[]{"org.sqlite.JDBC"}); driverClassNames.put(DataSourceConfig.TYPE_POSTGRESQL, new String[]{"org.postgresql.Driver"}); driverClassNames.put(DataSourceConfig.TYPE_CLICKHOUSE, new String[]{"com.github.housepower.jdbc.ClickHouseDriver", "ru.yandex.clickhouse.ClickHouseDriver"}); + driverClassNames.put(DataSourceConfig.TYPE_INFORMIX, new String[]{"com.informix.jdbc.IfxDriver"}); } -- Gitee From a6886ef2c3a19fce1606f0e684c392f5de99966c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 18 Mar 2022 09:47:41 +0800 Subject: [PATCH 1625/1965] GatewayConfig.interceptors support config spiName --- .../java/io/jboot/components/gateway/JbootGatewayConfig.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java index 502b395c..2db17662 100644 --- a/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java +++ b/src/main/java/io/jboot/components/gateway/JbootGatewayConfig.java @@ -15,6 +15,7 @@ */ package io.jboot.components.gateway; +import io.jboot.core.spi.JbootSpiLoader; import io.jboot.utils.ArrayUtil; import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; @@ -321,7 +322,8 @@ public class JbootGatewayConfig implements Serializable { } else { gatewayInterceptors = new GatewayInterceptor[interceptors.length]; for (int i = 0; i < interceptors.length; i++) { - GatewayInterceptor interceptor = ClassUtil.newInstance(interceptors[i]); +// GatewayInterceptor interceptor = ClassUtil.newInstance(interceptors[i]); + GatewayInterceptor interceptor = JbootSpiLoader.load(GatewayInterceptor.class, interceptors[i]); if (interceptor == null) { throw new NullPointerException("can not new instance by class:" + interceptors[i]); } -- Gitee From 79260539fbc8a6dfa04e8e3f997a91851a3d5881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 18 Mar 2022 13:11:52 +0800 Subject: [PATCH 1626/1965] add JsonUtil.java --- src/main/java/io/jboot/utils/JsonUtil.java | 239 ++++++++++++++++++ src/main/java/io/jboot/utils/ObjectUtil.java | 13 +- .../web/json/JsonBodyParseInterceptor.java | 15 +- 3 files changed, 261 insertions(+), 6 deletions(-) create mode 100644 src/main/java/io/jboot/utils/JsonUtil.java diff --git a/src/main/java/io/jboot/utils/JsonUtil.java b/src/main/java/io/jboot/utils/JsonUtil.java new file mode 100644 index 00000000..7a3ab948 --- /dev/null +++ b/src/main/java/io/jboot/utils/JsonUtil.java @@ -0,0 +1,239 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.utils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.jfinal.kit.LogKit; +import io.jboot.web.json.JsonBodyParseInterceptor; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; + +public class JsonUtil { + + public static String getString(String json, String key) { + return get(json, key, String.class); + } + + public static String getString(Object jsonObjectOrArray, String key) { + return get(jsonObjectOrArray, key, String.class); + } + + public static String getString(String json, String key, String defaultValue) { + String value = getString(json, key); + return value != null ? value : defaultValue; + } + + public static String getString(Object jsonObjectOrArray, String key, String defaultValue) { + String value = getString(jsonObjectOrArray, key); + return value != null ? value : defaultValue; + } + + public static Integer getInt(String json, String key) { + return get(json, key, Integer.class); + } + + public static Integer getInt(Object jsonObjectOrArray, String key) { + return get(jsonObjectOrArray, key, Integer.class); + } + + public static int getInt(String json, String key, int defaultValue) { + Integer value = getInt(json, key); + return value != null ? value : defaultValue; + } + + public static int getInt(Object jsonObjectOrArray, String key, int defaultValue) { + Integer value = getInt(jsonObjectOrArray, key); + return value != null ? value : defaultValue; + } + + public static Float getFloat(String json, String key) { + return get(json, key, Float.class); + } + + public static Float getFloat(Object jsonObjectOrArray, String key) { + return get(jsonObjectOrArray, key, Float.class); + } + + public static float getFloat(String json, String key, float defaultValue) { + Float value = getFloat(json, key); + return value != null ? value : defaultValue; + } + + public static float getFloat(Object jsonObjectOrArray, String key, float defaultValue) { + Float value = getFloat(jsonObjectOrArray, key); + return value != null ? value : defaultValue; + } + + public static Double getDouble(String json, String key) { + return get(json, key, Double.class); + } + + public static Double getDouble(Object jsonObjectOrArray, String key) { + return get(jsonObjectOrArray, key, Double.class); + } + + public static double getDouble(String json, String key, double defaultValue) { + Double value = getDouble(json, key); + return value != null ? value : defaultValue; + } + + public static double getDouble(Object jsonObjectOrArray, String key, double defaultValue) { + Double value = getDouble(jsonObjectOrArray, key); + return value != null ? value : defaultValue; + } + + public static Long getLong(String json, String key) { + return get(json, key, Long.class); + } + + public static Long getLong(Object jsonObjectOrArray, String key) { + return get(jsonObjectOrArray, key, Long.class); + } + + public static long getLong(String json, String key, long defaultValue) { + Long value = getLong(json, key); + return value != null ? value : defaultValue; + } + + public static long getLong(Object jsonObjectOrArray, String key, long defaultValue) { + Long value = getLong(jsonObjectOrArray, key); + return value != null ? value : defaultValue; + } + + public static BigInteger getBigInteger(String json, String key) { + return get(json, key, BigInteger.class); + } + + public static BigInteger getBigInteger(Object jsonObjectOrArray, String key) { + return get(jsonObjectOrArray, key, BigInteger.class); + } + + public static BigInteger getBigInteger(String json, String key, BigInteger defaultValue) { + BigInteger value = getBigInteger(json, key); + return value != null ? value : defaultValue; + } + + + public static BigInteger getBigInteger(Object jsonObjectOrArray, String key, BigInteger defaultValue) { + BigInteger value = getBigInteger(jsonObjectOrArray, key); + return value != null ? value : defaultValue; + } + + public static BigDecimal getBigDecimal(String json, String key) { + return get(json, key, BigDecimal.class); + } + + public static BigDecimal getBigDecimal(Object jsonObjectOrArray, String key) { + return get(jsonObjectOrArray, key, BigDecimal.class); + } + + public static BigDecimal getBigDecimal(String json, String key, BigDecimal defaultValue) { + BigDecimal value = getBigDecimal(json, key); + return value != null ? value : defaultValue; + } + + + public static BigDecimal getBigDecimal(Object jsonObjectOrArray, String key, BigDecimal defaultValue) { + BigDecimal value = getBigDecimal(jsonObjectOrArray, key); + return value != null ? value : defaultValue; + } + + + public static Date getDate(String json, String key) { + return get(json, key, Date.class); + } + + public static Date getDate(Object jsonObjectOrArray, String key) { + return get(jsonObjectOrArray, key, Date.class); + } + + + public static JSONObject getJSONObject(String json, String key) { + return get(json, key, JSONObject.class); + } + + public static JSONObject getJSONObject(Object jsonObjectOrArray, String key) { + return get(jsonObjectOrArray, key, JSONObject.class); + } + + + public static JSONArray getJSONArray(String json, String key) { + return get(json, key, JSONArray.class); + } + + public static JSONArray getJSONArray(Object jsonObjectOrArray, String key) { + return get(jsonObjectOrArray, key, JSONArray.class); + } + + + public static T get(String json, String key, Class clazz) { + if (StrUtil.isBlank(json)) { + return null; + } + try { + Object parse = JSON.parse(json); + return get(parse, key, clazz); + } catch (Exception e) { + LogKit.error(e.toString(), e); + } + return null; + } + + public static T get(Object jsonObjectOrArray, String key, Class clazz) { + if (jsonObjectOrArray == null) { + return null; + } + try { + return (T) JsonBodyParseInterceptor.parseJsonBody(jsonObjectOrArray, clazz, clazz, key); + } catch (Exception e) { + LogKit.error(e.toString(), e); + } + return null; + } + + + public static T get(String json, String key, TypeDef typeDef) { + if (StrUtil.isBlank(json)) { + return null; + } + try { + Object parse = JSON.parse(json); + return get(parse, key, typeDef); + } catch (Exception e) { + LogKit.error(e.toString(), e); + } + return null; + } + + + public static T get(Object jsonObjectOrArray, String key, TypeDef typeDef) { + if (jsonObjectOrArray == null) { + return null; + } + try { + return (T) JsonBodyParseInterceptor.parseJsonBody(jsonObjectOrArray, typeDef.getDefClass(), typeDef.getType(), key); + } catch (Exception e) { + LogKit.error(e.toString(), e); + } + return null; + } + + +} diff --git a/src/main/java/io/jboot/utils/ObjectUtil.java b/src/main/java/io/jboot/utils/ObjectUtil.java index 87e749d6..7bae78f8 100644 --- a/src/main/java/io/jboot/utils/ObjectUtil.java +++ b/src/main/java/io/jboot/utils/ObjectUtil.java @@ -139,8 +139,9 @@ public class ObjectUtil { if (value.getClass().isAssignableFrom(targetClass)) { return value; } - - if (targetClass == Integer.class || targetClass == int.class) { + if (targetClass == String.class) { + return value.toString(); + } else if (targetClass == Integer.class || targetClass == int.class) { if (value instanceof Number) { return ((Number) value).intValue(); } @@ -198,7 +199,13 @@ public class ObjectUtil { if (value instanceof Number) { return new Date(((Number) value).longValue()); } - return DateUtil.parseDate(value.toString()); + + String s = value.toString(); + if (StrUtil.isNumeric(s)) { + return new Date(Long.parseLong(s)); + } + + return DateUtil.parseDate(s); } public static Object getPrimitiveDefaultValue(Class paraClass) { diff --git a/src/main/java/io/jboot/web/json/JsonBodyParseInterceptor.java b/src/main/java/io/jboot/web/json/JsonBodyParseInterceptor.java index 15587107..7205874e 100644 --- a/src/main/java/io/jboot/web/json/JsonBodyParseInterceptor.java +++ b/src/main/java/io/jboot/web/json/JsonBodyParseInterceptor.java @@ -162,11 +162,16 @@ public class JsonBodyParseInterceptor implements Interceptor, InterceptorBuilder return paraClass.isPrimitive() ? ObjectUtil.getPrimitiveDefaultValue(paraClass) : null; } + if (paraClass == String.class && paraClass == paraType) { + return result.toString(); + } + + // JSONObject 类型 if (result instanceof JSONObject) { return toJavaObject((JSONObject) result, paraClass, paraType); - } else { - return ObjectUtil.convert(result, paraClass); } + + return ObjectUtil.convert(result, paraClass); } @@ -225,6 +230,11 @@ public class JsonBodyParseInterceptor implements Interceptor, InterceptorBuilder return new HashSet<>(jsonArray); } + //直接获取 JsonArray + if (typeClass == type && typeClass == JSONArray.class) { + return jsonArray; + } + return jsonArray.toJavaObject(type); } @@ -272,7 +282,6 @@ public class JsonBodyParseInterceptor implements Interceptor, InterceptorBuilder } - @Override public void build(Class targetClass, Method method, Interceptors interceptors) { if (Util.isController(targetClass)) { -- Gitee From a2ca1d9d7e568cf8e0e7183d6ce05719d84740fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 18 Mar 2022 19:17:54 +0800 Subject: [PATCH 1627/1965] optimize .... --- .../java/io/jboot/core/JbootCoreConfig.java | 8 +++++ .../java/io/jboot/utils/AnnotationUtil.java | 15 +++++++++ src/main/java/io/jboot/utils/JsonUtil.java | 11 +++++++ src/main/java/io/jboot/utils/NetUtil.java | 15 +++++++++ src/main/java/io/jboot/utils/TypeDef.java | 32 +++++++++++++++++++ 5 files changed, 81 insertions(+) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 11a45872..cd07d1dd 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -204,6 +204,13 @@ public class JbootCoreConfig extends JFinalConfig { routeList.addAll(routes.getRouteItemList()); } + private String removeLastSlash(String path) { + while (path.endsWith("/")) { + path = path.substring(0, path.length() - 1); + } + return path; + } + public static String[] getMappingAndViewPath(Class clazz) { RequestMapping rm = clazz.getAnnotation(RequestMapping.class); if (rm != null) { @@ -236,6 +243,7 @@ public class JbootCoreConfig extends JFinalConfig { path = AnnotationUtil.get(path); } + path = removeLastSlash(path); viewPath = AnnotationUtil.get(viewPath); if (Path.NULL_VIEW_PATH.equals(viewPath)) { diff --git a/src/main/java/io/jboot/utils/AnnotationUtil.java b/src/main/java/io/jboot/utils/AnnotationUtil.java index 10e8916f..daa1f02b 100644 --- a/src/main/java/io/jboot/utils/AnnotationUtil.java +++ b/src/main/java/io/jboot/utils/AnnotationUtil.java @@ -1,3 +1,18 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.jboot.utils; diff --git a/src/main/java/io/jboot/utils/JsonUtil.java b/src/main/java/io/jboot/utils/JsonUtil.java index 7a3ab948..9ac36ea6 100644 --- a/src/main/java/io/jboot/utils/JsonUtil.java +++ b/src/main/java/io/jboot/utils/JsonUtil.java @@ -235,5 +235,16 @@ public class JsonUtil { return null; } + public static Object getJsonObjectOrArray(String json) { + if (StrUtil.isNotBlank(json)) { + try { + return JSON.parse(json); + } catch (Exception e) { + LogKit.error(e.toString(), e); + } + } + return null; + } + } diff --git a/src/main/java/io/jboot/utils/NetUtil.java b/src/main/java/io/jboot/utils/NetUtil.java index 5c9fc4f7..3378c2b9 100644 --- a/src/main/java/io/jboot/utils/NetUtil.java +++ b/src/main/java/io/jboot/utils/NetUtil.java @@ -1,3 +1,18 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.jboot.utils; import java.net.InetAddress; diff --git a/src/main/java/io/jboot/utils/TypeDef.java b/src/main/java/io/jboot/utils/TypeDef.java index 1efe6893..da4b7f83 100644 --- a/src/main/java/io/jboot/utils/TypeDef.java +++ b/src/main/java/io/jboot/utils/TypeDef.java @@ -1,3 +1,18 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.jboot.utils; import java.lang.reflect.ParameterizedType; @@ -6,16 +21,33 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.util.List; import java.util.Map; +import java.util.Set; public class TypeDef { public static final TypeDef LIST_STRING = new TypeDef>() {}; public static final TypeDef LIST_INTEGER = new TypeDef>() {}; + public static final TypeDef LIST_LONG = new TypeDef>() {}; + public static final TypeDef LIST_DOUBLE = new TypeDef>() {}; + public static final TypeDef LIST_FLOAT= new TypeDef>() {}; public static final TypeDef LIST_BIGINTEGER = new TypeDef>() {}; public static final TypeDef LIST_BIGDECIMAL = new TypeDef>() {}; + + public static final TypeDef SET_STRING = new TypeDef>() {}; + public static final TypeDef SET_INTEGER = new TypeDef>() {}; + public static final TypeDef SET_LONG = new TypeDef>() {}; + public static final TypeDef SET_DOUBLE = new TypeDef>() {}; + public static final TypeDef SET_FLOAT = new TypeDef>() {}; + public static final TypeDef SET_BIGINTEGER = new TypeDef>() {}; + public static final TypeDef SET_BIGDECIMAL = new TypeDef>() {}; + + public static final TypeDef MAP_STRING = new TypeDef>() {}; public static final TypeDef MAP_INTEGER = new TypeDef>() {}; + public static final TypeDef MAP_LONG = new TypeDef>() {}; + public static final TypeDef MAP_DOUBLE = new TypeDef>() {}; + public static final TypeDef MAP_FLOAT= new TypeDef>() {}; public static final TypeDef MAP_BIGINTEGER = new TypeDef>() {}; public static final TypeDef MAP_BIGDECIMAL = new TypeDef>() {}; -- Gitee From af3cff1527bf3b9cada60fcf58a83c4f1a4a6d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 19 Mar 2022 13:15:33 +0800 Subject: [PATCH 1628/1965] optimize JsonUtil.java --- src/main/java/io/jboot/utils/JsonUtil.java | 99 +++++++++++++++++----- 1 file changed, 78 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/jboot/utils/JsonUtil.java b/src/main/java/io/jboot/utils/JsonUtil.java index 9ac36ea6..651ae208 100644 --- a/src/main/java/io/jboot/utils/JsonUtil.java +++ b/src/main/java/io/jboot/utils/JsonUtil.java @@ -25,6 +25,52 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.util.Date; +/** + * 基于 FastJson,方便解析 Json 内容 + *

        + * 例如: + *

        + * { + * "array": [ + * 1, + * 2, + * 3 + * ], + * "type": true, + * "null": null, + * "number": 123, + * "object": { + * "a": "b", + * "c": "d", + * "e":1 + * }, + * "key": "welcome to CodeFormat.CN" + * } + * + * Boolean type = JsonUtil.getBool(json,"type"); + * //type == true + * + * int e = JsonUtil.getInt(json,"object.e") + * // e == 1 + * + * BigInteger n = JsonUtil.getBigInteger("number") + * // n == 123 + * + * String[] array = JsonUtil.get(json,"array",String[].class) + * //array == ["1","2","3"] + * + * int[] array = JsonUtil.get(json,"array",int[].class) + * //array == [1,2,3] + * + * Map map = JsonUtil.get(json,"object",Map.class) + * //map == {"a":"b","c":"d","e":1} + * + * int x = JsonUtil.getInt(json,"array[1]"); + * // x == 2 + * + * String key = JsonUtil.getString(json,"key"); + * // key == "welcome to CodeFormat.CN" + */ public class JsonUtil { public static String getString(String json, String key) { @@ -45,6 +91,26 @@ public class JsonUtil { return value != null ? value : defaultValue; } + + public static Boolean getBool(String json, String key) { + return get(json, key, Boolean.class); + } + + public static Boolean getBool(Object jsonObjectOrArray, String key) { + return get(jsonObjectOrArray, key, Boolean.class); + } + + public static boolean getBool(String json, String key, boolean defaultValue) { + Boolean value = getBool(json, key); + return value != null ? value : defaultValue; + } + + public static boolean getBool(Object jsonObjectOrArray, String key, boolean defaultValue) { + Boolean value = getBool(jsonObjectOrArray, key); + return value != null ? value : defaultValue; + } + + public static Integer getInt(String json, String key) { return get(json, key, Integer.class); } @@ -164,6 +230,16 @@ public class JsonUtil { return get(jsonObjectOrArray, key, Date.class); } + public static Date getDate(String json, String key, Date defaultValue) { + Date date = get(json, key, Date.class); + return date != null ? date : defaultValue; + } + + public static Date getDate(Object jsonObjectOrArray, String key, Date defaultValue) { + Date date = get(jsonObjectOrArray, key, Date.class); + return date != null ? date : defaultValue; + } + public static JSONObject getJSONObject(String json, String key) { return get(json, key, JSONObject.class); @@ -173,7 +249,6 @@ public class JsonUtil { return get(jsonObjectOrArray, key, JSONObject.class); } - public static JSONArray getJSONArray(String json, String key) { return get(json, key, JSONArray.class); } @@ -184,16 +259,7 @@ public class JsonUtil { public static T get(String json, String key, Class clazz) { - if (StrUtil.isBlank(json)) { - return null; - } - try { - Object parse = JSON.parse(json); - return get(parse, key, clazz); - } catch (Exception e) { - LogKit.error(e.toString(), e); - } - return null; + return get(getJsonObjectOrArray(json), key, clazz); } public static T get(Object jsonObjectOrArray, String key, Class clazz) { @@ -210,16 +276,7 @@ public class JsonUtil { public static T get(String json, String key, TypeDef typeDef) { - if (StrUtil.isBlank(json)) { - return null; - } - try { - Object parse = JSON.parse(json); - return get(parse, key, typeDef); - } catch (Exception e) { - LogKit.error(e.toString(), e); - } - return null; + return get(getJsonObjectOrArray(json), key, typeDef); } -- Gitee From b3c6a7dfb3ebc501f64ccfc968675f3163442d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 19 Mar 2022 13:39:41 +0800 Subject: [PATCH 1629/1965] v3.14.2 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index eed3bd8f..3dc62a04 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.14.1 + 3.14.2 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 16285283..7cc500aa 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.14.1 + 3.14.2 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 4b852ac9..02c76b0a 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.14.1 + 3.14.2 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.14.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.14.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index a0733919..f0b8fcb7 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.14.1', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.14.2', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.1/jboot-3.14.1.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.2/jboot-3.14.2.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 13a82c32..06fd74b1 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.14.1 + 3.14.2 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 13c65002..30872d11 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.14.1"; + public static String VERSION = "3.14.2"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From fa1e9473fe608be92e615383c8b7ca9a713891a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 19 Mar 2022 13:40:00 +0800 Subject: [PATCH 1630/1965] v3.14.2 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 13 +++++++++++++ pom.xml | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 42f11a0e..28d000cf 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.14.1 + 3.14.2 ``` diff --git a/changes.txt b/changes.txt index cb399d48..07b9a748 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,16 @@ +jboot v3.14.2: +新增:Informix 数据库方言,感谢 @xujianxie 同学 +新增:JbootModel 新增 findByIdWithoutCache() 方法 +新增:新增 JbootModel 的 CPI 类,用于开放保护方法 +新增:Nacos 远程配置中心添加多实例的配置支持 +新增:网关拦截器 GatewayConfig.interceptors 添加 SPI 名称配置的支持 +新增:JsonUtil.java 方便解析 Json 数据 +优化:ValidUtil.java,支持在非自定义消息的时候输出拦截的列名 +优化:ClassScanner.java 默认关闭 Class 扫描信息在控制台输出 +优化:修改缓存拦截器的默认权重为 100,当前情况下拦截器在最后执行。 + + + jboot v3.14.1: 新增:注解 @Table(datasource="xxx") 新增对数据源的配置支持 新增:添加 jboo.app.listenerPackage 配置支持,用于只扫描哪些包的 listener diff --git a/pom.xml b/pom.xml index ebdeab31..ade507a7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.2-SNAPSHOT + 3.14.2 jar jboot -- Gitee From 33648504e840cd4e053d82b87f0ac95d55150951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 19 Mar 2022 13:49:13 +0800 Subject: [PATCH 1631/1965] v3.14.2 release (^.^)YYa!! --- pom.xml | 4 ++-- src/main/java/io/jboot/core/JbootCoreConfig.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index ade507a7..36668cb9 100644 --- a/pom.xml +++ b/pom.xml @@ -47,7 +47,7 @@ 3.4 3.3.0 4.0.3 - 2.13.1 + 2.13.2 5.1.49 2.2.16.Final 1.7.36 @@ -71,7 +71,7 @@ 3.13.2.Final 1.4.2 1.1.8 - 4.2.8 + 4.2.9 1.8 1.8 diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index cd07d1dd..2778ac1d 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -205,7 +205,7 @@ public class JbootCoreConfig extends JFinalConfig { } private String removeLastSlash(String path) { - while (path.endsWith("/")) { + while (path.endsWith("/") && path.length() > 1) { path = path.substring(0, path.length() - 1); } return path; -- Gitee From 8ea0aebb367f24f28c59894b6a9d962b75aa2766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 25 Mar 2022 09:03:08 +0800 Subject: [PATCH 1632/1965] v3.14.3 snapshot --- pom.xml | 2 +- .../java/io/jboot/db/model/JbootModel.java | 33 ++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 36668cb9..ebaaaa1e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.2 + 3.14.3-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 463aea1d..cfb5a849 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -427,16 +427,25 @@ public class JbootModel> extends Model { public boolean deleteByColumn(Column column) { + if (column == null || !column.checkAvailable()) { + throw new IllegalArgumentException("Column or value must not be null."); + } return deleteByColumns(Arrays.asList(column)); } public boolean deleteByColumns(Columns columns) { + if (columns == null || columns.isEmpty()) { + throw new IllegalArgumentException("Columns must not be null or empty."); + } return deleteByColumns(columns.getList()); } public boolean deleteByColumns(List columns) { + if (columns == null || columns.isEmpty()) { + throw new IllegalArgumentException("Columns must not be null or empty."); + } String sql = _getDialect().forDeleteByColumns(alias, joins, _getTableName(), columns); return Db.use(_getConfig().getName()).update(sql, Util.getValueArray(columns)) >= 1; } @@ -533,7 +542,8 @@ public class JbootModel> extends Model { public M findFirstByColumn(Column column) { if (column == null || !column.checkAvailable()) { - throw new IllegalArgumentException("Column or value must not be null."); +// throw new IllegalArgumentException("Column or value must not be null."); + return null; } return findFirstByColumns(Columns.create(column)); } @@ -541,7 +551,8 @@ public class JbootModel> extends Model { public M findFirstByColumn(Column column, String orderBy) { if (column == null || !column.checkAvailable()) { - throw new IllegalArgumentException("Column or value must not be null."); +// throw new IllegalArgumentException("Column or value must not be null."); + return null; } return findFirstByColumns(Columns.create(column), orderBy); } @@ -652,6 +663,10 @@ public class JbootModel> extends Model { } public List findListByColumn(Column column, String orderBy, Integer count) { + if (column == null || !column.checkAvailable()) { +// throw new IllegalArgumentException("Column or value must not be null."); + return null; + } return findListByColumns(Columns.create(column), orderBy, count); } @@ -693,10 +708,10 @@ public class JbootModel> extends Model { loadColumns = this.loadColumns; } if (StrUtil.isBlank(loadColumns)) { + String distinctColumn = get(JbootModelExts.DISTINCT); //使用 distinct - if (hasAnyJoinEffective() && StrUtil.isNotBlank(get(JbootModelExts.DISTINCT))) { - loadColumns = "DISTINCT " + get(JbootModelExts.DISTINCT) - + "," + (StrUtil.isNotBlank(alias) ? alias : _getTableName()) + ".*"; + if (hasAnyJoinEffective() && StrUtil.isNotBlank(distinctColumn)) { + loadColumns = "DISTINCT " + distinctColumn + "," + (StrUtil.isNotBlank(alias) ? alias : _getTableName()) + ".*"; } //未使用 distinct else { @@ -770,10 +785,12 @@ public class JbootModel> extends Model { } if (StrUtil.isBlank(loadColumns)) { + + String distinctColumn = get(JbootModelExts.DISTINCT); + //使用 distinct - if (hasAnyJoinEffective() && StrUtil.isNotBlank(get(JbootModelExts.DISTINCT))) { - loadColumns = "DISTINCT " + get(JbootModelExts.DISTINCT) - + "," + (StrUtil.isNotBlank(alias) ? alias : _getTableName()) + ".*"; + if (hasAnyJoinEffective() && StrUtil.isNotBlank(distinctColumn)) { + loadColumns = "DISTINCT " + distinctColumn + "," + (StrUtil.isNotBlank(alias) ? alias : _getTableName()) + ".*"; } //未使用 distinct else { -- Gitee From c9952eaff33f385657c13d68d4bb07620e6b0518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 25 Mar 2022 09:17:20 +0800 Subject: [PATCH 1633/1965] v3.14.3 snapshot --- src/main/java/io/jboot/db/ArpManager.java | 7 +++---- src/main/java/io/jboot/db/TableInfo.java | 12 ++++++------ src/main/java/io/jboot/db/TableInfoManager.java | 4 ++-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/jboot/db/ArpManager.java b/src/main/java/io/jboot/db/ArpManager.java index c75e3ab1..2293e2a1 100644 --- a/src/main/java/io/jboot/db/ArpManager.java +++ b/src/main/java/io/jboot/db/ArpManager.java @@ -18,7 +18,6 @@ package io.jboot.db; import com.jfinal.plugin.activerecord.ActiveRecordPlugin; import com.jfinal.plugin.activerecord.CaseInsensitiveContainerFactory; import com.jfinal.plugin.activerecord.IDbProFactory; -import com.jfinal.plugin.activerecord.Model; import com.jfinal.plugin.activerecord.dialect.Dialect; import io.jboot.Jboot; import io.jboot.components.cache.JbootCache; @@ -88,9 +87,9 @@ public class ArpManager { for (TableInfo table : tableInfos) { String tableName = StrUtil.isNotBlank(dataSourceConfig.getTablePrefix()) ? dataSourceConfig.getTablePrefix() + table.getTableName() : table.getTableName(); if (StrUtil.isNotBlank(table.getPrimaryKey())) { - activeRecordPlugin.addMapping(tableName, table.getPrimaryKey(), (Class>) table.getModelClass()); + activeRecordPlugin.addMapping(tableName, table.getPrimaryKey(), table.getModelClass()); } else { - activeRecordPlugin.addMapping(tableName, (Class>) table.getModelClass()); + activeRecordPlugin.addMapping(tableName, table.getModelClass()); } } } @@ -111,7 +110,7 @@ public class ArpManager { if (StrUtil.isNotBlank(config.getDbProFactory())) { IDbProFactory dbProFactory = Objects.requireNonNull(ClassUtil.newInstance(config.getDbProFactory()), - "Can not create dbProfactory by class : " + config.getDbProFactory()); + "Can not create dbProfactory by class: " + config.getDbProFactory()); activeRecordPlugin.setDbProFactory(dbProFactory); } else { activeRecordPlugin.setDbProFactory(new JbootDbProFactory()); diff --git a/src/main/java/io/jboot/db/TableInfo.java b/src/main/java/io/jboot/db/TableInfo.java index d96dfb0b..995f5e71 100644 --- a/src/main/java/io/jboot/db/TableInfo.java +++ b/src/main/java/io/jboot/db/TableInfo.java @@ -32,7 +32,7 @@ public class TableInfo { private String tableName; private String primaryKey; - private Class modelClass; + private Class> modelClass; private String datasource; private Set datasourceNames; @@ -55,11 +55,11 @@ public class TableInfo { this.primaryKey = primaryKey; } - public Class getModelClass() { + public Class> getModelClass() { return modelClass; } - public void setModelClass(Class modelClass) { + public void setModelClass(Class> modelClass) { this.modelClass = modelClass; } @@ -82,7 +82,7 @@ public class TableInfo { /** - * 添加这个表存绑定的数据源 + * 添加数据源:让此表绑定数据源 * * @param dataSourceConfig * @param fromDesignated 是否是通过 jboot.datasource.table 或者 @table(datasource="xxx") 来指定的 @@ -92,7 +92,7 @@ public class TableInfo { this.attachedDatasources = new ArrayList<>(); } - // 若未指定数据源,且已经存在了指定数据源的 datasource + // 若当前表未指定数据源 (fromDesignated == false),且当前表已经存在了指定数据源的 datasource // 则不能再添加该数据源 if (!fromDesignated && !this.attachedDatasources.isEmpty()) { for (DataSourceConfigWrapper dataSourceConfigWrapper : this.attachedDatasources) { @@ -104,7 +104,7 @@ public class TableInfo { this.attachedDatasources.add(new DataSourceConfigWrapper(dataSourceConfig, fromDesignated)); - // 若数据源配置了指定的表(亦或者表配置了指定的数据源),那么需要移除哪些未指定的默认数据源 + // 若新添加的数据源,是配置了指定的表(亦或者表配置了指定的数据源),那么需要移除哪些未指定的默认数据源 if (fromDesignated) { for (DataSourceConfigWrapper dataSourceConfigWrapper : attachedDatasources) { if (!dataSourceConfigWrapper.fromDesignated) { diff --git a/src/main/java/io/jboot/db/TableInfoManager.java b/src/main/java/io/jboot/db/TableInfoManager.java index 066bd837..89db10c0 100644 --- a/src/main/java/io/jboot/db/TableInfoManager.java +++ b/src/main/java/io/jboot/db/TableInfoManager.java @@ -129,9 +129,9 @@ public class TableInfoManager { } - private void addTable(List tableInfoList, Class clazz, Table tb) { + private void addTable(List tableInfoList, Class modelClass, Table tb) { TableInfo tableInfo = new TableInfo(); - tableInfo.setModelClass(clazz); + tableInfo.setModelClass((Class>) modelClass); tableInfo.setPrimaryKey(AnnotationUtil.get(tb.primaryKey())); tableInfo.setTableName(AnnotationUtil.get(tb.tableName())); tableInfo.setDatasource(AnnotationUtil.get(tb.datasource())); -- Gitee From 7f14c9bf8b413c2b47e0de8b6ecb02111898fd7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 25 Mar 2022 11:12:47 +0800 Subject: [PATCH 1634/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9APriorDatas?= =?UTF-8?q?ource=20=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/ArpManager.java | 42 +++++++----- src/main/java/io/jboot/db/JbootDb.java | 4 +- src/main/java/io/jboot/db/TableInfo.java | 27 ++++---- .../java/io/jboot/db/TableInfoManager.java | 7 +- .../jboot/db/annotation/PriorDatasource.java | 33 ++++++++++ .../PriorDatasourceInterceptor.java | 65 +++++++++++++++++++ .../java/io/jboot/db/model/JbootModel.java | 32 +++++++-- 7 files changed, 171 insertions(+), 39 deletions(-) create mode 100644 src/main/java/io/jboot/db/annotation/PriorDatasource.java create mode 100644 src/main/java/io/jboot/db/datasource/PriorDatasourceInterceptor.java diff --git a/src/main/java/io/jboot/db/ArpManager.java b/src/main/java/io/jboot/db/ArpManager.java index 2293e2a1..bbfdaa95 100644 --- a/src/main/java/io/jboot/db/ArpManager.java +++ b/src/main/java/io/jboot/db/ArpManager.java @@ -18,6 +18,7 @@ package io.jboot.db; import com.jfinal.plugin.activerecord.ActiveRecordPlugin; import com.jfinal.plugin.activerecord.CaseInsensitiveContainerFactory; import com.jfinal.plugin.activerecord.IDbProFactory; +import com.jfinal.plugin.activerecord.Model; import com.jfinal.plugin.activerecord.dialect.Dialect; import io.jboot.Jboot; import io.jboot.components.cache.JbootCache; @@ -57,39 +58,36 @@ public class ArpManager { private ArpManager() { Map datasourceConfigs = DataSourceConfigManager.me().getDatasourceConfigs(); - createRecordPlugin(datasourceConfigs); + createdRecordPlugins(datasourceConfigs); } - private void createRecordPlugin(Map allConfigs) { + private void createdRecordPlugins(Map allConfigs) { - Map arpDatasourceConfigs = new HashMap<>(); + Map dsCache = new HashMap<>(); - for (Map.Entry entry : allConfigs.entrySet()) { - DataSourceConfig datasourceConfig = entry.getValue(); - if (datasourceConfig.isConfigOk()) { + // 优先初始化 默认数据源 + DataSourceConfig mainDataSourceConfig = allConfigs.remove(DataSourceConfig.NAME_DEFAULT); + initRecordPlugin(dsCache, mainDataSourceConfig); - // 执行 createRecordPlugin(...) 的时候,会同时完善 DataSourceConfig 里绑定的表数据 - // createRecordPlugin完毕后,就可以通过 dataSourceConfig.getTableInfos() 去获取该数据源有哪些表 - ActiveRecordPlugin activeRecordPlugin = createRecordPlugin(datasourceConfig); - arpDatasourceConfigs.put(System.identityHashCode(activeRecordPlugin), datasourceConfig); - activeRecordPlugins.add(activeRecordPlugin); - } + // 初始化默认数据源后,再开始初始化其他数据库 + for (Map.Entry entry : allConfigs.entrySet()) { + initRecordPlugin(dsCache, entry.getValue()); } - // 为 activeRecordPlugin 添加 jfinal 的表映射 + // 为所有的 activeRecordPlugin 添加 jfinal 的表映射 for (ActiveRecordPlugin activeRecordPlugin : activeRecordPlugins) { - DataSourceConfig dataSourceConfig = arpDatasourceConfigs.get(System.identityHashCode(activeRecordPlugin)); + DataSourceConfig dataSourceConfig = dsCache.get(System.identityHashCode(activeRecordPlugin)); List tableInfos = dataSourceConfig.getTableInfos(); if (tableInfos != null && !tableInfos.isEmpty()) { for (TableInfo table : tableInfos) { String tableName = StrUtil.isNotBlank(dataSourceConfig.getTablePrefix()) ? dataSourceConfig.getTablePrefix() + table.getTableName() : table.getTableName(); if (StrUtil.isNotBlank(table.getPrimaryKey())) { - activeRecordPlugin.addMapping(tableName, table.getPrimaryKey(), table.getModelClass()); + activeRecordPlugin.addMapping(tableName, table.getPrimaryKey(), (Class>) table.getModelClass()); } else { - activeRecordPlugin.addMapping(tableName, table.getModelClass()); + activeRecordPlugin.addMapping(tableName, (Class>) table.getModelClass()); } } } @@ -97,6 +95,18 @@ public class ArpManager { } + private void initRecordPlugin(Map arpDatasourceConfigs, DataSourceConfig datasourceConfig) { + if (datasourceConfig != null && datasourceConfig.isConfigOk()) { + + // 执行 createRecordPlugin(...) 的时候,会同时完善 DataSourceConfig 里绑定的表数据 + // createRecordPlugin完毕后,就可以通过 dataSourceConfig.getTableInfos() 去获取该数据源有哪些表 + ActiveRecordPlugin activeRecordPlugin = createRecordPlugin(datasourceConfig); + + arpDatasourceConfigs.put(System.identityHashCode(activeRecordPlugin), datasourceConfig); + activeRecordPlugins.add(activeRecordPlugin); + } + } + /** * 创建 ActiveRecordPlugin 插件,用于数据库读写 diff --git a/src/main/java/io/jboot/db/JbootDb.java b/src/main/java/io/jboot/db/JbootDb.java index 1b59c525..d0dc2185 100644 --- a/src/main/java/io/jboot/db/JbootDb.java +++ b/src/main/java/io/jboot/db/JbootDb.java @@ -28,7 +28,7 @@ public class JbootDb extends Db { private static ThreadLocal CONFIG_NAME_TL = new ThreadLocal<>(); - public static String getCurentConfigName() { + public static String getCurrentConfigName() { return CONFIG_NAME_TL.get(); } @@ -45,7 +45,7 @@ public class JbootDb extends Db { } public static JbootDbPro use() { - String currentConfigName = getCurentConfigName(); + String currentConfigName = getCurrentConfigName(); return StrUtil.isBlank(currentConfigName) ? (JbootDbPro) Db.use() : use(currentConfigName); } diff --git a/src/main/java/io/jboot/db/TableInfo.java b/src/main/java/io/jboot/db/TableInfo.java index 995f5e71..eb8913b0 100644 --- a/src/main/java/io/jboot/db/TableInfo.java +++ b/src/main/java/io/jboot/db/TableInfo.java @@ -32,7 +32,7 @@ public class TableInfo { private String tableName; private String primaryKey; - private Class> modelClass; + private Class modelClass; private String datasource; private Set datasourceNames; @@ -55,11 +55,11 @@ public class TableInfo { this.primaryKey = primaryKey; } - public Class> getModelClass() { + public Class getModelClass() { return modelClass; } - public void setModelClass(Class> modelClass) { + public void setModelClass(Class modelClass) { this.modelClass = modelClass; } @@ -92,20 +92,15 @@ public class TableInfo { this.attachedDatasources = new ArrayList<>(); } - // 若当前表未指定数据源 (fromDesignated == false),且当前表已经存在了指定数据源的 datasource - // 则不能再添加该数据源 + // 只能添加到一个数据源,若 fromDesignated == false 的时候,直接返回 if (!fromDesignated && !this.attachedDatasources.isEmpty()) { - for (DataSourceConfigWrapper dataSourceConfigWrapper : this.attachedDatasources) { - if (dataSourceConfigWrapper.fromDesignated) { - return false; - } - } + return false; } - this.attachedDatasources.add(new DataSourceConfigWrapper(dataSourceConfig, fromDesignated)); - // 若新添加的数据源,是配置了指定的表(亦或者表配置了指定的数据源),那么需要移除哪些未指定的默认数据源 - if (fromDesignated) { + // 若新添加的数据源,是配置了指定的表(亦或者表配置了指定的数据源), + // 那么需要移除那些未指定表的默认数据源 + if (fromDesignated && !this.attachedDatasources.isEmpty()) { for (DataSourceConfigWrapper dataSourceConfigWrapper : attachedDatasources) { if (!dataSourceConfigWrapper.fromDesignated) { dataSourceConfigWrapper.dataSourceConfig.removeTableInfo(this); @@ -114,6 +109,12 @@ public class TableInfo { attachedDatasources.removeIf(dataSourceConfigWrapper -> !dataSourceConfigWrapper.fromDesignated); } + + // 一张表只能绑定一个数据源,jfinal 的 TableMapping 决定的,默认情况下使用该数据源去进行增删改查 + if (attachedDatasources.isEmpty()) { + this.attachedDatasources.add(new DataSourceConfigWrapper(dataSourceConfig, fromDesignated)); + } + return true; } diff --git a/src/main/java/io/jboot/db/TableInfoManager.java b/src/main/java/io/jboot/db/TableInfoManager.java index 89db10c0..d17a5e8f 100644 --- a/src/main/java/io/jboot/db/TableInfoManager.java +++ b/src/main/java/io/jboot/db/TableInfoManager.java @@ -78,6 +78,11 @@ public class TableInfoManager { dataSourceConfig.addTableInfo(tableInfo, true); } + //排除所有表,但允许当前数据源自己指定的表,指定的表不被排除 + if (configExTables != null && configExTables.contains("*")) { + continue; + } + // 注解 @Table(datasource="xxxx") 指定了数据源,而且当前数据源未匹配 if (!tableInfo.getDatasourceNames().isEmpty()) { continue; @@ -131,7 +136,7 @@ public class TableInfoManager { private void addTable(List tableInfoList, Class modelClass, Table tb) { TableInfo tableInfo = new TableInfo(); - tableInfo.setModelClass((Class>) modelClass); + tableInfo.setModelClass(modelClass); tableInfo.setPrimaryKey(AnnotationUtil.get(tb.primaryKey())); tableInfo.setTableName(AnnotationUtil.get(tb.tableName())); tableInfo.setDatasource(AnnotationUtil.get(tb.datasource())); diff --git a/src/main/java/io/jboot/db/annotation/PriorDatasource.java b/src/main/java/io/jboot/db/annotation/PriorDatasource.java new file mode 100644 index 00000000..41749fe4 --- /dev/null +++ b/src/main/java/io/jboot/db/annotation/PriorDatasource.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.db.annotation; + +import java.lang.annotation.*; + +/** + * @author Michael Yang 杨福海 (fuhai999@gmail.com) + *

        + * 用于配置,优先使用哪个数据源 + */ +@Inherited +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface PriorDatasource { + + String value(); + +} diff --git a/src/main/java/io/jboot/db/datasource/PriorDatasourceInterceptor.java b/src/main/java/io/jboot/db/datasource/PriorDatasourceInterceptor.java new file mode 100644 index 00000000..e6153395 --- /dev/null +++ b/src/main/java/io/jboot/db/datasource/PriorDatasourceInterceptor.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.db.datasource; + +import com.jfinal.aop.Interceptor; +import com.jfinal.aop.Invocation; +import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.Interceptors; +import io.jboot.aop.annotation.AutoLoad; +import io.jboot.db.JbootDb; +import io.jboot.db.annotation.PriorDatasource; +import io.jboot.utils.AnnotationUtil; +import io.jboot.utils.StrUtil; + +import java.lang.reflect.Method; + +/** + * @author Michael Yang 杨福海 (fuhai999@gmail.com) + */ +@AutoLoad +public class PriorDatasourceInterceptor implements Interceptor, InterceptorBuilder { + + + @Override + public void intercept(Invocation inv) { + PriorDatasource datasource = getAnnotation(inv); + if (datasource != null) { + String configName = AnnotationUtil.get(datasource.value()); + if (StrUtil.isNotBlank(configName)) { + JbootDb.setCurrentConfigName(configName); + } + } + + inv.invoke(); + } + + + + private PriorDatasource getAnnotation(Invocation inv) { + PriorDatasource annotation = inv.getController().getClass().getAnnotation(PriorDatasource.class); + return annotation != null ? annotation : inv.getMethod().getAnnotation(PriorDatasource.class); + } + + + + @Override + public void build(Class targetClass, Method method, Interceptors interceptors) { + if (Util.hasAnnotation(targetClass, PriorDatasource.class) || Util.hasAnnotation(method, PriorDatasource.class)) { + interceptors.add(this); + } + } +} diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index cfb5a849..858e41cc 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -15,6 +15,7 @@ */ package io.jboot.db.model; +import com.jfinal.kit.LogKit; import com.jfinal.log.Log; import com.jfinal.plugin.activerecord.*; import com.jfinal.plugin.activerecord.dialect.Dialect; @@ -190,7 +191,7 @@ public class JbootModel> extends Model { /** - * 修复 jfinal use 可能造成的线程安全问题 + * 修复 JFinal use 造成的线程安全问题 * * @param configName * @return @@ -245,9 +246,14 @@ public class JbootModel> extends Model { return DbKit.getConfig(datasourceName); } - String currentConfigName = JbootDb.getCurentConfigName(); + String currentConfigName = JbootDb.getCurrentConfigName(); if (StrUtil.isNotBlank(currentConfigName)) { - return DbKit.getConfig(currentConfigName); + Config config = DbKit.getConfig(currentConfigName); + if (config == null) { + LogKit.error("Can not use the datasource: {}, user default to replace.", currentConfigName); + } else { + return config; + } } return DbKit.getConfig(_getUsefulClass()); @@ -525,12 +531,26 @@ public class JbootModel> extends Model { protected JbootDialect _getDialect() { Config config = _getConfig(); if (config == null) { - throw new JbootException("class \"" + _getUsefulClass().getName() + "\" can not mapping to the table, maybe cannot connect to database. "); + return throwCannotMappingException(); } return (JbootDialect) config.getDialect(); } + private JbootDialect throwCannotMappingException() { + io.jboot.db.annotation.Table annotation = this.getClass().getAnnotation(io.jboot.db.annotation.Table.class); + if (annotation != null && StrUtil.isNotBlank(annotation.datasource())) { + throw new JbootException( + String.format("Model \"%s\" can not mapping to datasource: " + annotation.datasource() + , _getUsefulClass().getName())); + } else { + throw new JbootException( + String.format("Model \"%s\" can not mapping to database table, maybe application cannot connect to database. " + , _getUsefulClass().getName())); + } + } + + public M findFirstByColumn(String column, Object value) { return findFirstByColumn(Column.create(column, value)); } @@ -876,9 +896,7 @@ public class JbootModel> extends Model { if (table == null) { table = super._getTable(); if (table == null && validateMapping) { - throw new JbootException( - String.format("class %s can not mapping to database table, maybe application cannot connect to database. " - , _getUsefulClass().getName())); + throwCannotMappingException(); } } return table; -- Gitee From ef94e0fa2aeda707d6d36c16ef5855be40c7b761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E9=94=8B?= Date: Fri, 25 Mar 2022 14:49:27 +0800 Subject: [PATCH 1635/1965] =?UTF-8?q?=E5=A2=9E=E5=8A=A0RabbitMq=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=8F=82=E6=95=B0=EF=BC=8C=E4=BD=BF=E7=94=A8=E5=B9=BF?= =?UTF-8?q?=E6=92=AD=E6=88=96=E6=99=AE=E9=80=9A=E9=98=9F=E5=88=97=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E7=9A=84=E5=BC=80=E5=85=B3=EF=BC=8C=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=85=A8=E9=83=A8=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mq/rabbitmq/JbootRabbitmqConfig.java | 24 +++++++++++++++++++ .../mq/rabbitmq/JbootRabbitmqImpl.java | 19 ++++++++------- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java index 488f2c07..9a8007b3 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java @@ -46,6 +46,14 @@ public class JbootRabbitmqConfig { private boolean queueDeclareExclusive = false; private boolean queueDeclareAutoDelete = false; + /** + * 使用队列 + */ + private boolean useQueue=true; + /** + * 使用广播 + */ + private boolean useBroadcast=true; private String broadcastExchangeDeclareExchangeType = "fanout"; private boolean broadcastExchangeDeclareDurable = false; @@ -181,4 +189,20 @@ public class JbootRabbitmqConfig { public void setBroadcastQueueDeclareAutoDelete(boolean broadcastQueueDeclareAutoDelete) { this.broadcastQueueDeclareAutoDelete = broadcastQueueDeclareAutoDelete; } + + public boolean useQueue(){ + return useQueue; + } + + public void setUseQueue(boolean useQueue) { + this.useQueue = useQueue; + } + + public boolean useBroadcast() { + return useBroadcast; + } + + public void setUseBroadcast(boolean useBroadcast) { + this.useBroadcast=useBroadcast; + } } diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index 15ffe4b0..6d9e44d3 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -85,15 +85,16 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { @Override protected void onStartListening() { for (String toChannel : channels) { - - //广播通道 - Channel broadcastChannel = getChannel(toChannel, false); - bindChannel(broadcastChannel, buildBroadcastChannelName(toChannel), toChannel); - - - //队列通道 - final Channel queueChannel = getChannel(toChannel, true); - bindChannel(queueChannel, toChannel, toChannel); + if(rabbitmqConfig.useBroadcast()) { + //广播通道 + Channel broadcastChannel = getChannel(toChannel, false); + bindChannel(broadcastChannel, buildBroadcastChannelName(toChannel), toChannel); + } + if(rabbitmqConfig.useQueue()) { + //队列通道 + final Channel queueChannel = getChannel(toChannel, true); + bindChannel(queueChannel, toChannel, toChannel); + } } } -- Gitee From 3591dfa17fd9ec71f9ddc7e042ca035a45edeaeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E9=94=8B?= Date: Fri, 25 Mar 2022 16:30:46 +0800 Subject: [PATCH 1636/1965] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E7=9A=84Rabbitmq=E9=98=9F=E5=88=97=E3=80=81=E5=B9=BF=E6=92=AD?= =?UTF-8?q?=E9=80=9A=E9=81=93=E5=BC=80=E5=90=AF=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java | 2 ++ .../java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java | 2 +- .../java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java | 9 +++++---- .../java/io/jboot/test/mq/rabbit/RabbitMqSender.java | 1 + 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index 6d9e44d3..33595fbb 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -89,11 +89,13 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { //广播通道 Channel broadcastChannel = getChannel(toChannel, false); bindChannel(broadcastChannel, buildBroadcastChannelName(toChannel), toChannel); + System.out.println("广播通道"); } if(rabbitmqConfig.useQueue()) { //队列通道 final Channel queueChannel = getChannel(toChannel, true); bindChannel(queueChannel, toChannel, toChannel); + System.out.println("队列通道"); } } } diff --git a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java index 066fe9ff..3dba0e38 100644 --- a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java +++ b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java @@ -20,7 +20,7 @@ public class RabbitMqReceiver1 { //设置 mq 的相关信息 JbootApplication.setBootArg("jboot.mq.type", "rabbitmq"); - JbootApplication.setBootArg("jboot.mq.channel", "channel1"); + JbootApplication.setBootArg("jboot.mq.channel", "channel1,channel2"); //以下可以不用配置,是默认信息 diff --git a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java index c6fb0a1d..9baac9be 100644 --- a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java +++ b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java @@ -19,8 +19,9 @@ public class RabbitMqReceiver2 { //设置 mq 的相关信息 JbootApplication.setBootArg("jboot.mq.type", "rabbitmq"); - JbootApplication.setBootArg("jboot.mq.channel", "channel2"); - + JbootApplication.setBootArg("jboot.mq.channel", "channel1,channel2,myChannel"); +// JbootApplication.setBootArg("jboot.mq.rabbitmq.useQueue", false); + JbootApplication.setBootArg("jboot.mq.rabbitmq.broadcastExchangeDeclareExchangeType", "direct"); //以下可以不用配置,是默认信息 // JbootApplication.setBootArg("jboot.mq.rabbitmq.username", "guest"); @@ -30,7 +31,7 @@ public class RabbitMqReceiver2 { // JbootApplication.setBootArg("jboot.mq.rabbitmq.virtualHost", ""); //非常重要,多个应用如果同时接受同一个 channel 的广播,必须配置此项,而且必须不能相同,否则广播的时候只有一个应用能够接受到 - JbootApplication.setBootArg("jboot.mq.rabbitmq.broadcastChannelPrefix", "app2"); +// JbootApplication.setBootArg("jboot.mq.rabbitmq.broadcastChannelPrefix", "app2"); //启动应用程序 JbootApplication.run(args); @@ -43,7 +44,7 @@ public class RabbitMqReceiver2 { public void onMessage(String channel, Object message, MessageContext context) { System.out.println("Receive msg: " + message + ", from channel: " + channel); } - },"channel2"); + }); Jboot.getMq().startListening(); diff --git a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqSender.java b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqSender.java index 9b5d5634..620b023a 100644 --- a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqSender.java +++ b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqSender.java @@ -17,6 +17,7 @@ public class RabbitMqSender { //设置 mq 的相关信息 JbootApplication.setBootArg("jboot.mq.type", "rabbitmq"); + JbootApplication.setBootArg("jboot.mq.rabbitmq.broadcastExchangeDeclareExchangeType", "direct"); //以下可以不用配置,是默认信息 // JbootApplication.setBootArg("jboot.mq.rabbitmq.username", "guest"); -- Gitee From a5e4e0c1292be885845b20c09732bfd1803475f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E9=94=8B?= Date: Fri, 25 Mar 2022 16:45:23 +0800 Subject: [PATCH 1637/1965] =?UTF-8?q?=E6=B8=85=E7=90=86=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index 33595fbb..6d9e44d3 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -89,13 +89,11 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { //广播通道 Channel broadcastChannel = getChannel(toChannel, false); bindChannel(broadcastChannel, buildBroadcastChannelName(toChannel), toChannel); - System.out.println("广播通道"); } if(rabbitmqConfig.useQueue()) { //队列通道 final Channel queueChannel = getChannel(toChannel, true); bindChannel(queueChannel, toChannel, toChannel); - System.out.println("队列通道"); } } } -- Gitee From 0cb3844f1baea5b9e2d7f4b2562d01e4ad962979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 25 Mar 2022 18:27:05 +0800 Subject: [PATCH 1638/1965] =?UTF-8?q?rabbitmq=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=B9=BF=E6=92=AD=E5=92=8C=E9=98=9F=E5=88=97=E5=BC=80=E5=90=AF?= =?UTF-8?q?=E7=9A=84=E9=85=8D=E7=BD=AE=E5=BC=80=E5=85=B3=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E4=B9=8B=E5=89=8D=E6=A8=A1=E5=BC=8F=E6=98=AF=E5=85=A8?= =?UTF-8?q?=E9=83=A8=E5=BC=80=E5=90=AF=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mq/rabbitmq/JbootRabbitmqConfig.java | 29 +++++++++---------- .../mq/rabbitmq/JbootRabbitmqImpl.java | 10 ++++--- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java index 9a8007b3..bd2c883d 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqConfig.java @@ -42,18 +42,15 @@ public class JbootRabbitmqConfig { //若配置为 false,则需要在 OnMessage 里,调用 MessageContext.getChannel().baseAck(或者baseNack)进行消费(或者标识消费失败) private boolean autoAck = true; + // 默认开启队列 + private boolean queueEnable = true; private boolean queueDeclareDurable = false; private boolean queueDeclareExclusive = false; private boolean queueDeclareAutoDelete = false; - /** - * 使用队列 - */ - private boolean useQueue=true; - /** - * 使用广播 - */ - private boolean useBroadcast=true; + + // 默认开启广播模式 + private boolean broadcastEnable = true; private String broadcastExchangeDeclareExchangeType = "fanout"; private boolean broadcastExchangeDeclareDurable = false; @@ -190,19 +187,19 @@ public class JbootRabbitmqConfig { this.broadcastQueueDeclareAutoDelete = broadcastQueueDeclareAutoDelete; } - public boolean useQueue(){ - return useQueue; + public boolean isQueueEnable() { + return queueEnable; } - public void setUseQueue(boolean useQueue) { - this.useQueue = useQueue; + public void setQueueEnable(boolean queueEnable) { + this.queueEnable = queueEnable; } - public boolean useBroadcast() { - return useBroadcast; + public boolean isBroadcastEnable() { + return broadcastEnable; } - public void setUseBroadcast(boolean useBroadcast) { - this.useBroadcast=useBroadcast; + public void setBroadcastEnable(boolean broadcastEnable) { + this.broadcastEnable = broadcastEnable; } } diff --git a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java index 6d9e44d3..aa10128d 100644 --- a/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java +++ b/src/main/java/io/jboot/components/mq/rabbitmq/JbootRabbitmqImpl.java @@ -85,13 +85,15 @@ public class JbootRabbitmqImpl extends JbootmqBase implements Jbootmq { @Override protected void onStartListening() { for (String toChannel : channels) { - if(rabbitmqConfig.useBroadcast()) { - //广播通道 + + //广播通道 + if (rabbitmqConfig.isBroadcastEnable()) { Channel broadcastChannel = getChannel(toChannel, false); bindChannel(broadcastChannel, buildBroadcastChannelName(toChannel), toChannel); } - if(rabbitmqConfig.useQueue()) { - //队列通道 + + //队列通道 + if (rabbitmqConfig.isQueueEnable()) { final Channel queueChannel = getChannel(toChannel, true); bindChannel(queueChannel, toChannel, toChannel); } -- Gitee From 5b1f08c6416ddbf209ab1b1fda553737f6c5cb4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 25 Mar 2022 22:15:07 +0800 Subject: [PATCH 1639/1965] v3.14.3 snapshot --- src/main/java/io/jboot/db/TableInfoManager.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/TableInfoManager.java b/src/main/java/io/jboot/db/TableInfoManager.java index d17a5e8f..56560ca7 100644 --- a/src/main/java/io/jboot/db/TableInfoManager.java +++ b/src/main/java/io/jboot/db/TableInfoManager.java @@ -78,7 +78,7 @@ public class TableInfoManager { dataSourceConfig.addTableInfo(tableInfo, true); } - //排除所有表,但允许当前数据源自己指定的表,指定的表不被排除 + // 排除所有表,但允许当前数据源自己指定的表,指定的表不被排除 if (configExTables != null && configExTables.contains("*")) { continue; } @@ -88,6 +88,11 @@ public class TableInfoManager { continue; } + // 如果当前的数据源已经配置了绑定的表,且未当前表未命中,不让其他表添加到当前数据源 + if (configTables != null && !configTables.isEmpty()) { + continue; + } + dataSourceConfig.addTableInfo(tableInfo, false); } -- Gitee From e83167324dbdeaae92edb61734a006284389fac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 26 Mar 2022 12:16:09 +0800 Subject: [PATCH 1640/1965] v3.14.3 snapshot --- src/main/java/io/jboot/db/TableInfoManager.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/db/TableInfoManager.java b/src/main/java/io/jboot/db/TableInfoManager.java index 56560ca7..0686c79f 100644 --- a/src/main/java/io/jboot/db/TableInfoManager.java +++ b/src/main/java/io/jboot/db/TableInfoManager.java @@ -59,7 +59,7 @@ public class TableInfoManager { ? StrUtil.splitToSetByComma(dataSourceConfig.getExTable()) : null; - //所有的表信息 + // 所有的表信息 List allTableInfos = getAllTableInfos(); @@ -84,12 +84,12 @@ public class TableInfoManager { } // 注解 @Table(datasource="xxxx") 指定了数据源,而且当前数据源未匹配 - if (!tableInfo.getDatasourceNames().isEmpty()) { + if (tableInfo.getDatasourceNames().size() > 0) { continue; } // 如果当前的数据源已经配置了绑定的表,且未当前表未命中,不让其他表添加到当前数据源 - if (configTables != null && !configTables.isEmpty()) { + if (configTables != null && configTables.size() > 0) { continue; } -- Gitee From 5d6fc8185bf95eed60f82b842a7b378ab6aa350f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 27 Mar 2022 14:15:01 +0800 Subject: [PATCH 1641/1965] add CORSProcesser --- .../io/jboot/web/cors/CORSInterceptor.java | 63 ++---- .../web/cors/CORSInterceptorBuilder.java | 39 ---- .../java/io/jboot/web/cors/CORSProcesser.java | 193 ++++++++++++++++++ 3 files changed, 205 insertions(+), 90 deletions(-) delete mode 100644 src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java create mode 100644 src/main/java/io/jboot/web/cors/CORSProcesser.java diff --git a/src/main/java/io/jboot/web/cors/CORSInterceptor.java b/src/main/java/io/jboot/web/cors/CORSInterceptor.java index 478c14da..16a3c6dc 100644 --- a/src/main/java/io/jboot/web/cors/CORSInterceptor.java +++ b/src/main/java/io/jboot/web/cors/CORSInterceptor.java @@ -18,19 +18,19 @@ package io.jboot.web.cors; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import com.jfinal.ext.cors.EnableCORS; -import io.jboot.utils.AnnotationUtil; -import io.jboot.utils.StrUtil; +import io.jboot.aop.InterceptorBuilder; +import io.jboot.aop.Interceptors; +import io.jboot.aop.annotation.AutoLoad; -import javax.servlet.http.HttpServletResponse; +import java.lang.reflect.Method; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 * @Title: CORS 处理相关 拦截器 */ -public class CORSInterceptor implements Interceptor { - - private static final String METHOD_OPTIONS = "OPTIONS"; +@AutoLoad +public class CORSInterceptor implements Interceptor, InterceptorBuilder { @Override public void intercept(Invocation inv) { @@ -42,16 +42,7 @@ public class CORSInterceptor implements Interceptor { return; } - - doConfigCORS(inv, enableCORS); - - - String method = inv.getController().getRequest().getMethod(); - if (METHOD_OPTIONS.equalsIgnoreCase(method)) { - inv.getController().renderText(""); - } else { - inv.invoke(); - } + new CORSProcesser(enableCORS).process(inv); } @@ -61,41 +52,11 @@ public class CORSInterceptor implements Interceptor { } - private void doConfigCORS(Invocation inv, EnableCORS enableCORS) { - - HttpServletResponse response = inv.getController().getResponse(); - - String allowOrigin = AnnotationUtil.get(enableCORS.allowOrigin()); - String allowCredentials = AnnotationUtil.get(enableCORS.allowCredentials()); - String allowHeaders = AnnotationUtil.get(enableCORS.allowHeaders()); - String allowMethods = AnnotationUtil.get(enableCORS.allowMethods()); - String exposeHeaders = AnnotationUtil.get(enableCORS.exposeHeaders()); - String requestHeaders = AnnotationUtil.get(enableCORS.requestHeaders()); - String requestMethod = AnnotationUtil.get(enableCORS.requestMethod()); - String origin = AnnotationUtil.get(enableCORS.origin()); - String maxAge = AnnotationUtil.get(enableCORS.maxAge()); - - response.setHeader("Access-Control-Allow-Origin", allowOrigin); - response.setHeader("Access-Control-Allow-Methods", allowMethods); - response.setHeader("Access-Control-Allow-Headers", allowHeaders); - response.setHeader("Access-Control-Max-Age", maxAge); - response.setHeader("Access-Control-Allow-Credentials", allowCredentials); - - if (StrUtil.isNotBlank(exposeHeaders)) { - response.setHeader("Access-Control-Expose-Headers", exposeHeaders); - } - - if (StrUtil.isNotBlank(requestHeaders)) { - response.setHeader("Access-Control-Request-Headers", requestHeaders); - } - - if (StrUtil.isNotBlank(requestMethod)) { - response.setHeader("Access-Control-Request-Method", requestMethod); - } - - if (StrUtil.isNotBlank(origin)) { - response.setHeader("Origin", origin); + @Override + public void build(Class targetClass, Method method, Interceptors interceptors) { + if (Util.isController(targetClass) && Util.hasAnnotation(targetClass, method, EnableCORS.class)) { + interceptors.addToFirstIfNotExist(CORSInterceptor.class); } - } + } diff --git a/src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java b/src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java deleted file mode 100644 index 0a1b5e1b..00000000 --- a/src/main/java/io/jboot/web/cors/CORSInterceptorBuilder.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.web.cors; - -import com.jfinal.ext.cors.EnableCORS; -import io.jboot.aop.InterceptorBuilder; -import io.jboot.aop.Interceptors; -import io.jboot.aop.annotation.AutoLoad; - -import java.lang.reflect.Method; - -/** - * @author michael yang (fuhai999@gmail.com) - */ -@AutoLoad -public class CORSInterceptorBuilder implements InterceptorBuilder { - - @Override - public void build(Class targetClass, Method method, Interceptors interceptors) { - if (Util.isController(targetClass) && Util.hasAnnotation(targetClass, method, EnableCORS.class)) { - interceptors.addToFirst(CORSInterceptor.class); - } - } - - -} diff --git a/src/main/java/io/jboot/web/cors/CORSProcesser.java b/src/main/java/io/jboot/web/cors/CORSProcesser.java new file mode 100644 index 00000000..6de760a9 --- /dev/null +++ b/src/main/java/io/jboot/web/cors/CORSProcesser.java @@ -0,0 +1,193 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.cors; + +import com.jfinal.aop.Invocation; +import com.jfinal.ext.cors.EnableCORS; +import io.jboot.utils.AnnotationUtil; +import io.jboot.utils.StrUtil; + +import javax.servlet.http.HttpServletResponse; + +public class CORSProcesser { + + private static final String METHOD_OPTIONS = "OPTIONS"; + + + private String allowOrigin = "*"; + private String allowCredentials = "true"; + private String allowHeaders = "Origin,X-Requested-With,Content-Type,Accept,Authorization,Jwt"; + private String allowMethods = "GET,PUT,POST,DELETE,PATCH,OPTIONS"; + private String exposeHeaders; + private String requestHeaders; + private String requestMethod; + private String origin; + private String maxAge = "3600"; + + public CORSProcesser() { + } + + public CORSProcesser(String allowOrigin) { + this.allowOrigin = allowOrigin; + } + + public CORSProcesser(String allowOrigin, String allowHeaders) { + this.allowOrigin = allowOrigin; + this.allowHeaders = allowHeaders; + } + + public CORSProcesser(String allowOrigin + , String allowCredentials + , String allowHeaders + , String allowMethods + , String exposeHeaders + , String requestHeaders + , String requestMethod + , String origin + , String maxAge) { + this.allowOrigin = allowOrigin; + this.allowCredentials = allowCredentials; + this.allowHeaders = allowHeaders; + this.allowMethods = allowMethods; + this.exposeHeaders = exposeHeaders; + this.requestHeaders = requestHeaders; + this.requestMethod = requestMethod; + this.origin = origin; + this.maxAge = maxAge; + } + + public CORSProcesser(EnableCORS enableCORS) { + this.allowOrigin = AnnotationUtil.get(enableCORS.allowOrigin()); + this.allowCredentials = AnnotationUtil.get(enableCORS.allowCredentials()); + this.allowHeaders = AnnotationUtil.get(enableCORS.allowHeaders()); + this.allowMethods = AnnotationUtil.get(enableCORS.allowMethods()); + this.exposeHeaders = AnnotationUtil.get(enableCORS.exposeHeaders()); + this.requestHeaders = AnnotationUtil.get(enableCORS.requestHeaders()); + this.requestMethod = AnnotationUtil.get(enableCORS.requestMethod()); + this.origin = AnnotationUtil.get(enableCORS.origin()); + this.maxAge = AnnotationUtil.get(enableCORS.maxAge()); + } + + + public String getAllowOrigin() { + return allowOrigin; + } + + public void setAllowOrigin(String allowOrigin) { + this.allowOrigin = allowOrigin; + } + + public String getAllowCredentials() { + return allowCredentials; + } + + public void setAllowCredentials(String allowCredentials) { + this.allowCredentials = allowCredentials; + } + + public String getAllowHeaders() { + return allowHeaders; + } + + public void setAllowHeaders(String allowHeaders) { + this.allowHeaders = allowHeaders; + } + + public String getAllowMethods() { + return allowMethods; + } + + public void setAllowMethods(String allowMethods) { + this.allowMethods = allowMethods; + } + + public String getExposeHeaders() { + return exposeHeaders; + } + + public void setExposeHeaders(String exposeHeaders) { + this.exposeHeaders = exposeHeaders; + } + + public String getRequestHeaders() { + return requestHeaders; + } + + public void setRequestHeaders(String requestHeaders) { + this.requestHeaders = requestHeaders; + } + + public String getRequestMethod() { + return requestMethod; + } + + public void setRequestMethod(String requestMethod) { + this.requestMethod = requestMethod; + } + + public String getOrigin() { + return origin; + } + + public void setOrigin(String origin) { + this.origin = origin; + } + + public String getMaxAge() { + return maxAge; + } + + public void setMaxAge(String maxAge) { + this.maxAge = maxAge; + } + + public void process(Invocation inv) { + + //配置 http 头信息 + configHeaders(inv.getController().getResponse()); + + String method = inv.getController().getRequest().getMethod(); + if (METHOD_OPTIONS.equalsIgnoreCase(method)) { + inv.getController().renderText(""); + } else { + inv.invoke(); + } + } + + private void configHeaders(HttpServletResponse response) { + response.setHeader("Access-Control-Allow-Origin", allowOrigin); + response.setHeader("Access-Control-Allow-Methods", allowMethods); + response.setHeader("Access-Control-Allow-Headers", allowHeaders); + response.setHeader("Access-Control-Max-Age", maxAge); + response.setHeader("Access-Control-Allow-Credentials", allowCredentials); + + if (StrUtil.isNotBlank(exposeHeaders)) { + response.setHeader("Access-Control-Expose-Headers", exposeHeaders); + } + + if (StrUtil.isNotBlank(requestHeaders)) { + response.setHeader("Access-Control-Request-Headers", requestHeaders); + } + + if (StrUtil.isNotBlank(requestMethod)) { + response.setHeader("Access-Control-Request-Method", requestMethod); + } + + if (StrUtil.isNotBlank(origin)) { + response.setHeader("Origin", origin); + } + } +} -- Gitee From 7b342c216e52657f12a0663950f1400d261e69b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 28 Mar 2022 13:30:49 +0800 Subject: [PATCH 1642/1965] rename CORSProcesser to CORSConfig --- .../{CORSProcesser.java => CORSConfig.java} | 102 +++++++++--------- .../io/jboot/web/cors/CORSInterceptor.java | 45 +++++++- 2 files changed, 98 insertions(+), 49 deletions(-) rename src/main/java/io/jboot/web/cors/{CORSProcesser.java => CORSConfig.java} (64%) diff --git a/src/main/java/io/jboot/web/cors/CORSProcesser.java b/src/main/java/io/jboot/web/cors/CORSConfig.java similarity index 64% rename from src/main/java/io/jboot/web/cors/CORSProcesser.java rename to src/main/java/io/jboot/web/cors/CORSConfig.java index 6de760a9..d07f20f7 100644 --- a/src/main/java/io/jboot/web/cors/CORSProcesser.java +++ b/src/main/java/io/jboot/web/cors/CORSConfig.java @@ -15,41 +15,59 @@ */ package io.jboot.web.cors; -import com.jfinal.aop.Invocation; import com.jfinal.ext.cors.EnableCORS; import io.jboot.utils.AnnotationUtil; -import io.jboot.utils.StrUtil; -import javax.servlet.http.HttpServletResponse; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; -public class CORSProcesser { - - private static final String METHOD_OPTIONS = "OPTIONS"; +public class CORSConfig { + private static final CORSConfig DEFAULT_CONFIG = new CORSConfig(); + private static final Map cache = new ConcurrentHashMap<>(); private String allowOrigin = "*"; private String allowCredentials = "true"; private String allowHeaders = "Origin,X-Requested-With,Content-Type,Accept,Authorization,Jwt"; private String allowMethods = "GET,PUT,POST,DELETE,PATCH,OPTIONS"; - private String exposeHeaders; - private String requestHeaders; - private String requestMethod; - private String origin; + private String exposeHeaders = ""; + private String requestHeaders = ""; + private String requestMethod = ""; + private String origin = ""; private String maxAge = "3600"; - public CORSProcesser() { + public static CORSConfig getDefaultConfig() { + return DEFAULT_CONFIG; } - public CORSProcesser(String allowOrigin) { + public static CORSConfig fromAnnotation(EnableCORS enableCORS) { + int identityHashCode = System.identityHashCode(enableCORS); + CORSConfig corsConfig = cache.get(identityHashCode); + if (corsConfig == null) { + corsConfig = new CORSConfig(enableCORS); + if (corsConfig.equals(DEFAULT_CONFIG)) { + corsConfig = DEFAULT_CONFIG; + } + cache.put(identityHashCode, corsConfig); + } + return corsConfig; + } + + + public CORSConfig() { + } + + public CORSConfig(String allowOrigin) { this.allowOrigin = allowOrigin; } - public CORSProcesser(String allowOrigin, String allowHeaders) { + public CORSConfig(String allowOrigin, String allowHeaders) { this.allowOrigin = allowOrigin; this.allowHeaders = allowHeaders; } - public CORSProcesser(String allowOrigin + public CORSConfig(String allowOrigin , String allowCredentials , String allowHeaders , String allowMethods @@ -69,7 +87,7 @@ public class CORSProcesser { this.maxAge = maxAge; } - public CORSProcesser(EnableCORS enableCORS) { + public CORSConfig(EnableCORS enableCORS) { this.allowOrigin = AnnotationUtil.get(enableCORS.allowOrigin()); this.allowCredentials = AnnotationUtil.get(enableCORS.allowCredentials()); this.allowHeaders = AnnotationUtil.get(enableCORS.allowHeaders()); @@ -154,40 +172,28 @@ public class CORSProcesser { this.maxAge = maxAge; } - public void process(Invocation inv) { - - //配置 http 头信息 - configHeaders(inv.getController().getResponse()); - - String method = inv.getController().getRequest().getMethod(); - if (METHOD_OPTIONS.equalsIgnoreCase(method)) { - inv.getController().renderText(""); - } else { - inv.invoke(); - } - } - - private void configHeaders(HttpServletResponse response) { - response.setHeader("Access-Control-Allow-Origin", allowOrigin); - response.setHeader("Access-Control-Allow-Methods", allowMethods); - response.setHeader("Access-Control-Allow-Headers", allowHeaders); - response.setHeader("Access-Control-Max-Age", maxAge); - response.setHeader("Access-Control-Allow-Credentials", allowCredentials); - - if (StrUtil.isNotBlank(exposeHeaders)) { - response.setHeader("Access-Control-Expose-Headers", exposeHeaders); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - if (StrUtil.isNotBlank(requestHeaders)) { - response.setHeader("Access-Control-Request-Headers", requestHeaders); - } - - if (StrUtil.isNotBlank(requestMethod)) { - response.setHeader("Access-Control-Request-Method", requestMethod); - } - - if (StrUtil.isNotBlank(origin)) { - response.setHeader("Origin", origin); + if (o == null || getClass() != o.getClass()) { + return false; } + CORSConfig that = (CORSConfig) o; + return Objects.equals(allowOrigin, that.allowOrigin) + && Objects.equals(allowCredentials, that.allowCredentials) + && Objects.equals(allowHeaders, that.allowHeaders) + && Objects.equals(allowMethods, that.allowMethods) + && Objects.equals(exposeHeaders, that.exposeHeaders) + && Objects.equals(requestHeaders, that.requestHeaders) + && Objects.equals(requestMethod, that.requestMethod) + && Objects.equals(origin, that.origin) + && Objects.equals(maxAge, that.maxAge); + } + + @Override + public int hashCode() { + return Objects.hash(allowOrigin, allowCredentials, allowHeaders, allowMethods, exposeHeaders, requestHeaders, requestMethod, origin, maxAge); } } diff --git a/src/main/java/io/jboot/web/cors/CORSInterceptor.java b/src/main/java/io/jboot/web/cors/CORSInterceptor.java index 16a3c6dc..a489cf07 100644 --- a/src/main/java/io/jboot/web/cors/CORSInterceptor.java +++ b/src/main/java/io/jboot/web/cors/CORSInterceptor.java @@ -21,7 +21,9 @@ import com.jfinal.ext.cors.EnableCORS; import io.jboot.aop.InterceptorBuilder; import io.jboot.aop.Interceptors; import io.jboot.aop.annotation.AutoLoad; +import io.jboot.utils.StrUtil; +import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; /** @@ -32,6 +34,9 @@ import java.lang.reflect.Method; @AutoLoad public class CORSInterceptor implements Interceptor, InterceptorBuilder { + private static final String METHOD_OPTIONS = "OPTIONS"; + + @Override public void intercept(Invocation inv) { @@ -42,7 +47,7 @@ public class CORSInterceptor implements Interceptor, InterceptorBuilder { return; } - new CORSProcesser(enableCORS).process(inv); + process(inv, CORSConfig.fromAnnotation(enableCORS)); } @@ -52,6 +57,44 @@ public class CORSInterceptor implements Interceptor, InterceptorBuilder { } + public static void process(Invocation inv, CORSConfig config) { + //配置 http 头信息 + configHeaders(inv.getController().getResponse(), config); + + String method = inv.getController().getRequest().getMethod(); + if (METHOD_OPTIONS.equalsIgnoreCase(method)) { + inv.getController().renderText(""); + } else { + inv.invoke(); + } + } + + + private static void configHeaders(HttpServletResponse response, CORSConfig config) { + response.setHeader("Access-Control-Allow-Origin", config.getAllowOrigin()); + response.setHeader("Access-Control-Allow-Methods", config.getAllowMethods()); + response.setHeader("Access-Control-Allow-Headers", config.getAllowHeaders()); + response.setHeader("Access-Control-Max-Age", config.getMaxAge()); + response.setHeader("Access-Control-Allow-Credentials", config.getAllowCredentials()); + + if (StrUtil.isNotBlank(config.getExposeHeaders())) { + response.setHeader("Access-Control-Expose-Headers", config.getExposeHeaders()); + } + + if (StrUtil.isNotBlank(config.getRequestHeaders())) { + response.setHeader("Access-Control-Request-Headers", config.getRequestHeaders()); + } + + if (StrUtil.isNotBlank(config.getRequestMethod())) { + response.setHeader("Access-Control-Request-Method", config.getRequestMethod()); + } + + if (StrUtil.isNotBlank(config.getOrigin())) { + response.setHeader("Origin", config.getOrigin()); + } + } + + @Override public void build(Class targetClass, Method method, Interceptors interceptors) { if (Util.isController(targetClass) && Util.hasAnnotation(targetClass, method, EnableCORS.class)) { -- Gitee From 1ba2f1ddacca386949bd562f02e4dbdd764549c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 30 Mar 2022 18:23:58 +0800 Subject: [PATCH 1643/1965] =?UTF-8?q?fixed:=20=E4=BF=AE=E5=A4=8D=20HttpUti?= =?UTF-8?q?l=20=E5=9C=A8=20put=20=E8=AF=B7=E6=B1=82=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E6=AD=A3=E5=B8=B8=E6=8F=90=E4=BA=A4=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/components/http/JbootHttpRequest.java | 8 ++++++-- .../io/jboot/components/http/jboot/JbootHttpImpl.java | 8 ++++---- .../io/jboot/components/http/okhttp/OKHttpImpl.java | 10 +++++----- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/java/io/jboot/components/http/JbootHttpRequest.java b/src/main/java/io/jboot/components/http/JbootHttpRequest.java index 889f124f..6189f8a5 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpRequest.java +++ b/src/main/java/io/jboot/components/http/JbootHttpRequest.java @@ -282,6 +282,10 @@ public class JbootHttpRequest { return METHOD_POST.equalsIgnoreCase(method); } + public boolean isPutRequest() { + return METHOD_PUT.equalsIgnoreCase(method); + } + public String getCharset() { return charset; } @@ -316,7 +320,7 @@ public class JbootHttpRequest { public String getPostContent() { if (postContent != null) { - initGetUrl(); + appendParasToUrl(); return postContent; } else { return buildParams(); @@ -333,7 +337,7 @@ public class JbootHttpRequest { } - public void initGetUrl() { + public void appendParasToUrl() { String params = buildParams(); diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index c1eda6fa..a71c77eb 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -65,8 +65,8 @@ public class JbootHttpImpl implements JbootHttp { configConnection(connection, request); - //post 请求 - if (request.isPostRequest()) { + //post 或者 put 请求 + if (request.isPostRequest() || request.isPutRequest()) { connection.setDoOutput(true); @@ -211,8 +211,8 @@ public class JbootHttpImpl implements JbootHttp { } private static HttpURLConnection getConnection(JbootHttpRequest request) throws Exception { - if (!request.isPostRequest()) { - request.initGetUrl(); + if (!request.isPostRequest() && !request.isPutRequest()) { + request.appendParasToUrl(); } return request.isHttps() ? getHttpsConnection(request) : getHttpConnection(request); } diff --git a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java index 1b09e28d..b376d337 100644 --- a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java +++ b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java @@ -51,15 +51,15 @@ public class OKHttpImpl implements JbootHttp { private void doProcess(JbootHttpRequest request, JbootHttpResponse response) { try { - if (request.isPostRequest()) { + + // post 请求 或者 put 请求 + if (request.isPostRequest() || request.isPutRequest()) { doProcessPostRequest(request, response); } - /** - * get 获取 其他 请求 - */ + // 其他非 post 和 put 请求 else { - request.initGetUrl(); + request.appendParasToUrl(); doProcessGetRequest(request, response); } -- Gitee From 65e17c4ba074a425f3901139ba08ec37be29836f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 1 Apr 2022 10:36:14 +0800 Subject: [PATCH 1644/1965] v3.14.3 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- .../jboot/web/controller/JbootController.java | 20 +++++++++++++++++++ 7 files changed, 28 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 3dc62a04..04d27d13 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.14.2 + 3.14.3 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 7cc500aa..4de0ec96 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.14.2 + 3.14.3 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 02c76b0a..9341be97 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.14.2 + 3.14.3 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.14.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.14.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index f0b8fcb7..f56b9108 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.14.2', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.14.3', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.2/jboot-3.14.2.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.3/jboot-3.14.3.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 06fd74b1..c69ca52f 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.14.2 + 3.14.3 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 30872d11..3448c829 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.14.2"; + public static String VERSION = "3.14.3"; public static final String ATTR_REQUEST = "REQUEST"; diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 9eca3219..03365f16 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -515,6 +515,26 @@ public class JbootController extends Controller { } + @NotAction + public String getUnescapePara(String name) { + String value = getTrimPara(name); + if (value == null || value.length() == 0) { + return null; + } + return StrUtil.unEscapeHtml(value); + } + + + @NotAction + public String getUnescapePara(String name, String defaultValue) { + String value = getTrimPara(name); + if (value == null || value.length() == 0) { + return defaultValue; + } + return StrUtil.unEscapeHtml(value); + } + + @NotAction public String getOriginalPara(String name) { String value = getOrginalRequest().getParameter(name); -- Gitee From c33636109af5ef587f89aee18867b6047c200a1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 1 Apr 2022 10:47:56 +0800 Subject: [PATCH 1645/1965] v3.14.3 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 12 +++++++++++- pom.xml | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 28d000cf..4edf5c8b 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.14.2 + 3.14.3 ``` diff --git a/changes.txt b/changes.txt index 07b9a748..f77342ff 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,13 @@ +jboot v3.14.3: +优化:JbootModel.findByColumn 当传入 null 值 value 时,直接返回 null +优化:Model 保存和之前版本一致的行为,只允许绑定到一个数据源 +优化:当 Model 配置指定数据源,但数据源不存在的时候,给出更为明确的错误提示 +优化:Rabbitmq 添加自由开启队列和广播的开关,默认全部开启 +优化:重构 CORS 模块,使之代码可以用户模块里进行 "复用" +修复: HttpUtil 在 put 请求时,某些情况下无法正常提交数据的问题 + + + jboot v3.14.2: 新增:Informix 数据库方言,感谢 @xujianxie 同学 新增:JbootModel 新增 findByIdWithoutCache() 方法 @@ -7,7 +17,7 @@ jboot v3.14.2: 新增:JsonUtil.java 方便解析 Json 数据 优化:ValidUtil.java,支持在非自定义消息的时候输出拦截的列名 优化:ClassScanner.java 默认关闭 Class 扫描信息在控制台输出 -优化:修改缓存拦截器的默认权重为 100,当前情况下拦截器在最后执行。 +优化:修改缓存拦截器的默认权重为 100,当前情况下拦截器在最后执行 diff --git a/pom.xml b/pom.xml index ebaaaa1e..19523585 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.3-SNAPSHOT + 3.14.3 jar jboot -- Gitee From e2bdb62867c74dc0a67927633b49da68c54f947e Mon Sep 17 00:00:00 2001 From: wtusrss Date: Fri, 1 Apr 2022 14:16:02 +0800 Subject: [PATCH 1646/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96:=20ValidUtil?= =?UTF-8?q?=E4=B8=ADValidatorFactory=E5=88=9D=E5=A7=8B=E5=8C=96=E4=B8=80?= =?UTF-8?q?=E6=AC=A1=EF=BC=8C=E4=BE=9BValidator=E3=80=81MessageInterpolato?= =?UTF-8?q?r=E5=A4=8D=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/components/valid/ValidUtil.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/components/valid/ValidUtil.java b/src/main/java/io/jboot/components/valid/ValidUtil.java index 2394a0f1..c69c20cd 100644 --- a/src/main/java/io/jboot/components/valid/ValidUtil.java +++ b/src/main/java/io/jboot/components/valid/ValidUtil.java @@ -19,21 +19,27 @@ import com.jfinal.kit.Ret; import io.jboot.components.valid.interceptor.SimpleContext; import org.hibernate.validator.HibernateValidator; -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; +import javax.validation.*; import java.util.Set; public class ValidUtil { /** - * 验证器:用于数据验证 + * ValidatorFactory */ - private static Validator validator = Validation.byProvider(HibernateValidator.class) + private static ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class) .configure() .failFast(true) - .buildValidatorFactory() - .getValidator(); + .buildValidatorFactory(); + /** + * 验证器:用于数据验证 + */ + private static Validator validator = validatorFactory.getValidator(); + + /** + * 消息处理器: 用于校验消息处理 + */ + private static MessageInterpolator messageInterpolator = validatorFactory.getMessageInterpolator(); private static int errorCode = 400; @@ -67,8 +73,7 @@ public class ValidUtil { public static void throwValidException(String fieldName, String message, Ret paras, String reason) { if (isParaMessage(message)) { - message = fieldName + " " + Validation.buildDefaultValidatorFactory().getMessageInterpolator() - .interpolate(message, new SimpleContext(paras)); + message = fieldName + " " + messageInterpolator.interpolate(message, new SimpleContext(paras)); } throw new ValidException(message, reason, fieldName); -- Gitee From 9cb91e4fbf5ed545322c0383454daad11b0f6eba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 1 Apr 2022 14:51:14 +0800 Subject: [PATCH 1647/1965] v3.14.4 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 19523585..5630d13d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.3 + 3.14.4-SNAPSHOT jar jboot -- Gitee From 910fa17bcd1fb327d7c61a58a0fd4656e32b9e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 2 Apr 2022 14:46:40 +0800 Subject: [PATCH 1648/1965] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=20HttpUti?= =?UTF-8?q?l=20=E5=9C=A8=20POST=20=E7=9A=84=E6=97=B6=E5=80=99=EF=BC=8C?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E5=8F=82=E6=95=B0=E5=8F=88=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=20body=20=E5=86=85=E5=AE=B9=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E5=A4=B1=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/http/JbootHttpRequest.java | 23 ++++++++++++------- .../components/http/jboot/JbootHttpImpl.java | 15 +++++++----- .../components/http/okhttp/OKHttpImpl.java | 8 ++++--- src/main/java/io/jboot/utils/HttpUtil.java | 2 +- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/jboot/components/http/JbootHttpRequest.java b/src/main/java/io/jboot/components/http/JbootHttpRequest.java index 6189f8a5..ba0f07f4 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpRequest.java +++ b/src/main/java/io/jboot/components/http/JbootHttpRequest.java @@ -69,7 +69,7 @@ public class JbootHttpRequest { private File downloadFile; - private String postContent; + private String bodyContent; // 如果某些时候只是为了去读取 http 头信息,而不需要 http body,可以配置为 false private boolean readBody = true; @@ -286,6 +286,10 @@ public class JbootHttpRequest { return METHOD_PUT.equalsIgnoreCase(method); } + public boolean isPostOrPutRequest() { + return isPostRequest() || isPutRequest(); + } + public String getCharset() { return charset; } @@ -318,17 +322,21 @@ public class JbootHttpRequest { this.contentType = contentType; } - public String getPostContent() { - if (postContent != null) { - appendParasToUrl(); - return postContent; + + public String getBodyContent() { + return bodyContent; + } + + public String getUploadBodyString() { + if (bodyContent != null) { + return bodyContent; } else { return buildParams(); } } - public void setPostContent(String postContent) { - this.postContent = postContent; + public void setBodyContent(String bodyContent) { + this.bodyContent = bodyContent; } @@ -338,7 +346,6 @@ public class JbootHttpRequest { public void appendParasToUrl() { - String params = buildParams(); if (StrUtil.isBlank(params)) { diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index a71c77eb..c223baf3 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -73,16 +73,16 @@ public class JbootHttpImpl implements JbootHttp { //处理文件上传的post提交 if (request.isMultipartFormData()) { if (ArrayUtil.isNotEmpty(request.getParams())) { - uploadData(request, connection); + uploadByMultipart(request, connection); } } //处理正常的post提交 else { - String postContent = request.getPostContent(); - if (StrUtil.isNotEmpty(postContent)) { + String uploadBodyString = request.getUploadBodyString(); + if (StrUtil.isNotEmpty(uploadBodyString)) { try (OutputStream outStream = connection.getOutputStream();) { - outStream.write(postContent.getBytes(request.getCharset())); + outStream.write(uploadBodyString.getBytes(request.getCharset())); outStream.flush(); } } @@ -135,7 +135,7 @@ public class JbootHttpImpl implements JbootHttp { } - private void uploadData(JbootHttpRequest request, HttpURLConnection connection) throws IOException { + private void uploadByMultipart(JbootHttpRequest request, HttpURLConnection connection) throws IOException { String endFlag = "\r\n"; String startFlag = "--"; String boundary = "------" + StrUtil.uuid(); @@ -211,9 +211,12 @@ public class JbootHttpImpl implements JbootHttp { } private static HttpURLConnection getConnection(JbootHttpRequest request) throws Exception { - if (!request.isPostRequest() && !request.isPutRequest()) { + + //get 请求 或者 带有body内容的 post 请求,需要在 url 追加参数 + if (!request.isPostOrPutRequest() || request.getBodyContent() != null) { request.appendParasToUrl(); } + return request.isHttps() ? getHttpsConnection(request) : getHttpConnection(request); } diff --git a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java index b376d337..2a2b4715 100644 --- a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java +++ b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java @@ -51,9 +51,11 @@ public class OKHttpImpl implements JbootHttp { private void doProcess(JbootHttpRequest request, JbootHttpResponse response) { try { - // post 请求 或者 put 请求 - if (request.isPostRequest() || request.isPutRequest()) { + if (request.isPostOrPutRequest()) { + if (request.getBodyContent() != null) { + request.appendParasToUrl(); + } doProcessPostRequest(request, response); } @@ -100,7 +102,7 @@ public class OKHttpImpl implements JbootHttp { // requestBody = builder.build(); MediaType mediaType = MediaType.parse(request.getContentType()); - requestBody = RequestBody.create(mediaType, request.getPostContent()); + requestBody = RequestBody.create(mediaType, request.getUploadBodyString()); } diff --git a/src/main/java/io/jboot/utils/HttpUtil.java b/src/main/java/io/jboot/utils/HttpUtil.java index 6a9e1b08..58856678 100644 --- a/src/main/java/io/jboot/utils/HttpUtil.java +++ b/src/main/java/io/jboot/utils/HttpUtil.java @@ -123,7 +123,7 @@ public class HttpUtil { */ public static String httpPost(String url, Map paras, Map headers, String postData) { JbootHttpRequest request = JbootHttpRequest.create(url, paras, JbootHttpRequest.METHOD_POST); - request.setPostContent(postData); + request.setBodyContent(postData); request.addHeaders(headers); JbootHttpResponse response = handle(request); return response.isError() ? null : response.getContent(); -- Gitee From 22349824feb4fd3f678508982da42478cffc0757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 5 Apr 2022 15:17:43 +0800 Subject: [PATCH 1649/1965] optimize... --- .../components/event/JbootEventManager.java | 28 ++++++++++++------- .../io/jboot/test/http/HttpUtilTester.java | 13 +++++++++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/jboot/components/event/JbootEventManager.java b/src/main/java/io/jboot/components/event/JbootEventManager.java index 1603331f..9cc26c2c 100644 --- a/src/main/java/io/jboot/components/event/JbootEventManager.java +++ b/src/main/java/io/jboot/components/event/JbootEventManager.java @@ -27,19 +27,23 @@ import java.util.concurrent.*; public class JbootEventManager { - private final ExecutorService threadPool; + private static final Log LOG = Log.getLog(JbootEventManager.class); + private static JbootEventManager manager; + private final Map> asyncListenerMap; private final Map> listenerMap; - private static final Log log = Log.getLog(JbootEventManager.class); - private static JbootEventManager manager; + + private ExecutorService threadPool; + public JbootEventManager() { + asyncListenerMap = new ConcurrentHashMap<>(); + listenerMap = new ConcurrentHashMap<>(); + threadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), new NamedThreadFactory("jboot-event")); - asyncListenerMap = new ConcurrentHashMap<>(); - listenerMap = new ConcurrentHashMap<>(); initListeners(); } @@ -67,7 +71,7 @@ public class JbootEventManager { deleteListner(asyncListenerMap, listenerClass); if (Jboot.isDevMode()) { - log.debug(String.format("listener[%s]-->>unRegisterListener.", listenerClass)); + LOG.debug(String.format("listener[%s]-->>unRegisterListener.", listenerClass)); } } @@ -100,7 +104,7 @@ public class JbootEventManager { String[] actions = AnnotationUtil.get(listenerAnnotation.action()); if (actions == null) { - log.warn("listenerClass[" + listenerAnnotation + "] register fail, because action is null or blank."); + LOG.warn("listenerClass[" + listenerAnnotation + "] register fail, because action is null or blank."); return; } @@ -154,7 +158,7 @@ public class JbootEventManager { } if (Jboot.isDevMode()) { - log.debug(String.format("listener[%s]-->>registered.", eventListener)); + LOG.debug(String.format("listener[%s]-->>registered.", eventListener)); } } @@ -199,7 +203,7 @@ public class JbootEventManager { try { listener.onEvent(event); } catch (Throwable e) { - log.error(String.format("listener[%s] onEvent is error! ", listener.getClass()), e); + LOG.error(String.format("listener[%s] onEvent is error! ", listener.getClass()), e); } } } @@ -210,7 +214,7 @@ public class JbootEventManager { try { listener.onEvent(event); } catch (Throwable e) { - log.error(String.format("listener[%s] onEvent is error! ", listener.getClass()), e); + LOG.error(String.format("listener[%s] onEvent is error! ", listener.getClass()), e); } }); } @@ -219,4 +223,8 @@ public class JbootEventManager { public ExecutorService getThreadPool() { return threadPool; } + + public void setThreadPool(ExecutorService threadPool) { + this.threadPool = threadPool; + } } diff --git a/src/test/java/io/jboot/test/http/HttpUtilTester.java b/src/test/java/io/jboot/test/http/HttpUtilTester.java index 948cf7f2..a9b3d222 100644 --- a/src/test/java/io/jboot/test/http/HttpUtilTester.java +++ b/src/test/java/io/jboot/test/http/HttpUtilTester.java @@ -5,6 +5,7 @@ import io.jboot.components.http.HttpProxyInfo; import io.jboot.components.http.JbootHttpManager; import io.jboot.components.http.JbootHttpRequest; import io.jboot.components.http.JbootHttpResponse; +import io.jboot.utils.HttpUtil; import io.jboot.utils.StrUtil; import java.util.HashMap; @@ -37,6 +38,18 @@ public class HttpUtilTester { System.out.println(StrUtil.mapToQueryString(map)); + Map paras = new HashMap<>(); + paras.put("key","value"); + + Map headers = new HashMap<>(); + headers.put("key","value"); + + String postData = "abc"; + + + String s = HttpUtil.httpPost("https://www.baidu.com", paras, headers, postData); + System.out.println(s); + } -- Gitee From 0190729d06f05a20ea953ad6c099d70088a13b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 6 Apr 2022 09:50:48 +0800 Subject: [PATCH 1650/1965] upgrade jfinal and undertow --- pom.xml | 10 +++++----- .../java/io/jboot/web/render/JbootRenderFactory.java | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 5630d13d..b3281e06 100644 --- a/pom.xml +++ b/pom.xml @@ -41,19 +41,19 @@ - 4.9.21 + 4.9.22 2022.2 - 2.8 + 2.9 3.4 3.3.0 4.0.3 2.13.2 5.1.49 - 2.2.16.Final + 2.2.17.Final 1.7.36 2.57 - 1.2.79 + 1.2.80 31.1-jre 2.2.5 3.8.0 @@ -157,7 +157,7 @@ com.fasterxml.jackson.core jackson-databind - ${jackson.version} + 2.13.2.2 diff --git a/src/main/java/io/jboot/web/render/JbootRenderFactory.java b/src/main/java/io/jboot/web/render/JbootRenderFactory.java index 0251e0b0..4b4c27d1 100644 --- a/src/main/java/io/jboot/web/render/JbootRenderFactory.java +++ b/src/main/java/io/jboot/web/render/JbootRenderFactory.java @@ -17,6 +17,7 @@ package io.jboot.web.render; import com.jfinal.core.Action; import com.jfinal.render.ContentType; +import com.jfinal.render.ErrorRender; import com.jfinal.render.Render; import com.jfinal.render.RenderFactory; import io.jboot.components.valid.ValidErrorRender; @@ -63,7 +64,7 @@ public class JbootRenderFactory extends RenderFactory { @Override public Render getErrorRender(int errorCode) { - return new JbootErrorRender(errorCode, constants.getErrorView(errorCode)); + return new JbootErrorRender(errorCode, ErrorRender.getErrorView(errorCode)); } @Override -- Gitee From 4162363e632f18117bd4331dfde19b94f2afddb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 6 Apr 2022 10:30:47 +0800 Subject: [PATCH 1651/1965] update docs --- changes.txt | 2 +- doc/docs/db.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/changes.txt b/changes.txt index f77342ff..40f6f394 100644 --- a/changes.txt +++ b/changes.txt @@ -867,7 +867,7 @@ jboot v3.2.8: 修复:Seata 对 dubbo 的 SPI 过滤器文件名错误的问题 优化:当 jwt 解析出错时,输出错误的日志信息 优化:重命名 joinById() 为 joinByValue(),因为传入的值不一定只是 id -文档:新增 一多一、一对多、多对对查询的相关文档 +文档:新增 一多一、一对多、多对多查询的相关文档 diff --git a/doc/docs/db.md b/doc/docs/db.md index ebdaa62c..1fb31814 100644 --- a/doc/docs/db.md +++ b/doc/docs/db.md @@ -11,7 +11,7 @@ Jboot 数据库功能基于 JFinal 的 ActiveRecordPlugin 插件和 Apache shard - Columns 查询方式 - 关联查询 - 分页查询 -- 一对一、一对多、多对一、多对对 +- 一对一、一对多、多对一、多对多 - 事务操作 - 多数据源 - 读写分离 @@ -223,9 +223,9 @@ public List findListBy(int userAge,String articleTitle){ -## 一对一、一对多、多对一、多对对 +## 一对一、一对多、多对一、多对多 -在 Jboot 中,提供了 Join 系列方法,我们在 Service 层可以直接使用 Join 进行 一对一、一对多、多对一、多对对 的查询操作。 +在 Jboot 中,提供了 Join 系列方法,我们在 Service 层可以直接使用 Join 进行 一对一、一对多、多对一、多对多 的查询操作。 假设存在这样的关系:一篇文章只有一个作者,一个作者可以写多篇文章,一篇文章可以归属多个文章分类、一个文章分类有可以包含多篇文章。 @@ -275,7 +275,7 @@ CREATE TABLE `category` ( -文章分类和分类的 多对对关系表: article_category: +文章分类和分类的 多对多关系表: article_category: ```sql CREATE TABLE `article_category` ( -- Gitee From d3cd769dc93fd066f79019146e7501f5c72b7fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 6 Apr 2022 12:20:53 +0800 Subject: [PATCH 1652/1965] =?UTF-8?q?feature=EF=BC=9A=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E5=BC=80=E5=90=AF=20Controller=20Action=20?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E7=9A=84=E5=BC=80=E5=85=B3=EF=BC=8C=E6=96=B9?= =?UTF-8?q?=E4=BE=BF=E5=9C=A8=E4=B8=8D=E5=90=8C=E7=9A=84=E5=9C=BA=E6=99=AF?= =?UTF-8?q?=E4=B8=8B=E8=BF=9B=E8=A1=8C=E5=BC=80=E5=90=AF=E6=88=96=E8=80=85?= =?UTF-8?q?=E5=85=B3=E9=97=AD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/cache/JbootCacheConfig.java | 3 ++- .../cache/interceptor/CachePutInterceptor.java | 4 +++- .../interceptor/CacheableInterceptor.java | 18 +++++++++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java index de8a49fe..cc729e92 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java @@ -33,7 +33,8 @@ public class JbootCacheConfig { public static final String TYPE_CAREDIS = "caredis"; public static final String TYPE_NONE = "none"; - public static final Set TYPES = Sets.newHashSet(TYPE_EHCACHE, TYPE_REDIS, TYPE_EHREDIS, TYPE_J2CACHE, TYPE_CAFFEINE, TYPE_CAREDIS, TYPE_NONE); + public static final Set TYPES = Sets.newHashSet(TYPE_EHCACHE, TYPE_REDIS, TYPE_EHREDIS + , TYPE_J2CACHE, TYPE_CAFFEINE, TYPE_CAREDIS, TYPE_NONE); private String name = "default"; private String type = TYPE_CAFFEINE; diff --git a/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java index 2bd37ce5..6eba5e66 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java @@ -43,7 +43,9 @@ public class CachePutInterceptor implements Interceptor { } if (inv.isActionInvocation()) { - forController(inv, method, cachePut); + if (CacheableInterceptor.isActionCacheEnable()) { + forController(inv, method, cachePut); + } inv.invoke(); } else { inv.invoke(); diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java index 374ea825..4d529ece 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java @@ -47,6 +47,18 @@ public class CacheableInterceptor implements Interceptor { private static final String NULL_VALUE = "NULL_VALUE"; + //是否开启 Controller 的 Action 缓存 + //可用在 dev 模式下关闭,生产环境开启的场景,方便调试数据 + private static boolean actionCacheEnable = true; + + public static boolean isActionCacheEnable() { + return actionCacheEnable; + } + + public static void setActionCacheEnable(boolean actionCacheEnable) { + CacheableInterceptor.actionCacheEnable = actionCacheEnable; + } + @Override public void intercept(Invocation inv) { @@ -58,7 +70,11 @@ public class CacheableInterceptor implements Interceptor { } if (inv.isActionInvocation()) { - forController(inv, method, cacheable); + if (actionCacheEnable) { + forController(inv, method, cacheable); + } else { + inv.invoke(); + } } else { forService(inv, method, cacheable); } -- Gitee From 463c0b2f27255602fee202eba7142f55a2670b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 6 Apr 2022 12:25:52 +0800 Subject: [PATCH 1653/1965] =?UTF-8?q?feature=EF=BC=9A=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E5=BC=80=E5=90=AF=20Controller=20Action=20?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E7=9A=84=E5=BC=80=E5=85=B3=EF=BC=8C=E6=96=B9?= =?UTF-8?q?=E4=BE=BF=E5=9C=A8=E4=B8=8D=E5=90=8C=E7=9A=84=E5=9C=BA=E6=99=AF?= =?UTF-8?q?=E4=B8=8B=E8=BF=9B=E8=A1=8C=E5=BC=80=E5=90=AF=E6=88=96=E8=80=85?= =?UTF-8?q?=E5=85=B3=E9=97=AD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cache/interceptor/CachePutInterceptor.java | 13 ++++++------- .../cache/interceptor/CacheableInterceptor.java | 8 ++------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java index 6eba5e66..e69da34c 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java @@ -37,25 +37,20 @@ public class CachePutInterceptor implements Interceptor { Method method = inv.getMethod(); CachePut cachePut = method.getAnnotation(CachePut.class); - if (cachePut == null) { + if (cachePut == null || (inv.isActionInvocation() && !CacheableInterceptor.isActionCacheEnable())) { inv.invoke(); return; } if (inv.isActionInvocation()) { - if (CacheableInterceptor.isActionCacheEnable()) { - forController(inv, method, cachePut); - } - inv.invoke(); + forController(inv, method, cachePut); } else { - inv.invoke(); forService(inv, method, cachePut); } } private void forController(Invocation inv, Method method, CachePut cachePut) { - String unless = AnnotationUtil.get(cachePut.unless()); if (Utils.isUnless(unless, method, inv.getArgs())) { return; @@ -76,10 +71,14 @@ public class CachePutInterceptor implements Interceptor { //让 Controller 持有缓存的 responseProxy CPI._init_(controller, CPI.getAction(controller), controller.getRequest(), responseProxy, controller.getPara()); + inv.invoke(); } private void forService(Invocation inv, Method method, CachePut cachePut) { + + inv.invoke(); + Object data = inv.getReturnValue(); String unless = AnnotationUtil.get(cachePut.unless()); diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java index 4d529ece..5b64a830 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java @@ -64,17 +64,13 @@ public class CacheableInterceptor implements Interceptor { Method method = inv.getMethod(); Cacheable cacheable = method.getAnnotation(Cacheable.class); - if (cacheable == null) { + if (cacheable == null || (inv.isActionInvocation() && !actionCacheEnable)) { inv.invoke(); return; } if (inv.isActionInvocation()) { - if (actionCacheEnable) { - forController(inv, method, cacheable); - } else { - inv.invoke(); - } + forController(inv, method, cacheable); } else { forService(inv, method, cacheable); } -- Gitee From 4b2c0f3d8515fd13582fd7f43c89113e1af3b63a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 7 Apr 2022 17:06:13 +0800 Subject: [PATCH 1654/1965] =?UTF-8?q?feature=EF=BC=9A=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E9=94=99=E8=AF=AF=E6=B8=B2=E6=9F=93=E5=99=A8?= =?UTF-8?q?=20TemplateErrorRender?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/web/handler/JbootActionHandler.java | 32 +++++++++++++++---- .../jboot/web/render/JbootRenderFactory.java | 7 +++- .../jboot/web/render/TemplateErrorRender.java | 27 ++++++++++++++++ 3 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 src/main/java/io/jboot/web/render/TemplateErrorRender.java diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 009b4002..ef28f021 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -21,6 +21,7 @@ import com.jfinal.log.Log; import com.jfinal.render.IRenderFactory; import com.jfinal.render.Render; import com.jfinal.render.RenderException; +import com.jfinal.template.TemplateException; import io.jboot.app.JbootApplicationConfig; import io.jboot.components.cache.ActionCache; import io.jboot.components.valid.ValidErrorRender; @@ -150,6 +151,8 @@ public class JbootActionHandler extends ActionHandler { handleActionException(target, request, response, action, e); } catch (ValidException e) { handleValidException(target, request, response, action, e); + } catch (TemplateException e) { + handleTemplateException(target, request, response, action, e); } catch (Exception e) { handleException(target, request, response, action, e); } finally { @@ -298,13 +301,30 @@ public class JbootActionHandler extends ActionHandler { } - protected void handleException(String target, HttpServletRequest request, HttpServletResponse response, Action action, Exception e) { - if (LOG.isErrorEnabled()) { - String qs = request.getQueryString(); - String targetInfo = qs == null ? target : target + "?" + qs; - String info = ClassUtil.buildMethodString(action.getMethod()); - LOG.error(info + " \nQuery: " + targetInfo + "\n", e); + /** + * 处理模板错误 + */ + protected void handleTemplateException(String target, HttpServletRequest request, HttpServletResponse response, Action action, TemplateException e) { + String qs = request.getQueryString(); + String targetInfo = qs == null ? target : target + "?" + qs; + String info = ClassUtil.buildMethodString(action.getMethod()); + LOG.error(info + " \nQuery: " + targetInfo + "\n", e); + + IRenderFactory factory = renderManager.getRenderFactory(); + if (factory instanceof JbootRenderFactory) { + ((JbootRenderFactory) factory).getTemplateErrorRender(e).setContext(request, response, action.getViewPath()).render(); } + } + + + /** + * 处理其他业务错误 + */ + protected void handleException(String target, HttpServletRequest request, HttpServletResponse response, Action action, Exception e) { + String qs = request.getQueryString(); + String targetInfo = qs == null ? target : target + "?" + qs; + String info = ClassUtil.buildMethodString(action.getMethod()); + LOG.error(info + " \nQuery: " + targetInfo + "\n", e); renderManager.getRenderFactory().getErrorRender(500).setContext(request, response, action.getViewPath()).render(); } diff --git a/src/main/java/io/jboot/web/render/JbootRenderFactory.java b/src/main/java/io/jboot/web/render/JbootRenderFactory.java index 4b4c27d1..906275b4 100644 --- a/src/main/java/io/jboot/web/render/JbootRenderFactory.java +++ b/src/main/java/io/jboot/web/render/JbootRenderFactory.java @@ -20,6 +20,7 @@ import com.jfinal.render.ContentType; import com.jfinal.render.ErrorRender; import com.jfinal.render.Render; import com.jfinal.render.RenderFactory; +import com.jfinal.template.TemplateException; import io.jboot.components.valid.ValidErrorRender; import io.jboot.components.valid.ValidException; @@ -137,9 +138,13 @@ public class JbootRenderFactory extends RenderFactory { return new JbootReturnValueRender(action, returnValue); } - public ValidErrorRender getValidErrorRender(ValidException validException){ + public ValidErrorRender getValidErrorRender(ValidException validException) { return new ValidErrorRender(validException); } + public TemplateErrorRender getTemplateErrorRender(TemplateException e) { + return new TemplateErrorRender(e); + } + } diff --git a/src/main/java/io/jboot/web/render/TemplateErrorRender.java b/src/main/java/io/jboot/web/render/TemplateErrorRender.java new file mode 100644 index 00000000..b93a45e9 --- /dev/null +++ b/src/main/java/io/jboot/web/render/TemplateErrorRender.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.render; + +import com.jfinal.render.TextRender; +import com.jfinal.template.TemplateException; + +public class TemplateErrorRender extends TextRender { + + public TemplateErrorRender(TemplateException e) { + super(e.getMessage()); + } + +} -- Gitee From e541c1e2db6b89708cc1c966d6b85b3323961f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 7 Apr 2022 17:14:56 +0800 Subject: [PATCH 1655/1965] v3.14.4 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 04d27d13..d7dbae70 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.14.3 + 3.14.4 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 4de0ec96..126e5a64 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.14.3 + 3.14.4 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 9341be97..55057922 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.14.3 + 3.14.4 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.14.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.14.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index f56b9108..df59d6cc 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.14.3', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.14.4', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.3/jboot-3.14.3.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.4/jboot-3.14.4.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index c69ca52f..fd7f7ce3 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.14.3 + 3.14.4 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 3448c829..b5001e9c 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.14.3"; + public static String VERSION = "3.14.4"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From d60c6afa90f3b0de6d9bd9789ac17704a78a37f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 7 Apr 2022 17:15:01 +0800 Subject: [PATCH 1656/1965] v3.14.4 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 11 +++++++++++ pom.xml | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4edf5c8b..2a4f0aec 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.14.3 + 3.14.4 ``` diff --git a/changes.txt b/changes.txt index 40f6f394..8ed7b404 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,14 @@ +jboot v3.14.4: +新增:模板错误渲染器 TemplateErrorRender,用于追加模板指令错误内容 +新增:是否开启 Controller Action 缓存的开关,方便在不同的场景下进行开启或者关闭 +新增:JbootEventManager 可以设置自己的线程池 +优化:ValidUtil 中ValidatorFactory初始化一次,供Validator、MessageInterpolator复用,感谢 @wtusrss +优化:升级 JFinal/JFinal-Undertow 等到最新版本 +修复:HttpUtil 在 POST 的时候,添加了参数又设置 body 内容时,参数失效的问题 +文档:修改文档错别字 + + + jboot v3.14.3: 优化:JbootModel.findByColumn 当传入 null 值 value 时,直接返回 null 优化:Model 保存和之前版本一致的行为,只允许绑定到一个数据源 diff --git a/pom.xml b/pom.xml index b3281e06..258c94f0 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.4-SNAPSHOT + 3.14.4 jar jboot -- Gitee From e777a5797d84ccad83f345c14d00a0554e2e49a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 7 Apr 2022 17:29:36 +0800 Subject: [PATCH 1657/1965] v3.14.4 release (^.^)YYa!! --- .../jboot/web/render/TemplateErrorRender.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/web/render/TemplateErrorRender.java b/src/main/java/io/jboot/web/render/TemplateErrorRender.java index b93a45e9..242ee3eb 100644 --- a/src/main/java/io/jboot/web/render/TemplateErrorRender.java +++ b/src/main/java/io/jboot/web/render/TemplateErrorRender.java @@ -15,13 +15,32 @@ */ package io.jboot.web.render; -import com.jfinal.render.TextRender; +import com.jfinal.render.Render; +import com.jfinal.render.RenderException; import com.jfinal.template.TemplateException; -public class TemplateErrorRender extends TextRender { +import java.io.IOException; +import java.io.PrintWriter; + +public class TemplateErrorRender extends Render { + + private final TemplateException exception; public TemplateErrorRender(TemplateException e) { - super(e.getMessage()); + this.exception = e; } + @Override + public void render() { + try { + PrintWriter writer = response.getWriter(); + String message = exception.getMessage(); + if (message != null) { + message = message.replace("\n", "
        "); + } + writer.write("TemplateException: " + message); + } catch (IOException e) { + throw new RenderException(e); + } + } } -- Gitee From ec782ee5a99cb107f18addaf2b07d554a153d0aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 7 Apr 2022 17:45:06 +0800 Subject: [PATCH 1658/1965] v3.14.4 release (^.^)YYa!! --- changes.txt | 1 + .../directive/base/JbootDirectiveBase.java | 27 ++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 8ed7b404..83ec24bd 100644 --- a/changes.txt +++ b/changes.txt @@ -1,4 +1,5 @@ jboot v3.14.4: +新增:生产环境忽略模板指令渲染错误的功能,保证其他内容正常渲染 新增:模板错误渲染器 TemplateErrorRender,用于追加模板指令错误内容 新增:是否开启 Controller Action 缓存的开关,方便在不同的场景下进行开启或者关闭 新增:JbootEventManager 可以设置自己的线程池 diff --git a/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java b/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java index 65f3a161..fdff7e77 100644 --- a/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java +++ b/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java @@ -16,12 +16,14 @@ package io.jboot.web.directive.base; import com.jfinal.aop.Aop; +import com.jfinal.log.Log; import com.jfinal.template.Directive; import com.jfinal.template.Env; import com.jfinal.template.TemplateException; import com.jfinal.template.expr.ast.ExprList; import com.jfinal.template.io.Writer; import com.jfinal.template.stat.Scope; +import io.jboot.Jboot; import io.jboot.utils.StrUtil; import java.io.IOException; @@ -34,6 +36,17 @@ import java.util.Map; */ public abstract class JbootDirectiveBase extends Directive { + private static final Log LOG = Log.getLog(JbootDirectiveBase.class); + + private static boolean devMode = Jboot.isDevMode(); + + public static boolean isDevMode() { + return devMode; + } + + public static void setDevMode(boolean devMode) { + JbootDirectiveBase.devMode = devMode; + } public JbootDirectiveBase() { Aop.inject(this); @@ -51,7 +64,19 @@ public abstract class JbootDirectiveBase extends Directive { scope = new Scope(scope); scope.getCtrl().setLocalAssignment(); exprList.eval(scope); - onRender(env, scope, writer); + + try { + onRender(env, scope, writer); + } catch (Throwable e) { + if (devMode) { + throw e; + } + // 生产环境下,忽略指令错误渲染 + else { + LOG.error("Template Directive render error!!!", e); + } + } + } -- Gitee From 5225959b2e91686f2bc7dbc5eef7e6838f2e3c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 16 Apr 2022 16:23:30 +0800 Subject: [PATCH 1659/1965] v3.14.5 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 258c94f0..53368132 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.4 + 3.14.5 jar jboot -- Gitee From 400ae75a8140a107eeb2b814c75c4bc2276fbaf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 16 Apr 2022 16:24:00 +0800 Subject: [PATCH 1660/1965] optimize json... --- src/main/java/io/jboot/utils/JsonUtil.java | 77 +++++++++++-------- .../java/io/jboot/web/json/JbootJson.java | 7 +- .../web/json/JsonBodyParseInterceptor.java | 70 +++++++++-------- 3 files changed, 90 insertions(+), 64 deletions(-) diff --git a/src/main/java/io/jboot/utils/JsonUtil.java b/src/main/java/io/jboot/utils/JsonUtil.java index 651ae208..383f76aa 100644 --- a/src/main/java/io/jboot/utils/JsonUtil.java +++ b/src/main/java/io/jboot/utils/JsonUtil.java @@ -24,6 +24,7 @@ import io.jboot.web.json.JsonBodyParseInterceptor; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Date; +import java.util.List; /** * 基于 FastJson,方便解析 Json 内容 @@ -31,45 +32,45 @@ import java.util.Date; * 例如: *

        * { - * "array": [ - * 1, - * 2, - * 3 - * ], - * "type": true, - * "null": null, - * "number": 123, - * "object": { - * "a": "b", - * "c": "d", - * "e":1 - * }, - * "key": "welcome to CodeFormat.CN" - * } - * + * "array": [ + * 1, + * 2, + * 3 + * ], + * "type": true, + * "null": null, + * "number": 123, + * "object": { + * "a": "b", + * "c": "d", + * "e":1 + * }, + * "key": "welcome to CodeFormat.CN" + * } + *

        * Boolean type = JsonUtil.getBool(json,"type"); * //type == true - * + *

        * int e = JsonUtil.getInt(json,"object.e") * // e == 1 - * + *

        * BigInteger n = JsonUtil.getBigInteger("number") * // n == 123 - * + *

        * String[] array = JsonUtil.get(json,"array",String[].class) * //array == ["1","2","3"] - * + *

        * int[] array = JsonUtil.get(json,"array",int[].class) - * //array == [1,2,3] - * - * Map map = JsonUtil.get(json,"object",Map.class) - * //map == {"a":"b","c":"d","e":1} - * - * int x = JsonUtil.getInt(json,"array[1]"); - * // x == 2 - * - * String key = JsonUtil.getString(json,"key"); - * // key == "welcome to CodeFormat.CN" + * //array == [1,2,3] + *

        + * Map map = JsonUtil.get(json,"object",Map.class) + * //map == {"a":"b","c":"d","e":1} + *

        + * int x = JsonUtil.getInt(json,"array[1]"); + * // x == 2 + *

        + * String key = JsonUtil.getString(json,"key"); + * // key == "welcome to CodeFormat.CN" */ public class JsonUtil { @@ -258,6 +259,16 @@ public class JsonUtil { } + public static List getList(String json, String key, Class clazz) { + return getList(getJsonObjectOrArray(json), key, clazz); + } + + + public static List getList(Object jsonObjectOrArray, String key, Class clazz) { + return get(jsonObjectOrArray, key, new TypeDef>() {}); + } + + public static T get(String json, String key, Class clazz) { return get(getJsonObjectOrArray(json), key, clazz); } @@ -304,4 +315,10 @@ public class JsonUtil { } + public static void main(String[] args) { + List list1 = getList("{}", "", JsonUtil.class); + List list2 = get("{}", "", new TypeDef>(){}); + + } + } diff --git a/src/main/java/io/jboot/web/json/JbootJson.java b/src/main/java/io/jboot/web/json/JbootJson.java index a9284bab..d09cb55a 100644 --- a/src/main/java/io/jboot/web/json/JbootJson.java +++ b/src/main/java/io/jboot/web/json/JbootJson.java @@ -24,6 +24,7 @@ import com.jfinal.kit.StrKit; import com.jfinal.plugin.activerecord.CPI; import com.jfinal.plugin.activerecord.Model; import io.jboot.Jboot; +import io.jboot.db.model.JbootModel; import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; @@ -160,7 +161,11 @@ public class JbootJson extends JFinalJson { if (method.getParameterCount() != 0 || method.getReturnType() == void.class || !Modifier.isPublic(method.getModifiers()) - || "getClass".equals(method.getName())) { + || "getClass".equals(method.getName()) + || method.getDeclaringClass() == JbootModel.class + || method.getDeclaringClass() == Model.class + || method.getDeclaringClass() == Object.class + ) { continue; } diff --git a/src/main/java/io/jboot/web/json/JsonBodyParseInterceptor.java b/src/main/java/io/jboot/web/json/JsonBodyParseInterceptor.java index 7205874e..87ad7023 100644 --- a/src/main/java/io/jboot/web/json/JsonBodyParseInterceptor.java +++ b/src/main/java/io/jboot/web/json/JsonBodyParseInterceptor.java @@ -178,44 +178,48 @@ public class JsonBodyParseInterceptor implements Interceptor, InterceptorBuilder private static Object parseArray(Object rawJsonObjectOrArray, Class typeClass, Type type, String jsonKey) { JSONArray jsonArray = null; if (StrUtil.isBlank(jsonKey)) { - jsonArray = (JSONArray) rawJsonObjectOrArray; + if (rawJsonObjectOrArray instanceof JSONArray) { + jsonArray = (JSONArray) rawJsonObjectOrArray; + } } else { - JSONObject rawObject = (JSONObject) rawJsonObjectOrArray; - String[] keys = jsonKey.split("\\."); - for (int i = 0; i < keys.length; i++) { - if (rawObject == null || rawObject.isEmpty()) { - break; - } - String key = keys[i].trim(); - if (StrUtil.isNotBlank(key)) { - //the last - if (i == keys.length - 1) { - if (key.endsWith(endOfArray) && key.contains(startOfArray)) { - String realKey = key.substring(0, key.indexOf(startOfArray)); - JSONArray jarray = rawObject.getJSONArray(realKey.trim()); - if (jarray == null || jarray.isEmpty()) { - return null; - } - String subKey = key.substring(key.indexOf(startOfArray) + 1, key.length() - 1).trim(); - if (StrUtil.isBlank(subKey)) { - throw new IllegalStateException("Sub key can not empty: " + jsonKey); - } + if (rawJsonObjectOrArray instanceof JSONObject) { + JSONObject rawObject = (JSONObject) rawJsonObjectOrArray; + String[] keys = jsonKey.split("\\."); + for (int i = 0; i < keys.length; i++) { + if (rawObject == null || rawObject.isEmpty()) { + break; + } + String key = keys[i].trim(); + if (StrUtil.isNotBlank(key)) { + //the last + if (i == keys.length - 1) { + if (key.endsWith(endOfArray) && key.contains(startOfArray)) { + String realKey = key.substring(0, key.indexOf(startOfArray)); + JSONArray jarray = rawObject.getJSONArray(realKey.trim()); + if (jarray == null || jarray.isEmpty()) { + return null; + } + String subKey = key.substring(key.indexOf(startOfArray) + 1, key.length() - 1).trim(); + if (StrUtil.isBlank(subKey)) { + throw new IllegalStateException("Sub key can not empty: " + jsonKey); + } - JSONArray newJsonArray = new JSONArray(); - for (int j = 0; j < jarray.size(); j++) { - Object value = jarray.getJSONObject(j).get(subKey); - if (value != null) { - newJsonArray.add(value); + JSONArray newJsonArray = new JSONArray(); + for (int j = 0; j < jarray.size(); j++) { + Object value = jarray.getJSONObject(j).get(subKey); + if (value != null) { + newJsonArray.add(value); + } } + jsonArray = newJsonArray; + } else { + jsonArray = rawObject.getJSONArray(key); } - jsonArray = newJsonArray; - } else { - jsonArray = rawObject.getJSONArray(key); } - } - //not last - else { - rawObject = getJSONObjectByKey(rawObject, key); + //not last + else { + rawObject = getJSONObjectByKey(rawObject, key); + } } } } -- Gitee From 31e4c7c685e6d5dc09891e03b459c17b2982f806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 17 Apr 2022 16:18:51 +0800 Subject: [PATCH 1661/1965] optimize json... --- src/main/java/io/jboot/utils/JsonUtil.java | 19 ++-- src/main/java/io/jboot/utils/TypeDef.java | 106 +++++++++++++----- .../io/jboot/test/json/JsonUtilTester.java | 43 +++++++ src/test/java/io/jboot/test/json/MyBean.java | 11 ++ 4 files changed, 147 insertions(+), 32 deletions(-) create mode 100644 src/test/java/io/jboot/test/json/JsonUtilTester.java diff --git a/src/main/java/io/jboot/utils/JsonUtil.java b/src/main/java/io/jboot/utils/JsonUtil.java index 383f76aa..fb3daae8 100644 --- a/src/main/java/io/jboot/utils/JsonUtil.java +++ b/src/main/java/io/jboot/utils/JsonUtil.java @@ -25,6 +25,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.util.Date; import java.util.List; +import java.util.Set; /** * 基于 FastJson,方便解析 Json 内容 @@ -265,7 +266,17 @@ public class JsonUtil { public static List getList(Object jsonObjectOrArray, String key, Class clazz) { - return get(jsonObjectOrArray, key, new TypeDef>() {}); + return get(jsonObjectOrArray, key, TypeDef.wrapper(List.class, clazz)); + } + + + public static Set getSet(String json, String key, Class clazz) { + return getSet(getJsonObjectOrArray(json), key, clazz); + } + + + public static Set getSet(Object jsonObjectOrArray, String key, Class clazz) { + return get(jsonObjectOrArray, key, TypeDef.wrapper(Set.class, clazz)); } @@ -315,10 +326,4 @@ public class JsonUtil { } - public static void main(String[] args) { - List list1 = getList("{}", "", JsonUtil.class); - List list2 = get("{}", "", new TypeDef>(){}); - - } - } diff --git a/src/main/java/io/jboot/utils/TypeDef.java b/src/main/java/io/jboot/utils/TypeDef.java index da4b7f83..ddcb38d6 100644 --- a/src/main/java/io/jboot/utils/TypeDef.java +++ b/src/main/java/io/jboot/utils/TypeDef.java @@ -25,36 +25,58 @@ import java.util.Set; public class TypeDef { - public static final TypeDef LIST_STRING = new TypeDef>() {}; - public static final TypeDef LIST_INTEGER = new TypeDef>() {}; - public static final TypeDef LIST_LONG = new TypeDef>() {}; - public static final TypeDef LIST_DOUBLE = new TypeDef>() {}; - public static final TypeDef LIST_FLOAT= new TypeDef>() {}; - public static final TypeDef LIST_BIGINTEGER = new TypeDef>() {}; - public static final TypeDef LIST_BIGDECIMAL = new TypeDef>() {}; - - - public static final TypeDef SET_STRING = new TypeDef>() {}; - public static final TypeDef SET_INTEGER = new TypeDef>() {}; - public static final TypeDef SET_LONG = new TypeDef>() {}; - public static final TypeDef SET_DOUBLE = new TypeDef>() {}; - public static final TypeDef SET_FLOAT = new TypeDef>() {}; - public static final TypeDef SET_BIGINTEGER = new TypeDef>() {}; - public static final TypeDef SET_BIGDECIMAL = new TypeDef>() {}; - - - public static final TypeDef MAP_STRING = new TypeDef>() {}; - public static final TypeDef MAP_INTEGER = new TypeDef>() {}; - public static final TypeDef MAP_LONG = new TypeDef>() {}; - public static final TypeDef MAP_DOUBLE = new TypeDef>() {}; - public static final TypeDef MAP_FLOAT= new TypeDef>() {}; - public static final TypeDef MAP_BIGINTEGER = new TypeDef>() {}; - public static final TypeDef MAP_BIGDECIMAL = new TypeDef>() {}; + public static final TypeDef LIST_STRING = new TypeDef>() { + }; + public static final TypeDef LIST_INTEGER = new TypeDef>() { + }; + public static final TypeDef LIST_LONG = new TypeDef>() { + }; + public static final TypeDef LIST_DOUBLE = new TypeDef>() { + }; + public static final TypeDef LIST_FLOAT = new TypeDef>() { + }; + public static final TypeDef LIST_BIGINTEGER = new TypeDef>() { + }; + public static final TypeDef LIST_BIGDECIMAL = new TypeDef>() { + }; + + + public static final TypeDef SET_STRING = new TypeDef>() { + }; + public static final TypeDef SET_INTEGER = new TypeDef>() { + }; + public static final TypeDef SET_LONG = new TypeDef>() { + }; + public static final TypeDef SET_DOUBLE = new TypeDef>() { + }; + public static final TypeDef SET_FLOAT = new TypeDef>() { + }; + public static final TypeDef SET_BIGINTEGER = new TypeDef>() { + }; + public static final TypeDef SET_BIGDECIMAL = new TypeDef>() { + }; + + + public static final TypeDef MAP_STRING = new TypeDef>() { + }; + public static final TypeDef MAP_INTEGER = new TypeDef>() { + }; + public static final TypeDef MAP_LONG = new TypeDef>() { + }; + public static final TypeDef MAP_DOUBLE = new TypeDef>() { + }; + public static final TypeDef MAP_FLOAT = new TypeDef>() { + }; + public static final TypeDef MAP_BIGINTEGER = new TypeDef>() { + }; + public static final TypeDef MAP_BIGDECIMAL = new TypeDef>() { + }; protected Type type; protected Class defClass; + protected TypeDef() { Type superClass = getClass().getGenericSuperclass(); if (superClass == TypeDef.class) { @@ -73,6 +95,19 @@ public class TypeDef { } + private TypeDef(Class rawType, Type actualTypeArguments) { + this.defClass = rawType; + this.type = actualTypeArguments; + } + + public void setType(Type type) { + this.type = type; + } + + public void setDefClass(Class defClass) { + this.defClass = defClass; + } + public Type getType() { return type; } @@ -82,4 +117,25 @@ public class TypeDef { return defClass; } + + public static TypeDef wrapper(Class rawType, Type... actualTypeArguments) { + Type type = new ParameterizedType() { + @Override + public Type[] getActualTypeArguments() { + return actualTypeArguments; + } + + @Override + public Type getRawType() { + return rawType; + } + + @Override + public Type getOwnerType() { + return null; + } + }; + return new TypeDef<>(rawType, type); + } + } diff --git a/src/test/java/io/jboot/test/json/JsonUtilTester.java b/src/test/java/io/jboot/test/json/JsonUtilTester.java new file mode 100644 index 00000000..42539661 --- /dev/null +++ b/src/test/java/io/jboot/test/json/JsonUtilTester.java @@ -0,0 +1,43 @@ +package io.jboot.test.json; + +import io.jboot.utils.JsonUtil; +import io.jboot.utils.TypeDef; + +import java.util.List; +import java.util.Set; + +public class JsonUtilTester { + + public static void main(String[] args) { + String json = "{\n" + + " \"aaa\": [\n" + + " {\n" + + " \"id\": \"100\",\n" + + " \"age\": 10,\n" + + " \"amount\": 99\n" + + " },\n" + + " {\n" + + " \"id\": 101,\n" + + " \"age\": \"20\",\n" + + " \"amount\": 999\n" + + " },\n" + + " {\n" + + " \"id\": 101,\n" + + " \"age\": \"30\",\n" + + " \"amount\": \"9999\"\n" + + " }\n" + + " ]\n" + + "}"; + + + System.out.println("----"); + List myBeans0 = JsonUtil.get(json, "aaa", new TypeDef>() { + }); + + List myBeans1 = JsonUtil.getList(json, "aaa", MyBean.class); + + Set myBeans2 = JsonUtil.getSet(json, "aaa", MyBean.class); + + System.out.println("-----"); + } +} diff --git a/src/test/java/io/jboot/test/json/MyBean.java b/src/test/java/io/jboot/test/json/MyBean.java index ee60980f..e7b05a6a 100644 --- a/src/test/java/io/jboot/test/json/MyBean.java +++ b/src/test/java/io/jboot/test/json/MyBean.java @@ -2,6 +2,7 @@ package io.jboot.test.json; import javax.validation.constraints.NotNull; import java.math.BigInteger; +import java.util.Objects; public class MyBean { private String id; @@ -42,4 +43,14 @@ public class MyBean { ", amount=" + amount + '}'; } + + @Override + public boolean equals(Object obj) { + return id.equals(((MyBean)obj).id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } } -- Gitee From 58d1751e20e3d6137bd247c10fc9f11697d6f1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 19 Apr 2022 11:11:57 +0800 Subject: [PATCH 1662/1965] =?UTF-8?q?add=20RSAUtil=20=E5=92=8C=20DESUtil?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/utils/DESUtil.java | 120 ++++++++ src/main/java/io/jboot/utils/RSAUtil.java | 339 ++++++++++++++++++++++ 2 files changed, 459 insertions(+) create mode 100644 src/main/java/io/jboot/utils/DESUtil.java create mode 100644 src/main/java/io/jboot/utils/RSAUtil.java diff --git a/src/main/java/io/jboot/utils/DESUtil.java b/src/main/java/io/jboot/utils/DESUtil.java new file mode 100644 index 00000000..f648c419 --- /dev/null +++ b/src/main/java/io/jboot/utils/DESUtil.java @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.utils; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import java.security.SecureRandom; + + +/** + * DESUtil 对称加密工具类,非对称加密请参考 RSAUtil + */ +public class DESUtil { + + + /** + * 加密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] encrypt(byte[] data, byte[] key) throws Exception { + Cipher cipher = Cipher.getInstance("DES"); + DESKeySpec ds = new DESKeySpec(key); + SecureRandom sr = new SecureRandom(); + SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey skey = secretKeyFactory.generateSecret(ds); + cipher.init(Cipher.ENCRYPT_MODE, skey, sr); + return cipher.doFinal(data); + } + + + /** + * 加密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static String encrypt(String data, String key) throws Exception { + return byte2hex(encrypt(data.getBytes(), key.getBytes())); + } + + + /** + * 解密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] decrypt(byte[] data, byte[] key) throws Exception { + Cipher cipher = Cipher.getInstance("DES"); + DESKeySpec ds = new DESKeySpec(key); + SecureRandom sr = new SecureRandom(); + SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey skey = secretKeyFactory.generateSecret(ds); + cipher.init(Cipher.DECRYPT_MODE, skey, sr); + return cipher.doFinal(data); + } + + + /** + * 解密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static String decrypt(String data, String key) throws Exception { + return new String(decrypt(hex2byte(data.getBytes()), key.getBytes())); + } + + + private static String byte2hex(byte[] b) { + StringBuilder hs = new StringBuilder(); + String stmp; + for (int n = 0; b != null && n < b.length; n++) { + stmp = Integer.toHexString(b[n] & 0XFF); + if (stmp.length() == 1) { + hs.append('0'); + } + hs.append(stmp); + } + return hs.toString().toUpperCase(); + } + + + private static byte[] hex2byte(byte[] b) { + if ((b.length % 2) != 0) { + throw new IllegalArgumentException(); + } + byte[] b2 = new byte[b.length / 2]; + for (int n = 0; n < b.length; n += 2) { + String item = new String(b, n, 2); + b2[n / 2] = (byte) Integer.parseInt(item, 16); + } + return b2; + } +} diff --git a/src/main/java/io/jboot/utils/RSAUtil.java b/src/main/java/io/jboot/utils/RSAUtil.java new file mode 100644 index 00000000..cee6ae46 --- /dev/null +++ b/src/main/java/io/jboot/utils/RSAUtil.java @@ -0,0 +1,339 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.utils; + +import com.jfinal.kit.Base64Kit; + +import javax.crypto.Cipher; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +/** + * RSA 非对称加密工具类,对称加密请参考 DESUtil + */ +public class RSAUtil { + + + /** + * 生成key长度为 2048 的秘钥对 + * + * @return + * @throws Exception + */ + public static KeyPair getKeyPair2048() throws Exception { + //《2015 年加密宣言》密码指南建议,RSA 算法使用的密钥长度至少应为 2048 位。 + return getKeyPair(2048); + } + + /** + * 生成密钥对 + * + * @return + * @throws Exception + */ + public static KeyPair getKeyPair(int keysize) throws Exception { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(keysize); + return keyPairGenerator.generateKeyPair(); + } + + + /** + * 生成key长度为 2048 的秘钥对 + * + * @return [publicKey, privateKey] + * @throws Exception + */ + public static String[] getKeyPairAsBase64() throws Exception { + return getKeyPairAsBase64(2048); + } + + /** + * 生成密钥对 + * + * @return [publicKey, privateKey] + * @throws Exception + */ + public static String[] getKeyPairAsBase64(int keysize) throws Exception { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(keysize); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + + return new String[]{Base64Kit.encode(keyPair.getPublic().getEncoded()) + , Base64Kit.encode(keyPair.getPrivate().getEncoded())}; + } + + + /** + * 公钥字符串转PublicKey实例 + * + * @param publicKey + * @return + * @throws Exception + */ + public static PublicKey getPublicKey(String publicKey) throws Exception { + byte[] publicKeyBytes = Base64.getDecoder().decode(publicKey.getBytes()); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePublic(keySpec); + } + + /** + * 私钥字符串转PrivateKey实例 + * + * @param privateKey + * @return + * @throws Exception + */ + public static PrivateKey getPrivateKey(String privateKey) throws Exception { + byte[] privateKeyBytes = Base64.getDecoder().decode(privateKey.getBytes()); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePrivate(keySpec); + } + + /** + * 公钥加密 + * + * @param content + * @param publicKey + * @return + * @throws Exception + */ + public static byte[] encryptByPublicKey(byte[] content, PublicKey publicKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return cipher.doFinal(content); + } + + + /** + * 公钥加密 + * + * @param content + * @param publicKeyBase64 + * @return + * @throws Exception + */ + public static byte[] encryptByPublicKey(byte[] content, String publicKeyBase64) throws Exception { + return encryptByPublicKey(content, getPublicKey(publicKeyBase64)); + } + + + /** + * 公钥加密 + * + * @param content + * @param publicKey + * @return + * @throws Exception + */ + public static String encryptToBase64ByPublicKey(byte[] content, PublicKey publicKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return Base64Kit.encode(cipher.doFinal(content)); + } + + + /** + * 公钥加密 + * + * @param content + * @param publicKeyBase64 + * @return + * @throws Exception + */ + public static String encryptToBase64ByPublicKey(byte[] content, String publicKeyBase64) throws Exception { + return encryptToBase64ByPublicKey(content, getPublicKey(publicKeyBase64)); + } + + + /** + * 公钥加密 + * + * @param content + * @param publicKeyBase64 + * @return + * @throws Exception + */ + public static String encryptToBase64ByPublicKey(String content, String publicKeyBase64) throws Exception { + return encryptToBase64ByPublicKey(content.getBytes(StandardCharsets.UTF_8), getPublicKey(publicKeyBase64)); + } + + + /** + * 私钥加密 + * + * @param content + * @param privateKey + * @return + * @throws Exception + */ + public static byte[] encryptByPrivateKey(byte[] content, PrivateKey privateKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + return cipher.doFinal(content); + } + + + /** + * 私钥加密 + * + * @param content + * @param privateKeyBase64 + * @return + * @throws Exception + */ + public static byte[] encryptByPrivateKey(byte[] content, String privateKeyBase64) throws Exception { + return encryptByPrivateKey(content, getPrivateKey(privateKeyBase64)); + } + + + /** + * 私钥加密 + * + * @param content + * @param privateKey + * @return + * @throws Exception + */ + public static String encryptToBase64ByPrivateKey(byte[] content, PrivateKey privateKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + return Base64Kit.encode(cipher.doFinal(content)); + } + + + /** + * 私钥加密 + * + * @param content + * @param privateKeyBase64 + * @return + * @throws Exception + */ + public static String encryptToBase64ByPrivateKey(byte[] content, String privateKeyBase64) throws Exception { + return encryptToBase64ByPrivateKey(content, getPrivateKey(privateKeyBase64)); + } + + + /** + * 私钥加密 + * + * @param content + * @param privateKeyBase64 + * @return + * @throws Exception + */ + public static String encryptToBase64ByPrivateKey(String content, String privateKeyBase64) throws Exception { + return encryptToBase64ByPrivateKey(content.getBytes(StandardCharsets.UTF_8), getPrivateKey(privateKeyBase64)); + } + + + /** + * 私钥解密 + * + * @param content + * @param privateKey + * @return + * @throws Exception + */ + public static byte[] decryptByPrivateKey(byte[] content, PrivateKey privateKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + return cipher.doFinal(content); + } + + + /** + * 私钥解密 + * + * @param content + * @param privateKeyBase64 + * @return + * @throws Exception + */ + public static byte[] decryptByPrivateKey(byte[] content, String privateKeyBase64) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKeyBase64)); + return cipher.doFinal(content); + } + + + /** + * 私钥解密 + * + * @param base64Content + * @param privateKeyBase64 + * @return + * @throws Exception + */ + public static String decryptToStringByPrivateKey(String base64Content, String privateKeyBase64) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKeyBase64)); + return new String(cipher.doFinal(Base64Kit.decode(base64Content)), StandardCharsets.UTF_8); + } + + + /** + * 公钥解密 + * + * @param content + * @param publicKey + * @return + * @throws Exception + */ + public static byte[] decrypByPublicKey(byte[] content, PublicKey publicKey) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, publicKey); + return cipher.doFinal(content); + } + + + /** + * 公钥解密 + * + * @param content + * @param publicKeyBase64 + * @return + * @throws Exception + */ + public static byte[] decrypByPublicKey(byte[] content, String publicKeyBase64) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, getPublicKey(publicKeyBase64)); + return cipher.doFinal(content); + } + + + /** + * 公钥解密 + * + * @param base64Content + * @param publicKeyBase64 + * @return + * @throws Exception + */ + public static String decryptToStringByPublicKey(String base64Content, String publicKeyBase64) throws Exception { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, getPublicKey(publicKeyBase64)); + return new String(cipher.doFinal(Base64Kit.decode(base64Content)), StandardCharsets.UTF_8); + } + + +} -- Gitee From e4fb8de1c398f8433317d1cee7469a5a35dec886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 19 Apr 2022 11:18:35 +0800 Subject: [PATCH 1663/1965] v3.14.5 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index d7dbae70..ab970462 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.14.4 + 3.14.5 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 126e5a64..4e54deef 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.14.4 + 3.14.5 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 55057922..d41bb513 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.14.4 + 3.14.5 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.14.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.14.5', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index df59d6cc..6c44b90f 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.14.4', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.14.5', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.4/jboot-3.14.4.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.5/jboot-3.14.5.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index fd7f7ce3..04ad6ac9 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.14.4 + 3.14.5 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index b5001e9c..1cf5cfe5 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.14.4"; + public static String VERSION = "3.14.5"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 52f2af2ac92cf0f917eaa8e82d88d4dc960f28ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 19 Apr 2022 11:18:47 +0800 Subject: [PATCH 1664/1965] v3.14.5 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a4f0aec..3e210d5c 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.14.4 + 3.14.5 ``` diff --git a/changes.txt b/changes.txt index 83ec24bd..847d5291 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.14.5: +新增:JsonUtil.getList 和 JsonUtil.getSet 方法 +新增:RSAUtil 非对称加解密工具类 +新增:DESUtil 对称加解密工具类 + + + jboot v3.14.4: 新增:生产环境忽略模板指令渲染错误的功能,保证其他内容正常渲染 新增:模板错误渲染器 TemplateErrorRender,用于追加模板指令错误内容 -- Gitee From 267213cbb2ee45da3c9ddf916d97845d90d404b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 20 Apr 2022 14:21:12 +0800 Subject: [PATCH 1665/1965] v3.14.6 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 53368132..cc425f51 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.5 + 3.14.6-SNAPSHOT jar jboot -- Gitee From 7efab8f998026d34aedc0c8d78002902c29ca27d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 20 Apr 2022 14:21:55 +0800 Subject: [PATCH 1666/1965] remove JbootObjects --- .../counter/impl/JbootLocalCounter.java | 2 +- .../java/io/jboot/objects/list/JbootList.java | 23 -------- .../objects/lock/impl/JbootLocalLock.java | 2 +- .../java/io/jboot/objects/map/JbootMap.java | 55 ------------------- .../io/jboot/objects/map/JbootMapManager.java | 23 -------- .../jboot/objects/multimap/JbootMultimap.java | 8 --- .../io/jboot/objects/object/JbootObject.java | 23 -------- .../java/io/jboot/objects/set/JbootSet.java | 8 --- 8 files changed, 2 insertions(+), 142 deletions(-) delete mode 100644 src/main/java/io/jboot/objects/list/JbootList.java delete mode 100644 src/main/java/io/jboot/objects/map/JbootMap.java delete mode 100644 src/main/java/io/jboot/objects/map/JbootMapManager.java delete mode 100644 src/main/java/io/jboot/objects/multimap/JbootMultimap.java delete mode 100644 src/main/java/io/jboot/objects/object/JbootObject.java delete mode 100644 src/main/java/io/jboot/objects/set/JbootSet.java diff --git a/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java b/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java index dd51b893..8663d70a 100644 --- a/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java +++ b/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java @@ -35,7 +35,7 @@ public class JbootLocalCounter implements JbootCounter { atomicLong = atomicLongs.get(name); if (atomicLong == null) { AtomicLong newAl = new AtomicLong(); - atomicLongs.putIfAbsent(name, newAl); + atomicLong = atomicLongs.putIfAbsent(name, newAl); } } diff --git a/src/main/java/io/jboot/objects/list/JbootList.java b/src/main/java/io/jboot/objects/list/JbootList.java deleted file mode 100644 index 1e3c1487..00000000 --- a/src/main/java/io/jboot/objects/list/JbootList.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.objects.list; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/2/28 - */ -public interface JbootList { -} diff --git a/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java b/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java index 68ad5910..2e9c61e8 100644 --- a/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java +++ b/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java @@ -37,7 +37,7 @@ public class JbootLocalLock implements JbootLock { lock = LOCKS.get(name); if (lock == null) { ReentrantLock newLock = new ReentrantLock(); - LOCKS.putIfAbsent(name, newLock); + lock = LOCKS.putIfAbsent(name, newLock); } } diff --git a/src/main/java/io/jboot/objects/map/JbootMap.java b/src/main/java/io/jboot/objects/map/JbootMap.java deleted file mode 100644 index b7a2ec54..00000000 --- a/src/main/java/io/jboot/objects/map/JbootMap.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.objects.map; - -import java.util.List; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/2/28 - */ -public interface JbootMap { - - /** - * put value - * @param key - * @param value - */ - public void put(String key, Object value); - - /** - * get value - * @param key - * @param - * @return - */ - public T get(String key); - - /** - * get all values - * @param - * @return - */ - public List values(); - - /** - * get all keys - * @return - */ - public List keys(); - - -} diff --git a/src/main/java/io/jboot/objects/map/JbootMapManager.java b/src/main/java/io/jboot/objects/map/JbootMapManager.java deleted file mode 100644 index 82dc0c9d..00000000 --- a/src/main/java/io/jboot/objects/map/JbootMapManager.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.objects.map; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/2/28 - */ -public class JbootMapManager { -} diff --git a/src/main/java/io/jboot/objects/multimap/JbootMultimap.java b/src/main/java/io/jboot/objects/multimap/JbootMultimap.java deleted file mode 100644 index c34a4525..00000000 --- a/src/main/java/io/jboot/objects/multimap/JbootMultimap.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.jboot.objects.multimap; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/2/28 - */ -public interface JbootMultimap { -} diff --git a/src/main/java/io/jboot/objects/object/JbootObject.java b/src/main/java/io/jboot/objects/object/JbootObject.java deleted file mode 100644 index 4fffcb0e..00000000 --- a/src/main/java/io/jboot/objects/object/JbootObject.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.objects.object; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/2/29 - */ -public class JbootObject { -} diff --git a/src/main/java/io/jboot/objects/set/JbootSet.java b/src/main/java/io/jboot/objects/set/JbootSet.java deleted file mode 100644 index c27749c7..00000000 --- a/src/main/java/io/jboot/objects/set/JbootSet.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.jboot.objects.set; - -/** - * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/2/28 - */ -public interface JbootSet { -} -- Gitee From 4b3b42474e4b7057a68af9d65d3068a6514fd769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 20 Apr 2022 14:44:43 +0800 Subject: [PATCH 1667/1965] fixed JbootLocalCounter JbootLocalLock --- .../objects/counter/impl/JbootLocalCounter.java | 13 +++++++++---- .../io/jboot/objects/lock/impl/JbootLocalLock.java | 9 +++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java b/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java index 8663d70a..9fa501cc 100644 --- a/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java +++ b/src/main/java/io/jboot/objects/counter/impl/JbootLocalCounter.java @@ -27,15 +27,20 @@ import java.util.concurrent.atomic.AtomicLong; */ public class JbootLocalCounter implements JbootCounter { - private static Map atomicLongs = new HashMap<>(); + private static Map ATOMICLONGS = new HashMap<>(); private AtomicLong atomicLong; public JbootLocalCounter(String name) { - atomicLong = atomicLongs.get(name); + atomicLong = ATOMICLONGS.get(name); if (atomicLong == null) { - AtomicLong newAl = new AtomicLong(); - atomicLong = atomicLongs.putIfAbsent(name, newAl); + synchronized (JbootLocalCounter.class) { + atomicLong = ATOMICLONGS.get(name); + if (atomicLong == null) { + atomicLong = new AtomicLong(); + ATOMICLONGS.put(name, atomicLong); + } + } } } diff --git a/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java b/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java index 2e9c61e8..5c8e1ed0 100644 --- a/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java +++ b/src/main/java/io/jboot/objects/lock/impl/JbootLocalLock.java @@ -36,8 +36,13 @@ public class JbootLocalLock implements JbootLock { public JbootLocalLock(String name) { lock = LOCKS.get(name); if (lock == null) { - ReentrantLock newLock = new ReentrantLock(); - lock = LOCKS.putIfAbsent(name, newLock); + synchronized (JbootLocalLock.class) { + lock = LOCKS.get(name); + if (lock == null) { + lock = new ReentrantLock(); + LOCKS.put(name, lock); + } + } } } -- Gitee From 26993dfe74d9b57a1c913e1d9208bfbf3c78a2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 20 Apr 2022 14:53:11 +0800 Subject: [PATCH 1668/1965] v3.14.6 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index ab970462..6d123137 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.14.5 + 3.14.6 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 4e54deef..e89d76b8 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.14.5 + 3.14.6 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index d41bb513..43d865cd 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.14.5 + 3.14.6 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.14.5', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.14.6', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 6c44b90f..e7187a47 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.14.5', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.14.6', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.5/jboot-3.14.5.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.6/jboot-3.14.6.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 04ad6ac9..016da166 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.14.5 + 3.14.6 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 1cf5cfe5..dd0fb7e1 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.14.5"; + public static String VERSION = "3.14.6"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From e7551052fec72426fc9368a06742e2cb3d7a82f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 20 Apr 2022 14:53:20 +0800 Subject: [PATCH 1669/1965] v3.14.6 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ pom.xml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3e210d5c..b3494998 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.14.5 + 3.14.6 ``` diff --git a/changes.txt b/changes.txt index 847d5291..cb41239b 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.14.6: +修复:JbootLockCounter 和 JbootLocalLock 不可用的问题 +优化:删除一些无用的类或者空实现的类 + + + jboot v3.14.5: 新增:JsonUtil.getList 和 JsonUtil.getSet 方法 新增:RSAUtil 非对称加解密工具类 diff --git a/pom.xml b/pom.xml index cc425f51..78e5da5c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.6-SNAPSHOT + 3.14.6 jar jboot -- Gitee From 7595f101e19be3adf682e7801cf4e0d1b97bdf44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 21 Apr 2022 09:36:31 +0800 Subject: [PATCH 1670/1965] v3.14.7 snapshot --- pom.xml | 4 +-- .../listener/JbootAppListenerManager.java | 33 ++++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index 78e5da5c..9fe5fdf6 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.6 + 3.14.7-SNAPSHOT jar jboot @@ -41,7 +41,7 @@ - 4.9.22 + 4.9.23 2022.2 2.9 3.4 diff --git a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java index eb77d416..f7d71eee 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java @@ -18,7 +18,7 @@ package io.jboot.core.listener; import com.jfinal.config.Constants; import com.jfinal.config.Interceptors; import com.jfinal.config.Routes; -import com.jfinal.log.Log; +import com.jfinal.kit.LogKit; import com.jfinal.template.Engine; import io.jboot.aop.jfinal.JfinalHandlers; import io.jboot.aop.jfinal.JfinalPlugins; @@ -35,7 +35,6 @@ import java.util.Set; import java.util.function.Predicate; public class JbootAppListenerManager implements JbootAppListener { - private static final Log LOG = Log.getLog(JbootAppListenerManager.class); private static JbootAppListenerManager me = new JbootAppListenerManager(); @@ -43,7 +42,6 @@ public class JbootAppListenerManager implements JbootAppListener { return me; } - private List listeners = new ArrayList<>(); private JbootAppListenerManager() { @@ -74,7 +72,8 @@ public class JbootAppListenerManager implements JbootAppListener { if (appListener != null) { listeners.add(appListener); } else { - LOG.warn("Can not create JbootAppListener by class: " + className); + // log 组件还未配置,无法使用 log 组件输出 + System.err.println("Can not create JbootAppListener by class: " + className); } } }); @@ -107,7 +106,8 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onInit(); } catch (Throwable ex) { - LOG.error(ex.toString(), ex); + //在 init 的时候, log 组件未初始化,无法使用 + ex.printStackTrace(); } } } @@ -118,7 +118,8 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onConstantConfigBefore(constants); } catch (Throwable ex) { - LOG.error(ex.toString(), ex); + //在 onConstantConfigBefore 的时候, log 组件未初始化,无法使用 + ex.printStackTrace(); } } } @@ -129,7 +130,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onConstantConfig(constants); } catch (Throwable ex) { - LOG.error(ex.toString(), ex); + LogKit.error(ex.toString(), ex); } } } @@ -140,7 +141,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onRouteConfig(routes); } catch (Throwable ex) { - LOG.error(ex.toString(), ex); + LogKit.error(ex.toString(), ex); } } } @@ -151,7 +152,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onEngineConfig(engine); } catch (Throwable ex) { - LOG.error(ex.toString(), ex); + LogKit.error(ex.toString(), ex); } } } @@ -162,7 +163,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onPluginConfig(plugins); } catch (Throwable ex) { - LOG.error(ex.toString(), ex); + LogKit.error(ex.toString(), ex); } } } @@ -173,7 +174,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onInterceptorConfig(interceptors); } catch (Throwable ex) { - LOG.error(ex.toString(), ex); + LogKit.error(ex.toString(), ex); } } } @@ -185,7 +186,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onHandlerConfig(handlers); } catch (Throwable ex) { - LOG.error(ex.toString(), ex); + LogKit.error(ex.toString(), ex); } } } @@ -196,7 +197,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onStartBefore(); } catch (Throwable ex) { - LOG.error(ex.toString(), ex); + LogKit.error(ex.toString(), ex); } } } @@ -207,7 +208,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onStart(); } catch (Throwable ex) { - LOG.error(ex.toString(), ex); + LogKit.error(ex.toString(), ex); } } } @@ -218,7 +219,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onStartFinish(); } catch (Throwable ex) { - LOG.error(ex.toString(), ex); + LogKit.error(ex.toString(), ex); } } } @@ -229,7 +230,7 @@ public class JbootAppListenerManager implements JbootAppListener { try { listener.onStop(); } catch (Throwable ex) { - LOG.error(ex.toString(), ex); + LogKit.error(ex.toString(), ex); } } } -- Gitee From 01ba290c95d3e31ea417639b46785ed2e1492a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 21 Apr 2022 11:46:13 +0800 Subject: [PATCH 1671/1965] v3.14.7 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 6d123137..dcd24e77 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.14.6 + 3.14.7 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index e89d76b8..e1ae9761 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.14.6 + 3.14.7 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 43d865cd..2efcbdc7 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.14.6 + 3.14.7 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.14.6', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.14.7', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index e7187a47..8e1c8e3b 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.14.6', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.14.7', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.6/jboot-3.14.6.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.7/jboot-3.14.7.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 016da166..0bd2ff4c 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.14.6 + 3.14.7 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index dd0fb7e1..c10a4c39 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.14.6"; + public static String VERSION = "3.14.7"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From d4b03f2eb6fb23810c1a7e54a5d0dba926c74735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 21 Apr 2022 11:46:17 +0800 Subject: [PATCH 1672/1965] v3.14.7 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 5 +++++ pom.xml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b3494998..86a469af 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.14.6 + 3.14.7 ``` diff --git a/changes.txt b/changes.txt index cb41239b..9e628860 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,8 @@ +jboot v3.14.7: +修复:Jboot AppListener 启动时的错误信息,无法正确输出的问题 + + + jboot v3.14.6: 修复:JbootLockCounter 和 JbootLocalLock 不可用的问题 优化:删除一些无用的类或者空实现的类 diff --git a/pom.xml b/pom.xml index 9fe5fdf6..ef307bfb 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.7-SNAPSHOT + 3.14.7 jar jboot -- Gitee From 343f285e24f9d293578b0db7e3cb2f7fc67bec75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 22 Apr 2022 17:36:47 +0800 Subject: [PATCH 1673/1965] v3.14.8 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ef307bfb..6066274d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.7 + 3.14.8-SNAPSHOT jar jboot -- Gitee From 23458d434b51b42183a2a8ec7d8e6e4646c9de95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 22 Apr 2022 17:37:25 +0800 Subject: [PATCH 1674/1965] gitee close #I53II6 --- .../java/io/jboot/db/datasource/DataSourceConfigManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java b/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java index 9ee89910..a7eec257 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceConfigManager.java @@ -57,7 +57,7 @@ public class DataSourceConfigManager { public Map getDatasourceConfigs() { - return datasourceConfigs; + return new HashMap<>(datasourceConfigs); } public DataSourceConfig getMainDatasourceConfig() { -- Gitee From 0ad314e8609afe97d83242db0c92cc6bc91a5dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 22 Apr 2022 18:19:36 +0800 Subject: [PATCH 1675/1965] gitee close #I53MK6 --- .../components/cache/JbootCacheBase.java | 14 ++++--- .../cache/redis/JbootRedisCacheImpl.java | 8 ++-- .../io/jboot/test/cache/CacheUtilTester.java | 37 +++++++++++++++++++ 3 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 src/test/java/io/jboot/test/cache/CacheUtilTester.java diff --git a/src/main/java/io/jboot/components/cache/JbootCacheBase.java b/src/main/java/io/jboot/components/cache/JbootCacheBase.java index 11194d55..81da5def 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheBase.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheBase.java @@ -44,22 +44,26 @@ public abstract class JbootCacheBase implements JbootCache { } @Override - public void removeCurrentCacheNamePrefix(){ + public void removeCurrentCacheNamePrefix() { CACHE_NAME_PREFIX_TL.remove(); } + /** + * 构建缓存名称 + * + * @param cacheName + * @return + */ protected String buildCacheName(String cacheName) { + String cacheNamePrefix = CACHE_NAME_PREFIX_TL.get(); if (StrUtil.isBlank(cacheNamePrefix)) { cacheNamePrefix = config.getDefaultCachePrefix(); } - if (StrUtil.isBlank(cacheNamePrefix)) { - return cacheName; - } - return cacheNamePrefix + ":" + cacheName; + return StrUtil.isNotBlank(cacheNamePrefix) ? (cacheNamePrefix + ":" + cacheName) : cacheName; } diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java index b56b6ff8..614a14c4 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java @@ -102,8 +102,9 @@ public class JbootRedisCacheImpl extends JbootCacheBase { String cursor = "0"; int scanCount = 1000; boolean continueState = true; + String scanName = globalKeyPrefix + buildCacheName(cacheName); do { - RedisScanResult redisScanResult = redis.scan(buildCacheName(cacheName) + ":*", cursor, scanCount); + RedisScanResult redisScanResult = redis.scan(scanName + ":*", cursor, scanCount); List scanKeys = redisScanResult.getResults(); cursor = redisScanResult.getCursor(); @@ -179,14 +180,15 @@ public class JbootRedisCacheImpl extends JbootCacheBase { @Override public List getNames() { - Set set = redis.smembers(buildCacheName(redisCacheNamesKey)); + String key = buildCacheName(redisCacheNamesKey); + Set set = redis.smembers(key); return set == null ? null : new ArrayList(set); } @Override public List getKeys(String cacheName) { - cacheName = buildCacheName(cacheName); + cacheName = globalKeyPrefix + buildCacheName(cacheName); List keys = new ArrayList<>(); String cursor = "0"; int scanCount = 1000; diff --git a/src/test/java/io/jboot/test/cache/CacheUtilTester.java b/src/test/java/io/jboot/test/cache/CacheUtilTester.java new file mode 100644 index 00000000..0755da61 --- /dev/null +++ b/src/test/java/io/jboot/test/cache/CacheUtilTester.java @@ -0,0 +1,37 @@ +package io.jboot.test.cache; + +import io.jboot.app.config.JbootConfigManager; +import io.jboot.components.cache.JbootCacheManager; +import io.jboot.utils.CacheUtil; + +import java.util.List; + +public class CacheUtilTester { + + public static void main(String[] args) { + JbootConfigManager.setBootArg("jboot.cache.redis.globalKeyPrefix","globalKeyPrefix"); + JbootConfigManager.setBootArg("jboot.cache.type","redis"); + JbootConfigManager.setBootArg("jboot.redis.host","127.0.0.1"); + + List names = JbootCacheManager.me().getCache().getNames(); + System.out.println(names); + + + String cacheName = "tester"; + CacheUtil.put(cacheName,"key1","value1"); + CacheUtil.put(cacheName,"key2","value2"); + + System.out.println(CacheUtil.get(cacheName,"key1").toString()); + System.out.println(CacheUtil.get(cacheName,"key2").toString()); + + + List keys = CacheUtil.getKeys(cacheName); + System.out.println(keys); + + + CacheUtil.removeAll(cacheName); + + keys = CacheUtil.getKeys(cacheName); + System.out.println(keys); + } +} -- Gitee From 8ad286e07d03c5b06ad0c902d07bc84ac5c95a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 22 Apr 2022 18:27:21 +0800 Subject: [PATCH 1676/1965] v3.14.8 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index dcd24e77..36e7ddc6 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.14.7 + 3.14.8 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index e1ae9761..9d843f24 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.14.7 + 3.14.8 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 2efcbdc7..756e89c4 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.14.7 + 3.14.8 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.14.7', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.14.8', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 8e1c8e3b..4701a9ab 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.14.7', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.14.8', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.7/jboot-3.14.7.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.8/jboot-3.14.8.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 0bd2ff4c..721576b0 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.14.7 + 3.14.8 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index c10a4c39..34130861 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.14.7"; + public static String VERSION = "3.14.8"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 2736f9865bee516c9b16002dbeb108fc1a0d1279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 22 Apr 2022 18:27:26 +0800 Subject: [PATCH 1677/1965] v3.14.8 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ pom.xml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 86a469af..fdc4d8fc 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.14.7 + 3.14.8 ``` diff --git a/changes.txt b/changes.txt index 9e628860..e07b8093 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.14.8: +修复:DataSourceConfigManager 里的数据源在 APP 启动成功后会被清空的问题 +修复:配置 redis GlobalKeyPrefix,CacheUtil.getKeys(cacheName) 无法正确获取 keys 的问题 + + + jboot v3.14.7: 修复:Jboot AppListener 启动时的错误信息,无法正确输出的问题 diff --git a/pom.xml b/pom.xml index 6066274d..517d867b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.8-SNAPSHOT + 3.14.8 jar jboot -- Gitee From 8affa3717b2d980b29733351f0720dd0eceb0b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 24 Apr 2022 16:59:25 +0800 Subject: [PATCH 1678/1965] v3.14.9 snapshot --- pom.xml | 2 +- src/main/java/io/jboot/utils/StrUtil.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 517d867b..1bebbe27 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.8 + 3.14.9-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/utils/StrUtil.java b/src/main/java/io/jboot/utils/StrUtil.java index b2bb87ba..d1225d5d 100644 --- a/src/main/java/io/jboot/utils/StrUtil.java +++ b/src/main/java/io/jboot/utils/StrUtil.java @@ -323,7 +323,7 @@ public class StrUtil extends StrKit { private static final String[] escapeChars = {"&", "<", ">", "'", """}; public static String escapeHtml(String content) { - return isBlank(content) ? content : StringUtils.replaceEach(unEscapeHtml(content), htmlChars, escapeChars); + return isBlank(content) ? content : StringUtils.replaceEach(content, htmlChars, escapeChars); } public static String unEscapeHtml(String content) { @@ -340,7 +340,7 @@ public class StrUtil extends StrKit { Object value = model.get(attr); - if (value != null && value instanceof String) { + if (value instanceof String) { model.set(attr, escapeHtml(value.toString())); } } @@ -353,7 +353,7 @@ public class StrUtil extends StrKit { return map; } - Set keys = map.keySet(); + Set keys = map.keySet(); for (Object key : keys) { if (ArrayUtils.contains(ignoreKeys, key)) { continue; @@ -361,7 +361,7 @@ public class StrUtil extends StrKit { Object value = map.get(key); - if (value != null && value instanceof String) { + if (value instanceof String) { map.put(key, escapeHtml(value.toString())); } } -- Gitee From 1235829dbec9193ef1eb845827f60b69e908f66c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 24 Apr 2022 17:26:51 +0800 Subject: [PATCH 1679/1965] v3.14.9 snapshot --- .../io/jboot/components/cache/JbootCache.java | 32 +-- .../components/event/JbootEventListener.java | 4 +- .../io/jboot/components/http/JbootHttp.java | 4 +- .../java/io/jboot/components/mq/Jbootmq.java | 24 +-- .../io/jboot/components/rpc/Jbootrpc.java | 8 +- .../serializer/JbootSerializer.java | 4 +- .../jboot/objects/counter/JbootCounter.java | 11 +- .../io/jboot/support/redis/JbootRedis.java | 196 +++++++++--------- .../redis/jedis/JbootJedisClusterImpl.java | 83 ++++++++ .../web/attachment/AttachmentContainer.java | 12 +- .../web/attachment/AttachmentManager.java | 4 +- 11 files changed, 232 insertions(+), 150 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/JbootCache.java b/src/main/java/io/jboot/components/cache/JbootCache.java index f98d5867..98efc213 100644 --- a/src/main/java/io/jboot/components/cache/JbootCache.java +++ b/src/main/java/io/jboot/components/cache/JbootCache.java @@ -22,42 +22,42 @@ import java.util.List; public interface JbootCache extends com.jfinal.plugin.activerecord.cache.ICache { - public JbootCache setCurrentCacheNamePrefix(String cacheNamePrefix); + JbootCache setCurrentCacheNamePrefix(String cacheNamePrefix); - public void removeCurrentCacheNamePrefix(); + void removeCurrentCacheNamePrefix(); - public JbootCacheConfig getConfig(); + JbootCacheConfig getConfig(); @Override - public T get(String cacheName, Object key); + T get(String cacheName, Object key); @Override - public void put(String cacheName, Object key, Object value); + void put(String cacheName, Object key, Object value); - public void put(String cacheName, Object key, Object value, int liveSeconds); + void put(String cacheName, Object key, Object value, int liveSeconds); @Override - public void remove(String cacheName, Object key); + void remove(String cacheName, Object key); @Override - public void removeAll(String cacheName); + void removeAll(String cacheName); - public T get(String cacheName, Object key, IDataLoader dataLoader); + T get(String cacheName, Object key, IDataLoader dataLoader); - public T get(String cacheName, Object key, IDataLoader dataLoader, int liveSeconds); + T get(String cacheName, Object key, IDataLoader dataLoader, int liveSeconds); - public Integer getTtl(String cacheName, Object key); + Integer getTtl(String cacheName, Object key); - public void setTtl(String cacheName, Object key, int seconds); + void setTtl(String cacheName, Object key, int seconds); - public void refresh(String cacheName, Object key); + void refresh(String cacheName, Object key); - public void refresh(String cacheName); + void refresh(String cacheName); - public List getNames(); + List getNames(); - public List getKeys(String cacheName); + List getKeys(String cacheName); } diff --git a/src/main/java/io/jboot/components/event/JbootEventListener.java b/src/main/java/io/jboot/components/event/JbootEventListener.java index 3195c645..fe80b17a 100644 --- a/src/main/java/io/jboot/components/event/JbootEventListener.java +++ b/src/main/java/io/jboot/components/event/JbootEventListener.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -17,6 +17,6 @@ package io.jboot.components.event; public interface JbootEventListener { - public void onEvent(JbootEvent event); + void onEvent(JbootEvent event); } diff --git a/src/main/java/io/jboot/components/http/JbootHttp.java b/src/main/java/io/jboot/components/http/JbootHttp.java index 9c5a67ec..858038a2 100644 --- a/src/main/java/io/jboot/components/http/JbootHttp.java +++ b/src/main/java/io/jboot/components/http/JbootHttp.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -17,6 +17,6 @@ package io.jboot.components.http; public interface JbootHttp { - public JbootHttpResponse handle(JbootHttpRequest request); + JbootHttpResponse handle(JbootHttpRequest request); } diff --git a/src/main/java/io/jboot/components/mq/Jbootmq.java b/src/main/java/io/jboot/components/mq/Jbootmq.java index 4d50d35f..e5e4d79c 100644 --- a/src/main/java/io/jboot/components/mq/Jbootmq.java +++ b/src/main/java/io/jboot/components/mq/Jbootmq.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -20,26 +20,26 @@ import java.util.Collection; public interface Jbootmq { - public void enqueue(Object message, String toChannel); + void enqueue(Object message, String toChannel); - public void publish(Object message, String toChannel); + void publish(Object message, String toChannel); - public void addMessageListener(JbootmqMessageListener listener); + void addMessageListener(JbootmqMessageListener listener); - public void addMessageListener(JbootmqMessageListener listener, String forChannel); + void addMessageListener(JbootmqMessageListener listener, String forChannel); - public void removeListener(JbootmqMessageListener listener); + void removeListener(JbootmqMessageListener listener); - public void removeAllListeners(); + void removeAllListeners(); - public Collection getGlobalListeners(); + Collection getGlobalListeners(); - public Collection getListenersByChannel(String channel); + Collection getListenersByChannel(String channel); - public boolean startListening(); + boolean startListening(); - public boolean stopListening(); + boolean stopListening(); - public JbootmqConfig getConfig(); + JbootmqConfig getConfig(); } diff --git a/src/main/java/io/jboot/components/rpc/Jbootrpc.java b/src/main/java/io/jboot/components/rpc/Jbootrpc.java index c642191d..b52f663b 100644 --- a/src/main/java/io/jboot/components/rpc/Jbootrpc.java +++ b/src/main/java/io/jboot/components/rpc/Jbootrpc.java @@ -18,11 +18,11 @@ package io.jboot.components.rpc; public interface Jbootrpc { - public T serviceObtain(Class serviceClass, JbootrpcReferenceConfig referenceConfig); + T serviceObtain(Class serviceClass, JbootrpcReferenceConfig referenceConfig); - public boolean serviceExport(Class interfaceClass, Object object, JbootrpcServiceConfig serviceConfig); + boolean serviceExport(Class interfaceClass, Object object, JbootrpcServiceConfig serviceConfig); - public void onStart(); + void onStart(); - public void onStop(); + void onStop(); } diff --git a/src/main/java/io/jboot/components/serializer/JbootSerializer.java b/src/main/java/io/jboot/components/serializer/JbootSerializer.java index efdf6153..28011aca 100644 --- a/src/main/java/io/jboot/components/serializer/JbootSerializer.java +++ b/src/main/java/io/jboot/components/serializer/JbootSerializer.java @@ -17,7 +17,7 @@ package io.jboot.components.serializer; public interface JbootSerializer { - public byte[] serialize(Object obj); + byte[] serialize(Object obj); - public Object deserialize(byte[] bytes); + Object deserialize(byte[] bytes); } diff --git a/src/main/java/io/jboot/objects/counter/JbootCounter.java b/src/main/java/io/jboot/objects/counter/JbootCounter.java index d492d297..f93da079 100644 --- a/src/main/java/io/jboot/objects/counter/JbootCounter.java +++ b/src/main/java/io/jboot/objects/counter/JbootCounter.java @@ -21,9 +21,12 @@ package io.jboot.objects.counter; */ public interface JbootCounter { - public Long increment(); - public Long decrement(); - public Long get(); - public void set(long newValue); + Long increment(); + + Long decrement(); + + Long get(); + + void set(long newValue); } diff --git a/src/main/java/io/jboot/support/redis/JbootRedis.java b/src/main/java/io/jboot/support/redis/JbootRedis.java index 1c7cade8..78888fc0 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedis.java +++ b/src/main/java/io/jboot/support/redis/JbootRedis.java @@ -35,7 +35,7 @@ public interface JbootRedis { * 如果 key 已经持有其他值, SET 就覆写旧值,无视类型。 * 对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。 */ - public String set(Object key, Object value); + String set(Object key, Object value); /** @@ -45,28 +45,28 @@ public interface JbootRedis { * @param value 设置成功,返回 1,设置失败,返回 0 * @return */ - public Long setnx(Object key, Object value); + Long setnx(Object key, Object value); /** * 存放 key value 对到 redis * 如果 key 已经持有其他值, SET 就覆写旧值,无视类型。 * 此方法用了修改 incr 等的值 */ - public String setWithoutSerialize(Object key, Object value); + String setWithoutSerialize(Object key, Object value); /** * 存放 key value 对到 redis,并将 key 的生存时间设为 seconds (以秒为单位)。 * 如果 key 已经存在, SETEX 命令将覆写旧值。 */ - public String setex(Object key, int seconds, Object value); + String setex(Object key, int seconds, Object value); /** * 返回 key 所关联的 value 值 * 如果 key 不存在那么返回特殊值 nil 。 */ @SuppressWarnings("unchecked") - public T get(Object key); + T get(Object key); /** * 获取 数据不进行反序列 , 如果之前设置的是非String类型,得到String后自行转化 @@ -74,19 +74,19 @@ public interface JbootRedis { * @param key * @return */ - public String getWithoutSerialize(Object key); + String getWithoutSerialize(Object key); /** * 删除给定的一个 key * 不存在的 key 会被忽略。 */ - public Long del(Object key); + Long del(Object key); /** * 删除给定的多个 key * 不存在的 key 会被忽略。 */ - public Long del(Object... keys); + Long del(Object... keys); /** * 查找所有符合给定模式 pattern 的 key 。 @@ -96,7 +96,7 @@ public interface JbootRedis { * KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 。 * 特殊符号用 \ 隔开 */ - public Set keys(String pattern); + Set keys(String pattern); /** * 同时设置一个或多个 key-value 对。 @@ -108,14 +108,13 @@ public interface JbootRedis { * List list = cache.mget("k1", "k2"); // 利用多个键值得到上面代码放入的值 * */ - public String mset(Object... keysValues); + String mset(Object... keysValues); /** * 返回所有(一个或多个)给定 key 的值。 * 如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。因此,该命令永不失败。 */ - @SuppressWarnings("rawtypes") - public List mget(Object... keys); + List mget(Object... keys); /** * 将 key 中储存的数字值减一。 @@ -124,7 +123,7 @@ public interface JbootRedis { * 本操作的值限制在 64 位(bit)有符号数字表示之内。 * 关于递增(increment) / 递减(decrement)操作的更多信息,请参见 INCR 命令。 */ - public Long decr(Object key); + Long decr(Object key); /** * 将 key 所储存的值减去减量 decrement 。 @@ -133,7 +132,7 @@ public interface JbootRedis { * 本操作的值限制在 64 位(bit)有符号数字表示之内。 * 关于更多递增(increment) / 递减(decrement)操作的更多信息,请参见 INCR 命令。 */ - public Long decrBy(Object key, long value); + Long decrBy(Object key, long value); /** * 将 key 中储存的数字值增一。 @@ -141,7 +140,7 @@ public interface JbootRedis { * 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 * 本操作的值限制在 64 位(bit)有符号数字表示之内。 */ - public Long incr(Object key); + Long incr(Object key); /** * 将 key 所储存的值加上增量 increment 。 @@ -150,36 +149,36 @@ public interface JbootRedis { * 本操作的值限制在 64 位(bit)有符号数字表示之内。 * 关于递增(increment) / 递减(decrement)操作的更多信息,参见 INCR 命令。 */ - public Long incrBy(Object key, long value); + Long incrBy(Object key, long value); /** * 检查给定 key 是否存在。 */ - public boolean exists(Object key); + boolean exists(Object key); /** * 从当前数据库中随机返回(不删除)一个 key 。 */ - public String randomKey(); + String randomKey(); /** * 将 key 改名为 newkey 。 * 当 key 和 newkey 相同,或者 key 不存在时,返回一个错误。 * 当 newkey 已经存在时, RENAME 命令将覆盖旧值。 */ - public String rename(Object oldkey, Object newkey); + String rename(Object oldkey, Object newkey); /** * 将当前数据库的 key 移动到给定的数据库 db 当中。 * 如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定 key ,或者 key 不存在于当前数据库,那么 MOVE 没有任何效果。 * 因此,也可以利用这一特性,将 MOVE 当作锁(locking)原语(primitive)。 */ - public Long move(Object key, int dbIndex); + Long move(Object key, int dbIndex); /** * 将 key 原子性地从当前实例传送到目标实例的指定数据库上,一旦传送成功, key 保证会出现在目标实例上,而当前实例上的 key 会被删除。 */ - public String migrate(String host, int port, Object key, int destinationDb, int timeout); + String migrate(String host, int port, Object key, int destinationDb, int timeout); /** * 切换到指定的数据库,数据库索引号 index 用数字值指定,以 0 作为起始索引值。 @@ -190,123 +189,122 @@ public interface JbootRedis { * 2:使用 JbootRedis.call(ICallback) 进行操作 * 3:自行获取 Jedis 对象进行操作 */ - public String select(int databaseIndex); + String select(int databaseIndex); /** * 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。 * 在 JbootRedis 中,带有生存时间的 key 被称为『易失的』(volatile)。 */ - public Long expire(Object key, int seconds); + Long expire(Object key, int seconds); /** * EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置生存时间。不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。 */ - public Long expireAt(Object key, long unixTime); + Long expireAt(Object key, long unixTime); /** * 这个命令和 EXPIRE 命令的作用类似,但是它以毫秒为单位设置 key 的生存时间,而不像 EXPIRE 命令那样,以秒为单位。 */ - public Long pexpire(Object key, long milliseconds); + Long pexpire(Object key, long milliseconds); /** * 这个命令和 EXPIREAT 命令类似,但它以毫秒为单位设置 key 的过期 unix 时间戳,而不是像 EXPIREAT 那样,以秒为单位。 */ - public Long pexpireAt(Object key, long millisecondsTimestamp); + Long pexpireAt(Object key, long millisecondsTimestamp); /** * 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 * 当 key 存在但不是字符串类型时,返回一个错误。 */ - public T getSet(Object key, Object value); + T getSet(Object key, Object value); /** * 移除给定 key 的生存时间,将这个 key 从『易失的』(带生存时间 key )转换成『持久的』(一个不带生存时间、永不过期的 key )。 */ - public Long persist(Object key); + Long persist(Object key); /** * 返回 key 所储存的值的类型。 */ - public String type(Object key); + String type(Object key); /** * 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。 */ - public Long ttl(Object key); + Long ttl(Object key); /** * 这个命令类似于 TTL 命令,但它以毫秒为单位返回 key 的剩余生存时间,而不是像 TTL 命令那样,以秒为单位。 */ - public Long pttl(Object key); + Long pttl(Object key); /** * 对象被引用的数量 */ - public Long objectRefcount(Object key); + Long objectRefcount(Object key); /** * 对象没有被访问的空闲时间 */ - public Long objectIdletime(Object key); + Long objectIdletime(Object key); /** * 将哈希表 key 中的域 field 的值设为 value 。 * 如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。 * 如果域 field 已经存在于哈希表中,旧值将被覆盖。 */ - public Long hset(Object key, Object field, Object value); + Long hset(Object key, Object field, Object value); /** * 同时将多个 field-value (域-值)对设置到哈希表 key 中。 * 此命令会覆盖哈希表中已存在的域。 * 如果 key 不存在,一个空哈希表被创建并执行 HMSET 操作。 */ - public String hmset(Object key, Map hash); + String hmset(Object key, Map hash); /** * 返回哈希表 key 中给定域 field 的值。 */ - public T hget(Object key, Object field); + T hget(Object key, Object field); /** * 返回哈希表 key 中,一个或多个给定域的值。 * 如果给定的域不存在于哈希表,那么返回一个 nil 值。 * 因为不存在的 key 被当作一个空哈希表来处理,所以对一个不存在的 key 进行 HMGET 操作将返回一个只带有 nil 值的表。 */ - public List hmget(Object key, Object... fields); + List hmget(Object key, Object... fields); /** * 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。 */ - public Long hdel(Object key, Object... fields); + Long hdel(Object key, Object... fields); /** * 查看哈希表 key 中,给定域 field 是否存在。 */ - public boolean hexists(Object key, Object field); + boolean hexists(Object key, Object field); /** * 返回哈希表 key 中,所有的域和值。 * 在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。 */ - public Map hgetAll(Object key); + Map hgetAll(Object key); /** * 返回哈希表 key 中所有域的值。 */ - @SuppressWarnings("rawtypes") - public List hvals(Object key); + List hvals(Object key); /** * 返回哈希表 key 中的所有域。 * 底层实现此方法取名为 hfields 更为合适,在此仅为与底层保持一致 */ - public Set hkeys(Object key); + Set hkeys(Object key); /** * 返回哈希表 key 中域的数量。 */ - public Long hlen(Object key); + Long hlen(Object key); /** * 为哈希表 key 中的域 field 的值加上增量 increment 。 @@ -316,7 +314,7 @@ public interface JbootRedis { * 对一个储存字符串值的域 field 执行 HINCRBY 命令将造成一个错误。 * 本操作的值被限制在 64 位(bit)有符号数字表示之内。 */ - public Long hincrBy(Object key, Object field, long value); + Long hincrBy(Object key, Object field, long value); /** * 为哈希表 key 中的域 field 加上浮点数增量 increment 。 @@ -327,7 +325,7 @@ public interface JbootRedis { * 2:域 field 当前的值或给定的增量 increment 不能解释(parse)为双精度浮点数(double precision floating point number) * HINCRBYFLOAT 命令的详细功能和 INCRBYFLOAT 命令类似,请查看 INCRBYFLOAT 命令获取更多相关信息。 */ - public Double hincrByFloat(Object key, Object field, double value); + Double hincrByFloat(Object key, Object field, double value); /** @@ -337,7 +335,7 @@ public interface JbootRedis { * 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 * 如果 key 不是列表类型,返回一个错误。 */ - public T lindex(Object key, long index); + T lindex(Object key, long index); /** @@ -345,13 +343,13 @@ public interface JbootRedis { * 如果 key 不存在,则 key 被解释为一个空列表,返回 0 . * 如果 key 不是列表类型,返回一个错误。 */ - public Long llen(Object key); + Long llen(Object key); /** * 移除并返回列表 key 的头元素。 */ @SuppressWarnings("unchecked") - public T lpop(Object key); + T lpop(Object key); /** * 将一个或多个值 value 插入到列表 key 的表头 @@ -361,14 +359,14 @@ public interface JbootRedis { * 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 * 当 key 存在但不是列表类型时,返回一个错误。 */ - public Long lpush(Object key, Object... values); + Long lpush(Object key, Object... values); /** * 将列表 key 下标为 index 的元素的值设置为 value 。 * 当 index 参数超出范围,或对一个空列表( key 不存在)进行 LSET 时,返回一个错误。 * 关于列表下标的更多信息,请参考 LINDEX 命令。 */ - public String lset(Object key, long index, Object value); + String lset(Object key, long index, Object value); /** * 根据参数 count 的值,移除列表中与参数 value 相等的元素。 @@ -377,7 +375,7 @@ public interface JbootRedis { * count 小于 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。 * count 等于 0 : 移除表中所有与 value 相等的值。 */ - public Long lrem(Object key, long count, Object value); + Long lrem(Object key, long count, Object value); /** * 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。 @@ -389,7 +387,7 @@ public interface JbootRedis { * 获取 list 中下标 1 到 3 的数据: cache.lrange(listKey, 1, 3); * */ - public List lrange(Object key, long start, long end); + List lrange(Object key, long start, long end); /** * 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 @@ -398,12 +396,12 @@ public interface JbootRedis { * 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 * 当 key 不是列表类型时,返回一个错误。 */ - public String ltrim(Object key, long start, long end); + String ltrim(Object key, long start, long end); /** * 移除并返回列表 key 的尾元素。 */ - public T rpop(Object key); + T rpop(Object key); /** * 命令 RPOPLPUSH 在一个原子时间内,执行以下两个动作: @@ -411,7 +409,7 @@ public interface JbootRedis { * 将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素。 */ @SuppressWarnings("unchecked") - public T rpoplpush(Object srcKey, Object dstKey); + T rpoplpush(Object srcKey, Object dstKey); /** * 将一个或多个值 value 插入到列表 key 的表尾(最右边)。 @@ -421,21 +419,21 @@ public interface JbootRedis { * 如果 key 不存在,一个空列表会被创建并执行 RPUSH 操作。 * 当 key 存在但不是列表类型时,返回一个错误。 */ - public Long rpush(Object key, Object... values); + Long rpush(Object key, Object... values); /** * BLPOP 是列表的阻塞式(blocking)弹出原语。 * 它是 LPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BLPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 * 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。 */ - public List blpop(Object... keys); + List blpop(Object... keys); /** * BLPOP 是列表的阻塞式(blocking)弹出原语。 * 它是 LPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BLPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 * 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。 */ - public List blpop(Integer timeout, Object... keys); + List blpop(Integer timeout, Object... keys); /** * BRPOP 是列表的阻塞式(blocking)弹出原语。 @@ -443,7 +441,7 @@ public interface JbootRedis { * 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的尾部元素。 * 关于阻塞操作的更多信息,请查看 BLPOP 命令, BRPOP 除了弹出元素的位置和 BLPOP 不同之外,其他表现一致。 */ - public List brpop(Object... keys); + List brpop(Object... keys); /** * BRPOP 是列表的阻塞式(blocking)弹出原语。 @@ -451,52 +449,52 @@ public interface JbootRedis { * 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的尾部元素。 * 关于阻塞操作的更多信息,请查看 BLPOP 命令, BRPOP 除了弹出元素的位置和 BLPOP 不同之外,其他表现一致。 */ - public List brpop(Integer timeout, Object... keys); + List brpop(Integer timeout, Object... keys); /** * 使用客户端向 JbootRedis 服务器发送一个 PING ,如果服务器运作正常的话,会返回一个 PONG 。 * 通常用于测试与服务器的连接是否仍然生效,或者用于测量延迟值。 */ - public String ping(); + String ping(); /** * 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。 * 假如 key 不存在,则创建一个只包含 member 元素作成员的集合。 * 当 key 不是集合类型时,返回一个错误。 */ - public Long sadd(Object key, Object... members); + Long sadd(Object key, Object... members); /** * 返回集合 key 的基数(集合中元素的数量)。 */ - public Long scard(Object key); + Long scard(Object key); /** * 移除并返回集合中的一个随机元素。 * 如果只想获取一个随机元素,但不想该元素从集合中被移除的话,可以使用 SRANDMEMBER 命令。 */ - public T spop(Object key); + T spop(Object key); /** * 返回集合 key 中的所有成员。 * 不存在的 key 被视为空集合。 */ - public Set smembers(Object key); + Set smembers(Object key); /** * 判断 member 元素是否集合 key 的成员。 */ - public boolean sismember(Object key, Object member); + boolean sismember(Object key, Object member); /** * 返回多个集合的交集,多个集合由 keys 指定 */ - public Set sinter(Object... keys); + Set sinter(Object... keys); /** * 返回集合中的一个随机元素。 */ - public T srandmember(Object key); + T srandmember(Object key); /** * 返回集合中的 count 个随机元素。 @@ -505,49 +503,49 @@ public interface JbootRedis { * 如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。 * 该操作和 SPOP 相似,但 SPOP 将随机元素从集合中移除并返回,而 SRANDMEMBER 则仅仅返回随机元素,而不对集合进行任何改动。 */ - public List srandmember(Object key, int count); + List srandmember(Object key, int count); /** * 移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略。 */ - public Long srem(Object key, Object... members); + Long srem(Object key, Object... members); /** * 返回多个集合的并集,多个集合由 keys 指定 * 不存在的 key 被视为空集。 */ - public Set sunion(Object... keys); + Set sunion(Object... keys); /** * 返回一个集合的全部成员,该集合是所有给定集合之间的差集。 * 不存在的 key 被视为空集。 */ - public Set sdiff(Object... keys); + Set sdiff(Object... keys); /** * 将一个或多个 member 元素及其 score 值加入到有序集 key 当中。 * 如果某个 member 已经是有序集的成员,那么更新这个 member 的 score 值, * 并通过重新插入这个 member 元素,来保证该 member 在正确的位置上。 */ - public Long zadd(Object key, double score, Object member); + Long zadd(Object key, double score, Object member); - public Long zadd(Object key, Map scoreMembers); + Long zadd(Object key, Map scoreMembers); /** * 返回有序集 key 的基数。 */ - public Long zcard(Object key); + Long zcard(Object key); /** * 返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量。 * 关于参数 min 和 max 的详细使用方法,请参考 ZRANGEBYSCORE 命令。 */ - public Long zcount(Object key, double min, double max); + Long zcount(Object key, double min, double max); /** * 为有序集 key 的成员 member 的 score 值加上增量 increment 。 */ - public Double zincrby(Object key, double score, Object member); + Double zincrby(Object key, double score, Object member); /** * 返回有序集 key 中,指定区间内的成员。 @@ -555,7 +553,7 @@ public interface JbootRedis { * 具有相同 score 值的成员按字典序(lexicographical order )来排列。 * 如果你需要成员按 score 值递减(从大到小)来排列,请使用 ZREVRANGE 命令。 */ - public Set zrange(Object key, long start, long end); + Set zrange(Object key, long start, long end); /** * 返回有序集 key 中,指定区间内的成员。 @@ -563,39 +561,39 @@ public interface JbootRedis { * 具有相同 score 值的成员按字典序的逆序(reverse lexicographical order)排列。 * 除了成员按 score 值递减的次序排列这一点外, ZREVRANGE 命令的其他方面和 ZRANGE 命令一样。 */ - public Set zrevrange(Object key, long start, long end); + Set zrevrange(Object key, long start, long end); /** * 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。 * 有序集成员按 score 值递增(从小到大)次序排列。 */ - public Set zrangeByScore(Object key, double min, double max); + Set zrangeByScore(Object key, double min, double max); /** * 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。 * 排名以 0 为底,也就是说, score 值最小的成员排名为 0 。 * 使用 ZREVRANK 命令可以获得成员按 score 值递减(从大到小)排列的排名。 */ - public Long zrank(Object key, Object member); + Long zrank(Object key, Object member); /** * 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)排序。 * 排名以 0 为底,也就是说, score 值最大的成员排名为 0 。 * 使用 ZRANK 命令可以获得成员按 score 值递增(从小到大)排列的排名。 */ - public Long zrevrank(Object key, Object member); + Long zrevrank(Object key, Object member); /** * 移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。 * 当 key 存在但不是有序集类型时,返回一个错误。 */ - public Long zrem(Object key, Object... members); + Long zrem(Object key, Object... members); /** * 返回有序集 key 中,成员 member 的 score 值。 * 如果 member 元素不是有序集 key 的成员,或 key 不存在,返回 nil 。 */ - public Double zscore(Object key, Object member); + Double zscore(Object key, Object member); /** * 发布 @@ -603,7 +601,7 @@ public interface JbootRedis { * @param channel * @param message */ - public void publish(String channel, String message); + void publish(String channel, String message); /** * 发布 @@ -611,7 +609,7 @@ public interface JbootRedis { * @param channel * @param message */ - public void publish(byte[] channel, byte[] message); + void publish(byte[] channel, byte[] message); /** @@ -620,7 +618,7 @@ public interface JbootRedis { * @param listener * @param channels */ - public void subscribe(JedisPubSub listener, final String... channels); + void subscribe(JedisPubSub listener, final String... channels); /** * 订阅 @@ -628,7 +626,7 @@ public interface JbootRedis { * @param binaryListener * @param channels */ - public void subscribe(BinaryJedisPubSub binaryListener, final byte[]... channels); + void subscribe(BinaryJedisPubSub binaryListener, final byte[]... channels); /** * 扫描 @@ -637,27 +635,27 @@ public interface JbootRedis { * @param scanCount * @return */ - public RedisScanResult scan(String pattern, String cursor, int scanCount); + RedisScanResult scan(String pattern, String cursor, int scanCount); - public byte[] keyToBytes(Object key); + byte[] keyToBytes(Object key); - public String bytesToKey(byte[] bytes); + String bytesToKey(byte[] bytes); - public byte[][] keysToBytesArray(Object... keys); + byte[][] keysToBytesArray(Object... keys); - public void fieldSetFromBytesSet(Set data, Set result); + void fieldSetFromBytesSet(Set data, Set result); - public byte[] valueToBytes(Object value); + byte[] valueToBytes(Object value); - public Object valueFromBytes(byte[] bytes); + Object valueFromBytes(byte[] bytes); - public byte[][] valuesToBytesArray(Object... valuesArray); + byte[][] valuesToBytesArray(Object... valuesArray); - public void valueSetFromBytesSet(Set data, Set result); + void valueSetFromBytesSet(Set data, Set result); - public List valueListFromBytesList(Collection data); + List valueListFromBytesList(Collection data); Object eval(String script, int keyCount, String... params); } diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java index dd6386f1..df7dd63a 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java @@ -150,6 +150,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 如果 key 已经持有其他值, SET 就覆写旧值,无视类型。 * 此方法用了修改 incr 等的值 */ + @Override public String setWithoutSerialize(Object key, Object value) { return jedisCluster.set(keyToBytes(key), value.toString().getBytes()); } @@ -159,6 +160,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 存放 key value 对到 redis,并将 key 的生存时间设为 seconds (以秒为单位)。 * 如果 key 已经存在, SETEX 命令将覆写旧值。 */ + @Override public String setex(Object key, int seconds, Object value) { return jedisCluster.setex(keyToBytes(key), seconds, valueToBytes(value)); @@ -169,6 +171,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 返回 key 所关联的 value 值 * 如果 key 不存在那么返回特殊值 nil 。 */ + @Override @SuppressWarnings("unchecked") public T get(Object key) { @@ -189,6 +192,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 删除给定的一个 key * 不存在的 key 会被忽略。 */ + @Override public Long del(Object key) { return jedisCluster.del(keyToBytes(key)); } @@ -197,6 +201,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 删除给定的多个 key * 不存在的 key 会被忽略。 */ + @Override public Long del(Object... keys) { return jedisCluster.del(keysToBytesArray(keys)); @@ -211,6 +216,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 。 * 特殊符号用 \ 隔开 */ + @Override public Set keys(String pattern) { HashSet keys = new HashSet<>(); Map clusterNodes = jedisCluster.getClusterNodes(); @@ -240,6 +246,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * List list = cache.mget("k1", "k2"); // 利用多个键值得到上面代码放入的值 * */ + @Override public String mset(Object... keysValues) { if (keysValues.length % 2 != 0) throw new IllegalArgumentException("wrong number of arguments for met, keysValues length can not be odd"); @@ -259,6 +266,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 返回所有(一个或多个)给定 key 的值。 * 如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。因此,该命令永不失败。 */ + @Override @SuppressWarnings("rawtypes") public List mget(Object... keys) { @@ -275,6 +283,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 本操作的值限制在 64 位(bit)有符号数字表示之内。 * 关于递增(increment) / 递减(decrement)操作的更多信息,请参见 INCR 命令。 */ + @Override public Long decr(Object key) { return jedisCluster.decr(keyToBytes(key)); @@ -288,6 +297,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 本操作的值限制在 64 位(bit)有符号数字表示之内。 * 关于更多递增(increment) / 递减(decrement)操作的更多信息,请参见 INCR 命令。 */ + @Override public Long decrBy(Object key, long longValue) { return jedisCluster.decrBy(keyToBytes(key), longValue); @@ -300,6 +310,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 * 本操作的值限制在 64 位(bit)有符号数字表示之内。 */ + @Override public Long incr(Object key) { return jedisCluster.incr(keyToBytes(key)); @@ -313,6 +324,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 本操作的值限制在 64 位(bit)有符号数字表示之内。 * 关于递增(increment) / 递减(decrement)操作的更多信息,参见 INCR 命令。 */ + @Override public Long incrBy(Object key, long longValue) { return jedisCluster.incrBy(keyToBytes(key), longValue); @@ -321,6 +333,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 检查给定 key 是否存在。 */ + @Override public boolean exists(Object key) { return jedisCluster.exists(keyToBytes(key)); @@ -330,6 +343,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 从当前数据库中随机返回(不删除)一个 key 。 */ + @Override public String randomKey() { throw new JbootException("not support randomKey commmand in redis cluster."); @@ -341,6 +355,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 当 key 和 newkey 相同,或者 key 不存在时,返回一个错误。 * 当 newkey 已经存在时, RENAME 命令将覆盖旧值。 */ + @Override public String rename(Object oldkey, Object newkey) { return jedisCluster.rename(keyToBytes(oldkey), keyToBytes(newkey)); @@ -352,6 +367,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定 key ,或者 key 不存在于当前数据库,那么 MOVE 没有任何效果。 * 因此,也可以利用这一特性,将 MOVE 当作锁(locking)原语(primitive)。 */ + @Override public Long move(Object key, int dbIndex) { // return jedisCluster.move(keyToBytes(key), dbIndex); @@ -362,6 +378,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 将 key 原子性地从当前实例传送到目标实例的指定数据库上,一旦传送成功, key 保证会出现在目标实例上,而当前实例上的 key 会被删除。 */ + @Override public String migrate(String host, int port, Object key, int destinationDb, int timeout) { throw new JbootException("not support migrate commmand in redis cluster."); @@ -377,6 +394,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 2:使用 JbootRedis.call(ICallback) 进行操作 * 3:自行获取 Jedis 对象进行操作 */ + @Override public String select(int databaseIndex) { // return jedisCluster.select(databaseIndex); @@ -388,6 +406,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。 * 在 JbootRedis 中,带有生存时间的 key 被称为『易失的』(volatile)。 */ + @Override public Long expire(Object key, int seconds) { return jedisCluster.expire(keyToBytes(key), seconds); @@ -397,6 +416,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置生存时间。不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。 */ + @Override public Long expireAt(Object key, long unixTime) { return jedisCluster.expireAt(keyToBytes(key), unixTime); @@ -406,6 +426,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 这个命令和 EXPIRE 命令的作用类似,但是它以毫秒为单位设置 key 的生存时间,而不像 EXPIRE 命令那样,以秒为单位。 */ + @Override public Long pexpire(Object key, long milliseconds) { return jedisCluster.pexpire(keyToBytes(key), milliseconds); @@ -415,6 +436,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 这个命令和 EXPIREAT 命令类似,但它以毫秒为单位设置 key 的过期 unix 时间戳,而不是像 EXPIREAT 那样,以秒为单位。 */ + @Override public Long pexpireAt(Object key, long millisecondsTimestamp) { return jedisCluster.pexpireAt(keyToBytes(key), millisecondsTimestamp); @@ -425,6 +447,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 * 当 key 存在但不是字符串类型时,返回一个错误。 */ + @Override @SuppressWarnings("unchecked") public T getSet(Object key, Object value) { @@ -435,6 +458,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 移除给定 key 的生存时间,将这个 key 从『易失的』(带生存时间 key )转换成『持久的』(一个不带生存时间、永不过期的 key )。 */ + @Override public Long persist(Object key) { return jedisCluster.persist(keyToBytes(key)); @@ -444,6 +468,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 返回 key 所储存的值的类型。 */ + @Override public String type(Object key) { return jedisCluster.type(keyToBytes(key)); @@ -453,6 +478,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。 */ + @Override public Long ttl(Object key) { return jedisCluster.ttl(keyToBytes(key)); @@ -462,6 +488,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 这个命令类似于 TTL 命令,但它以毫秒为单位返回 key 的剩余生存时间,而不是像 TTL 命令那样,以秒为单位。 */ + @Override public Long pttl(Object key) { return jedisCluster.pttl(key.toString()); @@ -471,6 +498,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 对象被引用的数量 */ + @Override public Long objectRefcount(Object key) { // return jedisCluster.objectRefcount(keyToBytes(key)); @@ -480,6 +508,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 对象没有被访问的空闲时间 */ + @Override public Long objectIdletime(Object key) { // return jedisCluster.objectIdletime(keyToBytes(key)); @@ -492,6 +521,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。 * 如果域 field 已经存在于哈希表中,旧值将被覆盖。 */ + @Override public Long hset(Object key, Object field, Object value) { return jedisCluster.hset(keyToBytes(key), valueToBytes(field), valueToBytes(value)); @@ -503,6 +533,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 此命令会覆盖哈希表中已存在的域。 * 如果 key 不存在,一个空哈希表被创建并执行 HMSET 操作。 */ + @Override public String hmset(Object key, Map hash) { Map para = new HashMap(); @@ -515,6 +546,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 返回哈希表 key 中给定域 field 的值。 */ + @Override @SuppressWarnings("unchecked") public T hget(Object key, Object field) { @@ -527,6 +559,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 如果给定的域不存在于哈希表,那么返回一个 nil 值。 * 因为不存在的 key 被当作一个空哈希表来处理,所以对一个不存在的 key 进行 HMGET 操作将返回一个只带有 nil 值的表。 */ + @Override @SuppressWarnings("rawtypes") public List hmget(Object key, Object... fields) { @@ -538,6 +571,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。 */ + @Override public Long hdel(Object key, Object... fields) { return jedisCluster.hdel(keyToBytes(key), valuesToBytesArray(fields)); @@ -547,6 +581,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 查看哈希表 key 中,给定域 field 是否存在。 */ + @Override public boolean hexists(Object key, Object field) { return jedisCluster.hexists(keyToBytes(key), valueToBytes(field)); @@ -557,6 +592,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 返回哈希表 key 中,所有的域和值。 * 在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。 */ + @Override @SuppressWarnings("rawtypes") public Map hgetAll(Object key) { @@ -571,6 +607,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 返回哈希表 key 中所有域的值。 */ + @Override @SuppressWarnings("rawtypes") public List hvals(Object key) { @@ -583,6 +620,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 返回哈希表 key 中的所有域。 * 底层实现此方法取名为 hfields 更为合适,在此仅为与底层保持一致 */ + @Override public Set hkeys(Object key) { Set fieldSet = jedisCluster.hkeys(keyToBytes(key)); @@ -595,6 +633,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 返回哈希表 key 中域的数量。 */ + @Override public Long hlen(Object key) { return jedisCluster.hlen(keyToBytes(key)); @@ -609,6 +648,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 对一个储存字符串值的域 field 执行 HINCRBY 命令将造成一个错误。 * 本操作的值被限制在 64 位(bit)有符号数字表示之内。 */ + @Override public Long hincrBy(Object key, Object field, long value) { return jedisCluster.hincrBy(keyToBytes(key), valueToBytes(field), value); @@ -624,6 +664,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 2:域 field 当前的值或给定的增量 increment 不能解释(parse)为双精度浮点数(double precision floating point number) * HINCRBYFLOAT 命令的详细功能和 INCRBYFLOAT 命令类似,请查看 INCRBYFLOAT 命令获取更多相关信息。 */ + @Override public Double hincrByFloat(Object key, Object field, double value) { return jedisCluster.hincrByFloat(keyToBytes(key), valueToBytes(field), value); @@ -636,6 +677,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 * 如果 key 不是列表类型,返回一个错误。 */ + @Override @SuppressWarnings("unchecked") /** @@ -657,6 +699,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 如果 key 不存在,则 key 被解释为一个空列表,返回 0 . * 如果 key 不是列表类型,返回一个错误。 */ + @Override public Long llen(Object key) { return jedisCluster.llen(keyToBytes(key)); @@ -666,6 +709,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 移除并返回列表 key 的头元素。 */ + @Override @SuppressWarnings("unchecked") public T lpop(Object key) { @@ -681,6 +725,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 * 当 key 存在但不是列表类型时,返回一个错误。 */ + @Override public Long lpush(Object key, Object... values) { return jedisCluster.lpush(keyToBytes(key), valuesToBytesArray(values)); @@ -692,6 +737,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 当 index 参数超出范围,或对一个空列表( key 不存在)进行 LSET 时,返回一个错误。 * 关于列表下标的更多信息,请参考 LINDEX 命令。 */ + @Override public String lset(Object key, long index, Object value) { return jedisCluster.lset(keyToBytes(key), index, valueToBytes(value)); @@ -705,6 +751,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * count 小于 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。 * count 等于 0 : 移除表中所有与 value 相等的值。 */ + @Override public Long lrem(Object key, long count, Object value) { return jedisCluster.lrem(keyToBytes(key), count, valueToBytes(value)); @@ -721,6 +768,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 获取 list 中下标 1 到 3 的数据: cache.lrange(listKey, 1, 3); * */ + @Override @SuppressWarnings("rawtypes") public List lrange(Object key, long start, long end) { @@ -740,6 +788,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 * 当 key 不是列表类型时,返回一个错误。 */ + @Override public String ltrim(Object key, long start, long end) { return jedisCluster.ltrim(keyToBytes(key), start, end); @@ -749,6 +798,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 移除并返回列表 key 的尾元素。 */ + @Override @SuppressWarnings("unchecked") public T rpop(Object key) { @@ -761,6 +811,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端。 * 将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素。 */ + @Override @SuppressWarnings("unchecked") public T rpoplpush(Object srcKey, Object dstKey) { @@ -776,6 +827,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 如果 key 不存在,一个空列表会被创建并执行 RPUSH 操作。 * 当 key 存在但不是列表类型时,返回一个错误。 */ + @Override public Long rpush(Object key, Object... values) { return jedisCluster.rpush(keyToBytes(key), valuesToBytesArray(values)); @@ -787,6 +839,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 它是 LPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BLPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 * 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。 */ + @Override @SuppressWarnings("rawtypes") public List blpop(Object... keys) { // String[] keysStrings = new String[keys.length]; @@ -812,6 +865,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 它是 LPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BLPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 * 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。 */ + @Override @SuppressWarnings("rawtypes") public List blpop(Integer timeout, Object... keys) { @@ -826,6 +880,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的尾部元素。 * 关于阻塞操作的更多信息,请查看 BLPOP 命令, BRPOP 除了弹出元素的位置和 BLPOP 不同之外,其他表现一致。 */ + @Override @SuppressWarnings("rawtypes") public List brpop(Object... keys) { @@ -840,6 +895,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的尾部元素。 * 关于阻塞操作的更多信息,请查看 BLPOP 命令, BRPOP 除了弹出元素的位置和 BLPOP 不同之外,其他表现一致。 */ + @Override @SuppressWarnings("rawtypes") public List brpop(Integer timeout, Object... keys) { @@ -852,6 +908,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 使用客户端向 JbootRedis 服务器发送一个 PING ,如果服务器运作正常的话,会返回一个 PONG 。 * 通常用于测试与服务器的连接是否仍然生效,或者用于测量延迟值。 */ + @Override public String ping() { // jedisCluster.getClusterNodes().get("aa").getResource().ping // return jedisCluster..ping(); @@ -875,6 +932,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 假如 key 不存在,则创建一个只包含 member 元素作成员的集合。 * 当 key 不是集合类型时,返回一个错误。 */ + @Override public Long sadd(Object key, Object... members) { return jedisCluster.sadd(keyToBytes(key), valuesToBytesArray(members)); @@ -884,6 +942,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 返回集合 key 的基数(集合中元素的数量)。 */ + @Override public Long scard(Object key) { return jedisCluster.scard(keyToBytes(key)); @@ -894,6 +953,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 移除并返回集合中的一个随机元素。 * 如果只想获取一个随机元素,但不想该元素从集合中被移除的话,可以使用 SRANDMEMBER 命令。 */ + @Override @SuppressWarnings("unchecked") public T spop(Object key) { @@ -905,6 +965,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 返回集合 key 中的所有成员。 * 不存在的 key 被视为空集合。 */ + @Override @SuppressWarnings("rawtypes") public Set smembers(Object key) { @@ -918,6 +979,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 判断 member 元素是否集合 key 的成员。 */ + @Override public boolean sismember(Object key, Object member) { return jedisCluster.sismember(keyToBytes(key), valueToBytes(member)); @@ -927,6 +989,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 返回多个集合的交集,多个集合由 keys 指定 */ + @Override @SuppressWarnings("rawtypes") public Set sinter(Object... keys) { @@ -940,6 +1003,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 返回集合中的一个随机元素。 */ + @Override @SuppressWarnings("unchecked") public T srandmember(Object key) { @@ -955,6 +1019,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。 * 该操作和 SPOP 相似,但 SPOP 将随机元素从集合中移除并返回,而 SRANDMEMBER 则仅仅返回随机元素,而不对集合进行任何改动。 */ + @Override @SuppressWarnings("rawtypes") public List srandmember(Object key, int count) { @@ -966,6 +1031,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略。 */ + @Override public Long srem(Object key, Object... members) { return jedisCluster.srem(keyToBytes(key), valuesToBytesArray(members)); @@ -976,6 +1042,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 返回多个集合的并集,多个集合由 keys 指定 * 不存在的 key 被视为空集。 */ + @Override @SuppressWarnings("rawtypes") public Set sunion(Object... keys) { @@ -990,6 +1057,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 返回一个集合的全部成员,该集合是所有给定集合之间的差集。 * 不存在的 key 被视为空集。 */ + @Override @SuppressWarnings("rawtypes") public Set sdiff(Object... keys) { @@ -1005,12 +1073,14 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 如果某个 member 已经是有序集的成员,那么更新这个 member 的 score 值, * 并通过重新插入这个 member 元素,来保证该 member 在正确的位置上。 */ + @Override public Long zadd(Object key, double score, Object member) { return jedisCluster.zadd(keyToBytes(key), score, valueToBytes(member)); } + @Override public Long zadd(Object key, Map scoreMembers) { Map para = new HashMap(); @@ -1023,6 +1093,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 返回有序集 key 的基数。 */ + @Override public Long zcard(Object key) { return jedisCluster.zcard(keyToBytes(key)); @@ -1033,6 +1104,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量。 * 关于参数 min 和 max 的详细使用方法,请参考 ZRANGEBYSCORE 命令。 */ + @Override public Long zcount(Object key, double min, double max) { return jedisCluster.zcount(keyToBytes(key), min, max); @@ -1042,6 +1114,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { /** * 为有序集 key 的成员 member 的 score 值加上增量 increment 。 */ + @Override public Double zincrby(Object key, double score, Object member) { return jedisCluster.zincrby(keyToBytes(key), score, valueToBytes(member)); @@ -1054,6 +1127,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 具有相同 score 值的成员按字典序(lexicographical order )来排列。 * 如果你需要成员按 score 值递减(从大到小)来排列,请使用 ZREVRANGE 命令。 */ + @Override @SuppressWarnings("rawtypes") public Set zrange(Object key, long start, long end) { @@ -1070,6 +1144,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 具有相同 score 值的成员按字典序的逆序(reverse lexicographical order)排列。 * 除了成员按 score 值递减的次序排列这一点外, ZREVRANGE 命令的其他方面和 ZRANGE 命令一样。 */ + @Override @SuppressWarnings("rawtypes") public Set zrevrange(Object key, long start, long end) { @@ -1084,6 +1159,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。 * 有序集成员按 score 值递增(从小到大)次序排列。 */ + @Override @SuppressWarnings("rawtypes") public Set zrangeByScore(Object key, double min, double max) { @@ -1099,6 +1175,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 排名以 0 为底,也就是说, score 值最小的成员排名为 0 。 * 使用 ZREVRANK 命令可以获得成员按 score 值递减(从大到小)排列的排名。 */ + @Override public Long zrank(Object key, Object member) { return jedisCluster.zrank(keyToBytes(key), valueToBytes(member)); @@ -1110,6 +1187,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 排名以 0 为底,也就是说, score 值最大的成员排名为 0 。 * 使用 ZRANK 命令可以获得成员按 score 值递增(从小到大)排列的排名。 */ + @Override public Long zrevrank(Object key, Object member) { return jedisCluster.zrevrank(keyToBytes(key), valueToBytes(member)); @@ -1120,6 +1198,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。 * 当 key 存在但不是有序集类型时,返回一个错误。 */ + @Override public Long zrem(Object key, Object... members) { return jedisCluster.zrem(keyToBytes(key), valuesToBytesArray(members)); @@ -1130,6 +1209,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * 返回有序集 key 中,成员 member 的 score 值。 * 如果 member 元素不是有序集 key 的成员,或 key 不存在,返回 nil 。 */ + @Override public Double zscore(Object key, Object member) { return jedisCluster.zscore(keyToBytes(key), valueToBytes(member)); @@ -1142,6 +1222,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * @param channel * @param message */ + @Override public void publish(String channel, String message) { jedisCluster.publish(channel, message); @@ -1154,6 +1235,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * @param channel * @param message */ + @Override public void publish(byte[] channel, byte[] message) { jedisCluster.publish(channel, message); } @@ -1165,6 +1247,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * @param listener * @param channels */ + @Override public void subscribe(JedisPubSub listener, final String... channels) { /** * https://github.com/xetorthio/jedis/wiki/AdvancedUsage diff --git a/src/main/java/io/jboot/web/attachment/AttachmentContainer.java b/src/main/java/io/jboot/web/attachment/AttachmentContainer.java index 09a71dcb..60aa60a4 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentContainer.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentContainer.java @@ -30,7 +30,7 @@ public interface AttachmentContainer { * @param file * @return 返回文件的相对路径 */ - public String saveFile(File file); + String saveFile(File file); /** @@ -39,7 +39,7 @@ public interface AttachmentContainer { * @param file * @return 返回文件的相对路径 */ - public String saveFile(File file, String toRelativePath); + String saveFile(File file, String toRelativePath); /** * 保存文件 @@ -47,7 +47,7 @@ public interface AttachmentContainer { * @param inputStream * @return */ - public String saveFile(InputStream inputStream, String toRelativePath); + String saveFile(InputStream inputStream, String toRelativePath); /** @@ -56,7 +56,7 @@ public interface AttachmentContainer { * @param relativePath * @return */ - public boolean deleteFile(String relativePath); + boolean deleteFile(String relativePath); /** @@ -65,7 +65,7 @@ public interface AttachmentContainer { * @param relativePath * @return */ - public File getFile(String relativePath); + File getFile(String relativePath); /** @@ -74,7 +74,7 @@ public interface AttachmentContainer { * @param file * @return */ - public String getRelativePath(File file); + String getRelativePath(File file); } diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index 55f6d7d3..65621d2d 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -20,7 +20,6 @@ import com.jfinal.log.Log; import com.jfinal.render.IRenderFactory; import com.jfinal.render.Render; import com.jfinal.render.RenderManager; -import io.jboot.utils.StrUtil; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -286,8 +285,7 @@ public class AttachmentManager { * @return true 渲染成功,false 不进行渲染 */ public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { - if (StrUtil.isNotBlank(defaultContainer.getTargetPrefix()) - && target.startsWith(defaultContainer.getTargetPrefix()) + if (target.startsWith(defaultContainer.getTargetPrefix()) && target.lastIndexOf('.') != -1) { Render render = getFileRender(getFile(target)); render.setContext(request, response).render(); -- Gitee From e601fbf3552b6aacb47f5384b6ae608484dff500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 24 Apr 2022 17:30:12 +0800 Subject: [PATCH 1680/1965] v3.14.9 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 36e7ddc6..781743dd 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.14.8 + 3.14.9 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 9d843f24..5ca2bf48 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.14.8 + 3.14.9 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 756e89c4..70c28692 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.14.8 + 3.14.9 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.14.8', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.14.9', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 4701a9ab..a8780729 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.14.8', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.14.9', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.8/jboot-3.14.8.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.9/jboot-3.14.9.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 721576b0..7e45116a 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.14.8 + 3.14.9 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 34130861..495e74eb 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.14.8"; + public static String VERSION = "3.14.9"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 058f6d0b10f8ed601dbf1445c8b7dbb6aa305412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 24 Apr 2022 17:30:15 +0800 Subject: [PATCH 1681/1965] v3.14.9 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ pom.xml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fdc4d8fc..0402c42a 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.14.8 + 3.14.9 ``` diff --git a/changes.txt b/changes.txt index e07b8093..0bb2d5d1 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.14.9: +修复:在某些极端场景下,StrUtil.escapeHtml 无法通过 unEscapeHtml 还原的问题 +优化:删除某些无用的方法和类 + + + jboot v3.14.8: 修复:DataSourceConfigManager 里的数据源在 APP 启动成功后会被清空的问题 修复:配置 redis GlobalKeyPrefix,CacheUtil.getKeys(cacheName) 无法正确获取 keys 的问题 diff --git a/pom.xml b/pom.xml index 1bebbe27..b56803da 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.9-SNAPSHOT + 3.14.9 jar jboot -- Gitee From c279cef5c71f769a695b471af8c6f02c993bafcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 26 Apr 2022 11:49:13 +0800 Subject: [PATCH 1682/1965] v3.15.0 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b56803da..9cb1ac6d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.14.9 + 3.15.0-SNAPSHOT jar jboot -- Gitee From eea4c233f42be5b9751984dcb06eef27a86f941a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 26 Apr 2022 11:49:40 +0800 Subject: [PATCH 1683/1965] fixed Model.dao() error --- .../db/dialect/JbootClickHouseDialect.java | 2 +- .../java/io/jboot/db/model/JbootModel.java | 52 +++++++++++-------- .../io/jboot/db/model/JbootModelExts.java | 39 +++++++++++++- .../java/io/jboot/db/model/SqlBuilder.java | 2 +- .../java/io/jboot/test/db/dao/DaoTester.java | 42 +++++++++++++++ .../java/io/jboot/test/db/model/User.java | 2 +- 6 files changed, 112 insertions(+), 27 deletions(-) create mode 100644 src/test/java/io/jboot/test/db/dao/DaoTester.java diff --git a/src/main/java/io/jboot/db/dialect/JbootClickHouseDialect.java b/src/main/java/io/jboot/db/dialect/JbootClickHouseDialect.java index 9e4a2bd7..a720b79c 100644 --- a/src/main/java/io/jboot/db/dialect/JbootClickHouseDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootClickHouseDialect.java @@ -168,7 +168,7 @@ public class JbootClickHouseDialect extends AnsiSqlDialect implements JbootDiale public String forPaginateTotalRow(String select, String sqlExceptSelect, Object ext) { if (ext instanceof Model) { if (io.jboot.db.model.CPI.hasAnyJoinEffective((JbootModel) ext)) { - String distinct = ((JbootModel) ext).get(JbootModelExts.DISTINCT); + String distinct = JbootModelExts.getDistinctColumn((JbootModel) ext); if (StrUtil.isNotBlank(distinct)) { return "SELECT count(DISTINCT " + distinct + ") " + replaceOrderBy(sqlExceptSelect); } diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 858e41cc..bdfb2bf0 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -55,6 +55,7 @@ public class JbootModel> extends Model { String loadColumns = null; boolean isCopyModel = false; + public Joiner leftJoin(String table) { return joining(Join.TYPE_LEFT, table, true); } @@ -135,10 +136,12 @@ public class JbootModel> extends Model { throw new IllegalArgumentException("distinctColumnName must not be null or empty."); } M model = getOrCopyModel(); - model.put(JbootModelExts.DISTINCT, distinctColumnName); +// model._setExtAttr(JbootModelExts.DISTINCT, distinctColumnName); + JbootModelExts.setDistinctColumn(model, distinctColumnName); return model; } + private M getOrCopyModel() { if (isCopyModel) { return (M) this; @@ -724,19 +727,20 @@ public class JbootModel> extends Model { } public List findListByColumns(Columns columns, String orderBy, Integer count, String loadColumns) { - if (StrUtil.isBlank(loadColumns) && this.loadColumns != null) { + if (StrUtil.isBlank(loadColumns) && StrUtil.isNotBlank(this.loadColumns)) { loadColumns = this.loadColumns; } - if (StrUtil.isBlank(loadColumns)) { - String distinctColumn = get(JbootModelExts.DISTINCT); - //使用 distinct - if (hasAnyJoinEffective() && StrUtil.isNotBlank(distinctColumn)) { + + //使用 distinct + if (StrUtil.isBlank(loadColumns) && hasAnyJoinEffective()) { + String distinctColumn = JbootModelExts.getDistinctColumn(this); + if (StrUtil.isNotBlank(distinctColumn)) { loadColumns = "DISTINCT " + distinctColumn + "," + (StrUtil.isNotBlank(alias) ? alias : _getTableName()) + ".*"; } - //未使用 distinct - else { - loadColumns = "*"; - } + } + + if (StrUtil.isBlank(loadColumns)) { + loadColumns = "*"; } @@ -800,24 +804,23 @@ public class JbootModel> extends Model { } public Page paginateByColumns(int pageNumber, int pageSize, Columns columns, String orderBy, String loadColumns) { - if (StrUtil.isBlank(loadColumns) && this.loadColumns != null) { + if (StrUtil.isBlank(loadColumns) && StrUtil.isNotBlank(this.loadColumns)) { loadColumns = this.loadColumns; } - if (StrUtil.isBlank(loadColumns)) { - - String distinctColumn = get(JbootModelExts.DISTINCT); - - //使用 distinct - if (hasAnyJoinEffective() && StrUtil.isNotBlank(distinctColumn)) { + //使用 distinct + if (StrUtil.isBlank(loadColumns) && hasAnyJoinEffective()) { + String distinctColumn = JbootModelExts.getDistinctColumn(this); + if (StrUtil.isNotBlank(distinctColumn)) { loadColumns = "DISTINCT " + distinctColumn + "," + (StrUtil.isNotBlank(alias) ? alias : _getTableName()) + ".*"; } - //未使用 distinct - else { - loadColumns = "*"; - } } + if (StrUtil.isBlank(loadColumns)) { + loadColumns = "*"; + } + + String selectPartSql = _getDialect().forPaginateSelect(loadColumns); String fromPartSql = _getDialect().forPaginateFrom(alias, joins, _getTableName(), columns.getList(), orderBy); @@ -854,8 +857,11 @@ public class JbootModel> extends Model { String loadColumns = "*"; //使用 distinct - if (hasAnyJoinEffective() && StrUtil.isNotBlank(get(JbootModelExts.DISTINCT))) { - loadColumns = "DISTINCT " + get(JbootModelExts.DISTINCT); + if (hasAnyJoinEffective()) { + String distinctColumn = JbootModelExts.getDistinctColumn(this); + if (StrUtil.isNotBlank(distinctColumn)) { + loadColumns = "DISTINCT " + distinctColumn; + } } diff --git a/src/main/java/io/jboot/db/model/JbootModelExts.java b/src/main/java/io/jboot/db/model/JbootModelExts.java index 563745ec..e44b404c 100644 --- a/src/main/java/io/jboot/db/model/JbootModelExts.java +++ b/src/main/java/io/jboot/db/model/JbootModelExts.java @@ -15,8 +15,45 @@ */ package io.jboot.db.model; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + public class JbootModelExts { - public static final String DISTINCT = "__ext_distinct"; + private static Map> extAttrs = new ConcurrentHashMap<>(); + + private static final String DISTINCT = "distinct"; + + + public static Object getExtAttr(Object holder, String attr) { + Map map = extAttrs.get(System.identityHashCode(holder)); + return map != null ? map.get(attr) : null; + } + + + public static void setExtAttr(Object holder, String attr, Object value) { + Map map = extAttrs.get(System.identityHashCode(holder)); + if (map == null) { + synchronized (holder) { + map = extAttrs.get(System.identityHashCode(holder)); + if (map == null) { + map = new ConcurrentHashMap<>(); + extAttrs.put(System.identityHashCode(holder), map); + } + } + } + + map.put(attr, value); + } + + + public static void setDistinctColumn(JbootModel holder, String column) { + setExtAttr(holder, DISTINCT, column); + } + + public static String getDistinctColumn(JbootModel holder) { + return (String) getExtAttr(holder, DISTINCT); + } + } diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index 0ffc38fc..985bb837 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -247,7 +247,7 @@ public class SqlBuilder { public static String forPaginateDistinctTotalRow(String select, String sqlExceptSelect, Object ext) { if (ext instanceof JbootModel && CPI.hasAnyJoinEffective((JbootModel) ext)) { - String distinct = ((JbootModel) ext).get(JbootModelExts.DISTINCT); + String distinct = JbootModelExts.getDistinctColumn((JbootModel) ext); if (StrUtil.isNotBlank(distinct)) { return "SELECT count(DISTINCT " + distinct + ") " + replaceOrderBy(sqlExceptSelect); } diff --git a/src/test/java/io/jboot/test/db/dao/DaoTester.java b/src/test/java/io/jboot/test/db/dao/DaoTester.java new file mode 100644 index 00000000..bbdbc2f7 --- /dev/null +++ b/src/test/java/io/jboot/test/db/dao/DaoTester.java @@ -0,0 +1,42 @@ +package io.jboot.test.db.dao; + +import com.alibaba.fastjson.JSON; +import io.jboot.app.JbootApplication; +import io.jboot.db.model.Columns; +import io.jboot.test.db.model.User; +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; + +@RequestMapping("/dao") +public class DaoTester extends JbootController { + + + public static void main(String[] args) { + + //设置 datasource 1 的相关信息 + JbootApplication.setBootArg("jboot.datasource.type", "mysql"); + JbootApplication.setBootArg("jboot.datasource.url", "jdbc:mysql://127.0.0.1:3306/jbootdemo"); + JbootApplication.setBootArg("jboot.datasource.user", "root"); + JbootApplication.setBootArg("jboot.datasource.password", "123456"); + JbootApplication.setBootArg("jboot.model.scanPackage", "io.jboot.test.db.model"); + + + + //启动应用程序 + JbootApplication.run(args); + + } + + + public void index() { + User dao = new User().dao(); + renderJson(JSON.toJSON(dao.findCountByColumns(Columns.EMPTY))); + } + + + public void error() { + User dao = new User().dao(); + dao.save(); + } + +} diff --git a/src/test/java/io/jboot/test/db/model/User.java b/src/test/java/io/jboot/test/db/model/User.java index b8eb16c3..e00c770b 100644 --- a/src/test/java/io/jboot/test/db/model/User.java +++ b/src/test/java/io/jboot/test/db/model/User.java @@ -6,7 +6,7 @@ import io.jboot.db.model.JbootModel; import io.jboot.web.json.JsonIgnore; @Table(tableName = "user",primaryKey = "id") -public class User extends JbootModel { +public class User extends JbootModel { @JSONField(name = "sex") public String getSexString(){ -- Gitee From c90f8410035acbb7fd86dc9b1701b8a92466d8f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 26 Apr 2022 14:42:14 +0800 Subject: [PATCH 1684/1965] fixed Model.dao().use("....") error --- .../java/io/jboot/db/model/JbootModel.java | 71 ++++++++++++------- .../io/jboot/db/model/JbootModelExts.java | 9 +++ .../java/io/jboot/test/db/dao/DaoTester.java | 21 +++++- 3 files changed, 74 insertions(+), 27 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index bdfb2bf0..0a1cef3f 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -98,14 +98,14 @@ public class JbootModel> extends Model { if (StrUtil.isBlank(alias)) { throw new IllegalArgumentException("alias must not be null or empty."); } - M model = getOrCopyModel(); + M model = getOrCopyDao(); model.alias = alias; return model; } protected Joiner joining(String type, String table, boolean condition) { - M model = getOrCopyModel(); + M model = getOrCopyDao(); if (model.joins == null) { model.joins = new LinkedList<>(); } @@ -125,30 +125,30 @@ public class JbootModel> extends Model { if (StrUtil.isBlank(loadColumns)) { throw new IllegalArgumentException("loadColumns must not be null or empty."); } - M model = getOrCopyModel(); + M model = getOrCopyDao(); model.loadColumns = loadColumns; return model; } - public M distinct(String distinctColumnName) { - if (StrUtil.isBlank(distinctColumnName)) { - throw new IllegalArgumentException("distinctColumnName must not be null or empty."); + public M distinct(String columnName) { + if (StrUtil.isBlank(columnName)) { + throw new IllegalArgumentException("columnName must not be null or empty."); } - M model = getOrCopyModel(); + M dao = getOrCopyDao(); // model._setExtAttr(JbootModelExts.DISTINCT, distinctColumnName); - JbootModelExts.setDistinctColumn(model, distinctColumnName); - return model; + JbootModelExts.setDistinctColumn(dao, columnName); + return dao; } - private M getOrCopyModel() { + private M getOrCopyDao() { if (isCopyModel) { return (M) this; } else { - M model = copy()._setConfigName(datasourceName); - model.isCopyModel = true; - return model; + M dao = copy(false)._setConfigName(datasourceName); + dao.isCopyModel = true; + return dao; } } @@ -159,12 +159,25 @@ public class JbootModel> extends Model { * @return */ public M copy() { + return copy(true); + } + + + /** + * copy model with attrs or false + * + * @param withAttrs + * @return + */ + private M copy(boolean withAttrs) { M m = null; try { m = (M) _getUsefulClass().newInstance(); - m.put(_getAttrs()); - } catch (Throwable e) { - e.printStackTrace(); + if (withAttrs) { + m.put(_getAttrs()); + } + } catch (Exception e) { + LOG.error(e.toString(), e); } return m; } @@ -186,8 +199,8 @@ public class JbootModel> extends Model { m.set(attrKey, o); } } - } catch (Throwable e) { - e.printStackTrace(); + } catch (Exception e) { + LOG.error(e.toString(), e); } return m; } @@ -205,9 +218,15 @@ public class JbootModel> extends Model { } + /** + * 优先使用哪个数据源进行查询 + * + * @param configNames + * @return + */ public M useFirst(String... configNames) { if (configNames == null || configNames.length == 0) { - throw new IllegalArgumentException("configNames must not empty."); + throw new IllegalArgumentException("configNames must not be null or empty."); } for (String name : configNames) { @@ -220,18 +239,18 @@ public class JbootModel> extends Model { } - private M use(String configName, boolean validateExist) { - M newDao = this.get(DATASOURCE_CACHE_PREFIX + configName); + private M use(String configName, boolean validateDatasourceExist) { + M newDao = JbootModelExts.getDatasourceDAO(this, DATASOURCE_CACHE_PREFIX + configName); if (newDao == null) { - newDao = this.copy()._setConfigName(configName); + newDao = this.copy(false)._setConfigName(configName); if (newDao._getConfig() == null) { - if (validateExist) { - throw new JbootIllegalConfigException("the datasource \"" + configName + "\" not config well, please config it in jboot.properties."); + if (validateDatasourceExist) { + throw new JbootIllegalConfigException("The datasource \"" + configName + "\" not config well, please config it in jboot.properties."); } else { return null; } } else { - this.put(DATASOURCE_CACHE_PREFIX + configName, newDao); + JbootModelExts.setDatasourceDAO(this, DATASOURCE_CACHE_PREFIX + configName, newDao); } } return newDao; @@ -243,6 +262,7 @@ public class JbootModel> extends Model { return (M) this; } + @Override protected Config _getConfig() { if (datasourceName != null) { @@ -277,7 +297,6 @@ public class JbootModel> extends Model { set(column_created, new Date()); } - // 生成主键,只对单一主键的表生成,如果是多主键,不生成。 String[] pkeys = _getPrimaryKeys(); if (pkeys != null && pkeys.length == 1 && get(pkeys[0]) == null) { diff --git a/src/main/java/io/jboot/db/model/JbootModelExts.java b/src/main/java/io/jboot/db/model/JbootModelExts.java index e44b404c..62c291cf 100644 --- a/src/main/java/io/jboot/db/model/JbootModelExts.java +++ b/src/main/java/io/jboot/db/model/JbootModelExts.java @@ -56,4 +56,13 @@ public class JbootModelExts { } + public static void setDatasourceDAO(JbootModel holder, String key, JbootModel datasourceDAO) { + setExtAttr(holder, key, datasourceDAO); + } + + public static T getDatasourceDAO(JbootModel holder, String key) { + return (T) getExtAttr(holder, key); + } + + } diff --git a/src/test/java/io/jboot/test/db/dao/DaoTester.java b/src/test/java/io/jboot/test/db/dao/DaoTester.java index bbdbc2f7..d1593a2a 100644 --- a/src/test/java/io/jboot/test/db/dao/DaoTester.java +++ b/src/test/java/io/jboot/test/db/dao/DaoTester.java @@ -7,6 +7,8 @@ import io.jboot.test.db.model.User; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; +import java.util.List; + @RequestMapping("/dao") public class DaoTester extends JbootController { @@ -21,7 +23,6 @@ public class DaoTester extends JbootController { JbootApplication.setBootArg("jboot.model.scanPackage", "io.jboot.test.db.model"); - //启动应用程序 JbootApplication.run(args); @@ -33,6 +34,24 @@ public class DaoTester extends JbootController { renderJson(JSON.toJSON(dao.findCountByColumns(Columns.EMPTY))); } + public void distinct() { + User dao = new User().dao(); + List users = dao.distinct("id").findAll(); + renderJson(JSON.toJSON(users)); + } + + public void use() { + User dao = new User().dao(); + List users = dao.use("id").findAll(); + renderJson(JSON.toJSON(users)); + } + + public void useFirst() { + User dao = new User().dao(); + List users = dao.useFirst("id").findAll(); + renderJson(JSON.toJSON(users)); + } + public void error() { User dao = new User().dao(); -- Gitee From 0edfe40d6adeaa1a2638444e0c10ee56a35a95eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 26 Apr 2022 15:39:01 +0800 Subject: [PATCH 1685/1965] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=20@Size(max=3Dxxx)=20=E6=8B=A6=E6=88=AA=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../valid/interceptor/SizeInterceptor.java | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java index 2fcdb3ae..98a26410 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java @@ -20,6 +20,7 @@ import com.jfinal.aop.Invocation; import com.jfinal.kit.Ret; import io.jboot.components.valid.ValidUtil; import io.jboot.utils.ClassUtil; +import io.jboot.utils.StrUtil; import javax.validation.constraints.Size; import java.lang.reflect.Parameter; @@ -35,29 +36,38 @@ public class SizeInterceptor implements Interceptor { for (int index = 0; index < parameters.length; index++) { Size size = parameters[index].getAnnotation(Size.class); - if (size != null) { - Object validObject = inv.getArg(index); - if (validObject == null) { - String reason = parameters[index].getName() + " need size is " + size.min() + " ~ " + size.max() - + ", but current value is null at method: " + ClassUtil.buildMethodString(inv.getMethod()); - Ret paras = Ret.by("max", size.max()).set("min", size.min()); - ValidUtil.throwValidException(parameters[index].getName(), size.message(), paras, reason); - return; - } - - int len = getObjectLen(validObject); - if (len < size.min() || len > size.max()) { - String reason = parameters[index].getName() + " need size is " + size.min() + " ~ " + size.max() - + ", but current value size (or length) is " + len + " at method: " + ClassUtil.buildMethodString(inv.getMethod()); - Ret paras = Ret.by("max", size.max()).set("min", size.min()); - ValidUtil.throwValidException(parameters[index].getName(), size.message(), paras, reason); - } + if (size == null) { + continue; + } + + Object validObject = inv.getArg(index); + + //不指定 @Size(min=xxx),值配置了 max,则跳过空数据的内容 + if (size.min() == 0 && (validObject == null || (validObject instanceof String && StrUtil.isBlank((String) validObject)))) { + continue; + } + + if (validObject == null) { + String reason = parameters[index].getName() + " need size is " + size.min() + " ~ " + size.max() + + ", but current value is null at method: " + ClassUtil.buildMethodString(inv.getMethod()); + Ret paras = Ret.by("max", size.max()).set("min", size.min()); + ValidUtil.throwValidException(parameters[index].getName(), size.message(), paras, reason); + return; + } + + int len = getObjectLen(validObject); + if (len < size.min() || len > size.max()) { + String reason = parameters[index].getName() + " need size is " + size.min() + " ~ " + size.max() + + ", but current value size (or length) is " + len + " at method: " + ClassUtil.buildMethodString(inv.getMethod()); + Ret paras = Ret.by("max", size.max()).set("min", size.min()); + ValidUtil.throwValidException(parameters[index].getName(), size.message(), paras, reason); } } inv.invoke(); } + private int getObjectLen(Object validObject) { if (validObject instanceof Number) { return ((Number) validObject).intValue(); -- Gitee From e053cbbb8d8080d707ca6da94a461e940c496af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 26 Apr 2022 15:47:30 +0800 Subject: [PATCH 1686/1965] fixed close #I53SDX --- src/main/java/io/jboot/components/valid/ValidErrorRender.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/components/valid/ValidErrorRender.java b/src/main/java/io/jboot/components/valid/ValidErrorRender.java index 2bd1a062..bedcd21d 100644 --- a/src/main/java/io/jboot/components/valid/ValidErrorRender.java +++ b/src/main/java/io/jboot/components/valid/ValidErrorRender.java @@ -67,9 +67,8 @@ public class ValidErrorRender extends Render { } public String getErrorJson() { - Ret ret = Ret.fail().set("errorCode", errorCode); + Ret ret = Ret.fail(validException.getMessage()).set("errorCode", errorCode); ret.set("throwable", validException.getClass().getName() + ": " + this.validException.getMessage()); - ret.set("message", validException.getMessage()); ret.set("errorMessage", validException.getReason()); ret.set("formName", validException.getFormName()); return JsonKit.toJson(ret); -- Gitee From 353c258a6f537f50ab62be896814affed7346ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 26 Apr 2022 15:59:59 +0800 Subject: [PATCH 1687/1965] fixed close #I52SMK --- .../jboot/aop/InterceptorBuilderManager.java | 14 ++++++-------- src/main/java/io/jboot/aop/Interceptors.java | 6 ++++-- src/main/java/io/jboot/utils/ClassUtil.java | 18 ++++++++---------- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java index 65f6e48f..3c61b52a 100644 --- a/src/main/java/io/jboot/aop/InterceptorBuilderManager.java +++ b/src/main/java/io/jboot/aop/InterceptorBuilderManager.java @@ -37,17 +37,15 @@ public class InterceptorBuilderManager{ return me; } - public InterceptorBuilderManager() { + private InterceptorBuilderManager() { List> builderClasses = ClassScanner.scanSubClass(InterceptorBuilder.class,true); - if (builderClasses != null){ - for (Class builderClass : builderClasses){ - if (builderClass.getAnnotation(AutoLoad.class) != null){ - addInterceptorBuilder(ClassUtil.newInstance(builderClass,false)); - } + for (Class builderClass : builderClasses){ + if (builderClass.getAnnotation(AutoLoad.class) != null){ + addInterceptorBuilder(ClassUtil.newInstance(builderClass,false)); } - - InterceptorCache.clear(); } + + InterceptorCache.clear(); } private List interceptorBuilders = new CopyOnWriteArrayList(); diff --git a/src/main/java/io/jboot/aop/Interceptors.java b/src/main/java/io/jboot/aop/Interceptors.java index 740f6243..b61bedb6 100644 --- a/src/main/java/io/jboot/aop/Interceptors.java +++ b/src/main/java/io/jboot/aop/Interceptors.java @@ -15,6 +15,7 @@ */ package io.jboot.aop; +import com.jfinal.aop.Aop; import com.jfinal.aop.Interceptor; import com.jfinal.aop.InterceptorManager; import io.jboot.utils.ClassUtil; @@ -262,7 +263,7 @@ public class Interceptors { private Interceptor singleton(Class interceptorClass) { - return ClassUtil.singleton(interceptorClass, false, true); + return ClassUtil.singleton(interceptorClass, false); } public List getWarppers() { @@ -270,11 +271,12 @@ public class Interceptors { } static class InterceptorWarpper { + private Interceptor interceptor; private int weight; public InterceptorWarpper(Interceptor interceptor, int weight) { - this.interceptor = interceptor; + this.interceptor = Aop.inject(interceptor); this.weight = weight; } diff --git a/src/main/java/io/jboot/utils/ClassUtil.java b/src/main/java/io/jboot/utils/ClassUtil.java index 160a545f..4eb11b27 100644 --- a/src/main/java/io/jboot/utils/ClassUtil.java +++ b/src/main/java/io/jboot/utils/ClassUtil.java @@ -62,7 +62,7 @@ public class ClassUtil { if (ret != null) { singletons.put(clazz, ret); } else { - LOG.error("cannot new newInstance!!!!"); + LOG.error("Can not new newInstance for class: " + clazz.getName()); } } return (T) ret; @@ -78,7 +78,7 @@ public class ClassUtil { } singletons.put(clazz, ret); } else { - LOG.error("cannot new newInstance!!!!"); + LOG.error("Can not new newInstance for class: " + clazz.getName()); } } return (T) ret; @@ -126,7 +126,7 @@ public class ClassUtil { constructor.setAccessible(true); return (T) constructor.newInstance(); } catch (Exception e) { - LOG.error("can not newInstance class:" + clazz + "\n" + e.toString(), e); + LOG.error("Can not newInstance for class:" + clazz.getName() + "\n" + e, e); } return null; @@ -152,7 +152,7 @@ public class ClassUtil { } return (T) ret; } catch (Exception e) { - LOG.error("can not newInstance class:" + clazz + "\n" + e.toString(), e); + LOG.error("Can not newInstance for class:" + clazz.getName() + "\n" + e, e); } return null; @@ -174,7 +174,7 @@ public class ClassUtil { Method method = getStaticConstruct(staticConstruct.value(), clazz); if (method == null) { - throw new JbootException("Can not new instance by static constrauct for class: " + clazz); + throw new JbootException("Can not new instance by static constrauct for class: " + clazz.getName()); } try { @@ -182,8 +182,7 @@ public class ClassUtil { } catch (Exception e) { LOG.error("Can not invoke method:" + method.getName() - + " in class : "+ clazz + "\n" - + e, e); + + " in class : " + clazz.getName() + "\n" + e, e); if (e instanceof RuntimeException) { throw (RuntimeException) e; @@ -247,7 +246,7 @@ public class ClassUtil { Class clazz = (Class) Class.forName(clazzName, false, classLoader); return newInstance(clazz, createByAop); } catch (Exception e) { - LOG.error("can not newInstance class:" + clazzName + "\n" + e.toString(), e); + LOG.error("can not newInstance for class:" + clazzName + "\n" + e.toString(), e); } return null; @@ -263,7 +262,7 @@ public class ClassUtil { //io.jboot.test.app.TestAppListener_$$_jvstb9f_0 by:javassist final String name = clazz.getName(); - if (name.contains(ENHANCER_BY) || name.contains(JAVASSIST_BY)){ + if (name.contains(ENHANCER_BY) || name.contains(JAVASSIST_BY)) { return clazz.getSuperclass(); } @@ -326,7 +325,6 @@ public class ClassUtil { public static String buildMethodString(Method method) { - StringBuilder sb = new StringBuilder() .append(method.getDeclaringClass().getName()) .append(".") -- Gitee From 9bede172d3b4a2448b92399f16541525500491fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 26 Apr 2022 16:47:47 +0800 Subject: [PATCH 1688/1965] fixed: int[] length valid error --- .../interceptor/DecimalMaxInterceptor.java | 18 ++++---- .../interceptor/DecimalMinInterceptor.java | 15 +++--- .../valid/interceptor/DigitsInterceptor.java | 16 ++++--- .../valid/interceptor/EmailInterceptor.java | 14 +++--- .../valid/interceptor/MaxInterceptor.java | 24 +++++++--- .../valid/interceptor/MinInterceptor.java | 22 ++++++--- .../interceptor/NegativeInterceptor.java | 15 +++--- .../NegativeOrZeroInterceptor.java | 16 ++++--- .../interceptor/NotBlankInterceptor.java | 14 +++--- .../interceptor/NotEmptyInterceptor.java | 24 +++------- .../valid/interceptor/PatternInterceptor.java | 16 ++++--- .../interceptor/PositiveInterceptor.java | 14 +++--- .../PositiveOrZeroInterceptor.java | 13 +++--- .../valid/interceptor/SizeInterceptor.java | 26 +---------- .../components/valid/interceptor/Util.java | 46 +++++++++++++++++++ .../valid/interceptor/ValidInterceptor.java | 26 ++++++----- 16 files changed, 187 insertions(+), 132 deletions(-) create mode 100644 src/main/java/io/jboot/components/valid/interceptor/Util.java diff --git a/src/main/java/io/jboot/components/valid/interceptor/DecimalMaxInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/DecimalMaxInterceptor.java index adc48551..63b5dfc8 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/DecimalMaxInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/DecimalMaxInterceptor.java @@ -34,14 +34,16 @@ public class DecimalMaxInterceptor implements Interceptor { for (int index = 0; index < parameters.length; index++) { DecimalMax decimalMax = parameters[index].getAnnotation(DecimalMax.class); - if (decimalMax != null) { - Object validObject = inv.getArg(index); - if (validObject != null && !matches(decimalMax, validObject)) { - String reason = parameters[index].getName() + " max value is " + decimalMax.value() - + ", but current value is " + validObject + " at method: " + ClassUtil.buildMethodString(inv.getMethod()); - Ret paras = Ret.by("value", decimalMax.value()); - ValidUtil.throwValidException(parameters[index].getName(), decimalMax.message(), paras, reason); - } + if (decimalMax == null) { + continue; + } + + Object validObject = inv.getArg(index); + if (validObject != null && !matches(decimalMax, validObject)) { + String reason = parameters[index].getName() + " max value is " + decimalMax.value() + + ", but current value is " + validObject + " at method: " + ClassUtil.buildMethodString(inv.getMethod()); + Ret paras = Ret.by("value", decimalMax.value()); + ValidUtil.throwValidException(parameters[index].getName(), decimalMax.message(), paras, reason); } } diff --git a/src/main/java/io/jboot/components/valid/interceptor/DecimalMinInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/DecimalMinInterceptor.java index 32f210b0..789ba443 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/DecimalMinInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/DecimalMinInterceptor.java @@ -34,13 +34,14 @@ public class DecimalMinInterceptor implements Interceptor { for (int index = 0; index < parameters.length; index++) { DecimalMin decimalMin = parameters[index].getAnnotation(DecimalMin.class); - if (decimalMin != null) { - Object validObject = inv.getArg(index); - if (validObject != null && !matches(decimalMin, validObject)) { - String reason = parameters[index].getName() + " min value is " + decimalMin.value() + ", but current value is " + validObject + " at method: " + ClassUtil.buildMethodString(inv.getMethod()); - Ret paras = Ret.by("value", decimalMin.value()); - ValidUtil.throwValidException(parameters[index].getName(), decimalMin.message(), paras, reason); - } + if (decimalMin == null) { + continue; + } + Object validObject = inv.getArg(index); + if (validObject != null && !matches(decimalMin, validObject)) { + String reason = parameters[index].getName() + " min value is " + decimalMin.value() + ", but current value is " + validObject + " at method: " + ClassUtil.buildMethodString(inv.getMethod()); + Ret paras = Ret.by("value", decimalMin.value()); + ValidUtil.throwValidException(parameters[index].getName(), decimalMin.message(), paras, reason); } } diff --git a/src/main/java/io/jboot/components/valid/interceptor/DigitsInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/DigitsInterceptor.java index 07c6ec6f..40f307d5 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/DigitsInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/DigitsInterceptor.java @@ -31,13 +31,15 @@ public class DigitsInterceptor implements Interceptor { Parameter[] parameters = inv.getMethod().getParameters(); for (int index = 0; index < parameters.length; index++) { Digits digits = parameters[index].getAnnotation(Digits.class); - if (digits != null) { - Object validObject = inv.getArg(index); - if (validObject != null && !matchesDigits(digits, validObject)) { - String reason = parameters[index].getName() + " not matches @Digits at method: " + ClassUtil.buildMethodString(inv.getMethod()); - Ret paras = Ret.by("integer", digits.integer()).set("fraction", digits.fraction()); - ValidUtil.throwValidException(parameters[index].getName(), digits.message(), paras, reason); - } + if (digits == null) { + continue; + } + + Object validObject = inv.getArg(index); + if (validObject != null && !matchesDigits(digits, validObject)) { + String reason = parameters[index].getName() + " not matches @Digits at method: " + ClassUtil.buildMethodString(inv.getMethod()); + Ret paras = Ret.by("integer", digits.integer()).set("fraction", digits.fraction()); + ValidUtil.throwValidException(parameters[index].getName(), digits.message(), paras, reason); } } diff --git a/src/main/java/io/jboot/components/valid/interceptor/EmailInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/EmailInterceptor.java index 79b9ab0f..37611a71 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/EmailInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/EmailInterceptor.java @@ -32,12 +32,14 @@ public class EmailInterceptor implements Interceptor { Parameter[] parameters = inv.getMethod().getParameters(); for (int index = 0; index < parameters.length; index++) { Email email = parameters[index].getAnnotation(Email.class); - if (email != null) { - Object validObject = inv.getArg(index); - if (validObject == null || !matches(email, validObject.toString())) { - String reason = parameters[index].getName() + " is not email at method: " + ClassUtil.buildMethodString(inv.getMethod()); - ValidUtil.throwValidException(parameters[index].getName(), email.message(), reason); - } + if (email == null) { + continue; + } + + Object validObject = inv.getArg(index); + if (validObject == null || !matches(email, validObject.toString())) { + String reason = parameters[index].getName() + " is not email at method: " + ClassUtil.buildMethodString(inv.getMethod()); + ValidUtil.throwValidException(parameters[index].getName(), email.message(), reason); } } diff --git a/src/main/java/io/jboot/components/valid/interceptor/MaxInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/MaxInterceptor.java index c3117bee..942648b4 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/MaxInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/MaxInterceptor.java @@ -32,13 +32,21 @@ public class MaxInterceptor implements Interceptor { for (int index = 0; index < parameters.length; index++) { Max max = parameters[index].getAnnotation(Max.class); - if (max != null) { - Object validObject = inv.getArg(index); - if (validObject != null && max.value() < ((Number) validObject).longValue()) { - String reason = parameters[index].getName() + " max value is " + max.value() + ", but current value is " + validObject + " at method: " + ClassUtil.buildMethodString(inv.getMethod()); - Ret paras = Ret.by("value", max.value()); - ValidUtil.throwValidException(parameters[index].getName(), max.message(), paras, reason); - } + if (max == null) { + continue; + } + + Object validObject = inv.getArg(index); + if (validObject == null) { + continue; + } + + long objectLen = Util.getObjectLen(validObject); + + if (max.value() < objectLen) { + String reason = parameters[index].getName() + " max value is " + max.value() + ", but current value is " + validObject + " at method: " + ClassUtil.buildMethodString(inv.getMethod()); + Ret paras = Ret.by("value", max.value()); + ValidUtil.throwValidException(parameters[index].getName(), max.message(), paras, reason); } } @@ -46,4 +54,6 @@ public class MaxInterceptor implements Interceptor { } + + } \ No newline at end of file diff --git a/src/main/java/io/jboot/components/valid/interceptor/MinInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/MinInterceptor.java index 8904bdb3..ff60f7eb 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/MinInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/MinInterceptor.java @@ -32,13 +32,21 @@ public class MinInterceptor implements Interceptor { for (int index = 0; index < parameters.length; index++) { Min min = parameters[index].getAnnotation(Min.class); - if (min != null) { - Object validObject = inv.getArg(index); - if (validObject != null && min.value() > ((Number) validObject).longValue()) { - String reason = parameters[index].getName() + " min value is " + min.value() + ", but current value is " + validObject + " at method: " + ClassUtil.buildMethodString(inv.getMethod()); - Ret paras = Ret.by("value", min.value()); - ValidUtil.throwValidException(parameters[index].getName(), min.message(), paras, reason); - } + if (min == null) { + continue; + } + + Object validObject = inv.getArg(index); + if (validObject == null) { + continue; + } + + long objectLen = Util.getObjectLen(validObject); + + if (min.value() > objectLen) { + String reason = parameters[index].getName() + " min value is " + min.value() + ", but current value is " + validObject + " at method: " + ClassUtil.buildMethodString(inv.getMethod()); + Ret paras = Ret.by("value", min.value()); + ValidUtil.throwValidException(parameters[index].getName(), min.message(), paras, reason); } } diff --git a/src/main/java/io/jboot/components/valid/interceptor/NegativeInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/NegativeInterceptor.java index 13af998d..1069aa0e 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/NegativeInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/NegativeInterceptor.java @@ -30,12 +30,15 @@ public class NegativeInterceptor implements Interceptor { Parameter[] parameters = inv.getMethod().getParameters(); for (int index = 0; index < parameters.length; index++) { Negative negative = parameters[index].getAnnotation(Negative.class); - if (negative != null) { - Object validObject = inv.getArg(index); - if (validObject == null || ((Number) validObject).longValue() >= 0) { - String reason = parameters[index].getName() + " is null or not negative at method: " + ClassUtil.buildMethodString(inv.getMethod()); - ValidUtil.throwValidException(parameters[index].getName(), negative.message(), reason); - } + if (negative == null) { + continue; + } + + Object validObject = inv.getArg(index); + + if (!(validObject instanceof Number) || ((Number) validObject).longValue() >= 0) { + String reason = parameters[index].getName() + " is null or not negative at method: " + ClassUtil.buildMethodString(inv.getMethod()); + ValidUtil.throwValidException(parameters[index].getName(), negative.message(), reason); } } diff --git a/src/main/java/io/jboot/components/valid/interceptor/NegativeOrZeroInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/NegativeOrZeroInterceptor.java index 46ea3d06..9a2d88d8 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/NegativeOrZeroInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/NegativeOrZeroInterceptor.java @@ -30,13 +30,17 @@ public class NegativeOrZeroInterceptor implements Interceptor { Parameter[] parameters = inv.getMethod().getParameters(); for (int index = 0; index < parameters.length; index++) { NegativeOrZero negativeOrZero = parameters[index].getAnnotation(NegativeOrZero.class); - if (negativeOrZero != null) { - Object validObject = inv.getArg(index); - if (validObject == null || ((Number) validObject).longValue() > 0) { - String reason = parameters[index].getName() + " is null or greater than 0 at method: " + ClassUtil.buildMethodString(inv.getMethod()); - ValidUtil.throwValidException(parameters[index].getName(), negativeOrZero.message(), reason); - } + if (negativeOrZero == null) { + continue; } + + Object validObject = inv.getArg(index); + + if (!(validObject instanceof Number) || ((Number) validObject).longValue() > 0) { + String reason = parameters[index].getName() + " is null or greater than 0 at method: " + ClassUtil.buildMethodString(inv.getMethod()); + ValidUtil.throwValidException(parameters[index].getName(), negativeOrZero.message(), reason); + } + } inv.invoke(); diff --git a/src/main/java/io/jboot/components/valid/interceptor/NotBlankInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/NotBlankInterceptor.java index 184a3384..b44c78fd 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/NotBlankInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/NotBlankInterceptor.java @@ -31,12 +31,14 @@ public class NotBlankInterceptor implements Interceptor { Parameter[] parameters = inv.getMethod().getParameters(); for (int index = 0; index < parameters.length; index++) { NotBlank notBlank = parameters[index].getAnnotation(NotBlank.class); - if (notBlank != null) { - Object validObject = inv.getArg(index); - if (validObject == null || (validObject instanceof String && StrUtil.isBlank((String) validObject))) { - String msg = parameters[index].getName() + " is blank at method: " + ClassUtil.buildMethodString(inv.getMethod()); - ValidUtil.throwValidException(parameters[index].getName(), notBlank.message(), msg); - } + if (notBlank == null) { + continue; + } + Object validObject = inv.getArg(index); + + if (validObject == null || (validObject instanceof String && StrUtil.isBlank((String) validObject))) { + String msg = parameters[index].getName() + " is blank at method: " + ClassUtil.buildMethodString(inv.getMethod()); + ValidUtil.throwValidException(parameters[index].getName(), notBlank.message(), msg); } } diff --git a/src/main/java/io/jboot/components/valid/interceptor/NotEmptyInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/NotEmptyInterceptor.java index bfeea9d1..3ea515fd 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/NotEmptyInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/NotEmptyInterceptor.java @@ -19,12 +19,9 @@ import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; import io.jboot.components.valid.ValidUtil; import io.jboot.utils.ClassUtil; -import io.jboot.utils.StrUtil; import javax.validation.constraints.NotEmpty; import java.lang.reflect.Parameter; -import java.util.Collection; -import java.util.Map; public class NotEmptyInterceptor implements Interceptor { @@ -33,20 +30,13 @@ public class NotEmptyInterceptor implements Interceptor { Parameter[] parameters = inv.getMethod().getParameters(); for (int index = 0; index < parameters.length; index++) { NotEmpty notEmpty = parameters[index].getAnnotation(NotEmpty.class); - if (notEmpty != null) { - Object validObject = inv.getArg(index); - if (validObject == null - || (validObject instanceof String && StrUtil.isBlank((String) validObject)) - || (validObject instanceof Map && ((Map) validObject).isEmpty()) - || (validObject instanceof Collection && ((Collection) validObject).isEmpty()) - || (validObject.getClass().isArray() && ((Object[]) validObject).length == 0) - || (validObject.getClass() == int[].class && ((int[]) validObject).length == 0) - || (validObject.getClass() == long[].class && ((long[]) validObject).length == 0) - || (validObject.getClass() == short[].class && ((short[]) validObject).length == 0) - ) { - String reason = parameters[index].getName() + " is null or empty at method: " + ClassUtil.buildMethodString(inv.getMethod()); - ValidUtil.throwValidException(parameters[index].getName(), notEmpty.message(), reason); - } + if (notEmpty == null) { + continue; + } + Object validObject = inv.getArg(index); + if (validObject == null || Util.getObjectLen(validObject) <= 0) { + String reason = parameters[index].getName() + " is null or empty at method: " + ClassUtil.buildMethodString(inv.getMethod()); + ValidUtil.throwValidException(parameters[index].getName(), notEmpty.message(), reason); } } diff --git a/src/main/java/io/jboot/components/valid/interceptor/PatternInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/PatternInterceptor.java index e7e15436..fe9e1d6e 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/PatternInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/PatternInterceptor.java @@ -31,13 +31,15 @@ public class PatternInterceptor implements Interceptor { Parameter[] parameters = inv.getMethod().getParameters(); for (int index = 0; index < parameters.length; index++) { Pattern pattern = parameters[index].getAnnotation(Pattern.class); - if (pattern != null) { - Object validObject = inv.getArg(index); - if (validObject == null || !matches(pattern, validObject.toString())) { - String reason = parameters[index].getName() + " is null or not matches the regex at method: " + ClassUtil.buildMethodString(inv.getMethod()); - Ret paras = Ret.by("regexp", pattern.regexp()); - ValidUtil.throwValidException(parameters[index].getName(), pattern.message(), paras, reason); - } + if (pattern == null) { + continue; + } + + Object validObject = inv.getArg(index); + if (validObject == null || !matches(pattern, validObject.toString())) { + String reason = parameters[index].getName() + " is null or not matches the regex at method: " + ClassUtil.buildMethodString(inv.getMethod()); + Ret paras = Ret.by("regexp", pattern.regexp()); + ValidUtil.throwValidException(parameters[index].getName(), pattern.message(), paras, reason); } } diff --git a/src/main/java/io/jboot/components/valid/interceptor/PositiveInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/PositiveInterceptor.java index f55605cd..11eb8ff6 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/PositiveInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/PositiveInterceptor.java @@ -30,12 +30,14 @@ public class PositiveInterceptor implements Interceptor { Parameter[] parameters = inv.getMethod().getParameters(); for (int index = 0; index < parameters.length; index++) { Positive positive = parameters[index].getAnnotation(Positive.class); - if (positive != null) { - Object validObject = inv.getArg(index); - if (validObject == null || ((Number) validObject).longValue() <= 0) { - String reason = parameters[index].getName() + " is null or not positive at method: " + ClassUtil.buildMethodString(inv.getMethod()); - ValidUtil.throwValidException(parameters[index].getName(), positive.message(), reason); - } + if (positive == null) { + continue; + } + + Object validObject = inv.getArg(index); + if (!(validObject instanceof Number) || ((Number) validObject).longValue() <= 0) { + String reason = parameters[index].getName() + " is null or not positive at method: " + ClassUtil.buildMethodString(inv.getMethod()); + ValidUtil.throwValidException(parameters[index].getName(), positive.message(), reason); } } diff --git a/src/main/java/io/jboot/components/valid/interceptor/PositiveOrZeroInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/PositiveOrZeroInterceptor.java index d787c47e..6d7c2ff5 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/PositiveOrZeroInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/PositiveOrZeroInterceptor.java @@ -31,12 +31,13 @@ public class PositiveOrZeroInterceptor implements Interceptor { for (int index = 0; index < parameters.length; index++) { PositiveOrZero positiveOrZero = parameters[index].getAnnotation(PositiveOrZero.class); - if (positiveOrZero != null) { - Object validObject = inv.getArg(index); - if (validObject == null || ((Number) validObject).longValue() < 0) { - String reason = parameters[index].getName() + " is null or less than 0 at method: " + ClassUtil.buildMethodString(inv.getMethod()); - ValidUtil.throwValidException(parameters[index].getName(), positiveOrZero.message(), reason); - } + if (positiveOrZero == null) { + continue; + } + Object validObject = inv.getArg(index); + if (!(validObject instanceof Number) || ((Number) validObject).longValue() < 0) { + String reason = parameters[index].getName() + " is null or less than 0 at method: " + ClassUtil.buildMethodString(inv.getMethod()); + ValidUtil.throwValidException(parameters[index].getName(), positiveOrZero.message(), reason); } } diff --git a/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java index 98a26410..64592764 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/SizeInterceptor.java @@ -24,8 +24,6 @@ import io.jboot.utils.StrUtil; import javax.validation.constraints.Size; import java.lang.reflect.Parameter; -import java.util.Collection; -import java.util.Map; public class SizeInterceptor implements Interceptor { @@ -55,7 +53,8 @@ public class SizeInterceptor implements Interceptor { return; } - int len = getObjectLen(validObject); + long len = Util.getObjectLen(validObject); + if (len < size.min() || len > size.max()) { String reason = parameters[index].getName() + " need size is " + size.min() + " ~ " + size.max() + ", but current value size (or length) is " + len + " at method: " + ClassUtil.buildMethodString(inv.getMethod()); @@ -68,25 +67,4 @@ public class SizeInterceptor implements Interceptor { } - private int getObjectLen(Object validObject) { - if (validObject instanceof Number) { - return ((Number) validObject).intValue(); - } - if (validObject instanceof CharSequence) { - return ((CharSequence) validObject).length(); - } - if (validObject instanceof Map) { - return ((Map) validObject).size(); - } - if (validObject instanceof Collection) { - return ((Collection) validObject).size(); - } - if (validObject.getClass().isArray()) { - return ((Object[]) validObject).length; - } - - return -1; - } - - } \ No newline at end of file diff --git a/src/main/java/io/jboot/components/valid/interceptor/Util.java b/src/main/java/io/jboot/components/valid/interceptor/Util.java new file mode 100644 index 00000000..281c3430 --- /dev/null +++ b/src/main/java/io/jboot/components/valid/interceptor/Util.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.valid.interceptor; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Map; + +class Util { + + public static long getObjectLen(Object validObject) { + if (validObject == null) { + return 0; + } + if (validObject instanceof Number) { + return ((Number) validObject).longValue(); + } + if (validObject instanceof CharSequence) { + return ((CharSequence) validObject).length(); + } + if (validObject instanceof Map) { + return ((Map) validObject).size(); + } + if (validObject instanceof Collection) { + return ((Collection) validObject).size(); + } + if (validObject.getClass().isArray()) { + return Array.getLength(validObject); + } + throw new IllegalArgumentException("Can not get object length for class: " + validObject.getClass().getName()); + } + +} diff --git a/src/main/java/io/jboot/components/valid/interceptor/ValidInterceptor.java b/src/main/java/io/jboot/components/valid/interceptor/ValidInterceptor.java index 5b7718f3..a52467b8 100644 --- a/src/main/java/io/jboot/components/valid/interceptor/ValidInterceptor.java +++ b/src/main/java/io/jboot/components/valid/interceptor/ValidInterceptor.java @@ -36,19 +36,21 @@ public class ValidInterceptor implements Interceptor { for (int index = 0; index < parameters.length; index++) { if (parameters[index].getAnnotation(Valid.class) != null) { Object validObject = inv.getArg(index); - if (validObject != null) { - Set> constraintViolations = ValidUtil.validate(validObject); - if (constraintViolations != null && constraintViolations.size() > 0) { - StringBuilder msg = new StringBuilder(); - for (ConstraintViolation cv : constraintViolations) { - msg.append(cv.getRootBeanClass().getName()) - .append(".") - .append(cv.getPropertyPath()) - .append(cv.getMessage()); - } - String reason = parameters[index].getName() + " is valid failed at method: " + ClassUtil.buildMethodString(inv.getMethod()); - ValidUtil.throwValidException(parameters[index].getName(), msg.toString(), reason); + if (validObject == null) { + continue; + } + Set> constraintViolations = ValidUtil.validate(validObject); + if (constraintViolations != null && constraintViolations.size() > 0) { + StringBuilder msg = new StringBuilder(); + for (ConstraintViolation cv : constraintViolations) { + msg.append(cv.getRootBeanClass().getName()) + .append(".") + .append(cv.getPropertyPath()) + .append(cv.getMessage()); } + String reason = parameters[index].getName() + " is valid failed at method: " + ClassUtil.buildMethodString(inv.getMethod()); + ValidUtil.throwValidException(parameters[index].getName(), msg.toString(), reason); + } } } -- Gitee From ef2ab2d5a51a930649fa2a7022c3f50a5f352187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 26 Apr 2022 17:12:09 +0800 Subject: [PATCH 1689/1965] add array converters --- .../java/io/jboot/core/JbootCoreConfig.java | 4 +- .../jboot/web/converter/ArrayConverters.java | 196 ++++++++++++++++++ .../{ => converter}/TypeConverterFunc.java | 17 +- 3 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/jboot/web/converter/ArrayConverters.java rename src/main/java/io/jboot/web/{ => converter}/TypeConverterFunc.java (74%) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 2778ac1d..e5bea8c3 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -56,7 +56,8 @@ import io.jboot.utils.*; import io.jboot.web.JbootActionMapping; import io.jboot.web.JbootWebConfig; import io.jboot.web.PathVariableActionMapping; -import io.jboot.web.TypeConverterFunc; +import io.jboot.web.converter.ArrayConverters; +import io.jboot.web.converter.TypeConverterFunc; import io.jboot.web.attachment.AttachmentHandler; import io.jboot.web.attachment.LocalAttachmentContainerConfig; import io.jboot.web.controller.JbootControllerManager; @@ -400,6 +401,7 @@ public class JbootCoreConfig extends JFinalConfig { //自定义参数转换方法 TypeConverter.me().setConvertFunc(new TypeConverterFunc()); + ArrayConverters.init(); //使用场景:需要等所有组件 onStart() 完成之后,再去执行某些工作的时候 diff --git a/src/main/java/io/jboot/web/converter/ArrayConverters.java b/src/main/java/io/jboot/web/converter/ArrayConverters.java new file mode 100644 index 00000000..f20d9943 --- /dev/null +++ b/src/main/java/io/jboot/web/converter/ArrayConverters.java @@ -0,0 +1,196 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.converter; + +import com.jfinal.core.converter.IConverter; +import com.jfinal.core.converter.TypeConverter; +import io.jboot.utils.StrUtil; + +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * 针对 数组 类型实现 IConverter 接口 + */ +public class ArrayConverters { + + private static String array_separator = ","; + + public static String getArraySeparator() { + return array_separator; + } + + public static void setArraySeparator(String arraySeparator) { + ArrayConverters.array_separator = arraySeparator; + } + + public static class IntArrayConverter implements IConverter { + @Override + public int[] convert(String s) { + String[] strings = s.split(array_separator); + int[] ret = new int[strings.length]; + for (int i = 0; i < strings.length; i++) { + String str = strings[i]; + ret[i] = StrUtil.isBlank(str) ? 0 : Integer.parseInt(str); + } + return ret; + } + } + + + public static class Int1ArrayConverter implements IConverter { + @Override + public Integer[] convert(String s) { + String[] strings = s.split(array_separator); + Integer[] ret = new Integer[strings.length]; + for (int i = 0; i < strings.length; i++) { + String str = strings[i]; + ret[i] = StrUtil.isBlank(str) ? null : Integer.parseInt(str); + } + return ret; + } + } + + + public static class LongArrayConverter implements IConverter { + @Override + public long[] convert(String s) { + String[] strings = s.split(array_separator); + long[] ret = new long[strings.length]; + for (int i = 0; i < strings.length; i++) { + String str = strings[i]; + ret[i] = StrUtil.isBlank(str) ? 0 : Long.parseLong(str); + } + return ret; + } + } + + + public static class Long1ArrayConverter implements IConverter { + @Override + public Long[] convert(String s) { + String[] strings = s.split(array_separator); + Long[] ret = new Long[strings.length]; + for (int i = 0; i < strings.length; i++) { + String str = strings[i]; + ret[i] = StrUtil.isBlank(str) ? null : Long.parseLong(str); + } + return ret; + } + } + + + public static class FloatArrayConverter implements IConverter { + @Override + public float[] convert(String s) { + String[] strings = s.split(array_separator); + float[] ret = new float[strings.length]; + for (int i = 0; i < strings.length; i++) { + String str = strings[i]; + ret[i] = StrUtil.isBlank(str) ? 0 : Float.parseFloat(str); + } + return ret; + } + } + + + public static class Float1ArrayConverter implements IConverter { + @Override + public Float[] convert(String s) { + String[] strings = s.split(array_separator); + Float[] ret = new Float[strings.length]; + for (int i = 0; i < strings.length; i++) { + String str = strings[i]; + ret[i] = StrUtil.isBlank(str) ? null : Float.parseFloat(str); + } + return ret; + } + } + + public static class DoubleArrayConverter implements IConverter { + @Override + public double[] convert(String s) { + String[] strings = s.split(array_separator); + double[] ret = new double[strings.length]; + for (int i = 0; i < strings.length; i++) { + String str = strings[i]; + ret[i] = StrUtil.isBlank(str) ? 0 : Double.parseDouble(str); + } + return ret; + } + } + + + public static class Double1ArrayConverter implements IConverter { + @Override + public Double[] convert(String s) { + String[] strings = s.split(array_separator); + Double[] ret = new Double[strings.length]; + for (int i = 0; i < strings.length; i++) { + String str = strings[i]; + ret[i] = StrUtil.isBlank(str) ? null : Double.parseDouble(str); + } + return ret; + } + } + + + public static class BigIntegerArrayConverter implements IConverter { + @Override + public BigInteger[] convert(String s) { + String[] strings = s.split(array_separator); + BigInteger[] ret = new BigInteger[strings.length]; + for (int i = 0; i < strings.length; i++) { + String str = strings[i]; + ret[i] = StrUtil.isBlank(str) ? null : new BigInteger(str); + } + return ret; + } + } + + + public static class BigDecimalArrayConverter implements IConverter { + @Override + public BigDecimal[] convert(String s) { + String[] strings = s.split(array_separator); + BigDecimal[] ret = new BigDecimal[strings.length]; + for (int i = 0; i < strings.length; i++) { + String str = strings[i]; + ret[i] = StrUtil.isBlank(str) ? null : new BigDecimal(str); + } + return ret; + } + } + + + public static void init() { + TypeConverter.me().regist(int[].class, new IntArrayConverter()); + TypeConverter.me().regist(Integer[].class, new Int1ArrayConverter()); + + TypeConverter.me().regist(long[].class, new LongArrayConverter()); + TypeConverter.me().regist(Long[].class, new Long1ArrayConverter()); + + TypeConverter.me().regist(float[].class, new FloatArrayConverter()); + TypeConverter.me().regist(Float[].class, new Float1ArrayConverter()); + + TypeConverter.me().regist(double[].class, new DoubleArrayConverter()); + TypeConverter.me().regist(Double[].class, new Double1ArrayConverter()); + + TypeConverter.me().regist(BigInteger[].class, new BigIntegerArrayConverter()); + TypeConverter.me().regist(BigDecimal[].class, new BigDecimalArrayConverter()); + } + +} diff --git a/src/main/java/io/jboot/web/TypeConverterFunc.java b/src/main/java/io/jboot/web/converter/TypeConverterFunc.java similarity index 74% rename from src/main/java/io/jboot/web/TypeConverterFunc.java rename to src/main/java/io/jboot/web/converter/TypeConverterFunc.java index b8a10ef5..127b11f9 100644 --- a/src/main/java/io/jboot/web/TypeConverterFunc.java +++ b/src/main/java/io/jboot/web/converter/TypeConverterFunc.java @@ -1,4 +1,19 @@ -package io.jboot.web; +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.converter; import com.jfinal.core.JFinal; import com.jfinal.core.converter.IConverter; -- Gitee From 3fb282214a44987826c1f9974f0859976fe1ae04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 26 Apr 2022 17:12:19 +0800 Subject: [PATCH 1690/1965] fixed Copyright --- .../java/io/jboot/db/annotation/PriorDatasource.java | 2 +- .../db/datasource/PriorDatasourceInterceptor.java | 2 +- src/main/java/io/jboot/support/jwt/EnableJwt.java | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/db/annotation/PriorDatasource.java b/src/main/java/io/jboot/db/annotation/PriorDatasource.java index 41749fe4..d5c0cf3c 100644 --- a/src/main/java/io/jboot/db/annotation/PriorDatasource.java +++ b/src/main/java/io/jboot/db/annotation/PriorDatasource.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/db/datasource/PriorDatasourceInterceptor.java b/src/main/java/io/jboot/db/datasource/PriorDatasourceInterceptor.java index e6153395..2874bcb5 100644 --- a/src/main/java/io/jboot/db/datasource/PriorDatasourceInterceptor.java +++ b/src/main/java/io/jboot/db/datasource/PriorDatasourceInterceptor.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com). + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/io/jboot/support/jwt/EnableJwt.java b/src/main/java/io/jboot/support/jwt/EnableJwt.java index d82c3c1e..7dded5ed 100644 --- a/src/main/java/io/jboot/support/jwt/EnableJwt.java +++ b/src/main/java/io/jboot/support/jwt/EnableJwt.java @@ -1,12 +1,12 @@ /** - * Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com). - * + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + *

        + * 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. -- Gitee From 334c762add1cf122be74fd10ccdd0bbc3c32335e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 27 Apr 2022 09:24:40 +0800 Subject: [PATCH 1691/1965] update redis mq test --- .../io/jboot/test/distributed/Runable1.java | 34 +++++++++---------- .../io/jboot/test/distributed/Runable2.java | 34 +++++++++---------- .../io/jboot/test/distributed/Runable3.java | 34 +++++++++---------- .../io/jboot/test/mq/redis/RedisMqSender.java | 30 ++++++++-------- 4 files changed, 66 insertions(+), 66 deletions(-) diff --git a/src/test/java/io/jboot/test/distributed/Runable1.java b/src/test/java/io/jboot/test/distributed/Runable1.java index d2a41138..8aa12774 100644 --- a/src/test/java/io/jboot/test/distributed/Runable1.java +++ b/src/test/java/io/jboot/test/distributed/Runable1.java @@ -1,17 +1,17 @@ -package io.jboot.test.distributed; - -import io.jboot.components.schedule.annotation.EnableDistributedRunnable; -import io.jboot.components.schedule.annotation.FixedDelay; - -@FixedDelay(period = 30) -@EnableDistributedRunnable(redisKey = "aaa",expireSeconds = 30) -public class Runable1 implements Runnable { - int i =0; - @Override - public void run() { - if (i++ >= 3){ - throw new IllegalStateException("messss..."); - } - System.err.println("Runable1.running.........."+this); - } -} +//package io.jboot.test.distributed; +// +//import io.jboot.components.schedule.annotation.EnableDistributedRunnable; +//import io.jboot.components.schedule.annotation.FixedDelay; +// +//@FixedDelay(period = 30) +//@EnableDistributedRunnable(redisKey = "aaa",expireSeconds = 30) +//public class Runable1 implements Runnable { +// int i =0; +// @Override +// public void run() { +// if (i++ >= 3){ +// throw new IllegalStateException("messss..."); +// } +// System.err.println("Runable1.running.........."+this); +// } +//} diff --git a/src/test/java/io/jboot/test/distributed/Runable2.java b/src/test/java/io/jboot/test/distributed/Runable2.java index 3e03bbad..3913c7cf 100644 --- a/src/test/java/io/jboot/test/distributed/Runable2.java +++ b/src/test/java/io/jboot/test/distributed/Runable2.java @@ -1,17 +1,17 @@ -package io.jboot.test.distributed; - -import io.jboot.components.schedule.annotation.EnableDistributedRunnable; -import io.jboot.components.schedule.annotation.FixedDelay; - -@FixedDelay(period = 30) -@EnableDistributedRunnable(redisKey = "aaa",expireSeconds = 30) -public class Runable2 implements Runnable { - int i =0; - @Override - public void run() { - if (i++ >= 3){ - throw new IllegalStateException("messss..."); - } - System.err.println("Runable2.running.........."+this); - } -} +//package io.jboot.test.distributed; +// +//import io.jboot.components.schedule.annotation.EnableDistributedRunnable; +//import io.jboot.components.schedule.annotation.FixedDelay; +// +//@FixedDelay(period = 30) +//@EnableDistributedRunnable(redisKey = "aaa",expireSeconds = 30) +//public class Runable2 implements Runnable { +// int i =0; +// @Override +// public void run() { +// if (i++ >= 3){ +// throw new IllegalStateException("messss..."); +// } +// System.err.println("Runable2.running.........."+this); +// } +//} diff --git a/src/test/java/io/jboot/test/distributed/Runable3.java b/src/test/java/io/jboot/test/distributed/Runable3.java index 79499b82..0eea7a58 100644 --- a/src/test/java/io/jboot/test/distributed/Runable3.java +++ b/src/test/java/io/jboot/test/distributed/Runable3.java @@ -1,17 +1,17 @@ -package io.jboot.test.distributed; - -import io.jboot.components.schedule.annotation.EnableDistributedRunnable; -import io.jboot.components.schedule.annotation.FixedDelay; - -@FixedDelay(period = 30) -@EnableDistributedRunnable(redisKey = "aaa",expireSeconds = 30) -public class Runable3 implements Runnable { - int i =0; - @Override - public void run() { - if (i++ >= 3){ - throw new IllegalStateException("messss..."); - } - System.err.println("Runable3.running.........."+this); - } -} +//package io.jboot.test.distributed; +// +//import io.jboot.components.schedule.annotation.EnableDistributedRunnable; +//import io.jboot.components.schedule.annotation.FixedDelay; +// +//@FixedDelay(period = 30) +//@EnableDistributedRunnable(redisKey = "aaa",expireSeconds = 30) +//public class Runable3 implements Runnable { +// int i =0; +// @Override +// public void run() { +// if (i++ >= 3){ +// throw new IllegalStateException("messss..."); +// } +// System.err.println("Runable3.running.........."+this); +// } +//} diff --git a/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java b/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java index 8d67da1f..e71d6296 100644 --- a/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java +++ b/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java @@ -4,8 +4,6 @@ package io.jboot.test.mq.redis; import io.jboot.Jboot; import io.jboot.app.JbootApplication; -import java.util.UUID; - public class RedisMqSender { public static void main(String[] args) throws InterruptedException { @@ -17,28 +15,30 @@ public class RedisMqSender { JbootApplication.setBootArg("jboot.mq.type", "redis"); JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); -// JbootApplication.setBootArg("jboot.mq.type", "redis"); -// JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); + JbootApplication.setBootArg("jboot.mq.type", "redis"); + JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); - JbootApplication.setBootArg("jboot.mq.other1.type", "rocketmq"); - JbootApplication.setBootArg("jboot.mq.other1.channel", "channel1,channel2,myChannel"); - JbootApplication.setBootArg("jboot.mq.other1.typeName", "rktmq"); - JbootApplication.setBootArg("jboot.mq.rocket.rktmq.namesrvAddr", "127.0.0.1:9876"); +// JbootApplication.setBootArg("jboot.mq.other1.type", "rocketmq"); +// JbootApplication.setBootArg("jboot.mq.other1.channel", "channel1,channel2,myChannel"); +// JbootApplication.setBootArg("jboot.mq.other1.typeName", "rktmq"); +// JbootApplication.setBootArg("jboot.mq.rocket.rktmq.namesrvAddr", "127.0.0.1:9876"); //启动应用程序 JbootApplication.run(args); -// while (true) { + int count = 10; + for (int i = 0; i < count; i++) { + - Jboot.getMq().publish("message from RedisMqSender", "channel1"); - Jboot.getMq().publish("message from RedisMqSender", "channel2"); - Jboot.getMq().publish("message from RedisMqSender", "myChannel"); +// Jboot.getMq().publish("message from RedisMqSender", "channel1"); +// Jboot.getMq().publish("message from RedisMqSender", "channel2"); +// Jboot.getMq().publish("message from RedisMqSender", "myChannel"); - Jboot.getMq("other1").enqueue("message from RedisMqSender by enqueue : " + UUID.randomUUID(), "channel1"); + Jboot.getMq().enqueue("message " + i, "channel1"); - Thread.sleep(2000); +// Thread.sleep(2000); System.out.println("jboot mq publish success..."); -// } + } } -- Gitee From 69c0c83706b42debd7e3e5ddb9cdf773813dda65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 27 Apr 2022 10:43:55 +0800 Subject: [PATCH 1692/1965] v3.15.0 snapshot --- .../support/redis/jedis/JbootJedisImpl.java | 3 +++ .../java/io/jboot/utils/ClassScanner.java | 1 + .../io/jboot/test/ClassScannerTester.java | 23 +++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 src/test/java/io/jboot/test/ClassScannerTester.java diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java index 2ff9631d..6b58582a 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java @@ -1575,6 +1575,9 @@ public class JbootJedisImpl extends JbootRedisBase { } } + public JedisPool getJedisPool() { + return jedisPool; + } public void returnResource(Jedis jedis) { if (jedis != null) { diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 7b5de0b6..d2505f65 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -300,6 +300,7 @@ public class ClassScanner { excludeJars.add("mybatis-"); excludeJars.add("ip2region-"); excludeJars.add("java-uuid-generator-"); + excludeJars.add("quartz-"); } diff --git a/src/test/java/io/jboot/test/ClassScannerTester.java b/src/test/java/io/jboot/test/ClassScannerTester.java new file mode 100644 index 00000000..8d967cde --- /dev/null +++ b/src/test/java/io/jboot/test/ClassScannerTester.java @@ -0,0 +1,23 @@ +package io.jboot.test; + +import io.jboot.app.JbootApplication; +import io.jboot.core.listener.JbootAppListener; +import io.jboot.utils.ClassScanner; + +public class ClassScannerTester implements JbootAppListener { + + public static void main(String[] args) { + +// ClassScanner.addUnscanJarPrefix("encoder-"); +// ClassScanner.addUnscanJarPrefix("reflections-"); + +// JbootApplication.setBootArg("jboot.app.scanner.unScanJarPrefix","encoder-,reflections-"); + ClassScanner.setPrintScannerInfoEnable(true); + JbootApplication.run(args); + } + + @Override + public void onStart() { + + } +} -- Gitee From b1f580e93da1c4048c0cf822d314b36281775392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 27 Apr 2022 10:44:00 +0800 Subject: [PATCH 1693/1965] v3.15.0 snapshot --- src/main/java/io/jboot/core/JbootCoreConfig.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index e5bea8c3..cb87a6c6 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -108,9 +108,9 @@ public class JbootCoreConfig extends JFinalConfig { */ private void initSystemProperties() { //加载 jboot-system.properties 代替启动参数的 -D 配置 - File spf = new File(PathKit.getRootClassPath(), "jboot-system.properties"); - if (spf.exists() && spf.isFile()) { - Properties properties = PropKit.use(spf).getProperties(); + File systemPropFile = new File(PathKit.getRootClassPath(), "jboot-system.properties"); + if (systemPropFile.exists() && systemPropFile.isFile()) { + Properties properties = PropKit.use(systemPropFile).getProperties(); if (properties != null && !properties.isEmpty()) { for (Object key : properties.keySet()) { if (StrUtil.isNotBlank(key)) { -- Gitee From 378f47e8f5db34521b28f386b1172466942d3f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 27 Apr 2022 14:25:44 +0800 Subject: [PATCH 1694/1965] v3.15.0 snapshot --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 0402c42a..54060e97 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,9 @@ public class Helloworld extends JbootController { - 文档请访问:[www.jboot.com.cn](http://www.jboot.com.cn) - Demos 请访问:[这里](./src/test/java/io/jboot/test) +## 广告 + +- 一个好用的在线代码格式化工具:[http://www.CodeFormat.CN](http://www.codeformat.cn) ## 微信交流群 -- Gitee From 7ef24f52afabe3266164cb02bc5dd4431f700f7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 27 Apr 2022 17:17:05 +0800 Subject: [PATCH 1695/1965] add JbootController.getAttrs() method --- .../io/jboot/web/controller/JbootController.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 03365f16..105ac6aa 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -733,4 +733,20 @@ public class JbootController extends Controller { public BigDecimal getBigDecimal(String name, BigDecimal defaultValue) { return toBigDecimal(getTrimPara(name), defaultValue); } + + + /** + * 获取所有 attr 信息 + * + * @return attrs map + */ + @NotAction + public Map getAttrs() { + Map attrs = new HashMap<>(); + for (Enumeration names = getAttrNames(); names.hasMoreElements(); ) { + String attrName = names.nextElement(); + attrs.put(attrName, getAttr(attrName)); + } + return attrs; + } } -- Gitee From f37cbb676ad985f095c0a198a75cf612dd596c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 29 Apr 2022 10:55:59 +0800 Subject: [PATCH 1696/1965] add JbootController.renderToStringWithAttrs() method --- .../jboot/web/controller/JbootController.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 105ac6aa..2a023557 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -749,4 +749,26 @@ public class JbootController extends Controller { } return attrs; } + + + @NotAction + public String renderToStringWithAttrs(String template) { + return super.renderToString(template, getAttrs()); + } + + + @NotAction + public String renderToStringWithAttrs(String template, Map data) { + if (data == null) { + data = getAttrs(); + } else { + for (Enumeration names = getAttrNames(); names.hasMoreElements(); ) { + String attrName = names.nextElement(); + if (!data.containsKey(attrName)) { + data.put(attrName, getAttr(attrName)); + } + } + } + return super.renderToString(template, data); + } } -- Gitee From 95b9b0815b153e04e398f4f86b9354d8d1ef2df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 29 Apr 2022 18:47:02 +0800 Subject: [PATCH 1697/1965] v3.15.0 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 781743dd..30f0ed40 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.14.9 + 3.15.0 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 5ca2bf48..6f802afc 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.14.9 + 3.15.0 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 70c28692..8c9b77c3 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.14.9 + 3.15.0 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.14.9', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.15.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index a8780729..7bcea96c 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.14.9', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.15.0', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.14.9/jboot-3.14.9.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.0/jboot-3.15.0.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 7e45116a..5f3183bc 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.14.9 + 3.15.0 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 495e74eb..df61e3e8 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.14.9"; + public static String VERSION = "3.15.0"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From b714c0c3c6f15bc9a46cbf3a1b52ddbe4a8ddd56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 29 Apr 2022 18:47:10 +0800 Subject: [PATCH 1698/1965] v3.15.0 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 10 ++++++++++ pom.xml | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 54060e97..ccb75cb6 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.14.9 + 3.15.0 ``` diff --git a/changes.txt b/changes.txt index 0bb2d5d1..d32996a1 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,13 @@ +jboot v3.15.0: +优化:ValidErrorRender 使用 Ret.fail() 来创建,方便统一定义 message 字段 +优化:InterceptorBuilderManager 默认对所有 Interceptor 进行注入 +优化:Controller 新增 getAttrs() 和 renderToStringWithAttrs() 方法 +修复:Controller 接收 int[] 时,使用 @Size 等注解验证时出错的问题 +修复:Model.dao() 查询在某些情况下出错的问题 +修复:注解 @Size(max=xxx) 拦截错误的问题 + + + jboot v3.14.9: 修复:在某些极端场景下,StrUtil.escapeHtml 无法通过 unEscapeHtml 还原的问题 优化:删除某些无用的方法和类 diff --git a/pom.xml b/pom.xml index 9cb1ac6d..db3f721c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.0-SNAPSHOT + 3.15.0 jar jboot -- Gitee From 74673d4295174e9418ca5b2835ef58510579a293 Mon Sep 17 00:00:00 2001 From: xujianxie Date: Mon, 9 May 2022 11:07:00 +0000 Subject: [PATCH 1699/1965] =?UTF-8?q?update=20src/main/java/io/jboot/codeg?= =?UTF-8?q?en/CodeGenHelpler.java.=20=20=20=20=20=20=20=20=20=20=20=20=20c?= =?UTF-8?q?ase=20DataSourceConfig.TYPE=5FINFORMIX:=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20metaBuilder.setDialect(new=20Informi?= =?UTF-8?q?xDialect());=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?break;=20=E5=A2=9E=E5=8A=A0=E4=BA=86informix=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/codegen/CodeGenHelpler.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/io/jboot/codegen/CodeGenHelpler.java b/src/main/java/io/jboot/codegen/CodeGenHelpler.java index 98ab5b88..563c9eb5 100644 --- a/src/main/java/io/jboot/codegen/CodeGenHelpler.java +++ b/src/main/java/io/jboot/codegen/CodeGenHelpler.java @@ -99,6 +99,9 @@ public class CodeGenHelpler { case DataSourceConfig.TYPE_POSTGRESQL: metaBuilder.setDialect(new PostgreSqlDialect()); break; + case DataSourceConfig.TYPE_INFORMIX: + metaBuilder.setDialect(new InformixDialect()); + break; default: throw new JbootIllegalConfigException("only support datasource type : mysql、orcale、sqlserver、sqlite、ansisql and postgresql, please check your jboot.properties. "); } -- Gitee From 12aaaca5b70b49154ecd55918b15a89fa7d49a58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 9 May 2022 20:19:24 +0800 Subject: [PATCH 1700/1965] v3.15.1 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index db3f721c..b1751861 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.0 + 3.15.1-SNAPSHOT jar jboot -- Gitee From 015df628ef66cd7ecada2492a790bc388c7474dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 10 May 2022 09:49:25 +0800 Subject: [PATCH 1701/1965] upgrade jfinal to v5.0.0 --- pom.xml | 4 ++-- .../io/jboot/web/render/JbootCaptchaRender.java | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index b1751861..179e8594 100644 --- a/pom.xml +++ b/pom.xml @@ -41,9 +41,9 @@ - 4.9.23 + 5.0.0 2022.2 - 2.9 + 3.0 3.4 3.3.0 4.0.3 diff --git a/src/main/java/io/jboot/web/render/JbootCaptchaRender.java b/src/main/java/io/jboot/web/render/JbootCaptchaRender.java index ff1771d0..91c9be3a 100644 --- a/src/main/java/io/jboot/web/render/JbootCaptchaRender.java +++ b/src/main/java/io/jboot/web/render/JbootCaptchaRender.java @@ -2,9 +2,11 @@ package io.jboot.web.render; import com.jfinal.captcha.CaptchaRender; import com.jfinal.kit.StrKit; + import java.awt.*; import java.awt.geom.QuadCurve2D; import java.awt.image.BufferedImage; +import java.util.concurrent.ThreadLocalRandom; public class JbootCaptchaRender extends CaptchaRender { @@ -27,11 +29,12 @@ public class JbootCaptchaRender extends CaptchaRender { protected String getRandomString() { char[] randomChars = new char[4]; for (int i = 0; i < randomChars.length; i++) { - randomChars[i] = charArray[random.nextInt(charArray.length)]; + randomChars[i] = charArray[ThreadLocalRandom.current().nextInt(charArray.length)]; } return String.valueOf(randomChars); } + @Override protected void drawGraphic(String randomString, BufferedImage image) { // 获取图形上下文 @@ -43,14 +46,16 @@ public class JbootCaptchaRender extends CaptchaRender { // 字体抗锯齿 g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + ThreadLocalRandom random = ThreadLocalRandom.current(); + // 设定背景色 - g.setColor(getRandColor(210, 250)); + g.setColor(getRandomColor(210, 250, random)); g.fillRect(0, 0, WIDTH, HEIGHT); //绘制小字符背景 Color color = null; for (int i = 0; i < 20; i++) { - color = getRandColor(120, 200); + color = getRandomColor(120, 200, random); g.setColor(color); String rand = String.valueOf(charArray[random.nextInt(charArray.length)]); g.drawString(rand, random.nextInt(WIDTH), random.nextInt(HEIGHT)); @@ -71,7 +76,7 @@ public class JbootCaptchaRender extends CaptchaRender { //旋转区域 g.rotate(Math.toRadians(degree), x, y); //设定字体颜色 - color = getRandColor(20, 130); + color = getRandomColor(20, 130, random); g.setColor(color); //将认证码显示到图象中 g.drawString(String.valueOf(randomString.charAt(i)), x + 8, y + 10); -- Gitee From 1e248e57213dbad6d853d5e4def0450e7c7522b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 10 May 2022 09:52:10 +0800 Subject: [PATCH 1702/1965] codegen add infomix datasource type --- src/main/java/io/jboot/codegen/CodeGenHelpler.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/codegen/CodeGenHelpler.java b/src/main/java/io/jboot/codegen/CodeGenHelpler.java index 563c9eb5..baeb9f4a 100644 --- a/src/main/java/io/jboot/codegen/CodeGenHelpler.java +++ b/src/main/java/io/jboot/codegen/CodeGenHelpler.java @@ -35,12 +35,12 @@ import java.util.Set; */ public class CodeGenHelpler { + public static String getUserDir() { return System.getProperty("user.dir"); } - /** * 获取数据源 * @@ -101,9 +101,10 @@ public class CodeGenHelpler { break; case DataSourceConfig.TYPE_INFORMIX: metaBuilder.setDialect(new InformixDialect()); - break; + break; default: - throw new JbootIllegalConfigException("only support datasource type : mysql、orcale、sqlserver、sqlite、ansisql and postgresql, please check your jboot.properties. "); + throw new JbootIllegalConfigException("Only support datasource type : mysql、orcale、sqlserver、sqlite、ansisql、postgresql and infomix" + + ", please check your jboot.properties. "); } return metaBuilder; -- Gitee From dd82463e2b2842e2355b76b2420087374f9846d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 16 May 2022 11:30:51 +0800 Subject: [PATCH 1703/1965] add: JbootOutputDirectiveFactory to ignore template render exception in production mode. --- .../java/io/jboot/core/JbootCoreConfig.java | 3 + .../JbootOutputDirectiveFactory.java | 60 +++++++++++++++++++ .../directive/base/JbootDirectiveBase.java | 28 +-------- 3 files changed, 64 insertions(+), 27 deletions(-) create mode 100644 src/main/java/io/jboot/web/directive/JbootOutputDirectiveFactory.java diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index cb87a6c6..b8a9b55b 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -64,6 +64,7 @@ import io.jboot.web.controller.JbootControllerManager; import io.jboot.web.controller.annotation.GetMapping; import io.jboot.web.controller.annotation.PostMapping; import io.jboot.web.controller.annotation.RequestMapping; +import io.jboot.web.directive.JbootOutputDirectiveFactory; import io.jboot.web.directive.SharedEnumObject; import io.jboot.web.directive.annotation.*; import io.jboot.web.handler.JbootActionHandler; @@ -258,6 +259,8 @@ public class JbootCoreConfig extends JFinalConfig { @Override public void configEngine(Engine engine) { + engine.setOutputDirectiveFactory(JbootOutputDirectiveFactory.me); + //通过 java -jar xxx.jar 在单独的jar里运行 if (ApplicationUtil.runInFatjar()) { engine.setToClassPathSourceFactory(); diff --git a/src/main/java/io/jboot/web/directive/JbootOutputDirectiveFactory.java b/src/main/java/io/jboot/web/directive/JbootOutputDirectiveFactory.java new file mode 100644 index 00000000..47922881 --- /dev/null +++ b/src/main/java/io/jboot/web/directive/JbootOutputDirectiveFactory.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.directive; + +import com.jfinal.kit.LogKit; +import com.jfinal.template.Env; +import com.jfinal.template.expr.ast.ExprList; +import com.jfinal.template.io.Writer; +import com.jfinal.template.stat.Location; +import com.jfinal.template.stat.OutputDirectiveFactory; +import com.jfinal.template.stat.Scope; +import com.jfinal.template.stat.ast.Output; +import io.jboot.Jboot; + +public class JbootOutputDirectiveFactory extends OutputDirectiveFactory { + + public static final JbootOutputDirectiveFactory me = new JbootOutputDirectiveFactory(); + + private boolean ignoreTemplateException = !Jboot.isDevMode(); + + + @Override + public Output getOutputDirective(ExprList exprList, Location location) { + return new JbootOutput(exprList, location); + } + + + public static class JbootOutput extends Output { + + public JbootOutput(ExprList exprList, Location location) { + super(exprList, location); + } + + @Override + public void exec(Env env, Scope scope, Writer writer) { + try { + super.exec(env, scope, writer); + } catch (Exception e) { + if (me.ignoreTemplateException) { + LogKit.error(e.toString(), e); + } else { + throw e; + } + } + } + } +} diff --git a/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java b/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java index fdff7e77..9d66ea2c 100644 --- a/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java +++ b/src/main/java/io/jboot/web/directive/base/JbootDirectiveBase.java @@ -16,14 +16,12 @@ package io.jboot.web.directive.base; import com.jfinal.aop.Aop; -import com.jfinal.log.Log; import com.jfinal.template.Directive; import com.jfinal.template.Env; import com.jfinal.template.TemplateException; import com.jfinal.template.expr.ast.ExprList; import com.jfinal.template.io.Writer; import com.jfinal.template.stat.Scope; -import io.jboot.Jboot; import io.jboot.utils.StrUtil; import java.io.IOException; @@ -36,18 +34,6 @@ import java.util.Map; */ public abstract class JbootDirectiveBase extends Directive { - private static final Log LOG = Log.getLog(JbootDirectiveBase.class); - - private static boolean devMode = Jboot.isDevMode(); - - public static boolean isDevMode() { - return devMode; - } - - public static void setDevMode(boolean devMode) { - JbootDirectiveBase.devMode = devMode; - } - public JbootDirectiveBase() { Aop.inject(this); } @@ -64,19 +50,7 @@ public abstract class JbootDirectiveBase extends Directive { scope = new Scope(scope); scope.getCtrl().setLocalAssignment(); exprList.eval(scope); - - try { - onRender(env, scope, writer); - } catch (Throwable e) { - if (devMode) { - throw e; - } - // 生产环境下,忽略指令错误渲染 - else { - LOG.error("Template Directive render error!!!", e); - } - } - + onRender(env, scope, writer); } -- Gitee From 90454d5cb21ab8d34e4bbde4fa87f0ba20c0ca99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 16 May 2022 11:57:50 +0800 Subject: [PATCH 1704/1965] v3.15.1 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- .../web/directive/JbootOutputDirectiveFactory.java | 10 ++++++++++ 7 files changed, 18 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 30f0ed40..9759243c 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.15.0 + 3.15.1 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 6f802afc..1512ea5c 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.15.0 + 3.15.1 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 8c9b77c3..929718bc 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.15.0 + 3.15.1 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.15.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.15.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 7bcea96c..3c2749db 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.15.0', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.15.1', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.0/jboot-3.15.0.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.1/jboot-3.15.1.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 5f3183bc..423a23ad 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.15.0 + 3.15.1 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index df61e3e8..b2934d7e 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.15.0"; + public static String VERSION = "3.15.1"; public static final String ATTR_REQUEST = "REQUEST"; diff --git a/src/main/java/io/jboot/web/directive/JbootOutputDirectiveFactory.java b/src/main/java/io/jboot/web/directive/JbootOutputDirectiveFactory.java index 47922881..4b3eeb54 100644 --- a/src/main/java/io/jboot/web/directive/JbootOutputDirectiveFactory.java +++ b/src/main/java/io/jboot/web/directive/JbootOutputDirectiveFactory.java @@ -25,12 +25,22 @@ import com.jfinal.template.stat.Scope; import com.jfinal.template.stat.ast.Output; import io.jboot.Jboot; +/** + * 主要作用:在生产环境下,忽略模板引擎的错误输出。 + */ public class JbootOutputDirectiveFactory extends OutputDirectiveFactory { public static final JbootOutputDirectiveFactory me = new JbootOutputDirectiveFactory(); private boolean ignoreTemplateException = !Jboot.isDevMode(); + public boolean isIgnoreTemplateException() { + return ignoreTemplateException; + } + + public void setIgnoreTemplateException(boolean ignoreTemplateException) { + this.ignoreTemplateException = ignoreTemplateException; + } @Override public Output getOutputDirective(ExprList exprList, Location location) { -- Gitee From 269208677cbb74667881c8ba154540c75a7188d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 16 May 2022 11:57:55 +0800 Subject: [PATCH 1705/1965] v3.15.1 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ pom.xml | 8 ++++---- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ccb75cb6..8fedb123 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.15.0 + 3.15.1 ``` diff --git a/changes.txt b/changes.txt index d32996a1..71c8d612 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.15.1: +新增:JbootOutputDirectiveFactory,用于忽略在生产环境下,模板引擎执行的错误输出 +新增:CodeGenHelpler 新增对 infomix 数据库的支持,感谢 @xujianxie +优化:升级 JFinal 到 v5.0 最新版本,其他相关依赖也升级到新版本 + + + jboot v3.15.0: 优化:ValidErrorRender 使用 Ret.fail() 来创建,方便统一定义 message 字段 优化:InterceptorBuilderManager 默认对所有 Interceptor 进行注入 diff --git a/pom.xml b/pom.xml index 179e8594..c52a6d02 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.1-SNAPSHOT + 3.15.1 jar jboot @@ -47,7 +47,7 @@ 3.4 3.3.0 4.0.3 - 2.13.2 + 2.13.3 5.1.49 2.2.17.Final 1.7.36 @@ -57,7 +57,7 @@ 31.1-jre 2.2.5 3.8.0 - 1.14.3 + 1.15.1 2.10.9.2 2.9.3 3.12.0 @@ -200,7 +200,7 @@ org.javassist javassist - 3.28.0-GA + 3.29.0-GA -- Gitee From 764618e38b7af3160325619efed59d8c2a8f4e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 16 May 2022 12:04:04 +0800 Subject: [PATCH 1706/1965] v3.15.1 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c52a6d02..b30db711 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ 31.1-jre 2.2.5 3.8.0 - 1.15.1 + 1.14.3 2.10.9.2 2.9.3 3.12.0 -- Gitee From 45698aca9084c7e2e2daa5ccd8d95dad705cc8b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 17 May 2022 11:43:52 +0800 Subject: [PATCH 1707/1965] v3.15.2 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b30db711..7e5df56b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.1 + 3.15.2-SNAPSHOT jar jboot -- Gitee From a116f948e56eeae2863cf161722db7268a37a19a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 17 May 2022 11:44:29 +0800 Subject: [PATCH 1708/1965] optimize Columns.tostring() --- src/main/java/io/jboot/db/model/Columns.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 39e22bce..13cca20c 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -110,8 +110,6 @@ public class Columns implements Serializable { } - - /** * add new column in Columns * @@ -160,7 +158,6 @@ public class Columns implements Serializable { } - /** * equals * @@ -818,7 +815,8 @@ public class Columns implements Serializable { @Override public String toString() { - return getCacheKey(); + String cacheKey = getCacheKey(); + return StrUtil.isNotBlank(cacheKey) ? cacheKey : "{}"; } -- Gitee From f005264e7876cfcbb34ade71e5152ba04783da5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 17 May 2022 11:58:29 +0800 Subject: [PATCH 1709/1965] =?UTF-8?q?fixed=20=E4=BF=AE=E5=A4=8D=20DAO=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=BA=86=20loadColumns=20=E6=97=B6=E5=8F=88?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=BA=86=20distinct=20=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E8=8E=B7=E5=BE=97=E6=AD=A3=E7=A1=AE=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/db/model/JbootModel.java | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 0a1cef3f..5230ea34 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -746,15 +746,31 @@ public class JbootModel> extends Model { } public List findListByColumns(Columns columns, String orderBy, Integer count, String loadColumns) { + loadColumns = getLoadColumns(loadColumns); + String sql = _getDialect().forFindByColumns(alias, joins, _getTableName(), loadColumns, columns.getList(), orderBy, count); + return columns.isEmpty() ? find(sql) : find(sql, columns.getValueArray()); + } + + + private String getLoadColumns(String loadColumns) { if (StrUtil.isBlank(loadColumns) && StrUtil.isNotBlank(this.loadColumns)) { loadColumns = this.loadColumns; } - //使用 distinct - if (StrUtil.isBlank(loadColumns) && hasAnyJoinEffective()) { + //使用 join 的情况下,需要判断 distinct + if (hasAnyJoinEffective()) { String distinctColumn = JbootModelExts.getDistinctColumn(this); + + //用户配置了 distinct if (StrUtil.isNotBlank(distinctColumn)) { - loadColumns = "DISTINCT " + distinctColumn + "," + (StrUtil.isNotBlank(alias) ? alias : _getTableName()) + ".*"; + if (StrUtil.isBlank(loadColumns)) { + loadColumns = (StrUtil.isNotBlank(alias) ? alias : _getTableName()) + ".*"; + } + + //用户配置的 loadColumns 未包含 distinct 关键字 + if (!loadColumns.toLowerCase().contains("distinct ")) { + loadColumns = "DISTINCT " + distinctColumn + "," + loadColumns; + } } } @@ -762,9 +778,7 @@ public class JbootModel> extends Model { loadColumns = "*"; } - - String sql = _getDialect().forFindByColumns(alias, joins, _getTableName(), loadColumns, columns.getList(), orderBy, count); - return columns.isEmpty() ? find(sql) : find(sql, columns.getValueArray()); + return loadColumns; } @@ -823,21 +837,7 @@ public class JbootModel> extends Model { } public Page paginateByColumns(int pageNumber, int pageSize, Columns columns, String orderBy, String loadColumns) { - if (StrUtil.isBlank(loadColumns) && StrUtil.isNotBlank(this.loadColumns)) { - loadColumns = this.loadColumns; - } - - //使用 distinct - if (StrUtil.isBlank(loadColumns) && hasAnyJoinEffective()) { - String distinctColumn = JbootModelExts.getDistinctColumn(this); - if (StrUtil.isNotBlank(distinctColumn)) { - loadColumns = "DISTINCT " + distinctColumn + "," + (StrUtil.isNotBlank(alias) ? alias : _getTableName()) + ".*"; - } - } - - if (StrUtil.isBlank(loadColumns)) { - loadColumns = "*"; - } + loadColumns = getLoadColumns(loadColumns); String selectPartSql = _getDialect().forPaginateSelect(loadColumns); -- Gitee From f3b045cae4584861a618e8e9b44f96d7115c4929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 17 May 2022 12:11:56 +0800 Subject: [PATCH 1710/1965] =?UTF-8?q?fixed=20=E4=BF=AE=E5=A4=8D=20DAO=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=BA=86=20loadColumns=20=E6=97=B6=E5=8F=88?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=BA=86=20distinct=20=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E8=8E=B7=E5=BE=97=E6=AD=A3=E7=A1=AE=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/JbootModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 5230ea34..4e75fe79 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -769,7 +769,7 @@ public class JbootModel> extends Model { //用户配置的 loadColumns 未包含 distinct 关键字 if (!loadColumns.toLowerCase().contains("distinct ")) { - loadColumns = "DISTINCT " + distinctColumn + "," + loadColumns; + loadColumns = "DISTINCT " + distinctColumn + ", " + loadColumns; } } } -- Gitee From 014c9c621ce2e3a166661fe1a163d4377d9d0843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 19 May 2022 16:43:39 +0800 Subject: [PATCH 1711/1965] v3.15.2 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 9759243c..a7a6e140 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.15.1 + 3.15.2 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 1512ea5c..f4379546 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.15.1 + 3.15.2 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 929718bc..4c031772 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.15.1 + 3.15.2 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.15.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.15.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 3c2749db..cb0d2f8b 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.15.1', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.15.2', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.1/jboot-3.15.1.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.2/jboot-3.15.2.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 423a23ad..d64b3923 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.15.1 + 3.15.2 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index b2934d7e..8daff9fc 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.15.1"; + public static String VERSION = "3.15.2"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From d4987f06860c91ea877e762448589f9fed9fb275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 19 May 2022 16:44:06 +0800 Subject: [PATCH 1712/1965] v3.15.2 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ pom.xml | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8fedb123..b97a876b 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.15.1 + 3.15.2 ``` diff --git a/changes.txt b/changes.txt index 71c8d612..f456812c 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.15.2: +修复:DAO 配置了 loadColumns 时又配置了 distinct 时,无法获得正确结果的问题 +优化:优化 Columns.toString() 方法,防止在开发工具 debug 下显示 "null" 的问题 + + + jboot v3.15.1: 新增:JbootOutputDirectiveFactory,用于忽略在生产环境下,模板引擎执行的错误输出 新增:CodeGenHelpler 新增对 infomix 数据库的支持,感谢 @xujianxie diff --git a/pom.xml b/pom.xml index 7e5df56b..715bbc72 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.2-SNAPSHOT + 3.15.2 jar jboot @@ -157,7 +157,7 @@ com.fasterxml.jackson.core jackson-databind - 2.13.2.2 + ${jackson.version} -- Gitee From 197519de64d3b47974c7b1bf65732b1c9e960794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 30 May 2022 19:32:43 +0800 Subject: [PATCH 1713/1965] update readme --- README.md | 5 +++++ doc/docs/static/images/donate.png | Bin 0 -> 199292 bytes 2 files changed, 5 insertions(+) create mode 100644 doc/docs/static/images/donate.png diff --git a/README.md b/README.md index b97a876b..3f96cefa 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,11 @@ public class Helloworld extends JbootController { ![](./doc/docs/static/images/jboot-wechat-group.png) + +## 赞助 + +![](./doc/docs/static/images/donate.png) + ## JbootAdmin JbootAdmin 是 Jboot 官方推出的、收费的、企业级快速开发框架,真诚的为各位开发者提供一站式、保姆式的开发服务。 diff --git a/doc/docs/static/images/donate.png b/doc/docs/static/images/donate.png new file mode 100644 index 0000000000000000000000000000000000000000..c25e9e6ac97f12c17a084ce34ad5f40eaa703883 GIT binary patch literal 199292 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 AnsB!sk*lBZS7C$$rF-m?~*8Rr)-9*G{ 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

        eaWM1>Y7xvB`6lEZ3lj1zx zNvcSmH6hG@Sd6%J=Q2#`8u(wHV;ORf=eX!=<$+r1PG1jE@8pu-XZ+_<2A0YRU25Gx z?4}7#j#ft-{U6;puy~yK4In3+0&$~`00tL;J9Fjv-naYq_VX;hYm%S&&AZUd+Oj|;Tn>D;Qcf(5m#cyNjYDRIuOfb)B~ zH^@77=1omEc!%ZF@}5N(<{l|Qgx{}mbC%-L*|{=Rxr<+HvCFAub;sJfLE5HRMT!%C z&o&a@&2M@ALmaw&$vHew`;n6^k+sc~H(|F=#Vs=(WFD9(*OFZdJ<%|FQRy(;V8@YZ zMiyV%YR{TaSXiyxu-!pj-%ipGR>Q|+A+B2-EUq7=>@%7I(6;7oE^wK>hi=l1Ms_>GD_3yKbeIrY~eO?Ixw z0$AovSNP%lJlg9}G+$^%D5lZtTbw1LwWP*Jf6XV|(cqPpAM9nEa+;32QtjkjBIg+c zTRQQr>KE097=F{QL;+U^L6Su)HLj$8_pi?d)Z*~*^HU56LdiF`VD0CinR9M8eSG`p zNf@F}g&&tkOlR?n`t@h);3JIso)mUJne}Km-dQ2m`NJlQbAlWv*z+$&;0r zaK_Wq--kDike)ba@o603X%%$hTt=U0cDXc9%=e7vPNUrF?T42SP}gJw`Q~_G63gfx zzktB9d3Oi6ysyyjx=o(aEf;>5_|rzVWMuIx$L>&~Xp7{#%orO4D$FOD%P2Ra$6QN# zkkEyTlE2<_LOsX+7%LCH0=Y5j(i>Ni><>IuT+f(CY_qYp%MF0gpJQOR_PWzxca=#1 zr+ne1^BT%UpPz0ysTo!n?Oxv5-&W`!xJlXTXPcZbM1kYamzN;l0$DVnO^6YCf$@mnjk<4CC z5ptApIvj1U9So)RjbGGr%D2s6d9KIF^}Rrp$0RM_q$=;l?~-r#r)V>@{QNHDTYXtd3PIG9^7XQBf?=LJDf) zxy|G@__!b?d#8|gXrct#chg_Mp>VuXif8-s(Ytn1He$u~cgE#M?Z&tyfs4!n2pN9& zH|WlmdRHj`R|M`hqz2?G^K{0el0wskQkO`=`ulSZF`{UqSsN*Q%bj7V@*wRhBMRd{ z?0Wal?fZa1_BLt-FoeFu{JZ1UJ-Sh%8+BCq%HhMHeL=T__HNvJ(}y#lpxfW{&w%dj zzr7!jtOe2Jwjcz?Q)NZP`CrYAa7bbH%c~=yL@C0zFU_!me61rw&jB^!XVB+;fHmM? zdVkf(X#+*2#v>(?p3x(4VF_RuTV=~lZpCJPZ?F`M3(zrKs&l|bU{ZKxvOdHLB}isP zSBH!k$Es$GeC>-+f|b{HLK;rWhWskYqb2MJIcoRh4LIh6*DU*6ywXi1sK0BxZXPyE zUwzC{oX-Xq>M2h67Hp_uZ2Nr4*IG=kPwJiZE&I^%1m`huHq`h@ktsf=1jDj5z>yF) z%z33t7CM;Bk>ast;~Y+ZUXPSBwD+~Mq!lTGa$DF+F!xVAHxnJz2L=*Lj%x6w$2u>o z#taPk^1}2Q{7mkZBuAc`^k@|Vaozp+=VRSZgETliE-_)d=|;v-IZEZhRrW|mw3Bt> za$?a134>{r@kmI_63jDAT9Y6wlxIoe^hm|!3JCIG&U+SS)>Fn7KbXhZ(3TQ&4qZ!t zrA01DFJulXqvDTbe=0GhNw}yq`bnv)a*}Jbyzlz%wq*=mN~0App$U!A@~^d!4=)h3 zj|FWty1iboM9>>*-_}$}W@em@8%r=A^lK=SzkF!XuifCH0U7S{?3I;ZvQg}ZNE5xB zB&G76ch&?XWFmU9FZ4rr$^({I1IjYWz}75W*+EO^_>b|dPihM%z-R8dMf>|}%Eir) zj;fAEiIb3e=%Xbz@z!>x@&#RC7WZ`$NfoE%x9^BEnSK4b)yc~#9|YACQ~9tAoTHPQ z9iY=qcv+q^HUlqP@Q_;8R?V5mrBIRx;<}|p)3)Vm8`(Sdx^8mU)ex@*Y;Zn}FVL|%Ss3B@7bi<`oNCfyr-r#YN^^kRY@HCFCepZi~E-iBv`jmam})Y6iB znx9aeIt$QC9)i*nMc&I>&&O>Fcni1w1{FxI?5z0;zajtzcSw=AUZ~URqGDw`^~i>0 zX6qZ^@%1SZJjI=DRuKM5C^LWpVW1U(RAe6>I~F~792_zc<5;Z(B#_JZ%Ti$G9TtqK zEI7Q*Lp8d9_M-qU&8!W{T4#8f)m^*5dpIgn-?1vlQD*J*D7T`1ltuQukvp_==q}6e z0_4|Z`glra{mvz1%InpmlzH{8%4>?DPFA`FX(TgdCjpK@hJA@>ESV?z`jQ4TSQ9|p zcrq$6KSFq@ox+vq7{L3xxwd&WNo_xi;B5iTC-Dy`+JM|&ozKalou{IO;}Iu~=c*Ke zddt&oCf5((LBVT5+tBkEi~KwpsX@$lU#mD~LdHls7y)r(VO~*nCvC0prk5347rQ4o zQcz@Nc^g#BA48MdgVxpWc0r_%#*2>1`2s7G*6X<>t66`r*VbhYUw)zk!9-dCk!sJgkaeAjLi5O%!t!u~_|a7x}6l2@=6G!kpVghpy!xFlwO1v^-;OK>YNEFc5iSXYr^e5XVU{D6hQftkFU*XT+PeS{F-qvVHTsMYT3qdB=)!Na>ViJt8p;oUr zneK;K<-gfD6WyIHVcrmuHmv970aJsm?5`bQ8Wpq&8}?t3uK$_2 ziCfq1hZXv{HJJzU)sf)&JTZ4tLWhBlKBTMlnKPIufe2(SW9VC0wW4 z>f;V?+h#3sIJb#QnImm?uY2eLJqW|w`O3W+Ri0?alF;F6wz*=O&j8PwClP9(V9Q3# zs4%Oqi-6J6CtG$UDP=>!Iw$z}V|sr&9h?_bd54@r`3WYD_0RBez~J;87eRj|+BH|C z`%=mX$0?t+;MD2Z0yE`Usmun~^n~b;M*H_kaVbTs*-xMJOJ0PdLL%1hcLhEdcxO-S zGbTww&rOls28`>o_a1tOx!Th>Y!Owo>PDOKT;-kuGyB(p9bO+ih_Xe zE4;$aA*KNqH*!_|B0Lu`CVu9p4#wJGjR-(Z+0eU|jgJp%rR)e!YzG!cD6nJACZ+(g z|A~rbr&zcfY5`)Z6~%NN)N5Tk;9YP+L|WeZj(q*R;G-zmu@7*d&`4CZ_muv%GQEDE z!qcEmtbSQn7}&;jSE%#p1VtJR37#cMAW$)0daWW({|clIz_NY+$fJNlOr%F0VEgr0 z^83`7vG3q}pG5lYKY}Q+e})L4Bm0j624CRLyZ<>z?Djs;5kNV$w*gc0e=C5bn@9Ducl#AVeMX=RC)S1T#)T(D$#FinRZ zFwpG6_%j&Nap9QgfEW%T--@IK|H}5dHb*tvDJ#KUI~RwJa0v|Y(AyeonNtGxKrG%& z;9}!eI;PI%fA_xufZo!QqRLhE;Di%zdgl}j+U+fu-$0OtF?xgO*B2%{XwIz;G>ht7 z`@8teu6eJ7Ux%Go0HiGU4`y&Pe^AE1#tN{Ws`9Tqyu>CLqx;c*gGO_v9SW-4CDCo%ZGpP&ZU)*?NcoVbmQhspEX(8pToY%g?o($ zHa!2O0C8xUVhRvyC_RPIwl3J5;0ye#qjI#dCXx%9Xhnim9{&z z^m5Qe`pIA)qp&z)RU-6^MP`RjQ(Fb(chia2!T+R6g{{;ttfd%sEX+&IIeUrek|f+m zX~|cU&!|WTcY;?fz*KZ!k}!au*SR?SisJ#&tj~U;@{WmSphF7o+hS<0hv5pKBGoBb z|DK6mR?VAX8kBANz#~!$a2W|tDRW>2ablu#21mim;2x(yWT%HRBNxqnAFfJxRh(3g zRn#SktstHJpBr-hMJhBMbA(G)9Hq`8b@u-4l!oVPAPgTzuA?$_r&$H?;90|=l1Ht_ z=DrEY7GCCb0T#NTY9E)9z5rq)cIcKazmDsIW93TfPD|+16d+K>a`R2>v=4JPnm42Y z=q@(Cuiai=U2&90&zSTNm;(maufnwA7sP`((gI&?zHSMd@%cm-GyydU7wpQD6FR_Z zi`(F4JDdM7z``wGF-mWtv(fw_vf;7daQK9|TQrB0`Z3E*GMytL=TD?UKai2l@iL*$ z*M`#Z%9^iCOY*4m=bLKua$p_<2(jwitX^>kRyTH>O=M72$_o=cA5|eu7b9k`3ld!x_VJekO?;7&cc%@IuF?RXM!A;z* z_(PCkUK($yf6~>pDoLj@NeD2~xAmp=p_x{TP5b4~YR`GgaitXB&!*-BFdxc&JJj4r z^UXoD0u9b*Vq!4Ggvf(=e)3u5Zb)bPUs%@;F%z1R3a!eMemH!7>}wPm7jf?Q<;QSn zi1=d5am7yDpxbD&rQqvzGuBm)EQDgTbkxw9lkFj=Acx-{e1BUg2r8mdN&T5cQ8*5W z+oQ!ElsjC0Oq8l7t7%Dp&CH8-Inoej?Q&vz8=MZ;h6W8q1_KCAI|I>j(eYF>Z}=4pp+yZx+^L9=uE{QRuiLf@?+7zL(~7=6l4u`XAMbcaY!T}^ z@fXw;hMiyA=lV>4F}2qCR~Uc@&4Lranl-LTjh{P)LRz3Pf1wbrSoD|XNrG9zV##>g z&9jDpEMt&7b$*syB-_+7>R{QKsutrQh(Gq_VgEjAr_;t)C3Z z+QEcebtPjMHF2(68bVFTN|HBq7boe*VYOrYo;D>Z(Ft2kQXYc54Pmp=7LaWZud?5S zjawY@{V|o#t6ZWb*(DA_^O3VH(sZmGt6@r2~*{)Dib_1EJVK! zH?{UZ~* zQL1~1`MP8J=1nTvOhAY}<~>-DLRKidT%<$y zvS>PM>OT>1-3K%)<2=>Vj~JQojY*v)^f(IiOu=uh_3VTtQ8%t*CvqX9pDoL9Brj78 zHY}qr&}5pwX=gN-z0ekUqk7gpV_j}I&8U^DyHevoCPV(1(@o|8FsYm-hT*7%%9n7^ zRogP~@gMWSb#{{x>X&q+(o*0$FV|pnj&cp?GgRccE~IdRVQE)9_w0qw<*|LwCvT3z zX`OQ3y7}oWeFOb3rPo?`J05$`y!45Z;$pQ9MX`N86Go1Ll2lN}SA_V={m!8|kys)V zI>;K=BMV@j^x1mi>pfQ$s247d{;F%^@SUmC*nApLYo1EQ$=5tV61;d2E}oKHp{h<9 zC3uCq>#Wvk{PckVo$GxTA3GO3`pKcb+RA!yHc~mF8RMR*z}#O3qWUOJ=D>Pz0O+XDV?dcM7MbIGGMHy<&? z+l%Yrnkc}?FoUbaq?P=cMp!u}J(GLqk=OmKo(S;qW)8 zux{4=gQ24VQMkUoKo`Vs<(BfwYV+wh$|XuUXA;JzS@(w3Baq?&WdGT)1x}mw6DMVv z^SA}mvOw!Ct|M6~FmoiE-Dr8l?#S99t9 z<7p7uuVaSWCvjcWr)muKIyFPzj9e}^A7E?!ko}#p_Q(ob*b64a5XPo&lpy1 zw65D=RI5~g-62V{kR?~V2H`$2tX3LN>ftqzqHY?L5+%-U9hGK+s0$ zx6-z#h^43i`reZ!(GSK|KBGv!xf+yoVM|=<#RWaHMkT)+p-C-s&SGe)uwgJ1GlgqP zqaUD4c6dC|&q!7n8BLK;U0uIf;t9Fpaj&k9i`EOrHSQ8Ee@9gg ze-YFzF~R}aSdT`s7%_Bz6Rg^WlK8Ts%Q`zBCIkLCC%>&Ux#hev+{Y_QUu4Esi_CPg z_I%NI&S>q^W_JF+AG8=H(}X5l!{h>t3=5|O1U=@n2K~xKwLj0=I-wCp&j54>817uq z+hShEG(g8~vy+Jf>%SPPoBOmHpT=4o-Xhe%J{st~p1+Dy_W3d`)^c>zNBVL0&8Ws- z&sR#;6-r{p!T*o8_l#;{ZTr4)vlTmv6zLXv73tlw0TWDs(7T9~5Cc*Iglb2r0s)jN zgb+fN5=v+a0@5Kg>C#Dn(0lP6)P23zeXsZZ@O*e?t*nIPTr@RpKg89~ zelLpY_aMv-Vism$M}wACOMKyO>Z6(+67QP`(IHatJW?D^24<%&uY@#D9nZz|Q|8zV zvkkB!S?SEKY@x17dlGYUTV5DsZ;T14NEg!dq~*(uz31Ddk%;a(^3$zLZ~97@s`1$t zR<)~%*Ue1zT+tRx%5Gnmknpxzz}%?G9SWCWpx>1%-|?3uwW^xiASgaaM3a?%JdUd@ zvsy6SC)I8h*qbo9eyhQrf$Z=4I zi8mKSX~=?w@v`5a)(%4OxS|cA23#d|oj@d!zceE%-0vEa5d(MH)A}!v7Ewgv+nC=m>(1tY$_#I% z7d7DyTGjQJ0zlTw13g}9cvy=i0Y+m=g>**2GE0rz%Iu!`}H{6HU%rV$XRTLtQH?7*w_i{?cbngH2H)q1TM4}PPW$p3+h=K+X1ky6 zEIP2oGk3=sZ{4f`$&Fpf-HPl)27dAXg`9oFh%PKM_VmbOwlKU$;M*;O2H`0f0lQcmnYWh6IWu5mhRDUTYLtlOpafm z`%BXrZXgWxi}Z9(I%&(_@0~52Z9MJ@$<2$jkVbL$GOlm zb#`dlj&!*3Ypg(Gl0)VG<+9jqz<=7x8V+G^&sF&HBv`WNxd{gL!vY<2ZzMiGbVs%o z?1F8cBsKG!wo64=fm3DA<~)%t(nd{;gNAKu{XRj`k`$`8YZxQmwT90>hgqC4%o2JM z(y}0!uPncwcG0ahV^BLvBs!M1&vtTy)d2>X=)oqvv^ediyAT4i;8E&|RCKPNe$w$5731_UDuVrL zIR5z1U#l0xiel8<>g*p2a(G~4>aiE0hQbzl!*%#N$((O^Sy3OqgwI5eDovT0o;Vo` z3}>=6pBV&rpbe;{8zt@vCOtSI|km(Y%QX&NJPBh+AKIgZuT>)^UU?!@Zo zrAGP;MJH+dK8JXeEmXhO&%)R=oF)j{y%EC(mDvk~vlro@FQ6_y+4JHqE%@lnSf8Rk z_YopNAkvadU|^SL>Zu@8D^&#bx~Jm!EVH37ylPL8M2T4{R0b|$@DMvBcBb8|OCh8v zKD0YuOv9He=8o!hXYC8?n1)3Atw*JMZrX|5(AP;&?KqnXf78AJnRx|^p)Nd^M!Vu3 z^yv)pY6V)OX~vQdz?9m)cH1(;&|Lu4X-(y)3R7vt6iw%2ygCAdE zRfrrAKW&%pO48hw)bPgldf=m-p>_%aQE?KYbiO)-K5KsL)`bnFmZ@T(8hg=8vu8MJ z2@zOu=#Xw^h*}itjRLiwhDdV!HTJvd#a6FdMJ1_4L+hFPFG(gVp)lo37+Tn}%TGGF zD`?dJh@4KHQh-WZOst|-Xd#3KVNpcD8LpHW25fI(Nv`T{v2eRio*pDdk64;2C?aEX zVyCQ;A>lBRs2*+ZAF-x%E@CjD%5#;-8E*jiUX7Z6pil2P_+@9R%^ybe z^-?Xm@eELq_-Bd5{4XILvOYT87F`SGh-Is_Hmm4H;~T1^Mre_z46iS&C8Ng1lS|O! zbo8e_q3+_u%-dZ6m}$hcWkHoqXY2UUBr>|uqa#qr|F88=VhgH1rKEkK5rY^f=X12S zX`V=PogxGK-dyG_>xJvBd&&F!K_9O7B77k_vb%IEoMCrXu2&z{%mm!C3QoNnWGT1^t!xO%eJNh&lR#MAB_-sSs~&VN1UY~b(HT|f<6 zYR>lG^x)(z@+7TG@VgtOE6GI-J5toOlpLC!)p~c1&4%bzb&!BYzDDncixU(YuICa{IT20IK)xkej-;q6jAN0?)IJNzwtSu!UJ%^O+HyDHXlHk$_BPze~*t&FMXqh%G(L_=MQH_gmY2{_h8 z$JsNR7h7A(Hxuqi6gx|B5pt&zEG>M`U8e;R^rN4bmO81hG3-2}7!dTa=bCjxuMe=i zG;)%&Hq6bL>#WQ_S?h*L>p?%UA3j1O9t{l0^E|&8P9q$awtpB->Hx#(`ALS{wkvdJ zpWkNr`v=|MOmv}t-MRqeTaK3FM4HC&^bV=cr&!dN@=#m`uo$3ia&~WhPXn9+jkKFJ&e*t@EjHaX=?c3t@mRqTC+|c<1x)cc8^Z^ho~JUKqi{H7$gZb>kxn7^~qvH$L_EV z7DIhxV}#hVslBVqc%5DAg${d}%>>DGQ%n8wQlf4)WaegvfHNJB9gvL5c1b;4XzblA zGA-@RnhG$EqL zM=)oEaY}pff%nQhcSpdvyB(3y^YV9hnD$JG-RpZuxdHP*{W*$Y;;|?*(w4Z(O{?b= z=OfRKCLg+GpV1L@MGQ;dCS|8}T|8o-N>Z;AZeckj5@QkX))C66Bwef%un>^UgYikp z894)N#?Hf2TQk*+g_ozkhU6J|B3HeZr z_0KmO8onEVnSQHPe9lBQsL%P;D*tA|y!;(t4^23DrVnSU*06KRbse5rZUewHb(-2{ zn8z@AGB(sQZV+jtAf|bTYFlIL+#H zK1m(~%0aCta%(%v0w7=A$}#}t>%t#ibNKzTQNTj#dL%F_+2hU3H?Ghw$rREopZxoT z(gDU|{f9@!VyCD|=R<7r=K!WM{160~J zd!oV#%Zz@CRp-+#~qse|0C|8JO2~}l*At05rEvvF*=c3 zO>|(oV+L1d#rPaRN=6-C@fz6*6B761H@2N8KQyYuN4{dI;6hdNMN6DbpJyi=r^@i_ zN9cLtPC8F2IK~OH-M@L)#QVgDlYDgi5~8Bdd`2Vgt?pct5V4~H!uByGO9_Ll)N_OW z_JZbsss#jt%iMZDSIlE|i$jCBOR&lBt=1`s;~ejJ`lDMX`dWOQL5j5iG>$lDkSG!8D%bP)q{bx;=<|Y4cXvJO$37kP~OQzxzV$4oRvh?nEXOy3E+f| z1?kcV0KKxh6Z!7TC`55#o_;eDrZ}Bq>c?MF$&e*TqEm zn1^1x8^#+)5$=YIIvTxnc)uiHzuM%>-i$Fw3{?(kcFh_;W$$``xmzA%PwCtg9nk)i z0ZvqXY2-6KC{-$&X-a5*tLeqOm4}bmy4KQDaIP;Z^qQ%KHQX9JY_#royp9`xe>TUK z$2gH|mfVA2j12RIvZN($m$?F3DQng^)-eYgXM z@6dS*_aD$H6ayhVhz^|s3^2qGL-rC&67a)LUhjy+;26`{Q4j+yqrId9|VWd+Z06ebdKTBHzU?~-4wZHBgPE=%Tf zRr2=c`6+#-5fgmkd?3A5Eb%eIW%UkRI#L@UdHcz{Z!h(EP42ZU@-Z@07H8Xf!3HNp zWZ{({!DIoe$W-fjOF{4M&|Sc)3dB}YIxhQ@j?As@w!=R*0Qcp2;9fd(J^Q14wj#FgQ2`7tVLWmSw_OT+dh1W42)Zv`-6LqMheRuf1$E`iAe4i> zihKAK@mwIa{EMV6)#<|BlRg*UPqVb)rXM(N_W!7wjXa7oI&6kO8VmJ%_Om)dUAQR|cYw!u-G(o!8pQ`@2T5MxKHurYx@T?2t*#lJ>&l)Le$zY~ zlRoc`3aYiazV97t3OAUU|?j(FR#Nn5Mr^P(Yn??y{eZT7#nxeeYfNG zSjdm`>P``rMw9O&_De>jGVGo6fMi9_In9C*D(oIS%fI=oc|q>2KUTjyY*Cg$FLxLu zonBLg#Siq5%A>iM~6rZ4Twsroxw3#E(~ zHQnOw>m2AC+{?6bEHKRM4xdU)rt$VU`9GZhb#+jcW1`9RS)O0 zf6~PTOTF_Lx<&6t;8#j(jNdLwFhM7kVe=opz;8&T&#dk-s#x39lWO)Fwoszb- zo8H>>IZ0OJA8n1di}Odir$7Vx4(0!CyO6~8zoLgvs+bE-_+pZ*V)Lb%bijzAY?D5TGD)mUZc-r;^4KS6ueugMIFP2l?c~3M*ljj9M%e9pUa9+{StSY_lBd2*upW zzT(BZx5GHKNhl|gmDmOJ+~Gty zcI21jOmF?zm%}iT{b|P9zz>8$@d!eyi6+(9H7Bvh%RuU&16&2HT&)o6HR-0pUiU2Au zG79up9n|cicTJz#BUo(AO@L_}ux=6@eA5zMn9a?r+anCdCFO!U zL+V_q51f@#`~+|_+MPa4GeehBsYc4Tp~T)Kz=Z5kBCaKpBA%Y$$Mn~2LTtBVS9B=x zv@rg4Ve7ndi!W7#m}}CTF)EM!cJ_I1e>nmjwa%JPJY%UAPe5DA*a*Iy?rTFKaa~kh z!bxkg8^%mqGUPLl@~`w}pK{9nW$dgr89fd8&}RpOg{PQi4Cy?hD6_#`ZHv}3M_@4` z!LU}lj`y6)bQk^;bX*^Z5F7MQ=>>``H>=Kw5QU-W(AXQ>R}`ha3TjS^`K^xS`Xgqj zW1oi}mpscFL>|ZahKOJ&chJBgVj#%B9H<-i(!kxB$mP|~*i|f>O}BWm^4)WFQ}Ubl z!DbtRMUp0X=5*f8Y=QVjmUl`V#WnX7vO!yMKHK8jb)y~M_0Pp7H#Z^lNnu}+eu5w^ zR7>^;FI#@@B%J#>yRF2m;vZQ7edjVdseK%PD$5o>TwB#<_(nfSQRP`KFexg(1k`u3 z1ZcV5!9(sOS=a`Mm@KHkA6FA&0Cc?3Awz*_gKiO)T?}+vp!%a?E`H$D{9vdv){8^r zhVICdrf-WpjUBhUC%r_=`e2!d)R%LL8Q#e$sLUZg4$*|>~mBgm%4)(ss?LBplB7OKXrh0Q} z$reS4$+tYYJ5ZvkN;5R$xC?3cztg7szcOl<7}CXv zS}COaBFPje#!EQ5yh_(lEEF4wVgEbQXiwsioCx8;nZ(MQv2sl<;II^jUqWkL>Ft0)fEXVkmp0=m*WlAWB z^+%z|elU$@BbvdA{L5CkQV9pH8PS|^bY66tMeRhV4R_)YSV}J=N-CSymk|DB!VsFb zAsg;kU?deX3PbyKH8c;K#@#&ihtH0yTn+N`Whr~QToysQos_$=xbJHD{=NF`H>@n_wUrCwz z7%nqKi%FOm6eD=DIRR8`lE{DF5i_DfbB(Y;8@(*(;D$%pIUsC#Kgq4U-bUS=Hgl5n zVU+UMz|{jtgr1vyx`~GID|54uJbgd*IF_21WwNaeWg|-<#aZTk{)sRC2KuR|J15f1 zk{`R|8AIdVzgfDIu{QZ#U(-GbW@Me(?+9u)DGh|}CKiH1>W zb1FJyL0Oq1wGwQ?Q6+ zfCrr&F1Pm}%KPyr#+pA534yd>x+#KU8bty!;~I)R=twm|1Z)IMtnXupK5KC=soJ8?;ykDd_Q*So zQJQH0wAGs&OkfZ$tzag#_JVV$SsU3Zldsg$-B;XYM*R%I;d`MYAbg9b%G)_Y$#7}1 zQi8~GQ3HCFrA$L(Rz$ma{l3R+VaI9O>jfsuUm=%V@;nxpc1e=zXp?;i5bVBu{GXYe z{sqR`3KzLRa>t#%68k#yvR8=eXrGHC=7o)a^~Dr&ILG*(sc??NJ%_t!iw1w@r0ktS zGhXPClrTzli>Wt=F%1}6RQ)oZ5&p2`%iKmbG-nq{X=TM zjUNsC>UF0yRolUW(1kzcQ80Nq-Gf6d8i_UYB4%u9RaK3vREAlDOZ&w3`sRJHqp)Qw zo?9!T{~dWLzF9aQaf+ao(TuiY@2P(%ERoI-v$)abV-AAJ%o~JyUH<|~xARiM6Pp@Pb3^Gq;1mrlMrgrKCUCaa0j^Pqs($h z24!1ud_qa}eGh^f&(B$rG#Uj&!L>8SxpSWJ(;^;A@>}rA-2SI3`3`wBhkTO;V-_2ak4Rhhz8!tGHHL=+#}Q z(AajU!K{#WfXc&Vv_pWUUt0c{{gBaMo0f5rXQ?Hx=3v0tCv6wl_EzVoSpxRN4G2n6 zqXb2;Wg*&5(5o#ruU4gx@$ zAO{ztj`UH2Uj34`$_=;J-dTh@v`WRLJ1VNfiGH{@DZhyV@Ow`5)!rCX{#Gj@1^W^8 zrQqNSLKSe??xgHV+ZM}tY-rP>t0h?1xA61-q+199!hp3Ryf3fSZQCH%w%KvupJo zin)46zry9?44p;Rhc@`D#B^TYT5Ufd?Le%nza{n71CMNgHns+ZV6j7%CpzvDkesUf z20F^*rp$pM4zt)&B-~n+TeIiW^3Dy}j~muqhhYQOOA)`{o=x}AL}4a4qbE99Hy!h9 zoU8SL#AA1j)~>u=U^RRb|HxxRRD*y*lGh{Q;uO1MU$>_Fo1FlN&I}zJ96&s952CW} zsI0Kdpg8GaUS?Zbu8M|1nxhM&tA~PoDF60=3qR?mKdH(C$Sa68?dory&_%yv_l0XQ zUhvu*5s!>6>i&|*03(QhOYulD@&AoK`1|aOp3lFA7yz;C9RN5uT8<(&AW}PaP?WloDWiXbdG|?2`5fr%q8} zNs(tsv7;i?)Z+&>ndgdPZqzo!2FOz+cY0Sh`3Zd5)mi9pqys!@sygsH-Rsvs^ljwA zVu|R}KCgPNa*)AN*?dt=HQ$9*opPs8MoTe5U%prb6nQDOOSeSPKWE5UG>XU=S{T_d zlVk0nY9`wXkwJN)IjavCGPZ7R#ZFQH-v*U$>!UMPtHb{4o3}hQSgJAt5bG?jqfPwu z>AwI(6Z4L^^bNJyniGVbRe8oc}w(y1x}aMhPsK*&L$W?-WJ)v%mKxH1{4B;mk{!F zQX{5LAbH_I@xiqqK(hB9S)H8;gojQ>Tcyl|%*KnaG~=$Qs_1m#wNAbc=7;^ zlj6vsOwA3|%KyLbqu`lVQ2RnrQqPSzHTLPr}dAPUt(+R-c#!vh(N-cV%Yn&sn z8hCnuctNx|M$RG0SMrBG>(}r3#vpkiWw|261tirVx)mn^FVUl(k6g3+b&)iC-W`Xg ztk(=Ax4KtoWqkkcC?99!i4ms>>$i$@XJCaY*C1{xExjY=v!XQ(aWf31Q8td|M_|5S8h3*2_YbfKl}OTiTlT2Lh3Ic*}e7gTUOf8Vkcb{N^oy8TtAq1d%_>EH+%5&h_YW}EpTgJ zbyf0h@He^DT8@L>=n!})V~WAO5ltt@cU*gFfV7yrY)1^ed;zZs)oR-1?u9j5WC1c1Uj*5s0o(68pFP9V^8k4n&H3^R4M-OOz&au2<}X!?d17>fLMsOEc+E5?Cj~Y*9d# z3Ub9w^KA0uuxKJ3P#@&hOIi2JKQLutT%Qlm;J8?);c13e-l&+`1Pk*UEXHPs%!K7o z)<+KQB{Y5MK-`0#*EB(fABCYCH+ykfr@U}lANb#w_J!oXHn#CIy}#0XKZ8)EQ1iED zuG&SykEV?@>F2{gO1lw0H)kiLV%az=&0?(v{AtrCFqlc%uXTlPFR8kj1*8>54gtBS z((sKKv`S?eWU3TyW?0(m1C^yz3r)wl4Rq`0N*F`HQlqsU%vR%u%5%tqpnW=>(e_7l@BwZACy_oH8?TWuupb zvlR(o@omvaOEtH+h0WMUs{@Zlw8`(vD*pl^%)t>Y%TGA?KMFUe z9wcVlo{4-`>fK)V9@2@$;j3!Bi7@E1KPXqN-1(x=fo<*nb1_w{*F3x0WJe1@z zw;OczaQMJ4;=VvtY}gdLGf=z@naWFeNpNm+T@+3NBQqNqQK*ZG4d&$FudpA^?xv=0 z7`wV~N}#Yl1vX>50=F5DOSE8il{}&8)-c)JLQI0Wdp;Qk!$wVYiuf8#UcwtiRPE25 zG9+XU!?IP#FU&LQdz{m~t_CBL@7sFC4pu@$Lq$M>mzVFbk2xb{nJ`qj9{V0sDIAo& zHDOWNG>~gs<6g5Oaz;#(lO}a9x5;_tOhN-YEb&J)MlUyBPr(Dl;leKu!$JEDA1?2> zN+`DHLKer4x!X&mn#l}5ptxxIU2Ie-b75~`jVbl-OOM}VL~pkxIWG0+>RFs&m2%=7 zPJa|on8+F55n3(m+K7^_AiPr0kSotCtVd@>TZU{$lObX%HnAycT;bc0HH%e}C2`A+ z7nq);gxhzIcmx2qrVOcBngku)TFI}B6daYC@u^GVs^(JqXrfo_l>6s?06OBbA6Tb& zNU7c%I#L9EyQ3ewaBXS~W=h}llkT39{Gs}IGyoi_Svee^;huM0VNsj^^G4rY^XvCv z-gIsNw$}qtO;rCs8;nPg&xie_i`aH}zm?ng)nG5j>-QTY6oB@OZ3ETEkBcocKK0GF zhv7k0$1ArcUo^~~0(N9#Vvpm?fS1(P;QSn*2D0ZIxoHW>w7Aq{-qBLti~6)r zsF~AHi?2G)gOV?{e$w5QybV~5 z6p<^I$oWnE8>)5;Gt};dqhT=WDD;JJ?8>{?4i^{8TCl$Wi^^|Lv}+1lJ!-VNOl}C zE{ohsZn47E+Nuw%2UQEZWmQ=x$YU845i#*O8y-IZ<CarbsC0hFo`)TNqI<#pnP>*#bYC1pbeDyh7 z!m-xF2x7`UW3{^0V86qv5{a2}5_s{uB_rrg!Vn9`5@?Mt> z992OU25n1c$4%cd!H=+r6wf%uDw7FO$Jvf-bEhAhu7SrnGy z9LlSp*yyoXg$EOPC7i~@5`q=}b~#i+=Mq&byC?fJTq!zKf6-fZu(zIlZp-zR2^flr z0)u9s@(B3wJS*|BO2V2C_ESh;*RQ2Em?xJ1v$tjpS*8{C3lCbse|%}G!ZDci+3V+=3I{*c=N>>c~3!0lYg z;QXe~v^Lyy{e4D!?EVv*1?$@1b|*6SYYz{ZzwdPHn(zSpa86XZxrIThKK}PQ;6ErS z*T15ComP97IIwznD?bBk1bx2;(y8C9G{zTl@T~>4l;bL!;J^H^G>I1!G(4I}p|JGc z154uOO{mgxdq3)3l&+w!O|)ky)8{}>Yk#aW-E@%TeN>4jb&^UAgz;7>1=htCbSq)D zpz^MKmfX?VdlR@Hl{wOAz$=yU&|HM};?ywmZ-+Y)={}cp=fv_JC(jl;bq}6xPn;mh z*XJCgUSX}Ej!jYyrjC=rR~6cS_YnSD_kImXj-~hc{{p&I$i#P4T>o#N+i$uJ5R~X1 z&7VfmePaR&)Q*-br|6#Y0ruZV@y$`e+XdhwJ+An_^hE(cq7>k{k)8tbq+@?SRSe4? zM1-XzRU+qL1aijcp|^jK>@KJ$Km)ly_96NYml+b4FAjXEE8P$jLKk4{_ChxA%+y38b_n~3{l2_4>5;iw3#X_ZC z#M&eRWp8L&HqMg51|Hpw#~ao|`1vJxjo;1r#_s`4vpExxszj~u{E?r3>&w^&)nJ--`!CYYI!1tFFvE3PKA z;RjT0_4=KibCHuuhCB~P?&WJM=9=H?@o?0SIbeYpAhj;RI=+8y=yq|yJ2ATmH%KQ5 zy9l*JE9H|lQ&*K;X7<~~j!~FP1UgJ`++bpxw*kpg413l_zA=d2tEfWSrD!c*)Th_U zqe-wP{Bnl_rma(Tq5vpRI@!BXagJ4HI?%A&&|L2;S5dfM79nrW|T1>$AvQ1l94uUio7PIpL9)Ex4ZT~&z^tbuvs{NyEv)pjF0Sf z#iddepcbfWSeJlwg}t&1#sI~{iQ{SCx$%_idOO^uYXP?Fk(vA9{W~?%Ap*k9V9^Bc zTb_aGVOmq3HElZyXH?IJ4|JJTEt+9OnrI5Jh9ySf_BxvlO09HQI!9FV!Q(*bPEU0R ziClbd$m;f;tY~kTPA7w^Kfe1r*y*s=tt`o*tE_J-uJ;AQuMG_W8^RCyUt2Q)5@7`y zSx{N|csITAjYRbv%a1#J>Kwf9IeE#N*xv^eVb<$3uk>t526&x9o88TAML6&u&Za2j%C|PrL84AKtQiyxBgq zu#eCjC7M-j= z4I+{Pa9^E;$QcB_z8UWWym~+Qk zYRdH9!*M~h)gN%n5p2)tK=U17ZZAc(P|Ye`&4*#dKj~se0wt$BUp{--J}MWB$^9{m zg^CDQyBuHseTzPdGkYqh=j;=Q5o*6b{Ofo49B9D=y#De^zHy8GPG(acw}x&4PJB*& z%I=f>WCd~T?DrnS$>5O766?%^Q#P8}q4GE4T|y+bT0+c2L>t^%4$Tttj7p%2K{5|L z+}dxnChv}!Y>Knr7{Tb9Oh|P+uYb7(rwInVIHyt8tW!MDe6ihZ!fEBLPD6t<8X548 zp4-7o)=B4Fi8-FT*?R?mjLGWwrkvMQ`QM96D_Kas|3|~)6yPn19S@Za!C#?!HCSmB|_8SQ9oCY)q zZOX~UhsA)S9P1Nu!@ltq^wo~xv;(ruTRs)Ne?v+L-7+wDiTuaD$7+iqT&Njtl!iO+ zfB$Rl9rxVx3W$_Yw@M*maF}Ml`NX$;erQf208_t@WSxgX96ps=_34!iKOc*l`rf{h zsk8V@;kGgU$u^Ydyq~6qj{WrcYV7$UfOaH%U7RNR+}^F;OgBl*)x@zeLU=!J&jP<+ z&&APnu52<#{e-vQOQT`q?AxutL>OfS=k={U~uF*)MK1y$e^SJSO)0va$!X9tcA2vIj(#%+$APtJf$O}*KG^jKS!u)v_S))2kBUaKb zhQ#voGxy(gBzq9DeA|7O5*7r}fY4dQisy_gEKDY0A_R@SnSPbyyzr{jjZvl*Vj#lGyE&tX*;b4= z_$@d_c``k^eZh)pQ6xQff_jg57NjYvd|)^D1ZArt`+kFKrJ|DP!-cPr8#~LQ7w`EU z2;A5+<|ZJ7R4y?>9^zSr7(obE^#o)vqyDw_@OHoz^V)M-qn#L2$^i?~t*V3_S(flwkc_#RmwoFMudL=N# zMLVBx*~c<{4vv|GDI~7$e?Uv8p7z*=W|(%I=WK~NonFcje(epu)-=`{?&hBQ5@Yc} zVqDDmZjd#7db6fX8tPJfqeNO(3|FFU)C55@U%`1YC+#9W`BH1b4{eAbx<@d-!b!yh9aPuuyX5zok>_A@!^iQ>BPr`eNA>K!E@_7|6%PVfhR{WTZXlO-cguG3+&7?* zy!O!w>%LZu)nXnIyji7Saa)sxpShHc@AC%a=;trxjKdLt!$5wL`}BpqH$FT4YI=-i zPdBQ*1xy8e>8b-(!=s~x+-Nd-Y5DcWRP^%2`3uNe@ij+A0opSbZhZEuh`$5^-Ff}< zs^9wD=b2MKMcs*%u6VzAyMb3B16ge!fXD+&2=@RP2yrSr(~P-8jZC*7RgoXS7P=D2^mQrbN} z{03(mX@jL%Cz62SRNBzUm3vP-~eK%qpjUQ6F+n9}BHy7OvM6{^F+P@@cSbrU32^+jMfn z%V1xHe^hb~=dIxVPoUbw{#&WTw6ig!?9baj<_nBhi{H*29 zKWY>7#kA*x1Lfo6OO0eSbpI!vk2QP%AeWE!P|rYmGqQ%j5>P4ge9^c|85?1#`^ka+ z&!&df4k-Tk>@79qLcPFH0N2hAxXRWsP%vf8K`M{B3Hd(ysxP0`@1hJI^sooQ6A%VnitDf4QkRW$T@NrPiw zkAqdxDdFxAjMd)E(>Y3akQVQ8H%pDLQ6}09Q7y=gZ=iaMyQjvzPp)%E^+?F7o{s75 z!DKZR51P!=YmF$DQDVK30p+W!ToOTws!nZ+awLs{;5 z7WX0#dN(iq>S&;uAq^s_>UF6*(#1*&cj}?=mm8|r-FVA+KM855WafWb)v;DuYPQ+B zSTJyq*C$DAQFhvx6ftxXY7*kdFyNA}7Xx$7WBy_h&MI1t0QaeS*YsGPd;;5YMq_&* zxs5&27HzImfkz?vIf_Rg4u0dI>6f3~W7*`^N-J@GH>SObdIs7xLoinm0m?r+(6cJm?^?c(Wb|xU z+E&r14F+4BwZRs*XcFFLy=%N5m$Qh18Ia(qexUNB>5E^7hfDzv>Yktl63pNe})y zMzy)}^zY|%fMqUFPV>u_2Z-wwuh6mcJ^lOLuW9Qmr~dvmsQ6D*sD#jQ)7w?pJyW0X z=R@QFDRAPnk4eCO02y0LG3jA$LX zz4>g(UtE1GV$i^wgP@Xs-WpCTQTtK4E3|u*Xs|vBWOjlgGXB|;-8#f~7AoWXuHz4R z)IMAq?jPGtx$}90DLFP}sc}(!bT7E33%GM=6I5r%TNdd@yCHTe*dSU8JGid}B|qL0 zwZb_P0$f=ME|RES$WU-xA!tXJ=LDrqtPCW16|^v9dQ$vDjg!=7bq9yaBgQ9x;;QpH zq?dLg903dF%lIbmG*mo8rIGqw-rI?j?CzkQTPszOFR{g&Oc*mTp0)pNay?nUu_$8D zNv=dHyw&Nv6lmR#yn=1~_5XHCqxzHFcJd=qaPN;*R(|?BeQG({<*Nl2>Mx^m%9p># zgS&$q+YN&Vl=63c^J&yNx6O?GDn^27H}-J z7F1jiWOKcMs9FkJ>am_JiA|t7S4_QpG?DFy5Vu;lm$b~>^1TKySdOSRwuY!{tM(n` zs809zuab7*yC&RYN3q8kx=T5OY>MQ2Qq^+M;XkX@>9W~HEZoXfQSGJByHUIA4ju(` z*SQed#*Ceq0(0VwLqBEdh}Ojo`X+^5S#b20+AQ1fe!5K1sGk7gCzaY~$a;gP^B|I& zX!kXMwT_9c0`W!78B2;P@(o+*!?&MqRsZTG+`s;JZ%L5)Slr<6bD}zr!|$!^K{-i& zlcL(<1A@CvZUNUjNJkx%%!QM*6WGr=DLR)GFjZZ<7*w(ND%T(|xGy=qD zVId*|W-KfrFY2f#s`x$@6k60+4UGpQo(J&3eU8@Dsk-!QGg{g@Ta%l~r+)SP@TkBV z$zn!-9lrM*g4`S(bJ#iQ&%Yyq-aq62k3;jn1A%{y&*_+M0f7LZ0y>(Zp<_D6=2(_f zzQQc*5k2{>IVOi=*;_iS;5k5QxESP-{XKIEMd&%vz7q<-X^D#@%&ZI(&NQz85R$jRnvB3Uo8< zN%r3>eO6&a36=As3vjqaqx(5EOHG+d$y2BgMWwIj8yQ|Oh00eRZ)|XnU2=8N*4Wd; zY*$`28+|fe&k7xBP;xLx)`!bMA{%~a3on$K5}+Cp#dM09Gi$)f6IhnoqZcH!@O9QZP#y?^K- z^qEJLlIs+j%*KsGKb2mtHD*pN(&}i$IQh_kzck-s&MPYAcW6hF7(t#PyIC z&?FtZaK_@YZPx{*?bQ#HUPElb`ROB3lRs)685#;3jAWFdSf{W;ZX$RF?F|%vLqNkn zafZIZKxvlYdAHz+V9wT|MLQ|HP=oUs^y=fRi>(;6XF|gv?jKtblowUUC$UT@4=Yky z5XyFZ(6%ol)$9g@*cRv1gNcg0QGPk0z&Uoc&vi4BYgz;S#F#HZ&(gy5pV65|hgd~1 zz4q!g^US;nOdb; zVxV6|n;0tUtcupku)LOzEJ0FDu4FaFwd6TN^06GG$o@_xB{HW;sY8t<-SW&T*{eJg zdpI#=KHx-(E-}}s`C;t2o)S8uB$pM`i4oL{3Xm*A=DSzGEE^=%NG*^Ib;(EKm#bTd zk}wDizX0mO_J~UN2uaYCx8;7FP9$DA`h2kOA_HN|85pirGcOUpAm<~j+_$h&EYW{H z!51+YMKyW@*SVIRD7Ax!R`*H`jI*r6G0kE%2B+dZ6>(x8^);?P?`~_YTRdyz8xz3C zRe)0gOQK>n^N`xG^6Y~!F4kF8<>-(TBawtM? zQ>uPKS>IpYfY+AP_PER2W33eP4)YIU6z?9W&4mO$);b=q>1j-|7&lzQ zAcWRELe>UO`=mZZd}ZddKttW+G>PowTfd3B?Wom$b~|3#UUezw<1tCs03k2g#M%@H zr^6%5Q6QE59CKpgYxlc!REFsY=H`g{9~Dg1iGDN-YuUrd<23b0#akGlK8wR%ZK`?u z5|}=2xpkoGiYr;MR{J}bf>K8MD)P+Kh$u3dFzi&=U9;78JEYHV8TAT}9exM*vYxBa zuQ~Nt!?N`jXyN_x%_`7<&KxBwBp=grGJ4(02$eM?S+PbR# zth4J?i$WZNF000b{tDNSe2-c3twGQNm+yUGvHSw<4r_gOoGZ=#+F8&sT9?u*3}^ZS z7*B_&>v2wN;J-KmL=RDYP7OmRW=`OuEh{8;qKr9&ZV&XA!{241vNe_(s|8put0;#x z6MF3rcA_2{?u+pitm!Ircf*=TPxI%#flH+w%O?lU1R>&cK3p{{jw+a=fiJ){3PgVu zH!l$pvpK}(mqmvgA})Q1l$)!xfjc0>Q^0XlkknRIxfo=$C*>~@`a>}q3q3O?Cp+EwqJaVw=-QVLumm!d%?k=f~0(#;X~l$?fUzj>H6Dpmbd02s3o z`n6f%C~E`s(AUnHh}W;JM(FD6Znu!wee*xo8o*M5gQI&v=VR=%8o3b>ZRg>*PePQG zU^@W$;E$*K*f+i%KSSW)1t=yY?gO;}@6h*E$S*&9Q7-^D6R`aPtV%e%H&drF0Rk!^ zylU#?cOmwFk9#ob1((x&*<;2wX-;V{TIOyHnJAo%UR zR!^mQo#wsq$@w_#j*!1NXzOw@|E)8;)P`&gp})r1K72F%F^#x|{|B`o)L7F^)~&Fl zMu(Il!tcc8%Gsu&xfnBGO%G%=KtLuBDExi1Q4`90PkR}%nto<1S|wWW=J4+^51r?Y zZswZh(Z3c5aq1^i$EOQ|?=Pvy%DAWs(7`QD0uGe}GcnfdUfEx=C$~>s5-aLnUItmc z%R~0&uJX?_8b<%P)j6Q*>rH<#7%d}{{dqz=S-XNj$ySF!B{3vMOW z)=5NgZ*Hzhd=w#jl;87|SBap=u&ny~PET)&wEUdAf?cIhGy02^ zS5jY`AH#3igy|rXz=VQ2p$f7Mf0hk(jV8xte4Wis^?C8Z?EN5ZWWmrR)9g~5_sDKn z91#BS^Yp_24D9F^X*w|d_@=)0R*1>lzCN>&BKXOL!O2d#eje4A0aYXuZOsqA`J++D z4I=641D)X1KqVy^F|<;S=ga#|U_-&;FBzjxDBC&5x|phzHJ8&3(;JuANeSszYx$jh&2aOT=iN-22rC^W-h@Nh>K{Is!wNbu4nx=| zR;*;PHyqwtlneyXb1EWY{h%qDYC6XNesY`OqbV%yZl{^IvzrRz@I>++hwtUg6cyZZ za*X&@n074?1}f`s{SKG4#TU>iw7=)HRDexi-GYyME-PBZx+vLoBd>(JDJ|P%Fj4`< zk_M~X*pU>$ye!}N40rFpL&^@P)UihxmYrzRi&_rcYI`wNH`kMEPD4{_rW{0KG6_gR z@Ueg3AdV}hA2gdtQF{Y5yi8Ax|Jl)wtCl5Ey`Y|(7n3{|waxpN!dH9masDxTN)G_* zV6-KJ6^S$^u0B!i8FjBCruoiZ?Q7c^Apz-wP!PSdR*HvZ_l~vsfW6j-Fm+L1Dt)=F zr|{8D^)#la8v>z*Xy}R^{Ok#Vd+%yB3&$^< zH+hd2Gcu+JnAP0w%xgn7@m#&*>oRzS>LKd_*j!3eKhO6GmH31gh);ai(=WNH4uqQ$=X;O@ zfOh8Pw@HwLL&d6#p`Z~coTIw)Q}_c$;Eebz5pi4K`lKODsVTxOi zw(hSn7ovxMcg;o6UAj#$=NlrD+_Jih+JjE0$6EdV90>Y=9{16J>f-V=K;5aQ*iyh8 zlD$$TA{#b+M6xQOT)5095D6OAQy!z#=VS~w<~J^}FcWPg2>4BqsAEHJ3RY_5VmE{+htAI^7M1?9luiYW=X0}@BUJ(lK+lySXDuDn32ccn5le=$TcBFPS{urp;cSGv zJJa6BoN3iM(otR@S2c=Nw;+|wH*YAeWy8@j#w!lbuT_{8MTsvfM*2`r*2d#8hOa4B zR49mIk`u``&8ZkHEtxlM-Y2o;kKjluLD+u-srLjwZ!K zvae;27O1xct38iD(vvQ(Am`u)cGBpuZ~o&Z`%>wisMGV76-a9(cE$f9w!%DT>Fh`Q zH>cq8kl(`J#=n~qkbdwKrZ$j~XN;$b1i+FoEmSkoZ4>X&qD9VYF=B-kQ=)ijr*>i;lz08&eCN2uq_VWT4N`SY}m}VCq?`6LyunZEs$S_Q_2L zI02sLgi)QMjzE`_P)mrOOb<3l!m@KKTzW&a_%$*o;?==9h%Vy9=rBB@7^1cMXOf|$ zwARw5=+typSH@jap3T4c#4u|r6ga9OtT=+*=HHNiuAwbk7Lxd!S9ye_7M47}MO1j7f>4zTRTTL*v zqN*l`8*xL5@R8n=kNEpwpNWnHnD%W9)8{7H?z{!_)wvhj_NA>vtjE$SID6=PMOEY1 zZoO-ryzm04GT$drqSiR;#%Y%xzK(5$325+}dSWQ}N8K1(odGvOzIw>7$D!W*PlF_6~mij3! z#zZqr!s$MbV^bXa&-|mleM7+eM}4dRroR2f0qAd`|C9c<)Mv&m0FUhkvq**?s36tB zk>Ed9-vu4cuQ@v9x_t2)FM!By`|^4e3vR>Yy0hfuQ?NFAvcOJ&36Z1nLtvd(qa!2YKr?qExxjU zp#xf-*LQ*LgI-rNk#>9`tR$!Gy9W( zwIOb-bc1b;F)7ryW`JtuYMb2m0v2A&-SgS^@$kN3gTFYQ{>AZl!8dS*KJy5Rv$gI8 z{5&D~Q&l;CajczQ-PisvG;kx4_se0MI`=l6i|%tgJ)?7-wWv28#C>RHW*~@kqgpVa zuu$7?I(F%WipZm?B^`ooeE072-A23l(R6VqlqU^klH8uP_Tg2J??V(b2O3RkHb9_8 zT!kkNm+tRURlB_bhhd^?hPw<68>{WbBlcysRK55jlQ(k}NYJLSR6Ss{Q^=jw^x63(4sRXyJ zy2!70LJn3e0>pU|h2ngBET6&4S(a{siAKm?toIiB6e<-*);gorAXYna+sZfI-VN3I z$UuXIi|_P&=^2PViE&9_x#ZXRoEL`lKD~I{>BI4dW})`jJGa7PafQ{LN*4VLF+LMI zb$(+<)LKUyyw1jBY99cN7GGL=S>$D+hpUGMdaz1F{CA^Xf7>pk%mdf5{Xd7A0 zDL^!#LD7?(xc%?;={1mLOo;(GrM2}fV7G({5dw5|Ty0n8xUEF@9k;zs$_tZux@B*Bt-yZXhV;(KkU$t<0F?H{jsEm{2=EI@i@)?z+e9#Qzso zw}p7C|E+X48@3QPGN<2A;X??ug6M7f7l-HN8{)@DdcU5d4ms51ebF;2@A99ceZc@e zS(EMZy~oFonDwr`Mw%=s4dt83$@gsM>>N!IxQvf|Z_YZLu=QVAKXJpd<6@UrR@dLA z&eQ@S#5;nADI>U|g2YC^Q{qrsK5{RIwk5nQsr~&5hY}sm%iL)sI|&J9Y^zKvYqPZ5 z9A?|z`GI8e0cbc;HJ{_2??11t?|XYEi|Cv1rX`DnkpC?FG3C=$AW(7LU(rpA8i8iA zwp&78+;Z*E_T0!$@}V>aT$eFPxxRjv_^Qjtky2e zo6}%4FJ=%gpW4D$A1k#pb~#~`$+lJYe=q(a9b+8x=w^xwHuu+7Pn*YMyA;!K_^shO z9APe?ah6Vwq$$6oDwI5dqBh~LW%nI{dJczS(r#Kp{22Eh13&Mb-({#Z+)z1bDDm>y z1u@{KK6-!n;nt6X>`Mnx{HlS!($#wszLdkqQ!eWnO^k1fqs!Jv+L`1VLuOO{()TqH zbIj z50w}4)O(VM)xugj&W7I`@)6N(io#8ypEPWAZ+_s<-rpNE^QgIJch0Zlcr10Rdu&hM zC<^dVSfvRAE&~7Mq|-n=bo!{Wj!xRB!NYZij-uV|d6!^Ez_Tz~>-H5W)la-5+nw4z z(xhT9HQr(48kr-67#i`WK z=|B+k4unb~`itb5$ZimJjeK5BI$Q-Hd48E%KxMCLKE;3Zyf(F)(W(6QsZ@Flu_cC5L0Uikqe3ig1#=VA` zV?D{y&I5^LKuCRky0P{C{udnAe&tF(Ed4lR5f;!B6A*vb;=qIZz!^tNzYav@7DetA z$TgKltQWKJ#ic$ol$0SIgVU15)aw#88AWsBoL9E5zZ?~15I)jadD`!(kDr^mvCck` z8015FrrmM1q?jQvje)zxK75`qg3@^?`Ba^yqii5d!MoFJPp*HE8-q>}kGtx{;4|7_ zkt_AHTJzqK#n;uFUzQzY!cJH?_L-#i<81PfD3buff@6Okisw>|BQrbqs&7U^qNVM$ z+E{h9Cq$EJ+fP7)B(-p$XfEU66IWmK3uv7?uO=P6q?i1Ae>$kc5cYsAw^hpLWf1}w z0+!03SM+#&Bv$(O6;cKUQUWcauPxWp3LN#T-NBXK*7(?>mV3T;r`D{ZMOi6!H$JF1 zL(SlfBd=wpPBbus{c-YjK`-wnYJIZr9nOg-ST68-RVHLqdJnl6PYaFzUV~gO@k`&u zv^!hYp_(Jo>;p%#9E^Nck0-=uQ;?p{v2A~Gq@_$O#N4Aj1m4X{ z3`oh_Ov4_ZKtuWmEy{a~l#Ygl^4aq^LauxSx zCG;(V$DMq;W|my`-lS3WxqChZVM;w*HGcE@#&mQKvAXc4=>klC8TMSo9)ygF9~at; zm+N$KxBNO`aTjW-Zdu{votV;Cv2D&jRmC+?45{li>!GUbDo$V$ImMrB&)%~nP9rY* z$Czi8NkTJ%@^D0bn2*K^EDo;re0o=)y|LM~Mxi^5D4Ny(INLL-yk*7aS}sgB^tkY} zII|K2^|&}3a=U#pB4Iq~Yd?4IfbFt>D+r1a(()levt*k6L{HoEX0en6x1*u>msIG{ zH<7lA__UklQk89JD%eHzl+nuyA(8TjMt>;hq4qGrJjYCEAUWsbB}p`s+{zwVQe}du z`o5bG8;SHsiG8*)_TvByVJ?7lQ58;bhC{_q)$sxWg`YV6FFp)aYzKLG7%2qhN%}~a zlQd7ppHuWo<-;mCTGoNW7K~ft5eo6r_p%=%{oTe(Bu}!86v6@^%$WFe0+GgmSKsKg z-ks0M&Bt~J-%J%6Kt3^V!rVwaS4pq|cZL7bFnyY{q=&697g-Lt+XI>vE4&$*sYv$k zJsCSHGRpn7P#~Y}mZ4%h%nYqjXV{9mmekP{)J;oe*%EW?8^I%s{Aj5UfXwg+SW-|XLiyazdpMIP5%^P)L<9av@_fn>Q(_j zb-6EGRvr3!YJbt5r19rk>VyKoSCJDuYO*Rcg{v*|NlSLW#~o{3+0{{YIpWWh%-&48 zOL29$v>_DaUY2Tb<)>39KK1XrEnG^S_uJG=X;@kWcSq62HJXJ3_E%MAEe!N#q>5cr zpDmUdyR6UndiT-z>-P2E4plj?x7^-=%YsbNqDteEQPbpRpW}Bwgq&X&d(*u2`^&1= z#U;^{8YnYr(B(v@`o39_ki6R0=`L9>*c}D|`IFL4J1T{r!Y*@vy_Kf>j$tJ-oMKfO z(r{Mfm1q3JI)j227fOPpsaW4l&!pr`vEiv#@n}YniuQ};khaI&bH$w+oruT#W=l2^ z+3j#w0Z68;YV9fN$?>+lv_89k{U!6h+2PhHsnwafi;!wZ3GCJdBZIIq}r8c}|0o>`$+9EVte~HBcyf24H_d@cKG%VJv zM1Y^1E}B36NBfloCIkm@_W~4rfx76Cb;hmz}W}O17(q!G3SE*4Sca~W< z)>^aa$ZK`mN#{|}=>s5%UVc~vTd9%|t!M%BLnCVzotDKMIAB@1P_*Mbr?pczZe?d& zjvFz}ro;@1#}D&1E&auTC<*`8z|m#{4>|>T4*zH8&ILMm%z3NVG1Of1>L@W4^sG1Y*p#cUU#C5;}Z4zvom`+BZLa{=ll()v|oTd=YC1>e$?E?FBZWI^G$fEkznP_Sb0 zfLETgPt@+W@wh1u=A}{wbh9wdA{XE~*ztV@AKbVCxk*sI{M?GJAkdBGpV|{5>fIuR z5mb{(*B>C}vOF!K%q+xpQbn8rp7U5;{z)Nf*8|ufFi^ji`NSr>hfa$B{QhOBXW>G> zkBT1`gkZ{2v+k1ndaJ9Vt4`rdDHiw#POA>>HF+mcKwpy@qBFBGtpV%h3bqZQe|1j3uM(t|)uzX^chbzwi=xO+z3NE*?R$VafLdpe zzgwgOz~TAd-bnK`_=mTYBcAmqKwElX-5l=0NN8Lsn_kEmyY#i^HcLqvqm`0ySDX zL!%3I>ulaK_J>kRA=%Ywdhc!U#AfrBU@itF6$Kez=)qa;9 z-wH2RH#0KU#6R-x^+M;|HaD;tgd!h|ULV^RT zrzDFuO1S#@@^DW#gIut1IJzc@BDJ^p^|*uRB8Vzb?( zfSd?rWx8&bw5yMNYg`n+HRki4f%(F5-R-2;ksW4FUM5=IDs&6F)an&2ysVy6GP=L= zH!s_l>k5zaw(}=c2MtQP8DQl*=`wZRWl6;1X6XXa?7llGA%R~>jk3>B#?*=@dey7@ zke$&-*|2 z#hK_aC$VPvE{29RbNTy35FPWCDh+v-CS+%sr7}?2pMwv66M@5=l zD{=o*v@2ix=mhwytoYC0Hh=&BR-}?_SR|sKmOgA_jmpXGiMctf-#OL4kQB^-16hT9 z-y<6GvkpK04>e6)U}#`fdG{;OO3&xaN>YwmDJQp44+`$yy+5rwd0S!Ks;~PM`CP7E zg@=LEBf|l)tG5{5#8a`}a;7zv; zUxZb9MN20N2qy1${@H$=F)8XyAUb>VqJ`cKZXJ5D=B-zr1q#?sG{2uo)WDc>%JUxO zAS%uYfuqTktAWbu0orzV4*pIqiiWOjlVP@($BpkT9X0=We>)=t2YZzuCB`* zf)hb-w&NPgq_H3!~e8~X!ql#We9-i&zJ>QfUb8ceLBGT@C z|G!2m513?#$`g6Ii3~6>SombKy<-;^oj5n}X?i_$@=NpI!k>%2o|~oV=~lxSWi~6u zky$s>(x-e$%td)JgwxO@Am-ys^Yr<(Z}dgrcoUM$c#E8L!~1pi#Lh^^dU3$g_tvB;wLesk`UTN@+v)7R@e%YfpWyt$S`` zMl7OKR`K`__Mot7TuACiBwwe9oV7WT@}#;5!k%8z8{d4^i{@c^C~ONlL6QF0=LsZf z85JG%1w|1fynf9`xC)9YlV5^zBRfb&-~n0?am!n`p7D^0eo14xmj0>`ifY;E1IVKm zO+g>V?C!Ccd4VAUTD5mFctbvSJ+cqXO1R*_T5)<7=g~IoQ^Eng_YA0*Xc52tnlwifUshIvTe6Hop&a&~NvZ;;}RU z#;;Lm{MegTBe_>7Ui>^^W`zkZm_~5EQGHJHhlgHtl4(M__C9n6Wno^?yRG!;mbVlw zjb2tkU49*=A7E9sTe(5eZn-8Ml1&OZjeGd7kLi-T7$L0kja)5_8@VOtIo&;^e7oc| zCRH=;4hLY;ER{2+kur%n^tq)@4I4dnQ8YjCd_xfA87YK8(+WQsZzfD2U_Qs25u*4r z$%=dDp4WWTKiX>CgP(YL9$>w*15#wT0fKDf?UYy4gJxi*wW<0_G9BZp1BvgvgiPMU z`zdEy;Zc^j$!V>x9)hzaOPcnHnvSfyc~Ak`D$3ITqPt`jLRNT$psy2eFd5lWIId#nc3qq}=Qwt~;|0LWvH-ge{(Knrf7zNu52I|XV$+Q0t?+J%pP;NcpTXSE|4;9N+yS7jwZDUo4Gq0i8o6fJM(@*(q{>uLBZCURar7;;?( z4tK_f-03VwvwQjK9v$ZeyLd%<#vtAKsr+oIp*rzd>Yo`E*{?Vb z3*HANKfnzBV*}!k&pbWw%QqI3D`)m`JpGYHz{&BAx$=AYwSO1?t_=hUeXnxh{?E(& z4^c;jz(TQ(lVcxHYafuo@bqVG;8uT2fJy%6#*^AJtSlv9T z!inn((9B?e!A>)8O6hj3qvL)gUm|Noh^PALp$H$&9OJQWUzNPV(l*14g{8qskj)K{s< z61L%tY+U?lSIe;Q5Iq~5M z);u(cj*I>>?m&<-%fHnf#F)xT3~zpGOZ=j9{EtUg85AE*LmC(_ditDLfS$@BN^0#J z1<|BI)f5qH!jNs-^AlxJf_8!xj>Q1|XV&=m)%?l=zhr#O$wX{!nTR{K!e2d@i=I6g zpN8}$EAdFcbwx)MJ?>pALoOSEL0M=+{m@g9F_(VH)0h1qIE@oJ{fqolC(XEPWvFTg z!4DqXdWL%uDy?)$P9)JrYui%rT9I4au~uI4XC1w8eH^yV0=~vO@2{c~R&it*T5=xr ziLK;gm28*)+PpGN=U_>tGL40K{UnqjQ~qiAO*D$+VrVodq-+(ZM3)+9V(aneR%B(A z&&I$CT=K`q#%rAtssPpWFAko`*juowwx^82?sEL!O*x}E{!IvInsmh+$n(T;3nHJodJ$x)kx_x$8p~uS6B<2%LU7=?#Ys;!rNJ&KDqymac9Oco9 z`8-)Ao1hX%6lG-!I~*LIM;V&NtSw#?6$9zJTXfk6qN#8Yj5pT4vu}dNGJZ%l`xLAd zq4SA+ca(S0@=6^f8Dq!KkIj8fz$PWGzlusQ3>76zKbW>Ch%3)O7o(I4Uk=R28f;0& zLrk)_oTPhMmXnVNlfD*zlEt{Mg2j)nWJ(V9MThsyW3781ojzJ8Zytm7(78JyC(XtD z=9M!0BBRt;II$MEXYRJ^zyOlY>lj-#ig0(-B`D*plbN|=E8VD8|Mg-Chbm#*2d)=^X-|_@2{KL@i+?TX-`!+mafFb zfsxi#Jv-s*pcaWOzrZRONyftjj~^jkqrzEmC`1YKe5PeU5}iY?OhjJKh5GPI*Wb?_ zz&(upqSw+ybf^J&T(8USR^W6`wAbg|};NfgucE|+*&vE6LIMcgvQrS^#AmS&TgsAfy6LQZL6 zsjSzxpO{cZW!eI5!aWh*EH%E>KM(g#MvFhy zgiycJ(b5LwjMm-%uW9Umzk4XwYRmB(vi(Z8tL^u=(1nT5N{QDwjz3+3#a2)v-5V{X_@1@wJB=XRwT|d|8YfhXS30Z7g?CYjf*?t!hGZ+_ z!qTmI6HVlL$+I$@DE}@9v%DwVrIr`jEd5^FrA=6{ILb@EkW;6YSz&>~;n*R1Jbgrh zS6+3$lsRGsl#2?8i}nBzj|9!q^hXlJ*O}9aWI?AYLwA#hNr`ekr$ma80MVq#1^HPN zkpjA+LN<6Smn3Oj9LYe1fi?7vGg{kWs8%o}7gdnF8IG{aV74ho`ifCqjt^@iq0EIbipDbTxY38C! z)f3ipXtJV8vcqm}PiVBnBl3WJXZ&5Z6E787)9xe`&62b98CdLD5%Gvc^5?a3qjD+= z4(5fCPh}pKit9<*EXAtaS*YOj+H8QMB73ryS5M<`ZGRxoX z!B5cS3LoF3QRY3D+Zb@dtAO^*H-y=klk&NDDW1tOAQpS|xthF5%-5xxaTWJW?4k_2 zLbDu%?_%1Dh<1>JzCX8i1H=3g9e9~VNpdTW#)RXQRL4ZWO41NV%0 zVub9KGDjGv$mFuL2cyWF=3Eq)gf%$KPowz|?8dV-*~kX95l_8Np+4@rIU*fc3cO%iEFc7OwW5Xz(1)8z zQ;Oz_jqD+0e*Y_fj|K<8ta_?>AVeYNQ#Sh2v)0+b09<5K5NDo>W-RZB&Gn3?yCf^( zt#~}Jb{xT{A4b5A228$nu1{F6$d%47plG*iPxS!4dEarfoXeZZkL?RUg{Oa+ze=!X z;NED#-rA7wu2tyhUw93->oq+OGKGq^C&hcOq<+lasXBHIiVVQlD`Zhmb_)COtwepk z;ldyU%HoZR zy#@>QNWux-6P77eVUW{AH&dcF+8g9ofs11w^eOe-gq=VLXc#7D=;7LOril}um63bM5laN}uB>tNDn->x;85qE;J9FbB*-ov+RJhDaz`{i~Djl9n z+-}8mSe+ZdHO#7a!n6dB7SWkGWkLe1R}YfZ_$b7T@_5aH{E68&kor^O$b;M;+jQiP zR7j5*PUyK^K|&SkL$Y*B&zH|n7rc@qU9t<$c6bgaR(NYr$uDxdyzs+kJ5|mn^qE__ zr5REN6uq;E6|lI#f~gyWm}7cK7S5ITO%ju6w#NI>G96x7;+S?&WnN7`o42u1y*Xj6 zYrrkaY2g0;Gygpo{_pp1GYY>OXFcG8WlL`X%KFY)g?J6`{s;TmUjMcr2CTLB4?F-a z4}|w$I|EGnzy@R#e=Fkyu%v!F!;{_h_X_-PVZ5e_nbqv@y6 zWfwa!{oB574h}w1>4vJ2CD*{@uE#|fs+sBN1L!L>m-U99ow?#heb}o*iNCZ1aqa4# z7T#PX4_|jL?bNx>$X<;M{17^FH%wx>i^e-|LdFR_*;E`_Wb2+{Vt9&LJ4t@^vog{G zEqt16-nPuB{5aj=zE1z&>BjRWF^ouHJTX5sdC3o|UmLYVUtk8y`aVNpv<-!Dc5u z;?wDO!;&@o9iQMsZE>f9*ry>lwV> zqWl4XhiJY@A>qVLEYt$V4LLB-H;Sj&(5ELAu08SSb5n2-uVsRBW5Qf|#=*gp1CG=q za{91?kqklGsp<@bxeH*n_R=QEnttY~^e&$#5$puQ;L7)sBJ5r?GPVM+bU0F5#0fymx2tV9o*y zqB);trzg^z+606Xhe-?7A8&wdtCX;;Cf?Gxo?P_{v*sa>OOVAc>0!(JqgCYcoy6SV zM{Y0I%yt|U5O~(6kWbQ{t8SKi%+Pc_F&r8nV?D~g-CQP=uJI`Uyk7E={K{fsd{L#z3568GOB10g@usK920pXFFkXyu{W)= z9=@ewS-s@!_au^qp(FUGlH8;)(Rw0Sy##lc`t+!Pw;3d;i!GnhWJvrFKdg6 z5&)I4$}@VXC_ndwheU_R4UMmj9p9no0^s?^TGy9Qekz&?Z-m^*5E4={%F4>vN%-Bl zN-(#%Zc*Rg6&-q!>cLMldY>o&1R>u{4Q$6kFJ`Bz7Y*g1sN(U*kEZ>yi&ZK0VR$dm z476;n%`R&4CHL6Bwj~cVM40W!fy5(vW~j@~c# zHa7pTIBRI=q(uZE9AQd?$dDIiT$qdmYr_-F`mO$fgTVa2J#KhmRzSY5FNh;YkljZ$ zlt#IL)>=?x$D$6ftw5oCQw<1Sr-Xnd$+j;3cI~{(zBB*l zLjfg+9$9bQ`KC>5zgr0XN1K5Ab8KjY0oW9rz+2^qbsb1(|Lxx{0VJFON(Eq_0m^`6 zGauE^$@Cw|Wj_TVxoqJ7_eFES^Oq}}*+r?wzkI_wluByFe*W?P@{pU9)xQ7D1^FxR z0{i|-`&slo?DOAl{*U-~N(WU<~qOPZ`Kl2|*?XHAz! z^lGW*hk%ML zwri+m*D3HA>eP9qf}VGmO7Q2rX(^C<213Y7cICHWbzCJJj{f09xP7B3#xZLfdK|B$ zP4-u8G#y9l(7dF0qian9x+HEn$4E^3bf1VixIWZIM z)G+QiD4m5SqY&y-<_Irhl1C(4j#a?c^M?6AN-fIhmfrESnBx(e-Dy~EB^zi_>c{i3 zr_R?*gQPXqWWtA}^`MFqpEHusRnwKk(TC!PCS+?L;$ro;oCcx-6TF?dw_92>X4Wt% zw|elw?bzJN{7WOa=EZCn1)n<+V~pPa9bp4%wog0@8E9$KB#rSeC8|*CzywV62rU_49L=SoN`5&%1t&5JHB~ zcb%W~h^Tf``6;Rc0xoxlBHP$tTmGW9S4g7=6RugVY}uT0P47=ypH_vR`cUKyZ`6R) zn`NKeGM8_@h%w9yFw5;^OwpRN@{Sqa9mCAeOp#nbJ|5?Jjp@)Z((8zd9K*Gp7DlD{ z`t+5V@b&=ODfU`QlNB*jIwF$RhePm2x~4rX%^s>0ZWg#%+a31#Zb(GdD3+N^60pQY z#%S;t?pp%|7(D^art!OG2f>Jxs}x%FT~P#Uj>WF2!HO|ZFD z+c~3D*j;!$z&XIv`DmkeJT6hwT|GtT;vz(|MqU`2IaZ11nxHfEjJz(!*TWOjk05dl zbz+k>uWt!&FaK%B)l)0&?vR9Ov^sq&N-(!S+;O(*k)hY3A_k-qQA}!l_S7`c zEMtf;*5WpZ0fn<<>l7cLjOao%Nv;+smtmYP^Js`M^kRJ1dBt!URGk~tCYw>}TT5sX zI$%LH7vjxo>q=Cz>scCxn#5de+597PCd0%K7kbu!b9H>6HD}!<@mj@pTLBPIo%&L) zv&d_1^rQ|qQ9tpwwBO4zkd?HKW<1`>=XOUvGmT~Qc=<_I&yyz#2RFtO*yedx|D*}4 zNk>r#6;jMLDWQS23i$8KrDgu(@z9Gt;zF-xKs~_A*7YF8>tLRKl=xY7f1{Uup~Y6t zVtYmIXV@x|D*P4xZN}|s9@yU(3woDK9ZovQAo4D~j)-t>Vn zGRErTo2xW6WQTpezutgs*0iVrEy3ZadCA)*0khqevV;>g8f;~9A;@VPNfX*|oVz}v zF2ix|pGoo`>CEgV9vKFL#)x1)Y3@%NsO-KQ&4sgGT=m~C)x^-&Z%U~mrdJjdT?-b| z!8Hd{0vTYR$(#xo_RxIFqT0I_xn)YnNweI|z5xmpy%Jy7Ze}57MsmFbyVjGZrt!%w zY0>$$>r4um_pTAxkRNMqUc}No6!k}m>PDcV*m8!GF{`mhV9ay zD4k0BzUfY=n%k!7*1lPG=a1^Ai1;x?^AJ8Rt1Fo?`nKuRPV}UgZhpE!7A*+>k%*uvPh-rLKLyM#D^Uu{iQykR%8?fiV+P|`SV!E zIaHxtpb#?eLnF;tOLZjV-gFN*}dO5np(ar@@I=$v4yn1>BjtkvxY(q)Ab3M?C}W?ZIwumGs9 zkKeq?Sn{bL`8GA(tfHi5o8{fDNJFTS=eT@3sjs?pLqq?rd*QOT|Nit6D#}dWyQ&iw7UyhGOKRE^sr_)M z8?c^n_3>{hjpb`)+UdHC-e&h;_?heb-aYsUb@fjt`!3z~dF|T_gco^4^OP;U|E|HF z{-(j+KW_c(M^gGf%L%|B$?@Gp`TfiXzj6G=@on9~aoEIpN%QnKE|=oJ1%TA$#gEhf z^L`I)@?2DXH+$!LE2xNKikx2C@yM||a0m;`zh2oZsZU<$aKBQX{TIjXpMt)jJ0qph z`kiWV(X1Ct%l7!iH|}2tCXHUOhg^FJ>{$Jx(VO~^j-jj70w)Je!q>4CNOEGS0w@}) zN%YAsaGM{tSz4xOY%BLaqcy@D!TFZ09Z zm)yp>iKl~=^IDmNOjJ>3HKosc!$p^*VM>!cspnM2It|{zkBbsPf@p>5AH#h63Pl#m0mVtxs6A3JhZ0JR?*YkH`(mo0n>2D1h6UW znbzH~#=6??8iVsJzsGnyH0 zEoP%jFKU{`YI*oS&wKguRZaAsVWo`%v{!z9{ESkio5!^8p)QyoGc08Ts-pNs2o{%F z)U&>mK&o0ANdd3NTcZ78k5n2y+$l+CtH^+;5`#_t1!vp?qis`i&@7dssj+_5b2SaC zO{j&PC_1XRtOw#!fXg1Zpo&T~NbgQiXSsVU+^}P9L<;iDLgh{K3{J6n>L+d)SEb`L z>PQxyrmMB_or+$U%&8c2s?~f`Qn;3kH?Dm)wZ|jF!u%ywQ$;`;?%;{DAXdLH_n}*N z({%Z#MQS8NBPgk& zq84Npe$C;{TooLWlKmczrUZE)1VElNk=kQl$k&GnBo%wqiKH%=oZRZ4!Z?;ocV7yr zb7|UQ8A-6%d0wJ1oQJ@_s*Ww9SV~VhUp-uNP_iZe599wFLO=JoZU1H1`Tr<;&#3q z(n3+GQl%q7s)`8eyqWts@A+_^`?;=j@^S6GvUXW}mH+=&D*hee@jV;Scw633aQ0nM zs*!}uOv)7WIT#(Dr2qE;_#`%U&ZVwcUO3qr^KAarGwTIKaC@Ocd2P z)M>dZPbMD)t0S*os{gdusrL)de1HS!yXsKOo{QV-iqS9hecB{l;Xjoctp*b3EYl#dkc z8f!%hm(OZbHlR0shM&`>O4|y*2zAQZI4838%vh%|fzLV_(rkX?RL;cO1A`!7y@F5);Pa<%58-lvel&5GC&nyinBU3A+mt-j6=436L7 z?n!mlyCa%yzu-7;CR@p2>_5EHeK@}=aohRgdLC%19lP*-M}Eu`AUNOgWyu#M@@A<0 zX#)Mz-dPs!jeu3RB{y6}i+J@p*#)rQKAyE1sGUwIg|j+zv_j;*a*dXr=)1z~s72lt zGw=dl2VB>vK7*2Q^S#YonoF-RFut+ese@F9+$7=`hLr8@OMtSo=bick+ z%;KTBNA)?MwVDGhXx};G563qO@^#KjYzs~n<+y;_-u7Gc3MmP_o*@08%%u!NtxvDS z=g3Qhw0*nq zRlm`2@FQnBlh(PjvsM~-;79+D5Pj|`8oBc?q5x!h$SdvS`CAS!vn%iWUfrgeG`Ehd z`y$9>ec|YPwnEFIVOI?p>vw<4znCqNa@Y5Kzdhly3Ipf7sr)+>4zgsss^p6`jeM00 z*8bxLtq6-_xYMnCMh2gSl+Qa2dnjz?4}VsyU=dMQQhZ%6*Rr(+P#xSZt)DF>_B}Aciz)G2nOupGk=3jo&DUk|2@qqjgiaIO zv?#Ox#HG1{FQku2e<niqW`#)-<3JNv2A{gKU zLAo|)uLB)X_Ms@yiue#yYyz}N0ZgtCLKX+SBed`67p77!x3hs8CdIJU7jYCSa;>T<3@eI zJ@7q_Dj|(OT}5zhM1FlWl#8Es8p;PWC{;|qa_WAq*i(Jr(4YTdxc)`uM~!DKZxXgB zAh?~$J!h!ww$Fb*_?J?hGrrvWf~HbPXq4&lkt)==rg~0bK$UgsCVQx_@q?_hf-?%K zF0uzq^CKn7ZLvw32$jh&uOpEF`SK~PZTfHNZ?R=#1F_&NMOH!G*UMAqk@hGmK+`OB z2XmvLXxv;Nkr~$3rR484ZBTk+87g@TfBr%D=D7{m<>;{uXN+C6yGCtR=|v=Sszw4H zV5w5Ya7??SD3H$X^VTBUMLtppTKVppKR6I;T>qWscE;SbKHFq47t)*(ETXBslGb`Z zkbhe;F*A6|aWm&K-(9{4D#ZA=8>C(%1+XQNuBaMCltd;W!Du&aw&*oGLI{nufqg{L zX1__XTXnYxhWrr+L%-_@$S%XKqt}z71Ft%kYhhBbw~pQETG6(-6*E5}N4GqWj7_i_ zYxd8W@p(=R@twgz>JFOJhVvOR*FB%{C={E@?K;ikB1H_T+3o8O9;yifUdL+X0MaQ$Gm_-h`|+Z5;gqrD*;&ZriK z@UCPQJ&{{6Of4;iz_YH*>}7DBEPaPP)_v=sdL}=ghm@jf=n*C+0h{58N25W$%>($N z+9LLQ%#u_qxY@Y7p=*#-p;uL3}oRJ2Ak}JIvM@V&3ao z{%aB@XtG?v0_xg)UOF+I8u+4U>hA;3ud-*bb|;b^9f@285IH{2uhzT^nQRQC-AWDQ ztu%8x5=@5~S{(%L;UD843_q>-niUrNxvh^jm99Cb(84LnuYVHw)Z-=NriKzf11F{t zpa!n?* zkHGx>+}EC%`x2Xuj(e+qVvd#aFU{6qXBtJr)O_&hE%|j&+Vml6nvz0Sx~&@kP2qU(W-^luv}As{Z)Vme96DO~a8x8e@h+ zBItIF*&EyG*NJNqni@YhaBn;Z-1hX~pZ~oVi%NVy-~P9GAkLrq z|GNnOv+TXbIioR*2_x>pu}Q*lyJwwkekLG}?pPsGB5z?;{d-zi`l>*J{9xR^A`S^? zN<5^$nF};*R~#8npp)yL3B2Y8X{%>_92nQDIskjjlbPiGBZRW9XRH;zY>y>z?6ORvuoWLo>~FRhbs&gRVb_79`cT}Ce`_) z<9^zjH$FNP^}Q*5-Mwgq%mAN@8aS){ikm6nabYYwgS z$etOYF-Q;$wxmkqnoD47R!}eQiJ>ShMaGQK@wjEhAc4@-Woc(9ojop;_0wpS6$#(^ zlU0}D)s=f$tLJi^*>_{yS&ESi?F$zbQdSift27L^H%x(p_dj{<77(h6c!C1Tpk3;D zUo36_oAxOp>nu1J+GI30Y_!!#V~k#E$h=-uP$j#lYhSpGk-od5j=skjPq0y;T2%oM zl9I_Kw}qMr+$8@C4`O=KzJQ#`8dawS$O1HHJzqNKH<+#7*44aA>^!~}ft_}qDD}d! zoO)auF7FCb1&(3DvwEak&t-u9!vg z!63a+#n5HGeNfKl zU33HP$HACaAj5iw7T}S6xX`uQ5$v!m;W^)agOTqln_;AMCv)d#*?x8KrAAj1&J5(UVqLJS zo(_d5qB_7d6UMe?5L^HhcJZ7G{Wh;qnybL=FeS|O)*G5#o-;Z3elW(pd1?Fa170uS zsN*{pM^Pz7tZafAY5tctiED0pN$vqCsmM0A+NtS9#$q~+J{!Fz#-^-b}C1wzMEkN9@mSxE@N7q&<`GS(~-41oSB$pz=- z_4!dTBIZbn$R|Wg{cGNwa@ReHfCAt3kC0A)lX+5$Ur7TVqN&kbC0B!6TbgOE{M1xt zJ?9?~uvn%DHn9mkFUQsP;rHdjJ|z+s2m@$b!92aNk=RvRwCH|DVx8fR(O;BKSm`hP zkOSO(sAnEero6<4&m^CvW(N4(6N1?ASc`6p-Lc?i(z&0XPFYYKuzPsCV&1k8R zF>fC-?t&Zqq4BoE>%bwA;L3ma{Vx>xKj`uUBA~hS0TIyr!2gDn|Az{qqMsq9|3y2D z@TsZZ-_|Q8M-WEax4prcGa^LG|5}uY5`PE&ja`vG-1Uz$n(dk2#DDuIF8ByzE3FUx z^S?kYztOE)F+HsZF@eeEDIK8%N{lZk=SbLYFAt0MN5PugjB3u1N$Y0+_JHPJOpu6n zllUZmOm*$`f#d)E;tu~C6o2iXMbE*XL@CXGoe^Q=<@TqOqRhUlMA$!DPBa1kOO$>* z{_n#I>Oc8r*C^pNG1(>^R1*P=Ol62bEyu)4Dj`ATHxVzIb4F>Ly^`l5D=#f4Hx zz$|O07;OG%X4|n8AOSzS^flvAVP0lG8sclPn6Q4R@Z!Prs)aGvki3<7^X(}5d+Xya zYRMjr4$;^g3){5)6AGzmOX2c9t>G!f?bPFW!~RQ`m%hw*wwzBiTk&@)hogJwBDbV~ zkrTBA^A<}gXRofv6bG~~s|X61W)$+wUN?DsEzbG@bTQ7PtiwS8&SEiw73JrV4l!QS zlDlw)1sZq2|A7g&)qJ28`o~2rWGzgfR}|1+vK-j1@lElJMuJj9e72jyDrBx430WJq zbsHZDaq4}>9`bgenPo)7v? zTCRE9I#*{ghzx+S9P4{ov%VQiV}MXd&& zN;xMxPoF3aPLOyLocKw;BHjJg+#Ea>p6vWeMOtIQu;x9_{&NNDR&So6s(IS-2SPde z{o3rQ)J5pIggXit&rn;@> z@-hA2Jo~^NOh}|u%Du>~h0VZm#}vOb7oFQ?xm5b}N4pF>u&7`%D|IR3 zGN-|~q0ORio+ek=WbjQZ`aVFbUwOb!`w#b2rNr}_wRwFK@={b{(~L;-!Q*^dbLK-n zS4ozKOCzg&;Oy~r!p<+aZTR#7EOcZ+A+svp+7&66^8yUMFzd}i*0Lf$h9y1hx+yya zr{b?3lzdxOC4mYqDeiX`Ae3ttuJT#4h@0pAHAni3UYPgC`(`@@Z`SI*O`pdoOzj{# zha6DVeqs8F8g)kZ3i1tvv*F^eUNcSBYqdNrV-38DFLVS?k-Y-7`1Q5m$|)^4f#u}i zTPV>+tn&8$u)mix*BV@at>sXU5S>>quhW0gQx+I#mE5%4bBNdkKnB8=kkqubwM9{O8AkNJBTF=+ zzB{_`^`uAQgJw`aW+q(&Z36gpK%g5IzhcDJ8WLPCJW;)JwUV%QUGE(G9AoeB?2@J1 z@9+etRMM-zgj$3c(7NWsZA3}Mg%OsgVUz4=PQIe6tIkTc>>GjefTL}9!J_?GP0MGm z_LtsTZKaG($vM(FC;zeG;;parsAT@WhMBvt*k)gTVCRQav zx`^qZoQ^~!xf{#kd^{tQ+&%DTl$d@soeFUeFw>;fG>QI@R-t@|3=FJ1H z+c1B~`?Xem(EU%9d%Z&Z8M7Zpq+>i8`?$FKH{()l3G2M_4G8P}H)bHsPB`jD5hq#uM~4Iz6Y`VA=P=&%ghB0+gWW8-Nsn zT}np9zZJ0VoB{kMaR&v3#@y9E@Ca%x@{CazL33meq=@F;OAE&jB#Ts_afRvj5ECv^ zV>2OEDag*8TQ+F4;?b@&<|}p2k1vLb0U_5#`TDi<^ReA(Q}_|e&SI0UC~LLbZQzO{ zl>y1R?S`{|JbQ3eHszI6#m&-T*?5CdFNdckd*wqD1img8V~Cwygcx^40bYGFgQ9?( z17@U^OV*~Y&Nw!Z>O*o3cznhsrL^thCvp;?*M&Sx(f4%qcj!7|7Lr6Bti|?Z3S253 z;I8T_DrCSoW{qK7SU5Q<(<3tl1L9si7^l>^_&JS#Hankic~QN-hdI6AEDDRaOYl#o zCg2zjPOI8we;>e!$V%wJ(@Elyu2q@uGhh9pi5BR3-#fd;cP7VtK~t6sLPjB-Dr0AR zqSN$&{n<(+9n<5hC0o}G@Fp#=9(}Jt=hyz5KBLu0eb1{|Z9XsL*eZqYz}kyP3&+lu z%>SC`^Bn1~NCk4+J4+u+KQD*twPNDuT+7KOMP^Z6d$cV<$G6I{Y=7e5!IFG4vLJjF z5{FGVT&LAjqpV$jqBd7+FtV!iP(58Dh?lD*>)ixs)3{Lzc{}|4J)@#-d`zUq>&6&X zdV?%GQdQ(spPFxtepkrONcU^eV#5M1Q1dqK_{xBRPD`ae$aeB9{V_v0Ing0qDrSO< z`dtdFfrlGv!wqB|6p-YUrOVR!icsAlCiuB`Qf@%$AYIwM#@QuDqI3m1$2P0vvDKfA zx~B#z4TGTgP)~84b>=9?IwwL)tKW|1>S^Ei-X(!uqWOzR=$0bdTtV9YH6$9itaCKfO_yJaZ z(KuIl4o(A4RoaJ$q5Ifc0lOLy^9Y=8NNRfJ_mm-@K8GvpBZDb|6yfzt?_0s(53!^v(@LWhj3?<131Df+)d-L-{R~i{Ega^O50Y9a7uAY#AG}#$9ZD z{sc`kDh4+Kt25EGnW_;Fx1nN5-r1O^@B4OP!BA>nwnUlvYP#C*g=qShUH)(ifn<=! zz?)?a%%93@=&mcmx(4dtb4_3YfBf1x&ie%R=}+OH z>>|q#)t~2@ZA6!j5S=@W)*}>)B%_jntD6=v`K~?1Ikh~Xh0X3krIiS+_Lo=1Gr4H;E&r?U( zPPIdLP2I1QPK+|nI+J~RxOAabVLvNw*FyQ#&-&RAI3@|J+l`7D1-3>--QTJV>WgF7 zb2W`0|!XQ#^ZCwT^IKGq-heBb4>{<~ekNd_u zq)Jk$Zu)lqWz88id~Y$!bxY#&A#Pc!gk(t`!)v|{+e(Xu)Lmf$>yK_+iLwvR>)W^? z%IES#!p~74F4(1eBg+SN0ZA;AwCEvMR|o3TDNRW~p%P4nDA^{^qw7@Qju_8s7h;+01*H8cYpJsvo`*PFo!BcPc3|GAW z4Bp#l$p7Rw&_6ALlYAOIc*xH4kqGPbuV4O+LqXYgbsZ74cQI_~m%B`@os@#dxD@EN zOUHJ<{x2y|*l*H*9)we1BCede%s+&QM{fN5fu5r?X}ru3HhNBL?2WSsp*|D;dmT#o zQ54)87*Y{`3!CQPZ~J24x?{Ws1wqqU!W7k8o%qLgvwD%R9(j^kX{@%ucDNnh*%C$f z-KJ}YQCg#+5RxEU;ZqWA!HKPfFIL_4?(o+BH1s{h=JL=tg!!|NdG0k7xdT=!Zms`V-$DOLY@%3jONdLVcEJX4H1mkx^c1acjur_4@v|i-lgZ z*Mox66k$)4qSd~8%;8F()+gq4wQ|H^(jVn>sx<7^EI6@mopEfr3Har{#iBClSV!^$ z**-Z?sUYX)nXjpaCf$9T(J`3R6u(m^HPYEv=+i1G=ryf+U;D2QCk*5pt!D1P; zW+&NpIi@Lr9vmmuiVbzk=lQ%5-~H4P=YAF!>I;MgdEqIH(7CPqNLLD^U&@?SKo2L{ z$3M}7-DKPHEKI5tBeifjp?P2Brh;sn8jipM)_31vQWDhjc6SB7-S{+5-FGJt@mcZd z4i@5NPoXCsD3Wi~a!|*=Uk~~!Balv|lV_GQZi+A*o-J|IPVt@R+z`e`spdsPF1(>y95ax5?tY|ahoxbalC zK6%YL6Y$ZNk2=6BHFd!*0Ec_f4lGAOtAORSKrnT7G}`8%3YeDz01qpNbitaY)@f#n z2(Vp7+I#1Y!XL`fvBP8;zI&UhZN$`e&bxSb@-9+ z7qqj|!nVpAYthqU)SI1~h8jw^%7zb>O-#=#d{!GTcL2#tf>pDLk|O(tIdmzCZ9$

        b!4`}8=45Oh)GGE1{T-NqR>hU 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 OXCVGW>97BN`~Lxn`22nV literal 0 HcmV?d00001 -- Gitee From deeb93ed553db78df6fb7e69e81265655a728424 Mon Sep 17 00:00:00 2001 From: xujianxie Date: Wed, 1 Jun 2022 00:32:36 +0000 Subject: [PATCH 1714/1965] =?UTF-8?q?update=20src/main/java/io/jboot/codeg?= =?UTF-8?q?en/model/JbootModelGenerator.java.=20=20=20=20=20//=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E7=99=BD=E5=90=8D=E5=8D=95=E5=8A=9F=E8=83=BD=20=20=20?= =?UTF-8?q?=20=20public=20JbootModelGenerator=20addWhitelist(String...=20t?= =?UTF-8?q?ableNames)=20{=20=20=20=20=20=20=20=20=20if=20(tableNames=20!?= =?UTF-8?q?=3D=20null)=20{=20=20=20=20=20=20=20=20=20=20=20=20=20this.meta?= =?UTF-8?q?Builder.addWhitelist(tableNames);=20=20=20=20=20=20=20=20=20}?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20return=20this;=20=20=20=20=20}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/codegen/model/JbootModelGenerator.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/codegen/model/JbootModelGenerator.java b/src/main/java/io/jboot/codegen/model/JbootModelGenerator.java index 41a95958..3b9ac67a 100644 --- a/src/main/java/io/jboot/codegen/model/JbootModelGenerator.java +++ b/src/main/java/io/jboot/codegen/model/JbootModelGenerator.java @@ -55,6 +55,11 @@ public class JbootModelGenerator extends ModelGenerator { metaBuilder.setGenerateRemarks(generateRemarks); return this; } - - + //增加白名单功能 + public JbootModelGenerator addWhitelist(String... tableNames) { + if (tableNames != null) { + this.metaBuilder.addWhitelist(tableNames); + } + return this; + } } -- Gitee From 072d5257bfdbe939b111f62c4e1afac4eeb591e3 Mon Sep 17 00:00:00 2001 From: xujianxie Date: Wed, 1 Jun 2022 00:33:43 +0000 Subject: [PATCH 1715/1965] =?UTF-8?q?update=20src/main/java/io/jboot/codeg?= =?UTF-8?q?en/model/JbootBaseModelGenerator.java.=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=99=BD=E5=90=8D=E5=8D=95=20=20=20=20=20public=20JbootBaseMod?= =?UTF-8?q?elGenerator=20addWhitelist(String...=20tableNames)=20{=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20if=20(tableNames=20!=3D=20null)=20{=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20this.metaBuilder.addWhitelist(tab?= =?UTF-8?q?leNames);=20=20=20=20=20=20=20=20=20}=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20return=20this;=20=20=20=20=20}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/codegen/model/JbootBaseModelGenerator.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/codegen/model/JbootBaseModelGenerator.java b/src/main/java/io/jboot/codegen/model/JbootBaseModelGenerator.java index f54d0bc2..78cae4b8 100644 --- a/src/main/java/io/jboot/codegen/model/JbootBaseModelGenerator.java +++ b/src/main/java/io/jboot/codegen/model/JbootBaseModelGenerator.java @@ -59,6 +59,11 @@ public class JbootBaseModelGenerator extends BaseModelGenerator { metaBuilder.setGenerateRemarks(generateRemarks); return this; } - + public JbootBaseModelGenerator addWhitelist(String... tableNames) { + if (tableNames != null) { + this.metaBuilder.addWhitelist(tableNames); + } + return this; + } } -- Gitee From 306fa7572ab6a955279101e24283d0b8c2933eec Mon Sep 17 00:00:00 2001 From: xujianxie Date: Wed, 1 Jun 2022 00:35:02 +0000 Subject: [PATCH 1716/1965] =?UTF-8?q?update=20src/main/java/io/jboot/codeg?= =?UTF-8?q?en/service/JbootServiceInterfaceGenerator.java.=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E7=99=BD=E5=90=8D=E5=8D=95=E5=8A=9F=E8=83=BD=20=20=20?= =?UTF-8?q?=20=20public=20JbootServiceInterfaceGenerator=20addWhitelist(St?= =?UTF-8?q?ring...=20tableNames)=20{=20=20=20=20=20=20=20=20=20if=20(table?= =?UTF-8?q?Names=20!=3D=20null)=20{=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?this.metaBuilder.addWhitelist(tableNames);=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20}=20=20=20=20=20=20=20=20=20return=20this;=20=20=20=20=20?= =?UTF-8?q?}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codegen/service/JbootServiceInterfaceGenerator.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/codegen/service/JbootServiceInterfaceGenerator.java b/src/main/java/io/jboot/codegen/service/JbootServiceInterfaceGenerator.java index 87771e2f..4f7d5d6c 100644 --- a/src/main/java/io/jboot/codegen/service/JbootServiceInterfaceGenerator.java +++ b/src/main/java/io/jboot/codegen/service/JbootServiceInterfaceGenerator.java @@ -148,6 +148,11 @@ public class JbootServiceInterfaceGenerator extends BaseModelGenerator { osw.write(tableMeta.baseModelContent); } } - + public JbootServiceInterfaceGenerator addWhitelist(String... tableNames) { + if (tableNames != null) { + this.metaBuilder.addWhitelist(tableNames); + } + return this; + } } -- Gitee From be70813a49971e3265309f1b47029122849dee81 Mon Sep 17 00:00:00 2001 From: xujianxie Date: Wed, 1 Jun 2022 00:36:55 +0000 Subject: [PATCH 1717/1965] =?UTF-8?q?update=20src/main/java/io/jboot/codeg?= =?UTF-8?q?en/service/JbootServiceImplGenerator.java.=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=99=BD=E5=90=8D=E5=8D=95=E5=8A=9F=E8=83=BD=20=20=20=20=20pub?= =?UTF-8?q?lic=20JbootServiceImplGenerator=20addWhitelist(String...=20tabl?= =?UTF-8?q?eNames)=20{=20=20=20=20=20=20=20=20=20if=20(tableNames=20!=3D?= =?UTF-8?q?=20null)=20{=20=20=20=20=20=20=20=20=20=20=20=20=20this.metaBui?= =?UTF-8?q?lder.addWhitelist(tableNames);=20=20=20=20=20=20=20=20=20}=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20return=20this;=20=20=20=20=20}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/codegen/service/JbootServiceImplGenerator.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java b/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java index 9bed6242..d1792d15 100644 --- a/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java +++ b/src/main/java/io/jboot/codegen/service/JbootServiceImplGenerator.java @@ -89,7 +89,13 @@ public class JbootServiceImplGenerator { return this; } - + public JbootServiceImplGenerator addWhitelist(String... tableNames) { + if (tableNames != null) { + this.metaBuilder.addWhitelist(tableNames); + } + return this; + } + public JbootServiceImplGenerator setGenerateRemarks(boolean generateRemarks) { metaBuilder.setGenerateRemarks(generateRemarks); return this; -- Gitee From 6ebb261a897817883ebe30f881b1be9cab758572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 1 Jun 2022 16:46:30 +0800 Subject: [PATCH 1718/1965] v3.15.3 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index a7a6e140..dcbc0e09 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.15.2 + 3.15.3 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index f4379546..f6bbfc06 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.15.2 + 3.15.3 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 4c031772..7cc94ea5 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.15.2 + 3.15.3 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.15.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.15.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index cb0d2f8b..78975246 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.15.2', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.15.3', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.2/jboot-3.15.2.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.3/jboot-3.15.3.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index d64b3923..6056f8a8 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.15.2 + 3.15.3 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 8daff9fc..133bda8c 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.15.2"; + public static String VERSION = "3.15.3"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 8237a789e011f201001bb6f7bbdb48bb00d40954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 1 Jun 2022 16:46:34 +0800 Subject: [PATCH 1719/1965] v3.15.3 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ pom.xml | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3f96cefa..b01fc750 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.15.2 + 3.15.3 ``` diff --git a/changes.txt b/changes.txt index f456812c..dd196150 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.15.3: +新增:代码生成器新增 白名单 配置的支持,感谢 @xujianxie +优化:升级 FastJson 到最新版本 + + + jboot v3.15.2: 修复:DAO 配置了 loadColumns 时又配置了 distinct 时,无法获得正确结果的问题 优化:优化 Columns.toString() 方法,防止在开发工具 debug 下显示 "null" 的问题 diff --git a/pom.xml b/pom.xml index 715bbc72..1ccf1dee 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.2 + 3.15.3 jar jboot @@ -53,7 +53,7 @@ 1.7.36 2.57 - 1.2.80 + 1.2.83 31.1-jre 2.2.5 3.8.0 -- Gitee From 39833a468ad2a85edbb93bd4794f8df13f73680e Mon Sep 17 00:00:00 2001 From: zhangxn8 <840267340@qq.com> Date: Sat, 11 Jun 2022 13:15:20 +0800 Subject: [PATCH 1720/1965] =?UTF-8?q?1=E3=80=81=E5=8D=87=E7=BA=A7seata?= =?UTF-8?q?=E5=88=B0=E6=9C=80=E6=96=B0=E7=89=88=E6=9C=AC1.5.1=202=E3=80=81?= =?UTF-8?q?seata=E7=9A=84TCC=E6=A8=A1=E5=BC=8F=E6=A0=B9=E6=8D=AE=E6=9C=80?= =?UTF-8?q?=E6=96=B0=E7=89=88=E6=9C=AC=E4=BF=AE=E6=94=B9=E4=BC=98=E5=8C=96?= =?UTF-8?q?=EF=BC=9A=203=E3=80=81TCC=E6=96=B0=E5=A2=9E=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=EF=BC=8C=E4=BD=BF=E7=94=A8tcc=5Ffence=5Flog?= =?UTF-8?q?=E8=A1=A8=E5=8D=95=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../SeataGlobalTransactionHandler.java | 5 +- .../SeataGlobalTransactionalInterceptor.java | 2 +- .../seata/tcc/ActionInterceptorHandler.java | 69 +++- .../seata/tcc/JbootTCCResourceManager.java | 242 +++++++++++++ .../support/seata/tcc/TCCFenceHandler.java | 325 ++++++++++++++++++ .../seata/io.seata.core.model.ResourceManager | 1 + .../io/jboot/test/seata/commons/fescar_.sql | 12 + .../service/impl/TccActionOneServiceImpl.java | 2 +- .../seata/starter/AccountApplicaiton.java | 2 +- .../test/seata/starter/WebApplication.java | 2 +- 11 files changed, 652 insertions(+), 12 deletions(-) create mode 100644 src/main/java/io/jboot/support/seata/tcc/JbootTCCResourceManager.java create mode 100644 src/main/java/io/jboot/support/seata/tcc/TCCFenceHandler.java create mode 100644 src/main/resources/META-INF/seata/io.seata.core.model.ResourceManager diff --git a/pom.xml b/pom.xml index 1ccf1dee..e028ca85 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ 1.8.3 2.7.15 3.13.2.Final - 1.4.2 + 1.5.1 1.1.8 4.2.9 diff --git a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionHandler.java b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionHandler.java index 08ff6872..ed1a5d3a 100644 --- a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionHandler.java +++ b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionHandler.java @@ -91,7 +91,10 @@ public class SeataGlobalTransactionHandler { throw e.getCause(); case RollbackFailure: failureHandler.onRollbackFailure(e.getTransaction(), e.getCause()); - throw e.getCause(); + throw e.getCause(); + case RollbackRetrying: + failureHandler.onRollbackRetrying(e.getTransaction(), e.getOriginalException()); + throw e.getOriginalException(); default: throw new ShouldNeverHappenException("Unknown TransactionalExecutor.Code: " + code); diff --git a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java index 1147b07b..847e151e 100644 --- a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java +++ b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java @@ -72,7 +72,7 @@ public class SeataGlobalTransactionalInterceptor implements Interceptor { @Override public GlobalLockConfig getGlobalLockConfig() { GlobalLockConfig config = new GlobalLockConfig(); - config.setLockRetryInternal(globalLockAnno.lockRetryInternal()); + config.setLockRetryInterval(globalLockAnno.lockRetryInternal()); config.setLockRetryTimes(globalLockAnno.lockRetryTimes()); return config; } diff --git a/src/main/java/io/jboot/support/seata/tcc/ActionInterceptorHandler.java b/src/main/java/io/jboot/support/seata/tcc/ActionInterceptorHandler.java index 8c16211e..12cfe3c1 100644 --- a/src/main/java/io/jboot/support/seata/tcc/ActionInterceptorHandler.java +++ b/src/main/java/io/jboot/support/seata/tcc/ActionInterceptorHandler.java @@ -26,8 +26,12 @@ import io.seata.core.model.BranchType; import io.seata.rm.DefaultResourceManager; import io.seata.rm.tcc.TCCResource; import io.seata.rm.tcc.api.BusinessActionContext; +import io.seata.rm.tcc.api.BusinessActionContextParameter; +import io.seata.rm.tcc.api.BusinessActionContextUtil; import io.seata.rm.tcc.api.TwoPhaseBusinessAction; +import io.seata.rm.tcc.interceptor.ActionContextUtil; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.HashMap; @@ -40,7 +44,6 @@ import java.util.Map; * @author zhangsen/菜农 commit: https://gitee.com/fuhai/jboot/commit/55564bfd9e6eebfc39263291d89592cd16f77498 */ public class ActionInterceptorHandler { - private static final Log LOGGER = Log.getLog(TccActionInterceptor.class); /** @@ -50,10 +53,9 @@ public class ActionInterceptorHandler { * @param arguments the arguments * @param businessAction the business action * @return map map - * @throws Throwable the throwable */ public void proceed(Method method, Object[] arguments, String xid, TwoPhaseBusinessAction businessAction, - Invocation invocation) { + Invocation invocation) { //TCC name String actionName = businessAction.name(); @@ -79,8 +81,30 @@ public class ActionInterceptorHandler { e.printStackTrace(); } actionContext.setBranchId(branchId); - - invocation.invoke(); + // save the previous action context + BusinessActionContext previousActionContext = BusinessActionContextUtil.getContext(); + try { + //share actionContext implicitly + BusinessActionContextUtil.setContext(actionContext); + if (businessAction.useTCCFence()) { + // Use TCC Fence, and return the business result + TCCFenceHandler.prepareFence(xid, Long.valueOf(branchId), actionName); + } + invocation.invoke(); + } finally { + try { + //to report business action context finally if the actionContext.getUpdated() is true + BusinessActionContextUtil.reportContext(actionContext); + } finally { + if (previousActionContext != null) { + // recovery the previous action context + BusinessActionContextUtil.setContext(previousActionContext); + } else { + // clear the action context + BusinessActionContextUtil.clear(); + } + } + } } /** @@ -153,6 +177,7 @@ public class ActionInterceptorHandler { context.put(Constants.COMMIT_METHOD, businessAction.commitMethod()); context.put(Constants.ROLLBACK_METHOD, businessAction.rollbackMethod()); context.put(Constants.ACTION_NAME, businessAction.name()); + context.put(Constants.USE_TCC_FENCE, businessAction.useTCCFence()); } } @@ -193,10 +218,42 @@ public class ActionInterceptorHandler { tccResource.setRollbackMethod(ReflectionUtil .getMethod(interfaceClass.getClass(), businessAction.rollbackMethod(), new Class[] {BusinessActionContext.class})); + // set argsClasses + tccResource.setCommitArgsClasses(businessAction.commitArgsClasses()); + tccResource.setRollbackArgsClasses(businessAction.rollbackArgsClasses()); + // set phase two method's keys + tccResource.setPhaseTwoCommitKeys(this.getTwoPhaseArgs(tccResource.getCommitMethod(), + businessAction.commitArgsClasses())); + tccResource.setPhaseTwoRollbackKeys(this.getTwoPhaseArgs(tccResource.getRollbackMethod(), + businessAction.rollbackArgsClasses())); //registry tcc resource DefaultResourceManager.get().registerResource(tccResource); } } - + protected String[] getTwoPhaseArgs(Method method, Class[] argsClasses) { + Annotation[][] parameterAnnotations = method.getParameterAnnotations(); + String[] keys = new String[parameterAnnotations.length]; + /* + * get parameter's key + * if method's parameter list is like + * (BusinessActionContext, @BusinessActionContextParameter("a") A a, @BusinessActionContextParameter("b") B b) + * the keys will be [null, a, b] + */ + for (int i = 0; i < parameterAnnotations.length; i++) { + for (int j = 0; j < parameterAnnotations[i].length; j++) { + if (parameterAnnotations[i][j] instanceof BusinessActionContextParameter) { + BusinessActionContextParameter param = (BusinessActionContextParameter)parameterAnnotations[i][j]; + String key = ActionContextUtil.getParamNameFromAnnotation(param); + keys[i] = key; + break; + } + } + if (keys[i] == null && !(argsClasses[i].equals(BusinessActionContext.class))) { + throw new IllegalArgumentException("non-BusinessActionContext parameter should use annotation " + + "BusinessActionContextParameter"); + } + } + return keys; + } } diff --git a/src/main/java/io/jboot/support/seata/tcc/JbootTCCResourceManager.java b/src/main/java/io/jboot/support/seata/tcc/JbootTCCResourceManager.java new file mode 100644 index 00000000..c7bd105b --- /dev/null +++ b/src/main/java/io/jboot/support/seata/tcc/JbootTCCResourceManager.java @@ -0,0 +1,242 @@ +package io.jboot.support.seata.tcc; + +import com.alibaba.fastjson.JSON; +import io.seata.common.Constants; +import io.seata.common.exception.ShouldNeverHappenException; +import io.seata.common.exception.SkipCallbackWrapperException; +import io.seata.common.util.StringUtils; +import io.seata.core.exception.TransactionException; +import io.seata.core.model.BranchStatus; +import io.seata.core.model.BranchType; +import io.seata.core.model.Resource; +import io.seata.rm.AbstractResourceManager; +import io.seata.rm.tcc.TCCResource; +import io.seata.rm.tcc.TwoPhaseResult; +import io.seata.rm.tcc.api.BusinessActionContext; + +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author zhangxn + */ +public class JbootTCCResourceManager extends AbstractResourceManager { + + /** + * TCC resource cache + */ + private Map tccResourceCache = new ConcurrentHashMap<>(); + + /** + * Instantiates a new Tcc resource manager. + */ + public JbootTCCResourceManager() { + // not do anything + } + + /** + * registry TCC resource + * + * @param resource The resource to be managed. + */ + @Override + public void registerResource(Resource resource) { + TCCResource tccResource = (TCCResource)resource; + tccResourceCache.put(tccResource.getResourceId(), tccResource); + super.registerResource(tccResource); + } + + @Override + public Map getManagedResources() { + return tccResourceCache; + } + + /** + * TCC branch commit + * + * @param branchType + * @param xid Transaction id. + * @param branchId Branch id. + * @param resourceId Resource id. + * @param applicationData Application data bind with this branch. + * @return BranchStatus + * @throws TransactionException TransactionException + */ + @Override + public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId, + String applicationData) throws TransactionException { + TCCResource tccResource = (TCCResource)tccResourceCache.get(resourceId); + if (tccResource == null) { + throw new ShouldNeverHappenException(String.format("TCC resource is not exist, resourceId: %s", resourceId)); + } + Object targetTCCBean = tccResource.getTargetBean(); + Method commitMethod = tccResource.getCommitMethod(); + if (targetTCCBean == null || commitMethod == null) { + throw new ShouldNeverHappenException(String.format("TCC resource is not available, resourceId: %s", resourceId)); + } + try { + //BusinessActionContext + BusinessActionContext businessActionContext = getBusinessActionContext(xid, branchId, resourceId, + applicationData); + Object[] args = this.getTwoPhaseCommitArgs(tccResource, businessActionContext); + Object ret; + boolean result; + // add idempotent and anti hanging + if (Boolean.TRUE.equals(businessActionContext.getActionContext(Constants.USE_TCC_FENCE))) { + try { + result = TCCFenceHandler.commitFence(commitMethod, targetTCCBean, xid, branchId, args); + } catch (SkipCallbackWrapperException | UndeclaredThrowableException e) { + throw e.getCause(); + } + } else { + ret = commitMethod.invoke(targetTCCBean, null); + if (ret != null) { + if (ret instanceof TwoPhaseResult) { + result = ((TwoPhaseResult)ret).isSuccess(); + } else { + result = (boolean)ret; + } + } else { + result = true; + } + } + LOGGER.info("TCC resource commit result : {}, xid: {}, branchId: {}, resourceId: {}", result, xid, branchId, resourceId); + return result ? BranchStatus.PhaseTwo_Committed : BranchStatus.PhaseTwo_CommitFailed_Retryable; + } catch (Throwable t) { + String msg = String.format("commit TCC resource error, resourceId: %s, xid: %s.", resourceId, xid); + LOGGER.error(msg, t); + return BranchStatus.PhaseTwo_CommitFailed_Retryable; + } + } + + /** + * TCC branch rollback + * + * @param branchType the branch type + * @param xid Transaction id. + * @param branchId Branch id. + * @param resourceId Resource id. + * @param applicationData Application data bind with this branch. + * @return BranchStatus + * @throws TransactionException TransactionException + */ + @Override + public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId, + String applicationData) throws TransactionException { + TCCResource tccResource = (TCCResource)tccResourceCache.get(resourceId); + if (tccResource == null) { + throw new ShouldNeverHappenException(String.format("TCC resource is not exist, resourceId: %s", resourceId)); + } + Object targetTCCBean = tccResource.getTargetBean(); + Method rollbackMethod = tccResource.getRollbackMethod(); + if (targetTCCBean == null || rollbackMethod == null) { + throw new ShouldNeverHappenException(String.format("TCC resource is not available, resourceId: %s", resourceId)); + } + try { + //BusinessActionContext + BusinessActionContext businessActionContext = getBusinessActionContext(xid, branchId, resourceId, + applicationData); + Object[] args = this.getTwoPhaseRollbackArgs(tccResource, businessActionContext); + + Object ret; + boolean result; + // add idempotent and anti hanging + if (Boolean.TRUE.equals(businessActionContext.getActionContext(Constants.USE_TCC_FENCE))) { + try { + result = TCCFenceHandler.rollbackFence(rollbackMethod, targetTCCBean, xid, branchId, + args, tccResource.getActionName()); + } catch (SkipCallbackWrapperException | UndeclaredThrowableException e) { + throw e.getCause(); + } + } else { + ret = rollbackMethod.invoke(targetTCCBean,args); + if (ret != null) { + if (ret instanceof TwoPhaseResult) { + result = ((TwoPhaseResult)ret).isSuccess(); + } else { + result = (boolean)ret; + } + } else { + result = true; + } + } + LOGGER.info("TCC resource rollback result : {}, xid: {}, branchId: {}, resourceId: {}", result, xid, branchId, resourceId); + return result ? BranchStatus.PhaseTwo_Rollbacked : BranchStatus.PhaseTwo_RollbackFailed_Retryable; + } catch (Throwable t) { + String msg = String.format("rollback TCC resource error, resourceId: %s, xid: %s.", resourceId, xid); + LOGGER.error(msg, t); + return BranchStatus.PhaseTwo_RollbackFailed_Retryable; + } + } + + /** + * transfer tcc applicationData to BusinessActionContext + * + * @param xid the xid + * @param branchId the branch id + * @param resourceId the resource id + * @param applicationData the application data + * @return business action context + */ + protected BusinessActionContext getBusinessActionContext(String xid, long branchId, String resourceId, + String applicationData) { + Map actionContextMap = null; + if (StringUtils.isNotBlank(applicationData)) { + Map tccContext = JSON.parseObject(applicationData, Map.class); + actionContextMap = (Map)tccContext.get(Constants.TCC_ACTION_CONTEXT); + } + if (actionContextMap == null) { + actionContextMap = new HashMap<>(2); + } + + //instance the action context + BusinessActionContext businessActionContext = new BusinessActionContext( + xid, String.valueOf(branchId), actionContextMap); + businessActionContext.setActionName(resourceId); + return businessActionContext; + } + + /** + * get phase two commit method's args + * @param tccResource tccResource + * @param businessActionContext businessActionContext + * @return args + */ + private Object[] getTwoPhaseCommitArgs(TCCResource tccResource, BusinessActionContext businessActionContext) { + String[] keys = tccResource.getPhaseTwoCommitKeys(); + Class[] argsCommitClasses = tccResource.getCommitArgsClasses(); + return this.getTwoPhaseMethodParams(keys, argsCommitClasses, businessActionContext); + } + + /** + * get phase two rollback method's args + * @param tccResource tccResource + * @param businessActionContext businessActionContext + * @return args + */ + private Object[] getTwoPhaseRollbackArgs(TCCResource tccResource, BusinessActionContext businessActionContext) { + String[] keys = tccResource.getPhaseTwoRollbackKeys(); + Class[] argsRollbackClasses = tccResource.getRollbackArgsClasses(); + return this.getTwoPhaseMethodParams(keys, argsRollbackClasses, businessActionContext); + } + + private Object[] getTwoPhaseMethodParams(String[] keys, Class[] argsClasses, BusinessActionContext businessActionContext) { + Object[] args = new Object[argsClasses.length]; + for (int i = 0; i < argsClasses.length; i++) { + if (argsClasses[i].equals(BusinessActionContext.class)) { + args[i] = businessActionContext; + } else { + args[i] = businessActionContext.getActionContext(keys[i], argsClasses[i]); + } + } + return args; + } + + @Override + public BranchType getBranchType() { + return BranchType.TCC; + } +} diff --git a/src/main/java/io/jboot/support/seata/tcc/TCCFenceHandler.java b/src/main/java/io/jboot/support/seata/tcc/TCCFenceHandler.java new file mode 100644 index 00000000..2e4dad76 --- /dev/null +++ b/src/main/java/io/jboot/support/seata/tcc/TCCFenceHandler.java @@ -0,0 +1,325 @@ +package io.jboot.support.seata.tcc; + +/** + * @author zhangxn + * @date 2022/5/30 21:32 + */ + +import com.jfinal.plugin.activerecord.Db; +import com.jfinal.plugin.activerecord.DbPro; +import com.jfinal.plugin.activerecord.IAtom; +import io.jboot.db.datasource.DataSourceBuilder; +import io.jboot.db.datasource.DataSourceConfig; +import io.jboot.db.datasource.DataSourceConfigManager; +import io.jboot.utils.StrUtil; +import io.seata.common.exception.FrameworkErrorCode; +import io.seata.common.thread.NamedThreadFactory; +import io.seata.rm.tcc.TwoPhaseResult; +import io.seata.rm.tcc.constant.TCCFenceConstant; +import io.seata.rm.tcc.exception.TCCFenceException; +import io.seata.rm.tcc.store.TCCFenceDO; +import io.seata.rm.tcc.store.TCCFenceStore; +import io.seata.rm.tcc.store.db.TCCFenceStoreDataBaseDAO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * TCC Fence Handler(idempotent, non_rollback, suspend) + * + * @author kaka2code + */ +public class TCCFenceHandler { + + private TCCFenceHandler() { + throw new IllegalStateException("Utility class"); + } + + private static final Logger LOGGER = LoggerFactory.getLogger(io.seata.rm.tcc.TCCFenceHandler.class); + + private static final TCCFenceStore TCC_FENCE_DAO = TCCFenceStoreDataBaseDAO.getInstance(); + + private static DataSource dataSource; + + + private static final int MAX_THREAD_CLEAN = 1; + + private static final int MAX_QUEUE_SIZE = 500; + + private static final LinkedBlockingQueue LOG_QUEUE = new LinkedBlockingQueue<>(MAX_QUEUE_SIZE); + + private static FenceLogCleanRunnable fenceLogCleanRunnable; + + private static ExecutorService logCleanExecutor; + + static { + try { + initLogCleanExecutor(); + } catch (Exception e) { + LOGGER.error("init fence log clean executor error", e); + } + } + + /** + * tcc prepare method enhanced + * + * @param xid the global transaction id + * @param branchId the branch transaction id + * @param actionName the action name + * @return the boolean + */ + public static Object prepareFence(String xid, Long branchId, String actionName) { + DataSourceConfig dataSourceConfig = DataSourceConfigManager.me().getMainDatasourceConfig(); + DataSource dataSource = new DataSourceBuilder(dataSourceConfig).build(); + IAtom runnable = () -> { + Connection connection = dataSource.getConnection(); + boolean result = insertTCCFenceLog(connection, xid, branchId, actionName, TCCFenceConstant.STATUS_TRIED); + LOGGER.info("TCC fence prepare result: {}. xid: {}, branchId: {}", result, xid, branchId); + if (!result) { + throw new TCCFenceException(String.format("Insert tcc fence record error, prepare fence failed. xid= %s, branchId= %s", xid, branchId), + FrameworkErrorCode.InsertRecordError); + } + return result; + }; + DbPro dbPro = StrUtil.isBlank(dataSourceConfig.getName()) ? Db.use() : Db.use(dataSourceConfig.getName()); + return dbPro.tx(runnable); + } + + /** + * tcc commit method enhanced + * + * @param commitMethod commit method + * @param targetTCCBean target tcc bean + * @param xid the global transaction id + * @param branchId the branch transaction id + * @param args commit method's parameters + * @return the boolean + */ + public static boolean commitFence(Method commitMethod, Object targetTCCBean, + String xid, Long branchId, Object[] args) { + + DataSourceConfig dataSourceConfig = DataSourceConfigManager.me().getMainDatasourceConfig(); + DataSource dataSource = new DataSourceBuilder(dataSourceConfig).build(); + IAtom runnable = () -> { + Connection connection = dataSource.getConnection(); + TCCFenceDO tccFenceDO = TCC_FENCE_DAO.queryTCCFenceDO(connection, xid, branchId); + if (tccFenceDO == null){ + throw new TCCFenceException(String.format("Insert tcc fence record error, rollback fence method failed. xid= %s, branchId= %s", xid, branchId), + FrameworkErrorCode.InsertRecordError); + } + if (TCCFenceConstant.STATUS_COMMITTED == tccFenceDO.getStatus()) { + LOGGER.info("Branch transaction has already committed before. idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus()); + return true; + } + if (TCCFenceConstant.STATUS_ROLLBACKED == tccFenceDO.getStatus() || TCCFenceConstant.STATUS_SUSPENDED == tccFenceDO.getStatus()) { + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus()); + } + return false; + } + try { + return updateStatusAndInvokeTargetMethod(connection, commitMethod, targetTCCBean, xid, branchId, TCCFenceConstant.STATUS_COMMITTED, args); + } catch (Exception ex) { + throw new TCCFenceException(ex.getCause()); + } + }; + DbPro dbPro = StrUtil.isBlank(dataSourceConfig.getName()) ? Db.use() : Db.use(dataSourceConfig.getName()); + return dbPro.tx(runnable); + } + + /** + * tcc rollback method enhanced + * + * @param rollbackMethod rollback method + * @param targetTCCBean target tcc bean + * @param xid the global transaction id + * @param branchId the branch transaction id + * @param args rollback method's parameters + * @param actionName the action name + * @return the boolean + */ + public static boolean rollbackFence(Method rollbackMethod, Object targetTCCBean, + String xid, Long branchId, Object[] args, String actionName) { + DataSourceConfig dataSourceConfig = DataSourceConfigManager.me().getMainDatasourceConfig(); + DataSource dataSource = new DataSourceBuilder(dataSourceConfig).build(); + IAtom runnable = () -> { + try { + Connection connection = dataSource.getConnection(); + TCCFenceDO tccFenceDO = TCC_FENCE_DAO.queryTCCFenceDO(connection, xid, branchId); + if (tccFenceDO == null){ + boolean result = insertTCCFenceLog(connection, xid, branchId, actionName, TCCFenceConstant.STATUS_SUSPENDED); + LOGGER.info("Insert tcc fence record result: {}. xid: {}, branchId: {}", result, xid, branchId); + if (!result) { + throw new TCCFenceException(String.format("Insert tcc fence record error, rollback fence method failed. xid= %s, branchId= %s", xid, branchId), + FrameworkErrorCode.InsertRecordError); + } + return true; + } else { + if (TCCFenceConstant.STATUS_ROLLBACKED == tccFenceDO.getStatus() || TCCFenceConstant.STATUS_SUSPENDED == tccFenceDO.getStatus()) { + LOGGER.info("Branch transaction had already rollbacked before, idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus()); + return true; + } + if (TCCFenceConstant.STATUS_COMMITTED == tccFenceDO.getStatus()) { + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus()); + } + return false; + } + } + return updateStatusAndInvokeTargetMethod(connection, rollbackMethod, targetTCCBean, xid, branchId, TCCFenceConstant.STATUS_ROLLBACKED, args); + } catch (Throwable ex) { + throw new TCCFenceException(ex.getCause()); + } + }; + DbPro dbPro = StrUtil.isBlank(dataSourceConfig.getName()) ? Db.use() : Db.use(dataSourceConfig.getName()); + return dbPro.tx(Connection.TRANSACTION_READ_UNCOMMITTED, runnable); + } + + /** + * Insert TCC fence log + * + * @param conn the db connection + * @param xid the xid + * @param branchId the branchId + * @param status the status + * @return the boolean + */ + private static boolean insertTCCFenceLog(Connection conn, String xid, Long branchId, String actionName, Integer status) { + TCCFenceDO tccFenceDO = new TCCFenceDO(); + tccFenceDO.setXid(xid); + tccFenceDO.setBranchId(branchId); + tccFenceDO.setActionName(actionName); + tccFenceDO.setStatus(status); + return TCC_FENCE_DAO.insertTCCFenceDO(conn, tccFenceDO); + } + + /** + * Update TCC Fence status and invoke target method + * + * @param method target method + * @param targetTCCBean target bean + * @param xid the global transaction id + * @param branchId the branch transaction id + * @param status the tcc fence status + * @return the boolean + */ + private static boolean updateStatusAndInvokeTargetMethod(Connection conn, Method method, Object targetTCCBean, + String xid, Long branchId, int status, Object[] args) throws Exception { + boolean result = TCC_FENCE_DAO.updateTCCFenceDO(conn, xid, branchId, status, TCCFenceConstant.STATUS_TRIED); + if (result) { + // invoke two phase method + Object ret = method.invoke(targetTCCBean, args); + if (null != ret) { + if (ret instanceof TwoPhaseResult) { + result = ((TwoPhaseResult) ret).isSuccess(); + } else { + result = (boolean) ret; + } + } + } + return result; + } + + private static void initLogCleanExecutor() { + logCleanExecutor = new ThreadPoolExecutor(MAX_THREAD_CLEAN, MAX_THREAD_CLEAN, Integer.MAX_VALUE, + TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), + new NamedThreadFactory("fenceLogCleanThread", MAX_THREAD_CLEAN, true) + ); + fenceLogCleanRunnable = new FenceLogCleanRunnable(); + logCleanExecutor.submit(fenceLogCleanRunnable); + } + + /** + * Delete TCC Fence + * + * @param xid the global transaction id + * @param branchId the branch transaction id + * @return the boolean + */ + public static boolean deleteFence(String xid, Long branchId) { + + DataSourceConfig dataSourceConfig = DataSourceConfigManager.me().getMainDatasourceConfig(); + DataSource dataSource = new DataSourceBuilder(dataSourceConfig).build(); + IAtom runnable = () -> { + try { + Connection connection = dataSource.getConnection(); + boolean ret = TCC_FENCE_DAO.deleteTCCFenceDO(connection, xid, branchId); + return ret; + } catch (Throwable ex) { + return false; + } + }; + DbPro dbPro = StrUtil.isBlank(dataSourceConfig.getName()) ? Db.use() : Db.use(dataSourceConfig.getName()); + return dbPro.tx(Connection.TRANSACTION_READ_UNCOMMITTED, runnable); + } + + private static void addToLogCleanQueue(final String xid, final long branchId) { + FenceLogIdentity logIdentity = new FenceLogIdentity(); + logIdentity.setXid(xid); + logIdentity.setBranchId(branchId); + try { + LOG_QUEUE.add(logIdentity); + } catch (Exception e) { + LOGGER.warn("Insert tcc fence record into queue for async delete error,xid:{},branchId:{}", xid, branchId, e); + } + } + + /** + * clean fence log that has the final status runnable. + * + * @see TCCFenceConstant + */ + private static class FenceLogCleanRunnable implements Runnable { + @Override + public void run() { + while (true) { + try { + FenceLogIdentity logIdentity = LOG_QUEUE.take(); + boolean ret = deleteFence(logIdentity.getXid(), logIdentity.getBranchId()); + if (!ret) { + LOGGER.error("delete fence log failed, xid: {}, branchId: {}", logIdentity.getXid(), logIdentity.getBranchId()); + } + } catch (InterruptedException e) { + LOGGER.error("take fence log from queue for clean be interrupted", e); + } catch (Exception e) { + LOGGER.error("exception occur when clean fence log", e); + } + } + } + } + + private static class FenceLogIdentity { + /** + * the global transaction id + */ + private String xid; + + /** + * the branch transaction id + */ + private Long branchId; + + public String getXid() { + return xid; + } + + public Long getBranchId() { + return branchId; + } + + public void setXid(String xid) { + this.xid = xid; + } + + public void setBranchId(Long branchId) { + this.branchId = branchId; + } + } +} diff --git a/src/main/resources/META-INF/seata/io.seata.core.model.ResourceManager b/src/main/resources/META-INF/seata/io.seata.core.model.ResourceManager new file mode 100644 index 00000000..e6494fdd --- /dev/null +++ b/src/main/resources/META-INF/seata/io.seata.core.model.ResourceManager @@ -0,0 +1 @@ +io.jboot.support.seata.tcc.JbootTCCResourceManager \ No newline at end of file diff --git a/src/test/java/io/jboot/test/seata/commons/fescar_.sql b/src/test/java/io/jboot/test/seata/commons/fescar_.sql index 07c6c16e..4e37b906 100644 --- a/src/test/java/io/jboot/test/seata/commons/fescar_.sql +++ b/src/test/java/io/jboot/test/seata/commons/fescar_.sql @@ -49,3 +49,15 @@ CREATE TABLE `undo_log` ( PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +CREATE TABLE `tcc_fence_log` ( + `xid` varchar(128) NOT NULL COMMENT 'global id', + `branch_id` bigint NOT NULL COMMENT 'branch id', + `action_name` varchar(64) NOT NULL COMMENT 'action name', + `status` tinyint NOT NULL COMMENT 'status(tried:1;committed:2;rollbacked:3;suspended:4)', + `gmt_create` datetime(3) NOT NULL COMMENT 'create time', + `gmt_modified` datetime(3) NOT NULL COMMENT 'update time', + PRIMARY KEY (`xid`,`branch_id`), + KEY `idx_gmt_modified` (`gmt_modified`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; \ No newline at end of file diff --git a/src/test/java/io/jboot/test/seata/service/impl/TccActionOneServiceImpl.java b/src/test/java/io/jboot/test/seata/service/impl/TccActionOneServiceImpl.java index 63ca7c4c..1225d296 100644 --- a/src/test/java/io/jboot/test/seata/service/impl/TccActionOneServiceImpl.java +++ b/src/test/java/io/jboot/test/seata/service/impl/TccActionOneServiceImpl.java @@ -22,7 +22,7 @@ public class TccActionOneServiceImpl implements TccActionOneService { private IAccountService accountService; @Override - @TwoPhaseBusinessAction(name = "TccActionOne" , commitMethod = "commit", rollbackMethod = "rollback") + @TwoPhaseBusinessAction(name = "TccActionOne" , commitMethod = "commit", rollbackMethod = "rollback", useTCCFence = true) public boolean prepare(BusinessActionContext actionContext, String account,int money, boolean flag) { System.out.println("actionContext获取Xid prepare>>> "+actionContext.getXid()); System.out.println("actionContext获取TCC参数 prepare>>> "+actionContext.getActionContext("account")); diff --git a/src/test/java/io/jboot/test/seata/starter/AccountApplicaiton.java b/src/test/java/io/jboot/test/seata/starter/AccountApplicaiton.java index db3d1034..0370ba94 100644 --- a/src/test/java/io/jboot/test/seata/starter/AccountApplicaiton.java +++ b/src/test/java/io/jboot/test/seata/starter/AccountApplicaiton.java @@ -25,7 +25,7 @@ public class AccountApplicaiton { JbootApplication.setBootArg("jboot.datasource.type", "mysql"); JbootApplication.setBootArg("jboot.datasource.url", "jdbc:mysql://127.0.0.1:3306/mini?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull"); JbootApplication.setBootArg("jboot.datasource.user", "root"); - JbootApplication.setBootArg("jboot.datasource.password", "zhang123"); + JbootApplication.setBootArg("jboot.datasource.password", "zxn123"); JbootApplication.setBootArg("jboot.model.unscanPackage", "*"); JbootApplication.setBootArg("jboot.model.scanPackage", "io.jboot.test.seata.commons"); diff --git a/src/test/java/io/jboot/test/seata/starter/WebApplication.java b/src/test/java/io/jboot/test/seata/starter/WebApplication.java index d8642529..fa16a14b 100644 --- a/src/test/java/io/jboot/test/seata/starter/WebApplication.java +++ b/src/test/java/io/jboot/test/seata/starter/WebApplication.java @@ -33,7 +33,7 @@ public class WebApplication extends JbootController { JbootApplication.setBootArg("jboot.datasource.type", "mysql"); JbootApplication.setBootArg("jboot.datasource.url", "jdbc:mysql://127.0.0.1:3306/mini?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull"); JbootApplication.setBootArg("jboot.datasource.user", "root"); - JbootApplication.setBootArg("jboot.datasource.password", "zhang123"); + JbootApplication.setBootArg("jboot.datasource.password", "zxn123"); JbootApplication.setBootArg("jboot.model.unscanPackage", "*"); JbootApplication.setBootArg("jboot.model.scanPackage", "io.jboot.test.seata.commons"); -- Gitee From 062bf816812ec8283a84287b8a883bc9d5cbd7e2 Mon Sep 17 00:00:00 2001 From: zhangxn8 <840267340@qq.com> Date: Sat, 11 Jun 2022 13:18:24 +0800 Subject: [PATCH 1721/1965] =?UTF-8?q?1=E3=80=81=E4=BF=AE=E6=94=B9=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E9=87=8D=E8=AF=95=E5=BC=82=E5=B8=B8=E6=8A=9B=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seata/interceptor/SeataGlobalTransactionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionHandler.java b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionHandler.java index ed1a5d3a..0452515c 100644 --- a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionHandler.java +++ b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionHandler.java @@ -94,7 +94,7 @@ public class SeataGlobalTransactionHandler { throw e.getCause(); case RollbackRetrying: failureHandler.onRollbackRetrying(e.getTransaction(), e.getOriginalException()); - throw e.getOriginalException(); + throw e.getCause(); default: throw new ShouldNeverHappenException("Unknown TransactionalExecutor.Code: " + code); -- Gitee From 83d8e0191121de1fdd42078a276871f2ba6d2f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 12 Jun 2022 12:41:55 +0800 Subject: [PATCH 1722/1965] rename SeataGlobalLock.lockRetryInternal to lockRetryInterval --- .../java/io/jboot/support/seata/annotation/SeataGlobalLock.java | 2 +- .../seata/interceptor/SeataGlobalTransactionalInterceptor.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/support/seata/annotation/SeataGlobalLock.java b/src/main/java/io/jboot/support/seata/annotation/SeataGlobalLock.java index fc29e295..dea5672a 100644 --- a/src/main/java/io/jboot/support/seata/annotation/SeataGlobalLock.java +++ b/src/main/java/io/jboot/support/seata/annotation/SeataGlobalLock.java @@ -35,7 +35,7 @@ import java.lang.annotation.Target; @Inherited public @interface SeataGlobalLock { - int lockRetryInternal() default 0; + int lockRetryInterval() default 0; int lockRetryTimes() default -1; diff --git a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java index 847e151e..e8a6c69e 100644 --- a/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java +++ b/src/main/java/io/jboot/support/seata/interceptor/SeataGlobalTransactionalInterceptor.java @@ -72,7 +72,7 @@ public class SeataGlobalTransactionalInterceptor implements Interceptor { @Override public GlobalLockConfig getGlobalLockConfig() { GlobalLockConfig config = new GlobalLockConfig(); - config.setLockRetryInterval(globalLockAnno.lockRetryInternal()); + config.setLockRetryInterval(globalLockAnno.lockRetryInterval()); config.setLockRetryTimes(globalLockAnno.lockRetryTimes()); return config; } -- Gitee From 372347f563967249dc666d611e876e14dda1a381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 12 Jun 2022 13:06:06 +0800 Subject: [PATCH 1723/1965] v3.15.4 snapshot --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e028ca85..9b547ddf 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.3 + 3.15.4 jar jboot @@ -68,7 +68,7 @@ 4.1.52.Final 1.8.3 2.7.15 - 3.13.2.Final + 3.14.0.Final 1.5.1 1.1.8 4.2.9 -- Gitee From 60c397841db5d442d00fe61368324551179e7a04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 19 Jun 2022 17:53:59 +0800 Subject: [PATCH 1724/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9Adubbo2=20?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=88=B0=20dubbo3=20=E7=9A=84=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=E6=96=87=E6=A1=A3=EF=BC=8C=E6=84=9F=E8=B0=A2=20@?= =?UTF-8?q?=E8=87=AA=E7=94=B1=E9=A2=86=E4=B8=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/docs/dubbo3-upgrade.md | 47 +++++++++++++++++++ .../rpc/dubbo3/comsumer/ConsumerStarter.java | 43 +++++++++++++++++ .../comsumer/controller/DubboClient.java | 26 ++++++++++ .../rpc/dubbo3/provider/ProviderStarter.java | 41 ++++++++++++++++ .../service/impl/BlogServiceProvider.java | 22 +++++++++ .../java/io/jboot/test/rpc/dubbo3/readme.md | 10 ++++ .../test/rpc/dubbo3/service/BlogService.java | 8 ++++ 7 files changed, 197 insertions(+) create mode 100644 doc/docs/dubbo3-upgrade.md create mode 100644 src/test/java/io/jboot/test/rpc/dubbo3/comsumer/ConsumerStarter.java create mode 100644 src/test/java/io/jboot/test/rpc/dubbo3/comsumer/controller/DubboClient.java create mode 100644 src/test/java/io/jboot/test/rpc/dubbo3/provider/ProviderStarter.java create mode 100644 src/test/java/io/jboot/test/rpc/dubbo3/provider/service/impl/BlogServiceProvider.java create mode 100644 src/test/java/io/jboot/test/rpc/dubbo3/readme.md create mode 100644 src/test/java/io/jboot/test/rpc/dubbo3/service/BlogService.java diff --git a/doc/docs/dubbo3-upgrade.md b/doc/docs/dubbo3-upgrade.md new file mode 100644 index 00000000..18bab952 --- /dev/null +++ b/doc/docs/dubbo3-upgrade.md @@ -0,0 +1,47 @@ +# Dubbo2 升级 Dubbo3 文档 + +dubbo2 升级 dubbo3 ,请先阅读 dubbo3 官网的升级文档:https://dubbo.apache.org/zh/docs/migration/migration-service-discovery/ + + +总体上来说,3.x 是完全兼容 2.x 版本的,因此,理论上只需要添加两个配置即可: + +- 1、生产端增加配置: + jboot.rpc.dubbo.registry.registerMode=instance + + +- 2、消费端配置: + jboot.rpc.application.service-discovery.migration=FORCE_APPLICATION + + +[comment]: <> (为了保持兼容性最简单的升级方式是:) + + + +以下是升级流程: + +- 1、 修改工程 pom.xml 中的 dubbo 依赖到最新版本就可以完成升级,其它代码不用动,升级完成后,建议以下参数进行定制调整(2、3步) + + - 2、Provider 端 `jboot.properties` 中 + `jboot.rpc.dubbo.registry.registerMode` 的可选值 interface、instance、all,默认是 all,即接口级地址、应用级地址都注册。 + + - instance: 应用级注册; + - interface:接口级注册; + - all:双注册(默认值) 即接口和应用都同时注册到注册中心中,双注册不可避免的会带来额外的注册中心存储压力, + 建议修改默认值为 instance 模式,这是 dubbo3 推荐方式。 + +- 3.Consumer 端 `jboot.properties` 中 `jboot.rpc.application.service-discovery.migration` +支持的可选值: + + - FORCE_INTERFACE,只消费接口级地址,如无地址则报错,单订阅 2.x 地址 + - APPLICATION_FIRST,智能决策接口级/应用级地址,双订阅 + - FORCE_APPLICATION,只消费应用级地址,如无地址则报错,单订阅 3.x 地址 +jboot.rpc.dubbo.application.service-discovery.migration=APPLICATION_FIRST + +> 其他注意事项: +> 在 Dubbo3 中,服务提(Provider端)如果采用应用级注册 (instance),注册中心中只会注册应用实例信息,不会注册接口服务信息。接口服务信息存储在元数据中心,在元数据中心存储了实例和接口的映射关系。消费端通过元数据映射关系获取所需要的服务信息。 +这套流程是 dubbo3 为了兼容 dubbo2 自动来完成的,官方名称为:服务自省。详情: +https://dubbo.apache.org/zh/docs/examples/service-discovery/#%E4%BB%80%E4%B9%88%E6%98%AF%E6%9C%8D%E5%8A%A1%E8%87%AA%E7%9C%81 + + +## 参考代码 + diff --git a/src/test/java/io/jboot/test/rpc/dubbo3/comsumer/ConsumerStarter.java b/src/test/java/io/jboot/test/rpc/dubbo3/comsumer/ConsumerStarter.java new file mode 100644 index 00000000..43bc6669 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/dubbo3/comsumer/ConsumerStarter.java @@ -0,0 +1,43 @@ +package io.jboot.test.rpc.dubbo3.comsumer; + +import io.jboot.app.JbootApplication; + +public class ConsumerStarter { + public static void main(String[] args) { + + /* + * undertow.devMode=true + * undertow.port=8082 + * jboot.rpc.type = dubbo + * jboot.rpc.autoExportEnable=true + * jboot.rpc.application.service-discovery.migration=FORCE_APPLICATION + * jboot.rpc.dubbo.protocol.name=dubbo + * jboot.rpc.dubbo.protocol.port=28080 + * jboot.rpc.dubbo.registry.protocol=nacos + * jboot.rpc.dubbo.registry.address=127.0.0.1:8848 + * jboot.rpc.dubbo.application.name = BlogConsumer + * jboot.rpc.dubbo.application.version = 1.0 + */ + + JbootApplication.setBootArg("undertow.devMode","true"); + JbootApplication.setBootArg("undertow.port","8082"); + + JbootApplication.setBootArg("jboot.rpc.type","dubbo"); + JbootApplication.setBootArg("jboot.rpc.autoExportEnable","true"); + + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.name","dubbo"); + JbootApplication.setBootArg("jboot.rpc.dubbo.protocol.port","28080"); + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.protocol","nacos"); + JbootApplication.setBootArg("jboot.rpc.dubbo.registry.address","127.0.0.1:8848"); + JbootApplication.setBootArg("jboot.rpc.dubbo.application.name","BlogConsumer"); + JbootApplication.setBootArg("jboot.rpc.dubbo.application.version","1.0"); + + //dubbo3 主要是增加这个配置 + JbootApplication.setBootArg("jboot.rpc.application.service-discovery.migration","FORCE_APPLICATION"); + + + + JbootApplication.run(args); + System.out.println("Consumer Started"); + } +} \ No newline at end of file diff --git a/src/test/java/io/jboot/test/rpc/dubbo3/comsumer/controller/DubboClient.java b/src/test/java/io/jboot/test/rpc/dubbo3/comsumer/controller/DubboClient.java new file mode 100644 index 00000000..8e37e72b --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/dubbo3/comsumer/controller/DubboClient.java @@ -0,0 +1,26 @@ +package io.jboot.test.rpc.dubbo3.comsumer.controller; + +import com.alibaba.fastjson.JSONArray; +import io.jboot.components.rpc.annotation.RPCInject; +import io.jboot.test.rpc.dubbo3.service.BlogService; +import io.jboot.web.controller.JbootController; +import io.jboot.web.controller.annotation.RequestMapping; + +@RequestMapping("/dubbo") +public class DubboClient extends JbootController { + + @RPCInject + private BlogService blogService; + + + public void index() { + System.out.println(blogService); + renderText("blogId : " + blogService.findById()); + } + + + public void blogList() { + System.out.println(blogService); + renderText("blogList : " + JSONArray.toJSONString(blogService.findAll())); + } +} diff --git a/src/test/java/io/jboot/test/rpc/dubbo3/provider/ProviderStarter.java b/src/test/java/io/jboot/test/rpc/dubbo3/provider/ProviderStarter.java new file mode 100644 index 00000000..06496707 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/dubbo3/provider/ProviderStarter.java @@ -0,0 +1,41 @@ +package io.jboot.test.rpc.dubbo3.provider; + +import io.jboot.app.JbootSimpleApplication; + +public class ProviderStarter { + public static void main(String[] args) { + + /* + * undertow.devMode=true + * undertow.port=8081 + * jboot.rpc.type = dubbo + * jboot.rpc.autoExportEnable=true + * jboot.rpc.dubbo.application.version = 1.0 + * jboot.rpc.dubbo.protocol.name=dubbo + * jboot.rpc.dubbo.protocol.port=28080 + * jboot.rpc.dubbo.registry.protocol=nacos + * jboot.rpc.dubbo.registry.address=127.0.0.1:8848 + * jboot.rpc.dubbo.application.name = BlogProvider + * + * jboot.rpc.dubbo.registry.registerMode=instance + */ + + JbootSimpleApplication.setBootArg("jboot.rpc.type","dubbo"); + JbootSimpleApplication.setBootArg("jboot.rpc.autoExportEnable","true"); + + JbootSimpleApplication.setBootArg("jboot.rpc.dubbo.protocol.name","dubbo"); + JbootSimpleApplication.setBootArg("jboot.rpc.dubbo.protocol.port","28080"); + JbootSimpleApplication.setBootArg("jboot.rpc.dubbo.registry.protocol","nacos"); + JbootSimpleApplication.setBootArg("jboot.rpc.dubbo.registry.address","127.0.0.1:8848"); + JbootSimpleApplication.setBootArg("jboot.rpc.dubbo.application.name","BlogProvider"); + JbootSimpleApplication.setBootArg("jboot.rpc.dubbo.application.version","1.0"); + + //dubbo3 主要是添加这个配置 + JbootSimpleApplication.setBootArg("jboot.rpc.dubbo.registry.registerMod","instance"); + + + JbootSimpleApplication.run(args); + + System.out.println("Provider Started"); + } +} \ No newline at end of file diff --git a/src/test/java/io/jboot/test/rpc/dubbo3/provider/service/impl/BlogServiceProvider.java b/src/test/java/io/jboot/test/rpc/dubbo3/provider/service/impl/BlogServiceProvider.java new file mode 100644 index 00000000..3c42f6a7 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/dubbo3/provider/service/impl/BlogServiceProvider.java @@ -0,0 +1,22 @@ +package io.jboot.test.rpc.dubbo3.provider.service.impl; + +import com.google.common.collect.Lists; +import io.jboot.components.rpc.annotation.RPCBean; +import io.jboot.test.rpc.dubbo3.service.BlogService; + +import java.util.List; + +@RPCBean +public class BlogServiceProvider implements BlogService { + + + @Override + public String findById() { + return "id from provider"; + } + + @Override + public List findAll() { + return Lists.newArrayList("item1","item2"); + } +} diff --git a/src/test/java/io/jboot/test/rpc/dubbo3/readme.md b/src/test/java/io/jboot/test/rpc/dubbo3/readme.md new file mode 100644 index 00000000..78f967f2 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/dubbo3/readme.md @@ -0,0 +1,10 @@ +整体上来说,jboot 升级 dubbo2 到 dubbo3 是不需要修改其他任何代码的 + +理论上只需要添加两个配置就可以了 + +- 1、生产端增加配置: +jboot.rpc.dubbo.registry.registerMode=instance + + +- 2、消费端配置: +jboot.rpc.application.service-discovery.migration=FORCE_APPLICATION diff --git a/src/test/java/io/jboot/test/rpc/dubbo3/service/BlogService.java b/src/test/java/io/jboot/test/rpc/dubbo3/service/BlogService.java new file mode 100644 index 00000000..831826a7 --- /dev/null +++ b/src/test/java/io/jboot/test/rpc/dubbo3/service/BlogService.java @@ -0,0 +1,8 @@ +package io.jboot.test.rpc.dubbo3.service; + +import java.util.List; + +public interface BlogService { + String findById(); + List findAll(); +} -- Gitee From bbc4502a488dbf71781c30c0db1f5fcbe7efc2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 19 Jun 2022 17:54:56 +0800 Subject: [PATCH 1725/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9Adubbo2=20?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=88=B0=20dubbo3=20=E7=9A=84=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=E6=96=87=E6=A1=A3=EF=BC=8C=E6=84=9F=E8=B0=A2=20@?= =?UTF-8?q?=E8=87=AA=E7=94=B1=E9=A2=86=E4=B8=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/docs/dubbo3-upgrade.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/docs/dubbo3-upgrade.md b/doc/docs/dubbo3-upgrade.md index 18bab952..69eecbd9 100644 --- a/doc/docs/dubbo3-upgrade.md +++ b/doc/docs/dubbo3-upgrade.md @@ -45,3 +45,5 @@ https://dubbo.apache.org/zh/docs/examples/service-discovery/#%E4%BB%80%E4%B9%88% ## 参考代码 +https://gitee.com/JbootProjects/jboot/tree/master/src/test/java/io/jboot/test/rpc/dubbo3 + -- Gitee From b8b5c839fd29386ba35436b4cffbac0d815c719e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 19 Jun 2022 18:09:53 +0800 Subject: [PATCH 1726/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9Adubbo2=20?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=88=B0=20dubbo3=20=E7=9A=84=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=E6=96=87=E6=A1=A3=EF=BC=8C=E6=84=9F=E8=B0=A2=20@?= =?UTF-8?q?=E8=87=AA=E7=94=B1=E9=A2=86=E4=B8=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/docs/dubbo3-upgrade.md | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/doc/docs/dubbo3-upgrade.md b/doc/docs/dubbo3-upgrade.md index 69eecbd9..1c0c698b 100644 --- a/doc/docs/dubbo3-upgrade.md +++ b/doc/docs/dubbo3-upgrade.md @@ -8,20 +8,15 @@ dubbo2 升级 dubbo3 ,请先阅读 dubbo3 官网的升级文档:https://dubb - 1、生产端增加配置: jboot.rpc.dubbo.registry.registerMode=instance - - 2、消费端配置: jboot.rpc.application.service-discovery.migration=FORCE_APPLICATION -[comment]: <> (为了保持兼容性最简单的升级方式是:) - - - -以下是升级流程: +**以下是升级流程:** - 1、 修改工程 pom.xml 中的 dubbo 依赖到最新版本就可以完成升级,其它代码不用动,升级完成后,建议以下参数进行定制调整(2、3步) - - 2、Provider 端 `jboot.properties` 中 +- 2、Provider 端 `jboot.properties` 中 `jboot.rpc.dubbo.registry.registerMode` 的可选值 interface、instance、all,默认是 all,即接口级地址、应用级地址都注册。 - instance: 应用级注册; @@ -29,17 +24,18 @@ dubbo2 升级 dubbo3 ,请先阅读 dubbo3 官网的升级文档:https://dubb - all:双注册(默认值) 即接口和应用都同时注册到注册中心中,双注册不可避免的会带来额外的注册中心存储压力, 建议修改默认值为 instance 模式,这是 dubbo3 推荐方式。 -- 3.Consumer 端 `jboot.properties` 中 `jboot.rpc.application.service-discovery.migration` -支持的可选值: - - - FORCE_INTERFACE,只消费接口级地址,如无地址则报错,单订阅 2.x 地址 - - APPLICATION_FIRST,智能决策接口级/应用级地址,双订阅 - - FORCE_APPLICATION,只消费应用级地址,如无地址则报错,单订阅 3.x 地址 +- 3、Consumer 端 `jboot.properties` 中 `jboot.rpc.application.service-discovery.migration` +的可选值有: + - FORCE_INTERFACE:只消费接口级地址,如无地址则报错,单订阅 2.x 地址 + - APPLICATION_FIRST:智能决策接口级/应用级地址,双订阅 + - FORCE_APPLICATION:只消费应用级地址,如无地址则报错,单订阅 3.x 地址 jboot.rpc.dubbo.application.service-discovery.migration=APPLICATION_FIRST -> 其他注意事项: -> 在 Dubbo3 中,服务提(Provider端)如果采用应用级注册 (instance),注册中心中只会注册应用实例信息,不会注册接口服务信息。接口服务信息存储在元数据中心,在元数据中心存储了实例和接口的映射关系。消费端通过元数据映射关系获取所需要的服务信息。 -这套流程是 dubbo3 为了兼容 dubbo2 自动来完成的,官方名称为:服务自省。详情: +> 注意事项: +> +> 在 Dubbo3 中,服务端(Provider端)如果采用应用级注册 (instance),注册中心中只会注册应用实例信息,不会注册接口服务信息。接口服务信息存储在元数据中心,在元数据中心存储了实例和接口的映射关系。消费端通过元数据映射关系获取所需要的服务信息。 +> +> 这套流程是 dubbo3 为了兼容 dubbo2 自动来完成的,官方名称为:服务自省。详情: https://dubbo.apache.org/zh/docs/examples/service-discovery/#%E4%BB%80%E4%B9%88%E6%98%AF%E6%9C%8D%E5%8A%A1%E8%87%AA%E7%9C%81 -- Gitee From 04d5d417e998524a95f406afe4fbc6aa5621b6c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 21 Jun 2022 17:03:07 +0800 Subject: [PATCH 1727/1965] optimize CookieUtil --- src/main/java/io/jboot/utils/CookieUtil.java | 96 ++++++++++++++++++-- 1 file changed, 90 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/utils/CookieUtil.java b/src/main/java/io/jboot/utils/CookieUtil.java index deea9ca7..cdf50e37 100644 --- a/src/main/java/io/jboot/utils/CookieUtil.java +++ b/src/main/java/io/jboot/utils/CookieUtil.java @@ -19,9 +19,15 @@ import com.jfinal.core.Controller; import com.jfinal.kit.Base64Kit; import com.jfinal.kit.HashKit; import com.jfinal.kit.LogKit; +import com.jfinal.kit.StrKit; import com.jfinal.log.Log; +import io.jboot.Jboot; +import io.jboot.exception.JbootIllegalConfigException; import io.jboot.web.JbootWebConfig; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.math.BigInteger; /** @@ -60,6 +66,11 @@ public class CookieUtil { COOKIE_ENCRYPT_KEY = key; } + + public static String getEncryptKey() { + return COOKIE_ENCRYPT_KEY; + } + /** * 设置 默认的 Cookie 有效时间,单位:秒 * @@ -69,6 +80,10 @@ public class CookieUtil { COOKIE_MAX_AGE = seconds; } + public static int getDefaultCookieMaxAge() { + return COOKIE_MAX_AGE; + } + public static String getDefaultPath() { return defaultPath; @@ -90,6 +105,10 @@ public class CookieUtil { put(ctr, key, value, COOKIE_MAX_AGE, defaultPath, defaultDomain, COOKIE_ENCRYPT_KEY); } + public static void put(HttpServletResponse response, String key, Object value) { + put(response, key, value, COOKIE_MAX_AGE, defaultPath, defaultDomain, COOKIE_ENCRYPT_KEY); + } + public static void put(Controller ctr, String key, Object value, String secretKey) { put(ctr, key, value, COOKIE_MAX_AGE, defaultPath, defaultDomain, secretKey); @@ -106,25 +125,40 @@ public class CookieUtil { public static void put(Controller ctr, String key, Object value, int maxAgeInSeconds, String path, String domain, String secretKey) { - String cookie = buildCookieValue(value.toString(), maxAgeInSeconds, secretKey); - ctr.setCookie(key, cookie, maxAgeInSeconds, path, domain, true); + put(ctr.getResponse(), key, value, maxAgeInSeconds, path, domain, secretKey); } + public static void put(HttpServletResponse response, String key, Object value, int maxAgeInSeconds, String path, String domain, String secretKey) { + String cookie = buildCookieValue(value.toString(), maxAgeInSeconds, secretKey); + doSetCookie(response, key, cookie, maxAgeInSeconds, path, domain, true); + } public static void remove(Controller ctr, String key) { - ctr.removeCookie(key); + doSetCookie(ctr.getResponse(), key, null, 0, null, null, null); + } + + public static void remove(HttpServletResponse response, String key) { + doSetCookie(response, key, null, 0, null, null, null); } public static void remove(Controller ctr, String key, String path, String domain) { - ctr.removeCookie(key, path, domain); + doSetCookie(ctr.getResponse(), key, null, 0, path, domain, null); } public static String get(Controller ctr, String key) { return get(ctr, key, COOKIE_ENCRYPT_KEY); } + public static String get(HttpServletRequest request, String key) { + return get(request, key, COOKIE_ENCRYPT_KEY); + } + public static String get(Controller ctr, String key, String secretKey) { - String cookieValue = ctr.getCookie(key); + return get(ctr.getRequest(), key, secretKey); + } + + public static String get(HttpServletRequest request, String key, String secretKey) { + String cookieValue = getCookie(request, key, null); if (cookieValue == null) { return null; @@ -161,8 +195,17 @@ public class CookieUtil { } private static String encrypt(String secretKey, Object saveTime, Object maxAgeInSeconds, String value) { + + // 若使用了默认的加密秘钥 if (JbootWebConfig.DEFAULT_COOKIE_ENCRYPT_KEY.equals(secretKey)) { - LOG.warn("warn!!! encrypt key is defalut value. please config \"jboot.web.cookieEncryptKey = xxx\" in jboot.properties "); + + if (Jboot.isDevMode()) { + LOG.warn("Warn!!! encrypt key is defalut value. please config \"jboot.web.cookieEncryptKey = xxx\" in jboot.properties"); + } + // 生产环境下,直接抛出错误! + else { + throw new JbootIllegalConfigException("Error!!! Cookie encrypt key is not configured. please config \"jboot.web.cookieEncryptKey = xxx\" in jboot.properties"); + } } return HashKit.md5(secretKey + saveTime.toString() + maxAgeInSeconds.toString() + value); } @@ -238,4 +281,45 @@ public class CookieUtil { return null == value ? defalut : new BigInteger(value); } + + private static String getCookie(HttpServletRequest request, String name, String defaultValue) { + Cookie cookie = getCookieObject(request, name); + return cookie != null ? cookie.getValue() : defaultValue; + } + + /** + * Get cookie object by cookie name. + */ + private static Cookie getCookieObject(HttpServletRequest request, String name) { + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals(name)) { + return cookie; + } + } + } + return null; + } + + + private static void doSetCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds, String path, String domain, Boolean isHttpOnly) { + Cookie cookie = new Cookie(name, value); + cookie.setMaxAge(maxAgeInSeconds); + // set the default path value to "/" + if (StrKit.isBlank(path)) { + path = "/"; + } + cookie.setPath(path); + + if (domain != null) { + cookie.setDomain(domain); + } + if (isHttpOnly != null) { + cookie.setHttpOnly(isHttpOnly); + } + response.addCookie(cookie); + } + + } -- Gitee From 16cc14080fcbd106bbaf876fe27dbeb260fe8d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 21 Jun 2022 17:09:08 +0800 Subject: [PATCH 1728/1965] update readme --- README.md | 4 ---- doc/docs/static/images/donate.png | Bin 199292 -> 0 bytes 2 files changed, 4 deletions(-) delete mode 100644 doc/docs/static/images/donate.png diff --git a/README.md b/README.md index b01fc750..660023f6 100644 --- a/README.md +++ b/README.md @@ -58,10 +58,6 @@ public class Helloworld extends JbootController { ![](./doc/docs/static/images/jboot-wechat-group.png) -## 赞助 - -![](./doc/docs/static/images/donate.png) - ## JbootAdmin JbootAdmin 是 Jboot 官方推出的、收费的、企业级快速开发框架,真诚的为各位开发者提供一站式、保姆式的开发服务。 diff --git a/doc/docs/static/images/donate.png b/doc/docs/static/images/donate.png deleted file mode 100644 index c25e9e6ac97f12c17a084ce34ad5f40eaa703883..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199292 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 AnsB!sk*lBZS7C$$rF-m?~*8Rr)-9*G{ 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

        eaWM1>Y7xvB`6lEZ3lj1zx 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

        b!4`}8=45Oh)GGE1{T-NqR>hU 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 OXCVGW>97BN`~Lxn`22nV -- Gitee From 7679f71079da0e80cf096f340c44170ef4a910e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 21 Jun 2022 17:38:35 +0800 Subject: [PATCH 1729/1965] v3.15.4 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index dcbc0e09..178f4d74 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.15.3 + 3.15.4 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index f6bbfc06..88246510 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.15.3 + 3.15.4 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 7cc94ea5..1a7511e1 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.15.3 + 3.15.4 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.15.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.15.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 78975246..382cdcbf 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.15.3', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.15.4', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.3/jboot-3.15.3.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.4/jboot-3.15.4.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 6056f8a8..c4ecd191 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.15.3 + 3.15.4 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 133bda8c..c350708a 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.15.3"; + public static String VERSION = "3.15.4"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 052f0773a07b641e33d71d87c32001046c232e85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 21 Jun 2022 17:38:48 +0800 Subject: [PATCH 1730/1965] v3.15.4 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ pom.xml | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 660023f6..1eae0253 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.15.3 + 3.15.4 ``` diff --git a/changes.txt b/changes.txt index dd196150..af13d189 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.15.4: +优化:升级适配 Seata 到最新版本 1.5.1,感谢 @扫地猿 +优化:CookieUtil,方便在 Handler 里对 Cookie 进行操作 +文档:新增 dubbo2 升级到 dubbo3 的升级文档,感谢 @自由领主 + + + jboot v3.15.3: 新增:代码生成器新增 白名单 配置的支持,感谢 @xujianxie 优化:升级 FastJson 到最新版本 diff --git a/pom.xml b/pom.xml index 9b547ddf..f69de370 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ 4.0.3 2.13.3 5.1.49 - 2.2.17.Final + 2.2.18.Final 1.7.36 2.57 @@ -71,7 +71,7 @@ 3.14.0.Final 1.5.1 1.1.8 - 4.2.9 + 4.2.10 1.8 1.8 -- Gitee From 3cb8bf5410e449f8fcf682302b37515b67277141 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 21 Jun 2022 17:41:25 +0800 Subject: [PATCH 1731/1965] v3.15.4 release (^.^)YYa!! --- pom.xml | 2 +- .../jboot/test/rpc/dubbo3/comsumer/controller/DubboClient.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f69de370..01f851be 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ 3.14.0.Final 1.5.1 1.1.8 - 4.2.10 + 4.2.9 1.8 1.8 diff --git a/src/test/java/io/jboot/test/rpc/dubbo3/comsumer/controller/DubboClient.java b/src/test/java/io/jboot/test/rpc/dubbo3/comsumer/controller/DubboClient.java index 8e37e72b..45bdae59 100644 --- a/src/test/java/io/jboot/test/rpc/dubbo3/comsumer/controller/DubboClient.java +++ b/src/test/java/io/jboot/test/rpc/dubbo3/comsumer/controller/DubboClient.java @@ -6,7 +6,7 @@ import io.jboot.test.rpc.dubbo3.service.BlogService; import io.jboot.web.controller.JbootController; import io.jboot.web.controller.annotation.RequestMapping; -@RequestMapping("/dubbo") +@RequestMapping("/dubbo3") public class DubboClient extends JbootController { @RPCInject -- Gitee From 2e9a22058da23c553dc0ac88e18ab27f70bc0b3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 27 Jun 2022 16:55:58 +0800 Subject: [PATCH 1732/1965] v3.15.5 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 01f851be..539a75c1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.4 + 3.15.5-SNAPSHOT jar jboot -- Gitee From 397d77721f5ebb84ad40c12305deb7fb2790c062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 27 Jun 2022 16:56:19 +0800 Subject: [PATCH 1733/1965] fixed #I592AL close #I592AL --- .../java/io/jboot/app/config/JbootConfigKit.java | 7 +++++-- .../io/jboot/app/config/JbootConfigManager.java | 2 +- .../java/io/jboot/test/config/TestI592AL.java | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 src/test/java/io/jboot/test/config/TestI592AL.java diff --git a/src/main/java/io/jboot/app/config/JbootConfigKit.java b/src/main/java/io/jboot/app/config/JbootConfigKit.java index 9a2b7d7a..96456176 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigKit.java +++ b/src/main/java/io/jboot/app/config/JbootConfigKit.java @@ -74,11 +74,14 @@ public class JbootConfigKit { public static String parseValue(String value) { + return parseValue(JbootConfigManager.me(), value); + } + + public static String parseValue(JbootConfigManager manager, String value) { List paras = parseParas(value); if (paras == null || paras.size() == 0) { return value; } - JbootConfigManager manager = JbootConfigManager.me(); StringBuilder retBuilder = new StringBuilder(value.length()); int index = 0; for (ConfigPara para : paras) { @@ -161,7 +164,7 @@ public class JbootConfigKit { static Properties readExternalProperties(String fileName) { String currentJarFilePath = JbootConfigKit.class.getProtectionDomain().getCodeSource().getLocation().getFile(); File fileDir = new File(currentJarFilePath).getParentFile(); - File externalProperties = new File(fileDir, fileName+".properties"); + File externalProperties = new File(fileDir, fileName + ".properties"); if (externalProperties.exists()) { return new JbootProp(externalProperties).getProperties(); } diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 0e10c0ad..bdd5d134 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -259,7 +259,7 @@ public class JbootConfigManager { String originalValue = getOriginalConfigValue(properties, key); String stringValue = decryptor != null ? decryptor.decrypt(key, originalValue) : originalValue; - return JbootConfigKit.parseValue(stringValue); + return JbootConfigKit.parseValue(this, stringValue); } diff --git a/src/test/java/io/jboot/test/config/TestI592AL.java b/src/test/java/io/jboot/test/config/TestI592AL.java new file mode 100644 index 00000000..ec5df0d2 --- /dev/null +++ b/src/test/java/io/jboot/test/config/TestI592AL.java @@ -0,0 +1,16 @@ +package io.jboot.test.config; + +import io.jboot.app.JbootApplication; + +/** + * test https://gitee.com/JbootProjects/jboot/issues/I592AL + */ +public class TestI592AL { + + public static void main(String[] args) { + JbootApplication.setBootArg("jboot.config.nacos.enable","true"); + JbootApplication.setBootArg("jboot.config.nacos.serverAddr","${NACOS_SERVER:127.0.0.1:8848}"); + + JbootApplication.run(args); + } +} -- Gitee From 497bbbfbf652d9c8a06f2c3047ab173ef165bb5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 27 Jun 2022 17:17:02 +0800 Subject: [PATCH 1734/1965] add --config support --- .../io/jboot/app/config/JbootConfigKit.java | 21 +++++++++++++++---- .../jboot/app/config/JbootConfigManager.java | 18 +++++++++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigKit.java b/src/main/java/io/jboot/app/config/JbootConfigKit.java index 96456176..a2e2c9db 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigKit.java +++ b/src/main/java/io/jboot/app/config/JbootConfigKit.java @@ -161,17 +161,30 @@ public class JbootConfigKit { } - static Properties readExternalProperties(String fileName) { + + static Properties readExternalProperties(String propFileName) { + if (!propFileName.endsWith(".properties")){ + propFileName = propFileName + ".properties"; + } + String currentJarFilePath = JbootConfigKit.class.getProtectionDomain().getCodeSource().getLocation().getFile(); File fileDir = new File(currentJarFilePath).getParentFile(); - File externalProperties = new File(fileDir, fileName + ".properties"); - if (externalProperties.exists()) { - return new JbootProp(externalProperties).getProperties(); + File externalProperties = new File(fileDir, propFileName); + return readPropertiesFile(externalProperties); + } + + + + static Properties readPropertiesFile(File propertiesFile) { + if (propertiesFile.exists()) { + return new JbootProp(propertiesFile).getProperties(); } return null; } + + public static Object convert(Class convertClass, String s, Type genericType) { if (convertClass == String.class || s == null || convertClass == Object.class) { diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index bdd5d134..19d67bbc 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -86,9 +86,21 @@ public class JbootConfigManager { } - Properties externalProperties = JbootConfigKit.readExternalProperties(fileName); - if (externalProperties != null && !externalProperties.isEmpty()) { - mainProperties.putAll(externalProperties); + + //通过启动参数 --config=./xxx.properties 来指定配置文件启动 + String configFile = getConfigValue(null, "config"); + Properties configFileProperties = null; + if (configFile != null && configFile.startsWith("./")) { + configFileProperties = JbootConfigKit.readExternalProperties(configFile.substring(2)); + } else if (configFile != null) { + configFileProperties = JbootConfigKit.readPropertiesFile(new File(configFile)); + } else { + //通过在 fatjar 的相同目录下,创建 jboot.properties 配置文件来启动 + configFileProperties = JbootConfigKit.readExternalProperties(fileName); + } + + if (configFileProperties != null && !configFileProperties.isEmpty()) { + mainProperties.putAll(configFileProperties); } -- Gitee From cca84a6ba3156b4faf1aa4cfc1bffdf93b84dd25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 27 Jun 2022 17:43:20 +0800 Subject: [PATCH 1735/1965] optimize JbootConfigManager --- .../io/jboot/app/config/JbootConfigKit.java | 27 ++++++++++++++----- .../jboot/app/config/JbootConfigManager.java | 17 +++--------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigKit.java b/src/main/java/io/jboot/app/config/JbootConfigKit.java index a2e2c9db..42ba45fc 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigKit.java +++ b/src/main/java/io/jboot/app/config/JbootConfigKit.java @@ -161,23 +161,36 @@ public class JbootConfigKit { } + static Properties readProperties(String pathName, String fileName) { + fileName = fileName.trim(); + if (!fileName.endsWith(".properties")){ + fileName = fileName + ".properties"; + } + if (pathName != null && pathName.trim().length() > 0) { + return new JbootProp(new File(pathName, fileName)).getProperties(); + } else { + return new JbootProp(fileName).getProperties(); + } + } + - static Properties readExternalProperties(String propFileName) { - if (!propFileName.endsWith(".properties")){ - propFileName = propFileName + ".properties"; + static Properties readExternalProperties(String fileName) { + fileName = fileName.trim(); + if (!fileName.endsWith(".properties")){ + fileName = fileName + ".properties"; } String currentJarFilePath = JbootConfigKit.class.getProtectionDomain().getCodeSource().getLocation().getFile(); File fileDir = new File(currentJarFilePath).getParentFile(); - File externalProperties = new File(fileDir, propFileName); + File externalProperties = new File(fileDir, fileName); return readPropertiesFile(externalProperties); } - static Properties readPropertiesFile(File propertiesFile) { - if (propertiesFile.exists()) { - return new JbootProp(propertiesFile).getProperties(); + static Properties readPropertiesFile(File propFile) { + if (propFile.exists()) { + return new JbootProp(propFile).getProperties(); } return null; } diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 19d67bbc..e52f1039 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -71,22 +71,21 @@ public class JbootConfigManager { } String pathName = getConfigValue(null, "jboot_properties_path"); - mainProperties = getProperties(pathName, fileName + ".properties"); + mainProperties = JbootConfigKit.readProperties(pathName, fileName); String mode = getConfigValue("jboot.app.mode"); if (JbootConfigKit.isNotBlank(mode)) { + //开始加载 mode properties //并全部添加覆盖掉掉 main properties - - String modePropertiesName = fileName + "-" + mode + ".properties"; - Properties modeProperties = getProperties(pathName, modePropertiesName); + String modePropertiesName = fileName + "-" + mode; + Properties modeProperties = JbootConfigKit.readProperties(pathName, modePropertiesName); mainProperties.putAll(modeProperties); } - //通过启动参数 --config=./xxx.properties 来指定配置文件启动 String configFile = getConfigValue(null, "config"); Properties configFileProperties = null; @@ -109,14 +108,6 @@ public class JbootConfigManager { } - private Properties getProperties(String pathName, String name) { - if (pathName != null && pathName.trim().length() > 0) { - return new JbootProp(new File(pathName, name)).getProperties(); - } else { - return new JbootProp(name).getProperties(); - } - } - public JbootConfigDecryptor getDecryptor() { return decryptor; } -- Gitee From f8a7422678346d2dba8430ebfd054a217bdde709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 28 Jun 2022 09:22:42 +0800 Subject: [PATCH 1736/1965] optimize JbootConfigManager --- .../io/jboot/app/config/JbootConfigKit.java | 36 +++++++++---------- .../jboot/app/config/JbootConfigManager.java | 13 ++++--- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigKit.java b/src/main/java/io/jboot/app/config/JbootConfigKit.java index 42ba45fc..67905113 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigKit.java +++ b/src/main/java/io/jboot/app/config/JbootConfigKit.java @@ -161,13 +161,14 @@ public class JbootConfigKit { } - static Properties readProperties(String pathName, String fileName) { - fileName = fileName.trim(); - if (!fileName.endsWith(".properties")){ - fileName = fileName + ".properties"; - } - if (pathName != null && pathName.trim().length() > 0) { - return new JbootProp(new File(pathName, fileName)).getProperties(); + static Properties readProperties(String fileName) { + return readProperties(null,fileName); + } + + static Properties readProperties(String path, String fileName) { + fileName = appendSuffixIfNecessary(fileName); + if (path != null && path.trim().length() > 0) { + return new JbootProp(new File(path, fileName)).getProperties(); } else { return new JbootProp(fileName).getProperties(); } @@ -175,29 +176,28 @@ public class JbootConfigKit { static Properties readExternalProperties(String fileName) { - fileName = fileName.trim(); - if (!fileName.endsWith(".properties")){ - fileName = fileName + ".properties"; - } - - String currentJarFilePath = JbootConfigKit.class.getProtectionDomain().getCodeSource().getLocation().getFile(); - File fileDir = new File(currentJarFilePath).getParentFile(); - File externalProperties = new File(fileDir, fileName); + fileName = appendSuffixIfNecessary(fileName); + String jarPath = JbootConfigKit.class.getProtectionDomain().getCodeSource().getLocation().getFile(); + File parentPath = new File(jarPath).getParentFile(); + File externalProperties = new File(parentPath, fileName); return readPropertiesFile(externalProperties); } + private static String appendSuffixIfNecessary(String fileName) { + fileName = fileName.trim(); + return fileName.endsWith(".properties") ? fileName : fileName + ".properties"; + } + static Properties readPropertiesFile(File propFile) { if (propFile.exists()) { return new JbootProp(propFile).getProperties(); } - return null; + return new Properties(); } - - public static Object convert(Class convertClass, String s, Type genericType) { if (convertClass == String.class || s == null || convertClass == Object.class) { diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index e52f1039..455b62dd 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -79,8 +79,8 @@ public class JbootConfigManager { //开始加载 mode properties //并全部添加覆盖掉掉 main properties - String modePropertiesName = fileName + "-" + mode; - Properties modeProperties = JbootConfigKit.readProperties(pathName, modePropertiesName); + String modeFileName = fileName + "-" + mode; + Properties modeProperties = JbootConfigKit.readProperties(pathName, modeFileName); mainProperties.putAll(modeProperties); } @@ -191,13 +191,12 @@ public class JbootConfigManager { private void refreshMainProperties() { - Properties properties = new JbootProp("jboot.properties").getProperties(); - mainProperties.putAll(properties); + mainProperties.putAll(JbootConfigKit.readProperties("jboot")); - String mode = getConfigValue(properties, "jboot.app.mode"); + String mode = getConfigValue(mainProperties, "jboot.app.mode"); if (JbootConfigKit.isNotBlank(mode)) { - String modePropertiesName = "jboot-" + mode + ".properties"; - mainProperties.putAll(new JbootProp(modePropertiesName).getProperties()); + String modeFileName = "jboot-" + mode; + mainProperties.putAll(JbootConfigKit.readProperties(modeFileName)); } } -- Gitee From ddf42b0919a6fabde5535db76fb453e1e181e53f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 28 Jun 2022 10:00:54 +0800 Subject: [PATCH 1737/1965] =?UTF-8?q?fixed=EF=BC=9A=E4=BF=AE=E5=A4=8D=20Ht?= =?UTF-8?q?tpUtil=20=E8=8E=B7=E5=8F=96=20GBK=20=E7=BD=91=E5=9D=80=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E4=B9=B1=E7=A0=81=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/http/JbootHttpResponse.java | 49 +++++++++++++------ .../components/http/jboot/JbootHttpImpl.java | 5 +- .../components/http/okhttp/OKHttpImpl.java | 6 +-- .../io/jboot/test/http/GbkContentTest.java | 17 +++++++ 4 files changed, 52 insertions(+), 25 deletions(-) create mode 100644 src/test/java/io/jboot/test/http/GbkContentTest.java diff --git a/src/main/java/io/jboot/components/http/JbootHttpResponse.java b/src/main/java/io/jboot/components/http/JbootHttpResponse.java index 8149f8ee..46b62043 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpResponse.java +++ b/src/main/java/io/jboot/components/http/JbootHttpResponse.java @@ -25,6 +25,7 @@ public class JbootHttpResponse implements Closeable { private static final Log LOG = Log.getLog(JbootHttpResponse.class); + private final JbootHttpRequest request; private String content; private OutputStream contentStream; private File file; @@ -33,24 +34,26 @@ public class JbootHttpResponse implements Closeable { private int responseCode; private String contentType; - public JbootHttpResponse() { - this.contentStream = new ByteArrayOutputStream(); - } + public JbootHttpResponse(JbootHttpRequest request) { + this.request = request; - public JbootHttpResponse(File file) { - if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) { - LOG.error("Can not mkdirs for: " + file.getParentFile()); - } + if (request.getDownloadFile() != null) { + if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) { + LOG.error("Can not mkdirs for: " + file.getParentFile()); + } - if (file.exists() && !file.delete()) { - LOG.error("Can not delete file: " + file); - } + if (file.exists() && !file.delete()) { + LOG.error("Can not delete file: " + file); + } - try { - this.file = file; - this.contentStream = new FileOutputStream(file); - } catch (Exception e) { - throw new RuntimeException(e); + try { + this.file = request.getDownloadFile(); + this.contentStream = new FileOutputStream(file); + } catch (Exception e) { + throw new RuntimeException(e); + } + } else { + this.contentStream = new ByteArrayOutputStream(); } } @@ -61,15 +64,29 @@ public class JbootHttpResponse implements Closeable { * @return */ public String getContent() { + return getContent(request.getCharset()); + } + + /** + * 获取数据内容 + * + * @return + */ + public String getContent(String charset) { if (content != null) { return content; } if (contentStream != null && contentStream instanceof ByteArrayOutputStream) { - return new String(((ByteArrayOutputStream) contentStream).toByteArray()); + try { + return ((ByteArrayOutputStream) contentStream).toString(charset); + } catch (UnsupportedEncodingException e) { + LOG.error(e.toString(), e); + } } return null; } + public void setContent(String content) { this.content = content; } diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index c223baf3..b3f82af4 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -44,10 +44,7 @@ public class JbootHttpImpl implements JbootHttp { @Override public JbootHttpResponse handle(JbootHttpRequest request) { - - JbootHttpResponse response = request.getDownloadFile() == null - ? new JbootHttpResponse() - : new JbootHttpResponse(request.getDownloadFile()); + JbootHttpResponse response = new JbootHttpResponse(request); doProcess(request, response); return response; } diff --git a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java index 2a2b4715..a6e3ce12 100644 --- a/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java +++ b/src/main/java/io/jboot/components/http/okhttp/OKHttpImpl.java @@ -39,12 +39,8 @@ public class OKHttpImpl implements JbootHttp { @Override public JbootHttpResponse handle(JbootHttpRequest request) { - - JbootHttpResponse response = request.getDownloadFile() == null - ? new JbootHttpResponse() - : new JbootHttpResponse(request.getDownloadFile()); + JbootHttpResponse response = new JbootHttpResponse(request); doProcess(request, response); - return response; } diff --git a/src/test/java/io/jboot/test/http/GbkContentTest.java b/src/test/java/io/jboot/test/http/GbkContentTest.java new file mode 100644 index 00000000..8f4637e4 --- /dev/null +++ b/src/test/java/io/jboot/test/http/GbkContentTest.java @@ -0,0 +1,17 @@ +package io.jboot.test.http; + +import io.jboot.components.http.JbootHttpRequest; +import io.jboot.components.http.JbootHttpResponse; +import io.jboot.utils.HttpUtil; + +public class GbkContentTest { + + public static void main(String[] args) { + JbootHttpRequest request = new JbootHttpRequest("http://www.**.com.cn/20.asp"); + request.setMethod(JbootHttpRequest.METHOD_GET); + request.setCharset("GBK"); + + JbootHttpResponse response = HttpUtil.handle(request); + System.out.println(response.getContent()); + } +} -- Gitee From 88d08315523eda3ba4369c2f842203f0792567c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 28 Jun 2022 10:17:20 +0800 Subject: [PATCH 1738/1965] v3.15.5 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 178f4d74..bc9292cf 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.15.4 + 3.15.5 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 88246510..5c1f9432 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.15.4 + 3.15.5 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 1a7511e1..7b25ebee 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.15.4 + 3.15.5 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.15.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.15.5', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 382cdcbf..fd26b322 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.15.4', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.15.5', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.4/jboot-3.15.4.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.5/jboot-3.15.5.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index c4ecd191..5bab2911 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.15.4 + 3.15.5 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index c350708a..48450332 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.15.4"; + public static String VERSION = "3.15.5"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From ade6fc7b036d9726d758e57ef34dbf3234feec10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 28 Jun 2022 10:17:35 +0800 Subject: [PATCH 1739/1965] v3.15.5 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ pom.xml | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1eae0253..b68e570d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.15.4 + 3.15.5 ``` diff --git a/changes.txt b/changes.txt index af13d189..38b95f8d 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.15.5: +修复:当开启 nacos 配置中心,有启用带参数内容 ${} 时,出现 StackOverFlow 的问题 +修复:HttpUtil 获取 GBK 网址内容时,出现乱码的问题 + + + jboot v3.15.4: 优化:升级适配 Seata 到最新版本 1.5.1,感谢 @扫地猿 优化:CookieUtil,方便在 Handler 里对 Cookie 进行操作 diff --git a/pom.xml b/pom.xml index 539a75c1..fd432578 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.5-SNAPSHOT + 3.15.5 jar jboot @@ -43,7 +43,7 @@ 5.0.0 2022.2 - 3.0 + 3.1 3.4 3.3.0 4.0.3 -- Gitee From 45c9ae0998adb4241e677be78e30c879935fb9fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 29 Jun 2022 11:40:19 +0800 Subject: [PATCH 1740/1965] v3.15.6 snapshot --- pom.xml | 2 +- .../java/io/jboot/db/model/JbootModel.java | 24 +++++-------------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/pom.xml b/pom.xml index fd432578..aaa31aad 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.5 + 3.15.6-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 4e75fe79..12d65cdd 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -136,7 +136,6 @@ public class JbootModel> extends Model { throw new IllegalArgumentException("columnName must not be null or empty."); } M dao = getOrCopyDao(); -// model._setExtAttr(JbootModelExts.DISTINCT, distinctColumnName); JbootModelExts.setDistinctColumn(dao, columnName); return dao; } @@ -146,36 +145,23 @@ public class JbootModel> extends Model { if (isCopyModel) { return (M) this; } else { - M dao = copy(false)._setConfigName(datasourceName); + M dao = copy()._setConfigName(datasourceName); dao.isCopyModel = true; return dao; } } - /** - * copy new model with all attrs - * - * @return - */ - public M copy() { - return copy(true); - } - - /** * copy model with attrs or false * - * @param withAttrs * @return */ - private M copy(boolean withAttrs) { + public M copy() { M m = null; try { m = (M) _getUsefulClass().newInstance(); - if (withAttrs) { - m.put(_getAttrs()); - } + m.put(_getAttrs()); } catch (Exception e) { LOG.error(e.toString(), e); } @@ -242,7 +228,7 @@ public class JbootModel> extends Model { private M use(String configName, boolean validateDatasourceExist) { M newDao = JbootModelExts.getDatasourceDAO(this, DATASOURCE_CACHE_PREFIX + configName); if (newDao == null) { - newDao = this.copy(false)._setConfigName(configName); + newDao = this.copy()._setConfigName(configName); if (newDao._getConfig() == null) { if (validateDatasourceExist) { throw new JbootIllegalConfigException("The datasource \"" + configName + "\" not config well, please config it in jboot.properties."); @@ -894,6 +880,8 @@ public class JbootModel> extends Model { return get(_getPrimaryKey()); } + + public Object[] _getIdValues() { String[] pkeys = _getPrimaryKeys(); Object[] values = new Object[pkeys.length]; -- Gitee From 076cb9d27370db0347061f30f2e68cb1a383fd5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 29 Jun 2022 11:51:26 +0800 Subject: [PATCH 1741/1965] JbootmqBase add config ThreadPool support --- .../io/jboot/components/mq/JbootmqBase.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index 9bc62267..986bcadb 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -31,6 +31,8 @@ public abstract class JbootmqBase implements Jbootmq { private static final Log LOG = Log.getLog(JbootmqBase.class); + protected final JbootmqConfig config; + private List globalListeners = new CopyOnWriteArrayList<>(); private Map> channelListeners = new ConcurrentHashMap<>(); @@ -38,9 +40,8 @@ public abstract class JbootmqBase implements Jbootmq { protected Set syncRecevieMessageChannels = new HashSet<>(); protected JbootSerializer serializer; - protected final JbootmqConfig config; - private final ExecutorService threadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, + private ExecutorService threadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), new NamedThreadFactory("jbootmq")); @@ -217,4 +218,16 @@ public abstract class JbootmqBase implements Jbootmq { public JbootmqConfig getConfig() { return config; } + + public void setSerializer(JbootSerializer serializer) { + this.serializer = serializer; + } + + public ExecutorService getThreadPool() { + return threadPool; + } + + public void setThreadPool(ExecutorService threadPool) { + this.threadPool = threadPool; + } } -- Gitee From 8e3c98dcfc164f5f4b961d878357296fa0f8b8d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 1 Jul 2022 10:49:04 +0800 Subject: [PATCH 1742/1965] optimize JbootRedismqImpl.java --- .../components/mq/redismq/JbootRedismqImpl.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java index f814e149..6153888d 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java @@ -37,6 +37,7 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { private JbootRedis redis; private Thread dequeueThread; private BinaryJedisPubSub jedisPubSub; + private long interval = 100L; public JbootRedismqImpl(JbootmqConfig config) { super(config); @@ -109,7 +110,7 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { while (isStarted) { try { doExecuteDequeue(); - Thread.sleep(1); + Thread.sleep(interval); } catch (Exception ex) { LOG.error(ex.toString(), ex); } @@ -118,10 +119,18 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { public void doExecuteDequeue() { for (String channel : this.channels) { - Object data = redis.lpop(channel); + Object data = redis.rpop(channel); if (data != null) { notifyListeners(channel, data, new RedismqMessageContext(JbootRedismqImpl.this)); } } } + + public long getInterval() { + return interval; + } + + public void setInterval(long interval) { + this.interval = interval; + } } -- Gitee From 01dc886d209affb60ec9ac9544266a41cc82a5d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 1 Jul 2022 10:55:34 +0800 Subject: [PATCH 1743/1965] optimize... --- src/main/java/io/jboot/app/config/JbootConfigManager.java | 4 ---- src/test/java/io/jboot/test/TestAppStater.java | 1 + src/test/resources/jboot-product.properties | 2 ++ 3 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 src/test/resources/jboot-product.properties diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 455b62dd..5c6185fa 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -428,10 +428,6 @@ public class JbootConfigManager { } Collection listeners = listenersMultimap.get(key); - if (listeners == null || listeners.isEmpty()) { - return; - } - for (JbootConfigChangeListener listener : listeners) { try { listener.onChange(key, newValue, oldValue); diff --git a/src/test/java/io/jboot/test/TestAppStater.java b/src/test/java/io/jboot/test/TestAppStater.java index 4bf36c17..00123fb2 100644 --- a/src/test/java/io/jboot/test/TestAppStater.java +++ b/src/test/java/io/jboot/test/TestAppStater.java @@ -5,6 +5,7 @@ import io.jboot.app.JbootApplication; public class TestAppStater { public static void main(String[] args){ + JbootApplication.setBootArg("jboot.app.mode","product"); JbootApplication.run(args); } } diff --git a/src/test/resources/jboot-product.properties b/src/test/resources/jboot-product.properties new file mode 100644 index 00000000..9a9079f5 --- /dev/null +++ b/src/test/resources/jboot-product.properties @@ -0,0 +1,2 @@ +undertow.host=0.0.0.0 +undertow.port=6666 \ No newline at end of file -- Gitee From 6214118cf7f7d91ccfaa2d27c0e4072882c13520 Mon Sep 17 00:00:00 2001 From: gisphm Date: Wed, 6 Jul 2022 01:56:32 +0000 Subject: [PATCH 1744/1965] =?UTF-8?q?vuepress-reco=20=E7=9A=84=E9=A2=84?= =?UTF-8?q?=E8=A7=88=E9=87=8C=E6=B2=A1=E6=B3=95=E6=9F=A5=E7=9C=8B=20json?= =?UTF-8?q?=20=E8=BF=99=E4=B8=AA=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/docs/json.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/docs/json.md b/doc/docs/json.md index 591ec967..c037ecd4 100644 --- a/doc/docs/json.md +++ b/doc/docs/json.md @@ -430,7 +430,7 @@ public void array() { } ``` -此处要注意,如果我们想获取一个泛型 List,需要使用如下方法: +此处要注意,如果我们想获取一个泛型 `List`,需要使用如下方法: ```java public void array() { -- Gitee From 270e6c759b0780d3fafb789f80ee74d090a30a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 6 Jul 2022 10:04:01 +0800 Subject: [PATCH 1745/1965] optimize JbootPaginateDirective.java --- .../jboot/web/directive/JbootPaginateDirective.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/io/jboot/web/directive/JbootPaginateDirective.java b/src/main/java/io/jboot/web/directive/JbootPaginateDirective.java index 099b83e7..cdc1e48d 100644 --- a/src/main/java/io/jboot/web/directive/JbootPaginateDirective.java +++ b/src/main/java/io/jboot/web/directive/JbootPaginateDirective.java @@ -26,6 +26,7 @@ import io.jboot.web.directive.base.PaginateDirectiveBase; import javax.servlet.http.HttpServletRequest; import java.util.Enumeration; +import java.util.Map; public class JbootPaginateDirective extends PaginateDirectiveBase { @@ -116,6 +117,16 @@ public class JbootPaginateDirective extends PaginateDirectiveBase { } } } + + Map rootData = scope.getRootData(); + if (rootData != null){ + for (Object data : rootData.values()) { + if (data instanceof Page){ + return (Page) data; + } + } + } + return null; } -- Gitee From 3cf9a40554f56abda6659230a2819a31587836b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 6 Jul 2022 16:44:53 +0800 Subject: [PATCH 1746/1965] delete JbootModel.getBigInteger and JbootModel.getBigDecimal; add JbootModel.columnsProcess() method --- .../java/io/jboot/db/model/JbootModel.java | 58 ++++--------------- 1 file changed, 10 insertions(+), 48 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 12d65cdd..eaa06667 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -27,8 +27,6 @@ import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; -import java.math.BigDecimal; -import java.math.BigInteger; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -444,24 +442,17 @@ public class JbootModel> extends Model { if (column == null || !column.checkAvailable()) { throw new IllegalArgumentException("Column or value must not be null."); } - return deleteByColumns(Arrays.asList(column)); + return deleteByColumns(Columns.create(column)); } public boolean deleteByColumns(Columns columns) { + columnsProcess(columns, "delete"); if (columns == null || columns.isEmpty()) { throw new IllegalArgumentException("Columns must not be null or empty."); } - return deleteByColumns(columns.getList()); - } - - - public boolean deleteByColumns(List columns) { - if (columns == null || columns.isEmpty()) { - throw new IllegalArgumentException("Columns must not be null or empty."); - } - String sql = _getDialect().forDeleteByColumns(alias, joins, _getTableName(), columns); - return Db.use(_getConfig().getName()).update(sql, Util.getValueArray(columns)) >= 1; + String sql = _getDialect().forDeleteByColumns(alias, joins, _getTableName(), columns.getList()); + return Db.use(_getConfig().getName()).update(sql, Util.getValueArray(columns.getList())) >= 1; } @@ -596,6 +587,7 @@ public class JbootModel> extends Model { } public M findFirstByColumns(Columns columns, String orderby, String loadColumns) { + columnsProcess(columns, "findfirst"); if (StrUtil.isBlank(loadColumns) && this.loadColumns != null) { loadColumns = this.loadColumns; } @@ -692,7 +684,6 @@ public class JbootModel> extends Model { public List findListByColumn(Column column, String orderBy, Integer count) { if (column == null || !column.checkAvailable()) { -// throw new IllegalArgumentException("Column or value must not be null."); return null; } return findListByColumns(Columns.create(column), orderBy, count); @@ -732,12 +723,17 @@ public class JbootModel> extends Model { } public List findListByColumns(Columns columns, String orderBy, Integer count, String loadColumns) { + columnsProcess(columns, "findlist"); loadColumns = getLoadColumns(loadColumns); String sql = _getDialect().forFindByColumns(alias, joins, _getTableName(), loadColumns, columns.getList(), orderBy, count); return columns.isEmpty() ? find(sql) : find(sql, columns.getValueArray()); } + //方便在某些场景下,对 columns 进行二次加工 + protected void columnsProcess(Columns columns, String action) { + } + private String getLoadColumns(String loadColumns) { if (StrUtil.isBlank(loadColumns) && StrUtil.isNotBlank(this.loadColumns)) { loadColumns = this.loadColumns; @@ -881,7 +877,6 @@ public class JbootModel> extends Model { } - public Object[] _getIdValues() { String[] pkeys = _getPrimaryKeys(); Object[] values = new Object[pkeys.length]; @@ -1041,37 +1036,4 @@ public class JbootModel> extends Model { }, config, sql, paras); } - - ////// - - @Override - public BigInteger getBigInteger(String attr) { - Object data = _getAttrs().get(attr); - if (data instanceof BigInteger) { - return (BigInteger) data; - } - //数据类型 id(19 number)在 Oracle Jdbc 下对应的是 BigDecimal, - //但是在 MySql 下对应的是 BigInteger,这会导致在 MySql 下生成的代码无法在 Oracle 数据库中使用 - //此处是为了解决这个问题的 - else if (data instanceof BigDecimal) { - return ((BigDecimal) data).toBigInteger(); - } else if (data instanceof Number) { - return BigInteger.valueOf(((Number) data).longValue()); - } - //可能会抛出异常,应该让其抛出 - return (BigInteger) data; - } - - - @Override - public BigDecimal getBigDecimal(String attr) { - Object data = _getAttrs().get(attr); - if (data instanceof BigDecimal) { - return (BigDecimal) data; - } else if (data instanceof Number) { - return new BigDecimal(data.toString()); - } - //可能会抛出异常,应该让其抛出 - return (BigDecimal) data; - } } -- Gitee From ab717ad471c2caf77352d6be34767166ac5ea95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 6 Jul 2022 16:53:15 +0800 Subject: [PATCH 1747/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20JbootModel.proce?= =?UTF-8?q?ssColumns()=20=E6=96=B9=E6=B3=95=EF=BC=8C=E5=9C=A8=E6=9F=90?= =?UTF-8?q?=E4=BA=9B=E5=9C=BA=E6=99=AF=E4=B8=8B=E7=94=A8=E4=BA=8E=E5=AF=B9?= =?UTF-8?q?=20Columns=20=E8=BF=9B=E8=A1=8C=E4=BA=8C=E6=AC=A1=E5=8A=A0?= =?UTF-8?q?=E5=B7=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/JbootModel.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index eaa06667..00956e78 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -447,8 +447,9 @@ public class JbootModel> extends Model { public boolean deleteByColumns(Columns columns) { - columnsProcess(columns, "delete"); - if (columns == null || columns.isEmpty()) { + processColumns(columns, "delete"); + + if (columns.isEmpty()) { throw new IllegalArgumentException("Columns must not be null or empty."); } String sql = _getDialect().forDeleteByColumns(alias, joins, _getTableName(), columns.getList()); @@ -587,7 +588,7 @@ public class JbootModel> extends Model { } public M findFirstByColumns(Columns columns, String orderby, String loadColumns) { - columnsProcess(columns, "findfirst"); + processColumns(columns, "findFirst"); if (StrUtil.isBlank(loadColumns) && this.loadColumns != null) { loadColumns = this.loadColumns; } @@ -723,7 +724,7 @@ public class JbootModel> extends Model { } public List findListByColumns(Columns columns, String orderBy, Integer count, String loadColumns) { - columnsProcess(columns, "findlist"); + processColumns(columns, "findList"); loadColumns = getLoadColumns(loadColumns); String sql = _getDialect().forFindByColumns(alias, joins, _getTableName(), loadColumns, columns.getList(), orderBy, count); return columns.isEmpty() ? find(sql) : find(sql, columns.getValueArray()); @@ -731,7 +732,7 @@ public class JbootModel> extends Model { //方便在某些场景下,对 columns 进行二次加工 - protected void columnsProcess(Columns columns, String action) { + protected void processColumns(Columns columns, String action) { } private String getLoadColumns(String loadColumns) { @@ -819,6 +820,8 @@ public class JbootModel> extends Model { } public Page paginateByColumns(int pageNumber, int pageSize, Columns columns, String orderBy, String loadColumns) { + processColumns(columns, "paginate"); + loadColumns = getLoadColumns(loadColumns); @@ -855,6 +858,8 @@ public class JbootModel> extends Model { public long findCountByColumns(Columns columns) { + processColumns(columns, "findCount"); + String loadColumns = "*"; //使用 distinct -- Gitee From bbde4fa71ee5ef2e356a23ba36b38f1776ec7b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 6 Jul 2022 17:41:24 +0800 Subject: [PATCH 1748/1965] v3.15.6 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index bc9292cf..79db7e5a 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.15.5 + 3.15.6 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 5c1f9432..2b07e963 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.15.5 + 3.15.6 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 7b25ebee..483e7f1c 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.15.5 + 3.15.6 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.15.5', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.15.6', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index fd26b322..9924edcb 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.15.5', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.15.6', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.5/jboot-3.15.5.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.6/jboot-3.15.6.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 5bab2911..b66f5a6c 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.15.5 + 3.15.6 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 48450332..8d5d1514 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.15.5"; + public static String VERSION = "3.15.6"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From af5d63e76f1ec052314fa821347b3ee20f521070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 6 Jul 2022 17:41:35 +0800 Subject: [PATCH 1749/1965] v3.15.6 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 10 ++++++++++ pom.xml | 4 ++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b68e570d..a9cd3954 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.15.5 + 3.15.6 ``` diff --git a/changes.txt b/changes.txt index 38b95f8d..1f44b050 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,13 @@ +jboot v3.15.6: +新增:JbootmqBase 添加自定义线程池的接口方法 +新增:JbootModel.processColumns() 方法,在某些场景下用于对 Columns 进行二次加工 +优化:Redis MQ 的 lpush 对应应该是 rpop 的问题,同时添加 interval 设置方法 +优化:JbootPaginateDirective 添加自动从 scope 获取 Page 对象的方法 +优化:移除 JbootModel 的 getBigInteger 和 getBigDecimal 方法,新版本的 JFinal 已经实现了 +修复:修复 Model.use("datasource").save() 的时候无法正确保存数据的问题 + + + jboot v3.15.5: 修复:当开启 nacos 配置中心,有启用带参数内容 ${} 时,出现 StackOverFlow 的问题 修复:HttpUtil 获取 GBK 网址内容时,出现乱码的问题 diff --git a/pom.xml b/pom.xml index aaa31aad..e719f870 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.6-SNAPSHOT + 3.15.6 jar jboot @@ -41,7 +41,7 @@ - 5.0.0 + 5.0.1 2022.2 3.1 3.4 -- Gitee From 55a307a3cd8b091cf4471394ddccd51571d7f185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 6 Jul 2022 18:33:16 +0800 Subject: [PATCH 1750/1965] v3.15.6 release (^.^)YYa!! --- src/main/java/io/jboot/db/model/JbootModel.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 00956e78..96c1e754 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -735,6 +735,18 @@ public class JbootModel> extends Model { protected void processColumns(Columns columns, String action) { } + @Override + protected Class _getUsefulClass() { + Class c = getClass(); + // guice : Model$$EnhancerByGuice$$40471411 + // cglib : com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158 + // return c.getName().indexOf("EnhancerByCGLIB") == -1 ? c : c.getSuperclass(); + // return c.getName().indexOf("$$EnhancerBy") == -1 ? c : c.getSuperclass(); + + //不支持匿名类,匿名无法被创建 + return c.getName().indexOf("$") == -1 ? c : c.getSuperclass(); + } + private String getLoadColumns(String loadColumns) { if (StrUtil.isBlank(loadColumns) && StrUtil.isNotBlank(this.loadColumns)) { loadColumns = this.loadColumns; -- Gitee From 837a0354d11f31d31684149e63e7f65d24abc0ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 13 Jul 2022 18:24:18 +0800 Subject: [PATCH 1751/1965] v3.15.7 snapshot --- pom.xml | 2 +- src/main/java/io/jboot/utils/FileUtil.java | 8 ++++++++ src/main/java/io/jboot/utils/ModelUtil.java | 11 ++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e719f870..cc0899b9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.6 + 3.15.7-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index 0b0b7538..5add0c69 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -43,6 +43,14 @@ public class FileUtil { } + public static String removeSuffix(String src, String suffix) { + if (src != null && src.endsWith(suffix)) { + return src.substring(0, suffix.length()); + } + return src; + } + + public static String removeRootPath(String src) { return removePrefix(src, PathKit.getWebRootPath()); } diff --git a/src/main/java/io/jboot/utils/ModelUtil.java b/src/main/java/io/jboot/utils/ModelUtil.java index 9bf0c825..8aa288d5 100644 --- a/src/main/java/io/jboot/utils/ModelUtil.java +++ b/src/main/java/io/jboot/utils/ModelUtil.java @@ -15,6 +15,7 @@ */ package io.jboot.utils; +import com.jfinal.plugin.activerecord.Model; import com.jfinal.plugin.activerecord.Page; import io.jboot.db.model.JbootModel; import io.jboot.exception.JbootException; @@ -138,10 +139,18 @@ public class ModelUtil { try { return clazz.newInstance(); } catch (Exception e) { - throw new JbootException("can not newInstance class:" + clazz + "\n" + e, e); + throw new JbootException("Can not newInstance with class: " + clazz.getName() + "\n" + e, e); } } + public static void keep(List models, String... attrs) { + if (models == null || models.isEmpty()) { + return; + } + + models.forEach(model -> model.keep(attrs)); + } + } -- Gitee From 7c8238316dcf9592495a11df46e3fc75d507b222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 14 Jul 2022 17:05:40 +0800 Subject: [PATCH 1752/1965] v3.15.7 snapshot --- .../io/jboot/web/handler/JbootHandler.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/main/java/io/jboot/web/handler/JbootHandler.java b/src/main/java/io/jboot/web/handler/JbootHandler.java index 152d577a..ceed4379 100644 --- a/src/main/java/io/jboot/web/handler/JbootHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootHandler.java @@ -22,12 +22,41 @@ import io.jboot.web.session.JbootServletRequestWrapper; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.util.HashSet; +import java.util.Set; /** * 用于对 request 封装 和 CPATH 的设置 */ public class JbootHandler extends Handler { + private static boolean blockedSuffixEnable = false; + private static Set blockedSuffixes = new HashSet<>(); + + static { + blockedSuffixes.add(".asp"); + blockedSuffixes.add(".aspx"); + blockedSuffixes.add(".php"); + blockedSuffixes.add(".jsp"); + blockedSuffixes.add(".jspx"); + } + + public static boolean isBlockedSuffixEnable() { + return blockedSuffixEnable; + } + + public static void setBlockedSuffixEnable(boolean blockedSuffixEnable) { + JbootHandler.blockedSuffixEnable = blockedSuffixEnable; + } + + public static Set getBlockedSuffixes() { + return blockedSuffixes; + } + + public static void addBlockedSuffix(String suffix) { + blockedSuffixes.add(suffix.trim().toLowerCase()); + } + @Override public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { @@ -40,10 +69,24 @@ public class JbootHandler extends Handler { } } + private void doHandle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { request.setAttribute(JbootConsts.ATTR_REQUEST, request); request.setAttribute(JbootConsts.ATTR_CONTEXT_PATH, request.getContextPath()); + next.handle(target, request, response, isHandled); + + + // 让脚本页面使用 Error Render 来渲染 + if (isHandled[0] == false && blockedSuffixEnable) { + int indexOf = target.lastIndexOf("."); + if (indexOf > 0) { + String suffix = target.substring(indexOf); + if (blockedSuffixes.contains(suffix.trim().toLowerCase())) { + com.jfinal.kit.HandlerKit.renderError404(request, response, isHandled); + } + } + } } -- Gitee From 2f9eec70ed09be9c33189bf8f8b92e940a04c704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 15 Jul 2022 17:53:38 +0800 Subject: [PATCH 1753/1965] add CollectionUtil.toString() --- .../java/io/jboot/utils/CollectionUtil.java | 22 ++++++---- .../io/jboot/web/handler/JbootHandler.java | 41 ------------------- 2 files changed, 15 insertions(+), 48 deletions(-) diff --git a/src/main/java/io/jboot/utils/CollectionUtil.java b/src/main/java/io/jboot/utils/CollectionUtil.java index f1950c4b..3b40b4fb 100644 --- a/src/main/java/io/jboot/utils/CollectionUtil.java +++ b/src/main/java/io/jboot/utils/CollectionUtil.java @@ -15,18 +15,15 @@ */ package io.jboot.utils; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import java.util.*; /** * @author michael yang (fuhai999@gmail.com) - * @Date: 2020/3/20 */ public class CollectionUtil { - public static Map string2Map(String s){ - Map map = new HashMap(); + public static Map string2Map(String s) { + Map map = new LinkedHashMap<>(); String[] strings = s.split(","); for (String kv : strings) { if (kv != null && kv.contains(":")) { @@ -39,7 +36,18 @@ public class CollectionUtil { return map; } - public static boolean isEmpty(Collection collection){ + public static boolean isEmpty(Collection collection) { return collection == null || collection.isEmpty(); } + + + public static String toString(Collection collection, String delimiter) { + StringJoiner sb = new StringJoiner(delimiter); + if (collection != null) { + for (Object o : collection) { + sb.add(String.valueOf(o)); + } + } + return sb.toString(); + } } diff --git a/src/main/java/io/jboot/web/handler/JbootHandler.java b/src/main/java/io/jboot/web/handler/JbootHandler.java index ceed4379..93f5a6ac 100644 --- a/src/main/java/io/jboot/web/handler/JbootHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootHandler.java @@ -22,41 +22,12 @@ import io.jboot.web.session.JbootServletRequestWrapper; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.util.HashSet; -import java.util.Set; /** * 用于对 request 封装 和 CPATH 的设置 */ public class JbootHandler extends Handler { - private static boolean blockedSuffixEnable = false; - private static Set blockedSuffixes = new HashSet<>(); - - static { - blockedSuffixes.add(".asp"); - blockedSuffixes.add(".aspx"); - blockedSuffixes.add(".php"); - blockedSuffixes.add(".jsp"); - blockedSuffixes.add(".jspx"); - } - - public static boolean isBlockedSuffixEnable() { - return blockedSuffixEnable; - } - - public static void setBlockedSuffixEnable(boolean blockedSuffixEnable) { - JbootHandler.blockedSuffixEnable = blockedSuffixEnable; - } - - public static Set getBlockedSuffixes() { - return blockedSuffixes; - } - - public static void addBlockedSuffix(String suffix) { - blockedSuffixes.add(suffix.trim().toLowerCase()); - } - @Override public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { @@ -75,18 +46,6 @@ public class JbootHandler extends Handler { request.setAttribute(JbootConsts.ATTR_CONTEXT_PATH, request.getContextPath()); next.handle(target, request, response, isHandled); - - - // 让脚本页面使用 Error Render 来渲染 - if (isHandled[0] == false && blockedSuffixEnable) { - int indexOf = target.lastIndexOf("."); - if (indexOf > 0) { - String suffix = target.substring(indexOf); - if (blockedSuffixes.contains(suffix.trim().toLowerCase())) { - com.jfinal.kit.HandlerKit.renderError404(request, response, isHandled); - } - } - } } -- Gitee From 3425e79a7ba9fae94fe22a05f5077004f9e6a402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 15 Jul 2022 18:06:56 +0800 Subject: [PATCH 1754/1965] v3.15.7 snapshot --- .../java/io/jboot/utils/CollectionUtil.java | 2 +- src/main/java/io/jboot/utils/FileUtil.java | 17 ++++++++++++++--- src/main/java/io/jboot/utils/NetUtil.java | 4 ++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/utils/CollectionUtil.java b/src/main/java/io/jboot/utils/CollectionUtil.java index 3b40b4fb..685587f7 100644 --- a/src/main/java/io/jboot/utils/CollectionUtil.java +++ b/src/main/java/io/jboot/utils/CollectionUtil.java @@ -29,7 +29,7 @@ public class CollectionUtil { if (kv != null && kv.contains(":")) { String[] keyValue = kv.split(":"); if (keyValue.length == 2) { - map.put(keyValue[0], keyValue[1]); + map.put(keyValue[0].trim(), keyValue[1].trim()); } } } diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index 5add0c69..35b04725 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -55,7 +55,13 @@ public class FileUtil { return removePrefix(src, PathKit.getWebRootPath()); } + public static String readString(File file) { + return readString(file, JFinal.me().getConstants().getEncoding()); + } + + + public static String readString(File file, String charsetName) { ByteArrayOutputStream baos = null; FileInputStream fis = null; try { @@ -65,7 +71,7 @@ public class FileUtil { for (int len = 0; (len = fis.read(buffer)) > 0; ) { baos.write(buffer, 0, len); } - return new String(baos.toByteArray(), JFinal.me().getConstants().getEncoding()); + return baos.toString(charsetName); } catch (Exception e) { LogKit.error(e.toString(), e); } finally { @@ -74,14 +80,19 @@ public class FileUtil { return null; } - public static void writeString(File file, String string) { + public static void writeString(File file, String content) { + writeString(file, content, JFinal.me().getConstants().getEncoding()); + } + + + public static void writeString(File file, String content, String charsetName) { if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } FileOutputStream fos = null; try { fos = new FileOutputStream(file, false); - fos.write(string.getBytes(JFinal.me().getConstants().getEncoding())); + fos.write(content.getBytes(charsetName)); } catch (Exception e) { LogKit.error(e.toString(), e); } finally { diff --git a/src/main/java/io/jboot/utils/NetUtil.java b/src/main/java/io/jboot/utils/NetUtil.java index 3378c2b9..67223955 100644 --- a/src/main/java/io/jboot/utils/NetUtil.java +++ b/src/main/java/io/jboot/utils/NetUtil.java @@ -35,13 +35,13 @@ public class NetUtil { address = addresses.nextElement(); if (!address.isSiteLocalAddress() && !address.isLoopbackAddress() - && address.getHostAddress().indexOf(":") == -1) {// 外网IP + && !address.getHostAddress().contains(":")) { // 外网IP siteLocalIpAddress = address.getHostAddress(); findSiteLocalIpAddress = true; break; } else if (address.isSiteLocalAddress() && !address.isLoopbackAddress() - && address.getHostAddress().indexOf(":") == -1) {// 内网IP + && !address.getHostAddress().contains(":")) { // 内网IP hostIpAddress = address.getHostAddress(); } } -- Gitee From ce94bd2086b7bebe7cd45e62ab5bf2c2d9a71f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 16 Jul 2022 10:27:39 +0800 Subject: [PATCH 1755/1965] v3.15.7 --- src/main/java/io/jboot/utils/FileUtil.java | 24 ++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index 35b04725..cbeb4aa5 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -35,17 +35,26 @@ public class FileUtil { } - public static String removePrefix(String src, String prefix) { - if (src != null && src.startsWith(prefix)) { - return src.substring(prefix.length()); + public static String removePrefix(String src, String... prefixes) { + if (src != null) { + for (String prefix : prefixes) { + if (src.startsWith(prefix)) { + return src.substring(prefix.length()); + } + } } + return src; } - public static String removeSuffix(String src, String suffix) { - if (src != null && src.endsWith(suffix)) { - return src.substring(0, suffix.length()); + public static String removeSuffix(String src, String... suffixes) { + if (src != null) { + for (String suffix : suffixes) { + if (src.endsWith(suffix)) { + return src.substring(0, suffix.length()); + } + } } return src; } @@ -100,6 +109,7 @@ public class FileUtil { } } + public static void close(Closeable... closeable) { QuietlyUtil.closeQuietly(closeable); } @@ -158,6 +168,7 @@ public class FileUtil { } } + private static boolean isNotSafeFile(String name) { name = name.toLowerCase(); return name.endsWith(".jsp") || name.endsWith(".jspx"); @@ -168,6 +179,7 @@ public class FileUtil { return StrUtil.isNotBlank(path) && (path.startsWith("/") || path.indexOf(":") > 0); } + public static String getCanonicalPath(File file) { try { return file.getCanonicalPath(); -- Gitee From 4ff94fc3de6b05cff6fc868eb965388d96e527a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 16 Jul 2022 11:59:42 +0800 Subject: [PATCH 1756/1965] v3.15.7 --- src/main/java/io/jboot/utils/ArrayUtil.java | 16 ++++++++++++---- src/main/java/io/jboot/utils/FileUtil.java | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/utils/ArrayUtil.java b/src/main/java/io/jboot/utils/ArrayUtil.java index a5df3ac5..c859f1c0 100644 --- a/src/main/java/io/jboot/utils/ArrayUtil.java +++ b/src/main/java/io/jboot/utils/ArrayUtil.java @@ -15,10 +15,7 @@ */ package io.jboot.utils; -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.Objects; +import java.util.*; public class ArrayUtil { @@ -90,4 +87,15 @@ public class ArrayUtil { return false; } + + public static String toString(Object[] strings, String delimiter) { + StringJoiner sb = new StringJoiner(delimiter); + if (strings != null) { + for (Object o : strings) { + sb.add(String.valueOf(o)); + } + } + return sb.toString(); + } + } diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index cbeb4aa5..299349e3 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -89,6 +89,7 @@ public class FileUtil { return null; } + public static void writeString(File file, String content) { writeString(file, content, JFinal.me().getConstants().getEncoding()); } -- Gitee From fc9ca055546b37b3a1a6e3064553016b7f01b4c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 16 Jul 2022 12:30:22 +0800 Subject: [PATCH 1757/1965] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=20convert=20?= =?UTF-8?q?=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/utils/DateUtil.java | 7 +++++++ src/main/java/io/jboot/utils/ObjectUtil.java | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/utils/DateUtil.java b/src/main/java/io/jboot/utils/DateUtil.java index 9477e9e6..3b38e7b6 100644 --- a/src/main/java/io/jboot/utils/DateUtil.java +++ b/src/main/java/io/jboot/utils/DateUtil.java @@ -31,6 +31,7 @@ public class DateUtil { public static String datePatternWithoutDividing = "yyyyMMdd"; public static String datePattern = "yyyy-MM-dd"; public static final String dateMinutePattern = "yyyy-MM-dd HH:mm"; + public static final String dateMinutePattern2 = "yyyy-MM-dd'T'HH:mm"; public static String datetimePattern = "yyyy-MM-dd HH:mm:ss"; public static final String dateMillisecondPattern = "yyyy-MM-dd HH:mm:ss SSS"; public static final String dateCSTPattern = "EEE MMM dd HH:mm:ss zzz yyyy"; @@ -134,6 +135,9 @@ public class DateUtil { } else if (length == datePattern.length()) { return datePattern; } else if (length == dateMinutePattern.length()) { + if (dateString.contains("T")) { + return dateMinutePattern2; + } return dateMinutePattern; } else if (length == dateMillisecondPattern.length()) { return dateMillisecondPattern; @@ -1035,5 +1039,8 @@ public class DateUtil { System.out.println("本季度:" + toDateTimeString(getStartOfThisQuarter())); System.out.println("本季度:" + toDateTimeString(getEndOfThisQuarter())); + + System.out.println("datetime-local解析:" + parseDate("2022-12-03T16:00")); + } } diff --git a/src/main/java/io/jboot/utils/ObjectUtil.java b/src/main/java/io/jboot/utils/ObjectUtil.java index 7bae78f8..b709ce83 100644 --- a/src/main/java/io/jboot/utils/ObjectUtil.java +++ b/src/main/java/io/jboot/utils/ObjectUtil.java @@ -163,9 +163,9 @@ public class ObjectUtil { return Float.parseFloat(value.toString()); } else if (targetClass == Boolean.class || targetClass == boolean.class) { String v = value.toString().toLowerCase(); - if ("1".equals(v) || "true".equals(v)) { + if ("1".equals(v) || "true".equalsIgnoreCase(v)) { return Boolean.TRUE; - } else if ("0".equals(v) || "false".equals(v)) { + } else if ("0".equals(v) || "false".equalsIgnoreCase(v)) { return Boolean.FALSE; } else { throw new RuntimeException("Can not parse to boolean type of value: \"" + value + "\""); -- Gitee From 2c8f2687c6da42c3a8ba1ea55da362e7140eb28b Mon Sep 17 00:00:00 2001 From: guoyongkui Date: Sat, 16 Jul 2022 20:41:42 +0800 Subject: [PATCH 1758/1965] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9Arpc?= =?UTF-8?q?=E6=B3=A8=E5=85=A5=E6=97=B6=EF=BC=8C=E7=9B=B8=E5=90=8Cgroup?= =?UTF-8?q?=E5=92=8Cversion=E7=9A=84service=E5=88=9B=E5=BB=BA=E8=BF=87?= =?UTF-8?q?=E5=A4=9A=E9=87=8D=E5=A4=8D=E5=AF=B9=E8=B1=A1=EF=BC=8C=E8=80=8C?= =?UTF-8?q?motan=E5=8D=8F=E8=AE=AE=E4=B8=BAtcp=E9=95=BF=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=EF=BC=8C=E5=AF=BC=E8=87=B4=E7=B3=BB=E7=BB=9Ftcp=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E6=95=B0=E5=89=A7=E5=A2=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/components/rpc/JbootrpcBase.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java index 3d112b6b..fc86667b 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java @@ -52,7 +52,10 @@ public abstract class JbootrpcBase implements Jbootrpc { } protected String buildKey(Class interfaceClass, JbootrpcReferenceConfig config) { - return interfaceClass.getName() + "@" + System.identityHashCode(config); + StringBuilder sb = new StringBuilder(interfaceClass.getName()); + return sb.append(":").append(config.getGroup()) + .append(":").append(config.getVersion()) + .toString(); } protected synchronized void invokeOnStartIfNecessary() { -- Gitee From de3fe0621880504df4297063ca70af9ac9b059a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 17 Jul 2022 09:18:26 +0800 Subject: [PATCH 1759/1965] fixed: rpc build cache key error --- .../java/io/jboot/aop/JbootAopFactory.java | 11 +++-- .../io/jboot/components/rpc/JbootrpcBase.java | 5 +-- .../rpc/JbootrpcReferenceConfig.java | 17 ++++++++ .../components/rpc/ReferenceConfigCache.java | 40 ------------------- 4 files changed, 25 insertions(+), 48 deletions(-) delete mode 100644 src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index a28014cb..c0909b70 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -34,7 +34,7 @@ import io.jboot.components.mq.JbootmqMessageListener; import io.jboot.components.rpc.Jbootrpc; import io.jboot.components.rpc.JbootrpcManager; import io.jboot.components.rpc.JbootrpcReferenceConfig; -import io.jboot.components.rpc.ReferenceConfigCache; +import io.jboot.components.rpc.RPCUtil; import io.jboot.components.rpc.annotation.RPCInject; import io.jboot.db.model.JbootModel; import io.jboot.exception.JbootException; @@ -186,7 +186,7 @@ public class JbootAopFactory extends AopFactory { protected Object createFieldObjectLazy(Object targetObject, Field field) throws ReflectiveOperationException { - return JbootLazyLoaderFactory.me().getLoader().loadLazyObject(targetObject,field); + return JbootLazyLoaderFactory.me().getLoader().loadLazyObject(targetObject, field); } @@ -269,9 +269,12 @@ public class JbootAopFactory extends AopFactory { private Object createFieldObjectByRPCComponent(Object targetObject, Field field, RPCInject rpcInject) { try { Class fieldInjectedClass = field.getType(); - JbootrpcReferenceConfig referenceConfig = ReferenceConfigCache.getReferenceConfig(rpcInject); + + JbootrpcReferenceConfig config = new JbootrpcReferenceConfig(); + RPCUtil.appendAnnotation(RPCInject.class, rpcInject, config); + Jbootrpc jbootrpc = JbootrpcManager.me().getJbootrpc(); - return jbootrpc.serviceObtain(fieldInjectedClass, referenceConfig); + return jbootrpc.serviceObtain(fieldInjectedClass, config); } catch (NullPointerException npe) { LOG.error("Can not inject rpc service for \"" + field.getName() + "\" in class \"" + ClassUtil.getUsefulClass(targetObject.getClass()).getName() + "\", because @RPCInject.check ==\"true\" and target is not available. \n" + rpcInject, npe); } catch (Exception ex) { diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java index fc86667b..dfae7085 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java @@ -52,10 +52,7 @@ public abstract class JbootrpcBase implements Jbootrpc { } protected String buildKey(Class interfaceClass, JbootrpcReferenceConfig config) { - StringBuilder sb = new StringBuilder(interfaceClass.getName()); - return sb.append(":").append(config.getGroup()) - .append(":").append(config.getVersion()) - .toString(); + return interfaceClass + "@" + config.hashCode(); } protected synchronized void invokeOnStartIfNecessary() { diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java index 421adadb..a0e11bc3 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcReferenceConfig.java @@ -16,6 +16,7 @@ package io.jboot.components.rpc; import java.io.Serializable; +import java.util.Objects; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) @@ -273,4 +274,20 @@ public class JbootrpcReferenceConfig implements Serializable { this.id = id; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + JbootrpcReferenceConfig that = (JbootrpcReferenceConfig) o; + return Objects.equals(version, that.version) && Objects.equals(group, that.group) && Objects.equals(url, that.url) && Objects.equals(generic, that.generic) && Objects.equals(check, that.check) && Objects.equals(retries, that.retries) && Objects.equals(loadbalance, that.loadbalance) && Objects.equals(async, that.async) && Objects.equals(actives, that.actives) && Objects.equals(timeout, that.timeout) && Objects.equals(application, that.application) && Objects.equals(module, that.module) && Objects.equals(consumer, that.consumer) && Objects.equals(monitor, that.monitor) && Objects.equals(registry, that.registry) && Objects.equals(protocol, that.protocol) && Objects.equals(tag, that.tag) && Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(version, group, url, generic, check, retries, loadbalance, async, actives, timeout, application, module, consumer, monitor, registry, protocol, tag, id); + } } diff --git a/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java b/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java deleted file mode 100644 index 2b026425..00000000 --- a/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.components.rpc; - -import io.jboot.components.rpc.annotation.RPCInject; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class ReferenceConfigCache { - - private static Map configs = new ConcurrentHashMap<>(); - - public static JbootrpcReferenceConfig getReferenceConfig(RPCInject rpcInject) { - int identityHashCode = System.identityHashCode(rpcInject); - JbootrpcReferenceConfig referenceConfig = configs.get(identityHashCode); - if (referenceConfig == null) { - JbootrpcReferenceConfig config = new JbootrpcReferenceConfig(); - RPCUtil.appendAnnotation(RPCInject.class, rpcInject, config); - configs.putIfAbsent(identityHashCode, config); - - referenceConfig = configs.get(identityHashCode); - } - - return referenceConfig; - } -} -- Gitee From 729995e384537847aaaa3f4ccd29831680d5f22c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 17 Jul 2022 09:38:47 +0800 Subject: [PATCH 1760/1965] fixed: rpc build cache key error --- .../java/io/jboot/aop/JbootAopFactory.java | 9 +--- .../io/jboot/components/rpc/JbootrpcBase.java | 2 +- .../components/rpc/ReferenceConfigCache.java | 54 +++++++++++++++++++ 3 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index c0909b70..d4eaaf3f 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -31,10 +31,7 @@ import io.jboot.app.config.JbootConfigManager; import io.jboot.app.config.annotation.ConfigModel; import io.jboot.components.event.JbootEventListener; import io.jboot.components.mq.JbootmqMessageListener; -import io.jboot.components.rpc.Jbootrpc; -import io.jboot.components.rpc.JbootrpcManager; -import io.jboot.components.rpc.JbootrpcReferenceConfig; -import io.jboot.components.rpc.RPCUtil; +import io.jboot.components.rpc.*; import io.jboot.components.rpc.annotation.RPCInject; import io.jboot.db.model.JbootModel; import io.jboot.exception.JbootException; @@ -270,9 +267,7 @@ public class JbootAopFactory extends AopFactory { try { Class fieldInjectedClass = field.getType(); - JbootrpcReferenceConfig config = new JbootrpcReferenceConfig(); - RPCUtil.appendAnnotation(RPCInject.class, rpcInject, config); - + JbootrpcReferenceConfig config = ReferenceConfigCache.get(fieldInjectedClass, rpcInject); Jbootrpc jbootrpc = JbootrpcManager.me().getJbootrpc(); return jbootrpc.serviceObtain(fieldInjectedClass, config); } catch (NullPointerException npe) { diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java index dfae7085..bbd7cb4a 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java @@ -52,7 +52,7 @@ public abstract class JbootrpcBase implements Jbootrpc { } protected String buildKey(Class interfaceClass, JbootrpcReferenceConfig config) { - return interfaceClass + "@" + config.hashCode(); + return interfaceClass.getName() + "@" + config.hashCode(); } protected synchronized void invokeOnStartIfNecessary() { diff --git a/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java b/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java new file mode 100644 index 00000000..8e2258d9 --- /dev/null +++ b/src/main/java/io/jboot/components/rpc/ReferenceConfigCache.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.rpc; + +import io.jboot.components.rpc.annotation.RPCInject; + +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +public class ReferenceConfigCache { + + private static Map refrenceConfigCache = new ConcurrentHashMap<>(); + + + public static JbootrpcReferenceConfig get(Class targetClass, RPCInject rpcInject) { + String cacheKey = buildKey(targetClass, rpcInject); + JbootrpcReferenceConfig referenceConfig = refrenceConfigCache.get(cacheKey); + if (referenceConfig == null) { + JbootrpcReferenceConfig config = new JbootrpcReferenceConfig(); + RPCUtil.appendAnnotation(RPCInject.class, rpcInject, config); + refrenceConfigCache.putIfAbsent(cacheKey, config); + + referenceConfig = refrenceConfigCache.get(cacheKey); + } + return referenceConfig; + } + + + + private static String buildKey(Class targetClass, RPCInject rpcInject) { + return targetClass.getName() + "@" + hashCode(rpcInject); + } + + + private static int hashCode(RPCInject a) { + return Objects.hash(a.group(), a.version(), a.url(), a.generic(), a.check(), a.retries(), a.loadbalance(), a.async(), a.actives(), a.timeout() + , a.application(), a.module(), a.consumer(), a.monitor(), Arrays.hashCode(a.registry()), a.protocol(), a.tag(), a.id()); + } +} \ No newline at end of file -- Gitee From c707cf7a692f773c69da8654b0dd34b71c6abd7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 17 Jul 2022 09:59:23 +0800 Subject: [PATCH 1761/1965] optimize JbootrpcBase.java --- .../io/jboot/components/rpc/JbootrpcBase.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java index bbd7cb4a..4562627f 100644 --- a/src/main/java/io/jboot/components/rpc/JbootrpcBase.java +++ b/src/main/java/io/jboot/components/rpc/JbootrpcBase.java @@ -23,7 +23,7 @@ import java.util.concurrent.ConcurrentHashMap; public abstract class JbootrpcBase implements Jbootrpc { - protected static final Map objectCache = new ConcurrentHashMap<>(); + protected static final Map serviceCache = new ConcurrentHashMap<>(); protected static JbootrpcConfig rpcConfig = Jboot.config(JbootrpcConfig.class); private boolean started = false; @@ -31,24 +31,24 @@ public abstract class JbootrpcBase implements Jbootrpc { @Override public T serviceObtain(Class interfaceClass, JbootrpcReferenceConfig config) { String key = buildKey(interfaceClass, config); - T object = (T) objectCache.get(key); - if (object == null) { + T service = (T) serviceCache.get(key); + if (service == null) { synchronized (this) { - object = (T) objectCache.get(key); - if (object == null) { + service = (T) serviceCache.get(key); + if (service == null) { // onStart 方法是在 app 启动完成后,Jboot 主动去调用的 // 但是,在某些场景可能存在没有等 app 启动完成就去获取 Service 的情况 // 此时,需要主动先调用下 onStart 方法 invokeOnStartIfNecessary(); - object = onServiceCreate(interfaceClass, config); - if (object != null) { - objectCache.put(key, object); + service = onServiceCreate(interfaceClass, config); + if (service != null) { + serviceCache.put(key, service); } } } } - return object; + return service; } protected String buildKey(Class interfaceClass, JbootrpcReferenceConfig config) { -- Gitee From 7e3c6cbe2eade5a4563696c0e486f8955bfdba8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 17 Jul 2022 17:23:22 +0800 Subject: [PATCH 1762/1965] v3.15.7 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 79db7e5a..5e713c0d 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.15.6 + 3.15.7 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 2b07e963..e88856de 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.15.6 + 3.15.7 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 483e7f1c..62e4fb84 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.15.6 + 3.15.7 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.15.6', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.15.7', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 9924edcb..cffa829c 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.15.6', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.15.7', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.6/jboot-3.15.6.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.7/jboot-3.15.7.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index b66f5a6c..ba0b4c22 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.15.6 + 3.15.7 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 8d5d1514..5a56b5b3 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.15.6"; + public static String VERSION = "3.15.7"; public static final String ATTR_REQUEST = "REQUEST"; -- Gitee From 96f9a5f1cc4825bbc781da124809fca361ddac88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 17 Jul 2022 17:23:26 +0800 Subject: [PATCH 1763/1965] v3.15.7 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 10 ++++++++++ pom.xml | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a9cd3954..53058fe6 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.15.6 + 3.15.7 ``` diff --git a/changes.txt b/changes.txt index 1f44b050..8d14385f 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,13 @@ +jboot v3.15.7: +修复:RPC 的 Reference 缓存 key 构建错误导致无法命中缓存的问题 +新增:FileUtil.removeSuffix() 方法 +新增:ModelUtil.key(List models) 方法 +新增:CollectionUtil.toString() 方法 +新增:ArrayUtil.toString() 方法 +新增:DateUtil 解析 datetime-local 方法 + + + jboot v3.15.6: 新增:JbootmqBase 添加自定义线程池的接口方法 新增:JbootModel.processColumns() 方法,在某些场景下用于对 Columns 进行二次加工 diff --git a/pom.xml b/pom.xml index cc0899b9..e161a9de 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.7-SNAPSHOT + 3.15.7 jar jboot -- Gitee From 63f0f589ae5f5e302c2cb284bdead4975f6fea09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 20 Jul 2022 12:27:06 +0800 Subject: [PATCH 1764/1965] v3.16.0 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e161a9de..5776690c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.7 + 3.16.0-SNAPSHOT jar jboot -- Gitee From 366902bcf50f4370c5edc5d92023656ff13c4898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 2 Aug 2022 10:17:21 +0800 Subject: [PATCH 1765/1965] update docs --- doc/readme.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/readme.md b/doc/readme.md index 54e9a2db..e84f962f 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -29,5 +29,9 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos ## QQ群 -群1: 601440615(已满) -群2: 719614554 +- 群1: 601440615(已满) +- 群2: 719614554 + +## 备案信息 + +黔ICP备19009310号-7 © 2021-2022 Jboot.COM.CN ALLRIGHT RESERVER -- Gitee From 102ea7baa3c940c9dafc4490b560322e293a4882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 2 Aug 2022 10:17:39 +0800 Subject: [PATCH 1766/1965] add dm dialect --- src/main/java/io/jboot/db/ArpManager.java | 3 + .../jboot/db/datasource/DataSourceConfig.java | 1 + .../io/jboot/db/dialect/JbootDmDialect.java | 69 +++++++++++++++++++ .../io/jboot/db/driver/DriverClassNames.java | 1 + 4 files changed, 74 insertions(+) create mode 100644 src/main/java/io/jboot/db/dialect/JbootDmDialect.java diff --git a/src/main/java/io/jboot/db/ArpManager.java b/src/main/java/io/jboot/db/ArpManager.java index bbfdaa95..a0e57ace 100644 --- a/src/main/java/io/jboot/db/ArpManager.java +++ b/src/main/java/io/jboot/db/ArpManager.java @@ -251,6 +251,9 @@ public class ArpManager { case DataSourceConfig.TYPE_POSTGRESQL: activeRecordPlugin.setDialect(new JbootPostgreSqlDialect()); break; + case DataSourceConfig.TYPE_DM: + activeRecordPlugin.setDialect(new JbootDmDialect()); + break; case DataSourceConfig.TYPE_CLICKHOUSE: activeRecordPlugin.setDialect(new JbootClickHouseDialect()); break; diff --git a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java index a633021c..bff7ac1b 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java @@ -33,6 +33,7 @@ public class DataSourceConfig { public static final String TYPE_SQLITE = "sqlite"; public static final String TYPE_ANSISQL = "ansisql"; public static final String TYPE_POSTGRESQL = "postgresql"; + public static final String TYPE_DM = "dm"; public static final String TYPE_CLICKHOUSE = "clickhouse"; public static final String TYPE_INFORMIX = "informix"; diff --git a/src/main/java/io/jboot/db/dialect/JbootDmDialect.java b/src/main/java/io/jboot/db/dialect/JbootDmDialect.java new file mode 100644 index 00000000..992a83e3 --- /dev/null +++ b/src/main/java/io/jboot/db/dialect/JbootDmDialect.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.db.dialect; + +import com.jfinal.plugin.activerecord.dialect.MysqlDialect; +import io.jboot.db.model.Column; +import io.jboot.db.model.Join; +import io.jboot.db.model.SqlBuilder; + +import java.util.List; + +/** + * 达梦数据库的数据方言 + */ +public class JbootDmDialect extends MysqlDialect implements JbootDialect { + + @Override + public String forFindByColumns(String alias, List joins, String table, String loadColumns, List columns, String orderBy, Object limit) { + StringBuilder sqlBuilder = SqlBuilder.forFindByColumns(alias, joins, table, loadColumns, columns, orderBy, '`'); + + if (limit != null) { + sqlBuilder.append(" LIMIT " + limit); + } + + return sqlBuilder.toString(); + } + + @Override + public String forFindCountByColumns(String alias, List joins, String table, String loadColumns, List columns) { + return SqlBuilder.forFindCountByColumns(alias, joins, table, loadColumns, columns, '`'); + } + + @Override + public String forDeleteByColumns(String alias, List joins, String table, List columns) { + return SqlBuilder.forDeleteByColumns(alias, joins, table, columns, '`'); + } + + + @Override + public String forPaginateSelect(String loadColumns) { + return "SELECT " + loadColumns; + } + + + @Override + public String forPaginateFrom(String alias, List joins, String table, List columns, String orderBy) { + return SqlBuilder.forPaginateFrom(alias, joins, table, columns, orderBy, '`'); + } + + @Override + public String forPaginateTotalRow(String select, String sqlExceptSelect, Object ext) { + String distinctSql = SqlBuilder.forPaginateDistinctTotalRow(select, sqlExceptSelect, ext); + return distinctSql != null ? distinctSql : super.forPaginateTotalRow(select, sqlExceptSelect, ext); + } + +} diff --git a/src/main/java/io/jboot/db/driver/DriverClassNames.java b/src/main/java/io/jboot/db/driver/DriverClassNames.java index 0086d8b5..f903886a 100644 --- a/src/main/java/io/jboot/db/driver/DriverClassNames.java +++ b/src/main/java/io/jboot/db/driver/DriverClassNames.java @@ -32,6 +32,7 @@ public class DriverClassNames { driverClassNames.put(DataSourceConfig.TYPE_SQLSERVER, new String[]{"com.microsoft.sqlserver.jdbc.SQLServerDriver"}); driverClassNames.put(DataSourceConfig.TYPE_SQLITE, new String[]{"org.sqlite.JDBC"}); driverClassNames.put(DataSourceConfig.TYPE_POSTGRESQL, new String[]{"org.postgresql.Driver"}); + driverClassNames.put(DataSourceConfig.TYPE_DM, new String[]{"dm.jdbc.driver.DmDriver"}); driverClassNames.put(DataSourceConfig.TYPE_CLICKHOUSE, new String[]{"com.github.housepower.jdbc.ClickHouseDriver", "ru.yandex.clickhouse.ClickHouseDriver"}); driverClassNames.put(DataSourceConfig.TYPE_INFORMIX, new String[]{"com.informix.jdbc.IfxDriver"}); } -- Gitee From 974bac656fac19ff6ab7a809eec969d019cd15fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 4 Aug 2022 17:46:21 +0800 Subject: [PATCH 1767/1965] add getFirstFileOnly() method --- src/main/java/io/jboot/utils/FileUtil.java | 12 ++++- .../jboot/web/controller/JbootController.java | 46 +++++++++++++++++-- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index 299349e3..72461043 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -172,7 +172,7 @@ public class FileUtil { private static boolean isNotSafeFile(String name) { name = name.toLowerCase(); - return name.endsWith(".jsp") || name.endsWith(".jspx"); + return name.endsWith(".jsp") || name.endsWith(".jspx") || name.contains(".."); } @@ -189,4 +189,14 @@ public class FileUtil { } } + public static void delete(File file) { + if (file == null) { + return; + } + + if (!file.delete()) { + LogKit.error("file {} can not deleted!", getCanonicalPath(file)); + } + } + } \ No newline at end of file diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 2a023557..261be6a3 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -21,7 +21,9 @@ import com.jfinal.core.Controller; import com.jfinal.core.NotAction; import com.jfinal.kit.StrKit; import com.jfinal.render.RenderManager; +import com.jfinal.upload.UploadFile; import io.jboot.support.jwt.JwtManager; +import io.jboot.utils.FileUtil; import io.jboot.utils.RequestUtil; import io.jboot.utils.StrUtil; import io.jboot.utils.TypeDef; @@ -31,10 +33,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.math.BigDecimal; import java.math.BigInteger; -import java.util.Collection; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; +import java.util.*; public class JbootController extends Controller { @@ -749,7 +748,44 @@ public class JbootController extends Controller { } return attrs; } - + + + /** + * 建议使用 getFirstFileOnly,否则可能会有安全问题 + * @return + */ + @NotAction + @Deprecated + @Override + public UploadFile getFile() { + return super.getFile(); + } + + + /** + * 只获取第一个文件,若上传多个文件,则删除其他文件 + * @return + */ + @NotAction + public UploadFile getFirstFileOnly() { + List uploadFiles = getFiles(); + if (uploadFiles == null || uploadFiles.isEmpty()){ + return null; + } + + if (uploadFiles.size() == 1){ + return uploadFiles.get(0); + } + + for (int i = 1; i < uploadFiles.size(); i++) { + UploadFile uploadFile = uploadFiles.get(i); + FileUtil.delete(uploadFile.getFile()); + } + + return uploadFiles.get(0); + } + + @NotAction public String renderToStringWithAttrs(String template) { -- Gitee From ff04c9fc54222f9ae82dce62026018add7a0fedb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 5 Aug 2022 11:45:41 +0800 Subject: [PATCH 1768/1965] remove REQUEST Attr --- src/main/java/io/jboot/JbootConsts.java | 1 - src/main/java/io/jboot/web/handler/JbootHandler.java | 5 +++-- src/main/java/io/jboot/web/render/JbootJsonRender.java | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 5a56b5b3..9c9f9058 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -25,7 +25,6 @@ public class JbootConsts { public static String VERSION = "3.15.7"; - public static final String ATTR_REQUEST = "REQUEST"; public static final String ATTR_CONTEXT_PATH = "CPATH"; diff --git a/src/main/java/io/jboot/web/handler/JbootHandler.java b/src/main/java/io/jboot/web/handler/JbootHandler.java index 93f5a6ac..ef6889b9 100644 --- a/src/main/java/io/jboot/web/handler/JbootHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootHandler.java @@ -42,8 +42,9 @@ public class JbootHandler extends Handler { private void doHandle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { - request.setAttribute(JbootConsts.ATTR_REQUEST, request); - request.setAttribute(JbootConsts.ATTR_CONTEXT_PATH, request.getContextPath()); + if (request.getAttribute(JbootConsts.ATTR_CONTEXT_PATH) == null) { + request.setAttribute(JbootConsts.ATTR_CONTEXT_PATH, request.getContextPath()); + } next.handle(target, request, response, isHandled); } diff --git a/src/main/java/io/jboot/web/render/JbootJsonRender.java b/src/main/java/io/jboot/web/render/JbootJsonRender.java index 6bf71374..e7dc1cdb 100644 --- a/src/main/java/io/jboot/web/render/JbootJsonRender.java +++ b/src/main/java/io/jboot/web/render/JbootJsonRender.java @@ -25,7 +25,6 @@ import io.jboot.JbootConsts; public class JbootJsonRender extends JsonRender { static { - excludedAttrs.add(JbootConsts.ATTR_REQUEST); excludedAttrs.add(JbootConsts.ATTR_CONTEXT_PATH); } -- Gitee From 62681a3b8772bec9d81adcfb4247bf12fc024c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 5 Aug 2022 13:12:48 +0800 Subject: [PATCH 1769/1965] optimize ... --- src/main/java/io/jboot/utils/CookieUtil.java | 4 ++++ src/main/java/io/jboot/web/handler/JbootHandler.java | 5 +---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/utils/CookieUtil.java b/src/main/java/io/jboot/utils/CookieUtil.java index cdf50e37..228e0b62 100644 --- a/src/main/java/io/jboot/utils/CookieUtil.java +++ b/src/main/java/io/jboot/utils/CookieUtil.java @@ -109,6 +109,10 @@ public class CookieUtil { put(response, key, value, COOKIE_MAX_AGE, defaultPath, defaultDomain, COOKIE_ENCRYPT_KEY); } + public static void put(HttpServletResponse response, String key, Object value, int maxAgeInSeconds) { + put(response, key, value, maxAgeInSeconds, defaultPath, defaultDomain, COOKIE_ENCRYPT_KEY); + } + public static void put(Controller ctr, String key, Object value, String secretKey) { put(ctr, key, value, COOKIE_MAX_AGE, defaultPath, defaultDomain, secretKey); diff --git a/src/main/java/io/jboot/web/handler/JbootHandler.java b/src/main/java/io/jboot/web/handler/JbootHandler.java index ef6889b9..fe63c574 100644 --- a/src/main/java/io/jboot/web/handler/JbootHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootHandler.java @@ -42,10 +42,7 @@ public class JbootHandler extends Handler { private void doHandle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { - if (request.getAttribute(JbootConsts.ATTR_CONTEXT_PATH) == null) { - request.setAttribute(JbootConsts.ATTR_CONTEXT_PATH, request.getContextPath()); - } - + request.setAttribute(JbootConsts.ATTR_CONTEXT_PATH, request.getContextPath()); next.handle(target, request, response, isHandled); } -- Gitee From 192f24f9e4ecc9469f3d66b2d1d160b20b592edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 7 Aug 2022 17:25:44 +0800 Subject: [PATCH 1770/1965] v3.15.8 --- pom.xml | 2 +- .../java/io/jboot/db/dialect/JbootDmDialect.java | 5 +++++ .../java/io/jboot/test/jfinal/RedisLock.java | 4 ++++ .../java/io/jboot/test/jfinal/StaticInvoke.java | 16 ++++++++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/test/java/io/jboot/test/jfinal/RedisLock.java create mode 100644 src/test/java/io/jboot/test/jfinal/StaticInvoke.java diff --git a/pom.xml b/pom.xml index 5776690c..2de6b623 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ - 5.0.1 + 5.0.2 2022.2 3.1 3.4 diff --git a/src/main/java/io/jboot/db/dialect/JbootDmDialect.java b/src/main/java/io/jboot/db/dialect/JbootDmDialect.java index 992a83e3..bc407e98 100644 --- a/src/main/java/io/jboot/db/dialect/JbootDmDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootDmDialect.java @@ -27,6 +27,11 @@ import java.util.List; */ public class JbootDmDialect extends MysqlDialect implements JbootDialect { + public JbootDmDialect() { + throw new IllegalStateException("Not finished!"); + } + + @Override public String forFindByColumns(String alias, List joins, String table, String loadColumns, List columns, String orderBy, Object limit) { StringBuilder sqlBuilder = SqlBuilder.forFindByColumns(alias, joins, table, loadColumns, columns, orderBy, '`'); diff --git a/src/test/java/io/jboot/test/jfinal/RedisLock.java b/src/test/java/io/jboot/test/jfinal/RedisLock.java new file mode 100644 index 00000000..3a28a4b0 --- /dev/null +++ b/src/test/java/io/jboot/test/jfinal/RedisLock.java @@ -0,0 +1,4 @@ +package io.jboot.test.jfinal; + +public class RedisLock { +} diff --git a/src/test/java/io/jboot/test/jfinal/StaticInvoke.java b/src/test/java/io/jboot/test/jfinal/StaticInvoke.java new file mode 100644 index 00000000..856b4382 --- /dev/null +++ b/src/test/java/io/jboot/test/jfinal/StaticInvoke.java @@ -0,0 +1,16 @@ +package io.jboot.test.jfinal; + +import com.jfinal.template.Engine; + +public class StaticInvoke { + + public static void main(String[] args) { + + Engine engine = new Engine(); + String template = "#(io.jboot.utils.StrUtil::isNumeric('123'))"; + String string = engine.getTemplateByString(template).renderToString(null); + System.out.println(string); + + + } +} -- Gitee From 32bcf224273a97e28fb8d679c6e70d7727797d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 7 Aug 2022 17:33:02 +0800 Subject: [PATCH 1771/1965] v3.15.8 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 5e713c0d..5759b5cc 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.15.7 + 3.15.8 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index e88856de..0d2d8d91 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.15.7 + 3.15.8 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 62e4fb84..94a160bf 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.15.7 + 3.15.8 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.15.7', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.15.8', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index cffa829c..1ac7e4ce 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.15.7', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.15.8', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.7/jboot-3.15.7.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.8/jboot-3.15.8.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index ba0b4c22..431097aa 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.15.7 + 3.15.8 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 9c9f9058..d350bdd2 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.15.7"; + public static String VERSION = "3.15.8"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 0d75733b402cd50f66dfec1b9de43eafb05779ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 7 Aug 2022 17:33:07 +0800 Subject: [PATCH 1772/1965] v3.15.8 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 53058fe6..572ee452 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.15.7 + 3.15.8 ``` diff --git a/changes.txt b/changes.txt index 8d14385f..efd96359 100644 --- a/changes.txt +++ b/changes.txt @@ -1,7 +1,15 @@ +jboot v3.15.8: +新增:FileUtil.delete 方法 +新增:JbootController.getFirstFileOnly() 方法 +优化:JbootController.getFile() 设置为删除方法 +优化:升级 JFinal 到最新版本 + + + jboot v3.15.7: 修复:RPC 的 Reference 缓存 key 构建错误导致无法命中缓存的问题 新增:FileUtil.removeSuffix() 方法 -新增:ModelUtil.key(List models) 方法 +新增:ModelUtil.keep(List models) 方法 新增:CollectionUtil.toString() 方法 新增:ArrayUtil.toString() 方法 新增:DateUtil 解析 datetime-local 方法 -- Gitee From a8a9757f94d64204380a0e3ac5d8698a772d403c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 7 Aug 2022 17:38:23 +0800 Subject: [PATCH 1773/1965] v3.15.8 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2de6b623..17208208 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.0-SNAPSHOT + 3.15.8 jar jboot -- Gitee From 91cf3d36cc005d9f4c72a5773304990cf5e251c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 8 Aug 2022 09:46:25 +0800 Subject: [PATCH 1774/1965] v3.15.8 release (^.^)YYa!! --- README.md | 1 + doc/docs/static/images/jboot-logo.png | Bin 0 -> 13894 bytes 2 files changed, 1 insertion(+) create mode 100644 doc/docs/static/images/jboot-logo.png diff --git a/README.md b/README.md index 572ee452..c22ac661 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +![](./doc/docs/static/images/jboot-logo.png) Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos 等开发的国产框架。 diff --git a/doc/docs/static/images/jboot-logo.png b/doc/docs/static/images/jboot-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..68ff6014c5751bf0d6a8f48b34fe2f348dd0e45d GIT binary patch literal 13894 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 Date: Mon, 8 Aug 2022 20:40:41 +0000 Subject: [PATCH 1775/1965] vuln-fix: Zip Slip Vulnerability This fixes a Zip-Slip vulnerability. This change does one of two things. This change either 1. Inserts a guard to protect against Zip Slip. OR 2. Replaces `dir.getCanonicalPath().startsWith(parent.getCanonicalPath())`, which is vulnerable to partial path traversal attacks, with the more secure `dir.getCanonicalFile().toPath().startsWith(parent.getCanonicalFile().toPath())`. For number 2, consider `"/usr/outnot".startsWith("/usr/out")`. The check is bypassed although `/outnot` is not under the `/out` directory. It's important to understand that the terminating slash may be removed when using various `String` representations of the `File` object. For example, on Linux, `println(new File("/var"))` will print `/var`, but `println(new File("/var", "/")` will print `/var/`; however, `println(new File("/var", "/").getCanonicalPath())` will print `/var`. Weakness: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') Severity: High CVSSS: 7.4 Detection: CodeQL (https://codeql.github.com/codeql-query-help/java/java-zipslip/) & OpenRewrite (https://public.moderne.io/recipes/org.openrewrite.java.security.ZipSlip) Reported-by: Jonathan Leitschuh Signed-off-by: Jonathan Leitschuh Bug-tracker: https://github.com/JLLeitschuh/security-research/issues/16 Co-authored-by: Moderne --- src/main/java/io/jboot/utils/FileUtil.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index 72461043..8c693fb9 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -143,7 +143,11 @@ public class FileUtil { continue; } - File targetFile = new File(targetPath + File.separator + zipEntry.getName()); + File targetFile = new File(targetPath, zipEntry.getName()); + + if (!targetFile.toPath().normalize().startsWith(targetPath)) { + throw new RuntimeException("Bad zip entry"); + } if (safeUnzip && !getCanonicalPath(targetFile).startsWith(targetPath)) { //Unsafe continue; @@ -199,4 +203,4 @@ public class FileUtil { } } -} \ No newline at end of file +} -- Gitee From a121fa38622efe93d24c0435b37f04b0c0ff9f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 9 Aug 2022 11:56:56 +0800 Subject: [PATCH 1776/1965] v3.15.9 --- src/main/java/io/jboot/utils/FileUtil.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index 8c693fb9..c3c4903c 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -148,14 +148,11 @@ public class FileUtil { if (!targetFile.toPath().normalize().startsWith(targetPath)) { throw new RuntimeException("Bad zip entry"); } - if (safeUnzip && !getCanonicalPath(targetFile).startsWith(targetPath)) { - //Unsafe - continue; - } - if (!targetFile.getParentFile().exists()) { - targetFile.getParentFile().mkdirs(); + if (!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs()) { + throw new IOException("Can not mkdirs for file: " + targetFile.getParentFile()); } + os = new BufferedOutputStream(new FileOutputStream(targetFile)); is = zipFile.getInputStream(zipEntry); byte[] buffer = new byte[4096]; @@ -176,7 +173,7 @@ public class FileUtil { private static boolean isNotSafeFile(String name) { name = name.toLowerCase(); - return name.endsWith(".jsp") || name.endsWith(".jspx") || name.contains(".."); + return name.endsWith(".jsp") || name.endsWith(".jspx"); } -- Gitee From 8974d5dcf176dea2b7a4bfc1474b4c7cd0f0ba85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 9 Aug 2022 11:57:22 +0800 Subject: [PATCH 1777/1965] v3.15.9 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 17208208..b6b0af35 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.8 + 3.15.9-SNAPSHOT jar jboot -- Gitee From c77d3f47cee200d5b65207dc1e7a3217d64b0f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 9 Aug 2022 12:25:51 +0800 Subject: [PATCH 1778/1965] add getFileOnly() and getFilesOnly() --- .../jboot/web/controller/JbootController.java | 61 ++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 261be6a3..0bf7d1d2 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -752,6 +752,7 @@ public class JbootController extends Controller { /** * 建议使用 getFirstFileOnly,否则可能会有安全问题 + * * @return */ @NotAction @@ -764,16 +765,17 @@ public class JbootController extends Controller { /** * 只获取第一个文件,若上传多个文件,则删除其他文件 + * * @return */ @NotAction public UploadFile getFirstFileOnly() { List uploadFiles = getFiles(); - if (uploadFiles == null || uploadFiles.isEmpty()){ + if (uploadFiles == null || uploadFiles.isEmpty()) { return null; } - if (uploadFiles.size() == 1){ + if (uploadFiles.size() == 1) { return uploadFiles.get(0); } @@ -786,6 +788,61 @@ public class JbootController extends Controller { } + /** + * 值返回特定文件,其他文件则删除 + * + * @param name + * @return + */ + @NotAction + public UploadFile getFileOnly(String name) { + List uploadFiles = getFiles(); + if (uploadFiles == null || uploadFiles.isEmpty()) { + return null; + } + + UploadFile ret = getFile(name); + for (UploadFile uploadFile : uploadFiles) { + if (uploadFile != ret) { + FileUtil.delete(uploadFile.getFile()); + } + } + + return uploadFiles.get(0); + } + + + /** + * 只返回特定的名称的文件,其他文件则删除 + * + * @param names + * @return + */ + @NotAction + public List getFilesOnly(String... names) { + List uploadFiles = getFiles(); + if (uploadFiles == null || uploadFiles.isEmpty()) { + return null; + } + + Map filesMap = new HashMap<>(); + for (String name : names) { + for (UploadFile uploadFile : uploadFiles) { + if (uploadFile.getParameterName().equals(name) && !filesMap.containsKey(name)) { + filesMap.put(name, uploadFile); + } + } + } + + for (UploadFile uploadFile : uploadFiles) { + if (!filesMap.containsValue(uploadFile)) { + FileUtil.delete(uploadFile.getFile()); + } + } + + return new ArrayList<>(filesMap.values()); + } + @NotAction public String renderToStringWithAttrs(String template) { -- Gitee From 94e38ddd084d0daecba284513f3fb95edd3e3e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 11 Aug 2022 15:18:42 +0800 Subject: [PATCH 1779/1965] optimize getFilesOnly() --- .../jboot/web/controller/JbootController.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 0bf7d1d2..d0648bdb 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -16,6 +16,7 @@ package io.jboot.web.controller; import com.alibaba.fastjson.JSON; +import com.google.common.collect.Sets; import com.jfinal.core.ActionException; import com.jfinal.core.Controller; import com.jfinal.core.NotAction; @@ -819,28 +820,29 @@ public class JbootController extends Controller { * @return */ @NotAction - public List getFilesOnly(String... names) { + public Map getFilesOnly(String... names) { + if (names == null || names.length == 0) { + throw new IllegalArgumentException("names can not be null or empty."); + } + List uploadFiles = getFiles(); if (uploadFiles == null || uploadFiles.isEmpty()) { return null; } + Set keys = Sets.newHashSet(names); Map filesMap = new HashMap<>(); - for (String name : names) { - for (UploadFile uploadFile : uploadFiles) { - if (uploadFile.getParameterName().equals(name) && !filesMap.containsKey(name)) { - filesMap.put(name, uploadFile); - } - } - } for (UploadFile uploadFile : uploadFiles) { - if (!filesMap.containsValue(uploadFile)) { + String name = uploadFile.getParameterName(); + if (keys.contains(name) && !filesMap.containsKey(name)) { + filesMap.put(name, uploadFile); + } else { FileUtil.delete(uploadFile.getFile()); } } - return new ArrayList<>(filesMap.values()); + return filesMap; } -- Gitee From 2726963b82d7e73fa8e4db4e1a2429ab78546ab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 15 Aug 2022 13:23:39 +0800 Subject: [PATCH 1780/1965] update logo --- doc/docs/static/images/jboot-logo.png | Bin 13894 -> 23647 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/docs/static/images/jboot-logo.png b/doc/docs/static/images/jboot-logo.png index 68ff6014c5751bf0d6a8f48b34fe2f348dd0e45d..67983802cd3a645e7425ef7528f420e1908c0ace 100644 GIT binary patch literal 23647 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 Date: Wed, 17 Aug 2022 01:38:36 +0000 Subject: [PATCH 1781/1965] =?UTF-8?q?update=20src/main/java/io/jboot/suppo?= =?UTF-8?q?rt/swagger/Reader.java.=20=E5=BD=93ActionKey=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=20./=20=E7=9B=B8=E5=AF=B9=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E6=97=B6=EF=BC=8Cswagger=E6=96=87=E6=A1=A3=E4=B8=AD=E7=9A=84pa?= =?UTF-8?q?th=E4=BC=9A=E7=94=9F=E6=88=90=E5=8C=85=E5=90=AB=20./=20?= =?UTF-8?q?=E7=9A=84=E9=94=99=E8=AF=AFAPI=E8=B7=AF=E5=BE=84=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 蔡本祥 --- src/main/java/io/jboot/support/swagger/Reader.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/support/swagger/Reader.java b/src/main/java/io/jboot/support/swagger/Reader.java index bf3be3ee..3e98c535 100644 --- a/src/main/java/io/jboot/support/swagger/Reader.java +++ b/src/main/java/io/jboot/support/swagger/Reader.java @@ -76,7 +76,12 @@ public class Reader { //如果有ActionKey注解的URL路径,则使用该路径而不是方法名 ActionKey actionKeyAnnotation = ReflectionUtils.getAnnotation(method, ActionKey.class); if(actionKeyAnnotation != null && !actionKeyAnnotation.value().isEmpty()){ - operationPath = actionKeyAnnotation.value(); + if (StringUtils.startsWith(actionKeyAnnotation.value(), "./")) { + String actionName = StringUtils.substringAfter(actionKeyAnnotation.value(), "./"); + operationPath = JbootControllerManager.me().getPathByController((Class) context.getCls()) + "/" + actionName; + } else { + operationPath = actionKeyAnnotation.value(); + } } String httpMethod = extension.getHttpMethod(context, method); -- Gitee From 9eecbeda8e37f6869ad55fa4731034053ec7f4ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 17 Aug 2022 18:16:02 +0800 Subject: [PATCH 1782/1965] fixed... --- src/main/java/io/jboot/web/controller/JbootController.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index d0648bdb..207c37d7 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -857,6 +857,7 @@ public class JbootController extends Controller { if (data == null) { data = getAttrs(); } else { + data = new HashMap(data); for (Enumeration names = getAttrNames(); names.hasMoreElements(); ) { String attrName = names.nextElement(); if (!data.containsKey(attrName)) { @@ -864,6 +865,7 @@ public class JbootController extends Controller { } } } + return super.renderToString(template, data); } } -- Gitee From 4ca2835b072cb51aee49aa61731c380e1610ebbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 21 Aug 2022 16:29:43 +0800 Subject: [PATCH 1783/1965] add FileUtil.delete.. --- src/main/java/io/jboot/utils/FileUtil.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index c3c4903c..13027587 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -18,9 +18,11 @@ package io.jboot.utils; import com.jfinal.core.JFinal; import com.jfinal.kit.LogKit; import com.jfinal.kit.PathKit; +import com.jfinal.upload.UploadFile; import java.io.*; import java.util.Enumeration; +import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -200,4 +202,21 @@ public class FileUtil { } } + public static void delete(UploadFile file) { + if (file == null) { + return; + } + + delete(file.getFile()); + } + + + public static void delete(List files) { + if (files == null) { + return; + } + + files.forEach(FileUtil::delete); + } + } -- Gitee From 4b9a11742576268f1392b555780f335d1d705192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 21 Aug 2022 16:42:24 +0800 Subject: [PATCH 1784/1965] upgrade dependencies --- pom.xml | 9 +++++---- src/main/java/io/jboot/aop/JbootAopFactory.java | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index b6b0af35..6125104a 100644 --- a/pom.xml +++ b/pom.xml @@ -49,15 +49,16 @@ 4.0.3 2.13.3 5.1.49 - 2.2.18.Final + 2.2.19.Final 1.7.36 2.57 1.2.83 + 31.1-jre 2.2.5 3.8.0 - 1.14.3 + 1.15.2 2.10.9.2 2.9.3 3.12.0 @@ -71,7 +72,7 @@ 3.14.0.Final 1.5.1 1.1.8 - 4.2.9 + 4.2.11 1.8 1.8 @@ -200,7 +201,7 @@ org.javassist javassist - 3.29.0-GA + 3.29.1-GA diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index d4eaaf3f..fec75c13 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -266,7 +266,6 @@ public class JbootAopFactory extends AopFactory { private Object createFieldObjectByRPCComponent(Object targetObject, Field field, RPCInject rpcInject) { try { Class fieldInjectedClass = field.getType(); - JbootrpcReferenceConfig config = ReferenceConfigCache.get(fieldInjectedClass, rpcInject); Jbootrpc jbootrpc = JbootrpcManager.me().getJbootrpc(); return jbootrpc.serviceObtain(fieldInjectedClass, config); -- Gitee From febc1ab6473699941bb84e508dd6011936e1dde7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 21 Aug 2022 17:12:21 +0800 Subject: [PATCH 1785/1965] v3.15.9 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- .../java/io/jboot/web/controller/JbootController.java | 11 +++++++++-- 7 files changed, 17 insertions(+), 10 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 5759b5cc..b936fa75 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.15.8 + 3.15.9 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 0d2d8d91..b183c25a 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.15.8 + 3.15.9 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 94a160bf..6cd0f664 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.15.8 + 3.15.9 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.15.8', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.15.9', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 1ac7e4ce..1099f228 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.15.8', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.15.9', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.8/jboot-3.15.8.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.9/jboot-3.15.9.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 431097aa..30ecf564 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.15.8 + 3.15.9 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index d350bdd2..73e54218 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.15.8"; + public static String VERSION = "3.15.9"; public static final String ATTR_CONTEXT_PATH = "CPATH"; diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 207c37d7..37cdb2a8 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -835,10 +835,17 @@ public class JbootController extends Controller { for (UploadFile uploadFile : uploadFiles) { String name = uploadFile.getParameterName(); + if (StrUtil.isBlank(name)) { + FileUtil.delete(uploadFile); + continue; + } + + name = name.trim(); + if (keys.contains(name) && !filesMap.containsKey(name)) { filesMap.put(name, uploadFile); } else { - FileUtil.delete(uploadFile.getFile()); + FileUtil.delete(uploadFile); } } @@ -857,7 +864,7 @@ public class JbootController extends Controller { if (data == null) { data = getAttrs(); } else { - data = new HashMap(data); + data = new HashMap(data); for (Enumeration names = getAttrNames(); names.hasMoreElements(); ) { String attrName = names.nextElement(); if (!data.containsKey(attrName)) { -- Gitee From c8d3ab1c503ddeaaa5b10d7d9cb1629cac80c8c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 21 Aug 2022 17:12:39 +0800 Subject: [PATCH 1786/1965] v3.15.9 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ pom.xml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c22ac661..9a1a733b 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.15.8 + 3.15.9 ``` diff --git a/changes.txt b/changes.txt index efd96359..74f45937 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.15.9: +新增:Controller.getFileOnly(name) 和 getFilesOnly(names) +优化:升级 Jsoup 等相关依赖到最新版本 +修复:当 ActionKey 注解使用 ./ 相对路径时,swagger文档中的 path 会生成包含 ./ 的错误API路径的问题 + + + jboot v3.15.8: 新增:FileUtil.delete 方法 新增:JbootController.getFirstFileOnly() 方法 diff --git a/pom.xml b/pom.xml index 6125104a..0e060c0a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.9-SNAPSHOT + 3.15.9 jar jboot -- Gitee From 7039415cbda1f2927d281eb29a31056031e7dbf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 21 Aug 2022 17:21:57 +0800 Subject: [PATCH 1787/1965] v3.15.9 release (^.^)YYa!! --- src/main/java/io/jboot/support/swagger/Reader.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/support/swagger/Reader.java b/src/main/java/io/jboot/support/swagger/Reader.java index 3e98c535..caeaea04 100644 --- a/src/main/java/io/jboot/support/swagger/Reader.java +++ b/src/main/java/io/jboot/support/swagger/Reader.java @@ -75,10 +75,11 @@ public class Reader { String operationPath = JbootControllerManager.me().getPathByController((Class) context.getCls()) + methodPath; //如果有ActionKey注解的URL路径,则使用该路径而不是方法名 ActionKey actionKeyAnnotation = ReflectionUtils.getAnnotation(method, ActionKey.class); - if(actionKeyAnnotation != null && !actionKeyAnnotation.value().isEmpty()){ - if (StringUtils.startsWith(actionKeyAnnotation.value(), "./")) { - String actionName = StringUtils.substringAfter(actionKeyAnnotation.value(), "./"); - operationPath = JbootControllerManager.me().getPathByController((Class) context.getCls()) + "/" + actionName; + if (actionKeyAnnotation != null && !actionKeyAnnotation.value().isEmpty()) { + if (actionKeyAnnotation.value().startsWith("./")) { + String actionName = actionKeyAnnotation.value().substring(2); + String pathByController = JbootControllerManager.me().getPathByController((Class) context.getCls()); + operationPath = pathByController.endsWith("/") ? (pathByController + actionName) : (pathByController + "/" + actionName); } else { operationPath = actionKeyAnnotation.value(); } -- Gitee From c3b5c2544471595b12e8d6144b565a49ffb73c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 26 Aug 2022 18:08:52 +0800 Subject: [PATCH 1788/1965] v3.15.10 snapshot --- pom.xml | 2 +- src/main/java/io/jboot/app/ApplicationUtil.java | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0e060c0a..f1a7a2f1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.9 + 3.15.10-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/app/ApplicationUtil.java b/src/main/java/io/jboot/app/ApplicationUtil.java index 9b10362a..b4bcb192 100644 --- a/src/main/java/io/jboot/app/ApplicationUtil.java +++ b/src/main/java/io/jboot/app/ApplicationUtil.java @@ -18,8 +18,10 @@ package io.jboot.app; import io.jboot.app.config.JbootConfigManager; import java.io.File; +import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.net.URL; +import java.net.URLDecoder; public class ApplicationUtil { @@ -68,7 +70,13 @@ public class ApplicationUtil { // 在某些情况下 通过 java -jar 运行时,会以 /config/ 结束 if (urlStr.endsWith("/config/")) { - File urlPath = new File(url.getPath()); + File urlPath; + try { + //中文目录乱码的问题 + urlPath = new File(URLDecoder.decode(url.getFile(), "UTF-8")); + } catch (UnsupportedEncodingException e) { + urlPath = new File(url.getPath()); + } return !urlPath.exists() || !urlPath.isDirectory(); } -- Gitee From 1d3c328daf53f1097384e4b4829d3f87eefa748d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 30 Aug 2022 14:06:13 +0800 Subject: [PATCH 1789/1965] upgrade jsoup --- pom.xml | 2 +- src/main/java/io/jboot/aop/JbootAopFactory.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index f1a7a2f1..e755dfb0 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 31.1-jre 2.2.5 3.8.0 - 1.15.2 + 1.15.3 2.10.9.2 2.9.3 3.12.0 diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index fec75c13..11487b28 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -362,7 +362,7 @@ public class JbootAopFactory extends AopFactory { for (Class configurationClass : configurationClasses) { Object configurationObj = ClassUtil.newInstance(configurationClass, false); if (configurationObj == null) { - throw new NullPointerException("can not newInstance for class : " + configurationClass); + throw new NullPointerException("Can not new instance for class: " + configurationClass.getName()); } Method[] methods = configurationClass.getDeclaredMethods(); for (Method method : methods) { @@ -375,8 +375,8 @@ public class JbootAopFactory extends AopFactory { String beanName = StrUtil.obtainDefault(AnnotationUtil.get(beanAnnotation.name()), method.getName()); if (beansCache.containsKey(beanName)) { - throw new JbootException("application has contains beanName \"" + beanName + "\" for " + getBean(beanName) - + ", can not add again by method: " + ClassUtil.buildMethodString(method)); + throw new JbootException("Application has contains beanName \"" + beanName + "\" for " + getBean(beanName) + + ", Can not add again by method: " + ClassUtil.buildMethodString(method)); } try { -- Gitee From b0ebce11bd68c0d8bc6577bf29dcda66ff66c54d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 31 Aug 2022 19:02:04 +0800 Subject: [PATCH 1790/1965] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=20http=20=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E7=B1=BB=EF=BC=8C=E5=A2=9E=E5=8A=A0=E6=89=8B=E5=8A=A8?= =?UTF-8?q?=E6=90=BA=E5=B8=A6=20cookie=20redirect=20=E7=9A=84=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/http/JbootHttpRequest.java | 39 +++++++++- .../components/http/jboot/JbootHttpImpl.java | 75 +++++++++++++++++-- src/main/java/io/jboot/utils/HttpUtil.java | 15 +++- .../io/jboot/test/http/HttpUtilTester.java | 43 ----------- 4 files changed, 118 insertions(+), 54 deletions(-) diff --git a/src/main/java/io/jboot/components/http/JbootHttpRequest.java b/src/main/java/io/jboot/components/http/JbootHttpRequest.java index ba0f07f4..f301446a 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpRequest.java +++ b/src/main/java/io/jboot/components/http/JbootHttpRequest.java @@ -75,7 +75,7 @@ public class JbootHttpRequest { private boolean readBody = true; // 遇到重定向是否自动跟随 - private boolean instanceFollowRedirects = true; + private boolean instanceFollowRedirects = false; // 自定义 sslContext private SSLContext sslContext; @@ -83,6 +83,15 @@ public class JbootHttpRequest { // 自定义 http 代理 private HttpProxyInfo httpProxyInfo; + // 是否自动重定向(手动实现的,而非) + private boolean autoRedirect = true; + + // 最大的重定向次数 + private int maxRedirectCount = 3; + + // 当前的重定向次数 + private int currentRedirectCount = 0; + public static JbootHttpRequest create(String url) { return new JbootHttpRequest(url); @@ -237,6 +246,10 @@ public class JbootHttpRequest { return headers; } + public String getHeader(String name){ + return headers != null ? headers.get(name) : null; + } + public void setHeaders(Map headers) { this.headers = headers; } @@ -402,4 +415,28 @@ public class JbootHttpRequest { public Proxy getProxy() { return httpProxyInfo != null ? httpProxyInfo.getProxy() : null; } + + public boolean isAutoRedirect() { + return autoRedirect; + } + + public void setAutoRedirect(boolean autoRedirect) { + this.autoRedirect = autoRedirect; + } + + public int getMaxRedirectCount() { + return maxRedirectCount; + } + + public void setMaxRedirectCount(int maxRedirectCount) { + this.maxRedirectCount = maxRedirectCount; + } + + public int getCurrentRedirectCount() { + return currentRedirectCount; + } + + public void setCurrentRedirectCount(int currentRedirectCount) { + this.currentRedirectCount = currentRedirectCount; + } } diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index b3f82af4..f49fa0ed 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -27,12 +27,14 @@ import io.jboot.utils.StrUtil; import javax.net.ssl.*; import java.io.*; +import java.net.HttpCookie; import java.net.HttpURLConnection; import java.net.ProtocolException; import java.net.URL; import java.security.KeyStore; import java.security.SecureRandom; import java.security.cert.X509Certificate; +import java.util.List; import java.util.Map; import java.util.zip.GZIPInputStream; @@ -78,8 +80,14 @@ public class JbootHttpImpl implements JbootHttp { else { String uploadBodyString = request.getUploadBodyString(); if (StrUtil.isNotEmpty(uploadBodyString)) { + byte[] bytes = uploadBodyString.getBytes(request.getCharset()); + + if (StrUtil.isBlank(request.getHeader("Content-Length"))) { + connection.setRequestProperty("Content-Length", String.valueOf(bytes.length)); + } + try (OutputStream outStream = connection.getOutputStream();) { - outStream.write(uploadBodyString.getBytes(request.getCharset())); + outStream.write(bytes); outStream.flush(); } } @@ -88,12 +96,19 @@ public class JbootHttpImpl implements JbootHttp { //get 请求 else { - connection.setInstanceFollowRedirects(request.isInstanceFollowRedirects()); connection.connect(); } + int responseCode = connection.getResponseCode(); + + //自动重定向 + if (responseCode >= 300 && responseCode < 400 && request.isAutoRedirect()) { + processRedirect(request, response, connection); + return; + } + - inStream = getInputStream(connection); + inStream = getInputStream(connection, responseCode); response.setContentType(connection.getContentType()); response.setResponseCode(connection.getResponseCode()); @@ -114,16 +129,58 @@ public class JbootHttpImpl implements JbootHttp { } QuietlyUtil.closeQuietly(inStream, response); - } } - private InputStream getInputStream(HttpURLConnection connection) throws IOException { - InputStream stream = connection.getResponseCode() >= 400 - ? connection.getErrorStream() - : connection.getInputStream(); + /** + * 手动重定向 + * + * @param request + * @param response + * @param connection + */ + private void processRedirect(JbootHttpRequest request, JbootHttpResponse response, HttpURLConnection connection) throws IOException { + if (request.getCurrentRedirectCount() > request.getMaxRedirectCount()) { + throw new IOException("Exceeded redirect count."); + } + + + String location = connection.getHeaderField("Location"); + request.setCurrentRedirectCount(request.getCurrentRedirectCount() + 1); + + //绝对路径 + if (location.startsWith("/")) { + int firstSlash = request.getRequestUrl().indexOf("/", 8); // 8 == "https://".length() + location = request.getRequestUrl().substring(0, firstSlash) + location; + } + //相对路径 + else if (!location.toLowerCase().startsWith("http")) { + int lastSlash = request.getRequestUrl().lastIndexOf("/"); + location = request.getRequestUrl().substring(0, lastSlash + 1) + location; + } + + //携带 cookie + String responseCookieString = connection.getHeaderField("Set-Cookie"); + if (StrUtil.isNotBlank(responseCookieString)) { + List cookies = HttpCookie.parse(responseCookieString); + StringBuilder cookie = new StringBuilder(StrUtil.obtainDefault(request.getHeader("Cookie"), "")); + for (HttpCookie httpCookie : cookies) { + cookie.append(httpCookie.getName()).append("=").append(httpCookie.getValue()).append("; "); + } + request.addHeader("Cookie", cookie.toString()); + } + + request.setRequestUrl(location); + request.setMethod(JbootHttpRequest.METHOD_GET); + + doProcess(request, response); + } + + + private InputStream getInputStream(HttpURLConnection connection, int responseCode) throws IOException { + InputStream stream = responseCode >= 400 ? connection.getErrorStream() : connection.getInputStream(); if ("gzip".equalsIgnoreCase(connection.getContentEncoding())) { return new GZIPInputStream(stream); } else { @@ -132,6 +189,7 @@ public class JbootHttpImpl implements JbootHttp { } + private void uploadByMultipart(JbootHttpRequest request, HttpURLConnection connection) throws IOException { String endFlag = "\r\n"; String startFlag = "--"; @@ -196,6 +254,7 @@ public class JbootHttpImpl implements JbootHttp { connection.setReadTimeout(request.getReadTimeOut()); connection.setConnectTimeout(request.getConnectTimeOut()); connection.setRequestMethod(request.getMethod()); + connection.setInstanceFollowRedirects(request.isInstanceFollowRedirects()); //如果 reqeust 的 header 不配置 content-Type, 使用默认的 connection.setRequestProperty("Content-Type", request.getContentType()); diff --git a/src/main/java/io/jboot/utils/HttpUtil.java b/src/main/java/io/jboot/utils/HttpUtil.java index 58856678..cca94563 100644 --- a/src/main/java/io/jboot/utils/HttpUtil.java +++ b/src/main/java/io/jboot/utils/HttpUtil.java @@ -112,6 +112,19 @@ public class HttpUtil { return httpPost(url, paras, null, postData); } + /** + * Http post 操作 + * + * @param url + * @param paras + * @param headers + * @return + */ + public static String httpPost(String url, Map paras, Map headers) { + return httpPost(url, paras, headers, null); + } + + /** * Http post 操作 * @@ -205,7 +218,6 @@ public class HttpUtil { * @return */ public static String upload(String url, Map paras, Map headers, File file) { - Map newParas = new HashMap(); if (paras != null) { newParas.putAll(paras); @@ -225,5 +237,4 @@ public class HttpUtil { } - } diff --git a/src/test/java/io/jboot/test/http/HttpUtilTester.java b/src/test/java/io/jboot/test/http/HttpUtilTester.java index a9b3d222..201f83a8 100644 --- a/src/test/java/io/jboot/test/http/HttpUtilTester.java +++ b/src/test/java/io/jboot/test/http/HttpUtilTester.java @@ -1,16 +1,5 @@ package io.jboot.test.http; -import com.jfinal.kit.JsonKit; -import io.jboot.components.http.HttpProxyInfo; -import io.jboot.components.http.JbootHttpManager; -import io.jboot.components.http.JbootHttpRequest; -import io.jboot.components.http.JbootHttpResponse; -import io.jboot.utils.HttpUtil; -import io.jboot.utils.StrUtil; - -import java.util.HashMap; -import java.util.Map; - /** * @author michael yang (fuhai999@gmail.com) */ @@ -18,41 +7,9 @@ public class HttpUtilTester { public static void main(String[] args) { - JbootHttpRequest request = JbootHttpRequest.create("https://www.baidu.com", null, JbootHttpRequest.METHOD_GET); - request.setHttpProxyInfo(new HttpProxyInfo("127.0.0.1",8080)); - - JbootHttpResponse response = JbootHttpManager.me().getJbootHttp().handle(request); - -// System.out.println(response.getHeaders().get("Location").get(0)); - System.out.println(response); - - - String queryString = "aaa=ccc&a=123&b=&c=aa&d&"; - System.out.println(JsonKit.toJson(StrUtil.queryStringToMap(queryString))); - - Map map = new HashMap<>(); - map.put("aa",123); - map.put(null,"ddd"); - map.put("cc",null); - map.put("你好","xx"); - System.out.println(StrUtil.mapToQueryString(map)); - - - Map paras = new HashMap<>(); - paras.put("key","value"); - - Map headers = new HashMap<>(); - headers.put("key","value"); - - String postData = "abc"; - - - String s = HttpUtil.httpPost("https://www.baidu.com", paras, headers, postData); - System.out.println(s); } - } \ No newline at end of file -- Gitee From 89c84ca09c6a6acd7123d781b3387ddaacf92285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 31 Aug 2022 19:06:03 +0800 Subject: [PATCH 1791/1965] v3.15.10 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index b936fa75..d792f231 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.15.9 + 3.15.10 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index b183c25a..a0dc1ba8 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.15.9 + 3.15.10 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 6cd0f664..be49c4f5 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.15.9 + 3.15.10 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.15.9', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.15.10', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 1099f228..6f36edd6 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.15.9', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.15.10', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.9/jboot-3.15.9.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.10/jboot-3.15.10.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 30ecf564..affd4d85 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.15.9 + 3.15.10 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 73e54218..be08f137 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.15.9"; + public static String VERSION = "3.15.10"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 28d3563d91cf4a3bb04f5e14db606fb54654fffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 31 Aug 2022 19:06:08 +0800 Subject: [PATCH 1792/1965] v3.15.10 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ pom.xml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9a1a733b..c0b870e0 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.15.9 + 3.15.10 ``` diff --git a/changes.txt b/changes.txt index 74f45937..8c32d6de 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.15.10: +优化:增强 http 工具类,默认支持携带 cookie 重定向 +优化:升级 Jsoup 到 v1.15.3 最新版本 +修复:对在中文目录下部署时,可能对环境判断错误的问题 + + + jboot v3.15.9: 新增:Controller.getFileOnly(name) 和 getFilesOnly(names) 优化:升级 Jsoup 等相关依赖到最新版本 diff --git a/pom.xml b/pom.xml index e755dfb0..39736c2f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.10-SNAPSHOT + 3.15.10 jar jboot -- Gitee From bcb1e271c3f7b67def75666c8b5c1896caf196d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 20 Sep 2022 10:15:44 +0800 Subject: [PATCH 1793/1965] optimize FileUtil.unzip --- src/main/java/io/jboot/utils/FileUtil.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index 13027587..6fa68c76 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -21,6 +21,8 @@ import com.jfinal.kit.PathKit; import com.jfinal.upload.UploadFile; import java.io.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Enumeration; import java.util.List; import java.util.zip.ZipEntry; @@ -120,18 +122,18 @@ public class FileUtil { public static void unzip(String zipFilePath) throws IOException { String targetPath = zipFilePath.substring(0, zipFilePath.lastIndexOf(".")); - unzip(zipFilePath, targetPath, true); + unzip(zipFilePath, targetPath, true, StandardCharsets.UTF_8); } public static void unzip(String zipFilePath, String targetPath) throws IOException { - unzip(zipFilePath, targetPath, true); + unzip(zipFilePath, targetPath, true, StandardCharsets.UTF_8); } - public static void unzip(String zipFilePath, String targetPath, boolean safeUnzip) throws IOException { + public static void unzip(String zipFilePath, String targetPath, boolean safeUnzip, Charset charset) throws IOException { targetPath = getCanonicalPath(new File(targetPath)); - ZipFile zipFile = new ZipFile(zipFilePath); + ZipFile zipFile = new ZipFile(zipFilePath, charset); try { Enumeration entryEnum = zipFile.entries(); while (entryEnum.hasMoreElements()) { -- Gitee From 961c78fb58e827101262e4febbdbbb0077fed66d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 20 Sep 2022 10:20:45 +0800 Subject: [PATCH 1794/1965] fixed JbootServiceBase.initDao --- .../io/jboot/service/JbootServiceBase.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/service/JbootServiceBase.java b/src/main/java/io/jboot/service/JbootServiceBase.java index 65fe4cc2..327d0e8a 100644 --- a/src/main/java/io/jboot/service/JbootServiceBase.java +++ b/src/main/java/io/jboot/service/JbootServiceBase.java @@ -16,9 +16,7 @@ package io.jboot.service; import com.jfinal.kit.LogKit; -import com.jfinal.plugin.activerecord.Db; -import com.jfinal.plugin.activerecord.Model; -import com.jfinal.plugin.activerecord.Page; +import com.jfinal.plugin.activerecord.*; import io.jboot.db.model.Columns; import io.jboot.db.model.JbootModel; import io.jboot.utils.ClassUtil; @@ -56,13 +54,28 @@ public class JbootServiceBase> * @return */ protected M initDao() { - Type type = ClassUtil.getUsefulClass(getClass()).getGenericSuperclass(); + Class usefulClass = ClassUtil.getUsefulClass(getClass()); + return createDao(usefulClass); + } + + + private M createDao(Class usefulClass) { + Type type = usefulClass.getGenericSuperclass(); if (type instanceof ParameterizedType) { Class modelClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0]; return ClassUtil.newInstance(modelClass, false); } + //from child class + else if (type instanceof Class) { + Class typeClass = (Class) type; + if (typeClass != JbootServiceBase.class + && typeClass != Object.class + ) { + return createDao(typeClass); + } + } - LogKit.warn("Not define Model class in Servlce: " + ClassUtil.getUsefulClass(getClass())); + LogKit.warn("Not define Model class in service: " +usefulClass); return null; } -- Gitee From 1fd81f82dc9ec3b60ad23f7a930d377e9a9a4384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 20 Sep 2022 10:21:23 +0800 Subject: [PATCH 1795/1965] v3.15.11 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 39736c2f..c77d7bc0 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.10 + 3.15.11-SNAPSHOT jar jboot -- Gitee From e99e9bb6b98ae56eff1731a3ff036a9c90020858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 24 Sep 2022 12:02:01 +0800 Subject: [PATCH 1796/1965] fixed sqlbuilder error --- .../java/io/jboot/db/model/SqlBuilder.java | 67 ++++++++----------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index 985bb837..aa9d1064 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -36,10 +36,9 @@ public class SqlBuilder { public static String forDeleteByColumns(String alias, List joins, String table, List columns, char separator) { StringBuilder sqlBuilder = new StringBuilder(45); - sqlBuilder.append("DELETE FROM ") - .append(separator) - .append(table) - .append(separator); + sqlBuilder.append("DELETE FROM "); + + appendTextWithSeparator(sqlBuilder, table, separator); buildAlias(sqlBuilder, alias); buildJoinSql(sqlBuilder, joins, separator); @@ -131,19 +130,10 @@ public class SqlBuilder { private static void appendColumnName(StringBuilder sqlBuilder, Column column, char separator) { - if (column.getName().contains(".")) { - sqlBuilder.append(column.getName()) - .append(' ') - .append(column.getLogic()) - .append(' '); - } else { - sqlBuilder.append(separator) - .append(column.getName()) - .append(separator) - .append(' ') - .append(column.getLogic()) - .append(' '); - } + appendTextWithSeparator(sqlBuilder, column.getName(), separator); + sqlBuilder.append(' ') + .append(column.getLogic()) + .append(' '); } @@ -208,22 +198,26 @@ public class SqlBuilder { public static void appendBetweenLogic(StringBuilder sqlBuilder, Column column, char separator) { - sqlBuilder.append(separator) - .append(column.getName()) - .append(separator) - .append(' ') - .append(column.getLogic()); - + appendTextWithSeparator(sqlBuilder, column.getName(), separator); + sqlBuilder.append(' ').append(column.getLogic()); sqlBuilder.append(" ? AND ?"); } + + public static void appendTextWithSeparator(StringBuilder sqlBuilder, String text, char separator) { + if (text.indexOf(".") > 0) { + sqlBuilder.append(text); + } else { + sqlBuilder.append(separator).append(text).append(separator); + } + } + + public static StringBuilder forFindByColumns(String alias, List joins, String table, String loadColumns, List columns, String orderBy, char separator) { StringBuilder sqlBuilder = new StringBuilder("SELECT "); sqlBuilder.append(loadColumns) - .append(" FROM ") - .append(separator) - .append(table) - .append(separator); + .append(" FROM "); + appendTextWithSeparator(sqlBuilder, table, separator); buildAlias(sqlBuilder, alias); buildJoinSql(sqlBuilder, joins, separator); @@ -257,10 +251,8 @@ public class SqlBuilder { public static String forPaginateFrom(String alias, List joins, String table, List columns, String orderBy, char separator) { - StringBuilder sqlBuilder = new StringBuilder(" FROM ") - .append(separator) - .append(table) - .append(separator); + StringBuilder sqlBuilder = new StringBuilder(" FROM "); + appendTextWithSeparator(sqlBuilder, table, separator); buildAlias(sqlBuilder, alias); buildJoinSql(sqlBuilder, joins, separator); @@ -273,6 +265,7 @@ public class SqlBuilder { return sqlBuilder.toString(); } + public static void buildJoinSql(StringBuilder sqlBuilder, List joins, char separator) { if (joins == null || joins.isEmpty()) { return; @@ -282,10 +275,8 @@ public class SqlBuilder { continue; } - sqlBuilder.append(join.getType()) - .append(separator) - .append(join.getTable()) - .append(separator); + sqlBuilder.append(join.getType()); + appendTextWithSeparator(sqlBuilder, join.getTable(), separator); buildAlias(sqlBuilder, join.getAs()); @@ -303,10 +294,8 @@ public class SqlBuilder { public static String forFindCountByColumns(String alias, List joins, String table, String loadColumns, List columns, char separator) { - StringBuilder sqlBuilder = new StringBuilder("SELECT count(" + loadColumns + ") FROM ") - .append(separator) - .append(table) - .append(separator); + StringBuilder sqlBuilder = new StringBuilder("SELECT count(" + loadColumns + ") FROM "); + appendTextWithSeparator(sqlBuilder, table, separator); buildAlias(sqlBuilder, alias); buildJoinSql(sqlBuilder, joins, separator); -- Gitee From 9e3f41f3ed51db5cd3641fabd1c33007299226ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 24 Sep 2022 14:35:04 +0800 Subject: [PATCH 1797/1965] restructure @Cacheable for Controller --- .../interceptor/ActionCachedContent.java | 169 +++++++++++ .../interceptor/CachePutInterceptor.java | 12 +- .../interceptor/CacheableInterceptor.java | 70 +++-- .../components/cache/interceptor/Utils.java | 4 +- .../web/cached/CacheSupportResponseProxy.java | 254 ---------------- .../jboot/web/cached/CacheSupportWriter.java | 270 ------------------ .../io/jboot/web/cached/CachedContent.java | 83 ------ .../jboot/web/handler/JbootActionHandler.java | 10 - .../java/io/jboot/web/render/JbootRender.java | 2 +- .../io/jboot/web/render/JbootXmlRender.java | 2 +- .../interceptor/EmptyValidateInterceptor.java | 3 +- .../interceptor/ValidateInterceptorUtil.java | 7 +- 12 files changed, 229 insertions(+), 657 deletions(-) create mode 100644 src/main/java/io/jboot/components/cache/interceptor/ActionCachedContent.java delete mode 100644 src/main/java/io/jboot/web/cached/CacheSupportResponseProxy.java delete mode 100644 src/main/java/io/jboot/web/cached/CacheSupportWriter.java delete mode 100644 src/main/java/io/jboot/web/cached/CachedContent.java diff --git a/src/main/java/io/jboot/components/cache/interceptor/ActionCachedContent.java b/src/main/java/io/jboot/components/cache/interceptor/ActionCachedContent.java new file mode 100644 index 00000000..e710d033 --- /dev/null +++ b/src/main/java/io/jboot/components/cache/interceptor/ActionCachedContent.java @@ -0,0 +1,169 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.cache.interceptor; + +import com.jfinal.render.*; +import io.jboot.web.render.JbootTemplateRender; +import io.jboot.web.render.JbootXmlRender; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +class ActionCachedContent implements Serializable { + + private static final int RENDER_TEMPLATE = 0; + private static final int RENDER_XML = 1; + private static final int RENDER_JSON = 2; + private static final int RENDER_TEXT = 3; + + private static IRenderFactory renderFactory = RenderManager.me().getRenderFactory(); + + + private Map headers; + private Map attrs; + + private String contentType; + private Integer renderType; + private String viewOrText; + private Map otherPara = null; + + public ActionCachedContent(Render render) { + if (render == null) { + throw new IllegalArgumentException("Render can not be null."); + } + + // xml + if (render instanceof JbootXmlRender) { + renderType = RENDER_XML; + contentType = ((JbootXmlRender) render).getContentType(); + viewOrText = render.getView(); + } + // template + else if (render instanceof JbootTemplateRender) { + renderType = RENDER_TEMPLATE; + contentType = ((JbootTemplateRender) render).getContentType(); + viewOrText = render.getView(); + } + // text + else if (render instanceof TextRender) { + renderType = RENDER_TEXT; + contentType = ((TextRender) render).getContentType(); + viewOrText = ((TextRender) render).getText(); + } + // json + else if (render instanceof JsonRender) { + renderType = RENDER_JSON; + otherPara = new HashMap<>(); + + JsonRender jsonRender = (JsonRender) render; + otherPara.put("jsonText", jsonRender.getJsonText()); + otherPara.put("attrs", jsonRender.getAttrs()); + otherPara.put("forIE", jsonRender.getForIE()); + } else { + throw new IllegalArgumentException("@Cacheable Can not support the render of the type: " + render.getClass().getName()); + } + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public Map getAttrs() { + return attrs; + } + + public void setAttrs(Map attrs) { + this.attrs = attrs; + } + + public void addAttr(String key, Object value) { + if (this.attrs == null) { + this.attrs = new HashMap<>(); + } + this.attrs.put(key, value); + } + + public String getViewOrText() { + return viewOrText; + } + + public void setViewOrText(String viewOrText) { + this.viewOrText = viewOrText; + } + + public Integer getRenderType() { + return renderType; + } + + public void setRenderType(Integer renderType) { + this.renderType = renderType; + } + + public Map getOtherPara() { + return otherPara; + } + + public void setOtherPara(Map otherPara) { + this.otherPara = otherPara; + } + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public void addHeader(String key, String value) { + if (this.headers == null) { + this.headers = new HashMap<>(); + } + this.headers.put(key, value); + } + + + public Render createRender() { + switch (renderType) { + case RENDER_TEMPLATE: + return renderFactory.getTemplateRender(viewOrText); + case RENDER_XML: + return renderFactory.getXmlRender(viewOrText); + case RENDER_JSON: + JsonRender jsonRender; + if (otherPara.get("jsonText") != null) { + jsonRender = (JsonRender) renderFactory.getJsonRender((String) otherPara.get("jsonText")); + } else if (otherPara.get("attrs") != null) { + jsonRender = (JsonRender) renderFactory.getJsonRender((String[]) otherPara.get("attrs")); + } else { + jsonRender = (JsonRender) renderFactory.getJsonRender(); + } + if (Boolean.TRUE.equals(otherPara.get("forIE"))) { + jsonRender.forIE(); + } + return jsonRender; + case RENDER_TEXT: + return renderFactory.getTextRender(viewOrText, contentType); + default: + throw new IllegalArgumentException("@Cacheable can not support the renderType of the value: " + renderType); + } + } +} diff --git a/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java index e69da34c..29e58c36 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java @@ -18,12 +18,10 @@ package io.jboot.components.cache.interceptor; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; -import com.jfinal.core.CPI; import com.jfinal.core.Controller; import io.jboot.components.cache.AopCache; import io.jboot.components.cache.annotation.CachePut; import io.jboot.utils.AnnotationUtil; -import io.jboot.web.cached.CacheSupportResponseProxy; import java.lang.reflect.Method; @@ -63,15 +61,9 @@ public class CachePutInterceptor implements Interceptor { Controller controller = inv.getController(); - CacheSupportResponseProxy responseProxy = new CacheSupportResponseProxy(controller.getResponse()); - responseProxy.setCacheName(cacheName); - responseProxy.setCacheKey(cacheKey); - responseProxy.setCacheLiveSeconds(cachePut.liveSeconds()); - - //让 Controller 持有缓存的 responseProxy - CPI._init_(controller, CPI.getAction(controller), controller.getRequest(), responseProxy, controller.getPara()); - inv.invoke(); + + CacheableInterceptor.cacheActionContent(cacheName, cacheKey, cachePut.liveSeconds(), controller); } diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java index 5b64a830..dd51419c 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java @@ -18,10 +18,8 @@ package io.jboot.components.cache.interceptor; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; -import com.jfinal.core.CPI; import com.jfinal.core.Controller; import com.jfinal.plugin.activerecord.Page; -import io.jboot.components.cache.ActionCache; import io.jboot.components.cache.AopCache; import io.jboot.components.cache.annotation.Cacheable; import io.jboot.db.model.JbootModel; @@ -29,14 +27,11 @@ import io.jboot.exception.JbootException; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.ClassUtil; import io.jboot.utils.ModelUtil; -import io.jboot.web.cached.CacheSupportResponseProxy; -import io.jboot.web.cached.CachedContent; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * 缓存操作的拦截器 @@ -91,35 +86,66 @@ public class CacheableInterceptor implements Interceptor { Controller controller = inv.getController(); - CachedContent cachedContent = ActionCache.get(cacheName, cacheKey); - if (cachedContent != null) { - writeCachedContent(controller, cachedContent); + ActionCachedContent actionCachedContent = AopCache.get(cacheName, cacheKey); + if (actionCachedContent != null) { + renderActionCachedContent(controller, actionCachedContent); return; } + inv.invoke(); + cacheActionContent(cacheName, cacheKey, cacheable.liveSeconds(), controller); + } + + + /** + * 对 action 内容进行缓存 + * + * @param cacheName + * @param cacheKey + * @param liveSeconds + * @param controller + */ + public static void cacheActionContent(String cacheName, String cacheKey, int liveSeconds, Controller controller) { - CacheSupportResponseProxy responseProxy = new CacheSupportResponseProxy(controller.getResponse()); - responseProxy.setCacheName(cacheName); - responseProxy.setCacheKey(cacheKey); - responseProxy.setCacheLiveSeconds(cacheable.liveSeconds()); + ActionCachedContent cachedContent = new ActionCachedContent(controller.getRender()); - //让 Controller 持有缓存的 responseProxy - CPI._init_(controller, CPI.getAction(controller), controller.getRequest(), responseProxy, controller.getPara()); + HttpServletRequest request = controller.getRequest(); + for (Enumeration names = request.getAttributeNames(); names.hasMoreElements(); ) { + String name = names.nextElement(); + cachedContent.addAttr(name, request.getAttribute(name)); + } - inv.invoke(); + HttpServletResponse response = controller.getResponse(); + Collection headerNames = response.getHeaderNames(); + headerNames.forEach(name -> cachedContent.addHeader(name, response.getHeader(name))); + AopCache.putDataToCache(cacheName, cacheKey, cachedContent, liveSeconds); } - private void writeCachedContent(Controller controller, CachedContent cachedContent) { - HttpServletResponse response = controller.getResponse(); - Map headers = cachedContent.getHeaders(); - if (headers != null && !headers.isEmpty()) { + /** + * 渲染缓存的 ActionCachedContent + * + * @param controller + * @param actionCachedContent + */ + private void renderActionCachedContent(Controller controller, ActionCachedContent actionCachedContent) { + Map attrs = actionCachedContent.getAttrs(); + if (attrs != null) { + attrs.forEach(controller::setAttr); + } + + + Map headers = actionCachedContent.getHeaders(); + if (headers != null) { + HttpServletResponse response = controller.getResponse(); headers.forEach(response::addHeader); } - controller.render(cachedContent.createRender()); + + controller.render(actionCachedContent.createRender()); } + /** * Service 层的 Cacheable 使用 * diff --git a/src/main/java/io/jboot/components/cache/interceptor/Utils.java b/src/main/java/io/jboot/components/cache/interceptor/Utils.java index 7942db1e..c17cef63 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/Utils.java +++ b/src/main/java/io/jboot/components/cache/interceptor/Utils.java @@ -115,8 +115,8 @@ class Utils { public static void ensureCacheNameNotBlank(Method method, String cacheName) { if (StrUtil.isBlank(cacheName)) { - throw new JbootException(String.format("Cache name must not empty or blank in method: " + - ClassUtil.buildMethodString(method))); + throw new IllegalStateException("Cache Name must not empty or blank in method: " + + ClassUtil.buildMethodString(method)); } } diff --git a/src/main/java/io/jboot/web/cached/CacheSupportResponseProxy.java b/src/main/java/io/jboot/web/cached/CacheSupportResponseProxy.java deleted file mode 100644 index 3a1d6e74..00000000 --- a/src/main/java/io/jboot/web/cached/CacheSupportResponseProxy.java +++ /dev/null @@ -1,254 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.web.cached; - -import javax.servlet.ServletOutputStream; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Collection; -import java.util.Locale; - -public class CacheSupportResponseProxy implements HttpServletResponse { - - final private HttpServletResponse proxy; - private CacheSupportWriter cacheWriter; - private String cacheName; - private String cacheKey; - private int cacheLiveSeconds; - - - public CacheSupportResponseProxy(HttpServletResponse proxy) { - this.proxy = proxy; - } - - @Override - public void addCookie(Cookie cookie) { - proxy.addCookie(cookie); - } - - @Override - public boolean containsHeader(String name) { - return proxy.containsHeader(name); - } - - @Override - public String encodeURL(String url) { - return proxy.encodeURL(url); - } - - @Override - public String encodeRedirectURL(String url) { - return proxy.encodeRedirectURL(url); - } - - @Override - public String encodeUrl(String url) { - return proxy.encodeUrl(url); - } - - @Override - public String encodeRedirectUrl(String url) { - return proxy.encodeRedirectUrl(url); - } - - @Override - public void sendError(int sc, String msg) throws IOException { - proxy.sendError(sc, msg); - } - - @Override - public void sendError(int sc) throws IOException { - proxy.sendError(sc); - } - - @Override - public void sendRedirect(String location) throws IOException { - proxy.sendRedirect(location); - } - - @Override - public void setDateHeader(String name, long date) { - proxy.setDateHeader(name, date); - } - - @Override - public void addDateHeader(String name, long date) { - proxy.addDateHeader(name, date); - } - - @Override - public void setHeader(String name, String value) { - proxy.setHeader(name, value); - } - - @Override - public void addHeader(String name, String value) { - proxy.addHeader(name, value); - } - - @Override - public void setIntHeader(String name, int value) { - proxy.setIntHeader(name, value); - } - - @Override - public void addIntHeader(String name, int value) { - proxy.addIntHeader(name, value); - } - - @Override - public void setStatus(int sc) { - proxy.setStatus(sc); - } - - @Override - public void setStatus(int sc, String sm) { - proxy.setStatus(sc, sm); - } - - @Override - public int getStatus() { - return proxy.getStatus(); - } - - @Override - public String getHeader(String name) { - return proxy.getHeader(name); - } - - @Override - public Collection getHeaders(String name) { - return proxy.getHeaders(name); - } - - @Override - public Collection getHeaderNames() { - return proxy.getHeaderNames(); - } - - @Override - public String getCharacterEncoding() { - return proxy.getCharacterEncoding(); - } - - @Override - public String getContentType() { - return proxy.getContentType(); - } - - @Override - public ServletOutputStream getOutputStream() throws IOException { - return proxy.getOutputStream(); - } - - @Override - public PrintWriter getWriter() throws IOException { - if (cacheWriter == null) { - cacheWriter = new CacheSupportWriter(proxy.getWriter()); - } - return cacheWriter; - } - - @Override - public void setCharacterEncoding(String charset) { - proxy.setCharacterEncoding(charset); - } - - @Override - public void setContentLength(int len) { - proxy.setContentLength(len); - } - - @Override - public void setContentLengthLong(long len) { - proxy.setContentLengthLong(len); - } - - @Override - public void setContentType(String type) { - proxy.setContentType(type); - } - - @Override - public void setBufferSize(int size) { - proxy.setBufferSize(size); - } - - @Override - public int getBufferSize() { - return proxy.getBufferSize(); - } - - @Override - public void flushBuffer() throws IOException { - proxy.flushBuffer(); - } - - @Override - public void resetBuffer() { - proxy.resetBuffer(); - } - - @Override - public boolean isCommitted() { - return proxy.isCommitted(); - } - - @Override - public void reset() { - proxy.reset(); - } - - @Override - public void setLocale(Locale loc) { - proxy.setLocale(loc); - } - - @Override - public Locale getLocale() { - return proxy.getLocale(); - } - - public String getResponseString() { - return cacheWriter != null ? cacheWriter.getWriterString() : null; - } - - public String getCacheName() { - return cacheName; - } - - public void setCacheName(String cacheName) { - this.cacheName = cacheName; - } - - public String getCacheKey() { - return cacheKey; - } - - public void setCacheKey(String cacheKey) { - this.cacheKey = cacheKey; - } - - public int getCacheLiveSeconds() { - return cacheLiveSeconds; - } - - public void setCacheLiveSeconds(int cacheLiveSeconds) { - this.cacheLiveSeconds = cacheLiveSeconds; - } -} diff --git a/src/main/java/io/jboot/web/cached/CacheSupportWriter.java b/src/main/java/io/jboot/web/cached/CacheSupportWriter.java deleted file mode 100644 index 9740e8ea..00000000 --- a/src/main/java/io/jboot/web/cached/CacheSupportWriter.java +++ /dev/null @@ -1,270 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.web.cached; - -import java.io.*; -import java.util.Formatter; -import java.util.Locale; - -public class CacheSupportWriter extends PrintWriter { - - final PrintWriter proxy; - final StringWriter cached; - - private Formatter formatter; - - public CacheSupportWriter(PrintWriter proxy) { - super(proxy); - this.proxy = proxy; - this.cached = new StringWriter(); - } - - @Override - public void flush() { - proxy.flush(); - cached.flush(); - } - - @Override - public void close() { - proxy.close(); - try { - cached.close(); - } catch (IOException e) { - } - } - - @Override - public boolean checkError() { - return proxy.checkError(); - } - - - @Override - public void write(int c) { - proxy.write(c); - cached.write(c); - } - - @Override - public void write(char[] buf, int off, int len) { - proxy.write(buf, off, len); - cached.write(buf, off, len); - } - - @Override - public void write(char[] buf) { - proxy.write(buf); - try { - cached.write(buf); - } catch (IOException e) { - } - } - - @Override - public void write(String s, int off, int len) { - proxy.write(s, off, len); - cached.write(s, off, len); - } - - @Override - public void write(String s) { - proxy.write(s); - cached.write(s); - } - - @Override - public void print(boolean b) { - proxy.print(b); - cached.write(b ? "true" : "false"); - } - - @Override - public void print(char c) { - proxy.print(c); - cached.write(c); - } - - @Override - public void print(int i) { - proxy.print(i); - cached.write(i); - } - - @Override - public void print(long l) { - proxy.print(l); - cached.write(String.valueOf(l)); - } - - @Override - public void print(float f) { - proxy.print(f); - cached.write(String.valueOf(f)); - } - - @Override - public void print(double d) { - proxy.print(d); - cached.write(String.valueOf(d)); - } - - @Override - public void print(char[] s) { - proxy.print(s); - try { - cached.write(s); - } catch (IOException e) { - } - } - - @Override - public void print(String s) { - if (s == null) { - s = "null"; - } - proxy.print(s); - cached.write(s); - } - - @Override - public void print(Object obj) { - proxy.print(obj); - cached.write(String.valueOf(obj)); - } - - @Override - public void println() { - proxy.println(); - cached.write("\n"); - } - - @Override - public void println(boolean x) { - proxy.println(x); - cached.write(x + "\n"); - } - - @Override - public void println(char x) { - proxy.println(x); - cached.write(x + "\n"); - } - - @Override - public void println(int x) { - proxy.println(x); - cached.write(x + "\n"); - } - - @Override - public void println(long x) { - proxy.println(x); - cached.write(x + "\n"); - } - - @Override - public void println(float x) { - proxy.println(x); - cached.write(x + "\n"); - } - - @Override - public void println(double x) { - proxy.println(x); - cached.write(x + "\n"); - } - - @Override - public void println(char[] x) { - proxy.println(x); - cached.write(x + "\n"); - } - - @Override - public void println(String x) { - proxy.println(x); - cached.write(x + "\n"); - } - - @Override - public void println(Object x) { - proxy.println(x); - cached.write(x + "\n"); - } - - @Override - public PrintWriter printf(String format, Object... args) { -// proxy.printf(format, args); - format(format, args); - return this; - } - - @Override - public PrintWriter printf(Locale l, String format, Object... args) { -// proxy.printf(l, format, args); - format(l, format, args); - return this; - } - - @Override - public PrintWriter format(String format, Object... args) { - proxy.format(format, args); - - if ((formatter == null) - || (formatter.locale() != Locale.getDefault())) { - formatter = new Formatter(cached); - } - formatter.format(Locale.getDefault(), format, args); - return this; - } - - @Override - public PrintWriter format(Locale l, String format, Object... args) { - proxy.format(l, format, args); - - if ((formatter == null) || (formatter.locale() != l)) { - formatter = new Formatter(cached, l); - } - formatter.format(l, format, args); - return this; - } - - @Override - public PrintWriter append(CharSequence csq) { - proxy.append(csq); - cached.append(csq); - return this; - } - - @Override - public PrintWriter append(CharSequence csq, int start, int end) { - proxy.append(csq, start, end); - cached.append(csq, start, end); - return this; - } - - @Override - public PrintWriter append(char c) { - proxy.append(c); - cached.append(c); - return this; - } - - public String getWriterString() { - return cached.toString(); - } -} diff --git a/src/main/java/io/jboot/web/cached/CachedContent.java b/src/main/java/io/jboot/web/cached/CachedContent.java deleted file mode 100644 index bea4238c..00000000 --- a/src/main/java/io/jboot/web/cached/CachedContent.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.web.cached; - -import com.jfinal.render.Render; -import com.jfinal.render.TextRender; - -import java.io.Serializable; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -public class CachedContent implements Serializable { - - private String content; - private String contentType; - private Map headers; - - public CachedContent() { - } - - public static CachedContent fromResponseProxy(CacheSupportResponseProxy responseProxy) { - CachedContent cachedContent = new CachedContent(); - cachedContent.setContentType(responseProxy.getContentType()); - cachedContent.setContent(responseProxy.getResponseString()); - - Collection headerNames = responseProxy.getHeaderNames(); - if (headerNames != null) { - headerNames.forEach(s -> cachedContent.addHeader(s, responseProxy.getHeader(s))); - } - return cachedContent; - } - - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getContentType() { - return contentType; - } - - public void setContentType(String contentType) { - this.contentType = contentType; - } - - public Map getHeaders() { - return headers; - } - - public void setHeaders(Map headers) { - this.headers = headers; - } - - public void addHeader(String key, String value) { - if (this.headers == null) { - this.headers = new HashMap<>(); - } - this.headers.put(key, value); - } - - - public Render createRender() { - return new TextRender(getContent(), getContentType()); - } -} diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index ef28f021..1f3fe37b 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -23,13 +23,10 @@ import com.jfinal.render.Render; import com.jfinal.render.RenderException; import com.jfinal.template.TemplateException; import io.jboot.app.JbootApplicationConfig; -import io.jboot.components.cache.ActionCache; import io.jboot.components.valid.ValidErrorRender; import io.jboot.components.valid.ValidException; import io.jboot.components.valid.ValidUtil; import io.jboot.utils.ClassUtil; -import io.jboot.web.cached.CacheSupportResponseProxy; -import io.jboot.web.cached.CachedContent; import io.jboot.web.controller.JbootControllerContext; import io.jboot.web.render.JbootErrorRender; import io.jboot.web.render.JbootRenderFactory; @@ -162,13 +159,6 @@ public class JbootActionHandler extends ActionHandler { } protected void doAfterRender(Action action, Controller controller) { - // Controller 缓存的支持,必须在 render() 执行之后,才能通过 response 获取缓存信息 - if (controller.getResponse() instanceof CacheSupportResponseProxy) { - CacheSupportResponseProxy responseProxy = (CacheSupportResponseProxy) controller.getResponse(); - CachedContent cachedContent = CachedContent.fromResponseProxy(responseProxy); - ActionCache.putDataToCache(responseProxy.getCacheName(), responseProxy.getCacheKey(), - cachedContent, responseProxy.getCacheLiveSeconds()); - } } diff --git a/src/main/java/io/jboot/web/render/JbootRender.java b/src/main/java/io/jboot/web/render/JbootRender.java index 31cc1472..daedc14b 100644 --- a/src/main/java/io/jboot/web/render/JbootRender.java +++ b/src/main/java/io/jboot/web/render/JbootRender.java @@ -34,7 +34,7 @@ import java.util.Map; public class JbootRender extends Render { private static Engine engine; - private static final String contentType = "text/html; charset=" + getEncoding(); + private static String contentType = "text/html; charset=" + getEncoding(); private static JbootWebCdnConfig cdnConfig = Jboot.config(JbootWebCdnConfig.class); private Engine getEngine() { diff --git a/src/main/java/io/jboot/web/render/JbootXmlRender.java b/src/main/java/io/jboot/web/render/JbootXmlRender.java index 58b0e429..c48aa1c3 100644 --- a/src/main/java/io/jboot/web/render/JbootXmlRender.java +++ b/src/main/java/io/jboot/web/render/JbootXmlRender.java @@ -19,7 +19,7 @@ package io.jboot.web.render; * @author Michael Yang 杨福海 (fuhai999@gmail.com) * @version V1.0 */ -public class JbootXmlRender extends JbootRender { +public class JbootXmlRender extends JbootTemplateRender { private static final String contentType = "text/xml; charset=" + getEncoding(); diff --git a/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java b/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java index 159e366c..f03ed4ee 100644 --- a/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java +++ b/src/main/java/io/jboot/web/validate/interceptor/EmptyValidateInterceptor.java @@ -88,7 +88,7 @@ public class EmptyValidateInterceptor implements Interceptor { } } else { throw new IllegalArgumentException("@EmptyValidate not support form type : " + formType + ", " + - "see : io.jboot.web.controller.validate.FormType"); + "see: io.jboot.web.controller.validate.FormType"); } if (paraValue == null || paraValue.trim().length() == 0) { @@ -100,6 +100,7 @@ public class EmptyValidateInterceptor implements Interceptor { , AnnotationUtil.get(emptyParaValidate.htmlPath()) , formAnnotation.errorCode() ); + return false; } } diff --git a/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorUtil.java b/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorUtil.java index 99e02bdd..086d9f13 100644 --- a/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorUtil.java +++ b/src/main/java/io/jboot/web/validate/interceptor/ValidateInterceptorUtil.java @@ -42,7 +42,6 @@ public class ValidateInterceptorUtil { } - static String buildErrorMessage(Invocation inv, String annotation) { StringBuilder sb = new StringBuilder(); sb.append("method \"").append(inv.getController().getClass().getName()) @@ -74,7 +73,7 @@ public class ValidateInterceptorUtil { } break; case ValidateRenderType.JSON: - Ret baseRet = Ret.fail("message", message) + Ret baseRet = Ret.fail("message", message) .set("reason", reason) .set("errorCode", errorCode) .setIfNotNull("formName", formName); @@ -96,8 +95,10 @@ public class ValidateInterceptorUtil { } - public static interface ValidExceptionRetBuilder{ + public static interface ValidExceptionRetBuilder { + ValidExceptionRetBuilder DEFAULT_BUILDER = ret -> ret; + Ret build(Ret baseRet); } -- Gitee From 8e64635e072d2e7385335cdeab52aca807206505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 24 Sep 2022 14:58:05 +0800 Subject: [PATCH 1798/1965] restructure @Cacheable for Controller --- .../cache/interceptor/ActionCachedContent.java | 6 +++--- .../cache/interceptor/CacheableInterceptor.java | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/interceptor/ActionCachedContent.java b/src/main/java/io/jboot/components/cache/interceptor/ActionCachedContent.java index e710d033..ea2f0714 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/ActionCachedContent.java +++ b/src/main/java/io/jboot/components/cache/interceptor/ActionCachedContent.java @@ -145,8 +145,6 @@ class ActionCachedContent implements Serializable { switch (renderType) { case RENDER_TEMPLATE: return renderFactory.getTemplateRender(viewOrText); - case RENDER_XML: - return renderFactory.getXmlRender(viewOrText); case RENDER_JSON: JsonRender jsonRender; if (otherPara.get("jsonText") != null) { @@ -162,8 +160,10 @@ class ActionCachedContent implements Serializable { return jsonRender; case RENDER_TEXT: return renderFactory.getTextRender(viewOrText, contentType); + case RENDER_XML: + return renderFactory.getXmlRender(viewOrText); default: - throw new IllegalArgumentException("@Cacheable can not support the renderType of the value: " + renderType); + throw new IllegalStateException("@Cacheable can not support the renderType of the value: " + renderType); } } } diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java index dd51419c..a1a7193a 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java @@ -132,14 +132,27 @@ public class CacheableInterceptor implements Interceptor { private void renderActionCachedContent(Controller controller, ActionCachedContent actionCachedContent) { Map attrs = actionCachedContent.getAttrs(); if (attrs != null) { - attrs.forEach(controller::setAttr); + HttpServletRequest request = controller.getRequest(); + attrs.forEach((key, value) -> { + Object existValue = request.getAttribute(key); + if (existValue != null) { + value = existValue; + } + request.setAttribute(key, value); + }); } Map headers = actionCachedContent.getHeaders(); if (headers != null) { HttpServletResponse response = controller.getResponse(); - headers.forEach(response::addHeader); + headers.forEach((name, value) -> { + String existValue = response.getHeader(name); + if (existValue != null) { + value = existValue; + } + response.setHeader(name, value); + }); } controller.render(actionCachedContent.createRender()); -- Gitee From a0a5d3dbd9df99306f9562a5e7f7d0cd2e3dda23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 24 Sep 2022 15:07:50 +0800 Subject: [PATCH 1799/1965] restructure @Cacheable for Controller --- .../interceptor/CacheableInterceptor.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java index a1a7193a..a4237a4f 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java @@ -130,15 +130,14 @@ public class CacheableInterceptor implements Interceptor { * @param actionCachedContent */ private void renderActionCachedContent(Controller controller, ActionCachedContent actionCachedContent) { - Map attrs = actionCachedContent.getAttrs(); - if (attrs != null) { + Map cachedAttrs = actionCachedContent.getAttrs(); + if (cachedAttrs != null) { HttpServletRequest request = controller.getRequest(); - attrs.forEach((key, value) -> { - Object existValue = request.getAttribute(key); - if (existValue != null) { - value = existValue; + Set existAttrNames = getRequestAttrNames(request); + cachedAttrs.forEach((cachedAttrName, value) -> { + if (!existAttrNames.contains(cachedAttrName)) { + request.setAttribute(cachedAttrName, value); } - request.setAttribute(key, value); }); } @@ -147,9 +146,9 @@ public class CacheableInterceptor implements Interceptor { if (headers != null) { HttpServletResponse response = controller.getResponse(); headers.forEach((name, value) -> { - String existValue = response.getHeader(name); - if (existValue != null) { - value = existValue; + String existHeaderValue = response.getHeader(name); + if (existHeaderValue != null) { + value = existHeaderValue; } response.setHeader(name, value); }); @@ -159,6 +158,14 @@ public class CacheableInterceptor implements Interceptor { } + private Set getRequestAttrNames(HttpServletRequest request) { + Set ret = new HashSet<>(); + for (Enumeration attrNames = request.getAttributeNames(); attrNames.hasMoreElements(); ) { + ret.add(attrNames.nextElement()); + } + return ret; + } + /** * Service 层的 Cacheable 使用 * -- Gitee From 028f51f796919c1fc6991f1316e8b88ce24e0ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 24 Sep 2022 16:00:19 +0800 Subject: [PATCH 1800/1965] restructure @Cacheable for Controller --- .../interceptor/ActionCachedContent.java | 39 ++++++++++++++++--- .../interceptor/CacheableInterceptor.java | 11 +++++- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/interceptor/ActionCachedContent.java b/src/main/java/io/jboot/components/cache/interceptor/ActionCachedContent.java index ea2f0714..4d62b476 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/ActionCachedContent.java +++ b/src/main/java/io/jboot/components/cache/interceptor/ActionCachedContent.java @@ -16,21 +16,26 @@ package io.jboot.components.cache.interceptor; import com.jfinal.render.*; +import io.jboot.web.render.JbootRender; import io.jboot.web.render.JbootTemplateRender; import io.jboot.web.render.JbootXmlRender; import java.io.Serializable; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; -class ActionCachedContent implements Serializable { +public class ActionCachedContent implements Serializable { - private static final int RENDER_TEMPLATE = 0; - private static final int RENDER_XML = 1; - private static final int RENDER_JSON = 2; - private static final int RENDER_TEXT = 3; + private static final int RENDER_DEFAULT = 0; + private static final int RENDER_TEMPLATE = 1; + private static final int RENDER_XML = 2; + private static final int RENDER_JSON = 3; + private static final int RENDER_TEXT = 4; private static IRenderFactory renderFactory = RenderManager.me().getRenderFactory(); + private static final Set ignoreAttrs = new HashSet<>(); private Map headers; @@ -41,6 +46,14 @@ class ActionCachedContent implements Serializable { private String viewOrText; private Map otherPara = null; + public static Set getIgnoreAttrs() { + return ignoreAttrs; + } + + public static void addIgnoreAttr(String attrName) { + ignoreAttrs.add(attrName); + } + public ActionCachedContent(Render render) { if (render == null) { throw new IllegalArgumentException("Render can not be null."); @@ -58,6 +71,12 @@ class ActionCachedContent implements Serializable { contentType = ((JbootTemplateRender) render).getContentType(); viewOrText = render.getView(); } + // default + else if (render instanceof JbootRender) { + renderType = RENDER_DEFAULT; + contentType = ((JbootRender) render).getContentType(); + viewOrText = render.getView(); + } // text else if (render instanceof TextRender) { renderType = RENDER_TEXT; @@ -92,9 +111,17 @@ class ActionCachedContent implements Serializable { public void setAttrs(Map attrs) { this.attrs = attrs; + if (this.attrs != null) { + for (String ignoreAttr : ignoreAttrs) { + this.attrs.remove(ignoreAttr); + } + } } public void addAttr(String key, Object value) { + if (ignoreAttrs.contains(key)) { + return; + } if (this.attrs == null) { this.attrs = new HashMap<>(); } @@ -143,6 +170,8 @@ class ActionCachedContent implements Serializable { public Render createRender() { switch (renderType) { + case RENDER_DEFAULT: + return renderFactory.getRender(viewOrText); case RENDER_TEMPLATE: return renderFactory.getTemplateRender(viewOrText); case RENDER_JSON: diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java index a4237a4f..3208919b 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java @@ -41,6 +41,7 @@ import java.util.*; public class CacheableInterceptor implements Interceptor { private static final String NULL_VALUE = "NULL_VALUE"; + public static final String IGNORE_CACHED_ATTRS = "__ignore_cached_attrs"; //是否开启 Controller 的 Action 缓存 //可用在 dev 模式下关闭,生产环境开启的场景,方便调试数据 @@ -109,10 +110,18 @@ public class CacheableInterceptor implements Interceptor { ActionCachedContent cachedContent = new ActionCachedContent(controller.getRender()); + // 忽略的缓存配置 + Set ignoreCachedAttrs = controller.getAttr(IGNORE_CACHED_ATTRS); + if (ignoreCachedAttrs != null) { + ignoreCachedAttrs.add(IGNORE_CACHED_ATTRS); + } + HttpServletRequest request = controller.getRequest(); for (Enumeration names = request.getAttributeNames(); names.hasMoreElements(); ) { String name = names.nextElement(); - cachedContent.addAttr(name, request.getAttribute(name)); + if (ignoreCachedAttrs == null || !ignoreCachedAttrs.contains(name)) { + cachedContent.addAttr(name, request.getAttribute(name)); + } } HttpServletResponse response = controller.getResponse(); -- Gitee From fde13acd471437114cdfc17a292e326b6fcfaeb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 24 Sep 2022 19:31:26 +0800 Subject: [PATCH 1801/1965] update docs --- doc/docs/cache.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/doc/docs/cache.md b/doc/docs/cache.md index 3a7255dc..70bee8d2 100644 --- a/doc/docs/cache.md +++ b/doc/docs/cache.md @@ -28,7 +28,7 @@ Jboot 定位为高性能的微服务框架,然而高性能离不开合理的 默认情况下,用户无需做任何配置就可以使用 Jboot 的缓存功能,默认情况下 Jboot 是使用 `caffeine` 作为 Jboot 的缓存方案。 -如果需要修把 `caffeine` 方案修改为使用 `redis` ,则可以添加如下的配置: +如需把 `caffeine` 方案修改为使用 `redis` ,则可以添加如下的配置: ```properties jboot.cache.type = redis @@ -194,3 +194,59 @@ public class CommentServiceImpl implements CommentService { - `getCommentByIdWithCacheTime` 使用 `@Cacheable` 注解,但是添加了 `5秒` 的时间限制,因此,在 5秒钟内,无论调用多少次,返回的随机数都是一样的,5秒之后缓存被删除,再次调用之后会是一个新的随机数,新的随机数会继续缓存 5秒钟。 - `updateCache` 使用了注解 `@CachePut` ,每次调用此方法之后,会更新掉该 id 值的缓存 - `delCache` 使用了 `@CacheEvict` 注解,每次调用会删除该 id 值的缓存 + +**@Cacheable 在 Controller 中使用时注意事项** + +默认情况下,@Cacheable 在 Controller 中使用时,系统会缓存 Request 的所有 Attributes 内容,这些内容可能来源于 +Controller 的 Action 通过 setAttr 进行设置,也可能来源于 Interceptor 设置。 + +当下次请求的时候,Jboot 会直接使用缓存的数据进行直接渲染,而不再次执行 Controller 的 Action。 + +但是,在很多时候,我们并不希望 Jboot 缓存所有的数据,比如:用户的登录状态。此时,我们可以通过如下的方法过滤掉缓存数据, +保证每个请求都是最新的,而非缓存的数据。 + +```java +ActionCachedContent.addIgnoreAttr("ACCOUNT") +``` + +通过这个配置后,我们在拦截器 Interceptor 中添加的 ACCOUNT 属性,不会进行缓存。从而达到每次请求,都是都是最新的 ACCOUNT +数据。 + +```java +class AccountInterceptor extends Interceptor { + + public void intercept(Invocation inv) { + Accont account = accountService.findBy.... + inv.getController().setAttr("ACCOUNT",account); + + //.... + } +} +``` + +此时,虽然我们给 Controller 添加了 @Cacheable 属性,但是 ACCOUNT 的数据每个请求都是最新的。值得一提的是: + +```java +ActionCachedContent.addIgnoreAttr("ACCOUNT") +``` +是全局配置的,如果我们想要单独为每个请求配置不缓存的 attribute 时,可以在通过如下方式添加 + +```java +class AccountInterceptor extends Interceptor { + + public void intercept(Invocation inv) { + Set ignoreAttrs = new HashSet<>(); + ignoreAttrs.add("attr1"); + ignoreAttrs.add("attr2"); + ignoreAttrs.add("attr..."); + + //设置当前请求不进行缓存的 attr 属性 + inv.getController().set(CacheableInterceptor.IGNORE_CACHED_ATTRS,ignoreAttrs); + + Accont account = accountService.findBy.... + inv.getController().setAttr("ACCOUNT", account); + + //.... + } +} +``` \ No newline at end of file -- Gitee From aecc61cb1e92d2d4390ad67cf15467db463dfd16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 25 Sep 2022 15:55:29 +0800 Subject: [PATCH 1802/1965] v3.16.0 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index d792f231..0d602a56 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.15.10 + 3.16.0 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index a0dc1ba8..6e731b59 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.15.10 + 3.16.0 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index be49c4f5..4421db7a 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.15.10 + 3.16.0 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.15.10', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.16.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 6f36edd6..685de1ba 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.15.10', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.16.0', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.15.10/jboot-3.15.10.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.0/jboot-3.16.0.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index affd4d85..619c82cb 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.15.10 + 3.16.0 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index be08f137..3734c8b7 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.15.10"; + public static String VERSION = "3.16.0"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From e16c02989c9af7a6a59876afd14227dda2aa132e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 25 Sep 2022 15:55:34 +0800 Subject: [PATCH 1803/1965] v3.16.0 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 9 +++++++++ pom.xml | 10 +++++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c0b870e0..0ba2b307 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.15.10 + 3.16.0 ``` diff --git a/changes.txt b/changes.txt index 8c32d6de..206b377e 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,12 @@ +jboot v3.16.0: +优化:重构 @Cacheable 在 Controller 中的使用,使之更加灵活方便 +优化:FileUtil.unzip 方法,添加可以指定解压缩编码的参数 +优化:升级 JFinal、 jackson-core 等到最新版本 +修复:JbootServiceBase.initDao 在非泛型子类时初始化出错的问题 +修复:sqlbuilder 在使用别名 + between 时,生成的 SQL 出错的问题 + + + jboot v3.15.10: 优化:增强 http 工具类,默认支持携带 cookie 重定向 优化:升级 Jsoup 到 v1.15.3 最新版本 diff --git a/pom.xml b/pom.xml index c77d7bc0..0fe0815c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.15.11-SNAPSHOT + 3.16.0 jar jboot @@ -41,13 +41,13 @@ - 5.0.2 + 5.0.3 2022.2 3.1 3.4 3.3.0 4.0.3 - 2.13.3 + 2.13.4 5.1.49 2.2.19.Final 1.7.36 @@ -72,7 +72,7 @@ 3.14.0.Final 1.5.1 1.1.8 - 4.2.11 + 4.2.12 1.8 1.8 @@ -201,7 +201,7 @@ org.javassist javassist - 3.29.1-GA + 3.29.2-GA -- Gitee From fc773189ef5e075c980e227423ed987b040f587b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 29 Sep 2022 17:26:36 +0800 Subject: [PATCH 1804/1965] v3.16.1 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0fe0815c..0d091c0f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.0 + 3.16.1-SNAPSHOT jar jboot -- Gitee From 52ca7981fdb130caf52bd319d59d66555a882f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 29 Sep 2022 17:26:50 +0800 Subject: [PATCH 1805/1965] add: local url print --- .../java/io/jboot/app/JbootApplication.java | 17 +++++++++++++++++ src/test/resources/jboot-product.properties | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/app/JbootApplication.java b/src/main/java/io/jboot/app/JbootApplication.java index 96e70f81..246c57c9 100644 --- a/src/main/java/io/jboot/app/JbootApplication.java +++ b/src/main/java/io/jboot/app/JbootApplication.java @@ -107,6 +107,7 @@ public class JbootApplication { ApplicationUtil.printApplicationInfo(appConfig); ApplicationUtil.printClassPath(); + printLocalURL(undertowConfig); return new JbootUndertowServer(undertowConfig) .configWeb(webBuilder -> { @@ -121,6 +122,22 @@ public class JbootApplication { } + private static void printLocalURL(UndertowConfig config) { + String localUrlMsg = "JbootApplication Local URL: http://localhost:" + config.getPort() + getContextPathInfo(config); + if (config.isSslEnable()) { + localUrlMsg = localUrlMsg + ", https://localhost:" + config.getSslConfig().getPort() + getContextPathInfo(config); + } + + System.out.println(localUrlMsg); + } + + + private static String getContextPathInfo(UndertowConfig config) { + return "/".equals(config.getContextPath()) ? "" : config.getContextPath(); + } + + + public static UndertowConfig createUndertowConfig(JbootApplicationConfig appConfig) { UndertowConfig undertowConfig = new JbootUndertowConfig(appConfig.getJfinalConfig()); undertowConfig.addSystemClassPrefix("io.jboot.app"); diff --git a/src/test/resources/jboot-product.properties b/src/test/resources/jboot-product.properties index 9a9079f5..0d39dd57 100644 --- a/src/test/resources/jboot-product.properties +++ b/src/test/resources/jboot-product.properties @@ -1,2 +1,2 @@ undertow.host=0.0.0.0 -undertow.port=6666 \ No newline at end of file +undertow.port=8866 \ No newline at end of file -- Gitee From 77314ab9b4d9bafd79a6ee38af909cd70748f799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 29 Sep 2022 17:49:28 +0800 Subject: [PATCH 1806/1965] =?UTF-8?q?fixed:=20=E4=BF=AE=E5=A4=8D=20Control?= =?UTF-8?q?ler=20=E9=80=9A=E8=BF=87=20return=20=E6=B8=B2=E6=9F=93=20JSON?= =?UTF-8?q?=20=E6=95=B0=E6=8D=AE=E6=97=B6=EF=BC=8C=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E5=8F=B0=E4=B8=8D=E8=BE=93=E5=87=BA=20Render=20=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jboot/web/handler/JbootActionHandler.java | 6 +++ .../web/handler/JbootActionReporter.java | 40 ++++++++++++++----- .../web/render/JbootReturnValueRender.java | 4 ++ .../java/io/jboot/test/TestAppStater.java | 2 +- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 1f3fe37b..5258f43e 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -191,11 +191,17 @@ public class JbootActionHandler extends ActionHandler { return; } else { render = returnValueRender; + + //重新设置到 Controller,JbootActionReporter 才能 Controller 获取 render 判断 render 类型 + controller.render(render); } } if (render == null) { render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); + + //重新设置到 Controller,JbootActionReporter 才能 Controller 获取 render 判断 render 类型 + controller.render(render); } render.setContext(controller.getRequest(), controller.getResponse(), action.getViewPath()).render(); diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index 5ea4179b..afc4f776 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -31,6 +31,7 @@ import io.jboot.utils.ReflectUtil; import io.jboot.utils.RequestUtil; import io.jboot.utils.StrUtil; import io.jboot.web.controller.JbootController; +import io.jboot.web.render.JbootReturnValueRender; import javassist.*; import javax.servlet.http.HttpServletRequest; @@ -55,6 +56,7 @@ public class JbootActionReporter { private static ActionReporter actionReporter = JFinal.me().getConstants().getActionReporter(); private static boolean reportEnable = Jboot.isDevMode(); private static boolean colorRenderEnable = true; + private static boolean reportAllText = false; private static final ThreadLocal sdf = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); @@ -85,6 +87,14 @@ public class JbootActionReporter { JbootActionReporter.reportEnable = reportEnable; } + public static boolean isReportAllText() { + return reportAllText; + } + + public static void setReportAllText(boolean reportAllText) { + JbootActionReporter.reportAllText = reportAllText; + } + public static boolean isColorRenderEnable() { return colorRenderEnable; } @@ -219,15 +229,14 @@ public class JbootActionReporter { } } - appendRenderMessage(controller, sb); + appendRenderMessage(controller.getRender(), sb); sb.append("----------------------------------- taked " + (System.currentTimeMillis() - time) + " ms --------------------------------\n\n\n"); writer.write(sb.toString()); } - private static void appendRenderMessage(Controller controller, StringBuilder sb) { - Render render = controller.getRender(); + private static void appendRenderMessage(Render render, StringBuilder sb) { if (render == null) { return; } @@ -240,20 +249,14 @@ public class JbootActionReporter { jsontext = ""; } jsontext = jsontext.replace("\n", ""); - if (jsontext.length() > 100) { - jsontext = jsontext.substring(0, 100) + "..."; - } - sb.append("Render : ").append(jsontext); + sb.append("Render : ").append(getRenderText(jsontext)); } else if (render instanceof TextRender) { String text = ((TextRender) render).getText(); if (text == null) { text = ""; } text = text.replace("\n", ""); - if (text.length() > 100) { - text = text.substring(0, 100) + "..."; - } - sb.append("Render : ").append(text); + sb.append("Render : ").append(getRenderText(text)); } else if (render instanceof FileRender) { File file = ReflectUtil.getFieldValue(render, "file"); sb.append("Render : ").append(file); @@ -262,6 +265,8 @@ public class JbootActionReporter { sb.append("Redirect : ").append(url); } else if (render instanceof NullRender) { sb.append("Render : null"); + } else if (render instanceof JbootReturnValueRender) { + appendRenderMessage(((JbootReturnValueRender) render).getRealRender(), sb); } else { sb.append("Render : ").append(ClassUtil.getUsefulClass(render.getClass()).getName()); } @@ -269,6 +274,19 @@ public class JbootActionReporter { } + private static String getRenderText(String orignalText) { + if (StrUtil.isBlank(orignalText)) { + return ""; + } + + if (!reportAllText && orignalText.length() > 100) { + return orignalText.substring(0, 100) + "..."; + } + + return orignalText; + } + + private static String getClassFileName(Class clazz) { String classFileName = clazz.getName(); if (classFileName.contains("$")) { diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index 475444cc..848b4b65 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -157,6 +157,10 @@ public class JbootReturnValueRender extends Render { || c == BigDecimal.class || c == BigInteger.class; } + public Render getRealRender(){ + return render; + } + public String getForwardTo() { return forwardTo; } diff --git a/src/test/java/io/jboot/test/TestAppStater.java b/src/test/java/io/jboot/test/TestAppStater.java index 00123fb2..640e36c5 100644 --- a/src/test/java/io/jboot/test/TestAppStater.java +++ b/src/test/java/io/jboot/test/TestAppStater.java @@ -5,7 +5,7 @@ import io.jboot.app.JbootApplication; public class TestAppStater { public static void main(String[] args){ - JbootApplication.setBootArg("jboot.app.mode","product"); +// JbootApplication.setBootArg("jboot.app.mode","product"); JbootApplication.run(args); } } -- Gitee From 00ce5e5a0488df721e66b78463caa4eee5a13b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 29 Sep 2022 18:35:23 +0800 Subject: [PATCH 1807/1965] fixed JbootController.getFileOnly() --- .../jboot/web/controller/JbootController.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 37cdb2a8..e787cad1 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -752,15 +752,14 @@ public class JbootController extends Controller { /** - * 建议使用 getFirstFileOnly,否则可能会有安全问题 + * 使用 getFirstFileOnly,否则恶意上传的安全问题 * * @return */ @NotAction - @Deprecated @Override public UploadFile getFile() { - return super.getFile(); + return getFirstFileOnly(); } @@ -782,7 +781,7 @@ public class JbootController extends Controller { for (int i = 1; i < uploadFiles.size(); i++) { UploadFile uploadFile = uploadFiles.get(i); - FileUtil.delete(uploadFile.getFile()); + FileUtil.delete(uploadFile); } return uploadFiles.get(0); @@ -802,14 +801,16 @@ public class JbootController extends Controller { return null; } - UploadFile ret = getFile(name); + UploadFile ret = null; for (UploadFile uploadFile : uploadFiles) { - if (uploadFile != ret) { - FileUtil.delete(uploadFile.getFile()); + if (ret == null && name.equals(uploadFile.getParameterName())) { + ret = uploadFile; + } else { + FileUtil.delete(uploadFile); } } - return uploadFiles.get(0); + return ret; } @@ -834,16 +835,16 @@ public class JbootController extends Controller { Map filesMap = new HashMap<>(); for (UploadFile uploadFile : uploadFiles) { - String name = uploadFile.getParameterName(); - if (StrUtil.isBlank(name)) { + String parameterName = uploadFile.getParameterName(); + if (StrUtil.isBlank(parameterName)) { FileUtil.delete(uploadFile); continue; } - name = name.trim(); + parameterName = parameterName.trim(); - if (keys.contains(name) && !filesMap.containsKey(name)) { - filesMap.put(name, uploadFile); + if (keys.contains(parameterName) && !filesMap.containsKey(parameterName)) { + filesMap.put(parameterName, uploadFile); } else { FileUtil.delete(uploadFile); } -- Gitee From 3e74fbbe4f3cf37a18d8fee15002c0b1216a008e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 29 Sep 2022 18:35:52 +0800 Subject: [PATCH 1808/1965] add FileUtil.ensuresParentExists --- src/main/java/io/jboot/utils/FileUtil.java | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index 6fa68c76..df6e4273 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -100,11 +100,9 @@ public class FileUtil { public static void writeString(File file, String content, String charsetName) { - if (!file.getParentFile().exists()) { - file.getParentFile().mkdirs(); - } FileOutputStream fos = null; try { + ensuresParentExists(file); fos = new FileOutputStream(file, false); fos.write(content.getBytes(charsetName)); } catch (Exception e) { @@ -114,6 +112,13 @@ public class FileUtil { } } + public static void ensuresParentExists(File currentFile) throws IOException { + if (!currentFile.getParentFile().exists() + && !currentFile.getParentFile().mkdirs()) { + throw new IOException("Can not mkdirs for file: " + currentFile.getParentFile()); + } + } + public static void close(Closeable... closeable) { QuietlyUtil.closeQuietly(closeable); @@ -149,12 +154,10 @@ public class FileUtil { File targetFile = new File(targetPath, zipEntry.getName()); - if (!targetFile.toPath().normalize().startsWith(targetPath)) { - throw new RuntimeException("Bad zip entry"); - } + ensuresParentExists(targetFile); - if (!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs()) { - throw new IOException("Can not mkdirs for file: " + targetFile.getParentFile()); + if (!targetFile.toPath().normalize().startsWith(targetPath)) { + throw new IOException("Bad zip entry"); } os = new BufferedOutputStream(new FileOutputStream(targetFile)); @@ -200,7 +203,7 @@ public class FileUtil { } if (!file.delete()) { - LogKit.error("file {} can not deleted!", getCanonicalPath(file)); + LogKit.error("File {} can not deleted!", getCanonicalPath(file)); } } -- Gitee From 761ec363f5fc49f4afd7334d61e78d48b947bec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 30 Sep 2022 09:52:42 +0800 Subject: [PATCH 1809/1965] add JbootModel.deleteAll() method --- src/main/java/io/jboot/db/model/JbootModel.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 96c1e754..135d2f0b 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -457,6 +457,17 @@ public class JbootModel> extends Model { } + public boolean deleteAll() { + Columns columns = Columns.create(); + + //通过 processColumns 可以重构 deleteAll 的行为 + processColumns(columns, "deleteAll"); + + String sql = _getDialect().forDeleteByColumns(alias, joins, _getTableName(), columns.getList()); + return Db.use(_getConfig().getName()).update(sql, Util.getValueArray(columns.getList())) >= 1; + } + + public boolean batchDeleteByIds(Object... idValues) { if (idValues == null || idValues.length == 0) { return false; -- Gitee From b4fe10c9c3a0a9bf5e841d54a417ae33bd24de1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 30 Sep 2022 10:09:28 +0800 Subject: [PATCH 1810/1965] v3.16.1 release (^.^)YYa!! --- src/main/java/io/jboot/JbootConsts.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 3734c8b7..0f17592d 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.16.0"; + public static String VERSION = "3.16.1"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 51cd1d6dbe1da5ac8a66236cb5bce74790e7b642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 30 Sep 2022 10:09:32 +0800 Subject: [PATCH 1811/1965] v3.16.1 release (^.^)YYa!! --- changes.txt | 11 ++++++++++- pom.xml | 6 +++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/changes.txt b/changes.txt index 206b377e..28519aa8 100644 --- a/changes.txt +++ b/changes.txt @@ -1,9 +1,18 @@ +jboot v3.16.1: +新增:APP 在启动的时候,输出 Local URL,方便通过控制台启动浏览器 +新增:FileUtil.ensuresParentExists() 方法 +新增:DAO.deleteAll() 方法 +修复:Controller 通过 return 渲染 Json 数据时,控制台不输出 Render 信息的问题 +修复:JbootController.getFileOnly(name) 返回的数据不正确 + + + jboot v3.16.0: 优化:重构 @Cacheable 在 Controller 中的使用,使之更加灵活方便 优化:FileUtil.unzip 方法,添加可以指定解压缩编码的参数 优化:升级 JFinal、 jackson-core 等到最新版本 修复:JbootServiceBase.initDao 在非泛型子类时初始化出错的问题 -修复:sqlbuilder 在使用别名 + between 时,生成的 SQL 出错的问题 +修复:Sqlbuilder 在使用别名 + between 时,生成的 SQL 出错的问题 diff --git a/pom.xml b/pom.xml index 0d091c0f..a2753d5e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.1-SNAPSHOT + 3.16.1 jar jboot @@ -592,14 +592,14 @@ org.apache.shiro shiro-core - 1.8.0 + 1.9.1 provided org.apache.shiro shiro-web - 1.8.0 + 1.9.1 provided -- Gitee From 7c3161be78bba3eb5fdad854f052946f89634edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 30 Sep 2022 10:12:18 +0800 Subject: [PATCH 1812/1965] v3.16.1 release (^.^)YYa!! --- README.md | 2 +- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0ba2b307..ba131f8c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.16.0 + 3.16.1 ``` diff --git a/doc/docs/install.md b/doc/docs/install.md index 0d602a56..c9860d1c 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.16.0 + 3.16.1 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 6e731b59..2f989002 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.16.0 + 3.16.1 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 4421db7a..53900d8e 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.16.0 + 3.16.1 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.16.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.16.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 685de1ba..003704ee 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.16.0', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.16.1', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.0/jboot-3.16.0.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.1/jboot-3.16.1.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 619c82cb..6c42071c 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.16.0 + 3.16.1 -- Gitee From 9d80a1c55081e3571d0d17dd633a3dfc699fea52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 6 Oct 2022 16:50:35 +0800 Subject: [PATCH 1813/1965] v3.16.2 snapshot --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a2753d5e..a94cef92 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.1 + 3.16.2-SNAPSHOT jar jboot @@ -43,7 +43,7 @@ 5.0.3 2022.2 - 3.1 + 3.2 3.4 3.3.0 4.0.3 -- Gitee From 17cfdc4c8689fbd7ecce44d06f07ff654c325b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 6 Oct 2022 16:51:08 +0800 Subject: [PATCH 1814/1965] upgrade jfinal-undertow and remove JbootApplication.printLocalURL --- .../java/io/jboot/app/JbootApplication.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/main/java/io/jboot/app/JbootApplication.java b/src/main/java/io/jboot/app/JbootApplication.java index 246c57c9..804e656e 100644 --- a/src/main/java/io/jboot/app/JbootApplication.java +++ b/src/main/java/io/jboot/app/JbootApplication.java @@ -107,8 +107,6 @@ public class JbootApplication { ApplicationUtil.printApplicationInfo(appConfig); ApplicationUtil.printClassPath(); - printLocalURL(undertowConfig); - return new JbootUndertowServer(undertowConfig) .configWeb(webBuilder -> { tryAddContenTypes(webBuilder); @@ -122,22 +120,6 @@ public class JbootApplication { } - private static void printLocalURL(UndertowConfig config) { - String localUrlMsg = "JbootApplication Local URL: http://localhost:" + config.getPort() + getContextPathInfo(config); - if (config.isSslEnable()) { - localUrlMsg = localUrlMsg + ", https://localhost:" + config.getSslConfig().getPort() + getContextPathInfo(config); - } - - System.out.println(localUrlMsg); - } - - - private static String getContextPathInfo(UndertowConfig config) { - return "/".equals(config.getContextPath()) ? "" : config.getContextPath(); - } - - - public static UndertowConfig createUndertowConfig(JbootApplicationConfig appConfig) { UndertowConfig undertowConfig = new JbootUndertowConfig(appConfig.getJfinalConfig()); undertowConfig.addSystemClassPrefix("io.jboot.app"); -- Gitee From 1f70b3747c67d0114135e4b96de27b2acf68dd01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 6 Oct 2022 18:07:03 +0800 Subject: [PATCH 1815/1965] add ObjectUtil.obtainNotNull and StrUtil.obtainNotBlank --- src/main/java/io/jboot/utils/ObjectUtil.java | 15 +++++++++++++++ src/main/java/io/jboot/utils/StrUtil.java | 18 +++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/utils/ObjectUtil.java b/src/main/java/io/jboot/utils/ObjectUtil.java index b709ce83..02664ec7 100644 --- a/src/main/java/io/jboot/utils/ObjectUtil.java +++ b/src/main/java/io/jboot/utils/ObjectUtil.java @@ -225,4 +225,19 @@ public class ObjectUtil { } } + + public static T obtainNotNull(T ... ts){ + if (ts == null || ts.length == 0){ + throw new IllegalArgumentException("Arguments is null or empty."); + } + + for (T t : ts) { + if (t != null){ + return t; + } + } + + return null; + } + } diff --git a/src/main/java/io/jboot/utils/StrUtil.java b/src/main/java/io/jboot/utils/StrUtil.java index d1225d5d..077d4157 100644 --- a/src/main/java/io/jboot/utils/StrUtil.java +++ b/src/main/java/io/jboot/utils/StrUtil.java @@ -160,6 +160,21 @@ public class StrUtil extends StrKit { return isBlank(value) ? defaultValue : value; } + + public static String obtainNotBlank(String... values) { + if (values == null || values.length == 0) { + throw new IllegalArgumentException("Arguments is null or empty."); + } + + for (String value : values) { + if (isNotBlank(value)) { + return value; + } + } + + return null; + } + /** * 不是空数据,注意:空格不是空数据 * @@ -184,10 +199,11 @@ public class StrUtil extends StrKit { /** * null 或者 空内容字符串 - * + * 使用 isBlank 代替 * @param str * @return */ + @Deprecated public static boolean isNullOrBlank(String str) { return isBlank(str); } -- Gitee From a8bb8f3c7a910ed703b7bc93db907102d7bd2732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 7 Oct 2022 11:06:07 +0800 Subject: [PATCH 1816/1965] v3.16.2 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index c9860d1c..bef14f71 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.16.1 + 3.16.2 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 2f989002..056aaae0 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.16.1 + 3.16.2 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 53900d8e..3e6aff2e 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.16.1 + 3.16.2 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.16.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.16.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 003704ee..a6c1a163 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.16.1', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.16.2', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.1/jboot-3.16.1.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.2/jboot-3.16.2.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 6c42071c..6bd4868a 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.16.1 + 3.16.2 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 0f17592d..1327cfc0 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.16.1"; + public static String VERSION = "3.16.2"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 0d43309d5c79cf15a6b2b527f7baca9cf350dbba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 7 Oct 2022 11:06:12 +0800 Subject: [PATCH 1817/1965] v3.16.2 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ pom.xml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ba131f8c..94d585c6 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.16.1 + 3.16.2 ``` diff --git a/changes.txt b/changes.txt index 28519aa8..a5429774 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.16.2 2022-10-07: +新增:ObjectUtil.obtainNotNull 方法 +新增:StrUtil.obtainNotBlank 方法 +优化:升级 jfinal-undertow 到最新版本并移除 JbootApplication 的 URL 打印功功能。 + + + jboot v3.16.1: 新增:APP 在启动的时候,输出 Local URL,方便通过控制台启动浏览器 新增:FileUtil.ensuresParentExists() 方法 diff --git a/pom.xml b/pom.xml index a94cef92..aa6a42bd 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.2-SNAPSHOT + 3.16.2 jar jboot -- Gitee From 971b4e9d6472e54fd6af8797c10e064ed92b0bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 12 Oct 2022 12:32:33 +0800 Subject: [PATCH 1818/1965] v3.16.3 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index aa6a42bd..21b62311 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.2 + 3.16.3-SNAPSHOT jar jboot -- Gitee From 9cebd4b6b27999fa35579eca5a5a636880d50391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 12 Oct 2022 12:32:56 +0800 Subject: [PATCH 1819/1965] optimize RequestUtil.getIpAddress() --- src/main/java/io/jboot/utils/RequestUtil.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/io/jboot/utils/RequestUtil.java b/src/main/java/io/jboot/utils/RequestUtil.java index d295fdb9..2efee3d4 100644 --- a/src/main/java/io/jboot/utils/RequestUtil.java +++ b/src/main/java/io/jboot/utils/RequestUtil.java @@ -147,6 +147,10 @@ public class RequestUtil { } } + if ("0:0:0:0:0:0:0:1".equals(ip)) { + ip = "127.0.0.1"; + } + return ip; } -- Gitee From 909b5754dcb87e3fbc5a1947792c4a5b5a7c8655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 12 Oct 2022 12:33:25 +0800 Subject: [PATCH 1820/1965] add method: InterceptorBuilder.Util.isChildClassOf --- src/main/java/io/jboot/aop/InterceptorBuilder.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/io/jboot/aop/InterceptorBuilder.java b/src/main/java/io/jboot/aop/InterceptorBuilder.java index cf202754..ee0cfe93 100644 --- a/src/main/java/io/jboot/aop/InterceptorBuilder.java +++ b/src/main/java/io/jboot/aop/InterceptorBuilder.java @@ -41,6 +41,10 @@ public interface InterceptorBuilder { class Util { + public static boolean isChildClassOf(Class childClass, Class parentClass) { + return parentClass.isAssignableFrom(childClass); + } + public static boolean isController(Class serviceClass) { return Controller.class.isAssignableFrom(serviceClass); } -- Gitee From 21bf421723a3d85203c3a8f5232d467306b3251e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 14 Oct 2022 19:04:05 +0800 Subject: [PATCH 1821/1965] update JbootResourceLoader.java --- .../io/jboot/app/JbootResourceLoader.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/app/JbootResourceLoader.java b/src/main/java/io/jboot/app/JbootResourceLoader.java index a3b018b2..9f26bf89 100644 --- a/src/main/java/io/jboot/app/JbootResourceLoader.java +++ b/src/main/java/io/jboot/app/JbootResourceLoader.java @@ -60,12 +60,14 @@ public class JbootResourceLoader { List resourcesDirs = new ArrayList<>(); findResourcesPath(srcRootPath, resourcesDirs); + String targetPath = classPath.endsWith("/config") + ? new File(classPath,"..").getCanonicalPath() : classPath; for (File resourcesDir : resourcesDirs) { - startNewScanner(resourcesDir.getCanonicalFile(), classPath); + startNewScanner(resourcesDir.getCanonicalFile(), targetPath); } - Runtime.getRuntime().addShutdownHook(new Thread(() -> JbootResourceLoader.this.stop())); - System.err.println("JbootResourceLoader started, Watched resource path name : " + resourcePathName); + Runtime.getRuntime().addShutdownHook(new Thread(JbootResourceLoader.this::stop)); + System.err.println("JbootResourceLoader is started, and watching resource dir: " + resourcePathName); } catch (Exception e) { e.printStackTrace(); @@ -73,12 +75,12 @@ public class JbootResourceLoader { } public void stop() { - scanners.forEach(fileScanner -> fileScanner.stop()); - System.out.println("JbootResourceLoader stoped ......"); + scanners.forEach(FileScanner::stop); + System.out.println("JbootResourceLoader has stopped."); } private void findResourcesPath(File root, List resourcesDirs) { - File[] dirs = root.listFiles(pathname -> pathname.isDirectory()); + File[] dirs = root.listFiles(File::isDirectory); if (dirs == null || dirs.length == 0) { return; } @@ -90,7 +92,7 @@ public class JbootResourceLoader { } if (dir.getName().equals(resourcePathName) - && parentFile.getName().equals("main")) { + && "main".equals(parentFile.getName())) { resourcesDirs.add(dir); } else { findResourcesPath(dir, resourcesDirs); @@ -116,14 +118,16 @@ public class JbootResourceLoader { //文件删除 if (FileScanner.ACTION_DELETE.equals(action)) { - target.delete(); + if (!target.delete()){ + System.err.println("JbootResourceLoader can not delete file: " + target); + } } //新增文件 或 修改文件 else { try { FileUtils.copyFile(new File(file), target); } catch (IOException e) { - e.printStackTrace(); + System.err.println("JbootResourceLoader copy file error: " + e.getMessage()); } } } -- Gitee From 98de493131bd876afc8a81f9fd908aece8de555f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 16 Oct 2022 16:10:38 +0800 Subject: [PATCH 1822/1965] add: SqlBuilder.escapeOrderBySql --- .../java/io/jboot/db/model/SqlBuilder.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index aa9d1064..e76c5468 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -15,6 +15,7 @@ */ package io.jboot.db.model; +import com.jfinal.kit.LogKit; import io.jboot.utils.ArrayUtil; import io.jboot.utils.StrUtil; @@ -223,6 +224,7 @@ public class SqlBuilder { buildJoinSql(sqlBuilder, joins, separator); buildWhereSql(sqlBuilder, columns, separator); + orderBy = escapeOrderBySql(orderBy); if (StrUtil.isNotBlank(orderBy)) { sqlBuilder.append(" ORDER BY ").append(orderBy); } @@ -258,6 +260,8 @@ public class SqlBuilder { buildJoinSql(sqlBuilder, joins, separator); buildWhereSql(sqlBuilder, columns, separator); + orderBy = escapeOrderBySql(orderBy); + if (StrUtil.isNotBlank(orderBy)) { sqlBuilder.append(" ORDER BY ").append(orderBy); } @@ -303,4 +307,23 @@ public class SqlBuilder { return sqlBuilder.toString(); } + + + public static String escapeOrderBySql(String orignalOrderBy) { + if (StrUtil.isNotBlank(orignalOrderBy) && !isValidOrderBySql(orignalOrderBy)) { + LogKit.error("Sql Error: order_by value has inject chars: " + orignalOrderBy); + return ""; + } + return orignalOrderBy; + } + + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + private static String SQL_ORDER_BY_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + private static boolean isValidOrderBySql(String value) { + return value.matches(SQL_ORDER_BY_PATTERN); + } } -- Gitee From 11df640e62d7653157dbe8780747b3374d59bf85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 17 Oct 2022 12:01:04 +0800 Subject: [PATCH 1823/1965] add: JbootController.getParaToBigInteger(index) --- .../jboot/web/controller/JbootController.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index e787cad1..74a66fa7 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -619,6 +619,18 @@ public class JbootController extends Controller { } + @NotAction + public BigInteger getParaToBigInteger(int index) { + return toBigInteger(getTrimPara(index), null); + } + + + @NotAction + public BigInteger getParaToBigInteger(int index, BigInteger defaultValue) { + return toBigInteger(getTrimPara(index), defaultValue); + } + + /** * Returns the value of a request parameter and convert to BigInteger. * @@ -652,6 +664,18 @@ public class JbootController extends Controller { } + @NotAction + public BigInteger getBigInteger(int index) { + return toBigInteger(getTrimPara(index), null); + } + + + @NotAction + public BigInteger getBigInteger(int index, BigInteger defaultValue) { + return toBigInteger(getTrimPara(index), defaultValue); + } + + private BigDecimal toBigDecimal(String value, BigDecimal defaultValue) { try { if (StrKit.isBlank(value)) { -- Gitee From a14a7da90f7e43a7fde1dc928b5e30de0b83c7ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 17 Oct 2022 12:08:55 +0800 Subject: [PATCH 1824/1965] add: SqlBuilder.escapeOrderBySql --- src/main/java/io/jboot/db/model/SqlBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index e76c5468..fa786eb4 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -311,7 +311,7 @@ public class SqlBuilder { public static String escapeOrderBySql(String orignalOrderBy) { if (StrUtil.isNotBlank(orignalOrderBy) && !isValidOrderBySql(orignalOrderBy)) { - LogKit.error("Sql Error: order_by value has inject chars: " + orignalOrderBy); + LogKit.warn("Sql Warn: order_by value has inject chars and be filtered, order_by value: " + orignalOrderBy); return ""; } return orignalOrderBy; -- Gitee From aacc5e61a4d94c720ed74eeddffce5f66c774f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 17 Oct 2022 12:40:42 +0800 Subject: [PATCH 1825/1965] v3.16.3 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index bef14f71..2fc474c6 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.16.2 + 3.16.3 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 056aaae0..7ba4b3f1 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.16.2 + 3.16.3 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 3e6aff2e..0dafc070 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.16.2 + 3.16.3 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.16.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.16.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index a6c1a163..1c48ec82 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.16.2', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.16.3', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.2/jboot-3.16.2.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.3/jboot-3.16.3.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 6bd4868a..314c47c9 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.16.2 + 3.16.3 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 1327cfc0..856a76eb 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.16.2"; + public static String VERSION = "3.16.3"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 61f1ae4ae032941a5431c67bb5f05a5fad168a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 17 Oct 2022 12:40:45 +0800 Subject: [PATCH 1826/1965] v3.16.3 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 9 +++++++++ pom.xml | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 94d585c6..be391d7d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.16.2 + 3.16.3 ``` diff --git a/changes.txt b/changes.txt index a5429774..251bc828 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,12 @@ +jboot v3.16.3 2022-10-17: +新增:SqlBuilder.escapeOrderBySql 用于过滤 Order By 参数 +新增:InterceptorBuilder.Util.isChildClassOf 方法 +新增:JbootController.getParaToBigInteger(index) 方法 +优化:RequestUtil.getIpAddress() 对 127.0.0.1 的处理 +优化:JbootResourceLoader 的日志输出和目标路径的优化 + + + jboot v3.16.2 2022-10-07: 新增:ObjectUtil.obtainNotNull 方法 新增:StrUtil.obtainNotBlank 方法 diff --git a/pom.xml b/pom.xml index 21b62311..9d2b2e36 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.3-SNAPSHOT + 3.16.3 jar jboot -- Gitee From e9fe60a204d2bf973205571bce0f469e6c45f576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 18 Oct 2022 11:56:03 +0800 Subject: [PATCH 1827/1965] v3.16.4 snapshot --- pom.xml | 2 +- .../java/io/jboot/wechat/controller/JbootWechatController.java | 2 +- .../java/io/jboot/wechat/interceptor/WechatUserInterceptor.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 9d2b2e36..ababe8f9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.3 + 3.16.4-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/wechat/controller/JbootWechatController.java b/src/main/java/io/jboot/wechat/controller/JbootWechatController.java index aa8da69a..4f51cc89 100644 --- a/src/main/java/io/jboot/wechat/controller/JbootWechatController.java +++ b/src/main/java/io/jboot/wechat/controller/JbootWechatController.java @@ -192,7 +192,7 @@ public abstract class JbootWechatController extends JbootController { } @NotAction - public void doNotAlloVisitRedirect() { + public void doNotAllowVisitRedirect() { /** * 一般情况下,此方法是为了调整到其他页面,比如让用户扫描二维码之类的 * 由子类去实现 diff --git a/src/main/java/io/jboot/wechat/interceptor/WechatUserInterceptor.java b/src/main/java/io/jboot/wechat/interceptor/WechatUserInterceptor.java index 4a9266e7..51e50e6e 100644 --- a/src/main/java/io/jboot/wechat/interceptor/WechatUserInterceptor.java +++ b/src/main/java/io/jboot/wechat/interceptor/WechatUserInterceptor.java @@ -61,7 +61,7 @@ public class WechatUserInterceptor implements Interceptor { return; } - controller.doNotAlloVisitRedirect(); + controller.doNotAllowVisitRedirect(); } -- Gitee From a83319181c691f6627c28c1f0935830daabf86b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 19 Oct 2022 20:39:08 +0800 Subject: [PATCH 1828/1965] v3.16.4 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- .../jboot/components/cache/ActionCache.java | 8 +++---- .../io/jboot/components/cache/AopCache.java | 8 +++---- .../io/jboot/components/cache/JbootCache.java | 19 +++++++++++++-- .../components/cache/JbootCacheBase.java | 24 ++++++++++++++++--- .../components/cache/none/NoneCacheImpl.java | 14 +++++++++-- src/main/java/io/jboot/utils/CacheUtil.java | 8 +++---- 12 files changed, 70 insertions(+), 27 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 2fc474c6..96de0622 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.16.3 + 3.16.4 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 7ba4b3f1..b0ed91e4 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.16.3 + 3.16.4 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 0dafc070..45726a90 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.16.3 + 3.16.4 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.16.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.16.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 1c48ec82..82212e48 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.16.3', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.16.4', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.3/jboot-3.16.3.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.4/jboot-3.16.4.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 314c47c9..affe8606 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.16.3 + 3.16.4 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 856a76eb..db967064 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.16.3"; + public static String VERSION = "3.16.4"; public static final String ATTR_CONTEXT_PATH = "CPATH"; diff --git a/src/main/java/io/jboot/components/cache/ActionCache.java b/src/main/java/io/jboot/components/cache/ActionCache.java index 25b5a4b0..a62eae39 100644 --- a/src/main/java/io/jboot/components/cache/ActionCache.java +++ b/src/main/java/io/jboot/components/cache/ActionCache.java @@ -12,12 +12,12 @@ public class ActionCache { private static JbootCache actionCache; - public static JbootCache setCurrentPrefix(String cacheNamePrefix) { - return getActionCache().setCurrentCacheNamePrefix(cacheNamePrefix); + public static JbootCache setThreadCacheNamePrefix(String cacheNamePrefix) { + return getActionCache().setThreadCacheNamePrefix(cacheNamePrefix); } - public static void clearCurrentPrefix() { - getActionCache().removeCurrentCacheNamePrefix(); + public static void clearThreadCacheNamePrefix() { + getActionCache().clearThreadCacheNamePrefix(); } static JbootCache getActionCache() { diff --git a/src/main/java/io/jboot/components/cache/AopCache.java b/src/main/java/io/jboot/components/cache/AopCache.java index 089c4dbf..dcbcc813 100644 --- a/src/main/java/io/jboot/components/cache/AopCache.java +++ b/src/main/java/io/jboot/components/cache/AopCache.java @@ -12,12 +12,12 @@ public class AopCache { private static JbootCache aopCache; - public static JbootCache setCurrentPrefix(String cacheNamePrefix) { - return getAopCache().setCurrentCacheNamePrefix(cacheNamePrefix); + public static JbootCache setThreadCacheNamePrefix(String cacheNamePrefix) { + return getAopCache().setThreadCacheNamePrefix(cacheNamePrefix); } - public static void clearCurrentPrefix() { - getAopCache().removeCurrentCacheNamePrefix(); + public static void clearThreadCacheNamePrefix() { + getAopCache().clearThreadCacheNamePrefix(); } static JbootCache getAopCache() { diff --git a/src/main/java/io/jboot/components/cache/JbootCache.java b/src/main/java/io/jboot/components/cache/JbootCache.java index 98efc213..8ddd65cf 100644 --- a/src/main/java/io/jboot/components/cache/JbootCache.java +++ b/src/main/java/io/jboot/components/cache/JbootCache.java @@ -22,9 +22,24 @@ import java.util.List; public interface JbootCache extends com.jfinal.plugin.activerecord.cache.ICache { - JbootCache setCurrentCacheNamePrefix(String cacheNamePrefix); + //设置当前 线程 的缓存前缀 + JbootCache setThreadCacheNamePrefix(String cacheNamePrefix); + + //清除当前 线程 的缓存前缀 + void clearThreadCacheNamePrefix(); + + //配置哪些缓存名称可以忽略线程前缀的影响 + boolean addThreadCacheNamePrefixIngore(String cacheName); + + default void addThreadCacheNamePrefixIngores(String... cacheNames) { + for (String cacheName : cacheNames) { + addThreadCacheNamePrefixIngore(cacheName); + } + } + + //移除对 addThreadCacheNamePrefixIngore 的配置 + boolean removeThreadCacheNamePrefixIngore(String cacheName); - void removeCurrentCacheNamePrefix(); JbootCacheConfig getConfig(); diff --git a/src/main/java/io/jboot/components/cache/JbootCacheBase.java b/src/main/java/io/jboot/components/cache/JbootCacheBase.java index 81da5def..ffe6c0ec 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheBase.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheBase.java @@ -18,6 +18,9 @@ package io.jboot.components.cache; import io.jboot.utils.StrUtil; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + public abstract class JbootCacheBase implements JbootCache { protected JbootCacheConfig config; @@ -26,6 +29,7 @@ public abstract class JbootCacheBase implements JbootCache { this.config = config; } + private Set ignoreThreadCacheNames = ConcurrentHashMap.newKeySet(); private ThreadLocal CACHE_NAME_PREFIX_TL = new ThreadLocal<>(); @Override @@ -34,7 +38,7 @@ public abstract class JbootCacheBase implements JbootCache { } @Override - public JbootCache setCurrentCacheNamePrefix(String cacheNamePrefix) { + public JbootCache setThreadCacheNamePrefix(String cacheNamePrefix) { if (StrUtil.isNotBlank(cacheNamePrefix)) { CACHE_NAME_PREFIX_TL.set(cacheNamePrefix); } else { @@ -44,11 +48,21 @@ public abstract class JbootCacheBase implements JbootCache { } @Override - public void removeCurrentCacheNamePrefix() { + public void clearThreadCacheNamePrefix() { CACHE_NAME_PREFIX_TL.remove(); } + @Override + public boolean addThreadCacheNamePrefixIngore(String cacheName) { + return ignoreThreadCacheNames.add(cacheName); + } + + @Override + public boolean removeThreadCacheNamePrefixIngore(String cacheName) { + return ignoreThreadCacheNames.remove(cacheName); + } + /** * 构建缓存名称 * @@ -57,7 +71,11 @@ public abstract class JbootCacheBase implements JbootCache { */ protected String buildCacheName(String cacheName) { - String cacheNamePrefix = CACHE_NAME_PREFIX_TL.get(); + String cacheNamePrefix = null; + + if (!ignoreThreadCacheNames.contains(cacheName)){ + cacheNamePrefix = CACHE_NAME_PREFIX_TL.get(); + } if (StrUtil.isBlank(cacheNamePrefix)) { cacheNamePrefix = config.getDefaultCachePrefix(); diff --git a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java index 4e3c94ef..9e4e86ce 100644 --- a/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/none/NoneCacheImpl.java @@ -34,15 +34,25 @@ public class NoneCacheImpl implements JbootCache { } @Override - public JbootCache setCurrentCacheNamePrefix(String cacheNamePrefix) { + public JbootCache setThreadCacheNamePrefix(String cacheNamePrefix) { return this; } @Override - public void removeCurrentCacheNamePrefix() { + public void clearThreadCacheNamePrefix() { } + @Override + public boolean addThreadCacheNamePrefixIngore(String cacheName) { + return true; + } + + @Override + public boolean removeThreadCacheNamePrefixIngore(String cacheName) { + return true; + } + @Override public JbootCacheConfig getConfig() { return config; diff --git a/src/main/java/io/jboot/utils/CacheUtil.java b/src/main/java/io/jboot/utils/CacheUtil.java index 2f2d2e81..28737c38 100644 --- a/src/main/java/io/jboot/utils/CacheUtil.java +++ b/src/main/java/io/jboot/utils/CacheUtil.java @@ -33,12 +33,12 @@ public class CacheUtil { return JbootCacheManager.me().getCache(name); } - public static JbootCache setCurrentPrefix(String cacheNamePrefix) { - return JbootCacheManager.me().getCache().setCurrentCacheNamePrefix(cacheNamePrefix); + public static JbootCache setThreadCacheNamePrefix(String cacheNamePrefix) { + return JbootCacheManager.me().getCache().setThreadCacheNamePrefix(cacheNamePrefix); } - public static void clearCurrentPrefix() { - JbootCacheManager.me().getCache().removeCurrentCacheNamePrefix(); + public static void clearThreadCacheNamePrefix() { + JbootCacheManager.me().getCache().clearThreadCacheNamePrefix(); } public static T get(String cacheName, Object key) { -- Gitee From 8b24c975a784308d7d6dd6339fa232b36531ee51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 19 Oct 2022 20:39:25 +0800 Subject: [PATCH 1829/1965] v3.16.4 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ pom.xml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index be391d7d..0d5a7191 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.16.3 + 3.16.4 ``` diff --git a/changes.txt b/changes.txt index 251bc828..c27597e4 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.16.4 2022-10-19: +优化:doNotAlloVisitRedirect 错别字 +优化:修改 CacheUtil.setCurrentPrefix() 方法为 setThreadCacheNamePrefix +新增:添加 JbootCache.addThreadCacheNamePrefixIngore() 方法 + + + jboot v3.16.3 2022-10-17: 新增:SqlBuilder.escapeOrderBySql 用于过滤 Order By 参数 新增:InterceptorBuilder.Util.isChildClassOf 方法 diff --git a/pom.xml b/pom.xml index ababe8f9..af00ff8a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.4-SNAPSHOT + 3.16.4 jar jboot -- Gitee From 29af71ca5c79b0eecdb4db9d207bae982a1e44a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 21 Oct 2022 14:03:11 +0800 Subject: [PATCH 1830/1965] v3.16.5 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index af00ff8a..25f0a10e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.4 + 3.16.5-SNAPSHOT jar jboot -- Gitee From 946b4538059a229a40204ff99629bb4e9aa85bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 21 Oct 2022 14:04:31 +0800 Subject: [PATCH 1831/1965] =?UTF-8?q?fixed:=20=E4=BF=AE=E5=A4=8D=20Control?= =?UTF-8?q?ler=20=E4=BD=BF=E7=94=A8=E8=BF=94=E5=9B=9E=E5=80=BC=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E5=B9=B6=E5=9C=A8=20Controller=20=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=20cacheable=20=E6=B3=A8=E8=A7=A3=E7=BC=93=E5=AD=98=E6=97=B6?= =?UTF-8?q?=E5=87=BA=E9=94=99=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interceptor/CachePutInterceptor.java | 2 +- .../interceptor/CacheableInterceptor.java | 45 +++++++++++++++++-- .../jboot/web/handler/JbootActionHandler.java | 3 +- .../jboot/web/render/JbootRenderFactory.java | 5 +-- .../web/render/JbootReturnValueRender.java | 6 +-- .../test/controller/CacheController.java | 10 +++++ 6 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java index 29e58c36..b47a6694 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CachePutInterceptor.java @@ -63,7 +63,7 @@ public class CachePutInterceptor implements Interceptor { inv.invoke(); - CacheableInterceptor.cacheActionContent(cacheName, cacheKey, cachePut.liveSeconds(), controller); + CacheableInterceptor.cacheActionContent(cacheName, cacheKey, cachePut.liveSeconds(), inv , method); } diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java index 3208919b..286c7b0c 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java @@ -18,8 +18,12 @@ package io.jboot.components.cache.interceptor; import com.jfinal.aop.Interceptor; import com.jfinal.aop.Invocation; +import com.jfinal.core.Action; +import com.jfinal.core.CPI; import com.jfinal.core.Controller; import com.jfinal.plugin.activerecord.Page; +import com.jfinal.render.Render; +import com.jfinal.render.RenderManager; import io.jboot.components.cache.AopCache; import io.jboot.components.cache.annotation.Cacheable; import io.jboot.db.model.JbootModel; @@ -27,6 +31,8 @@ import io.jboot.exception.JbootException; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.ClassUtil; import io.jboot.utils.ModelUtil; +import io.jboot.web.render.JbootRenderFactory; +import io.jboot.web.render.JbootReturnValueRender; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -94,7 +100,7 @@ public class CacheableInterceptor implements Interceptor { } inv.invoke(); - cacheActionContent(cacheName, cacheKey, cacheable.liveSeconds(), controller); + cacheActionContent(cacheName, cacheKey, cacheable.liveSeconds(), inv, method); } @@ -104,11 +110,20 @@ public class CacheableInterceptor implements Interceptor { * @param cacheName * @param cacheKey * @param liveSeconds - * @param controller + * @param inv + * @param method */ - public static void cacheActionContent(String cacheName, String cacheKey, int liveSeconds, Controller controller) { + public static void cacheActionContent(String cacheName, String cacheKey, int liveSeconds, Invocation inv, Method method) { - ActionCachedContent cachedContent = new ActionCachedContent(controller.getRender()); + Render render = getControllerRender(inv, method); + + if (render == null){ + return; + } + + ActionCachedContent cachedContent = new ActionCachedContent(render); + + Controller controller = inv.getController(); // 忽略的缓存配置 Set ignoreCachedAttrs = controller.getAttr(IGNORE_CACHED_ATTRS); @@ -132,6 +147,28 @@ public class CacheableInterceptor implements Interceptor { } + protected static final RenderManager renderManager = RenderManager.me(); + + private static Render getControllerRender(Invocation inv, Method method) { + Render render = inv.getController().getRender(); + if (render == null && void.class != method.getReturnType() + && renderManager.getRenderFactory() instanceof JbootRenderFactory) { + + JbootRenderFactory factory = (JbootRenderFactory) renderManager.getRenderFactory(); + JbootReturnValueRender returnValueRender = factory.getReturnValueRender(inv.getReturnValue()); + + //有可能为 null,比如 Forward 的情况 + render = returnValueRender.getRealRender(); + + }else if (render == null) { + Action action = CPI.getAction(inv.getController()); + render = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); + } + + return render; + } + + /** * 渲染缓存的 ActionCachedContent * diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 5258f43e..7bf59973 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -183,7 +183,7 @@ public class JbootActionHandler extends ActionHandler { && renderManager.getRenderFactory() instanceof JbootRenderFactory) { JbootRenderFactory factory = (JbootRenderFactory) renderManager.getRenderFactory(); - JbootReturnValueRender returnValueRender = factory.getReturnValueRender(action, invocation.getReturnValue()); + JbootReturnValueRender returnValueRender = factory.getReturnValueRender(invocation.getReturnValue()); String forwardTo = returnValueRender.getForwardTo(); if (forwardTo != null) { @@ -191,7 +191,6 @@ public class JbootActionHandler extends ActionHandler { return; } else { render = returnValueRender; - //重新设置到 Controller,JbootActionReporter 才能 Controller 获取 render 判断 render 类型 controller.render(render); } diff --git a/src/main/java/io/jboot/web/render/JbootRenderFactory.java b/src/main/java/io/jboot/web/render/JbootRenderFactory.java index 906275b4..231fe122 100644 --- a/src/main/java/io/jboot/web/render/JbootRenderFactory.java +++ b/src/main/java/io/jboot/web/render/JbootRenderFactory.java @@ -15,7 +15,6 @@ */ package io.jboot.web.render; -import com.jfinal.core.Action; import com.jfinal.render.ContentType; import com.jfinal.render.ErrorRender; import com.jfinal.render.Render; @@ -134,8 +133,8 @@ public class JbootRenderFactory extends RenderFactory { return new JbootCaptchaRender(); } - public JbootReturnValueRender getReturnValueRender(Action action, Object returnValue) { - return new JbootReturnValueRender(action, returnValue); + public JbootReturnValueRender getReturnValueRender(Object returnValue) { + return new JbootReturnValueRender(returnValue); } public ValidErrorRender getValidErrorRender(ValidException validException) { diff --git a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java index 848b4b65..f386a4f2 100644 --- a/src/main/java/io/jboot/web/render/JbootReturnValueRender.java +++ b/src/main/java/io/jboot/web/render/JbootReturnValueRender.java @@ -15,7 +15,6 @@ */ package io.jboot.web.render; -import com.jfinal.core.Action; import com.jfinal.kit.JsonKit; import com.jfinal.render.IRenderFactory; import com.jfinal.render.Render; @@ -38,15 +37,12 @@ import java.util.Date; public class JbootReturnValueRender extends Render { protected IRenderFactory factory = RenderManager.me().getRenderFactory(); - protected Action action; protected Object value; protected Render render; protected String forwardTo; - public JbootReturnValueRender(Action action, Object returnValue) { - - this.action = action; + public JbootReturnValueRender(Object returnValue) { if (returnValue == null) { this.value = null; diff --git a/src/test/java/io/jboot/test/controller/CacheController.java b/src/test/java/io/jboot/test/controller/CacheController.java index 7301e5a9..87b90498 100644 --- a/src/test/java/io/jboot/test/controller/CacheController.java +++ b/src/test/java/io/jboot/test/controller/CacheController.java @@ -30,6 +30,16 @@ public class CacheController extends JbootController { } + @Cacheable(name = "json99", liveSeconds = 3) + public Map json99() { + System.out.println("json99() invoked!!!!!!!!!"); + Map data = new HashMap<>(); + data.put("age", 1); + data.put("name", "张三"); + data.put("sex", 1); + return data; + } + @Cacheable(name = "aaa", liveSeconds = 10, unless = "para('unless')=='nocache'") public void json2() { System.out.println("json2() invoked!!!!!!!!!"); -- Gitee From 7a2256cb6715edfcb9fa76e8de320d8cf91bd531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 21 Oct 2022 14:20:43 +0800 Subject: [PATCH 1832/1965] v3.16.5 snapshot --- .../io/jboot/test/controller/CacheController.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/test/java/io/jboot/test/controller/CacheController.java b/src/test/java/io/jboot/test/controller/CacheController.java index 87b90498..085bd41c 100644 --- a/src/test/java/io/jboot/test/controller/CacheController.java +++ b/src/test/java/io/jboot/test/controller/CacheController.java @@ -1,5 +1,6 @@ package io.jboot.test.controller; +import io.jboot.aop.annotation.DefaultValue; import io.jboot.components.cache.annotation.CacheEvict; import io.jboot.components.cache.annotation.Cacheable; import io.jboot.web.controller.JbootController; @@ -40,6 +41,16 @@ public class CacheController extends JbootController { return data; } + @Cacheable(name = "json88", liveSeconds = 10) + public Map json88(@DefaultValue("3")Integer age) { + System.out.println("json99() invoked!!!!!!!!!"); + Map data = new HashMap<>(); + data.put("age", 1); + data.put("name", "张三"); + data.put("sex", 1); + return data; + } + @Cacheable(name = "aaa", liveSeconds = 10, unless = "para('unless')=='nocache'") public void json2() { System.out.println("json2() invoked!!!!!!!!!"); -- Gitee From 14ed361e854850451b19a9e03c1b894c09b92339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 23 Oct 2022 17:07:50 +0800 Subject: [PATCH 1833/1965] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E7=9A=84=E7=B1=BB=E5=9E=8B=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/utils/DateUtil.java | 18 ++++++++++++++++++ src/main/java/io/jboot/utils/ObjectUtil.java | 20 ++++++++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/utils/DateUtil.java b/src/main/java/io/jboot/utils/DateUtil.java index 3b38e7b6..d5900be6 100644 --- a/src/main/java/io/jboot/utils/DateUtil.java +++ b/src/main/java/io/jboot/utils/DateUtil.java @@ -115,6 +115,24 @@ public class DateUtil { try { return sdf.parse(dateString); } catch (ParseException ex) { + //2022-10-23 00:00:00.0 + int lastIndexOf = dateString.lastIndexOf("."); + if (lastIndexOf == 19) { + return parseDate(dateString.substring(0, lastIndexOf)); + } + + //2022-10-23 00:00:00,0 + lastIndexOf = dateString.lastIndexOf(","); + if (lastIndexOf == 19) { + return parseDate(dateString.substring(0, lastIndexOf)); + } + + //2022-10-23 00:00:00 000123 + lastIndexOf = dateString.lastIndexOf(" "); + if (lastIndexOf == 19) { + return parseDate(dateString.substring(0, lastIndexOf)); + } + if (dateString.contains(".") || dateString.contains("/")) { dateString = dateString.replace(".", "-").replace("/", "-"); return sdf.parse(dateString); diff --git a/src/main/java/io/jboot/utils/ObjectUtil.java b/src/main/java/io/jboot/utils/ObjectUtil.java index 02664ec7..2028a102 100644 --- a/src/main/java/io/jboot/utils/ObjectUtil.java +++ b/src/main/java/io/jboot/utils/ObjectUtil.java @@ -15,6 +15,7 @@ */ package io.jboot.utils; +import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -199,7 +200,18 @@ public class ObjectUtil { if (value instanceof Number) { return new Date(((Number) value).longValue()); } - + if (value instanceof Timestamp) { + return new Date(((Timestamp) value).getTime()); + } + if (value instanceof LocalDate) { + return DateUtil.toDate((LocalDate) value); + } + if (value instanceof LocalDateTime) { + return DateUtil.toDate((LocalDateTime) value); + } + if (value instanceof LocalTime) { + return DateUtil.toDate((LocalTime) value); + } String s = value.toString(); if (StrUtil.isNumeric(s)) { return new Date(Long.parseLong(s)); @@ -226,13 +238,13 @@ public class ObjectUtil { } - public static T obtainNotNull(T ... ts){ - if (ts == null || ts.length == 0){ + public static T obtainNotNull(T... ts) { + if (ts == null || ts.length == 0) { throw new IllegalArgumentException("Arguments is null or empty."); } for (T t : ts) { - if (t != null){ + if (t != null) { return t; } } -- Gitee From 5bc65ab9780839819679f8a0ad57593965c1acb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 23 Oct 2022 18:20:55 +0800 Subject: [PATCH 1834/1965] v3.16.5 release (^.^)YYa!! --- src/main/java/io/jboot/utils/DateUtil.java | 25 +++++++++++++++ src/main/java/io/jboot/utils/ObjectUtil.java | 33 +++----------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/main/java/io/jboot/utils/DateUtil.java b/src/main/java/io/jboot/utils/DateUtil.java index d5900be6..312df0f0 100644 --- a/src/main/java/io/jboot/utils/DateUtil.java +++ b/src/main/java/io/jboot/utils/DateUtil.java @@ -17,6 +17,7 @@ package io.jboot.utils; import com.jfinal.kit.SyncWriteMap; +import java.sql.Timestamp; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.*; @@ -105,6 +106,30 @@ public class DateUtil { } + public static Date parseDate(Object value) { + if (value instanceof Number) { + return new Date(((Number) value).longValue()); + } + if (value instanceof Timestamp) { + return new Date(((Timestamp) value).getTime()); + } + if (value instanceof LocalDate) { + return DateUtil.toDate((LocalDate) value); + } + if (value instanceof LocalDateTime) { + return DateUtil.toDate((LocalDateTime) value); + } + if (value instanceof LocalTime) { + return DateUtil.toDate((LocalTime) value); + } + String s = value.toString(); + if (StrUtil.isNumeric(s)) { + return new Date(Long.parseLong(s)); + } + return DateUtil.parseDate(s); + } + + public static Date parseDate(String dateString) { if (StrUtil.isBlank(dateString)) { return null; diff --git a/src/main/java/io/jboot/utils/ObjectUtil.java b/src/main/java/io/jboot/utils/ObjectUtil.java index 2028a102..0415d184 100644 --- a/src/main/java/io/jboot/utils/ObjectUtil.java +++ b/src/main/java/io/jboot/utils/ObjectUtil.java @@ -15,7 +15,6 @@ */ package io.jboot.utils; -import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -178,13 +177,13 @@ public class ObjectUtil { } else if (targetClass == byte[].class) { return value.toString().getBytes(); } else if (targetClass == Date.class) { - return parseDate(value); + return DateUtil.parseDate(value); } else if (targetClass == LocalDateTime.class) { - return DateUtil.toLocalDateTime(parseDate(value)); + return DateUtil.toLocalDateTime(DateUtil.parseDate(value)); } else if (targetClass == LocalDate.class) { - return DateUtil.toLocalDate(parseDate(value)); + return DateUtil.toLocalDate(DateUtil.parseDate(value)); } else if (targetClass == LocalTime.class) { - return DateUtil.toLocalTime(parseDate(value)); + return DateUtil.toLocalTime(DateUtil.parseDate(value)); } else if (targetClass == Short.class || targetClass == short.class) { if (value instanceof Number) { return ((Number) value).shortValue(); @@ -196,30 +195,6 @@ public class ObjectUtil { } - private static Date parseDate(Object value) { - if (value instanceof Number) { - return new Date(((Number) value).longValue()); - } - if (value instanceof Timestamp) { - return new Date(((Timestamp) value).getTime()); - } - if (value instanceof LocalDate) { - return DateUtil.toDate((LocalDate) value); - } - if (value instanceof LocalDateTime) { - return DateUtil.toDate((LocalDateTime) value); - } - if (value instanceof LocalTime) { - return DateUtil.toDate((LocalTime) value); - } - String s = value.toString(); - if (StrUtil.isNumeric(s)) { - return new Date(Long.parseLong(s)); - } - - return DateUtil.parseDate(s); - } - public static Object getPrimitiveDefaultValue(Class paraClass) { if (paraClass == int.class || paraClass == long.class || paraClass == float.class || paraClass == double.class) { return 0; -- Gitee From bd0f2aafc78ff31606ae328d51c71928a25c0589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 23 Oct 2022 18:21:52 +0800 Subject: [PATCH 1835/1965] v3.16.5 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 96de0622..dd4821c2 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.16.4 + 3.16.5 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index b0ed91e4..57980818 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.16.4 + 3.16.5 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 45726a90..b23afd73 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.16.4 + 3.16.5 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.16.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.16.5', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 82212e48..1bdf0136 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.16.4', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.16.5', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.4/jboot-3.16.4.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.5/jboot-3.16.5.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index affe8606..bda010fe 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.16.4 + 3.16.5 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index db967064..7b844d0a 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.16.4"; + public static String VERSION = "3.16.5"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 8e746ae4e272af4693451da84d74fe62fd9f81e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 23 Oct 2022 18:21:56 +0800 Subject: [PATCH 1836/1965] v3.16.5 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ pom.xml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0d5a7191..073c70dc 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.16.4 + 3.16.5 ``` diff --git a/changes.txt b/changes.txt index c27597e4..325a5246 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.16.5 2022-10-23: +修复:Controller 使用返回值渲染并使用 cacheable 注解对其缓存时出错的问题 +修复:ObjectUtil.convert() 在某些极端情况下错误的问题 + + + jboot v3.16.4 2022-10-19: 优化:doNotAlloVisitRedirect 错别字 优化:修改 CacheUtil.setCurrentPrefix() 方法为 setThreadCacheNamePrefix diff --git a/pom.xml b/pom.xml index 25f0a10e..002e250f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.5-SNAPSHOT + 3.16.5 jar jboot -- Gitee From 942275ae7daf6ead82bfcb74e87e7271c43714a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 25 Oct 2022 10:58:23 +0800 Subject: [PATCH 1837/1965] v3.16.6 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- .../jboot/components/http/JbootHttpResponse.java | 16 ++++++++-------- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index dd4821c2..0aa3164f 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.16.5 + 3.16.6 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 57980818..893cb074 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.16.5 + 3.16.6 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index b23afd73..bf1825a6 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.16.5 + 3.16.6 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.16.5', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.16.6', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 1bdf0136..9d46c82d 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.16.5', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.16.6', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.5/jboot-3.16.5.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.6/jboot-3.16.6.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index bda010fe..2fbaf77c 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.16.5 + 3.16.6 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 7b844d0a..3fc33acb 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.16.5"; + public static String VERSION = "3.16.6"; public static final String ATTR_CONTEXT_PATH = "CPATH"; diff --git a/src/main/java/io/jboot/components/http/JbootHttpResponse.java b/src/main/java/io/jboot/components/http/JbootHttpResponse.java index 46b62043..30d15438 100644 --- a/src/main/java/io/jboot/components/http/JbootHttpResponse.java +++ b/src/main/java/io/jboot/components/http/JbootHttpResponse.java @@ -37,17 +37,17 @@ public class JbootHttpResponse implements Closeable { public JbootHttpResponse(JbootHttpRequest request) { this.request = request; - if (request.getDownloadFile() != null) { - if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) { - LOG.error("Can not mkdirs for: " + file.getParentFile()); - } + File downloadToFile = request.getDownloadFile(); - if (file.exists() && !file.delete()) { - LOG.error("Can not delete file: " + file); + if (downloadToFile != null) { + if (!downloadToFile.getParentFile().exists() && !downloadToFile.getParentFile().mkdirs()) { + LOG.error("Can not mkdirs for: " + downloadToFile.getParentFile()); + } + if (downloadToFile.exists() && !downloadToFile.delete()) { + LOG.error("Can not delete file: " + downloadToFile); } - try { - this.file = request.getDownloadFile(); + this.file = downloadToFile; this.contentStream = new FileOutputStream(file); } catch (Exception e) { throw new RuntimeException(e); -- Gitee From 1aee90cb5183865f4807246d36162967221f6035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 25 Oct 2022 10:58:38 +0800 Subject: [PATCH 1838/1965] v3.16.6 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 5 +++++ pom.xml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 073c70dc..6e00b147 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.16.5 + 3.16.6 ``` diff --git a/changes.txt b/changes.txt index 325a5246..581b8d28 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,8 @@ +jboot v3.16.5 2022-10-25: +修复:HttpUtil.download 出现 NPE 的问题 + + + jboot v3.16.5 2022-10-23: 修复:Controller 使用返回值渲染并使用 cacheable 注解对其缓存时出错的问题 修复:ObjectUtil.convert() 在某些极端情况下错误的问题 diff --git a/pom.xml b/pom.xml index 002e250f..d80d1a70 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.5 + 3.16.6 jar jboot -- Gitee From f04e386418adae620da48abe50a160d30bd92acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 29 Oct 2022 13:52:16 +0800 Subject: [PATCH 1839/1965] v3.16.7 snapshot --- pom.xml | 2 +- src/main/java/io/jboot/app/JbootResourceLoader.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d80d1a70..e78461f5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.6 + 3.16.7-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/app/JbootResourceLoader.java b/src/main/java/io/jboot/app/JbootResourceLoader.java index 9f26bf89..ab43826f 100644 --- a/src/main/java/io/jboot/app/JbootResourceLoader.java +++ b/src/main/java/io/jboot/app/JbootResourceLoader.java @@ -111,6 +111,11 @@ public class JbootResourceLoader { return; } + //忽略掉 windows 下的临时文件 + if(file.endsWith("~")){ + return; + } + int indexOf = file.indexOf(path); File target = new File(classPath, resourcePathName + File.separator + file.substring(indexOf + path.length())); -- Gitee From a1e885fdd7c61d8d52ffa25252a949d5d47c12d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 29 Oct 2022 14:52:51 +0800 Subject: [PATCH 1840/1965] optimize ValidErrorRender --- .../io/jboot/components/valid/ValidErrorRender.java | 4 ++-- .../io/jboot/web/handler/JbootActionHandler.java | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/components/valid/ValidErrorRender.java b/src/main/java/io/jboot/components/valid/ValidErrorRender.java index bedcd21d..ffea6e57 100644 --- a/src/main/java/io/jboot/components/valid/ValidErrorRender.java +++ b/src/main/java/io/jboot/components/valid/ValidErrorRender.java @@ -28,8 +28,8 @@ public class ValidErrorRender extends Render { protected static final String htmlContentType = "text/html;charset=" + getEncoding(); protected static final String jsonContentType = "application/json;charset=" + getEncoding(); - protected static final String html_header = "Parameter Valid Error" + - "

        Parameter Valid Error

        " + + protected static final String html_header = "Invalid parameter" + + "

        Invalid parameter

        " + "
        "; protected static final String poweredBy = "
        Powered by Jboot
        "; diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 7bf59973..e32d7b83 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -276,20 +276,21 @@ public class JbootActionHandler extends ActionHandler { /** * 处理参数验证错误 */ - protected void handleValidException(String target, HttpServletRequest request, HttpServletResponse response, Action action, ValidException e) { + protected void handleValidException(String target, HttpServletRequest request, HttpServletResponse response, Action action, ValidException validException) { if (LOG.isErrorEnabled()) { - String qs = request.getQueryString(); - String targetInfo = qs == null ? target : target + "?" + qs; - LOG.error(e.getReason() + " : " + targetInfo, e); +// String qs = request.getQueryString(); +// String targetInfo = qs == null ? target : target + "?" + qs; +// LOG.error(validException.getReason() + " : " + targetInfo, validException); + LOG.error("Invalid parameter: " + validException.getReason()); } IRenderFactory factory = renderManager.getRenderFactory(); if (factory instanceof JbootRenderFactory) { - ValidErrorRender render = ((JbootRenderFactory) factory).getValidErrorRender(e); + ValidErrorRender render = ((JbootRenderFactory) factory).getValidErrorRender(validException); render.setContext(request, response, action.getViewPath()).render(); } else { Render render = renderManager.getRenderFactory().getErrorRender(ValidUtil.getErrorCode()); if (render instanceof JbootErrorRender) { - ((JbootErrorRender) render).setThrowable(e); + ((JbootErrorRender) render).setThrowable(validException); } render.setContext(request, response, action.getViewPath()).render(); } -- Gitee From 7c2a7094f82e3499c3e2ea452bf8738ce7a27c74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 29 Oct 2022 15:07:37 +0800 Subject: [PATCH 1841/1965] upgrade jfinal version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e78461f5..d1b2e8d9 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ - 5.0.3 + 5.0.5 2022.2 3.2 3.4 -- Gitee From f601c8c0e1a42ad7a3400320d3d3310112607a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 30 Oct 2022 14:09:28 +0800 Subject: [PATCH 1842/1965] add default ingore cacheName prefixes --- .../components/cache/support/JbootAccessTokenCache.java | 6 ++++++ .../jboot/components/cache/support/JbootCaptchaCache.java | 5 +++++ .../io/jboot/components/cache/support/JbootTokenCache.java | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/src/main/java/io/jboot/components/cache/support/JbootAccessTokenCache.java b/src/main/java/io/jboot/components/cache/support/JbootAccessTokenCache.java index 5f1f7213..c4dafc04 100644 --- a/src/main/java/io/jboot/components/cache/support/JbootAccessTokenCache.java +++ b/src/main/java/io/jboot/components/cache/support/JbootAccessTokenCache.java @@ -3,11 +3,17 @@ package io.jboot.components.cache.support; import com.jfinal.weixin.sdk.cache.IAccessTokenCache; import io.jboot.Jboot; import io.jboot.components.cache.CacheTime; +import io.jboot.components.cache.JbootCacheManager; public class JbootAccessTokenCache implements IAccessTokenCache { static final String CACHE_NAME = "wechat_access_tokens"; + public JbootAccessTokenCache() { + JbootCacheManager.me().getCache() + .addThreadCacheNamePrefixIngore(CACHE_NAME); + } + @Override public String get(String key) { return Jboot.getCache().get(CACHE_NAME, key); diff --git a/src/main/java/io/jboot/components/cache/support/JbootCaptchaCache.java b/src/main/java/io/jboot/components/cache/support/JbootCaptchaCache.java index d2e5ce76..4a614996 100644 --- a/src/main/java/io/jboot/components/cache/support/JbootCaptchaCache.java +++ b/src/main/java/io/jboot/components/cache/support/JbootCaptchaCache.java @@ -3,12 +3,17 @@ package io.jboot.components.cache.support; import com.jfinal.captcha.Captcha; import com.jfinal.captcha.ICaptchaCache; import io.jboot.Jboot; +import io.jboot.components.cache.JbootCacheManager; import io.jboot.utils.StrUtil; public class JbootCaptchaCache implements ICaptchaCache { public static final String CACHE_NAME = "jboot_captchas"; + public JbootCaptchaCache() { + JbootCacheManager.me().getCache().addThreadCacheNamePrefixIngore(CACHE_NAME); + } + @Override public void put(Captcha captcha) { Jboot.getCache().put(CACHE_NAME, captcha.getKey(), captcha, (int) ((captcha.getExpireAt() - System.currentTimeMillis()) / 1000)); diff --git a/src/main/java/io/jboot/components/cache/support/JbootTokenCache.java b/src/main/java/io/jboot/components/cache/support/JbootTokenCache.java index 72e7c1f0..510f7417 100644 --- a/src/main/java/io/jboot/components/cache/support/JbootTokenCache.java +++ b/src/main/java/io/jboot/components/cache/support/JbootTokenCache.java @@ -3,6 +3,7 @@ package io.jboot.components.cache.support; import com.jfinal.token.ITokenCache; import com.jfinal.token.Token; import io.jboot.Jboot; +import io.jboot.components.cache.JbootCacheManager; import java.util.List; @@ -10,6 +11,10 @@ public class JbootTokenCache implements ITokenCache { static final String CACHE_NAME = "jboot_tokens"; + public JbootTokenCache() { + JbootCacheManager.me().getCache().addThreadCacheNamePrefixIngore(CACHE_NAME); + } + @Override public void put(Token token) { Jboot.getCache().put(CACHE_NAME, token.getId(), token, (int) ((token.getExpirationTime() - System.currentTimeMillis()) / 1000)); -- Gitee From 85b9845c4df58840ff8a11ef0cb92cf1825e6933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 31 Oct 2022 10:33:07 +0800 Subject: [PATCH 1843/1965] add CachePrinter support --- .../jboot/components/cache/CachePrinter.java | 9 ++++++++ .../io/jboot/components/cache/JbootCache.java | 2 ++ .../components/cache/JbootCacheBase.java | 8 +++++++ .../components/cache/JbootCacheConfig.java | 9 ++++++++ .../components/cache/JbootCacheManager.java | 14 +++++++++++++ .../cache/caffeine/CaffeineCacheImpl.java | 20 +++++++++++++++--- .../cache/ehcache/JbootEhcacheImpl.java | 21 +++++++++++++++---- .../components/cache/interceptor/Utils.java | 5 +---- .../components/cache/j2cache/J2cacheImpl.java | 14 ++++++++++++- .../cache/redis/JbootRedisCacheImpl.java | 11 +++++++++- 10 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 src/main/java/io/jboot/components/cache/CachePrinter.java diff --git a/src/main/java/io/jboot/components/cache/CachePrinter.java b/src/main/java/io/jboot/components/cache/CachePrinter.java new file mode 100644 index 00000000..a3ca2fcb --- /dev/null +++ b/src/main/java/io/jboot/components/cache/CachePrinter.java @@ -0,0 +1,9 @@ +package io.jboot.components.cache; + +public interface CachePrinter { + + default void println(String debugInfo){ + System.out.println(debugInfo); + } + +} diff --git a/src/main/java/io/jboot/components/cache/JbootCache.java b/src/main/java/io/jboot/components/cache/JbootCache.java index 8ddd65cf..05492669 100644 --- a/src/main/java/io/jboot/components/cache/JbootCache.java +++ b/src/main/java/io/jboot/components/cache/JbootCache.java @@ -22,6 +22,8 @@ import java.util.List; public interface JbootCache extends com.jfinal.plugin.activerecord.cache.ICache { + + //设置当前 线程 的缓存前缀 JbootCache setThreadCacheNamePrefix(String cacheNamePrefix); diff --git a/src/main/java/io/jboot/components/cache/JbootCacheBase.java b/src/main/java/io/jboot/components/cache/JbootCacheBase.java index ffe6c0ec..7017b4cb 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheBase.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheBase.java @@ -63,6 +63,7 @@ public abstract class JbootCacheBase implements JbootCache { return ignoreThreadCacheNames.remove(cacheName); } + /** * 构建缓存名称 * @@ -94,4 +95,11 @@ public abstract class JbootCacheBase implements JbootCache { public void refresh(String cacheName) { } + + + protected void println(String debugInfo){ + if (config.isDevMode()) { + JbootCacheManager.me().getPrinter().println(debugInfo); + } + } } diff --git a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java index cc729e92..460e7ca5 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java @@ -17,6 +17,7 @@ package io.jboot.components.cache; import com.google.common.collect.Sets; +import io.jboot.Jboot; import io.jboot.app.config.annotation.ConfigModel; import java.util.Set; @@ -41,6 +42,7 @@ public class JbootCacheConfig { private String typeName; private String defaultCachePrefix; + private Boolean devMode; public String getName() { @@ -76,4 +78,11 @@ public class JbootCacheConfig { this.defaultCachePrefix = defaultCachePrefix; } + public boolean isDevMode() { + return devMode == null ? Jboot.isDevMode() : devMode; + } + + public void setDevMode(Boolean devMode) { + this.devMode = devMode; + } } diff --git a/src/main/java/io/jboot/components/cache/JbootCacheManager.java b/src/main/java/io/jboot/components/cache/JbootCacheManager.java index 866cabda..765ae847 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheManager.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheManager.java @@ -40,6 +40,12 @@ public class JbootCacheManager { } private Map cacheMap = new ConcurrentHashMap<>(); + private CachePrinter printer = new CachePrinter() { + @Override + public void println(String debugInfo) { + CachePrinter.super.println(debugInfo); + } + }; public static JbootCacheManager me() { return me; @@ -100,4 +106,12 @@ public class JbootCacheManager { return JbootSpiLoader.load(JbootCache.class, config.getType(), config); } } + + public CachePrinter getPrinter() { + return printer; + } + + public void setPrinter(CachePrinter printer) { + this.printer = printer; + } } diff --git a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java index 703b2c4a..394acd6b 100644 --- a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java @@ -71,17 +71,22 @@ public class CaffeineCacheImpl extends JbootCacheBase { cache.invalidate(key); return null; } - return (T) data.getValue(); + + T cacheData = (T) data.getValue(); + println("CaffeineCache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + cacheData); + return cacheData; } @Override public void put(String cacheName, Object key, Object value) { putData(getCache(cacheName), key, new CaffeineCacheObject(value)); + println("CaffeineCache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); } @Override public void put(String cacheName, Object key, Object value, int liveSeconds) { putData(getCache(cacheName), key, new CaffeineCacheObject(value, liveSeconds)); + println("CaffeineCache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); } @@ -92,6 +97,7 @@ public class CaffeineCacheImpl extends JbootCacheBase { if (cache != null) { cache.invalidate(key); } + println("CaffeineCache REMOVE: cacheName[" +cacheName+ "] CacheKey["+key+"]"); } @Override @@ -101,6 +107,7 @@ public class CaffeineCacheImpl extends JbootCacheBase { cache.invalidateAll(); } cacheMap.remove(cacheName); + println("CaffeineCache REMOVEALL: cacheName[" +cacheName+ "]"); } @Override @@ -112,10 +119,13 @@ public class CaffeineCacheImpl extends JbootCacheBase { if (newValue != null) { data = new CaffeineCacheObject(newValue); putData(cache, key, data); + println("CaffeineCache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + newValue); } return (T) newValue; } else { - return (T) data.getValue(); + Object cacheData = data.getValue(); + println("CaffeineCache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + cacheData); + return (T) cacheData; } } @@ -128,10 +138,13 @@ public class CaffeineCacheImpl extends JbootCacheBase { if (newValue != null) { data = new CaffeineCacheObject(newValue, liveSeconds); putData(cache, key, data); + println("CaffeineCache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + newValue); } return (T) newValue; } else { - return (T) data.getValue(); + Object cacheData = data.getValue(); + println("CaffeineCache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + cacheData); + return (T) cacheData; } } @@ -164,6 +177,7 @@ public class CaffeineCacheImpl extends JbootCacheBase { data.setLiveSeconds(seconds); putData(cache, key, data); + println("CaffeineCache SETTTL: cacheName[" +cacheName+ "] CacheKey["+key+"] seconds:" + seconds); } diff --git a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java index 6c405de0..c9991909 100644 --- a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java @@ -47,8 +47,8 @@ public class JbootEhcacheImpl extends JbootCacheBase { cacheManager = CacheManager.create(); } else { String configPath = ehconfig.getConfigFileName(); - if (!configPath.startsWith("/")){ - configPath = PathKit.getRootClassPath()+"/"+configPath; + if (!configPath.startsWith("/")) { + configPath = PathKit.getRootClassPath() + "/" + configPath; } cacheManager = CacheManager.create(configPath); } @@ -85,12 +85,20 @@ public class JbootEhcacheImpl extends JbootCacheBase { @Override public T get(String cacheName, Object key) { Element element = getOrAddCache(cacheName).get(key); - return element != null ? (T) element.getObjectValue() : null; + if (element == null) { + return null; + } + + Object objectValue = element.getObjectValue(); + println("Ehcache GET: cacheName[" + cacheName + "] CacheKey[" + key + "] value:" + objectValue); + return (T) objectValue; + } @Override public void put(String cacheName, Object key, Object value) { getOrAddCache(cacheName).put(new Element(key, value)); + println("Ehcache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); } @Override @@ -102,17 +110,20 @@ public class JbootEhcacheImpl extends JbootCacheBase { Element element = new Element(key, value); element.setTimeToLive(liveSeconds); getOrAddCache(cacheName).put(element); + println("Ehcache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); } @Override public void remove(String cacheName, Object key) { getOrAddCache(cacheName).remove(key); + println("Ehcache REMOVE: cacheName[" +cacheName+ "] CacheKey["+key+"]"); } @Override public void removeAll(String cacheName) { getOrAddCache(cacheName).removeAll(); cacheManager.removeCache(cacheName); + println("Ehcache REMOVEALL: cacheName[" +cacheName+ "]"); } @Override @@ -122,6 +133,7 @@ public class JbootEhcacheImpl extends JbootCacheBase { data = dataLoader.load(); put(cacheName, key, data); } + println("Ehcache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + data); return (T) data; } @@ -135,6 +147,7 @@ public class JbootEhcacheImpl extends JbootCacheBase { data = dataLoader.load(); put(cacheName, key, data, liveSeconds); } + println("Ehcache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + data); return (T) data; } @@ -154,6 +167,7 @@ public class JbootEhcacheImpl extends JbootCacheBase { element.setTimeToLive(seconds); getOrAddCache(cacheName).put(element); + println("Ehcache SETTTL: cacheName[" +cacheName+ "] CacheKey["+key+"] seconds:" + seconds); } @Override @@ -167,7 +181,6 @@ public class JbootEhcacheImpl extends JbootCacheBase { } - public CacheManager getCacheManager() { return cacheManager; } diff --git a/src/main/java/io/jboot/components/cache/interceptor/Utils.java b/src/main/java/io/jboot/components/cache/interceptor/Utils.java index c17cef63..76f2d46a 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/Utils.java +++ b/src/main/java/io/jboot/components/cache/interceptor/Utils.java @@ -158,7 +158,7 @@ class Utils { } if (!isSupportClass(object.getClass())) { - String msg = "Unsupported empty key for annotation @Cacheable, @CacheEvict or @CachePut " + + String msg = "Unsupport empty key for annotation @Cacheable, @CacheEvict or @CachePut " + "at method[" + ClassUtil.buildMethodString(method) + "], " + "Please config key properties in the annotation."; throw new IllegalArgumentException(msg); @@ -229,10 +229,7 @@ class Utils { return false; } -// if (!unlessString.contains("#")) { unlessString = "#(" + unlessString + ")"; -// } - return "true".equals(engineRender(unlessString, method, arguments)); } diff --git a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java index 7e13a9ed..90e6314c 100644 --- a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java +++ b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java @@ -46,19 +46,27 @@ public class J2cacheImpl extends JbootCacheBase { public T get(String cacheName, Object key) { cacheName = buildCacheName(cacheName); CacheObject cacheObject = J2Cache.getChannel().get(cacheName, key.toString(), false); - return cacheObject != null ? (T) cacheObject.getValue() : null; + if (cacheObject != null) { + Object value = cacheObject.getValue(); + println("J2cache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); + return (T) value; + } else { + return null; + } } @Override public void put(String cacheName, Object key, Object value) { cacheName = buildCacheName(cacheName); J2Cache.getChannel().set(cacheName, key.toString(), value); + println("J2cache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); } @Override public void put(String cacheName, Object key, Object value, int liveSeconds) { cacheName = buildCacheName(cacheName); J2Cache.getChannel().set(cacheName, key.toString(), value, liveSeconds); + println("J2cache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); } @@ -66,12 +74,14 @@ public class J2cacheImpl extends JbootCacheBase { public void remove(String cacheName, Object key) { cacheName = buildCacheName(cacheName); J2Cache.getChannel().evict(cacheName, key.toString()); + println("J2cache REMOVE: cacheName[" +cacheName+ "] CacheKey["+key+"]"); } @Override public void removeAll(String cacheName) { cacheName = buildCacheName(cacheName); J2Cache.getChannel().clear(cacheName); + println("J2cache REMOVEALL: cacheName[" +cacheName+ "]"); } @Override @@ -83,6 +93,7 @@ public class J2cacheImpl extends JbootCacheBase { put(cacheName, key, value); } } + println("J2cache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); return (T) value; } @@ -97,6 +108,7 @@ public class J2cacheImpl extends JbootCacheBase { data = dataLoader.load(); put(cacheName, key, data, liveSeconds); } + println("J2cache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + data); return (T) data; } diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java index 614a14c4..532ff986 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java @@ -62,7 +62,9 @@ public class JbootRedisCacheImpl extends JbootCacheBase { @Override public T get(String cacheName, Object key) { - return redis.get(buildKey(cacheName, key)); + T value = redis.get(buildKey(cacheName, key)); + println("RedisCache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); + return value; } @Override @@ -73,6 +75,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { } redis.set(buildKey(cacheName, key), value); redis.sadd(buildCacheName(redisCacheNamesKey), cacheName); + println("RedisCache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); } @Override @@ -88,12 +91,14 @@ public class JbootRedisCacheImpl extends JbootCacheBase { redis.setex(buildKey(cacheName, key), liveSeconds, value); redis.sadd(buildCacheName(redisCacheNamesKey), cacheName); + println("RedisCache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); } @Override public void remove(String cacheName, Object key) { redis.del(buildKey(cacheName, key)); + println("RedisCache REMOVE: cacheName[" +cacheName+ "] CacheKey["+key+"]"); } @@ -118,6 +123,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { } while (continueState); redis.srem(buildCacheName(redisCacheNamesKey), cacheName); + println("RedisCache REMOVEALL: cacheName[" +cacheName+ "]"); } @@ -128,6 +134,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { data = dataLoader.load(); put(cacheName, key, data); } + println("RedisCache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + data); return (T) data; } @@ -161,6 +168,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { data = dataLoader.load(); put(cacheName, key, data, liveSeconds); } + println("RedisCache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + data); return (T) data; } @@ -175,6 +183,7 @@ public class JbootRedisCacheImpl extends JbootCacheBase { @Override public void setTtl(String cacheName, Object key, int seconds) { redis.expire(buildKey(cacheName, key), seconds); + println("RedisCache SETTTL: cacheName[" +cacheName+ "] CacheKey["+key+"] seconds:" + seconds); } -- Gitee From d504faabed3a2556cacbf71cca92433b4e7f2417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 31 Oct 2022 11:18:25 +0800 Subject: [PATCH 1844/1965] add CachePrinter support --- .../jboot/components/cache/CachePrinter.java | 2 +- .../components/cache/JbootCacheBase.java | 8 ++-- .../cache/caffeine/CaffeineCacheImpl.java | 43 ++++++++++++++----- .../cache/ehcache/JbootEhcacheImpl.java | 40 +++++++++++++---- .../components/cache/j2cache/J2cacheImpl.java | 35 ++++++++++++--- .../cache/redis/JbootRedisCacheImpl.java | 41 ++++++++++++++---- 6 files changed, 129 insertions(+), 40 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/CachePrinter.java b/src/main/java/io/jboot/components/cache/CachePrinter.java index a3ca2fcb..aa277fc3 100644 --- a/src/main/java/io/jboot/components/cache/CachePrinter.java +++ b/src/main/java/io/jboot/components/cache/CachePrinter.java @@ -3,7 +3,7 @@ package io.jboot.components.cache; public interface CachePrinter { default void println(String debugInfo){ - System.out.println(debugInfo); + System.out.println(debugInfo+"\n"); } } diff --git a/src/main/java/io/jboot/components/cache/JbootCacheBase.java b/src/main/java/io/jboot/components/cache/JbootCacheBase.java index 7017b4cb..cf07b540 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheBase.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheBase.java @@ -74,7 +74,7 @@ public abstract class JbootCacheBase implements JbootCache { String cacheNamePrefix = null; - if (!ignoreThreadCacheNames.contains(cacheName)){ + if (!ignoreThreadCacheNames.contains(cacheName)) { cacheNamePrefix = CACHE_NAME_PREFIX_TL.get(); } @@ -97,9 +97,7 @@ public abstract class JbootCacheBase implements JbootCache { } - protected void println(String debugInfo){ - if (config.isDevMode()) { - JbootCacheManager.me().getPrinter().println(debugInfo); - } + protected void println(String debugInfo) { + JbootCacheManager.me().getPrinter().println(debugInfo); } } diff --git a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java index 394acd6b..351eb84b 100644 --- a/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caffeine/CaffeineCacheImpl.java @@ -73,20 +73,26 @@ public class CaffeineCacheImpl extends JbootCacheBase { } T cacheData = (T) data.getValue(); - println("CaffeineCache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + cacheData); + if (config.isDevMode()) { + println("CaffeineCache GET: cacheName[" + buildCacheName(cacheName) + "] cacheKey[" + key + "] value:" + cacheData); + } return cacheData; } @Override public void put(String cacheName, Object key, Object value) { putData(getCache(cacheName), key, new CaffeineCacheObject(value)); - println("CaffeineCache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); + if (config.isDevMode()) { + println("CaffeineCache PUT: cacheName[" + buildCacheName(cacheName) + "] cacheKey[" + key + "] value:" + value); + } } @Override public void put(String cacheName, Object key, Object value, int liveSeconds) { putData(getCache(cacheName), key, new CaffeineCacheObject(value, liveSeconds)); - println("CaffeineCache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); + if (config.isDevMode()) { + println("CaffeineCache PUT: cacheName[" + buildCacheName(cacheName) + "] cacheKey[" + key + "] value:" + value); + } } @@ -97,7 +103,9 @@ public class CaffeineCacheImpl extends JbootCacheBase { if (cache != null) { cache.invalidate(key); } - println("CaffeineCache REMOVE: cacheName[" +cacheName+ "] CacheKey["+key+"]"); + if (config.isDevMode()) { + println("CaffeineCache REMOVE: cacheName[" + buildCacheName(cacheName) + "] cacheKey[" + key + "]"); + } } @Override @@ -106,8 +114,10 @@ public class CaffeineCacheImpl extends JbootCacheBase { if (cache != null) { cache.invalidateAll(); } - cacheMap.remove(cacheName); - println("CaffeineCache REMOVEALL: cacheName[" +cacheName+ "]"); + cacheMap.remove(buildCacheName(cacheName)); + if (config.isDevMode()) { + println("CaffeineCache REMOVEALL: cacheName[" + buildCacheName(cacheName) + "]"); + } } @Override @@ -119,12 +129,16 @@ public class CaffeineCacheImpl extends JbootCacheBase { if (newValue != null) { data = new CaffeineCacheObject(newValue); putData(cache, key, data); - println("CaffeineCache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + newValue); + if (config.isDevMode()) { + println("CaffeineCache PUT: cacheName[" + buildCacheName(cacheName) + "] cacheKey[" + key + "] value:" + newValue); + } } return (T) newValue; } else { Object cacheData = data.getValue(); - println("CaffeineCache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + cacheData); + if (config.isDevMode()) { + println("CaffeineCache GET: cacheName[" + buildCacheName(cacheName) + "] cacheKey[" + key + "] value:" + cacheData); + } return (T) cacheData; } } @@ -138,12 +152,16 @@ public class CaffeineCacheImpl extends JbootCacheBase { if (newValue != null) { data = new CaffeineCacheObject(newValue, liveSeconds); putData(cache, key, data); - println("CaffeineCache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + newValue); + if (config.isDevMode()) { + println("CaffeineCache PUT: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + newValue); + } } return (T) newValue; } else { Object cacheData = data.getValue(); - println("CaffeineCache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + cacheData); + if (config.isDevMode()) { + println("CaffeineCache GET: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + cacheData); + } return (T) cacheData; } } @@ -177,7 +195,10 @@ public class CaffeineCacheImpl extends JbootCacheBase { data.setLiveSeconds(seconds); putData(cache, key, data); - println("CaffeineCache SETTTL: cacheName[" +cacheName+ "] CacheKey["+key+"] seconds:" + seconds); + + if (config.isDevMode()) { + println("CaffeineCache SETTTL: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] seconds:" + seconds); + } } diff --git a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java index c9991909..b52f6143 100644 --- a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java @@ -90,7 +90,9 @@ public class JbootEhcacheImpl extends JbootCacheBase { } Object objectValue = element.getObjectValue(); - println("Ehcache GET: cacheName[" + cacheName + "] CacheKey[" + key + "] value:" + objectValue); + if (config.isDevMode()) { + println("Ehcache GET: cacheName[" + buildCacheName(cacheName) + "] cacheKey[" + key + "] value:" + objectValue); + } return (T) objectValue; } @@ -98,7 +100,10 @@ public class JbootEhcacheImpl extends JbootCacheBase { @Override public void put(String cacheName, Object key, Object value) { getOrAddCache(cacheName).put(new Element(key, value)); - println("Ehcache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); + + if (config.isDevMode()) { + println("Ehcache PUT: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + value); + } } @Override @@ -110,20 +115,29 @@ public class JbootEhcacheImpl extends JbootCacheBase { Element element = new Element(key, value); element.setTimeToLive(liveSeconds); getOrAddCache(cacheName).put(element); - println("Ehcache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); + + if (config.isDevMode()) { + println("Ehcache PUT: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + value); + } } @Override public void remove(String cacheName, Object key) { getOrAddCache(cacheName).remove(key); - println("Ehcache REMOVE: cacheName[" +cacheName+ "] CacheKey["+key+"]"); + + if (config.isDevMode()) { + println("Ehcache REMOVE: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"]"); + } } @Override public void removeAll(String cacheName) { getOrAddCache(cacheName).removeAll(); cacheManager.removeCache(cacheName); - println("Ehcache REMOVEALL: cacheName[" +cacheName+ "]"); + + if (config.isDevMode()) { + println("Ehcache REMOVEALL: cacheName[" +buildCacheName(cacheName)+ "]"); + } } @Override @@ -133,7 +147,10 @@ public class JbootEhcacheImpl extends JbootCacheBase { data = dataLoader.load(); put(cacheName, key, data); } - println("Ehcache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + data); + + if (config.isDevMode()) { + println("Ehcache GET: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + data); + } return (T) data; } @@ -147,7 +164,11 @@ public class JbootEhcacheImpl extends JbootCacheBase { data = dataLoader.load(); put(cacheName, key, data, liveSeconds); } - println("Ehcache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + data); + + if (config.isDevMode()) { + println("Ehcache GET: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + data); + } + return (T) data; } @@ -167,7 +188,10 @@ public class JbootEhcacheImpl extends JbootCacheBase { element.setTimeToLive(seconds); getOrAddCache(cacheName).put(element); - println("Ehcache SETTTL: cacheName[" +cacheName+ "] CacheKey["+key+"] seconds:" + seconds); + + if (config.isDevMode()) { + println("Ehcache SETTTL: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] seconds:" + seconds); + } } @Override diff --git a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java index 90e6314c..6fe5c988 100644 --- a/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java +++ b/src/main/java/io/jboot/components/cache/j2cache/J2cacheImpl.java @@ -48,7 +48,9 @@ public class J2cacheImpl extends JbootCacheBase { CacheObject cacheObject = J2Cache.getChannel().get(cacheName, key.toString(), false); if (cacheObject != null) { Object value = cacheObject.getValue(); - println("J2cache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); + if (config.isDevMode()) { + println("J2cache GET: cacheName[" +cacheName+ "] cacheKey["+key+"] value:" + value); + } return (T) value; } else { return null; @@ -59,14 +61,20 @@ public class J2cacheImpl extends JbootCacheBase { public void put(String cacheName, Object key, Object value) { cacheName = buildCacheName(cacheName); J2Cache.getChannel().set(cacheName, key.toString(), value); - println("J2cache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); + + if (config.isDevMode()) { + println("J2cache PUT: cacheName[" +cacheName+ "] cacheKey["+key+"] value:" + value); + } } @Override public void put(String cacheName, Object key, Object value, int liveSeconds) { cacheName = buildCacheName(cacheName); J2Cache.getChannel().set(cacheName, key.toString(), value, liveSeconds); - println("J2cache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); + + if (config.isDevMode()) { + println("J2cache PUT: cacheName[" +cacheName+ "] cacheKey["+key+"] value:" + value); + } } @@ -74,14 +82,20 @@ public class J2cacheImpl extends JbootCacheBase { public void remove(String cacheName, Object key) { cacheName = buildCacheName(cacheName); J2Cache.getChannel().evict(cacheName, key.toString()); - println("J2cache REMOVE: cacheName[" +cacheName+ "] CacheKey["+key+"]"); + + if (config.isDevMode()) { + println("J2cache REMOVE: cacheName[" +cacheName+ "] cacheKey["+key+"]"); + } } @Override public void removeAll(String cacheName) { cacheName = buildCacheName(cacheName); J2Cache.getChannel().clear(cacheName); - println("J2cache REMOVEALL: cacheName[" +cacheName+ "]"); + + if (config.isDevMode()) { + println("J2cache REMOVEALL: cacheName[" +cacheName+ "]"); + } } @Override @@ -93,7 +107,11 @@ public class J2cacheImpl extends JbootCacheBase { put(cacheName, key, value); } } - println("J2cache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); + + if (config.isDevMode()) { + println("J2cache GET: cacheName[" + buildCacheName(cacheName) + "] cacheKey[" + key + "] value:" + value); + } + return (T) value; } @@ -108,7 +126,10 @@ public class J2cacheImpl extends JbootCacheBase { data = dataLoader.load(); put(cacheName, key, data, liveSeconds); } - println("J2cache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + data); + + if (config.isDevMode()) { + println("J2cache GET: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + data); + } return (T) data; } diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java index 532ff986..2bd039d7 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java @@ -63,7 +63,10 @@ public class JbootRedisCacheImpl extends JbootCacheBase { @Override public T get(String cacheName, Object key) { T value = redis.get(buildKey(cacheName, key)); - println("RedisCache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); + + if (config.isDevMode()) { + println("RedisCache GET: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + value); + } return value; } @@ -75,7 +78,10 @@ public class JbootRedisCacheImpl extends JbootCacheBase { } redis.set(buildKey(cacheName, key), value); redis.sadd(buildCacheName(redisCacheNamesKey), cacheName); - println("RedisCache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); + + if (config.isDevMode()) { + println("RedisCache PUT: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + value); + } } @Override @@ -91,14 +97,20 @@ public class JbootRedisCacheImpl extends JbootCacheBase { redis.setex(buildKey(cacheName, key), liveSeconds, value); redis.sadd(buildCacheName(redisCacheNamesKey), cacheName); - println("RedisCache PUT: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + value); + + if (config.isDevMode()) { + println("RedisCache PUT: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + value); + } } @Override public void remove(String cacheName, Object key) { redis.del(buildKey(cacheName, key)); - println("RedisCache REMOVE: cacheName[" +cacheName+ "] CacheKey["+key+"]"); + + if (config.isDevMode()) { + println("RedisCache REMOVE: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"]"); + } } @@ -123,7 +135,10 @@ public class JbootRedisCacheImpl extends JbootCacheBase { } while (continueState); redis.srem(buildCacheName(redisCacheNamesKey), cacheName); - println("RedisCache REMOVEALL: cacheName[" +cacheName+ "]"); + + if (config.isDevMode()) { + println("RedisCache REMOVEALL: cacheName[" +buildCacheName(cacheName)+ "]"); + } } @@ -134,7 +149,11 @@ public class JbootRedisCacheImpl extends JbootCacheBase { data = dataLoader.load(); put(cacheName, key, data); } - println("RedisCache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + data); + + if (config.isDevMode()) { + println("RedisCache GET: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + data); + } + return (T) data; } @@ -168,7 +187,10 @@ public class JbootRedisCacheImpl extends JbootCacheBase { data = dataLoader.load(); put(cacheName, key, data, liveSeconds); } - println("RedisCache GET: cacheName[" +cacheName+ "] CacheKey["+key+"] value:" + data); + + if (config.isDevMode()) { + println("RedisCache GET: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] value:" + data); + } return (T) data; } @@ -183,7 +205,10 @@ public class JbootRedisCacheImpl extends JbootCacheBase { @Override public void setTtl(String cacheName, Object key, int seconds) { redis.expire(buildKey(cacheName, key), seconds); - println("RedisCache SETTTL: cacheName[" +cacheName+ "] CacheKey["+key+"] seconds:" + seconds); + + if (config.isDevMode()) { + println("RedisCache SETTTL: cacheName[" +buildCacheName(cacheName)+ "] cacheKey["+key+"] seconds:" + seconds); + } } -- Gitee From 1489c6712684ae258dd2ee5e0dbffab18e6e6f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 31 Oct 2022 11:29:36 +0800 Subject: [PATCH 1845/1965] v3.16.7 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 0aa3164f..aa41cbaf 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.16.6 + 3.16.7 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 893cb074..69e3994a 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.16.6 + 3.16.7 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index bf1825a6..588220b2 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.16.6 + 3.16.7 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.16.6', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.16.7', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 9d46c82d..898f1749 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.16.6', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.16.7', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.6/jboot-3.16.6.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.7/jboot-3.16.7.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 2fbaf77c..25959d49 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.16.6 + 3.16.7 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 3fc33acb..c4d55698 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.16.6"; + public static String VERSION = "3.16.7"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From bffe38e2115e5cf8999125dc122ce18461a8af88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 31 Oct 2022 11:29:41 +0800 Subject: [PATCH 1846/1965] v3.16.7 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 10 +++++++++- pom.xml | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6e00b147..23091904 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.16.6 + 3.16.7 ``` diff --git a/changes.txt b/changes.txt index 581b8d28..ee352d4d 100644 --- a/changes.txt +++ b/changes.txt @@ -1,4 +1,12 @@ -jboot v3.16.5 2022-10-25: +jboot v3.16.7 2022-10-31: +新增:添加 CachePrinter 的支持,方便把缓存信息输出在控制台或者日志上 +优化:JbootResourceLoader 优化过滤掉 Windows 的临时文件 +优化:使用注解验证数据时,控制台不再抛出异常信息,只打印基本验证错误信息 +优化:为验证码等组件缓存过滤掉线程前缀的设置 + + + +jboot v3.16.6 2022-10-25: 修复:HttpUtil.download 出现 NPE 的问题 diff --git a/pom.xml b/pom.xml index d1b2e8d9..31274d59 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.7-SNAPSHOT + 3.16.7 jar jboot -- Gitee From 3d0ccda36a5094679e93476420c911e6496ecd34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 2 Nov 2022 11:44:41 +0800 Subject: [PATCH 1847/1965] v3.16.8 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 31274d59..696a74b4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.7 + 3.16.8-SNAPSHOT jar jboot -- Gitee From c825ab7a2b13ef52409e5aa0fdf247b26bb84ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 2 Nov 2022 11:44:53 +0800 Subject: [PATCH 1848/1965] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=85=B3=E9=97=AD?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E8=B0=83=E8=AF=95=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/components/cache/JbootCacheConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java index 460e7ca5..1bcf28bd 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java @@ -42,7 +42,7 @@ public class JbootCacheConfig { private String typeName; private String defaultCachePrefix; - private Boolean devMode; + private Boolean devMode = false; public String getName() { -- Gitee From 3136b4a49a0ac1d9ac0881d3d33c720d1ea02553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 2 Nov 2022 11:48:46 +0800 Subject: [PATCH 1849/1965] add getFilesOnly(Set paraNames) --- .../jboot/web/controller/JbootController.java | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 74a66fa7..2cc8e61f 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -841,24 +841,37 @@ public class JbootController extends Controller { /** * 只返回特定的名称的文件,其他文件则删除 * - * @param names + * @param paraNames * @return */ @NotAction - public Map getFilesOnly(String... names) { - if (names == null || names.length == 0) { + public Map getFilesOnly(String... paraNames) { + if (paraNames == null || paraNames.length == 0) { throw new IllegalArgumentException("names can not be null or empty."); } + return getFilesOnly(Sets.newHashSet(paraNames)); + } - List uploadFiles = getFiles(); - if (uploadFiles == null || uploadFiles.isEmpty()) { + + /** + * 只返回特定的名称的文件,其他文件则删除 + * @param paraNames + * @return + */ + @NotAction + public Map getFilesOnly(Set paraNames) { + if (paraNames == null || paraNames.size() == 0) { + throw new IllegalArgumentException("names can not be null or empty."); + } + + List allUploadFiles = getFiles(); + if (allUploadFiles == null || allUploadFiles.isEmpty()) { return null; } - Set keys = Sets.newHashSet(names); Map filesMap = new HashMap<>(); - for (UploadFile uploadFile : uploadFiles) { + for (UploadFile uploadFile : allUploadFiles) { String parameterName = uploadFile.getParameterName(); if (StrUtil.isBlank(parameterName)) { FileUtil.delete(uploadFile); @@ -867,7 +880,7 @@ public class JbootController extends Controller { parameterName = parameterName.trim(); - if (keys.contains(parameterName) && !filesMap.containsKey(parameterName)) { + if (paraNames.contains(parameterName) && !filesMap.containsKey(parameterName)) { filesMap.put(parameterName, uploadFile); } else { FileUtil.delete(uploadFile); -- Gitee From 9e7e7f5c32f348b12dd8818551b26b8a469f81d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 2 Nov 2022 11:52:23 +0800 Subject: [PATCH 1850/1965] upgrade jfinal and undertow --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 696a74b4..49f87a67 100644 --- a/pom.xml +++ b/pom.xml @@ -41,15 +41,15 @@ - 5.0.5 + 5.0.6 2022.2 - 3.2 + 3.3 3.4 3.3.0 4.0.3 2.13.4 5.1.49 - 2.2.19.Final + 2.3.0.Final 1.7.36 2.57 -- Gitee From 4936c201f4e174e0608de2175771b47382856c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 3 Nov 2022 09:58:21 +0800 Subject: [PATCH 1851/1965] optimize... --- pom.xml | 2 +- .../jboot/support/redis/JbootRedisLock.java | 31 ++++++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 49f87a67..dcf352da 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ 4.0.3 2.13.4 5.1.49 - 2.3.0.Final + 2.2.19.Final 1.7.36 2.57 diff --git a/src/main/java/io/jboot/support/redis/JbootRedisLock.java b/src/main/java/io/jboot/support/redis/JbootRedisLock.java index 0641aa0a..9387206b 100644 --- a/src/main/java/io/jboot/support/redis/JbootRedisLock.java +++ b/src/main/java/io/jboot/support/redis/JbootRedisLock.java @@ -46,10 +46,13 @@ import io.jboot.utils.QuietlyUtil; */ public class JbootRedisLock { - long expireMsecs = 1000 * 60;// 60 秒 expireMsecs 锁持有超时,防止线程在入锁以后,无限的执行下去,让锁无法释放 - long timeoutMsecs = 0;// 锁等待超时 + // 60 秒 expireMsecs 锁持有超时,防止线程在入锁以后,无限的执行下去,让锁无法释放 + long expireMsecs = 1000 * 60; - private String lockName; + // 锁等待超时 + long timeoutMsecs = 0; + + private final String lockName; private boolean locked = false; private JbootRedis redis; @@ -60,7 +63,7 @@ public class JbootRedisLock { */ public JbootRedisLock(String lockName) { if (lockName == null) { - throw new NullPointerException("lockName must not null !"); + throw new NullPointerException("lockName must not be null."); } this.lockName = lockName; this.redis = Jboot.getRedis(); @@ -74,13 +77,31 @@ public class JbootRedisLock { */ public JbootRedisLock(String lockName, long timeoutMsecs) { if (lockName == null) { - throw new NullPointerException("lockName must not null !"); + throw new NullPointerException("lockName must not be null."); + } + this.lockName = lockName; + this.timeoutMsecs = timeoutMsecs; + this.redis = Jboot.getRedis(); + } + + + /** + * @param lockName 锁名称 + * @param timeoutMsecs 获取锁的时候,等待时长 + * @param expireMsecs 超时时长 + */ + public JbootRedisLock(String lockName, long timeoutMsecs, long expireMsecs) { + if (lockName == null) { + throw new NullPointerException("lockName must not be null."); } this.lockName = lockName; this.timeoutMsecs = timeoutMsecs; + this.expireMsecs = expireMsecs; + this.redis = Jboot.getRedis(); } + public long getTimeoutMsecs() { return timeoutMsecs; } -- Gitee From 88d54f33a6110376db97003babc833730db8aa9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 6 Nov 2022 09:29:03 +0800 Subject: [PATCH 1852/1965] v3.16.8 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index aa41cbaf..71c66458 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.16.7 + 3.16.8 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 69e3994a..6b570e76 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.16.7 + 3.16.8 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 588220b2..aeceec10 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.16.7 + 3.16.8 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.16.7', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.16.8', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 898f1749..c39353a7 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.16.7', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.16.8', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.7/jboot-3.16.7.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.8/jboot-3.16.8.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 25959d49..4fb6c773 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.16.7 + 3.16.8 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index c4d55698..dae26005 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.16.7"; + public static String VERSION = "3.16.8"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From dc0a9f3c0b0ee8c4909fdcb0f73f2ff95dd56420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 6 Nov 2022 09:29:09 +0800 Subject: [PATCH 1853/1965] v3.16.8 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ pom.xml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 23091904..32b39a65 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.16.7 + 3.16.8 ``` diff --git a/changes.txt b/changes.txt index ee352d4d..2943ce57 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.16.8 2022-11-06: +新增:Controller.getFilesOnly(Set paraNames) 方法 +优化:默认关闭缓存日志 + + + jboot v3.16.7 2022-10-31: 新增:添加 CachePrinter 的支持,方便把缓存信息输出在控制台或者日志上 优化:JbootResourceLoader 优化过滤掉 Windows 的临时文件 diff --git a/pom.xml b/pom.xml index dcf352da..69545de3 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.8-SNAPSHOT + 3.16.8 jar jboot -- Gitee From 0f4f7c8aee51ed9ddb3f84f8fc8b4696de630be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 12 Nov 2022 15:10:29 +0800 Subject: [PATCH 1854/1965] v3.16.9 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 +-- doc/jbootadmin/start.md | 4 +-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- .../interceptor/CacheableInterceptor.java | 30 +++++++++++++++++++ 7 files changed, 38 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 71c66458..3c18f9a1 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.16.8 + 3.16.9 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 6b570e76..34a64fb5 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.16.8 + 3.16.9 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index aeceec10..d6d42dd2 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.16.8 + 3.16.9 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.16.8', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.16.9', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index c39353a7..ad6a6813 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.16.8', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.16.9', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.8/jboot-3.16.8.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.9/jboot-3.16.9.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 4fb6c773..d564f269 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.16.8 + 3.16.9 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index dae26005..563c09c8 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.16.8"; + public static String VERSION = "3.16.9"; public static final String ATTR_CONTEXT_PATH = "CPATH"; diff --git a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java index 286c7b0c..8f5aa44a 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java +++ b/src/main/java/io/jboot/components/cache/interceptor/CacheableInterceptor.java @@ -31,6 +31,7 @@ import io.jboot.exception.JbootException; import io.jboot.utils.AnnotationUtil; import io.jboot.utils.ClassUtil; import io.jboot.utils.ModelUtil; +import io.jboot.utils.StrUtil; import io.jboot.web.render.JbootRenderFactory; import io.jboot.web.render.JbootReturnValueRender; @@ -52,6 +53,8 @@ public class CacheableInterceptor implements Interceptor { //是否开启 Controller 的 Action 缓存 //可用在 dev 模式下关闭,生产环境开启的场景,方便调试数据 private static boolean actionCacheEnable = true; + private static String actionCacheRefreshKey; + private static String actionCacheRefreshValue = "1"; public static boolean isActionCacheEnable() { return actionCacheEnable; @@ -61,6 +64,25 @@ public class CacheableInterceptor implements Interceptor { CacheableInterceptor.actionCacheEnable = actionCacheEnable; } + public static String getActionCacheRefreshKey() { + return actionCacheRefreshKey; + } + + public static void setActionCacheRefreshKey(String actionCacheRefreshKey) { + CacheableInterceptor.actionCacheRefreshKey = actionCacheRefreshKey; + } + + public static String getActionCacheRefreshValue() { + return actionCacheRefreshValue; + } + + public static void setActionCacheRefreshValue(String actionCacheRefreshValue) { + if (actionCacheRefreshValue == null || actionCacheRefreshValue.trim().length() == 0){ + throw new NullPointerException("actionCacheRefresValue can not be null or empty."); + } + CacheableInterceptor.actionCacheRefreshValue = actionCacheRefreshValue; + } + @Override public void intercept(Invocation inv) { @@ -93,6 +115,14 @@ public class CacheableInterceptor implements Interceptor { Controller controller = inv.getController(); + //刷新当前页面缓存 + if (StrUtil.isNotBlank(actionCacheRefreshKey) + && actionCacheRefreshValue.equals(inv.getController().getPara(actionCacheRefreshKey))){ + inv.invoke(); + cacheActionContent(cacheName, cacheKey, cacheable.liveSeconds(), inv, method); + return; + } + ActionCachedContent actionCachedContent = AopCache.get(cacheName, cacheKey); if (actionCachedContent != null) { renderActionCachedContent(controller, actionCachedContent); -- Gitee From c8f1e44eba17274fa3e9b59ea3e8f089201b56ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 12 Nov 2022 15:10:33 +0800 Subject: [PATCH 1855/1965] v3.16.9 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ pom.xml | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 32b39a65..b7ecc58b 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.16.8 + 3.16.9 ``` diff --git a/changes.txt b/changes.txt index 2943ce57..b2d76d72 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v3.16.9 2022-11-12: +新增:自定义 Controller 缓存查询 key 的功能 +优化:升级 jackson-core 等到最新版本 + + + jboot v3.16.8 2022-11-06: 新增:Controller.getFilesOnly(Set paraNames) 方法 优化:默认关闭缓存日志 diff --git a/pom.xml b/pom.xml index 69545de3..f3d8ed88 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.8 + 3.16.9 jar jboot @@ -47,7 +47,7 @@ 3.4 3.3.0 4.0.3 - 2.13.4 + 2.14.0 5.1.49 2.2.19.Final 1.7.36 -- Gitee From 6a54eb671f33b83f3de27247ef03f181f1d46888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 12 Nov 2022 18:50:41 +0800 Subject: [PATCH 1856/1965] v3.17.0 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- .../java/io/jboot/components/mq/JbootmqBase.java | 12 +++++++----- .../java/io/jboot/components/mq/JbootmqConfig.java | 2 +- .../java/io/jboot/components/mq/JbootmqManager.java | 10 +++++++++- src/main/java/io/jboot/core/JbootCoreConfig.java | 8 ++++++++ 10 files changed, 33 insertions(+), 15 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 3c18f9a1..bae81b56 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.16.9 + 3.17.0 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 34a64fb5..aeb470f3 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.16.9 + 3.17.0 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index d6d42dd2..45d621cc 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.16.9 + 3.17.0 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.16.9', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.17.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index ad6a6813..aa60cac0 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.16.9', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.17.0', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.16.9/jboot-3.16.9.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.17.0/jboot-3.17.0.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index d564f269..8d19992a 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.16.9 + 3.17.0 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 563c09c8..b7b75e13 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.16.9"; + public static String VERSION = "3.17.0"; public static final String ATTR_CONTEXT_PATH = "CPATH"; diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index 986bcadb..6f39ba4f 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -19,7 +19,6 @@ import com.jfinal.kit.LogKit; import com.jfinal.log.Log; import io.jboot.Jboot; import io.jboot.components.serializer.JbootSerializer; -import io.jboot.exception.JbootException; import io.jboot.utils.NamedThreadFactory; import io.jboot.utils.StrUtil; @@ -84,6 +83,7 @@ public abstract class JbootmqBase implements Jbootmq { channelListeners.put(channel, listeners); } listeners.add(listener); + channels.add(channel); } @@ -167,11 +167,13 @@ public abstract class JbootmqBase implements Jbootmq { @Override public boolean startListening() { if (isStarted) { - throw new JbootException("Jboot MQ has started."); + return true; } if (channels == null || channels.isEmpty()) { - throw new JbootException("Jboot MQ's channels is null or empty, Please config channels."); + LogKit.warn("Jboot MQ started fail. because it's channels is empty, please config channels. " + + "MQ name: {}, type:{}", config.getName(), config.getType()); + return false; } try { @@ -183,14 +185,14 @@ public abstract class JbootmqBase implements Jbootmq { return false; } - return true; } + @Override public boolean stopListening() { if (!isStarted) { - throw new JbootException("Jboot MQ has stoped."); + return true; } try { diff --git a/src/main/java/io/jboot/components/mq/JbootmqConfig.java b/src/main/java/io/jboot/components/mq/JbootmqConfig.java index a11102f0..22fe8408 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqConfig.java +++ b/src/main/java/io/jboot/components/mq/JbootmqConfig.java @@ -38,7 +38,7 @@ public class JbootmqConfig { private String name = "default"; // MQ 的名称,可以配置多个 MQ 实例,但是需要名称不能一样 private String type; // MQ 的类型: redis、rocketmq 等 - private String typeName; // MQ 相同的类型,可能有多一个实例,比如两个 redis,此时需要配置示例的名称 + private String typeName; // MQ 相同的类型,可能有多一个实例,比如两个 redis,此时需要配置实例的名称 private String channel; // 发送的通道,或者是 topic,多个用英文逗号二开 private String syncRecevieMessageChannel; //可同步接收消息的 channel 配置 private String serializer; // MQ 默认的序列化方案 diff --git a/src/main/java/io/jboot/components/mq/JbootmqManager.java b/src/main/java/io/jboot/components/mq/JbootmqManager.java index 4eaa886e..011b638c 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqManager.java +++ b/src/main/java/io/jboot/components/mq/JbootmqManager.java @@ -44,7 +44,6 @@ public class JbootmqManager { private Map jbootmqMap = new ConcurrentHashMap<>(); - public Jbootmq getJbootmq() { return getJbootmq("default"); } @@ -108,4 +107,13 @@ public class JbootmqManager { } } + + + public void init() { + jbootmqMap.values().forEach(Jbootmq::startListening); + } + + public void stop() { + jbootmqMap.values().forEach(Jbootmq::stopListening); + } } diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index b8a9b55b..93e54b99 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -36,6 +36,7 @@ import io.jboot.components.cache.support.JbootTokenCache; import io.jboot.components.gateway.JbootGatewayHandler; import io.jboot.components.gateway.JbootGatewayManager; import io.jboot.components.limiter.LimiterManager; +import io.jboot.components.mq.JbootmqManager; import io.jboot.components.rpc.JbootrpcManager; import io.jboot.components.schedule.JbootScheduleManager; import io.jboot.core.listener.JbootAppListenerManager; @@ -406,9 +407,14 @@ public class JbootCoreConfig extends JFinalConfig { TypeConverter.me().setConvertFunc(new TypeConverterFunc()); ArrayConverters.init(); + //一般情况下,各个模块会在 onStart 进行添加监听器 + //此时可以主动去启动下 mq + JbootmqManager.me().init(); //使用场景:需要等所有组件 onStart() 完成之后,再去执行某些工作的时候 JbootAppListenerManager.me().onStartFinish(); + + } @Override @@ -418,6 +424,8 @@ public class JbootCoreConfig extends JFinalConfig { JbootScheduleManager.me().stop(); JbootSeataManager.me().stop(); JbootrpcManager.me().stop(); + + JbootmqManager.me().stop(); } -- Gitee From 760a8773b6cc09acada18e4b602622f64b7ff65a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 12 Nov 2022 18:50:46 +0800 Subject: [PATCH 1857/1965] v3.17.0 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++-- pom.xml | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b7ecc58b..479a7dcf 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.16.9 + 3.17.0 ``` diff --git a/changes.txt b/changes.txt index b2d76d72..ac1a251b 100644 --- a/changes.txt +++ b/changes.txt @@ -1,5 +1,7 @@ -jboot v3.16.9 2022-11-12: -新增:自定义 Controller 缓存查询 key 的功能 +jboot v3.17.0 2022-11-12: +新增:自定义 Controller 缓存刷新 key 的功能 +优化:Jboot MQ 在启动和停止的时候,不对已启动进行错误抛出,方便多模块可以自由启动或停止 +优化:Jboot MQ 添加监听器的时候,自动添加 channel 信息 优化:升级 jackson-core 等到最新版本 diff --git a/pom.xml b/pom.xml index f3d8ed88..69386693 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.16.9 + 3.17.0 jar jboot -- Gitee From 2f8f7774958b5dc26b9a6ae625b93e78eaf83a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 12 Nov 2022 18:54:59 +0800 Subject: [PATCH 1858/1965] v3.17.0 release (^.^)YYa!! --- .../io/jboot/test/mq/rabbit/RabbitMqReceiver1.java | 14 ++++++++------ .../io/jboot/test/mq/rabbit/RabbitMqReceiver2.java | 14 +++++++------- .../io/jboot/test/mq/rabbit/RabbitMqReceiver3.java | 8 ++++---- .../io/jboot/test/mq/redis/RedisMqReceiver1.java | 7 ++++--- .../io/jboot/test/mq/redis/RedisMqReceiver2.java | 9 ++++++--- 5 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java index 3dba0e38..d52df70a 100644 --- a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java +++ b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver1.java @@ -3,8 +3,8 @@ package io.jboot.test.mq.rabbit; import io.jboot.Jboot; import io.jboot.app.JbootApplication; -import io.jboot.components.mq.MessageContext; import io.jboot.components.mq.JbootmqMessageListener; +import io.jboot.components.mq.MessageContext; /** * 开始之前 先通过通过 docker 把 rabbitmq 运行起来 @@ -33,11 +33,8 @@ public class RabbitMqReceiver1 { //非常重要,多个应用如果同时接受同一个 channel 的广播,必须配置此项,而且必须不能相同,否则广播的时候只有一个应用能够接受到 JbootApplication.setBootArg("jboot.mq.rabbitmq.broadcastChannelPrefix", "app1"); - //启动应用程序 - JbootApplication.run(args); - - // 只监听 myChannel 这个通道 + // 只监听 channel1 这个通道 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @Override public void onMessage(String channel, Object message, MessageContext context) { @@ -46,7 +43,12 @@ public class RabbitMqReceiver1 { },"channel1"); - Jboot.getMq().startListening(); + + + //启动应用程序 + JbootApplication.run(args); + + System.out.println("RabbitMqReceiver1 started."); } diff --git a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java index 9baac9be..b544a321 100644 --- a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java +++ b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver2.java @@ -33,21 +33,21 @@ public class RabbitMqReceiver2 { //非常重要,多个应用如果同时接受同一个 channel 的广播,必须配置此项,而且必须不能相同,否则广播的时候只有一个应用能够接受到 // JbootApplication.setBootArg("jboot.mq.rabbitmq.broadcastChannelPrefix", "app2"); - //启动应用程序 - JbootApplication.run(args); - - - // 只监听 myChannel 这个通道 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @Override public void onMessage(String channel, Object message, MessageContext context) { System.out.println("Receive msg: " + message + ", from channel: " + channel); } - }); + },"myChannel"); + + + //启动应用程序 + JbootApplication.run(args); + + - Jboot.getMq().startListening(); System.out.println("RabbitMqReceiver2 started."); } diff --git a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver3.java b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver3.java index fc2ca4c7..a9923139 100644 --- a/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver3.java +++ b/src/test/java/io/jboot/test/mq/rabbit/RabbitMqReceiver3.java @@ -32,9 +32,6 @@ public class RabbitMqReceiver3 { //非常重要,多个应用如果同时接受同一个 channel 的广播,必须配置此项,而且必须不能相同,否则广播的时候只有一个应用能够接受到 JbootApplication.setBootArg("jboot.mq.rabbitmq.broadcastChannelPrefix", "app3"); - //启动应用程序 - JbootApplication.run(args); - //添加监听,不指定通道,则监听所有通道 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @Override @@ -45,7 +42,10 @@ public class RabbitMqReceiver3 { - Jboot.getMq().startListening(); + //启动应用程序 + JbootApplication.run(args); + + System.out.println("RabbitMqReceiver3 started."); } diff --git a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java index ee652310..9dd6788b 100644 --- a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java +++ b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java @@ -18,8 +18,6 @@ public class RedisMqReceiver1 { JbootApplication.setBootArg("jboot.mq.channel", "channel1,channel2,myChannel"); JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); - //启动应用程序 - JbootApplication.run(args); //添加监听 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @@ -37,7 +35,10 @@ public class RedisMqReceiver1 { } },"myChannel"); - Jboot.getMq().startListening(); + + //启动应用程序 + JbootApplication.run(args); + System.out.println("RedisMqReceiver1 started."); } diff --git a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java index 1b77c202..2d62864b 100644 --- a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java +++ b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java @@ -18,8 +18,6 @@ public class RedisMqReceiver2 { JbootApplication.setBootArg("jboot.mq.channel", "channel1,channel2,myChannel"); JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); - //启动应用程序 - JbootApplication.run(args); //添加监听 Jboot.getMq().addMessageListener(new JbootmqMessageListener() { @@ -37,7 +35,12 @@ public class RedisMqReceiver2 { } },"myChannel"); - Jboot.getMq().startListening(); + + + //启动应用程序 + JbootApplication.run(args); + + System.out.println("RedisMqReceiver1 started."); } -- Gitee From 4c318386d8e00e5168a9e964e74455e96e802bf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 17 Nov 2022 19:05:14 +0800 Subject: [PATCH 1859/1965] v3.17.1 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 69386693..7eba30f8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.17.0 + 3.17.1-SNAPSHOT jar jboot -- Gitee From 71bffc00af41063f490326c1429826447d0604a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 17 Nov 2022 19:05:22 +0800 Subject: [PATCH 1860/1965] optimize Columns.java --- src/main/java/io/jboot/db/model/Columns.java | 32 ++++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 13cca20c..98e7ff0b 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -275,8 +275,8 @@ public class Columns implements Serializable { * @param condition * @return */ - public Columns isNullIf(String name, boolean condition) { - if (condition) { + public Columns isNullIf(String name, Boolean condition) { + if (condition != null && condition) { add(Column.create(name, null, Column.LOGIC_IS_NULL)); } return this; @@ -301,8 +301,8 @@ public class Columns implements Serializable { * @param condition * @return */ - public Columns isNotNullIf(String name, boolean condition) { - if (condition) { + public Columns isNotNullIf(String name, Boolean condition) { + if (condition != null && condition) { add(Column.create(name, null, Column.LOGIC_IS_NOT_NULL)); } return this; @@ -415,11 +415,11 @@ public class Columns implements Serializable { * @param conditon * @return */ - public Columns groupIf(Columns columns, boolean conditon) { + public Columns groupIf(Columns columns, Boolean conditon) { if (columns == this) { throw new IllegalArgumentException("Columns.group(...) need a new Columns"); } - if (conditon && !columns.isEmpty()) { + if ( conditon != null && conditon && !columns.isEmpty()) { add(new Group(columns)); } return this; @@ -499,8 +499,8 @@ public class Columns implements Serializable { * @param condition * @return */ - public Columns sqlPartIf(String sql, boolean condition) { - if (condition && StrUtil.isNotBlank(sql)) { + public Columns sqlPartIf(String sql, Boolean condition) { + if (condition != null && condition && StrUtil.isNotBlank(sql)) { add(new SqlPart(sql)); } return this; @@ -514,9 +514,9 @@ public class Columns implements Serializable { * @param paras * @return */ - public Columns sqlPartIf(String sql, boolean condition, Object... paras) { + public Columns sqlPartIf(String sql, Boolean condition, Object... paras) { Util.checkNullParas(this, paras); - if (condition && StrUtil.isNotBlank(sql)) { + if (condition != null && condition && StrUtil.isNotBlank(sql)) { add(new SqlPart(sql, paras)); } return this; @@ -557,8 +557,8 @@ public class Columns implements Serializable { * @param condition * @return */ - public Columns sqlPartWithoutLinkIf(String sql, boolean condition) { - if (condition && StrUtil.isNotBlank(sql)) { + public Columns sqlPartWithoutLinkIf(String sql, Boolean condition) { + if (condition != null && condition && StrUtil.isNotBlank(sql)) { add(new SqlPart(sql, true)); } return this; @@ -572,9 +572,9 @@ public class Columns implements Serializable { * @param paras * @return */ - public Columns sqlPartWithoutLinkIf(String sql, boolean condition, Object... paras) { + public Columns sqlPartWithoutLinkIf(String sql, Boolean condition, Object... paras) { Util.checkNullParas(this, paras); - if (condition && StrUtil.isNotBlank(sql)) { + if (condition != null && condition && StrUtil.isNotBlank(sql)) { add(new SqlPart(sql, paras, true)); } return this; @@ -629,8 +629,8 @@ public class Columns implements Serializable { * @param columns * @return */ - public Columns appendIf(Columns columns, boolean condition) { - if (condition) { + public Columns appendIf(Columns columns, Boolean condition) { + if (condition != null && condition) { append(columns); } return this; -- Gitee From 3116483558999994b5b11f510f9cb6fffccc4664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E7=AB=8B=E5=88=9A?= Date: Fri, 18 Nov 2022 11:05:46 +0800 Subject: [PATCH 1861/1965] =?UTF-8?q?=E6=89=AB=E6=8F=8Fjar=E5=8C=85?= =?UTF-8?q?=E9=87=8C=E9=9D=A2=E7=9A=84jar=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/utils/ClassScanner.java | 1702 +++++++++-------- 1 file changed, 878 insertions(+), 824 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index d2505f65..91f8d6f6 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -1,824 +1,878 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.utils; - -import io.jboot.app.config.JbootConfigManager; - -import java.io.File; -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Modifier; -import java.net.URL; -import java.net.URLClassLoader; -import java.net.URLDecoder; -import java.util.*; -import java.util.function.Predicate; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.stream.Collectors; - -public class ClassScanner { - - private static final Set appClassesCache = new HashSet<>(); - - public static final Set scanJars = new HashSet<>(); - public static final Set excludeJars = new HashSet<>(); - - public static final Set scanClasses = new HashSet<>(); - public static final Set excludeClasses = new HashSet<>(); - - // 默认关闭扫描信息的控制台输出 - private static boolean printScannerInfoEnable = false; - - public static boolean isPrintScannerInfoEnable() { - return printScannerInfoEnable; - } - - public static void setPrintScannerInfoEnable(boolean printScannerInfoEnable) { - ClassScanner.printScannerInfoEnable = printScannerInfoEnable; - } - - - public static void addScanJarPrefix(String prefix) { - scanJars.add(prefix.toLowerCase().trim()); - } - - static { - scanJars.add("jboot"); - } - - - public static void addUnscanJarPrefix(String prefix) { - excludeJars.add(prefix.toLowerCase().trim()); - } - - static { - excludeJars.add("jfinal-"); - excludeJars.add("cos-"); - excludeJars.add("cglib-"); - excludeJars.add("undertow-"); - excludeJars.add("xnio-"); - excludeJars.add("javax."); - excludeJars.add("hikaricp-"); - excludeJars.add("druid-"); - excludeJars.add("mysql-"); - excludeJars.add("db2jcc-"); - excludeJars.add("db2jcc4-"); - excludeJars.add("ojdbc"); - excludeJars.add("junit-"); - excludeJars.add("junit5-"); - excludeJars.add("org.junit"); - excludeJars.add("hamcrest-"); - excludeJars.add("jboss-"); - excludeJars.add("motan-"); - excludeJars.add("commons-pool"); - excludeJars.add("commons-beanutils"); - excludeJars.add("commons-codec"); - excludeJars.add("commons-collections"); - excludeJars.add("commons-configuration"); - excludeJars.add("commons-lang"); - excludeJars.add("commons-logging"); - excludeJars.add("commons-io"); - excludeJars.add("commons-httpclient"); - excludeJars.add("commons-fileupload"); - excludeJars.add("commons-validator"); - excludeJars.add("commons-email"); - excludeJars.add("commons-text"); - excludeJars.add("commons-cli"); - excludeJars.add("commons-math"); - excludeJars.add("commons-jxpath"); - excludeJars.add("commons-compress"); - excludeJars.add("audience-"); - excludeJars.add("hessian-"); - excludeJars.add("metrics-"); - excludeJars.add("javapoet-"); - excludeJars.add("netty-"); - excludeJars.add("consul-"); - excludeJars.add("gson-"); - excludeJars.add("zookeeper-"); - excludeJars.add("slf4j-"); - excludeJars.add("fastjson-"); - excludeJars.add("guava-"); - excludeJars.add("failureaccess-"); - excludeJars.add("listenablefuture-"); - excludeJars.add("jsr305-"); - excludeJars.add("checker-qual-"); - excludeJars.add("error_prone_annotations-"); - excludeJars.add("j2objc-"); - excludeJars.add("animal-sniffer-"); - excludeJars.add("cron4j-"); - excludeJars.add("jedis-"); - excludeJars.add("lettuce-"); - excludeJars.add("reactor-"); - excludeJars.add("fst-"); - excludeJars.add("kryo-"); - excludeJars.add("jackson-"); - excludeJars.add("javassist-"); - excludeJars.add("objenesis-"); - excludeJars.add("reflectasm-"); - excludeJars.add("asm-"); - excludeJars.add("minlog-"); - excludeJars.add("jsoup-"); - excludeJars.add("ons-client-"); - excludeJars.add("amqp-client-"); - excludeJars.add("ehcache-"); - excludeJars.add("sharding-"); - excludeJars.add("snakeyaml-"); - excludeJars.add("groovy-"); - excludeJars.add("profiler-"); - excludeJars.add("joda-time-"); - excludeJars.add("shiro-"); - excludeJars.add("dubbo-"); - excludeJars.add("curator-"); - excludeJars.add("resteasy-"); - excludeJars.add("reactive-"); - excludeJars.add("validation-"); - excludeJars.add("httpclient-"); - excludeJars.add("httpcore-"); - excludeJars.add("httpmime-"); - excludeJars.add("jcip-"); - excludeJars.add("jcl-"); - excludeJars.add("microprofile-"); - excludeJars.add("org.osgi"); - excludeJars.add("zkclient-"); - excludeJars.add("jjwt-"); - excludeJars.add("okhttp-"); - excludeJars.add("okio-"); - excludeJars.add("zbus-"); - excludeJars.add("swagger-"); - excludeJars.add("j2cache-"); - excludeJars.add("caffeine-"); - excludeJars.add("jline-"); - excludeJars.add("qpid-"); - excludeJars.add("geronimo-"); - excludeJars.add("activation-"); - excludeJars.add("org.abego"); - excludeJars.add("antlr-"); - excludeJars.add("antlr4-"); - excludeJars.add("st4-"); - excludeJars.add("icu4j-"); - excludeJars.add("idea_rt"); - excludeJars.add("mrjtoolkit"); - excludeJars.add("logback-"); - excludeJars.add("log4j-"); - excludeJars.add("log4j2-"); - excludeJars.add("aliyun-java-sdk-"); - excludeJars.add("aliyun-sdk-"); - excludeJars.add("archaius-"); - excludeJars.add("aopalliance-"); - excludeJars.add("hdrhistogram-"); - excludeJars.add("jdom-"); - excludeJars.add("rxjava-"); - excludeJars.add("jersey-"); - excludeJars.add("stax-"); - excludeJars.add("stax2-"); - excludeJars.add("jettison-"); - excludeJars.add("commonmark-"); - excludeJars.add("jaxb-"); - excludeJars.add("json-20"); - excludeJars.add("jcseg-"); - excludeJars.add("lucene-"); - excludeJars.add("elasticsearch-"); - excludeJars.add("jopt-"); - excludeJars.add("httpasyncclient-"); - excludeJars.add("jna-"); - excludeJars.add("lang-mustache-client-"); - excludeJars.add("parent-join-client-"); - excludeJars.add("rank-eval-client-"); - excludeJars.add("aggs-matrix-stats-client-"); - excludeJars.add("t-digest-"); - excludeJars.add("compiler-"); - excludeJars.add("hppc-"); - excludeJars.add("libthrift-"); - excludeJars.add("seata-"); - excludeJars.add("eureka-"); - excludeJars.add("netflix-"); - excludeJars.add("nacos-"); - excludeJars.add("apollo-"); - excludeJars.add("guice-"); - excludeJars.add("servlet-"); - excludeJars.add("debugger-agent.jar"); - excludeJars.add("xpp3_min-"); - excludeJars.add("latency"); - excludeJars.add("micrometer-"); - excludeJars.add("xstream-"); - excludeJars.add("jsr311-"); - excludeJars.add("servo-"); - excludeJars.add("compactmap-"); - excludeJars.add("dexx-"); - excludeJars.add("spotbugs-"); - excludeJars.add("xmlpull-"); - excludeJars.add("shardingsphere-"); - excludeJars.add("sentinel-"); - excludeJars.add("spring-"); - excludeJars.add("simpleclient-"); - excludeJars.add("breeze-"); - excludeJars.add("config-"); - excludeJars.add("encrypt-core-"); - excludeJars.add("lombok-"); - excludeJars.add("hutool-"); - excludeJars.add("jakarta."); - excludeJars.add("protostuff-"); - excludeJars.add("poi-"); - excludeJars.add("easypoi-"); - excludeJars.add("ognl-"); - excludeJars.add("xmlbeans-"); - excludeJars.add("master-slave-core-"); - excludeJars.add("shadow-core-rewrite-"); - excludeJars.add("apiguardian-api-"); - excludeJars.add("opentest4j-"); - excludeJars.add("opentracing-"); - excludeJars.add("freemarker-"); - excludeJars.add("protobuf-"); - excludeJars.add("jdom2-"); - excludeJars.add("useragentutils-"); - excludeJars.add("common-io-"); - excludeJars.add("common-image-"); - excludeJars.add("common-lang-"); - excludeJars.add("imageio-"); - excludeJars.add("curvesapi-"); - excludeJars.add("myexcel-"); - excludeJars.add("oshi-"); - excludeJars.add("classmate-"); - excludeJars.add("hibernate-"); - excludeJars.add("aspectjweaver-"); - excludeJars.add("aspectjrt-"); - excludeJars.add("simpleclient_"); - excludeJars.add("rocketmq-"); - excludeJars.add("clickhouse-"); - excludeJars.add("lz4-"); - excludeJars.add("commons-digester-"); - excludeJars.add("opencc4j-"); - excludeJars.add("heaven-"); - excludeJars.add("tinypinyin-"); - excludeJars.add("jieba-"); - excludeJars.add("ahocorasick-"); - excludeJars.add("kotlin-"); - excludeJars.add("xml-apis-"); - excludeJars.add("dom4j-"); - excludeJars.add("ini4j-"); - excludeJars.add("cache-api-"); - excludeJars.add("byte-buddy-"); - excludeJars.add("jodd-"); - excludeJars.add("redisson-"); - excludeJars.add("bcprov-"); - excludeJars.add("pay-java-"); - excludeJars.add("alipay-sdk-"); - excludeJars.add("mapper-extras-"); - excludeJars.add("org.jacoco"); - excludeJars.add("jxl-"); - excludeJars.add("jxls-"); - excludeJars.add("jstl-"); - excludeJars.add("batik-"); - excludeJars.add("xmlsec-"); - excludeJars.add("pdfbox-"); - excludeJars.add("fontbox-"); - excludeJars.add("xk-time-"); - excludeJars.add("geohash-"); - excludeJars.add("ezmorph-"); - excludeJars.add("async-http-"); - excludeJars.add("jsr-"); - excludeJars.add("jsr250"); - excludeJars.add("pinyin4j"); - excludeJars.add("ijpay-"); - excludeJars.add("wildfly-"); - excludeJars.add("liquibase-"); - excludeJars.add("flowable-"); - excludeJars.add("mybatis-"); - excludeJars.add("ip2region-"); - excludeJars.add("java-uuid-generator-"); - excludeJars.add("quartz-"); - } - - - public static void addUnscanClassPrefix(String prefix) { - excludeClasses.add(prefix.trim()); - } - - static { - excludeClasses.add("java."); - excludeClasses.add("javax."); - excludeClasses.add("junit."); - excludeClasses.add("jline."); - excludeClasses.add("redis."); - excludeClasses.add("lombok."); - excludeClasses.add("oshi."); - excludeClasses.add("jodd."); - excludeClasses.add("javassist."); - excludeClasses.add("google."); - excludeClasses.add("com.jfinal."); - excludeClasses.add("com.aliyuncs."); - excludeClasses.add("com.carrotsearch."); - excludeClasses.add("org.aopalliance."); - excludeClasses.add("org.apache."); - excludeClasses.add("org.nustaq."); - excludeClasses.add("net.sf."); - excludeClasses.add("org.slf4j."); - excludeClasses.add("org.antlr."); - excludeClasses.add("org.jboss."); - excludeClasses.add("org.checkerframework."); - excludeClasses.add("org.jsoup."); - excludeClasses.add("org.objenesis."); - excludeClasses.add("org.ow2."); - excludeClasses.add("org.reactivest."); - excludeClasses.add("org.yaml."); - excludeClasses.add("org.checker"); - excludeClasses.add("org.codehaus"); - excludeClasses.add("org.commonmark"); - excludeClasses.add("org.jdom2."); - excludeClasses.add("org.aspectj."); - excludeClasses.add("org.hibernate."); - excludeClasses.add("org.ahocorasick."); - excludeClasses.add("org.lionsoul.jcseg."); - excludeClasses.add("org.ini4j."); - excludeClasses.add("org.jetbrains."); - excludeClasses.add("org.jacoco."); - excludeClasses.add("org.xnio."); - excludeClasses.add("org.bouncycastle."); - excludeClasses.add("org.elasticsearch."); - excludeClasses.add("org.hamcrest."); - excludeClasses.add("org.objectweb."); - excludeClasses.add("org.joda."); - excludeClasses.add("org.wildfly."); - excludeClasses.add("org.owasp."); - excludeClasses.add("aj.org."); - excludeClasses.add("ch.qos."); - excludeClasses.add("joptsimple."); - excludeClasses.add("com.alibaba.csp."); - excludeClasses.add("com.alibaba.nacos."); - excludeClasses.add("com.alibaba.druid."); - excludeClasses.add("com.alibaba.fastjson."); - excludeClasses.add("com.aliyun.open"); - excludeClasses.add("com.caucho"); - excludeClasses.add("com.codahale"); - excludeClasses.add("com.ctrip.framework.apollo"); - excludeClasses.add("com.ecwid."); - excludeClasses.add("com.esotericsoftware."); - excludeClasses.add("com.fasterxml."); - excludeClasses.add("com.github."); - excludeClasses.add("io.github."); - excludeClasses.add("com.google."); - excludeClasses.add("metrics_influxdb."); - excludeClasses.add("com.rabbitmq."); - excludeClasses.add("com.squareup."); - excludeClasses.add("com.sun."); - excludeClasses.add("com.typesafe."); - excludeClasses.add("com.weibo.api.motan."); - excludeClasses.add("com.zaxxer."); - excludeClasses.add("com.mysql."); - excludeClasses.add("com.papertrail."); - excludeClasses.add("com.egzosn."); - excludeClasses.add("com.alipay.api"); - excludeClasses.add("org.gjt."); - excludeClasses.add("org.fusesource."); - excludeClasses.add("org.redisson."); - excludeClasses.add("io.dropwizard"); - excludeClasses.add("io.prometheus"); - excludeClasses.add("io.jsonwebtoken"); - excludeClasses.add("io.lettuce"); - excludeClasses.add("reactor.adapter"); - excludeClasses.add("io.seata."); - excludeClasses.add("io.swagger."); - excludeClasses.add("io.undertow."); - excludeClasses.add("io.netty."); - excludeClasses.add("io.opentracing."); - excludeClasses.add("it.sauronsoftware"); - excludeClasses.add("net.oschina.j2cache"); - excludeClasses.add("net.bytebuddy"); - excludeClasses.add("cn.hutool."); - excludeClasses.add("com.dyuproject."); - excludeClasses.add("io.protostuff."); - excludeClasses.add("io.reactivex."); - excludeClasses.add("freemarker."); - excludeClasses.add("com.twelvemonkeys."); - excludeClasses.add("eu.bitwalker."); - excludeClasses.add("jstl."); - excludeClasses.add("jxl."); - excludeClasses.add("org.jxls"); - excludeClasses.add("org.kordamp"); - excludeClasses.add("org.mybatis"); - excludeClasses.add("org.lisonsoul"); - excludeClasses.add("org.flowable"); - } - - - public static void addScanClassPrefix(String prefix) { - scanClasses.add(prefix.toLowerCase().trim()); - } - - static { - scanClasses.add("io.jboot.support.shiro.directives"); - } - - static { - String scanJarPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.scanJarPrefix"); - if (scanJarPrefix != null) { - String[] prefixes = scanJarPrefix.split(","); - for (String prefix : prefixes) { - if (prefix != null && prefix.trim().length() > 0) { - addScanJarPrefix(prefix.trim()); - } - } - } - - String unScanJarPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.unScanJarPrefix"); - if (unScanJarPrefix != null) { - String[] prefixes = unScanJarPrefix.split(","); - for (String prefix : prefixes) { - if (prefix != null && prefix.trim().length() > 0) { - addUnscanJarPrefix(prefix.trim()); - } - } - } - - String unScanClassPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.unScanClassPrefix"); - if (unScanClassPrefix != null) { - String[] prefixes = unScanClassPrefix.split(","); - for (String prefix : prefixes) { - if (prefix != null && prefix.trim().length() > 0) { - addUnscanClassPrefix(prefix.trim()); - } - } - } - - String scanClassPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.scanClassPrefix"); - if (scanClassPrefix != null) { - String[] prefixes = scanClassPrefix.split(","); - for (String prefix : prefixes) { - if (prefix != null && prefix.trim().length() > 0) { - addScanClassPrefix(prefix.trim()); - } - } - } - - } - - public static List> scanSubClass(Class pclazz) { - return scanSubClass(pclazz, false); - } - - - public static List> scanSubClass(Class pclazz, boolean instantiable) { - initIfNecessary(); - List> classes = new ArrayList<>(); - findChildClasses(classes, pclazz, instantiable); - return classes; - } - - public static List scanClass() { - return scanClass(false); - } - - public static List scanClass(boolean isInstantiable) { - - initIfNecessary(); - - if (!isInstantiable) { - return new ArrayList<>(appClassesCache); - } - - return scanClass(ClassScanner::isInstantiable); - - } - - public static List scanClass(Predicate filter) { - - initIfNecessary(); - - return appClassesCache.stream() - .filter(filter) - .collect(Collectors.toList()); - - } - - public static void clearAppClassesCache() { - appClassesCache.clear(); - } - - - private static boolean isInstantiable(Class clazz) { - return !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()); - } - - - public static List scanClassByAnnotation(Class annotationClass, boolean instantiable) { - initIfNecessary(); - - List list = new ArrayList<>(); - for (Class clazz : appClassesCache) { - Annotation annotation = clazz.getAnnotation(annotationClass); - if (annotation == null) { - continue; - } - - if (instantiable && !isInstantiable(clazz)) { - continue; - } - - list.add(clazz); - } - - return list; - } - - private static void initIfNecessary() { - if (appClassesCache.isEmpty()) { - initAppClasses(); - } - } - - - private static void findChildClasses(List> classes, Class parent, boolean instantiable) { - for (Class clazz : appClassesCache) { - - if (!parent.isAssignableFrom(clazz)) { - continue; - } - - if (instantiable && !isInstantiable(clazz)) { - continue; - } - - classes.add(clazz); - } - } - - - private static void initAppClasses() { - - Set jarPaths = new HashSet<>(); - Set classPaths = new HashSet<>(); - - - // jdk8 及以下、 - // tomcat 容器、 - // jfinal-undertow、 - // 以上三种加载模式通过 classloader 获取 - findClassPathsAndJarsByClassloader(jarPaths, classPaths, ClassScanner.class.getClassLoader()); - - //jdk9+ 等其他方式通过 classpath 获取 - findClassPathsAndJarsByClassPath(jarPaths, classPaths); - - - String tomcatClassPath = null; - - for (String classPath : classPaths) { - //过滤tomcat自身的lib 以及 bin 下的jar - File tomcatApiJarFile = new File(classPath, "tomcat-api.jar"); - File tomcatJuliJarFile = new File(classPath, "tomcat-juli.jar"); - if (tomcatApiJarFile.exists() || tomcatJuliJarFile.exists()) { - tomcatClassPath = tomcatApiJarFile - .getParentFile() - .getParentFile().getAbsolutePath().replace('\\', '/'); - continue; - } - - if (isPrintScannerInfoEnable()) { - System.out.println("Jboot Scan ClassPath: " + classPath); - } - - addClassesFromClassPath(classPath); - } - - for (String jarPath : jarPaths) { - - //过滤 tomcat 的 jar,但是不能过滤 webapps 目录下的 - if (tomcatClassPath != null - && jarPath.startsWith(tomcatClassPath) - && !jarPath.contains("webapps")) { - continue; - } - - if (!isIncludeJar(jarPath)) { - continue; - } - - if (isPrintScannerInfoEnable()) { - System.out.println("Jboot Scan Jar: " + jarPath); - } - - addClassesFromJar(jarPath); - } - - - } - - private static void addClassesFromJar(String jarPath) { - JarFile jarFile = null; - try { - jarFile = new JarFile(jarPath); - Enumeration entries = jarFile.entries(); - while (entries.hasMoreElements()) { - JarEntry jarEntry = entries.nextElement(); - if (!jarEntry.isDirectory()) { - String entryName = jarEntry.getName(); - if (entryName.endsWith(".class")) { - String className = entryName.replace("/", ".").substring(0, entryName.length() - 6); - addClass(classForName(className)); - } - } - } - } catch (IOException e1) { - } finally { - if (jarFile != null) { - try { - jarFile.close(); - } catch (IOException e) { - } - } - } - } - - - private static void addClassesFromClassPath(String classPath) { - - List classFileList = new ArrayList<>(); - scanClassFile(classFileList, classPath); - - for (File file : classFileList) { - - int start = classPath.length(); - int end = file.toString().length() - ".class".length(); - - String classFile = file.toString().substring(start + 1, end); - String className = classFile.replace(File.separator, "."); - - addClass(classForName(className)); - } - } - - private static void addClass(Class clazz) { - if (clazz != null && isNotExcludeClass(clazz.getName())) { - appClassesCache.add(clazz); - } - } - - //用于在进行 fatjar 打包时,提高性能 - private static boolean isNotExcludeClass(String clazzName) { - for (String prefix : scanClasses) { - if (clazzName.startsWith(prefix)) { - return true; - } - } - for (String prefix : excludeClasses) { - if (clazzName.startsWith(prefix)) { - return false; - } - } - return true; - } - - - private static void findClassPathsAndJarsByClassloader(Set jarPaths, Set classPaths, ClassLoader classLoader) { - try { - URL[] urls = null; - if (classLoader instanceof URLClassLoader) { - URLClassLoader ucl = (URLClassLoader) classLoader; - urls = ucl.getURLs(); - } - if (urls != null) { - for (URL url : urls) { - String path = url.getPath(); - path = URLDecoder.decode(path, "UTF-8"); - - // path : /d:/xxx - if (path.startsWith("/") && path.indexOf(":") == 2) { - path = path.substring(1); - } - - if (!path.toLowerCase().endsWith(".jar")) { - classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); - } else { - jarPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); - } - } - } - } catch (Exception ex) { - ex.printStackTrace(); - } - - ClassLoader parent = classLoader.getParent(); - if (parent != null) { - findClassPathsAndJarsByClassloader(jarPaths, classPaths, parent); - } - } - - private static void findClassPathsAndJarsByClassPath(Set jarPaths, Set classPaths) { - String classPath = System.getProperty("java.class.path"); - if (classPath == null || classPath.trim().length() == 0) { - return; - } - String[] classPathArray = classPath.split(File.pathSeparator); - if (classPathArray == null || classPathArray.length == 0) { - return; - } - for (String path : classPathArray) { - path = path.trim(); - - if (path.startsWith("./")) { - path = path.substring(2); - } - - if (path.startsWith("/") && path.indexOf(":") == 2) { - path = path.substring(1); - } - try { - if (!path.toLowerCase().endsWith(".jar") && !jarPaths.contains(path)) { - classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); - } else { - jarPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); - } - } catch (IOException e) { - } - } - } - - - private static boolean isIncludeJar(String path) { - - String jarName = new File(path).getName().toLowerCase(); - - for (String include : scanJars) { - if (jarName.startsWith(include)) { - return true; - } - } - - for (String exclude : excludeJars) { - if (jarName.startsWith(exclude)) { - return false; - } - } - - //from jre lib - if (path.contains("/jre/lib")) { - return false; - } - - //from java home - if (getJavaHome() != null - && path.startsWith(getJavaHome())) { - return false; - } - - return true; - } - - - @SuppressWarnings("unchecked") - private static Class classForName(String className) { - try { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - return Class.forName(className, false, cl); - } catch (Throwable ex) { - //ignore - } - return null; - } - - - private static void scanClassFile(List fileList, String path) { - File[] files = new File(path).listFiles(); - if (null == files || files.length == 0) { - return; - } - for (File file : files) { - if (file.isDirectory()) { - scanClassFile(fileList, file.getAbsolutePath()); - } else if (file.getName().endsWith(".class")) { - fileList.add(file); - } - } - } - - - private static String javaHome; - - private static String getJavaHome() { - if (javaHome == null) { - try { - String javaHomeString = System.getProperty("java.home"); - if (javaHomeString != null && javaHomeString.trim().length() > 0) { - javaHome = new File(javaHomeString, "..").getCanonicalPath().replace('\\', '/'); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - return javaHome; - } - -} +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.utils; + +import java.io.File; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.stream.Collectors; + +import io.jboot.app.config.JbootConfigManager; + +public class ClassScanner { + + private static final Set> appClassesCache = new HashSet<>(); + + public static final Set scanJars = new HashSet<>(); + public static final Set excludeJars = new HashSet<>(); + + public static final Set scanClasses = new HashSet<>(); + public static final Set excludeClasses = new HashSet<>(); + // dev模式打开扫描信息打印 + private static boolean printScannerInfoEnable = JbootConfigManager.me().isDevMode(); + + public static boolean isPrintScannerInfoEnable() { + return printScannerInfoEnable; + } + + public static void setPrintScannerInfoEnable(boolean printScannerInfoEnable) { + ClassScanner.printScannerInfoEnable = printScannerInfoEnable; + } + + + public static void addScanJarPrefix(String prefix) { + scanJars.add(prefix.toLowerCase().trim()); + } + + static { + scanJars.add("jboot"); + } + + + public static void addUnscanJarPrefix(String prefix) { + excludeJars.add(prefix.toLowerCase().trim()); + } + + static { + excludeJars.add("jfinal-"); + excludeJars.add("cos-"); + excludeJars.add("cglib-"); + excludeJars.add("undertow-"); + excludeJars.add("xnio-"); + excludeJars.add("javax."); + excludeJars.add("hikaricp-"); + excludeJars.add("druid-"); + excludeJars.add("mysql-"); + excludeJars.add("db2jcc-"); + excludeJars.add("db2jcc4-"); + excludeJars.add("ojdbc"); + excludeJars.add("junit-"); + excludeJars.add("junit5-"); + excludeJars.add("org.junit"); + excludeJars.add("hamcrest-"); + excludeJars.add("jboss-"); + excludeJars.add("motan-"); + excludeJars.add("commons-pool"); + excludeJars.add("commons-beanutils"); + excludeJars.add("commons-codec"); + excludeJars.add("commons-collections"); + excludeJars.add("commons-configuration"); + excludeJars.add("commons-lang"); + excludeJars.add("commons-logging"); + excludeJars.add("commons-io"); + excludeJars.add("commons-httpclient"); + excludeJars.add("commons-fileupload"); + excludeJars.add("commons-validator"); + excludeJars.add("commons-email"); + excludeJars.add("commons-text"); + excludeJars.add("commons-cli"); + excludeJars.add("commons-math"); + excludeJars.add("commons-jxpath"); + excludeJars.add("commons-compress"); + excludeJars.add("audience-"); + excludeJars.add("hessian-"); + excludeJars.add("metrics-"); + excludeJars.add("javapoet-"); + excludeJars.add("netty-"); + excludeJars.add("consul-"); + excludeJars.add("gson-"); + excludeJars.add("zookeeper-"); + excludeJars.add("slf4j-"); + excludeJars.add("fastjson-"); + excludeJars.add("guava-"); + excludeJars.add("failureaccess-"); + excludeJars.add("listenablefuture-"); + excludeJars.add("jsr305-"); + excludeJars.add("checker-qual-"); + excludeJars.add("error_prone_annotations-"); + excludeJars.add("j2objc-"); + excludeJars.add("animal-sniffer-"); + excludeJars.add("cron4j-"); + excludeJars.add("jedis-"); + excludeJars.add("lettuce-"); + excludeJars.add("reactor-"); + excludeJars.add("fst-"); + excludeJars.add("kryo-"); + excludeJars.add("jackson-"); + excludeJars.add("javassist-"); + excludeJars.add("objenesis-"); + excludeJars.add("reflectasm-"); + excludeJars.add("asm-"); + excludeJars.add("minlog-"); + excludeJars.add("jsoup-"); + excludeJars.add("ons-client-"); + excludeJars.add("amqp-client-"); + excludeJars.add("ehcache-"); + excludeJars.add("sharding-"); + excludeJars.add("snakeyaml-"); + excludeJars.add("groovy-"); + excludeJars.add("profiler-"); + excludeJars.add("joda-time-"); + excludeJars.add("shiro-"); + excludeJars.add("dubbo-"); + excludeJars.add("curator-"); + excludeJars.add("resteasy-"); + excludeJars.add("reactive-"); + excludeJars.add("validation-"); + excludeJars.add("httpclient-"); + excludeJars.add("httpcore-"); + excludeJars.add("httpmime-"); + excludeJars.add("jcip-"); + excludeJars.add("jcl-"); + excludeJars.add("microprofile-"); + excludeJars.add("org.osgi"); + excludeJars.add("zkclient-"); + excludeJars.add("jjwt-"); + excludeJars.add("okhttp-"); + excludeJars.add("okio-"); + excludeJars.add("zbus-"); + excludeJars.add("swagger-"); + excludeJars.add("j2cache-"); + excludeJars.add("caffeine-"); + excludeJars.add("jline-"); + excludeJars.add("qpid-"); + excludeJars.add("geronimo-"); + excludeJars.add("activation-"); + excludeJars.add("org.abego"); + excludeJars.add("antlr-"); + excludeJars.add("antlr4-"); + excludeJars.add("st4-"); + excludeJars.add("icu4j-"); + excludeJars.add("idea_rt"); + excludeJars.add("mrjtoolkit"); + excludeJars.add("logback-"); + excludeJars.add("log4j-"); + excludeJars.add("log4j2-"); + excludeJars.add("aliyun-java-sdk-"); + excludeJars.add("aliyun-sdk-"); + excludeJars.add("archaius-"); + excludeJars.add("aopalliance-"); + excludeJars.add("hdrhistogram-"); + excludeJars.add("jdom-"); + excludeJars.add("rxjava-"); + excludeJars.add("jersey-"); + excludeJars.add("stax-"); + excludeJars.add("stax2-"); + excludeJars.add("jettison-"); + excludeJars.add("commonmark-"); + excludeJars.add("jaxb-"); + excludeJars.add("json-20"); + excludeJars.add("jcseg-"); + excludeJars.add("lucene-"); + excludeJars.add("elasticsearch-"); + excludeJars.add("jopt-"); + excludeJars.add("httpasyncclient-"); + excludeJars.add("jna-"); + excludeJars.add("lang-mustache-client-"); + excludeJars.add("parent-join-client-"); + excludeJars.add("rank-eval-client-"); + excludeJars.add("aggs-matrix-stats-client-"); + excludeJars.add("t-digest-"); + excludeJars.add("compiler-"); + excludeJars.add("hppc-"); + excludeJars.add("libthrift-"); + excludeJars.add("seata-"); + excludeJars.add("eureka-"); + excludeJars.add("netflix-"); + excludeJars.add("nacos-"); + excludeJars.add("apollo-"); + excludeJars.add("guice-"); + excludeJars.add("servlet-"); + excludeJars.add("debugger-agent.jar"); + excludeJars.add("xpp3_min-"); + excludeJars.add("latency"); + excludeJars.add("micrometer-"); + excludeJars.add("xstream-"); + excludeJars.add("jsr311-"); + excludeJars.add("servo-"); + excludeJars.add("compactmap-"); + excludeJars.add("dexx-"); + excludeJars.add("spotbugs-"); + excludeJars.add("xmlpull-"); + excludeJars.add("shardingsphere-"); + excludeJars.add("sentinel-"); + excludeJars.add("spring-"); + excludeJars.add("simpleclient-"); + excludeJars.add("breeze-"); + excludeJars.add("config-"); + excludeJars.add("encrypt-core-"); + excludeJars.add("lombok-"); + excludeJars.add("hutool-"); + excludeJars.add("jakarta."); + excludeJars.add("protostuff-"); + excludeJars.add("poi-"); + excludeJars.add("easypoi-"); + excludeJars.add("ognl-"); + excludeJars.add("xmlbeans-"); + excludeJars.add("master-slave-core-"); + excludeJars.add("shadow-core-rewrite-"); + excludeJars.add("apiguardian-api-"); + excludeJars.add("opentest4j-"); + excludeJars.add("opentracing-"); + excludeJars.add("freemarker-"); + excludeJars.add("protobuf-"); + excludeJars.add("jdom2-"); + excludeJars.add("useragentutils-"); + excludeJars.add("common-io-"); + excludeJars.add("common-image-"); + excludeJars.add("common-lang-"); + excludeJars.add("imageio-"); + excludeJars.add("curvesapi-"); + excludeJars.add("myexcel-"); + excludeJars.add("oshi-"); + excludeJars.add("classmate-"); + excludeJars.add("hibernate-"); + excludeJars.add("aspectjweaver-"); + excludeJars.add("aspectjrt-"); + excludeJars.add("simpleclient_"); + excludeJars.add("rocketmq-"); + excludeJars.add("clickhouse-"); + excludeJars.add("lz4-"); + excludeJars.add("commons-digester-"); + excludeJars.add("opencc4j-"); + excludeJars.add("heaven-"); + excludeJars.add("tinypinyin-"); + excludeJars.add("jieba-"); + excludeJars.add("ahocorasick-"); + excludeJars.add("kotlin-"); + excludeJars.add("xml-apis-"); + excludeJars.add("dom4j-"); + excludeJars.add("ini4j-"); + excludeJars.add("cache-api-"); + excludeJars.add("byte-buddy-"); + excludeJars.add("jodd-"); + excludeJars.add("redisson-"); + excludeJars.add("bcprov-"); + excludeJars.add("pay-java-"); + excludeJars.add("alipay-sdk-"); + excludeJars.add("mapper-extras-"); + excludeJars.add("org.jacoco"); + excludeJars.add("jxl-"); + excludeJars.add("jxls-"); + excludeJars.add("jstl-"); + excludeJars.add("batik-"); + excludeJars.add("xmlsec-"); + excludeJars.add("pdfbox-"); + excludeJars.add("fontbox-"); + excludeJars.add("xk-time-"); + excludeJars.add("geohash-"); + excludeJars.add("ezmorph-"); + excludeJars.add("async-http-"); + excludeJars.add("jsr-"); + excludeJars.add("jsr250"); + excludeJars.add("pinyin4j"); + excludeJars.add("ijpay-"); + excludeJars.add("wildfly-"); + excludeJars.add("liquibase-"); + excludeJars.add("flowable-"); + excludeJars.add("mybatis-"); + excludeJars.add("ip2region-"); + excludeJars.add("java-uuid-generator-"); + excludeJars.add("quartz-"); + } + + + public static void addUnscanClassPrefix(String prefix) { + excludeClasses.add(prefix.trim()); + } + + static { + excludeClasses.add("java."); + excludeClasses.add("javax."); + excludeClasses.add("junit."); + excludeClasses.add("jline."); + excludeClasses.add("redis."); + excludeClasses.add("lombok."); + excludeClasses.add("oshi."); + excludeClasses.add("jodd."); + excludeClasses.add("javassist."); + excludeClasses.add("google."); + excludeClasses.add("com.jfinal."); + excludeClasses.add("com.aliyuncs."); + excludeClasses.add("com.carrotsearch."); + excludeClasses.add("org.aopalliance."); + excludeClasses.add("org.apache."); + excludeClasses.add("org.nustaq."); + excludeClasses.add("net.sf."); + excludeClasses.add("org.slf4j."); + excludeClasses.add("org.antlr."); + excludeClasses.add("org.jboss."); + excludeClasses.add("org.checkerframework."); + excludeClasses.add("org.jsoup."); + excludeClasses.add("org.objenesis."); + excludeClasses.add("org.ow2."); + excludeClasses.add("org.reactivest."); + excludeClasses.add("org.yaml."); + excludeClasses.add("org.checker"); + excludeClasses.add("org.codehaus"); + excludeClasses.add("org.commonmark"); + excludeClasses.add("org.jdom2."); + excludeClasses.add("org.aspectj."); + excludeClasses.add("org.hibernate."); + excludeClasses.add("org.ahocorasick."); + excludeClasses.add("org.lionsoul.jcseg."); + excludeClasses.add("org.ini4j."); + excludeClasses.add("org.jetbrains."); + excludeClasses.add("org.jacoco."); + excludeClasses.add("org.xnio."); + excludeClasses.add("org.bouncycastle."); + excludeClasses.add("org.elasticsearch."); + excludeClasses.add("org.hamcrest."); + excludeClasses.add("org.objectweb."); + excludeClasses.add("org.joda."); + excludeClasses.add("org.wildfly."); + excludeClasses.add("org.owasp."); + excludeClasses.add("aj.org."); + excludeClasses.add("ch.qos."); + excludeClasses.add("joptsimple."); + excludeClasses.add("com.alibaba.csp."); + excludeClasses.add("com.alibaba.nacos."); + excludeClasses.add("com.alibaba.druid."); + excludeClasses.add("com.alibaba.fastjson."); + excludeClasses.add("com.aliyun.open"); + excludeClasses.add("com.caucho"); + excludeClasses.add("com.codahale"); + excludeClasses.add("com.ctrip.framework.apollo"); + excludeClasses.add("com.ecwid."); + excludeClasses.add("com.esotericsoftware."); + excludeClasses.add("com.fasterxml."); + excludeClasses.add("com.github."); + excludeClasses.add("io.github."); + excludeClasses.add("com.google."); + excludeClasses.add("metrics_influxdb."); + excludeClasses.add("com.rabbitmq."); + excludeClasses.add("com.squareup."); + excludeClasses.add("com.sun."); + excludeClasses.add("com.typesafe."); + excludeClasses.add("com.weibo.api.motan."); + excludeClasses.add("com.zaxxer."); + excludeClasses.add("com.mysql."); + excludeClasses.add("com.papertrail."); + excludeClasses.add("com.egzosn."); + excludeClasses.add("com.alipay.api"); + excludeClasses.add("org.gjt."); + excludeClasses.add("org.fusesource."); + excludeClasses.add("org.redisson."); + excludeClasses.add("io.dropwizard"); + excludeClasses.add("io.prometheus"); + excludeClasses.add("io.jsonwebtoken"); + excludeClasses.add("io.lettuce"); + excludeClasses.add("reactor.adapter"); + excludeClasses.add("io.seata."); + excludeClasses.add("io.swagger."); + excludeClasses.add("io.undertow."); + excludeClasses.add("io.netty."); + excludeClasses.add("io.opentracing."); + excludeClasses.add("it.sauronsoftware"); + excludeClasses.add("net.oschina.j2cache"); + excludeClasses.add("net.bytebuddy"); + excludeClasses.add("cn.hutool."); + excludeClasses.add("com.dyuproject."); + excludeClasses.add("io.protostuff."); + excludeClasses.add("io.reactivex."); + excludeClasses.add("freemarker."); + excludeClasses.add("com.twelvemonkeys."); + excludeClasses.add("eu.bitwalker."); + excludeClasses.add("jstl."); + excludeClasses.add("jxl."); + excludeClasses.add("org.jxls"); + excludeClasses.add("org.kordamp"); + excludeClasses.add("org.mybatis"); + excludeClasses.add("org.lisonsoul"); + excludeClasses.add("org.flowable"); + } + + + public static void addScanClassPrefix(String prefix) { + scanClasses.add(prefix.toLowerCase().trim()); + } + + static { + scanClasses.add("io.jboot.support.shiro.directives"); + } + + static { + String scanJarPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.scanJarPrefix"); + if (scanJarPrefix != null) { + String[] prefixes = scanJarPrefix.split(","); + for (String prefix : prefixes) { + if (prefix != null && prefix.trim().length() > 0) { + addScanJarPrefix(prefix.trim()); + } + } + } + + String unScanJarPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.unScanJarPrefix"); + if (unScanJarPrefix != null) { + String[] prefixes = unScanJarPrefix.split(","); + for (String prefix : prefixes) { + if (prefix != null && prefix.trim().length() > 0) { + addUnscanJarPrefix(prefix.trim()); + } + } + } + + String unScanClassPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.unScanClassPrefix"); + if (unScanClassPrefix != null) { + String[] prefixes = unScanClassPrefix.split(","); + for (String prefix : prefixes) { + if (prefix != null && prefix.trim().length() > 0) { + addUnscanClassPrefix(prefix.trim()); + } + } + } + + String scanClassPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.scanClassPrefix"); + if (scanClassPrefix != null) { + String[] prefixes = scanClassPrefix.split(","); + for (String prefix : prefixes) { + if (prefix != null && prefix.trim().length() > 0) { + addScanClassPrefix(prefix.trim()); + } + } + } + + } + + public static List> scanSubClass(Class pclazz) { + return scanSubClass(pclazz, false); + } + + + public static List> scanSubClass(Class pclazz, boolean instantiable) { + initIfNecessary(); + List> classes = new ArrayList<>(); + findChildClasses(classes, pclazz, instantiable); + return classes; + } + + public static List scanClass() { + return scanClass(false); + } + + public static List scanClass(boolean isInstantiable) { + + initIfNecessary(); + + if (!isInstantiable) { + return new ArrayList<>(appClassesCache); + } + + return scanClass(ClassScanner::isInstantiable); + + } + + public static List scanClass(Predicate filter) { + + initIfNecessary(); + + return appClassesCache.stream() + .filter(filter) + .collect(Collectors.toList()); + + } + + public static void clearAppClassesCache() { + appClassesCache.clear(); + } + + + private static boolean isInstantiable(Class clazz) { + return !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()); + } + + + public static List scanClassByAnnotation(Class annotationClass, boolean instantiable) { + initIfNecessary(); + + List list = new ArrayList<>(); + for (Class clazz : appClassesCache) { + Annotation annotation = clazz.getAnnotation(annotationClass); + if (annotation == null) { + continue; + } + + if (instantiable && !isInstantiable(clazz)) { + continue; + } + + list.add(clazz); + } + + return list; + } + + private static void initIfNecessary() { + if (appClassesCache.isEmpty()) { + initAppClasses(); + } + } + + + private static void findChildClasses(List> classes, Class parent, boolean instantiable) { + for (Class clazz : appClassesCache) { + + if (!parent.isAssignableFrom(clazz)) { + continue; + } + + if (instantiable && !isInstantiable(clazz)) { + continue; + } + + classes.add(clazz); + } + } + + + private static void initAppClasses() { + + Set jarPaths = new HashSet<>(); + Set classPaths = new HashSet<>(); + + + // jdk8 及以下、 + // tomcat 容器、 + // jfinal-undertow、 + // 以上三种加载模式通过 classloader 获取 + findClassPathsAndJarsByClassloader(jarPaths, classPaths, ClassScanner.class.getClassLoader()); + + //jdk9+ 等其他方式通过 classpath 获取 + findClassPathsAndJarsByClassPath(jarPaths, classPaths); + + + String tomcatClassPath = null; + + for (String classPath : classPaths) { + //过滤tomcat自身的lib 以及 bin 下的jar + File tomcatApiJarFile = new File(classPath, "tomcat-api.jar"); + File tomcatJuliJarFile = new File(classPath, "tomcat-juli.jar"); + if (tomcatApiJarFile.exists() || tomcatJuliJarFile.exists()) { + tomcatClassPath = tomcatApiJarFile + .getParentFile() + .getParentFile().getAbsolutePath().replace('\\', '/'); + continue; + } + + if (isPrintScannerInfoEnable()) { + System.out.println("Jboot Scan ClassPath: " + classPath); + } + + addClassesFromClassPath(classPath); + } + + for (String jarPath : jarPaths) { + + //过滤 tomcat 的 jar,但是不能过滤 webapps 目录下的 + if (tomcatClassPath != null + && jarPath.startsWith(tomcatClassPath) + && !jarPath.contains("webapps")) { + continue; + } + + if (!isIncludeJar(jarPath)) { + continue; + } + + if (isPrintScannerInfoEnable()) { + System.out.println("Jboot Scan Jar: " + jarPath); + } + + addClassesFromJar(jarPath); + } + + + } + + private static void addClassesFromJar(String jarPath) { + JarFile jarFile = null; + try { + jarFile = new JarFile(jarPath); + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry jarEntry = entries.nextElement(); + if (jarEntry.isDirectory()) { + String entryName = jarEntry.getName(); + if (isPrintScannerInfoEnable()) { + System.out.println("Jboot Scan entryName: " + entryName); + } + + if (entryName.startsWith("BOOT-INF/classes/")) { + if (entryName.endsWith(".class")) { + String className = entryName.replace("/", ".").substring(0, entryName.length() - 6); + addClass(classForName(className)); + } + } + } + else { + String entryName = jarEntry.getName(); + if (entryName.endsWith(".class")) { + String className = entryName.replace("/", ".").substring(0, entryName.length() - 6); + addClass(classForName(className)); + } + else if (entryName.startsWith("BOOT-INF/lib/") && entryName.endsWith(".jar")) { + if (!isIncludeJar(entryName)) { + continue; + } + + if (isPrintScannerInfoEnable()) { + System.out.println("Jboot Scan Jar: " + entryName); + } + JarInputStream jarIS = new JarInputStream(jarFile + .getInputStream(jarEntry)); + + JarEntry innerEntry = jarIS.getNextJarEntry(); + while (innerEntry != null) { + if (!innerEntry.isDirectory()) { + String nestedEntryName = innerEntry.getName(); + if (nestedEntryName.endsWith(".class")) { + String className = nestedEntryName.replace("/", ".").substring(0, nestedEntryName.length() - 6); + addClass(classForName(className)); + } + } + innerEntry = jarIS.getNextJarEntry(); + } + if (jarIS != null) { + jarIS.close(); + } +// addClassesFromJar(nestedJarPath); + } + } + } + } catch (IOException e1) { + } finally { + if (jarFile != null) { + try { + jarFile.close(); + } catch (IOException e) { + } + } + } + } + +// public static void main(String[] args) { +// String filePath = "D:\\test\\springbootest.jar"; +// addClassesFromJar(filePath); +// } + + private static void addClassesFromClassPath(String classPath) { + + List classFileList = new ArrayList<>(); + scanClassFile(classFileList, classPath); + + for (File file : classFileList) { + + int start = classPath.length(); + int end = file.toString().length() - ".class".length(); + + String classFile = file.toString().substring(start + 1, end); + String className = classFile.replace(File.separator, "."); + + addClass(classForName(className)); + } + } + + private static void addClass(Class clazz) { + if (clazz != null && isNotExcludeClass(clazz.getName())) { + appClassesCache.add(clazz); + } + } + + //用于在进行 fatjar 打包时,提高性能 + private static boolean isNotExcludeClass(String clazzName) { + for (String prefix : scanClasses) { + if (clazzName.startsWith(prefix)) { + return true; + } + } + for (String prefix : excludeClasses) { + if (clazzName.startsWith(prefix)) { + return false; + } + } + return true; + } + + + private static void findClassPathsAndJarsByClassloader(Set jarPaths, Set classPaths, ClassLoader classLoader) { + try { + URL[] urls = null; + if (classLoader instanceof URLClassLoader) { + URLClassLoader ucl = (URLClassLoader) classLoader; + urls = ucl.getURLs(); + } + if (urls != null) { + for (URL url : urls) { + String path = url.getPath(); + path = URLDecoder.decode(path, "UTF-8"); + + // path : /d:/xxx + if (path.startsWith("/") && path.indexOf(":") == 2) { + path = path.substring(1); + } + + if (!path.toLowerCase().endsWith(".jar")) { + if(path.toLowerCase().endsWith("!/") || path.toLowerCase().endsWith("!")) { } + else{ + classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); + } + } else { + jarPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); + } + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + + ClassLoader parent = classLoader.getParent(); + if (parent != null) { + findClassPathsAndJarsByClassloader(jarPaths, classPaths, parent); + } + } + + private static void findClassPathsAndJarsByClassPath(Set jarPaths, Set classPaths) { + String classPath = System.getProperty("java.class.path"); + if (classPath == null || classPath.trim().length() == 0) { + return; + } + String[] classPathArray = classPath.split(File.pathSeparator); + if (classPathArray == null || classPathArray.length == 0) { + return; + } + for (String path : classPathArray) { + path = path.trim(); + + if (path.startsWith("./")) { + path = path.substring(2); + } + + if (path.startsWith("/") && path.indexOf(":") == 2) { + path = path.substring(1); + } + try { + if (!path.toLowerCase().endsWith(".jar") && !jarPaths.contains(path)) { + if (path.toLowerCase().endsWith("!/") || path.toLowerCase().endsWith("!")) {} + else{ + classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); + } + } else { + jarPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); + } + } catch (IOException e) { + } + } + } + + + private static boolean isIncludeJar(String path) { + + String jarName = new File(path).getName().toLowerCase(); + + for (String include : scanJars) { + if (jarName.startsWith(include)) { + return true; + } + } + + for (String exclude : excludeJars) { + if (jarName.startsWith(exclude)) { + return false; + } + } + + //from jre lib + if (path.contains("/jre/lib")) { + return false; + } + + //from java home + if (getJavaHome() != null + && path.startsWith(getJavaHome())) { + return false; + } + + return true; + } + + + @SuppressWarnings("unchecked") + private static Class classForName(String className) { + try { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + return Class.forName(className, false, cl); + } catch (Throwable ex) { + //ignore + } + return null; + } + + + private static void scanClassFile(List fileList, String path) { + File[] files = new File(path).listFiles(); + if (null == files || files.length == 0) { + return; + } + for (File file : files) { + if (file.isDirectory()) { + scanClassFile(fileList, file.getAbsolutePath()); + } else if (file.getName().endsWith(".class")) { + fileList.add(file); + } + } + } + + + private static String javaHome; + + private static String getJavaHome() { + if (javaHome == null) { + try { + String javaHomeString = System.getProperty("java.home"); + if (javaHomeString != null && javaHomeString.trim().length() > 0) { + javaHome = new File(javaHomeString, "..").getCanonicalPath().replace('\\', '/'); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + return javaHome; + } + +} -- Gitee From 0ae143be048a2154bd77b33f1816cd0f0f9faf4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E7=AB=8B=E5=88=9A?= Date: Fri, 18 Nov 2022 12:03:35 +0800 Subject: [PATCH 1862/1965] =?UTF-8?q?redis=E6=B6=88=E6=81=AF=E9=98=9F?= =?UTF-8?q?=E5=88=97=E6=94=AF=E6=8C=81=E5=A4=9A=E4=B8=AAdatabase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/components/mq/JbootmqBase.java | 470 +++++++++--------- .../jboot/components/mq/JbootmqManager.java | 250 +++++----- .../mq/redismq/JbootRedismqImpl.java | 287 ++++++----- .../java/io/jboot/utils/ClassScanner.java | 20 +- .../jboot/test/mq/redis/RedisMqReceiver1.java | 92 ++-- .../jboot/test/mq/redis/RedisMqReceiver2.java | 93 ++-- .../io/jboot/test/mq/redis/RedisMqSender.java | 103 ++-- 7 files changed, 677 insertions(+), 638 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index 6f39ba4f..4ed3093e 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -1,235 +1,235 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.components.mq; - -import com.jfinal.kit.LogKit; -import com.jfinal.log.Log; -import io.jboot.Jboot; -import io.jboot.components.serializer.JbootSerializer; -import io.jboot.utils.NamedThreadFactory; -import io.jboot.utils.StrUtil; - -import java.util.*; -import java.util.concurrent.*; - - -public abstract class JbootmqBase implements Jbootmq { - - private static final Log LOG = Log.getLog(JbootmqBase.class); - - protected final JbootmqConfig config; - - private List globalListeners = new CopyOnWriteArrayList<>(); - private Map> channelListeners = new ConcurrentHashMap<>(); - - protected Set channels = new HashSet<>(); - protected Set syncRecevieMessageChannels = new HashSet<>(); - protected JbootSerializer serializer; - - - private ExecutorService threadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, - 60L, TimeUnit.SECONDS, - new SynchronousQueue<>(), new NamedThreadFactory("jbootmq")); - - - public JbootmqBase(JbootmqConfig config) { - this.config = config; - String channelString = config.getChannel(); - if (StrUtil.isBlank(channelString)) { - return; - } - - this.channels.addAll(StrUtil.splitToSet(channelString, ",")); - - if (StrUtil.isNotBlank(config.getSyncRecevieMessageChannel())) { - this.syncRecevieMessageChannels.addAll(StrUtil.splitToSet(config.getSyncRecevieMessageChannel(), ",")); - } - } - - - @Override - public void addMessageListener(JbootmqMessageListener listener) { - globalListeners.add(listener); - } - - - @Override - public void addMessageListener(JbootmqMessageListener listener, String forChannel) { - String[] forChannels = forChannel.split(","); - for (String channel : forChannels) { - if (StrUtil.isNotBlank(channel)) { - addChannelListener(channel.trim(), listener); - } - } - } - - private synchronized void addChannelListener(String channel, JbootmqMessageListener listener) { - List listeners = channelListeners.get(channel); - if (listeners == null) { - listeners = new CopyOnWriteArrayList<>(); - channelListeners.put(channel, listeners); - } - listeners.add(listener); - channels.add(channel); - } - - - @Override - public void removeListener(JbootmqMessageListener listener) { - globalListeners.remove(listener); - for (List listeners : channelListeners.values()) { - listeners.remove(listener); - } - } - - @Override - public void removeAllListeners() { - globalListeners.clear(); - channelListeners.forEach((s, list) -> list.clear()); - channelListeners.clear(); - } - - - @Override - public Collection getGlobalListeners() { - return globalListeners; - } - - - @Override - public Collection getListenersByChannel(String channel) { - return channelListeners.get(channel); - } - - public void notifyListeners(String channel, Object message, MessageContext context) { - - boolean globalResult = notifyListeners(channel, message, context, globalListeners); - boolean channelResult = notifyListeners(channel, message, context, channelListeners.get(channel)); - - if (!globalResult && !channelResult) { - LOG.warn("Jboot has recevied mq message, But it has no listener to process. channel:" + - channel + " message:" + message); - } - } - - - protected boolean notifyListeners(String channel, Object message, MessageContext context, Collection listeners) { - if (listeners == null || listeners.size() == 0) { - return false; - } - - if (syncRecevieMessageChannels.contains(channel)) { - for (JbootmqMessageListener listener : listeners) { - try { - listener.onMessage(channel, message, context); - } catch (Throwable ex) { - LOG.warn("listener[" + listener.getClass().getName() + "] execute mq message is error. channel:" + - channel + " message:" + message); - } - } - } else { - for (JbootmqMessageListener listener : listeners) { - threadPool.execute(() -> { - listener.onMessage(channel, message, context); - }); - } - } - - return true; - } - - - public JbootSerializer getSerializer() { - if (serializer == null) { - serializer = StrUtil.isNotBlank(config.getSerializer()) - ? Jboot.getSerializer(config.getSerializer()) - : Jboot.getSerializer(); - } - return serializer; - } - - - protected boolean isStarted = false; - - @Override - public boolean startListening() { - if (isStarted) { - return true; - } - - if (channels == null || channels.isEmpty()) { - LogKit.warn("Jboot MQ started fail. because it's channels is empty, please config channels. " + - "MQ name: {}, type:{}", config.getName(), config.getType()); - return false; - } - - try { - isStarted = true; - onStartListening(); - } catch (Exception ex) { - LogKit.error("Jboot MQ start fail!", ex); - isStarted = false; - return false; - } - - return true; - } - - - @Override - public boolean stopListening() { - if (!isStarted) { - return true; - } - - try { - isStarted = false; - onStopListening(); - } catch (Exception ex) { - LogKit.error("Jboot MQ stop fail!", ex); - isStarted = true; - return false; - } - - return true; - } - - public boolean isStarted() { - return isStarted; - } - - protected abstract void onStartListening(); - - protected abstract void onStopListening(); - - - @Override - public JbootmqConfig getConfig() { - return config; - } - - public void setSerializer(JbootSerializer serializer) { - this.serializer = serializer; - } - - public ExecutorService getThreadPool() { - return threadPool; - } - - public void setThreadPool(ExecutorService threadPool) { - this.threadPool = threadPool; - } -} +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.mq; + +import com.jfinal.kit.LogKit; +import com.jfinal.log.Log; +import io.jboot.Jboot; +import io.jboot.components.serializer.JbootSerializer; +import io.jboot.utils.NamedThreadFactory; +import io.jboot.utils.StrUtil; + +import java.util.*; +import java.util.concurrent.*; + + +public abstract class JbootmqBase implements Jbootmq { + + private static final Log LOG = Log.getLog(JbootmqBase.class); + + protected final JbootmqConfig config; + + private List globalListeners = new CopyOnWriteArrayList<>(); + private Map> channelListeners = new ConcurrentHashMap<>(); + + protected Set channels = new HashSet<>(); + protected Set syncRecevieMessageChannels = new HashSet<>(); + protected JbootSerializer serializer; + + + private ExecutorService threadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue<>(), new NamedThreadFactory("jbootmq")); + + + public JbootmqBase(JbootmqConfig config) { + this.config = config; + String channelString = config.getChannel(); + if (StrUtil.isBlank(channelString)) { + return; + } + + this.channels.addAll(StrUtil.splitToSet(channelString, ",")); + + if (StrUtil.isNotBlank(config.getSyncRecevieMessageChannel())) { + this.syncRecevieMessageChannels.addAll(StrUtil.splitToSet(config.getSyncRecevieMessageChannel(), ",")); + } + } + + + @Override + public void addMessageListener(JbootmqMessageListener listener) { + globalListeners.add(listener); + } + + + @Override + public void addMessageListener(JbootmqMessageListener listener, String forChannel) { + String[] forChannels = forChannel.split(","); + for (String channel : forChannels) { + if (StrUtil.isNotBlank(channel)) { + addChannelListener(channel.trim(), listener); + } + } + } + + public final synchronized void addChannelListener(String channel, JbootmqMessageListener listener) { + List listeners = channelListeners.get(channel); + if (listeners == null) { + listeners = new CopyOnWriteArrayList<>(); + channelListeners.put(channel, listeners); + } + listeners.add(listener); + channels.add(channel); + } + + + @Override + public void removeListener(JbootmqMessageListener listener) { + globalListeners.remove(listener); + for (List listeners : channelListeners.values()) { + listeners.remove(listener); + } + } + + @Override + public void removeAllListeners() { + globalListeners.clear(); + channelListeners.forEach((s, list) -> list.clear()); + channelListeners.clear(); + } + + + @Override + public Collection getGlobalListeners() { + return globalListeners; + } + + + @Override + public Collection getListenersByChannel(String channel) { + return channelListeners.get(channel); + } + + public void notifyListeners(String channel, Object message, MessageContext context) { + + boolean globalResult = notifyListeners(channel, message, context, globalListeners); + boolean channelResult = notifyListeners(channel, message, context, channelListeners.get(channel)); + + if (!globalResult && !channelResult) { + LOG.warn("Jboot has recevied mq message, But it has no listener to process. channel:" + + channel + " message:" + message); + } + } + + + protected boolean notifyListeners(String channel, Object message, MessageContext context, Collection listeners) { + if (listeners == null || listeners.size() == 0) { + return false; + } + + if (syncRecevieMessageChannels.contains(channel)) { + for (JbootmqMessageListener listener : listeners) { + try { + listener.onMessage(channel, message, context); + } catch (Throwable ex) { + LOG.warn("listener[" + listener.getClass().getName() + "] execute mq message is error. channel:" + + channel + " message:" + message); + } + } + } else { + for (JbootmqMessageListener listener : listeners) { + threadPool.execute(() -> { + listener.onMessage(channel, message, context); + }); + } + } + + return true; + } + + + public JbootSerializer getSerializer() { + if (serializer == null) { + serializer = StrUtil.isNotBlank(config.getSerializer()) + ? Jboot.getSerializer(config.getSerializer()) + : Jboot.getSerializer(); + } + return serializer; + } + + + protected boolean isStarted = false; + + @Override + public boolean startListening() { + if (isStarted) { + return true; + } + + if (channels == null || channels.isEmpty()) { + LogKit.warn("Jboot MQ started fail. because it's channels is empty, please config channels. " + + "MQ name: {}, type:{}", config.getName(), config.getType()); + return false; + } + + try { + isStarted = true; + onStartListening(); + } catch (Exception ex) { + LogKit.error("Jboot MQ start fail!", ex); + isStarted = false; + return false; + } + + return true; + } + + + @Override + public boolean stopListening() { + if (!isStarted) { + return true; + } + + try { + isStarted = false; + onStopListening(); + } catch (Exception ex) { + LogKit.error("Jboot MQ stop fail!", ex); + isStarted = true; + return false; + } + + return true; + } + + public boolean isStarted() { + return isStarted; + } + + protected abstract void onStartListening(); + + protected abstract void onStopListening(); + + + @Override + public JbootmqConfig getConfig() { + return config; + } + + public void setSerializer(JbootSerializer serializer) { + this.serializer = serializer; + } + + public ExecutorService getThreadPool() { + return threadPool; + } + + public void setThreadPool(ExecutorService threadPool) { + this.threadPool = threadPool; + } +} diff --git a/src/main/java/io/jboot/components/mq/JbootmqManager.java b/src/main/java/io/jboot/components/mq/JbootmqManager.java index 011b638c..42a84618 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqManager.java +++ b/src/main/java/io/jboot/components/mq/JbootmqManager.java @@ -1,119 +1,131 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.components.mq; - -import io.jboot.Jboot; -import io.jboot.components.mq.aliyunmq.JbootAliyunmqImpl; -import io.jboot.components.mq.local.JbootLocalmqImpl; -import io.jboot.components.mq.qpidmq.JbootQpidmqImpl; -import io.jboot.components.mq.rabbitmq.JbootRabbitmqImpl; -import io.jboot.components.mq.redismq.JbootRedismqImpl; -import io.jboot.components.mq.rocketmq.JbootRocketmqImpl; -import io.jboot.core.spi.JbootSpiLoader; -import io.jboot.exception.JbootIllegalConfigException; -import io.jboot.utils.ClassUtil; -import io.jboot.utils.ConfigUtil; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - - -public class JbootmqManager { - - private static JbootmqManager manager; - - public static JbootmqManager me() { - if (manager == null) { - manager = ClassUtil.singleton(JbootmqManager.class); - } - return manager; - } - - private Map jbootmqMap = new ConcurrentHashMap<>(); - - public Jbootmq getJbootmq() { - return getJbootmq("default"); - } - - - public Jbootmq getJbootmq(String name) { - Jbootmq mq = jbootmqMap.get(name); - if (mq == null) { - synchronized (this) { - mq = jbootmqMap.get(name); - if (mq == null) { - Map configModels = ConfigUtil.getConfigModels(JbootmqConfig.class); - JbootmqConfig.TYPES.forEach(configModels::remove); - - configModels.putIfAbsent("default", Jboot.config(JbootmqConfig.class)); - - if (!configModels.containsKey(name)) { - throw new JbootIllegalConfigException("Please config \"jboot.mq." + name + ".type\" in your jboot.properties."); - } - - mq = getJbootmq(configModels.get(name)); - if (mq != null) { - jbootmqMap.put(name, mq); - } - } - } - } - return mq; - } - - public Jbootmq getJbootmq(JbootmqConfig config) { - return buildJbootmq(config); - } - - private Jbootmq buildJbootmq(JbootmqConfig config) { - if (config == null) { - throw new IllegalArgumentException("config must not be null"); - } - - if (!config.isConfigOk()) { - return null; - } - - switch (config.getType()) { - case JbootmqConfig.TYPE_REDIS: - return new JbootRedismqImpl(config); - case JbootmqConfig.TYPE_ALIYUNMQ: - return new JbootAliyunmqImpl(config); - case JbootmqConfig.TYPE_RABBITMQ: - return new JbootRabbitmqImpl(config); - case JbootmqConfig.TYPE_ROCKETMQ: - return new JbootRocketmqImpl(config); - case JbootmqConfig.TYPE_QPID: - return new JbootQpidmqImpl(config); - case JbootmqConfig.TYPE_ACTIVEMQ: - throw new RuntimeException("not finished!!!!"); - case JbootmqConfig.TYPE_LOCAL: - return new JbootLocalmqImpl(config); - default: - return JbootSpiLoader.load(Jbootmq.class, config.getType(), config); - } - - } - - - public void init() { - jbootmqMap.values().forEach(Jbootmq::startListening); - } - - public void stop() { - jbootmqMap.values().forEach(Jbootmq::stopListening); - } -} +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.mq; + +import io.jboot.Jboot; +import io.jboot.components.mq.aliyunmq.JbootAliyunmqImpl; +import io.jboot.components.mq.local.JbootLocalmqImpl; +import io.jboot.components.mq.qpidmq.JbootQpidmqImpl; +import io.jboot.components.mq.rabbitmq.JbootRabbitmqImpl; +import io.jboot.components.mq.redismq.JbootRedismqImpl; +import io.jboot.components.mq.rocketmq.JbootRocketmqImpl; +import io.jboot.core.spi.JbootSpiLoader; +import io.jboot.exception.JbootIllegalConfigException; +import io.jboot.utils.ClassUtil; +import io.jboot.utils.ConfigUtil; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +public class JbootmqManager { + + private static JbootmqManager manager; + + public static JbootmqManager me() { + if (manager == null) { + manager = ClassUtil.singleton(JbootmqManager.class); + } + return manager; + } + + private Map jbootmqMap = new ConcurrentHashMap<>(); + + public Jbootmq getJbootmq() { + return getJbootmq("default"); + } + + + public Jbootmq getJbootmq(String name) { + Jbootmq mq = jbootmqMap.get(name); + if (mq == null) { + synchronized (this) { + mq = jbootmqMap.get(name); + if (mq == null) { + Map configModels = ConfigUtil.getConfigModels(JbootmqConfig.class); + JbootmqConfig.TYPES.forEach(configModels::remove); + + configModels.putIfAbsent("default", Jboot.config(JbootmqConfig.class)); + + JbootmqConfig mqConfig = null; + if (!configModels.containsKey(name)) { + for (JbootmqConfig config : configModels.values()) { + if (name.equals(config.getTypeName())) { + mqConfig = config; + break; + } + } + if (mqConfig == null) { + throw new JbootIllegalConfigException("Please config \"jboot.mq.other" + name + ".type\" in your jboot.properties."); + } + } + else { + mqConfig = configModels.get(name); + } + + mq = getJbootmq(mqConfig); + if (mq != null) { + jbootmqMap.put(name, mq); + } + } + } + } + return mq; + } + + public Jbootmq getJbootmq(JbootmqConfig config) { + return buildJbootmq(config); + } + + private Jbootmq buildJbootmq(JbootmqConfig config) { + if (config == null) { + throw new IllegalArgumentException("config must not be null"); + } + + if (!config.isConfigOk()) { + return null; + } + + switch (config.getType()) { + case JbootmqConfig.TYPE_REDIS: + return new JbootRedismqImpl(config); + case JbootmqConfig.TYPE_ALIYUNMQ: + return new JbootAliyunmqImpl(config); + case JbootmqConfig.TYPE_RABBITMQ: + return new JbootRabbitmqImpl(config); + case JbootmqConfig.TYPE_ROCKETMQ: + return new JbootRocketmqImpl(config); + case JbootmqConfig.TYPE_QPID: + return new JbootQpidmqImpl(config); + case JbootmqConfig.TYPE_ACTIVEMQ: + throw new RuntimeException("not finished!!!!"); + case JbootmqConfig.TYPE_LOCAL: + return new JbootLocalmqImpl(config); + default: + return JbootSpiLoader.load(Jbootmq.class, config.getType(), config); + } + + } + + + public void init() { + jbootmqMap.values().forEach(Jbootmq::startListening); + } + + public void stop() { + jbootmqMap.values().forEach(Jbootmq::stopListening); + } +} diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java index 6153888d..f1249cd3 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java @@ -1,136 +1,151 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.components.mq.redismq; - -import com.jfinal.log.Log; -import io.jboot.Jboot; -import io.jboot.components.mq.Jbootmq; -import io.jboot.components.mq.JbootmqBase; -import io.jboot.components.mq.JbootmqConfig; -import io.jboot.exception.JbootIllegalConfigException; -import io.jboot.support.redis.JbootRedis; -import io.jboot.support.redis.JbootRedisManager; -import io.jboot.utils.ConfigUtil; -import io.jboot.utils.StrUtil; -import redis.clients.jedis.BinaryJedisPubSub; - -import java.util.Map; - - -public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { - - private static final Log LOG = Log.getLog(JbootRedismqImpl.class); - - private JbootRedis redis; - private Thread dequeueThread; - private BinaryJedisPubSub jedisPubSub; - private long interval = 100L; - - public JbootRedismqImpl(JbootmqConfig config) { - super(config); - - JbootRedismqConfig redisConfig = null; - String typeName = config.getTypeName(); - if (StrUtil.isNotBlank(typeName)) { - Map configModels = ConfigUtil.getConfigModels(JbootRedismqConfig.class); - if (!configModels.containsKey(typeName)) { - throw new JbootIllegalConfigException("Please config \"jboot.mq.redis." + typeName + ".host\" in your jboot.properties."); - } - redisConfig = configModels.get(typeName); - } else { - redisConfig = Jboot.config(JbootRedismqConfig.class); - } - - if (redisConfig.isConfigOk()) { - redis = JbootRedisManager.me().getRedis(redisConfig); - } else { - redis = Jboot.getRedis(); - } - - if (redis == null) { - throw new JbootIllegalConfigException("can not use redis mq (redis mq is default), " + - "please config jboot.redis.host=your-host , or use other mq component. "); - } - } - - @Override - protected void onStartListening() { - - String[] channels = this.channels.toArray(new String[]{}); - jedisPubSub = new BinaryJedisPubSub() { - @Override - public void onMessage(byte[] channel, byte[] message) { - notifyListeners(redis.bytesToKey(channel), getSerializer().deserialize(message) - , new RedismqMessageContext(JbootRedismqImpl.this)); - } - }; - - redis.subscribe(jedisPubSub, redis.keysToBytesArray(channels)); - - dequeueThread = new Thread(this, "redis-dequeue-thread"); - dequeueThread.start(); - } - - @Override - protected void onStopListening() { - if (jedisPubSub != null) { - jedisPubSub.unsubscribe(); - } - dequeueThread.interrupt(); - } - - - @Override - public void enqueue(Object message, String toChannel) { - redis.lpush(toChannel, message); - } - - - @Override - public void publish(Object message, String toChannel) { - redis.publish(redis.keyToBytes(toChannel), getSerializer().serialize(message)); - } - - - @Override - public void run() { - while (isStarted) { - try { - doExecuteDequeue(); - Thread.sleep(interval); - } catch (Exception ex) { - LOG.error(ex.toString(), ex); - } - } - } - - public void doExecuteDequeue() { - for (String channel : this.channels) { - Object data = redis.rpop(channel); - if (data != null) { - notifyListeners(channel, data, new RedismqMessageContext(JbootRedismqImpl.this)); - } - } - } - - public long getInterval() { - return interval; - } - - public void setInterval(long interval) { - this.interval = interval; - } -} +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.components.mq.redismq; + +import com.jfinal.log.Log; +import io.jboot.Jboot; +import io.jboot.components.mq.Jbootmq; +import io.jboot.components.mq.JbootmqBase; +import io.jboot.components.mq.JbootmqConfig; +import io.jboot.components.mq.JbootmqMessageListener; +import io.jboot.exception.JbootIllegalConfigException; +import io.jboot.support.redis.JbootRedis; +import io.jboot.support.redis.JbootRedisManager; +import io.jboot.utils.ConfigUtil; +import io.jboot.utils.StrUtil; +import redis.clients.jedis.BinaryJedisPubSub; + +import java.util.HashMap; +import java.util.Map; + + +public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { + + private static final Log LOG = Log.getLog(JbootRedismqImpl.class); + + private JbootRedis redis; + private Thread dequeueThread; + private BinaryJedisPubSub jedisPubSub; + private long interval = 100L; + + private Integer database = 0; + + public JbootRedismqImpl(JbootmqConfig config) { + super(config); + + JbootRedismqConfig redisConfig = null; + String typeName = config.getTypeName(); + if (StrUtil.isNotBlank(typeName)) { + Map configModels = ConfigUtil.getConfigModels(JbootRedismqConfig.class); + if (!configModels.containsKey(typeName)) { + throw new JbootIllegalConfigException("Please config \"jboot.mq.redis." + typeName + ".host\" in your jboot.properties."); + } + redisConfig = configModels.get(typeName); + } else { + redisConfig = Jboot.config(JbootRedismqConfig.class); + } + + database = redisConfig.getDatabase(); + + if (redisConfig.isConfigOk()) { + redis = JbootRedisManager.me().getRedis(redisConfig); + } else { + redis = Jboot.getRedis(); + } + + if (redis == null) { + throw new JbootIllegalConfigException("can not use redis mq (redis mq is default), " + + "please config jboot.redis.host=your-host , or use other mq component. "); + } + } + + private Map outterChannelMap = new HashMap<>(); + + @Override + protected void onStartListening() { + String[] channels = this.channels.toArray(new String[]{}); + jedisPubSub = new BinaryJedisPubSub() { + @Override + public void onMessage(byte[] channel, byte[] message) { + String thisChannel = redis.bytesToKey(channel); + String realChannel = outterChannelMap.get(thisChannel); + if (realChannel == null) { + LOG.warn("Jboot has recevied mq message, But it has no listener to process. channel:" + thisChannel); + } + notifyListeners(realChannel, getSerializer().deserialize(message) + , new RedismqMessageContext(JbootRedismqImpl.this)); + } + }; + + for (int i = 0; i< channels.length; i++) { + outterChannelMap.put(channels[i] + "_" + database, channels[i]); + channels[i] = channels[i] + "_" + database; + } + redis.subscribe(jedisPubSub, redis.keysToBytesArray(channels)); + + dequeueThread = new Thread(this, "redis-dequeue-thread"); + dequeueThread.start(); + } + + @Override + protected void onStopListening() { + if (jedisPubSub != null) { + jedisPubSub.unsubscribe(); + } + dequeueThread.interrupt(); + } + + + @Override + public void enqueue(Object message, String toChannel) { + redis.lpush(toChannel + "_" + database, message); + } + + + @Override + public void publish(Object message, String toChannel) { + redis.publish(redis.keyToBytes(toChannel + "_" + database), getSerializer().serialize(message)); + } + + @Override + public void run() { + while (isStarted) { + try { + doExecuteDequeue(); + Thread.sleep(interval); + } catch (Exception ex) { + LOG.error(ex.toString(), ex); + } + } + } + + public void doExecuteDequeue() { + for (String channel : this.channels) { + Object data = redis.lpop(channel + "_" + database); + if (data != null) { + notifyListeners(channel, data, new RedismqMessageContext(JbootRedismqImpl.this)); + } + } + } + + public long getInterval() { + return interval; + } + + public void setInterval(long interval) { + this.interval = interval; + } +} diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 91f8d6f6..03cb05da 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -44,7 +44,7 @@ public class ClassScanner { public static final Set scanClasses = new HashSet<>(); public static final Set excludeClasses = new HashSet<>(); - // dev模式打开扫描信息打印 + // dev模式打开扫描信息打印 private static boolean printScannerInfoEnable = JbootConfigManager.me().isDevMode(); public static boolean isPrintScannerInfoEnable() { @@ -669,10 +669,10 @@ public class ClassScanner { innerEntry = jarIS.getNextJarEntry(); } if (jarIS != null) { - jarIS.close(); + jarIS.close(); } -// addClassesFromJar(nestedJarPath); - } +// addClassesFromJar(nestedJarPath); + } } } } catch (IOException e1) { @@ -687,8 +687,8 @@ public class ClassScanner { } // public static void main(String[] args) { -// String filePath = "D:\\test\\springbootest.jar"; -// addClassesFromJar(filePath); +// String filePath = "D:\\test\\springbootest.jar"; +// addClassesFromJar(filePath); // } private static void addClassesFromClassPath(String classPath) { @@ -748,10 +748,10 @@ public class ClassScanner { } if (!path.toLowerCase().endsWith(".jar")) { - if(path.toLowerCase().endsWith("!/") || path.toLowerCase().endsWith("!")) { } - else{ + if(path.toLowerCase().endsWith("!/") || path.toLowerCase().endsWith("!")) { } + else{ classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); - } + } } else { jarPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); } @@ -788,7 +788,7 @@ public class ClassScanner { } try { if (!path.toLowerCase().endsWith(".jar") && !jarPaths.contains(path)) { - if (path.toLowerCase().endsWith("!/") || path.toLowerCase().endsWith("!")) {} + if (path.toLowerCase().endsWith("!/") || path.toLowerCase().endsWith("!")) {} else{ classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); } diff --git a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java index 9dd6788b..78f6dd89 100644 --- a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java +++ b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver1.java @@ -1,45 +1,47 @@ -package io.jboot.test.mq.redis; - - -import io.jboot.Jboot; -import io.jboot.app.JbootApplication; -import io.jboot.components.mq.MessageContext; -import io.jboot.components.mq.JbootmqMessageListener; - -public class RedisMqReceiver1 { - - public static void main(String[] args) { - - //Undertow端口号配置 - JbootApplication.setBootArg("undertow.port", "8001"); - - //设置 mq 的相关信息 - JbootApplication.setBootArg("jboot.mq.type", "redis"); - JbootApplication.setBootArg("jboot.mq.channel", "channel1,channel2,myChannel"); - JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); - - - //添加监听 - Jboot.getMq().addMessageListener(new JbootmqMessageListener() { - @Override - public void onMessage(String channel, Object message, MessageContext context) { - System.out.println("Receive msg: " + message + ", from channel: " + channel); - } - }); - - // 只监听 myChannel 这个通道 - Jboot.getMq().addMessageListener(new JbootmqMessageListener() { - @Override - public void onMessage(String channel, Object message, MessageContext context) { - System.out.println("Receive msg: " + message + ", from channel: " + channel); - } - },"myChannel"); - - - //启动应用程序 - JbootApplication.run(args); - - - System.out.println("RedisMqReceiver1 started."); - } -} +package io.jboot.test.mq.redis; + + +import io.jboot.Jboot; +import io.jboot.app.JbootApplication; +import io.jboot.components.mq.MessageContext; +import io.jboot.components.mq.JbootmqMessageListener; + +public class RedisMqReceiver1 { + + public static void main(String[] args) { + + //Undertow端口号配置 + JbootApplication.setBootArg("undertow.port", "8001"); + + //设置 mq 的相关信息 + JbootApplication.setBootArg("jboot.mq.type", "redis"); + JbootApplication.setBootArg("jboot.mq.channel", "channel1,channel2,myChannel"); + JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); + JbootApplication.setBootArg("jboot.mq.redis.port", 6379); + JbootApplication.setBootArg("jboot.mq.redis.database", 9); + JbootApplication.setBootArg("jboot.mq.redis.password", ""); + + //添加监听 + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, MessageContext context) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } + }); + + // 只监听 myChannel 这个通道 + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, MessageContext context) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } + },"myChannel"); + + + //启动应用程序 + JbootApplication.run(args); + Jboot.getMq().startListening(); + + System.out.println("RedisMqReceiver1 started."); + } +} diff --git a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java index 2d62864b..052e09cd 100644 --- a/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java +++ b/src/test/java/io/jboot/test/mq/redis/RedisMqReceiver2.java @@ -1,47 +1,46 @@ -package io.jboot.test.mq.redis; - - -import io.jboot.Jboot; -import io.jboot.app.JbootApplication; -import io.jboot.components.mq.MessageContext; -import io.jboot.components.mq.JbootmqMessageListener; - -public class RedisMqReceiver2 { - - public static void main(String[] args) { - - //Undertow端口号配置 - JbootApplication.setBootArg("undertow.port", "8002"); - - //设置 mq 的相关信息 - JbootApplication.setBootArg("jboot.mq.type", "redis"); - JbootApplication.setBootArg("jboot.mq.channel", "channel1,channel2,myChannel"); - JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); - - - //添加监听 - Jboot.getMq().addMessageListener(new JbootmqMessageListener() { - @Override - public void onMessage(String channel, Object message, MessageContext context) { - System.out.println("Receive msg: " + message + ", from channel: " + channel); - } - }); - - // 只监听 myChannel 这个通道 - Jboot.getMq().addMessageListener(new JbootmqMessageListener() { - @Override - public void onMessage(String channel, Object message, MessageContext context) { - System.out.println("Receive msg: " + message + ", from channel: " + channel); - } - },"myChannel"); - - - - //启动应用程序 - JbootApplication.run(args); - - - - System.out.println("RedisMqReceiver1 started."); - } -} +package io.jboot.test.mq.redis; + + +import io.jboot.Jboot; +import io.jboot.app.JbootApplication; +import io.jboot.components.mq.MessageContext; +import io.jboot.components.mq.JbootmqMessageListener; + +public class RedisMqReceiver2 { + + public static void main(String[] args) { + + //Undertow端口号配置 + JbootApplication.setBootArg("undertow.port", "8002"); + + //设置 mq 的相关信息 + JbootApplication.setBootArg("jboot.mq.type", "redis"); + JbootApplication.setBootArg("jboot.mq.channel", "channel1,channel2,myChannel"); + JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); + JbootApplication.setBootArg("jboot.mq.redis.port", 6379); + JbootApplication.setBootArg("jboot.mq.redis.database", 10); + + //添加监听 + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, MessageContext context) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } + }); + + // 只监听 myChannel 这个通道 + Jboot.getMq().addMessageListener(new JbootmqMessageListener() { + @Override + public void onMessage(String channel, Object message, MessageContext context) { + System.out.println("Receive msg: " + message + ", from channel: " + channel); + } + },"myChannel"); + + //启动应用程序 + JbootApplication.run(args); + + Jboot.getMq().startListening(); + + System.out.println("RedisMqReceiver2 started."); + } +} diff --git a/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java b/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java index e71d6296..f47458f9 100644 --- a/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java +++ b/src/test/java/io/jboot/test/mq/redis/RedisMqSender.java @@ -1,46 +1,57 @@ -package io.jboot.test.mq.redis; - - -import io.jboot.Jboot; -import io.jboot.app.JbootApplication; - -public class RedisMqSender { - - public static void main(String[] args) throws InterruptedException { - - //Undertow端口号配置 - JbootApplication.setBootArg("undertow.port", "8659"); - - //设置 mq 的相关信息 - JbootApplication.setBootArg("jboot.mq.type", "redis"); - JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); - - JbootApplication.setBootArg("jboot.mq.type", "redis"); - JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); - -// JbootApplication.setBootArg("jboot.mq.other1.type", "rocketmq"); -// JbootApplication.setBootArg("jboot.mq.other1.channel", "channel1,channel2,myChannel"); -// JbootApplication.setBootArg("jboot.mq.other1.typeName", "rktmq"); -// JbootApplication.setBootArg("jboot.mq.rocket.rktmq.namesrvAddr", "127.0.0.1:9876"); - - //启动应用程序 - JbootApplication.run(args); - - int count = 10; - for (int i = 0; i < count; i++) { - - -// Jboot.getMq().publish("message from RedisMqSender", "channel1"); -// Jboot.getMq().publish("message from RedisMqSender", "channel2"); -// Jboot.getMq().publish("message from RedisMqSender", "myChannel"); - - Jboot.getMq().enqueue("message " + i, "channel1"); - -// Thread.sleep(2000); - System.out.println("jboot mq publish success..."); - } - - } - - -} +package io.jboot.test.mq.redis; + + +import io.jboot.Jboot; +import io.jboot.app.JbootApplication; + +public class RedisMqSender { + + public static void main(String[] args) throws InterruptedException { + + //Undertow端口号配置 + JbootApplication.setBootArg("undertow.port", "8659"); + + //设置 mq 的相关信息 + JbootApplication.setBootArg("jboot.mq.type", "redis"); + JbootApplication.setBootArg("jboot.mq.redis.host", "127.0.0.1"); + JbootApplication.setBootArg("jboot.mq.redis.port", 6379); + JbootApplication.setBootArg("jboot.mq.redis.database", 9); + JbootApplication.setBootArg("jboot.mq.redis.password", ""); + + JbootApplication.setBootArg("jboot.mq.other1.type", "redis"); + JbootApplication.setBootArg("jboot.mq.other1.typeName", "test"); + //JbootApplication.setBootArg("jboot.mq.other1.channel", "channel1,channel2,myChannel"); + JbootApplication.setBootArg("jboot.mq.redis.test.host", "127.0.0.1"); + JbootApplication.setBootArg("jboot.mq.redis.test.port", 6379); + JbootApplication.setBootArg("jboot.mq.redis.test.database", 10); + JbootApplication.setBootArg("jboot.mq.redis.test.password", ""); + + //启动应用程序 + JbootApplication.run(args); + + int count = 10; + for (int i = 0; i < count; i++) { + Jboot.getMq().publish("message from RedisMqSender", "channel1"); + Jboot.getMq().publish("message from RedisMqSender", "channel2"); + Jboot.getMq().publish("message from RedisMqSender", "myChannel"); + + Jboot.getMq().enqueue("message " + i, "channel1"); + + Thread.sleep(1000); + System.out.println("jboot mq publish success..."); + } + + for (int i = 0; i < count; i++) { + Jboot.getMq("test").publish("message from RedisMqSender", "channel1"); + Jboot.getMq("test").publish("message from RedisMqSender", "channel2"); + Jboot.getMq("test").publish("message from RedisMqSender", "myChannel"); + + Jboot.getMq("test").enqueue("message " + i, "channel1"); + + Thread.sleep(1000); + System.out.println("jboot mq publish success..."); + } + } + + +} -- Gitee From 05a7a38079ae9c00630d747d5558ed2a3ee7b53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 25 Nov 2022 16:35:04 +0800 Subject: [PATCH 1863/1965] v3.17.1 snapshot --- .../java/io/jboot/core/JbootCoreConfig.java | 3 ++ .../attachment/SecurityFileNamePolicy.java | 47 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/main/java/io/jboot/web/attachment/SecurityFileNamePolicy.java diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index 93e54b99..cb8eb525 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -57,6 +57,7 @@ import io.jboot.utils.*; import io.jboot.web.JbootActionMapping; import io.jboot.web.JbootWebConfig; import io.jboot.web.PathVariableActionMapping; +import io.jboot.web.attachment.SecurityFileNamePolicy; import io.jboot.web.converter.ArrayConverters; import io.jboot.web.converter.TypeConverterFunc; import io.jboot.web.attachment.AttachmentHandler; @@ -148,6 +149,8 @@ public class JbootCoreConfig extends JFinalConfig { @Override public void configConstant(Constants constants) { + SecurityFileNamePolicy.init(); + JbootAppListenerManager.me().onConstantConfigBefore(constants); constants.setRenderFactory(JbootRenderFactory.me()); diff --git a/src/main/java/io/jboot/web/attachment/SecurityFileNamePolicy.java b/src/main/java/io/jboot/web/attachment/SecurityFileNamePolicy.java new file mode 100644 index 00000000..35f0f7bb --- /dev/null +++ b/src/main/java/io/jboot/web/attachment/SecurityFileNamePolicy.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.web.attachment; + +import com.jfinal.upload.MultipartRequest; + +import com.oreilly.servlet.multipart.DefaultFileRenamePolicy; +import io.jboot.utils.ReflectUtil; + +import java.io.File; + +public class SecurityFileNamePolicy extends DefaultFileRenamePolicy { + + @Override + public File rename(File f) { + String name = f.getName(); + + int lastIndexOf = name.lastIndexOf("."); + if (lastIndexOf > 0) { + String suffix = name.substring(lastIndexOf).toLowerCase(); + if (".jsp".equals(suffix) || ".jspx".equals(suffix)) { + File safeFile = new File(f.getParentFile(), name + ".keepsafe"); + return super.rename(safeFile); + } + } + + return super.rename(f); + } + + + public static void init() { + ReflectUtil.setStaticFieldValue(MultipartRequest.class, "fileRenamePolicy", new SecurityFileNamePolicy()); + } +} -- Gitee From 63cd33643437c8a437a7660d7e003fb10c08f252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 25 Nov 2022 17:44:50 +0800 Subject: [PATCH 1864/1965] v3.17.1 snapshot --- .../java/io/jboot/web/attachment/SecurityFileNamePolicy.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/web/attachment/SecurityFileNamePolicy.java b/src/main/java/io/jboot/web/attachment/SecurityFileNamePolicy.java index 35f0f7bb..1e47ab37 100644 --- a/src/main/java/io/jboot/web/attachment/SecurityFileNamePolicy.java +++ b/src/main/java/io/jboot/web/attachment/SecurityFileNamePolicy.java @@ -30,9 +30,9 @@ public class SecurityFileNamePolicy extends DefaultFileRenamePolicy { int lastIndexOf = name.lastIndexOf("."); if (lastIndexOf > 0) { - String suffix = name.substring(lastIndexOf).toLowerCase(); + String suffix = name.substring(lastIndexOf).toLowerCase().trim(); if (".jsp".equals(suffix) || ".jspx".equals(suffix)) { - File safeFile = new File(f.getParentFile(), name + ".keepsafe"); + File safeFile = new File(f.getParentFile(), name + "_unsafe"); return super.rename(safeFile); } } -- Gitee From 26e953a13dbca76eaad71cde11191baac8570fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 25 Nov 2022 17:45:01 +0800 Subject: [PATCH 1865/1965] v3.17.1 snapshot --- .../java/io/jboot/web/handler/JbootActionHandler.java | 9 +++++++++ .../io/jboot/web/handler/PathVariableActionHandler.java | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index e32d7b83..54cf3143 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -91,6 +91,10 @@ public class JbootActionHandler extends ActionHandler { @Override public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { if (target.lastIndexOf('.') != -1) { + if (isJspTarget(target)) { + isHandled[0] = true; + renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); + } return; } @@ -158,6 +162,11 @@ public class JbootActionHandler extends ActionHandler { } } + protected boolean isJspTarget(String target) { + return target.toLowerCase().contains(".jsp"); + } + + protected void doAfterRender(Action action, Controller controller) { } diff --git a/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java b/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java index cafe3128..e83daea9 100644 --- a/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java +++ b/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java @@ -50,6 +50,10 @@ public class PathVariableActionHandler extends JbootActionHandler { @Override public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { if (target.lastIndexOf('.') != -1) { + if (isJspTarget(target)){ + isHandled[0] = true; + renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); + } return; } -- Gitee From f5e6151d623f877cf1c80b1ba975c8c709c51a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 25 Nov 2022 17:49:03 +0800 Subject: [PATCH 1866/1965] upgrade dependencies --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7eba30f8..adef60d0 100644 --- a/pom.xml +++ b/pom.xml @@ -47,7 +47,7 @@ 3.4 3.3.0 4.0.3 - 2.14.0 + 2.14.1 5.1.49 2.2.19.Final 1.7.36 @@ -72,7 +72,7 @@ 3.14.0.Final 1.5.1 1.1.8 - 4.2.12 + 4.2.13 1.8 1.8 -- Gitee From 88c32f58310fc3049fb588be4e06eef129b6ee45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 25 Nov 2022 17:52:39 +0800 Subject: [PATCH 1867/1965] v3.17.1 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index bae81b56..50c872fd 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.17.0 + 3.17.1 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index aeb470f3..61b7072b 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.17.0 + 3.17.1 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 45d621cc..812098e7 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.17.0 + 3.17.1 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.17.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='3.17.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index aa60cac0..3c98decb 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.17.0', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='3.17.1', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.17.0/jboot-3.17.0.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.17.1/jboot-3.17.1.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 8d19992a..4319c02c 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.17.0 + 3.17.1 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index b7b75e13..3a3db705 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.17.0"; + public static String VERSION = "3.17.1"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 88cb050ca4eaa8ca2a247e38f28e245bea327829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 25 Nov 2022 17:52:43 +0800 Subject: [PATCH 1868/1965] v3.17.1 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ pom.xml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 479a7dcf..99e340e4 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.17.0 + 3.17.1 ``` diff --git a/changes.txt b/changes.txt index ac1a251b..076243aa 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v3.17.1 2022-11-13: +优化:Columns.java 对空条件的判断 +优化:ClassScanner.java 支持扫描 jar 包里的 jar 包,感谢 @陈立刚 +优化:Redis 消息队列设置支持多个 database,感谢 @陈立刚 + + + jboot v3.17.0 2022-11-12: 新增:自定义 Controller 缓存刷新 key 的功能 优化:Jboot MQ 在启动和停止的时候,不对已启动进行错误抛出,方便多模块可以自由启动或停止 diff --git a/pom.xml b/pom.xml index adef60d0..7db0b3a9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.17.1-SNAPSHOT + 3.17.1 jar jboot -- Gitee From 77757174344f34d5cff0f86ec19bcae39d622ce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 25 Nov 2022 17:54:23 +0800 Subject: [PATCH 1869/1965] v3.17.1 release (^.^)YYa!! --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7db0b3a9..e2468f89 100644 --- a/pom.xml +++ b/pom.xml @@ -542,7 +542,7 @@ io.dropwizard.metrics metrics-servlets - ${metrics.version} + 4.2.12 -- Gitee From 9261674867a69813a42309f06715c43625f69d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 25 Nov 2022 18:21:08 +0800 Subject: [PATCH 1870/1965] v3.17.1 release (^.^)YYa!! --- .../java/io/jboot/utils/ClassScanner.java | 71 ++++++++----------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 03cb05da..9765132c 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -15,6 +15,8 @@ */ package io.jboot.utils; +import io.jboot.app.config.JbootConfigManager; + import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; @@ -22,19 +24,13 @@ import java.lang.reflect.Modifier; import java.net.URL; import java.net.URLClassLoader; import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.function.Predicate; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarInputStream; import java.util.stream.Collectors; -import io.jboot.app.config.JbootConfigManager; - public class ClassScanner { private static final Set> appClassesCache = new HashSet<>(); @@ -45,7 +41,7 @@ public class ClassScanner { public static final Set scanClasses = new HashSet<>(); public static final Set excludeClasses = new HashSet<>(); // dev模式打开扫描信息打印 - private static boolean printScannerInfoEnable = JbootConfigManager.me().isDevMode(); + private static boolean printScannerInfoEnable = false; public static boolean isPrintScannerInfoEnable() { return printScannerInfoEnable; @@ -627,26 +623,22 @@ public class ClassScanner { Enumeration entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry jarEntry = entries.nextElement(); - if (jarEntry.isDirectory()) { - String entryName = jarEntry.getName(); + String entryName = jarEntry.getName(); + if (jarEntry.isDirectory() && entryName.startsWith("BOOT-INF/classes/")) { + if (isPrintScannerInfoEnable()) { System.out.println("Jboot Scan entryName: " + entryName); } - if (entryName.startsWith("BOOT-INF/classes/")) { - if (entryName.endsWith(".class")) { - String className = entryName.replace("/", ".").substring(0, entryName.length() - 6); - addClass(classForName(className)); - } - } - } - else { - String entryName = jarEntry.getName(); if (entryName.endsWith(".class")) { String className = entryName.replace("/", ".").substring(0, entryName.length() - 6); addClass(classForName(className)); } - else if (entryName.startsWith("BOOT-INF/lib/") && entryName.endsWith(".jar")) { + } else { + if (entryName.endsWith(".class")) { + String className = entryName.replace("/", ".").substring(0, entryName.length() - 6); + addClass(classForName(className)); + } else if (entryName.startsWith("BOOT-INF/lib/") && entryName.endsWith(".jar")) { if (!isIncludeJar(entryName)) { continue; } @@ -654,24 +646,21 @@ public class ClassScanner { if (isPrintScannerInfoEnable()) { System.out.println("Jboot Scan Jar: " + entryName); } - JarInputStream jarIS = new JarInputStream(jarFile - .getInputStream(jarEntry)); - - JarEntry innerEntry = jarIS.getNextJarEntry(); - while (innerEntry != null) { - if (!innerEntry.isDirectory()) { - String nestedEntryName = innerEntry.getName(); - if (nestedEntryName.endsWith(".class")) { - String className = nestedEntryName.replace("/", ".").substring(0, nestedEntryName.length() - 6); - addClass(classForName(className)); + + try (JarInputStream jarStream = new JarInputStream(jarFile + .getInputStream(jarEntry));){ + JarEntry innerEntry = jarStream.getNextJarEntry(); + while (innerEntry != null) { + if (!innerEntry.isDirectory()) { + String nestedEntryName = innerEntry.getName(); + if (nestedEntryName.endsWith(".class")) { + String className = nestedEntryName.replace("/", ".").substring(0, nestedEntryName.length() - 6); + addClass(classForName(className)); + } } + innerEntry = jarStream.getNextJarEntry(); } - innerEntry = jarIS.getNextJarEntry(); - } - if (jarIS != null) { - jarIS.close(); } -// addClassesFromJar(nestedJarPath); } } } @@ -686,10 +675,6 @@ public class ClassScanner { } } -// public static void main(String[] args) { -// String filePath = "D:\\test\\springbootest.jar"; -// addClassesFromJar(filePath); -// } private static void addClassesFromClassPath(String classPath) { @@ -748,8 +733,8 @@ public class ClassScanner { } if (!path.toLowerCase().endsWith(".jar")) { - if(path.toLowerCase().endsWith("!/") || path.toLowerCase().endsWith("!")) { } - else{ + if (path.toLowerCase().endsWith("!/") || path.toLowerCase().endsWith("!")) { + } else { classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); } } else { @@ -788,8 +773,8 @@ public class ClassScanner { } try { if (!path.toLowerCase().endsWith(".jar") && !jarPaths.contains(path)) { - if (path.toLowerCase().endsWith("!/") || path.toLowerCase().endsWith("!")) {} - else{ + if (path.toLowerCase().endsWith("!/") || path.toLowerCase().endsWith("!")) { + } else { classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); } } else { -- Gitee From ae18405c4c6970b13240bf56ef516afbb5fa0c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 27 Nov 2022 10:57:50 +0800 Subject: [PATCH 1871/1965] v4.0.0 snapshot --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e2468f89..12112946 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 3.17.1 + 4.0.0-snapshot jar jboot @@ -41,7 +41,7 @@ - 5.0.6 + 5.0.7 2022.2 3.3 3.4 -- Gitee From 7bfa32dd7fce967da74ac133a450def00f0cd5b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 27 Nov 2022 10:58:29 +0800 Subject: [PATCH 1872/1965] v4.0.0 snapshot --- .../java/io/jboot/core/JbootCoreConfig.java | 7 +-- .../attachment/SecurityFileNamePolicy.java | 47 ------------------- .../jboot/web/handler/JbootActionHandler.java | 4 -- .../handler/PathVariableActionHandler.java | 4 -- 4 files changed, 2 insertions(+), 60 deletions(-) delete mode 100644 src/main/java/io/jboot/web/attachment/SecurityFileNamePolicy.java diff --git a/src/main/java/io/jboot/core/JbootCoreConfig.java b/src/main/java/io/jboot/core/JbootCoreConfig.java index cb8eb525..14e113a4 100644 --- a/src/main/java/io/jboot/core/JbootCoreConfig.java +++ b/src/main/java/io/jboot/core/JbootCoreConfig.java @@ -57,15 +57,14 @@ import io.jboot.utils.*; import io.jboot.web.JbootActionMapping; import io.jboot.web.JbootWebConfig; import io.jboot.web.PathVariableActionMapping; -import io.jboot.web.attachment.SecurityFileNamePolicy; -import io.jboot.web.converter.ArrayConverters; -import io.jboot.web.converter.TypeConverterFunc; import io.jboot.web.attachment.AttachmentHandler; import io.jboot.web.attachment.LocalAttachmentContainerConfig; import io.jboot.web.controller.JbootControllerManager; import io.jboot.web.controller.annotation.GetMapping; import io.jboot.web.controller.annotation.PostMapping; import io.jboot.web.controller.annotation.RequestMapping; +import io.jboot.web.converter.ArrayConverters; +import io.jboot.web.converter.TypeConverterFunc; import io.jboot.web.directive.JbootOutputDirectiveFactory; import io.jboot.web.directive.SharedEnumObject; import io.jboot.web.directive.annotation.*; @@ -149,8 +148,6 @@ public class JbootCoreConfig extends JFinalConfig { @Override public void configConstant(Constants constants) { - SecurityFileNamePolicy.init(); - JbootAppListenerManager.me().onConstantConfigBefore(constants); constants.setRenderFactory(JbootRenderFactory.me()); diff --git a/src/main/java/io/jboot/web/attachment/SecurityFileNamePolicy.java b/src/main/java/io/jboot/web/attachment/SecurityFileNamePolicy.java deleted file mode 100644 index 1e47ab37..00000000 --- a/src/main/java/io/jboot/web/attachment/SecurityFileNamePolicy.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.web.attachment; - -import com.jfinal.upload.MultipartRequest; - -import com.oreilly.servlet.multipart.DefaultFileRenamePolicy; -import io.jboot.utils.ReflectUtil; - -import java.io.File; - -public class SecurityFileNamePolicy extends DefaultFileRenamePolicy { - - @Override - public File rename(File f) { - String name = f.getName(); - - int lastIndexOf = name.lastIndexOf("."); - if (lastIndexOf > 0) { - String suffix = name.substring(lastIndexOf).toLowerCase().trim(); - if (".jsp".equals(suffix) || ".jspx".equals(suffix)) { - File safeFile = new File(f.getParentFile(), name + "_unsafe"); - return super.rename(safeFile); - } - } - - return super.rename(f); - } - - - public static void init() { - ReflectUtil.setStaticFieldValue(MultipartRequest.class, "fileRenamePolicy", new SecurityFileNamePolicy()); - } -} diff --git a/src/main/java/io/jboot/web/handler/JbootActionHandler.java b/src/main/java/io/jboot/web/handler/JbootActionHandler.java index 54cf3143..0434f8f2 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionHandler.java +++ b/src/main/java/io/jboot/web/handler/JbootActionHandler.java @@ -91,10 +91,6 @@ public class JbootActionHandler extends ActionHandler { @Override public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { if (target.lastIndexOf('.') != -1) { - if (isJspTarget(target)) { - isHandled[0] = true; - renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); - } return; } diff --git a/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java b/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java index e83daea9..cafe3128 100644 --- a/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java +++ b/src/main/java/io/jboot/web/handler/PathVariableActionHandler.java @@ -50,10 +50,6 @@ public class PathVariableActionHandler extends JbootActionHandler { @Override public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { if (target.lastIndexOf('.') != -1) { - if (isJspTarget(target)){ - isHandled[0] = true; - renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); - } return; } -- Gitee From 87fe31487bc6c432e61e085b0ad5c96f16e7b48a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 27 Nov 2022 11:40:34 +0800 Subject: [PATCH 1873/1965] v4.0.0 snapshot --- .../io/jboot/app/JbootApplicationConfig.java | 12 ++++++- src/main/java/io/jboot/app/JdkUtil.java | 35 +++++++++++++++++++ .../serializer/JbootSerializerConfig.java | 13 ++++++- .../jboot/web/directive/SharedEnumObject.java | 18 ++++------ 4 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 src/main/java/io/jboot/app/JdkUtil.java diff --git a/src/main/java/io/jboot/app/JbootApplicationConfig.java b/src/main/java/io/jboot/app/JbootApplicationConfig.java index 1bc94bf5..77983e4a 100644 --- a/src/main/java/io/jboot/app/JbootApplicationConfig.java +++ b/src/main/java/io/jboot/app/JbootApplicationConfig.java @@ -18,6 +18,7 @@ package io.jboot.app; import io.jboot.JbootConsts; import io.jboot.app.config.JbootConfigManager; import io.jboot.app.config.annotation.ConfigModel; +import io.jboot.utils.StrUtil; @ConfigModel(prefix = "jboot.app") public class JbootApplicationConfig { @@ -31,7 +32,7 @@ public class JbootApplicationConfig { private String listener = "*"; private String listenerPackage = "*"; private boolean handle404 = true; - private String proxy = "cglib"; //cglib or javassist + private String proxy; //cglib or javassist public String getMode() { @@ -99,9 +100,18 @@ public class JbootApplicationConfig { } public String getProxy() { + if (StrUtil.isBlank(proxy)) { + proxy = initProxy(); + } return proxy; } + + private String initProxy() { + ///cglib javassist + return JdkUtil.isJdk11To19() ? "javassist" : "cglib"; + } + public void setProxy(String proxy) { this.proxy = proxy; } diff --git a/src/main/java/io/jboot/app/JdkUtil.java b/src/main/java/io/jboot/app/JdkUtil.java new file mode 100644 index 00000000..8cec5af0 --- /dev/null +++ b/src/main/java/io/jboot/app/JdkUtil.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). + *

        + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

        + * http://www.apache.org/licenses/LICENSE-2.0 + *

        + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jboot.app; + +public class JdkUtil { + + public static boolean isJdk1x() { + //1.8.0_261 + String javaVersion = System.getProperty("java.version"); + return javaVersion != null && javaVersion.startsWith("1."); + } + + + public static boolean isJdk11To19() { + //17.0.1 + String javaVersion = System.getProperty("java.version"); + return javaVersion != null && javaVersion.startsWith("1") + && javaVersion.indexOf(".") == 2; + } + + +} diff --git a/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java b/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java index 1ae23252..040bf448 100644 --- a/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java +++ b/src/main/java/io/jboot/components/serializer/JbootSerializerConfig.java @@ -15,7 +15,9 @@ */ package io.jboot.components.serializer; +import io.jboot.app.JdkUtil; import io.jboot.app.config.annotation.ConfigModel; +import io.jboot.utils.StrUtil; @ConfigModel(prefix = "jboot.serializer") @@ -25,12 +27,21 @@ public class JbootSerializerConfig { public static final String FASTJSON = "fastjson"; public static final String KRYO = "kryo"; - public String type = FST; + public String type; + public String getType() { + if (StrUtil.isBlank(type)){ + type = initType(); + } return type; } + private String initType() { + return JdkUtil.isJdk11To19() ? FASTJSON : FST; + } + + public void setType(String type) { this.type = type; } diff --git a/src/main/java/io/jboot/web/directive/SharedEnumObject.java b/src/main/java/io/jboot/web/directive/SharedEnumObject.java index 4884f1af..8c46fd46 100644 --- a/src/main/java/io/jboot/web/directive/SharedEnumObject.java +++ b/src/main/java/io/jboot/web/directive/SharedEnumObject.java @@ -17,6 +17,7 @@ package io.jboot.web.directive; import com.jfinal.kit.LogKit; import com.jfinal.template.expr.ast.MethodKeyBuilder; +import io.jboot.app.JdkUtil; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; @@ -176,17 +177,12 @@ public class SharedEnumObject extends LinkedHashMap { } } - SharedEnumObject ret; - String javaVersion = System.getProperty("java.version"); - if (javaVersion != null && javaVersion.startsWith("1.")) { - // jdk 1.x - ret = (SharedEnumObject) newClass.toClass().newInstance(); - } else { - // jdk 17 - // toClass() must add neighbor class in jdk17 - // neighbor: A class belonging to the same package that this class belongs to - ret = (SharedEnumObject) newClass.toClass(SharedEnumObject.class).newInstance(); - } + // jdk 17 + // toClass() must add neighbor class in jdk17 + // neighbor: A class belonging to the same package that this class belongs to + + SharedEnumObject ret = JdkUtil.isJdk1x() ? (SharedEnumObject) newClass.toClass().newInstance() + : (SharedEnumObject) newClass.toClass(SharedEnumObject.class).newInstance(); ret.init(enumClass, enumStaticMethods); return ret; } catch (Exception e) { -- Gitee From e40c78b9a1f9a719ffb4e2ba2bf9f2e2e963dfb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 27 Nov 2022 11:45:08 +0800 Subject: [PATCH 1874/1965] v4.0.0 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 50c872fd..bd6284ae 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 3.17.1 + 4.0.0 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 61b7072b..a3411a1f 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 3.17.1 + 4.0.0 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 812098e7..3179bd04 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 3.17.1 + 4.0.0 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='3.17.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='4.0.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 3c98decb..c5885311 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='3.17.1', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='4.0.0', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/3.17.1/jboot-3.17.1.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/4.0.0/jboot-4.0.0.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 4319c02c..93e69bf5 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 3.17.1 + 4.0.0 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 3a3db705..d129f406 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "3.17.1"; + public static String VERSION = "4.0.0"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From ad93c6ab1c6f3c60a5c7ad17536858dc83d36b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 27 Nov 2022 11:46:11 +0800 Subject: [PATCH 1875/1965] v4.0.0 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ pom.xml | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 99e340e4..4da1948b 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 3.17.1 + 4.0.0 ``` diff --git a/changes.txt b/changes.txt index 076243aa..edbc1473 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v4.0.0 2022-11-27: +优化:正式支持 jdk 17.x + + + +===================== v3.x ========================= + jboot v3.17.1 2022-11-13: 优化:Columns.java 对空条件的判断 优化:ClassScanner.java 支持扫描 jar 包里的 jar 包,感谢 @陈立刚 diff --git a/pom.xml b/pom.xml index 12112946..9a1fcbed 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.0-snapshot + 4.0.0 jar jboot @@ -542,7 +542,7 @@ io.dropwizard.metrics metrics-servlets - 4.2.12 + ${metrics.version} -- Gitee From 76c593b39558e1297ca45cfc50d279f893d46685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 1 Dec 2022 16:39:11 +0800 Subject: [PATCH 1876/1965] v4.0.1 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9a1fcbed..4ce63352 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.0 + 4.0.1-SNAPSHOT jar jboot -- Gitee From 27e0d6d52c4f4ebeb18063bc9629e0ca641af77f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 1 Dec 2022 16:39:21 +0800 Subject: [PATCH 1877/1965] optimize utils --- src/main/java/io/jboot/utils/ModelUtil.java | 2 +- src/main/java/io/jboot/utils/ObjectUtil.java | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/utils/ModelUtil.java b/src/main/java/io/jboot/utils/ModelUtil.java index 8aa288d5..feeccd91 100644 --- a/src/main/java/io/jboot/utils/ModelUtil.java +++ b/src/main/java/io/jboot/utils/ModelUtil.java @@ -144,7 +144,7 @@ public class ModelUtil { } - public static void keep(List models, String... attrs) { + public static void keep(List models, String... attrs) { if (models == null || models.isEmpty()) { return; } diff --git a/src/main/java/io/jboot/utils/ObjectUtil.java b/src/main/java/io/jboot/utils/ObjectUtil.java index 0415d184..06b3d293 100644 --- a/src/main/java/io/jboot/utils/ObjectUtil.java +++ b/src/main/java/io/jboot/utils/ObjectUtil.java @@ -135,7 +135,12 @@ public class ObjectUtil { } - public static Object convert(Object value, Class targetClass) { + public static Object convert(Object value, Class targetClass) { + if (value == null || (value.getClass() == String.class && StrUtil.isBlank((String) value) + && targetClass != String.class)) { + return null; + } + if (value.getClass().isAssignableFrom(targetClass)) { return value; } -- Gitee From fa785c0785fe9b50f4e4f163daba67058fd67a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 3 Dec 2022 12:31:01 +0800 Subject: [PATCH 1878/1965] fixed use("ds").update() --- .../java/io/jboot/db/model/JbootModel.java | 21 +++++++++---------- .../io/jboot/db/model/JbootModelExts.java | 9 -------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index 135d2f0b..cb8ad66f 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -160,6 +160,10 @@ public class JbootModel> extends Model { try { m = (M) _getUsefulClass().newInstance(); m.put(_getAttrs()); + + for (String attr : _getModifyFlag()) { + m._getModifyFlag().add(attr); + } } catch (Exception e) { LOG.error(e.toString(), e); } @@ -224,20 +228,15 @@ public class JbootModel> extends Model { private M use(String configName, boolean validateDatasourceExist) { - M newDao = JbootModelExts.getDatasourceDAO(this, DATASOURCE_CACHE_PREFIX + configName); - if (newDao == null) { - newDao = this.copy()._setConfigName(configName); - if (newDao._getConfig() == null) { - if (validateDatasourceExist) { - throw new JbootIllegalConfigException("The datasource \"" + configName + "\" not config well, please config it in jboot.properties."); - } else { - return null; - } + M newModel = copy()._setConfigName(configName); + if (newModel._getConfig() == null) { + if (validateDatasourceExist) { + throw new JbootIllegalConfigException("The datasource \"" + configName + "\" not config well, please config it in jboot.properties."); } else { - JbootModelExts.setDatasourceDAO(this, DATASOURCE_CACHE_PREFIX + configName, newDao); + return null; } } - return newDao; + return newModel; } diff --git a/src/main/java/io/jboot/db/model/JbootModelExts.java b/src/main/java/io/jboot/db/model/JbootModelExts.java index 62c291cf..e44b404c 100644 --- a/src/main/java/io/jboot/db/model/JbootModelExts.java +++ b/src/main/java/io/jboot/db/model/JbootModelExts.java @@ -56,13 +56,4 @@ public class JbootModelExts { } - public static void setDatasourceDAO(JbootModel holder, String key, JbootModel datasourceDAO) { - setExtAttr(holder, key, datasourceDAO); - } - - public static T getDatasourceDAO(JbootModel holder, String key) { - return (T) getExtAttr(holder, key); - } - - } -- Gitee From 24fad6134e71a51f85bdb63522a6933d826b4a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 3 Dec 2022 12:36:56 +0800 Subject: [PATCH 1879/1965] =?UTF-8?q?add=20=E6=96=B0=E5=A2=9E=E5=8F=96?= =?UTF-8?q?=E6=B6=88=20aop=20=E7=BC=93=E5=AD=98=E7=9A=84=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/components/cache/AopCache.java | 8 +++++++- .../java/io/jboot/components/cache/AopCacheConfig.java | 5 +++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/AopCache.java b/src/main/java/io/jboot/components/cache/AopCache.java index dcbcc813..c0acfde6 100644 --- a/src/main/java/io/jboot/components/cache/AopCache.java +++ b/src/main/java/io/jboot/components/cache/AopCache.java @@ -131,8 +131,14 @@ public class AopCache { liveSeconds = liveSeconds > 0 ? liveSeconds : CONFIG.getLiveSeconds(); if (liveSeconds > 0) { put(cacheName, cacheKey, data, liveSeconds); - } else { + } + //永久有效 + else if (liveSeconds == 0){ put(cacheName, cacheKey, data); } + // -1 负数,取消 AOP 缓存 + else { + // do nothing + } } } diff --git a/src/main/java/io/jboot/components/cache/AopCacheConfig.java b/src/main/java/io/jboot/components/cache/AopCacheConfig.java index 44c80aca..5c6b2679 100644 --- a/src/main/java/io/jboot/components/cache/AopCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/AopCacheConfig.java @@ -23,9 +23,10 @@ import io.jboot.app.config.annotation.ConfigModel; @ConfigModel(prefix = "jboot.aop.cache") public class AopCacheConfig { - // AOP 缓存的默认有效时间,0为永久有效,单位秒, + // AOP 缓存的默认有效时间,0 为永久有效,单位秒, // 当 @Cacheable 和 @CachePut 注解不配置的时候默认用这个配置 - private int liveSeconds = 60 * 60; //默认为 1 个小时 + + private int liveSeconds = 60 * 10; //默认为 10 分钟 private String useCacheName = "default"; public int getLiveSeconds() { -- Gitee From 2503250164edfbeb535dc989aac0a60552472dd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 3 Dec 2022 12:38:06 +0800 Subject: [PATCH 1880/1965] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20aop=20action=20?= =?UTF-8?q?=E7=AD=89=E9=BB=98=E8=AE=A4=E7=BC=93=E5=AD=98=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E4=B8=BA=2010=20=E5=88=86=E9=92=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/components/cache/ActionCacheConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/cache/ActionCacheConfig.java b/src/main/java/io/jboot/components/cache/ActionCacheConfig.java index 896afdc2..20ea4794 100644 --- a/src/main/java/io/jboot/components/cache/ActionCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/ActionCacheConfig.java @@ -25,7 +25,8 @@ public class ActionCacheConfig { // AOP 缓存的默认有效时间,0为永久有效,单位秒, // 当 @Cacheable 和 @CachePut 注解不配置的时候默认用这个配置 - private int liveSeconds = 60 * 60; //默认为 1 个小时 + + private int liveSeconds = 60 * 10; //默认为 10 分钟 private String useCacheName = "default"; public int getLiveSeconds() { -- Gitee From 4488cf27883cda42e0e5b013673954382bfbd00e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 3 Dec 2022 12:54:53 +0800 Subject: [PATCH 1881/1965] add jboot.cache.useFirstLevelOnly config --- .../components/cache/JbootCacheConfig.java | 12 ++++++ .../cache/caredis/JbootCaredisCacheImpl.java | 37 +++++++++++++------ .../cache/ehredis/JbootEhredisCacheImpl.java | 32 +++++++++++----- 3 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java index 1bcf28bd..bb347902 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java @@ -44,6 +44,10 @@ public class JbootCacheConfig { private String defaultCachePrefix; private Boolean devMode = false; + //只启用一级缓存,针对分布式场景下,redis 有关闭了 scan keys 等指令的时候 + //可以使用 redis 的消息机制做缓存同步 + private boolean useFirstLevelOnly = false; + public String getName() { return name; @@ -85,4 +89,12 @@ public class JbootCacheConfig { public void setDevMode(Boolean devMode) { this.devMode = devMode; } + + public boolean isUseFirstLevelOnly() { + return useFirstLevelOnly; + } + + public void setUseFirstLevelOnly(boolean useFirstLevelOnly) { + this.useFirstLevelOnly = useFirstLevelOnly; + } } diff --git a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java index 3f4af435..8075c880 100644 --- a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java @@ -74,7 +74,7 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { @Override public T get(String cacheName, Object key) { T value = caffeineCacheImpl.get(cacheName, key); - if (value == null) { + if (value == null && !config.isUseFirstLevelOnly()) { value = redisCacheImpl.get(cacheName, key); if (value != null) { Integer ttl = redisCacheImpl.getTtl(cacheName, key); @@ -92,7 +92,9 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { public void put(String cacheName, Object key, Object value) { try { caffeineCacheImpl.put(cacheName, key, value); - redisCacheImpl.put(cacheName, key, value); + if (!config.isUseFirstLevelOnly()) { + redisCacheImpl.put(cacheName, key, value); + } } finally { publishMessage(JbootCaredisMessage.ACTION_PUT, cacheName, key); } @@ -107,7 +109,9 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { } try { caffeineCacheImpl.put(cacheName, key, value, liveSeconds); - redisCacheImpl.put(cacheName, key, value, liveSeconds); + if (!config.isUseFirstLevelOnly()) { + redisCacheImpl.put(cacheName, key, value, liveSeconds); + } } finally { publishMessage(JbootCaredisMessage.ACTION_PUT, cacheName, key); } @@ -117,7 +121,9 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { public void remove(String cacheName, Object key) { try { caffeineCacheImpl.remove(cacheName, key); - redisCacheImpl.remove(cacheName, key); + if (!config.isUseFirstLevelOnly()) { + redisCacheImpl.remove(cacheName, key); + } } finally { publishMessage(JbootCaredisMessage.ACTION_REMOVE, cacheName, key); } @@ -127,7 +133,9 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { public void removeAll(String cacheName) { try { caffeineCacheImpl.removeAll(cacheName); - redisCacheImpl.removeAll(cacheName); + if (!config.isUseFirstLevelOnly()) { + redisCacheImpl.removeAll(cacheName); + } } finally { publishMessage(JbootCaredisMessage.ACTION_REMOVE_ALL, cacheName, null); } @@ -168,7 +176,7 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { @Override public Integer getTtl(String cacheName, Object key) { Integer ttl = caffeineCacheImpl.getTtl(cacheName, key); - if (ttl == null) { + if (ttl == null && !config.isUseFirstLevelOnly()) { ttl = redisCacheImpl.getTtl(cacheName, key); } return ttl; @@ -179,7 +187,10 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { public void setTtl(String cacheName, Object key, int seconds) { try { caffeineCacheImpl.setTtl(cacheName, key, seconds); - redisCacheImpl.setTtl(cacheName, key, seconds); + + if (!config.isUseFirstLevelOnly()) { + redisCacheImpl.setTtl(cacheName, key, seconds); + } } finally { publishMessage(JbootCaredisMessage.ACTION_REMOVE, cacheName, key); } @@ -200,7 +211,7 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { @Override public List getNames() { - return redisCacheImpl.getNames(); + return config.isUseFirstLevelOnly() ? null : redisCacheImpl.getNames(); } @@ -211,11 +222,13 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { return list; } - list = redisCacheImpl.getKeys(cacheName); - if (list == null) { - list = new ArrayList(); + if (!config.isUseFirstLevelOnly()) { + list = redisCacheImpl.getKeys(cacheName); + if (list == null) { + list = new ArrayList(); + } + keysCache.put(cacheName, list); } - keysCache.put(cacheName, list); return list; } diff --git a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java index ffd38f5a..da41c3be 100644 --- a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java @@ -80,7 +80,7 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL @Override public T get(String cacheName, Object key) { T value = ehcacheImpl.get(cacheName, key); - if (value == null) { + if (value == null && !config.isUseFirstLevelOnly()) { value = redisCacheImpl.get(cacheName, key); if (value != null) { Integer ttl = redisCacheImpl.getTtl(cacheName, key); @@ -98,7 +98,9 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL public void put(String cacheName, Object key, Object value) { try { ehcacheImpl.put(cacheName, key, value); - redisCacheImpl.put(cacheName, key, value); + if (!config.isUseFirstLevelOnly()) { + redisCacheImpl.put(cacheName, key, value); + } } finally { publishMessage(JbootEhredisMessage.ACTION_PUT, cacheName, key); } @@ -113,7 +115,10 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL } try { ehcacheImpl.put(cacheName, key, value, liveSeconds); - redisCacheImpl.put(cacheName, key, value, liveSeconds); + + if (!config.isUseFirstLevelOnly()) { + redisCacheImpl.put(cacheName, key, value, liveSeconds); + } } finally { publishMessage(JbootEhredisMessage.ACTION_PUT, cacheName, key); } @@ -123,7 +128,10 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL public void remove(String cacheName, Object key) { try { ehcacheImpl.remove(cacheName, key); - redisCacheImpl.remove(cacheName, key); + + if (!config.isUseFirstLevelOnly()) { + redisCacheImpl.remove(cacheName, key); + } } finally { publishMessage(JbootEhredisMessage.ACTION_REMOVE, cacheName, key); } @@ -133,7 +141,10 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL public void removeAll(String cacheName) { try { ehcacheImpl.removeAll(cacheName); - redisCacheImpl.removeAll(cacheName); + + if (!config.isUseFirstLevelOnly()) { + redisCacheImpl.removeAll(cacheName); + } } finally { publishMessage(JbootEhredisMessage.ACTION_REMOVE_ALL, cacheName, null); } @@ -174,7 +185,7 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL @Override public Integer getTtl(String cacheName, Object key) { Integer ttl = ehcacheImpl.getTtl(cacheName, key); - if (ttl == null) { + if (ttl == null && !config.isUseFirstLevelOnly()) { ttl = redisCacheImpl.getTtl(cacheName, key); } return ttl; @@ -185,7 +196,10 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL public void setTtl(String cacheName, Object key, int seconds) { try { ehcacheImpl.setTtl(cacheName, key, seconds); - redisCacheImpl.setTtl(cacheName, key, seconds); + + if (!config.isUseFirstLevelOnly()) { + redisCacheImpl.setTtl(cacheName, key, seconds); + } } finally { publishMessage(JbootEhredisMessage.ACTION_REMOVE, cacheName, key); } @@ -216,13 +230,13 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL @Override public List getNames() { - return redisCacheImpl.getNames(); + return config.isUseFirstLevelOnly() ? null : redisCacheImpl.getNames(); } @Override public List getKeys(String cacheName) { List list = keysCache.getIfPresent(cacheName); - if (list == null) { + if (list == null && !config.isUseFirstLevelOnly()) { list = redisCacheImpl.getKeys(cacheName); if (list == null) { list = new ArrayList(); -- Gitee From 6800494989c767d226b79bed3ad0cda42a5d8b00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 3 Dec 2022 13:32:02 +0800 Subject: [PATCH 1882/1965] fixed use("ds").update() --- .../java/io/jboot/db/model/JbootModel.java | 39 +++++++++++++++++-- .../io/jboot/db/model/JbootModelExts.java | 9 +++++ .../io/jboot/service/JbootServiceBase.java | 2 +- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jboot/db/model/JbootModel.java b/src/main/java/io/jboot/db/model/JbootModel.java index cb8ad66f..5b5645eb 100644 --- a/src/main/java/io/jboot/db/model/JbootModel.java +++ b/src/main/java/io/jboot/db/model/JbootModel.java @@ -149,6 +149,17 @@ public class JbootModel> extends Model { } } + @Override + public M dao() { + put("__is_dao", true); + return (M) this; + } + + + private boolean isDaoModel() { + Boolean flag = getBoolean("__is_dao"); + return flag != null && flag; + } /** * copy model with attrs or false @@ -228,15 +239,35 @@ public class JbootModel> extends Model { private M use(String configName, boolean validateDatasourceExist) { - M newModel = copy()._setConfigName(configName); - if (newModel._getConfig() == null) { - if (validateDatasourceExist) { + + //非 service 的 dao,例如 new User().user('ds').save()/upate() + if (!isDaoModel()) { + _setConfigName(configName); + return validDatasourceExist((M) this, validateDatasourceExist, configName); + } + + //定义在 service 中的 DAO + M newDao = JbootModelExts.getDatasourceDAO(this, DATASOURCE_CACHE_PREFIX + configName); + if (newDao == null) { + newDao = this.copy()._setConfigName(configName); + newDao = validDatasourceExist(newDao, validateDatasourceExist, configName); + if (newDao != null) { + JbootModelExts.setDatasourceDAO(this, DATASOURCE_CACHE_PREFIX + configName, newDao); + } + } + return newDao; + } + + + private M validDatasourceExist(M model, boolean valid, String configName) { + if (model._getConfig() == null) { + if (valid) { throw new JbootIllegalConfigException("The datasource \"" + configName + "\" not config well, please config it in jboot.properties."); } else { return null; } } - return newModel; + return model; } diff --git a/src/main/java/io/jboot/db/model/JbootModelExts.java b/src/main/java/io/jboot/db/model/JbootModelExts.java index e44b404c..62c291cf 100644 --- a/src/main/java/io/jboot/db/model/JbootModelExts.java +++ b/src/main/java/io/jboot/db/model/JbootModelExts.java @@ -56,4 +56,13 @@ public class JbootModelExts { } + public static void setDatasourceDAO(JbootModel holder, String key, JbootModel datasourceDAO) { + setExtAttr(holder, key, datasourceDAO); + } + + public static T getDatasourceDAO(JbootModel holder, String key) { + return (T) getExtAttr(holder, key); + } + + } diff --git a/src/main/java/io/jboot/service/JbootServiceBase.java b/src/main/java/io/jboot/service/JbootServiceBase.java index 327d0e8a..864dd76e 100644 --- a/src/main/java/io/jboot/service/JbootServiceBase.java +++ b/src/main/java/io/jboot/service/JbootServiceBase.java @@ -63,7 +63,7 @@ public class JbootServiceBase> Type type = usefulClass.getGenericSuperclass(); if (type instanceof ParameterizedType) { Class modelClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0]; - return ClassUtil.newInstance(modelClass, false); + return ClassUtil.newInstance(modelClass, false).dao(); } //from child class else if (type instanceof Class) { -- Gitee From 23309bef07b2883736ce86736b4155cf74202608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 3 Dec 2022 17:48:11 +0800 Subject: [PATCH 1883/1965] optimize utils --- .../java/io/jboot/utils/AnnotationUtil.java | 42 +++++++++---------- src/main/java/io/jboot/utils/ClassUtil.java | 2 - 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/jboot/utils/AnnotationUtil.java b/src/main/java/io/jboot/utils/AnnotationUtil.java index daa1f02b..1ab48c8a 100644 --- a/src/main/java/io/jboot/utils/AnnotationUtil.java +++ b/src/main/java/io/jboot/utils/AnnotationUtil.java @@ -34,54 +34,50 @@ public class AnnotationUtil { return defaultValue; } + public static String[] get(String[] value) { + if (ArrayUtil.isNullOrEmpty(value)) { + return null; + } + + String[] rets = new String[value.length]; + for (int i = 0; i < rets.length; i++) { + rets[i] = get(value[i]); + } + return rets; + } + + public static Integer getInt(String value) { String intValue = get(value); - if (intValue == null) return null; - return Integer.valueOf(intValue); + return intValue == null ? null : Integer.parseInt(intValue); } public static Integer getInt(String value, int defaultValue) { String intValue = get(value); - if (intValue == null) return defaultValue; - return Integer.valueOf(intValue); + return intValue == null ? defaultValue : Integer.parseInt(intValue); } public static Long getLong(String value) { String longValue = get(value); - if (longValue == null) return null; - return Long.valueOf(longValue); + return longValue == null ? null : Long.parseLong(longValue); } public static Long getLong(String value, long defaultValue) { String longValue = get(value); - if (longValue == null) return defaultValue; - return Long.valueOf(longValue); + return longValue == null ? defaultValue : Long.parseLong(longValue); } public static Boolean getBool(String value) { String boolValue = get(value); - if (boolValue == null) return null; - return Boolean.valueOf(boolValue); + return boolValue == null ? null : Boolean.parseBoolean(boolValue); } public static Boolean getBool(String value, boolean defaultValue) { String boolValue = get(value); - if (boolValue == null) return defaultValue; - return Boolean.valueOf(boolValue); + return boolValue == null ? defaultValue : Boolean.parseBoolean(boolValue); } - public static String[] get(String[] value) { - if (ArrayUtil.isNullOrEmpty(value)) { - return null; - } - - String[] rets = new String[value.length]; - for (int i = 0; i < rets.length; i++) { - rets[i] = get(value[i]); - } - return rets; - } } diff --git a/src/main/java/io/jboot/utils/ClassUtil.java b/src/main/java/io/jboot/utils/ClassUtil.java index 4eb11b27..8d06e620 100644 --- a/src/main/java/io/jboot/utils/ClassUtil.java +++ b/src/main/java/io/jboot/utils/ClassUtil.java @@ -267,8 +267,6 @@ public class ClassUtil { } return clazz; - -// return clazz.getName().indexOf(ENHANCER_BY) == -1 ? clazz : clazz.getSuperclass(); } -- Gitee From a075a1aef468cd82afaac09e230ceb50e0b869c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 3 Dec 2022 18:06:23 +0800 Subject: [PATCH 1884/1965] v4.0.1 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index bd6284ae..7c011702 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 4.0.0 + 4.0.1 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index a3411a1f..8d84d0e7 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 4.0.0 + 4.0.1 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 3179bd04..95915ec2 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 4.0.0 + 4.0.1 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='4.0.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='4.0.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index c5885311..1957e7f7 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='4.0.0', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='4.0.1', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/4.0.0/jboot-4.0.0.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/4.0.1/jboot-4.0.1.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 93e69bf5..84222d7f 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 4.0.0 + 4.0.1 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index d129f406..7b387043 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "4.0.0"; + public static String VERSION = "4.0.1"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 127e6ffa59e2df231bca85d2fbbf227e939c48ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 3 Dec 2022 18:06:29 +0800 Subject: [PATCH 1885/1965] v4.0.1 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 11 ++++++++++- pom.xml | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4da1948b..a381073b 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 4.0.0 + 4.0.1 ``` diff --git a/changes.txt b/changes.txt index edbc1473..c916ec3b 100644 --- a/changes.txt +++ b/changes.txt @@ -1,8 +1,17 @@ +jboot v4.0.1 2022-12-03: +新增:新增可以通过配置取消 AOP 缓存的功能 +新增:新增 jboot.cache.useFirstLevelOnly 配置的功能,可以在分布式下只开启 1 级缓存 +优化:修改 aop action 等默认缓存时间为 10 分钟 +优化:ModelUtil.keep 方法 +优化:ObjectUti.convert 方法 +修复:new Model().use("ds").update() 在某些场景下出错的问题 + + + jboot v4.0.0 2022-11-27: 优化:正式支持 jdk 17.x - ===================== v3.x ========================= jboot v3.17.1 2022-11-13: diff --git a/pom.xml b/pom.xml index 4ce63352..c2e08281 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.1-SNAPSHOT + 4.0.1 jar jboot -- Gitee From 25773ab2e7be9e2bb113e3f866534c0b61f61a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 3 Dec 2022 18:14:04 +0800 Subject: [PATCH 1886/1965] v4.0.1 release (^.^)YYa!! --- src/main/java/io/jboot/app/JbootApplicationConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/app/JbootApplicationConfig.java b/src/main/java/io/jboot/app/JbootApplicationConfig.java index 77983e4a..50f1d65c 100644 --- a/src/main/java/io/jboot/app/JbootApplicationConfig.java +++ b/src/main/java/io/jboot/app/JbootApplicationConfig.java @@ -131,7 +131,7 @@ public class JbootApplicationConfig { " name='" + name + '\'' + ", mode='" + mode + '\'' + ", version='" + version + '\'' + - ", proxy='" + proxy + '\'' + + ", proxy='" + getProxy() + '\'' + // ", config='" + jfinalConfig + '\'' + ", listener='" + listener + '\'' + ", listenerPackage='" + listenerPackage + '\'' + -- Gitee From 0d2e729a461eb4509ef7b615448970b9b3f3c7dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 6 Dec 2022 12:27:20 +0800 Subject: [PATCH 1887/1965] v4.0.2 snapshot --- pom.xml | 2 +- src/main/java/io/jboot/db/ArpManager.java | 4 -- .../java/io/jboot/db/record/JbootRecord.java | 57 --------------- .../jboot/db/record/JbootRecordBuilder.java | 71 ------------------- 4 files changed, 1 insertion(+), 133 deletions(-) delete mode 100644 src/main/java/io/jboot/db/record/JbootRecord.java delete mode 100644 src/main/java/io/jboot/db/record/JbootRecordBuilder.java diff --git a/pom.xml b/pom.xml index c2e08281..0685aef1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.1 + 4.0.2-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/db/ArpManager.java b/src/main/java/io/jboot/db/ArpManager.java index a0e57ace..2540d7f5 100644 --- a/src/main/java/io/jboot/db/ArpManager.java +++ b/src/main/java/io/jboot/db/ArpManager.java @@ -27,7 +27,6 @@ import io.jboot.db.datasource.DataSourceConfig; import io.jboot.db.datasource.DataSourceConfigManager; import io.jboot.db.dbpro.JbootDbProFactory; import io.jboot.db.dialect.*; -import io.jboot.db.record.JbootRecordBuilder; import io.jboot.exception.JbootException; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.ClassUtil; @@ -145,9 +144,6 @@ public class ArpManager { configSqlTemplate(activeRecordPlugin, config); configDialect(activeRecordPlugin, config); - //配置 Record 构建器 - activeRecordPlugin.getConfig().getDialect().setRecordBuilder(new JbootRecordBuilder()); - /** * 在一个表有多个数据源的情况下,应该只需要添加一个映射就可以了 * 添加映射:默认为该 model 的数据源 diff --git a/src/main/java/io/jboot/db/record/JbootRecord.java b/src/main/java/io/jboot/db/record/JbootRecord.java deleted file mode 100644 index e1c4dd7a..00000000 --- a/src/main/java/io/jboot/db/record/JbootRecord.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.db.record; - -import com.jfinal.plugin.activerecord.Record; - -import java.math.BigDecimal; -import java.math.BigInteger; - -public class JbootRecord extends Record { - - - ////// - @Override - public BigInteger getBigInteger(String attr) { - Object data = get(attr); - if (data instanceof BigInteger) { - return (BigInteger) data; - } - //数据类型 id(19 number)在 Oracle Jdbc 下对应的是 BigDecimal, - //但是在 MySql 下对应的是 BigInteger,这会导致在 MySql 下生成的代码无法在 Oracle 数据库中使用 - //此处是为了解决这个问题的 - else if (data instanceof BigDecimal) { - return ((BigDecimal) data).toBigInteger(); - } else if (data instanceof Number) { - return BigInteger.valueOf(((Number) data).longValue()); - } - //可能会抛出异常,应该让其抛出 - return (BigInteger) data; - } - - - @Override - public BigDecimal getBigDecimal(String attr) { - Object data = get(attr); - if (data instanceof BigDecimal) { - return (BigDecimal) data; - } else if (data instanceof Number) { - return new BigDecimal(data.toString()); - } - //可能会抛出异常,应该让其抛出 - return (BigDecimal) data; - } -} diff --git a/src/main/java/io/jboot/db/record/JbootRecordBuilder.java b/src/main/java/io/jboot/db/record/JbootRecordBuilder.java deleted file mode 100644 index 01da6810..00000000 --- a/src/main/java/io/jboot/db/record/JbootRecordBuilder.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.db.record; - -import com.jfinal.plugin.activerecord.*; - -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Types; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.Function; - -public class JbootRecordBuilder extends RecordBuilder { - - @Override - public List build(Config config, ResultSet rs, Function func) throws SQLException { - List result = new ArrayList(); - ResultSetMetaData rsmd = rs.getMetaData(); - int columnCount = rsmd.getColumnCount(); - String[] labelNames = new String[columnCount + 1]; - int[] types = new int[columnCount + 1]; - buildLabelNamesAndTypes(rsmd, labelNames, types); - while (rs.next()) { - Record record = new JbootRecord(); - CPI.setColumnsMap(record,config.getContainerFactory().getColumnsMap()); - Map columns = record.getColumns(); - for (int i=1; i<=columnCount; i++) { - Object value; - if (types[i] < Types.BLOB) { - value = rs.getObject(i); - } else { - if (types[i] == Types.CLOB) { - value = ModelBuilder.me.handleClob(rs.getClob(i)); - } else if (types[i] == Types.NCLOB) { - value = ModelBuilder.me.handleClob(rs.getNClob(i)); - } else if (types[i] == Types.BLOB) { - value = ModelBuilder.me.handleBlob(rs.getBlob(i)); - } else { - value = rs.getObject(i); - } - } - columns.put(labelNames[i], value); - } - - if (func == null) { - result.add(record); - } else { - if ( ! func.apply(record) ) { - break ; - } - } - } - return result; - } -} -- Gitee From 548c341a99a8209d41638f099c03d4619d714fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 6 Dec 2022 12:41:35 +0800 Subject: [PATCH 1888/1965] add db.each sql printer --- src/main/java/io/jboot/db/SqlDebugger.java | 4 +- .../java/io/jboot/db/dbpro/JbootDbPro.java | 46 ++++++++++++++++--- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/db/SqlDebugger.java b/src/main/java/io/jboot/db/SqlDebugger.java index 31910c7d..a95fcbca 100644 --- a/src/main/java/io/jboot/db/SqlDebugger.java +++ b/src/main/java/io/jboot/db/SqlDebugger.java @@ -90,7 +90,7 @@ public class SqlDebugger { } - public static interface SqlDebugPrinter { + public interface SqlDebugPrinter { SqlDebugPrinter DEFAULT_PRINTER = new SqlDebugPrinter() { @@ -148,7 +148,7 @@ public class SqlDebugger { void print(String sql, Long takedTimeMillis); } - public static interface SqlRunner { + public interface SqlRunner { V run() throws SQLException; } } diff --git a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java index ef9da445..22d8cf1e 100644 --- a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java +++ b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java @@ -15,20 +15,16 @@ */ package io.jboot.db.dbpro; -import com.jfinal.plugin.activerecord.Config; -import com.jfinal.plugin.activerecord.DbPro; -import com.jfinal.plugin.activerecord.Record; +import com.jfinal.plugin.activerecord.*; import com.jfinal.plugin.activerecord.dialect.Dialect; import io.jboot.db.SqlDebugger; import io.jboot.db.dialect.JbootDialect; import io.jboot.db.model.Columns; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.Statement; +import java.sql.*; import java.util.ArrayList; import java.util.List; +import java.util.function.Function; /** * @author Michael Yang 杨福海 (fuhai999@gmail.com) @@ -116,5 +112,41 @@ public class JbootDbPro extends DbPro { return columns.isEmpty() ? delete(sql) : delete(sql, columns.getValueArray()); } + @Override + public void each(Function func, String sql, Object... paras) { + //Connection conn = null; + // try { + // conn = config.getConnection(); + // + // try (PreparedStatement pst = conn.prepareStatement(sql)) { + // config.dialect.fillStatement(pst, paras); + // ResultSet rs = pst.executeQuery(); + // config.dialect.eachRecord(config, rs, func); + // DbKit.close(rs); + // } + // + // } catch (Exception e) { + // throw new ActiveRecordException(e); + // } finally { + // config.close(conn); + // } + Dialect dialect = config.getDialect(); + try { + SqlDebugger.run(() -> { + try (Connection conn = config.getConnection(); + PreparedStatement pst = conn.prepareStatement(sql)) { + + dialect.fillStatement(pst, paras); + + try (ResultSet rs = pst.executeQuery();) { + dialect.eachRecord(config, rs, func); + } + } + return true; + }, config, sql, paras); + } catch (Exception e) { + throw new ActiveRecordException(e); + } + } } -- Gitee From ceddb9ebdcdda47b248a88a892ed7933dd4a605a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 11 Dec 2022 12:18:04 +0800 Subject: [PATCH 1889/1965] upgrade jfinal version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0685aef1..0d8ef341 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ - 5.0.7 + 5.0.8 2022.2 3.3 3.4 -- Gitee From 72fcf4818e76f973761e4c0cea103c61d78560ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 14 Dec 2022 16:50:45 +0800 Subject: [PATCH 1890/1965] add auto trim support for get paras in controller --- .../jboot/web/controller/JbootController.java | 38 ++++++++++++++++--- .../web/converter/TypeConverterFunc.java | 4 +- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 2cc8e61f..7259f1d3 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -464,6 +464,7 @@ public class JbootController extends Controller { } + @NotAction public Map getParas() { Map map = null; @@ -479,19 +480,44 @@ public class JbootController extends Controller { } + @Override + public String getPara() { + return tryToTrim(super.getPara()); + } + + + @Override + public String getPara(String name) { + return tryToTrim(super.getPara(name)); + } + + + @Override + public String getPara(int index, String defaultValue) { + return tryToTrim(super.getPara(index, defaultValue)); + } + + + @Override + public String getPara(String name, String defaultValue) { + return tryToTrim(super.getPara(name, defaultValue)); + } + + @NotAction public String getTrimPara(String name) { - String value = super.getPara(name); - value = (value == null ? null : value.trim()); - return "".equals(value) ? null : value; + return getPara(name); } @NotAction public String getTrimPara(int index) { - String value = super.getPara(index); - value = (value == null ? null : value.trim()); - return "".equals(value) ? null : value; + return getPara(index); + } + + + private String tryToTrim(String value){ + return value != null ? value.trim() : null; } diff --git a/src/main/java/io/jboot/web/converter/TypeConverterFunc.java b/src/main/java/io/jboot/web/converter/TypeConverterFunc.java index 127b11f9..d312bc31 100644 --- a/src/main/java/io/jboot/web/converter/TypeConverterFunc.java +++ b/src/main/java/io/jboot/web/converter/TypeConverterFunc.java @@ -19,6 +19,7 @@ import com.jfinal.core.JFinal; import com.jfinal.core.converter.IConverter; import com.jfinal.core.converter.TypeConverter; import com.jfinal.kit.Func; +import io.jboot.utils.StrUtil; import java.text.ParseException; @@ -33,7 +34,8 @@ public class TypeConverterFunc implements Func.F21, String, Object> { // mysql type: varchar, char, enum, set, text, tinytext, mediumtext, longtext if (type == String.class) { - return ("".equals(s) ? null : s); // 用户在表单域中没有输入内容时将提交过来 "", 因为没有输入,所以要转成 null. +// return ("".equals(s) ? null : s); // 用户在表单域中没有输入内容时将提交过来 "", 因为没有输入,所以要转成 null. + return StrUtil.isBlank(s) ? null : s.trim(); } s = s.trim(); if ("".equals(s)) { // 前面的 String跳过以后,所有的空字符串全都转成 null, 这是合理的 -- Gitee From 91d57f0a5c58c536fedfdf380da98efe4de415b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 16 Dec 2022 12:27:55 +0800 Subject: [PATCH 1891/1965] =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E5=A4=96=E9=83=A8=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/app/config/JbootConfigManager.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 5c6185fa..28c379f7 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -74,6 +74,11 @@ public class JbootConfigManager { mainProperties = JbootConfigKit.readProperties(pathName, fileName); + //可以直接在 默认目录下的 jboot.properties 再次指定外部目录 + fileName = getConfigValue(mainProperties, "jboot_properties_name"); + pathName = getConfigValue(mainProperties, "jboot_properties_path"); + + String mode = getConfigValue("jboot.app.mode"); if (JbootConfigKit.isNotBlank(mode)) { -- Gitee From 8bc4ed2e0da57d8b8574a2c3b1929d647a98088b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 16 Dec 2022 12:33:58 +0800 Subject: [PATCH 1892/1965] =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E5=A4=96=E9=83=A8=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/app/config/JbootConfigManager.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 28c379f7..c9aa2901 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -75,8 +75,15 @@ public class JbootConfigManager { //可以直接在 默认目录下的 jboot.properties 再次指定外部目录 - fileName = getConfigValue(mainProperties, "jboot_properties_name"); - pathName = getConfigValue(mainProperties, "jboot_properties_path"); + String newFileName = getConfigValue(mainProperties, "jboot_properties_name"); + if (newFileName != null && newFileName.trim().length() > 0 && "jboot".equals(fileName)) { + fileName = newFileName; + } + + String newPathName = getConfigValue(mainProperties, "jboot_properties_path"); + if (newPathName != null && newPathName.trim().length() > 0 && (pathName == null || pathName.trim().length() == 0)) { + pathName = newPathName; + } String mode = getConfigValue("jboot.app.mode"); -- Gitee From f8e4096e438e09aea60d8844900226ef7cac0e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 16 Dec 2022 12:38:58 +0800 Subject: [PATCH 1893/1965] v4.0.2 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 7c011702..56b06b4f 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 4.0.1 + 4.0.2 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 8d84d0e7..7bafdf57 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 4.0.1 + 4.0.2 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 95915ec2..1ce84f05 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 4.0.1 + 4.0.2 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='4.0.1', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='4.0.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 1957e7f7..c1b5f28b 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='4.0.1', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='4.0.2', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/4.0.1/jboot-4.0.1.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/4.0.2/jboot-4.0.2.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 84222d7f..d53289ad 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 4.0.1 + 4.0.2 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 7b387043..e304b857 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "4.0.1"; + public static String VERSION = "4.0.2"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From cbc3b5c3194f3402202a008291ae57b5a79de8d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 16 Dec 2022 12:39:02 +0800 Subject: [PATCH 1894/1965] v4.0.2 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 8 ++++++++ pom.xml | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a381073b..c3b70f8f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 4.0.1 + 4.0.2 ``` diff --git a/changes.txt b/changes.txt index c916ec3b..5a90b64d 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,11 @@ +jboot v4.0.2 2022-12-16: +新增:db.each 的 sql 打印输出 +新增:通过 Controller 获取参数时,自动对参数进行 trim 操作 +新增:配置文件可以外部的任意目录 +优化:升级 jfinal 到最新版本 + + + jboot v4.0.1 2022-12-03: 新增:新增可以通过配置取消 AOP 缓存的功能 新增:新增 jboot.cache.useFirstLevelOnly 配置的功能,可以在分布式下只开启 1 级缓存 diff --git a/pom.xml b/pom.xml index 0d8ef341..e1ca58be 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.2-SNAPSHOT + 4.0.2 jar jboot -- Gitee From 2a5f79c5a7e40dac4a00f1602ee88c05e232acf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 16 Dec 2022 12:42:57 +0800 Subject: [PATCH 1895/1965] v4.0.2 release (^.^)YYa!! --- src/main/java/io/jboot/web/controller/JbootController.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/io/jboot/web/controller/JbootController.java b/src/main/java/io/jboot/web/controller/JbootController.java index 7259f1d3..770bd14f 100644 --- a/src/main/java/io/jboot/web/controller/JbootController.java +++ b/src/main/java/io/jboot/web/controller/JbootController.java @@ -481,24 +481,28 @@ public class JbootController extends Controller { @Override + @NotAction public String getPara() { return tryToTrim(super.getPara()); } @Override + @NotAction public String getPara(String name) { return tryToTrim(super.getPara(name)); } @Override + @NotAction public String getPara(int index, String defaultValue) { return tryToTrim(super.getPara(index, defaultValue)); } @Override + @NotAction public String getPara(String name, String defaultValue) { return tryToTrim(super.getPara(name, defaultValue)); } @@ -516,6 +520,7 @@ public class JbootController extends Controller { } + @NotAction private String tryToTrim(String value){ return value != null ? value.trim() : null; } -- Gitee From f4cbc6b03cd16212131d75dc36e969abf3537e1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 16 Dec 2022 12:44:31 +0800 Subject: [PATCH 1896/1965] v4.0.2 release (^.^)YYa!! --- src/main/java/io/jboot/app/config/JbootConfigManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index c9aa2901..374e148f 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -66,7 +66,7 @@ public class JbootConfigManager { private void init() { String fileName = getConfigValue(null, "jboot_properties_name"); - if (fileName == null || fileName.length() == 0) { + if (fileName == null || fileName.trim().length() == 0) { fileName = "jboot"; } -- Gitee From fab239842daaa350db7abb78b0bf8e7aafce1637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 23 Dec 2022 11:49:19 +0800 Subject: [PATCH 1897/1965] v4.0.3 snapshot --- pom.xml | 2 +- .../components/serializer/JbootSerializerManager.java | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index e1ca58be..07b5f7ff 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.2 + 4.0.3-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java b/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java index 2ff5ee1c..83e18090 100644 --- a/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java +++ b/src/main/java/io/jboot/components/serializer/JbootSerializerManager.java @@ -18,7 +18,6 @@ package io.jboot.components.serializer; import io.jboot.Jboot; import io.jboot.core.spi.JbootSpiLoader; import io.jboot.exception.JbootIllegalConfigException; -import io.jboot.utils.ClassUtil; import io.jboot.utils.StrUtil; import java.util.Map; @@ -28,14 +27,11 @@ import java.util.concurrent.ConcurrentHashMap; public class JbootSerializerManager { - private static JbootSerializerManager me; + private static JbootSerializerManager me = new JbootSerializerManager(); private static Map serializerCaches = new ConcurrentHashMap<>(); public static JbootSerializerManager me() { - if (me == null) { - me = ClassUtil.singleton(JbootSerializerManager.class); - } return me; } @@ -48,6 +44,7 @@ public class JbootSerializerManager { return getSerializer(config.getType()); } + public JbootSerializer getSerializer(String serializerName) { JbootSerializer serializer = serializerCaches.get(serializerName); if (serializer == null) { -- Gitee From ee4ac9f48f7d5a3d63aca9484d983fb6a9adfd46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 23 Dec 2022 15:32:39 +0800 Subject: [PATCH 1898/1965] v4.0.3 snapshot --- .../java/io/jboot/components/event/JbootEventManager.java | 7 ++----- src/main/java/io/jboot/components/mq/JbootmqManager.java | 8 +++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/jboot/components/event/JbootEventManager.java b/src/main/java/io/jboot/components/event/JbootEventManager.java index 9cc26c2c..1d29e9ee 100644 --- a/src/main/java/io/jboot/components/event/JbootEventManager.java +++ b/src/main/java/io/jboot/components/event/JbootEventManager.java @@ -28,7 +28,7 @@ import java.util.concurrent.*; public class JbootEventManager { private static final Log LOG = Log.getLog(JbootEventManager.class); - private static JbootEventManager manager; + private static JbootEventManager manager = new JbootEventManager(); private final Map> asyncListenerMap; private final Map> listenerMap; @@ -37,7 +37,7 @@ public class JbootEventManager { private ExecutorService threadPool; - public JbootEventManager() { + private JbootEventManager() { asyncListenerMap = new ConcurrentHashMap<>(); listenerMap = new ConcurrentHashMap<>(); @@ -49,9 +49,6 @@ public class JbootEventManager { } public static JbootEventManager me() { - if (manager == null) { - manager = ClassUtil.singleton(JbootEventManager.class); - } return manager; } diff --git a/src/main/java/io/jboot/components/mq/JbootmqManager.java b/src/main/java/io/jboot/components/mq/JbootmqManager.java index 42a84618..268986d6 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqManager.java +++ b/src/main/java/io/jboot/components/mq/JbootmqManager.java @@ -24,7 +24,6 @@ import io.jboot.components.mq.redismq.JbootRedismqImpl; import io.jboot.components.mq.rocketmq.JbootRocketmqImpl; import io.jboot.core.spi.JbootSpiLoader; import io.jboot.exception.JbootIllegalConfigException; -import io.jboot.utils.ClassUtil; import io.jboot.utils.ConfigUtil; import java.util.Map; @@ -33,12 +32,11 @@ import java.util.concurrent.ConcurrentHashMap; public class JbootmqManager { - private static JbootmqManager manager; + private static JbootmqManager manager = new JbootmqManager(); + + private JbootmqManager(){} public static JbootmqManager me() { - if (manager == null) { - manager = ClassUtil.singleton(JbootmqManager.class); - } return manager; } -- Gitee From 97a3dde46cf10dd4eef424d7d8ae7a172c10cde6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 23 Dec 2022 15:43:58 +0800 Subject: [PATCH 1899/1965] v4.0.3 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 56b06b4f..e70e4287 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 4.0.2 + 4.0.3 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 7bafdf57..2aca8bc1 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 4.0.2 + 4.0.3 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 1ce84f05..bb4184a0 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 4.0.2 + 4.0.3 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='4.0.2', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='4.0.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index c1b5f28b..6b1b5841 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='4.0.2', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='4.0.3', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/4.0.2/jboot-4.0.2.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/4.0.3/jboot-4.0.3.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index d53289ad..6d82e295 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 4.0.2 + 4.0.3 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index e304b857..5bee8512 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "4.0.2"; + public static String VERSION = "4.0.3"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 983036002e09100c8e636fae4c0757e8228eed8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 23 Dec 2022 15:44:02 +0800 Subject: [PATCH 1900/1965] v4.0.3 release (^.^)YYa!! --- README.md | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c3b70f8f..cfaadb8e 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 4.0.2 + 4.0.3 ``` diff --git a/pom.xml b/pom.xml index 07b5f7ff..1cedd3fb 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.3-SNAPSHOT + 4.0.3 jar jboot -- Gitee From ce77d2b91841d503d27ac4aee711a0fde420d7b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 27 Dec 2022 12:45:17 +0800 Subject: [PATCH 1901/1965] v4.0.4 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- .../io/jboot/components/cache/JbootCacheConfig.java | 10 ++++++++++ .../cache/caredis/JbootCaredisCacheImpl.java | 6 +++++- .../cache/ehredis/JbootEhredisCacheImpl.java | 4 ++++ 9 files changed, 27 insertions(+), 9 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index e70e4287..5cd75d0d 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 4.0.3 + 4.0.4 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 2aca8bc1..6dacf50b 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 4.0.3 + 4.0.4 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index bb4184a0..e054fbda 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 4.0.3 + 4.0.4 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='4.0.3', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='4.0.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 6b1b5841..b2c68bb4 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='4.0.3', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='4.0.4', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/4.0.3/jboot-4.0.3.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/4.0.4/jboot-4.0.4.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 6d82e295..9ca249bc 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 4.0.3 + 4.0.4 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 5bee8512..db70984a 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "4.0.3"; + public static String VERSION = "4.0.4"; public static final String ATTR_CONTEXT_PATH = "CPATH"; diff --git a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java index bb347902..ce2f2d1a 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheConfig.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheConfig.java @@ -48,6 +48,8 @@ public class JbootCacheConfig { //可以使用 redis 的消息机制做缓存同步 private boolean useFirstLevelOnly = false; + private String cacheSyncMqChannel; + public String getName() { return name; @@ -97,4 +99,12 @@ public class JbootCacheConfig { public void setUseFirstLevelOnly(boolean useFirstLevelOnly) { this.useFirstLevelOnly = useFirstLevelOnly; } + + public String getCacheSyncMqChannel() { + return cacheSyncMqChannel; + } + + public void setCacheSyncMqChannel(String cacheSyncMqChannel) { + this.cacheSyncMqChannel = cacheSyncMqChannel; + } } diff --git a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java index 8075c880..f234e3bd 100644 --- a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java @@ -60,8 +60,12 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { this.redisCacheImpl = new JbootRedisCacheImpl(config); this.clientId = StrUtil.uuid(); this.serializer = Jboot.getSerializer(); - this.redis = redisCacheImpl.getRedis(); + if (StrUtil.isNotBlank(config.getCacheSyncMqChannel())){ + this.channel = config.getCacheSyncMqChannel(); + } + + this.redis = redisCacheImpl.getRedis(); this.redis.subscribe(new BinaryJedisPubSub() { @Override public void onMessage(byte[] channel, byte[] message) { diff --git a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java index da41c3be..604b81ec 100644 --- a/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehredis/JbootEhredisCacheImpl.java @@ -67,6 +67,10 @@ public class JbootEhredisCacheImpl extends JbootCacheBase implements CacheEventL this.clientId = StrUtil.uuid(); this.serializer = Jboot.getSerializer(); + if (StrUtil.isNotBlank(config.getCacheSyncMqChannel())){ + this.channel = config.getCacheSyncMqChannel(); + } + this.redis = redisCacheImpl.getRedis(); this.redis.subscribe(new BinaryJedisPubSub() { @Override -- Gitee From d52fdae6f0cb4074e5c13ecce28da2356e560726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 27 Dec 2022 12:45:22 +0800 Subject: [PATCH 1902/1965] v4.0.4 release (^.^)YYa!! --- README.md | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cfaadb8e..639b9031 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 4.0.3 + 4.0.4 ``` diff --git a/pom.xml b/pom.xml index 1cedd3fb..07dc2d7b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.3 + 4.0.4 jar jboot -- Gitee From ffa2d0e40f578f7814961dc0145cbae2309ab601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 28 Dec 2022 11:39:01 +0800 Subject: [PATCH 1903/1965] v4.0.5 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 07dc2d7b..aa61881c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.4 + 4.0.5-SNAPSHOT jar jboot -- Gitee From aeee7055c588a50067a2a14d9667006f690aabbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 28 Dec 2022 12:09:25 +0800 Subject: [PATCH 1904/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/cache/caredis/JbootCaredisCacheImpl.java | 1 + .../io/jboot/components/cache/redis/JbootRedisCacheImpl.java | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java index f234e3bd..3f2e41c9 100644 --- a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java @@ -61,6 +61,7 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { this.clientId = StrUtil.uuid(); this.serializer = Jboot.getSerializer(); + //在某些场景下,多个应用使用同一个 redis 实例,此时可以通过配置 cacheSyncMqChannel 来解决缓存冲突的问题 if (StrUtil.isNotBlank(config.getCacheSyncMqChannel())){ this.channel = config.getCacheSyncMqChannel(); } diff --git a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java index 2bd039d7..956db724 100644 --- a/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/redis/JbootRedisCacheImpl.java @@ -41,7 +41,9 @@ public class JbootRedisCacheImpl extends JbootCacheBase { public JbootRedisCacheImpl(JbootCacheConfig config) { super(config); + cacheConfig = Jboot.config(JbootRedisCacheConfig.class); + if (StrUtil.isNotBlank(cacheConfig.getGlobalKeyPrefix())) { globalKeyPrefix = cacheConfig.getGlobalKeyPrefix() + ":"; redisCacheNamesKey = globalKeyPrefix + redisCacheNamesKey; @@ -55,7 +57,8 @@ public class JbootRedisCacheImpl extends JbootCacheBase { } if (redis == null) { - throw new JbootIllegalConfigException("can not get redis, please check your jboot.properties , please correct config jboot.cache.redis.host or jboot.redis.host "); + throw new JbootIllegalConfigException("Can not get redis component in JbootRedisCacheImpl, Please check your jboot.properties " + + "and config jboot.cache.redis.host or jboot.redis.host correct."); } } -- Gitee From 32b319f8178465b76c3bdcfd1a00c6c8f014be49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 28 Dec 2022 12:50:21 +0800 Subject: [PATCH 1905/1965] optimize ClassUtil.java --- src/main/java/io/jboot/utils/ClassUtil.java | 68 +++++++++++++------ .../io/jboot/test/utils/ClassUtilTest.java | 58 ++++++++++++++++ 2 files changed, 105 insertions(+), 21 deletions(-) create mode 100644 src/test/java/io/jboot/test/utils/ClassUtilTest.java diff --git a/src/main/java/io/jboot/utils/ClassUtil.java b/src/main/java/io/jboot/utils/ClassUtil.java index 8d06e620..5fe0ad43 100644 --- a/src/main/java/io/jboot/utils/ClassUtil.java +++ b/src/main/java/io/jboot/utils/ClassUtil.java @@ -21,6 +21,7 @@ import io.jboot.aop.annotation.StaticConstruct; import io.jboot.exception.JbootException; import java.lang.reflect.*; +import java.util.Arrays; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -62,7 +63,7 @@ public class ClassUtil { if (ret != null) { singletons.put(clazz, ret); } else { - LOG.error("Can not new newInstance for class: " + clazz.getName()); + LOG.error("Can not new instance for class: " + clazz.getName()); } } return (T) ret; @@ -78,7 +79,7 @@ public class ClassUtil { } singletons.put(clazz, ret); } else { - LOG.error("Can not new newInstance for class: " + clazz.getName()); + LOG.error("Can not new instance for class: " + clazz.getName()); } } return (T) ret; @@ -126,7 +127,7 @@ public class ClassUtil { constructor.setAccessible(true); return (T) constructor.newInstance(); } catch (Exception e) { - LOG.error("Can not newInstance for class:" + clazz.getName() + "\n" + e, e); + LOG.error("Can not new instance for class:" + clazz.getName() + "\n" + e, e); } return null; @@ -136,29 +137,54 @@ public class ClassUtil { public static T newInstance(Class clazz, boolean createByAop, Object... paras) { try { - Class[] classes = new Class[paras.length]; - for (int i = 0; i < paras.length; i++) { - Object data = paras[i]; - if (data == null) { - throw new IllegalArgumentException("paras must not null"); + Constructor[] constructors = clazz.getDeclaredConstructors(); + for (Constructor constructor : constructors) { + if (isConstructorMatchedParas(constructor, paras)) { +// constructor.setAccessible(true); + Object ret = constructor.newInstance(paras); + if (createByAop) { + Aop.inject(ret); + } + return (T) ret; } - classes[i] = data.getClass(); - } - Constructor constructor = clazz.getDeclaredConstructor(classes); - constructor.setAccessible(true); - Object ret = constructor.newInstance(paras); - if (createByAop) { - Aop.inject(ret); } - return (T) ret; + + throw new IllegalArgumentException("Can not matched constructor by paras: " + Arrays.toString(paras) + " in class: " + clazz.getName()); } catch (Exception e) { - LOG.error("Can not newInstance for class:" + clazz.getName() + "\n" + e, e); + LOG.error("Can not new instance for class: " + clazz.getName() + "\n" + e, e); } return null; } + private static boolean isConstructorMatchedParas(Constructor constructor, Object[] paras) { + if (constructor.getParameterCount() == 0 && (paras == null || paras.length == 0)) { + return true; + } + + if (constructor.getParameterCount() == 0 && paras != null && paras.length > 0) { + return false; + } + + if (constructor.getParameterCount() > 0 + && (paras == null || paras.length != constructor.getParameterCount())) { + return false; + } + + Class[] parameterTypes = constructor.getParameterTypes(); + for (int i = 0; i < parameterTypes.length; i++) { + Class parameterType = parameterTypes[i]; + Object paraObject = paras[i]; + if (paraObject != null && !parameterType.isAssignableFrom(paraObject.getClass())) { + return false; + } + } + + return true; + } + + public static T newInstanceByStaticConstruct(Class clazz) { StaticConstruct staticConstruct = clazz.getAnnotation(StaticConstruct.class); if (staticConstruct == null) { @@ -174,15 +200,15 @@ public class ClassUtil { Method method = getStaticConstruct(staticConstruct.value(), clazz); if (method == null) { - throw new JbootException("Can not new instance by static constrauct for class: " + clazz.getName()); + throw new JbootException("Can not new instance by static construct for class: " + clazz.getName()); } try { return (T) method.invoke(null, null); } catch (Exception e) { - LOG.error("Can not invoke method:" + method.getName() - + " in class : " + clazz.getName() + "\n" + e, e); + LOG.error("Can not invoke method: " + method.getName() + + " in class: " + clazz.getName() + "\n" + e, e); if (e instanceof RuntimeException) { throw (RuntimeException) e; @@ -246,7 +272,7 @@ public class ClassUtil { Class clazz = (Class) Class.forName(clazzName, false, classLoader); return newInstance(clazz, createByAop); } catch (Exception e) { - LOG.error("can not newInstance for class:" + clazzName + "\n" + e.toString(), e); + LOG.error("Can not new instance for class: " + clazzName + "\n" + e.toString(), e); } return null; diff --git a/src/test/java/io/jboot/test/utils/ClassUtilTest.java b/src/test/java/io/jboot/test/utils/ClassUtilTest.java new file mode 100644 index 00000000..e2839ffc --- /dev/null +++ b/src/test/java/io/jboot/test/utils/ClassUtilTest.java @@ -0,0 +1,58 @@ +package io.jboot.test.utils; + +import io.jboot.utils.ClassUtil; +import org.junit.Test; + +public class ClassUtilTest { + + + public static class TestObject{ + private String a; + private String b; + + public TestObject() { + } + + public TestObject(String a) { + this.a = a; + } + + public TestObject(String a, String b) { + this.a = a; + this.b = b; + } + + @Override + public String toString() { + return "TestObject{" + + "a='" + a + '\'' + + ", b='" + b + '\'' + + '}'; + } + } + + + @Test + public void testNewInstance(){ +// TestObject testObject0 = ClassUtil.newInstance(TestObject.class); +// System.out.println(testObject0); +// +// TestObject testObject1 = ClassUtil.newInstance(TestObject.class,"a"); +// System.out.println(testObject1); +// +// TestObject testObject2 = ClassUtil.newInstance(TestObject.class,null); +// System.out.println(testObject2); +// +// TestObject testObject3 = ClassUtil.newInstance(TestObject.class,"aa","bbb"); +// System.out.println(testObject3); +// +// TestObject testObject4 = ClassUtil.newInstance(TestObject.class,null,"bbb"); +// System.out.println(testObject4); + + TestObject testObject5 = ClassUtil.newInstance(TestObject.class,null,"bbb","ccc"); + System.out.println(testObject5); + } + + + +} -- Gitee From e7596b19a82340e51bd2f93db11a77bda6871b19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 29 Dec 2022 10:30:45 +0800 Subject: [PATCH 1906/1965] optimize ClassUtil.java --- src/main/java/io/jboot/utils/ClassUtil.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassUtil.java b/src/main/java/io/jboot/utils/ClassUtil.java index 5fe0ad43..8faa1f43 100644 --- a/src/main/java/io/jboot/utils/ClassUtil.java +++ b/src/main/java/io/jboot/utils/ClassUtil.java @@ -159,12 +159,8 @@ public class ClassUtil { private static boolean isConstructorMatchedParas(Constructor constructor, Object[] paras) { - if (constructor.getParameterCount() == 0 && (paras == null || paras.length == 0)) { - return true; - } - - if (constructor.getParameterCount() == 0 && paras != null && paras.length > 0) { - return false; + if (constructor.getParameterCount() == 0) { + return paras == null || paras.length == 0; } if (constructor.getParameterCount() > 0 -- Gitee From f57dafc9e2dccbb19b293a70ec65c5308a82f5b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 29 Dec 2022 21:34:44 +0800 Subject: [PATCH 1907/1965] optimize ... --- src/main/java/io/jboot/components/cache/JbootCacheManager.java | 2 +- .../io/jboot/components/cache/ehcache/JbootEhcacheImpl.java | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/JbootCacheManager.java b/src/main/java/io/jboot/components/cache/JbootCacheManager.java index 765ae847..bb782f25 100644 --- a/src/main/java/io/jboot/components/cache/JbootCacheManager.java +++ b/src/main/java/io/jboot/components/cache/JbootCacheManager.java @@ -57,7 +57,7 @@ public class JbootCacheManager { public JbootCache getCache(String name) { if (StrUtil.isBlank(name)) { - throw new IllegalArgumentException("cache name must not be null or blank."); + throw new IllegalArgumentException("Cache name must not be null or blank."); } JbootCache cache = cacheMap.get(name); diff --git a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java index b52f6143..8c97f164 100644 --- a/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java +++ b/src/main/java/io/jboot/components/cache/ehcache/JbootEhcacheImpl.java @@ -16,7 +16,6 @@ package io.jboot.components.cache.ehcache; import com.jfinal.kit.PathKit; -import com.jfinal.log.Log; import com.jfinal.plugin.ehcache.IDataLoader; import io.jboot.Jboot; import io.jboot.components.cache.JbootCacheBase; @@ -36,8 +35,6 @@ public class JbootEhcacheImpl extends JbootCacheBase { private CacheManager cacheManager; private static Object locker = new Object(); - private static final Log log = Log.getLog(JbootEhcacheImpl.class); - private CacheEventListener cacheEventListener; public JbootEhcacheImpl(JbootCacheConfig config) { -- Gitee From a3f56aa61b33a122b2ad4f6335b53506647cd7a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 29 Dec 2022 21:34:52 +0800 Subject: [PATCH 1908/1965] remove GenTester.java --- src/main/java/io/jboot/codegen/GenTester.java | 54 ------------------- 1 file changed, 54 deletions(-) delete mode 100644 src/main/java/io/jboot/codegen/GenTester.java diff --git a/src/main/java/io/jboot/codegen/GenTester.java b/src/main/java/io/jboot/codegen/GenTester.java deleted file mode 100644 index 4fab6994..00000000 --- a/src/main/java/io/jboot/codegen/GenTester.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). - *

        - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

        - * http://www.apache.org/licenses/LICENSE-2.0 - *

        - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.jboot.codegen; - -import io.jboot.app.JbootApplication; -import io.jboot.codegen.model.JbootBaseModelGenerator; -import io.jboot.codegen.model.JbootModelGenerator; -import io.jboot.codegen.service.JbootServiceImplGenerator; -import io.jboot.codegen.service.JbootServiceInterfaceGenerator; - -/** - * @author Michael Yang 杨福海 (fuhai999@gmail.com) - * @version V1.0 - */ -public class GenTester { - - public static void main(String[] args) { - - JbootApplication.setBootArg("jboot.datasource.url", "jdbc:mysql://127.0.0.1:3306/jbootdemo"); - JbootApplication.setBootArg("jboot.datasource.user", "root"); - - String modelPackage = "io.jboot.codegen.test.model"; - String baseModelPackage = modelPackage + ".base"; - - String modelDir = CodeGenHelpler.getUserDir() + "/src/main/java/" + modelPackage.replace(".", "/"); - String baseModelDir = CodeGenHelpler.getUserDir() + "/src/main/java/" + baseModelPackage.replace(".", "/"); - - System.out.println("start generate..."); - System.out.println("generate dir:" + modelDir); - - - new JbootBaseModelGenerator(baseModelPackage, baseModelDir).setGenerateRemarks(true).generate(); - new JbootModelGenerator(modelPackage, baseModelPackage, modelDir).generate(); - - - String servicePackage = "io.jboot.codegen.test.service"; - new JbootServiceInterfaceGenerator(servicePackage, modelPackage).generate(); - new JbootServiceImplGenerator(servicePackage , modelPackage).setImplName("provider").generate(); - - } -} -- Gitee From 0b748541c5231a4b966f3e4d15bc38bfeb75ae9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 29 Dec 2022 23:05:48 +0800 Subject: [PATCH 1909/1965] v4.0.5 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 4 ++-- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 5cd75d0d..3e895bb9 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 4.0.4 + 4.0.5 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 6dacf50b..8aab1883 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 4.0.4 + 4.0.5 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index e054fbda..4ea3366b 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 4.0.4 + 4.0.5 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='4.0.4', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='4.0.5', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index b2c68bb4..0083f3ca 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='4.0.4', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='4.0.5', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 @@ -126,7 +126,7 @@ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/ ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/4.0.4/jboot-4.0.4.jar +ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/4.0.5/jboot-4.0.5.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 9ca249bc..9ec3e6e1 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 4.0.4 + 4.0.5 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index db70984a..3af268ad 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "4.0.4"; + public static String VERSION = "4.0.5"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 9979f863c59d21cdc4f10f4367b131c5a474486f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 29 Dec 2022 23:05:57 +0800 Subject: [PATCH 1910/1965] v4.0.5 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 11 +++++++++++ pom.xml | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 639b9031..8846f474 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 4.0.4 + 4.0.5 ``` diff --git a/changes.txt b/changes.txt index 5a90b64d..064173d9 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,14 @@ +jboot v4.0.5 2022-12-29: +新增:jboot.cache.cacheSyncMqChannel 的配置,用于对分布式缓存的 channel 进行自定义 +优化:JbootRedisCacheImpl 初始化的错误提示内容 +优化:ClassUtil.newInstance() 方法,方便对构造器进行传参 +优化:删除无用的 GenTester.java 文件 +优化:不再对 JbootSerializerManager 进行 Aop 增强 +优化:不再对 JbootmqManager 进行 Aop 增强 +优化:不再对 JbootEventManager 进行 Aop 增强 + + + jboot v4.0.2 2022-12-16: 新增:db.each 的 sql 打印输出 新增:通过 Controller 获取参数时,自动对参数进行 trim 操作 diff --git a/pom.xml b/pom.xml index aa61881c..f5e85f27 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.5-SNAPSHOT + 4.0.5 jar jboot -- Gitee From 0b0a92c36e91a6493520d2417103a5d380d578ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 31 Dec 2022 20:27:45 +0800 Subject: [PATCH 1911/1965] v4.0.6 snapshot --- pom.xml | 2 +- .../java/io/jboot/components/cache/interceptor/Utils.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index f5e85f27..ec0c08e1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.5 + 4.0.6-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/components/cache/interceptor/Utils.java b/src/main/java/io/jboot/components/cache/interceptor/Utils.java index 76f2d46a..5e83feed 100644 --- a/src/main/java/io/jboot/components/cache/interceptor/Utils.java +++ b/src/main/java/io/jboot/components/cache/interceptor/Utils.java @@ -146,7 +146,7 @@ class Utils { || clazz == LocalDateTime.class || clazz == LocalTime.class || clazz.isArray() - || clazz.isAssignableFrom(Collection.class) + || Collection.class.isAssignableFrom(clazz) || clazz == Columns.class ; @@ -159,8 +159,8 @@ class Utils { if (!isSupportClass(object.getClass())) { String msg = "Unsupport empty key for annotation @Cacheable, @CacheEvict or @CachePut " + - "at method[" + ClassUtil.buildMethodString(method) + "], " + - "Please config key properties in the annotation."; + "at method [" + ClassUtil.buildMethodString(method) + "], " + + "please config key in the annotation."; throw new IllegalArgumentException(msg); } -- Gitee From 844f80b38b59566e6e3e7d9483e7a2793369e9e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 31 Dec 2022 20:40:10 +0800 Subject: [PATCH 1912/1965] optimize ApplicationUtil.java --- src/main/java/io/jboot/app/ApplicationUtil.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/app/ApplicationUtil.java b/src/main/java/io/jboot/app/ApplicationUtil.java index b4bcb192..c2627a55 100644 --- a/src/main/java/io/jboot/app/ApplicationUtil.java +++ b/src/main/java/io/jboot/app/ApplicationUtil.java @@ -89,9 +89,13 @@ public class ApplicationUtil { if (runInFatjar()) { System.out.println("JbootApplication is running in fatjar."); } else { - System.out.println("JbootApplication ClassPath: " + ApplicationUtil.class.getResource("/").toURI().getPath()); + String path = ApplicationUtil.class.getResource("/").toURI().getPath(); + if (path.indexOf(":") == 2) { + path = path.substring(1); + } + System.out.println("JbootApplication ClassPath: " + path); } - } catch (URISyntaxException e) { + } catch (Exception e) { e.printStackTrace(); } } -- Gitee From 33ee3ab277f2c68f747b7d2e80417befeda0b49d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 31 Dec 2022 20:49:32 +0800 Subject: [PATCH 1913/1965] optimize ApplicationUtil.java --- src/main/java/io/jboot/app/ApplicationUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/app/ApplicationUtil.java b/src/main/java/io/jboot/app/ApplicationUtil.java index c2627a55..4b23e72b 100644 --- a/src/main/java/io/jboot/app/ApplicationUtil.java +++ b/src/main/java/io/jboot/app/ApplicationUtil.java @@ -19,7 +19,6 @@ import io.jboot.app.config.JbootConfigManager; import java.io.File; import java.io.UnsupportedEncodingException; -import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; @@ -90,10 +89,11 @@ public class ApplicationUtil { System.out.println("JbootApplication is running in fatjar."); } else { String path = ApplicationUtil.class.getResource("/").toURI().getPath(); + // 例如: /D:/JAVA/workSpace_idea/... if (path.indexOf(":") == 2) { path = path.substring(1); } - System.out.println("JbootApplication ClassPath: " + path); + System.out.println("JbootApplication classpath: " + path); } } catch (Exception e) { e.printStackTrace(); -- Gitee From 3036d745c9acd31cda8637f87823896d85c0b5c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 31 Dec 2022 20:56:07 +0800 Subject: [PATCH 1914/1965] optimize ApplicationUtil.java --- src/main/java/io/jboot/app/ApplicationUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/app/ApplicationUtil.java b/src/main/java/io/jboot/app/ApplicationUtil.java index 4b23e72b..f4c1d5a6 100644 --- a/src/main/java/io/jboot/app/ApplicationUtil.java +++ b/src/main/java/io/jboot/app/ApplicationUtil.java @@ -88,7 +88,7 @@ public class ApplicationUtil { if (runInFatjar()) { System.out.println("JbootApplication is running in fatjar."); } else { - String path = ApplicationUtil.class.getResource("/").toURI().getPath(); + String path = ApplicationUtil.class.getResource("/").getPath(); // 例如: /D:/JAVA/workSpace_idea/... if (path.indexOf(":") == 2) { path = path.substring(1); -- Gitee From 1d6a199112888c0b5235a6dfe02564f29adc154b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 31 Dec 2022 21:09:53 +0800 Subject: [PATCH 1915/1965] optimize --- src/main/java/io/jboot/aop/DefaultValueInterceptor.java | 1 + src/main/java/io/jboot/aop/ValueFilterInterceptor.java | 7 ++++--- src/main/java/io/jboot/app/JbootApplicationConfig.java | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/aop/DefaultValueInterceptor.java b/src/main/java/io/jboot/aop/DefaultValueInterceptor.java index b2d5fd47..5822a955 100644 --- a/src/main/java/io/jboot/aop/DefaultValueInterceptor.java +++ b/src/main/java/io/jboot/aop/DefaultValueInterceptor.java @@ -65,6 +65,7 @@ public class DefaultValueInterceptor implements Interceptor, InterceptorBuilder for (Parameter p : parameters) { if (p.getAnnotation(DefaultValue.class) != null) { interceptors.addIfNotExist(this); + break; } } } diff --git a/src/main/java/io/jboot/aop/ValueFilterInterceptor.java b/src/main/java/io/jboot/aop/ValueFilterInterceptor.java index 79ce1385..a5c44134 100644 --- a/src/main/java/io/jboot/aop/ValueFilterInterceptor.java +++ b/src/main/java/io/jboot/aop/ValueFilterInterceptor.java @@ -33,15 +33,15 @@ public class ValueFilterInterceptor implements Interceptor, InterceptorBuilder { for (int index = 0; index < parameters.length; index++) { FilterBy filter = parameters[index].getAnnotation(FilterBy.class); if (filter != null) { - Object orignal = inv.getArg(index); + Object original = inv.getArg(index); Class[] classes = filter.value(); for (Class aClass : classes) { ValueFilter vf = Aop.get(aClass); - orignal = vf.doFilter(orignal); + original = vf.doFilter(original); } - inv.setArg(index, orignal); + inv.setArg(index, original); } } @@ -56,6 +56,7 @@ public class ValueFilterInterceptor implements Interceptor, InterceptorBuilder { for (Parameter p : parameters) { if (p.getAnnotation(FilterBy.class) != null) { interceptors.addIfNotExist(this); + break; } } } diff --git a/src/main/java/io/jboot/app/JbootApplicationConfig.java b/src/main/java/io/jboot/app/JbootApplicationConfig.java index 50f1d65c..bd350005 100644 --- a/src/main/java/io/jboot/app/JbootApplicationConfig.java +++ b/src/main/java/io/jboot/app/JbootApplicationConfig.java @@ -32,7 +32,7 @@ public class JbootApplicationConfig { private String listener = "*"; private String listenerPackage = "*"; private boolean handle404 = true; - private String proxy; //cglib or javassist + private String proxy; //cglib or javassist public String getMode() { -- Gitee From 1dbc4dba70816db1e5789b7015b76691399363ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 31 Dec 2022 21:41:50 +0800 Subject: [PATCH 1916/1965] optimize --- src/main/java/io/jboot/codegen/CodeGenHelpler.java | 4 ++-- src/main/java/io/jboot/db/ArpManager.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/codegen/CodeGenHelpler.java b/src/main/java/io/jboot/codegen/CodeGenHelpler.java index baeb9f4a..16afe3fa 100644 --- a/src/main/java/io/jboot/codegen/CodeGenHelpler.java +++ b/src/main/java/io/jboot/codegen/CodeGenHelpler.java @@ -103,7 +103,7 @@ public class CodeGenHelpler { metaBuilder.setDialect(new InformixDialect()); break; default: - throw new JbootIllegalConfigException("Only support datasource type : mysql、orcale、sqlserver、sqlite、ansisql、postgresql and infomix" + + throw new JbootIllegalConfigException("Only support datasource type: mysql、orcale、sqlserver、sqlite、ansisql、postgresql and infomix" + ", please check your jboot.properties. "); } @@ -123,7 +123,7 @@ public class CodeGenHelpler { Set excludeTableSet = StrUtil.splitToSet(excludeTables.toLowerCase(), ","); for (TableMeta tableMeta : list) { if (excludeTableSet.contains(tableMeta.name.toLowerCase())) { - System.out.println("exclude table : " + tableMeta.name); + System.out.println("exclude table: " + tableMeta.name); continue; } newTableMetaList.add(tableMeta); diff --git a/src/main/java/io/jboot/db/ArpManager.java b/src/main/java/io/jboot/db/ArpManager.java index 2540d7f5..589f1d1c 100644 --- a/src/main/java/io/jboot/db/ArpManager.java +++ b/src/main/java/io/jboot/db/ArpManager.java @@ -219,7 +219,7 @@ public class ArpManager { if (datasourceConfig.getDialectClass() != null) { Dialect dialect = ClassUtil.newInstance(datasourceConfig.getDialectClass(), false); if (dialect == null) { - throw new NullPointerException("can not new instance by class:" + datasourceConfig.getDialectClass()); + throw new JbootIllegalConfigException("Can not new instance by class: " + datasourceConfig.getDialectClass()); } activeRecordPlugin.setDialect(dialect); return; @@ -257,7 +257,7 @@ public class ArpManager { activeRecordPlugin.setDialect(new JbootInformixDialect()); break; default: - throw new JbootIllegalConfigException("only support datasource type : mysql、orcale、sqlserver、sqlite、ansisql、postgresql and clickhouse, please check your jboot.properties. "); + throw new JbootIllegalConfigException("only support datasource type: mysql、orcale、sqlserver、sqlite、ansisql、postgresql and clickhouse, please check your jboot.properties. "); } } -- Gitee From aea134c93d8a3ebf566d20908ffcfa4076339649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 31 Dec 2022 22:11:09 +0800 Subject: [PATCH 1917/1965] optimize --- .../jboot/app/config/JbootConfigManager.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigManager.java b/src/main/java/io/jboot/app/config/JbootConfigManager.java index 374e148f..d9c2ebd6 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigManager.java +++ b/src/main/java/io/jboot/app/config/JbootConfigManager.java @@ -66,7 +66,7 @@ public class JbootConfigManager { private void init() { String fileName = getConfigValue(null, "jboot_properties_name"); - if (fileName == null || fileName.trim().length() == 0) { + if (fileName == null || fileName.length() == 0) { fileName = "jboot"; } @@ -76,19 +76,25 @@ public class JbootConfigManager { //可以直接在 默认目录下的 jboot.properties 再次指定外部目录 String newFileName = getConfigValue(mainProperties, "jboot_properties_name"); - if (newFileName != null && newFileName.trim().length() > 0 && "jboot".equals(fileName)) { + if (newFileName != null && newFileName.length() > 0 && "jboot".equals(fileName)) { fileName = newFileName; } String newPathName = getConfigValue(mainProperties, "jboot_properties_path"); - if (newPathName != null && newPathName.trim().length() > 0 && (pathName == null || pathName.trim().length() == 0)) { + if (newPathName != null && newPathName.length() > 0 && (pathName == null || pathName.length() == 0)) { pathName = newPathName; } + //配置了 pathName,需要再去 path 读取 jboot.properties 文件 + if (pathName != null && pathName.length() > 0) { + Properties newMainProperties = JbootConfigKit.readProperties(pathName, fileName); + mainProperties.putAll(newMainProperties); + } + + String mode = getConfigValue("jboot.app.mode"); if (JbootConfigKit.isNotBlank(mode)) { - //开始加载 mode properties //并全部添加覆盖掉掉 main properties String modeFileName = fileName + "-" + mode; @@ -268,12 +274,12 @@ public class JbootConfigManager { public String getConfigValue(Properties properties, String key) { if (JbootConfigKit.isBlank(key)) { - return ""; + return null; } String originalValue = getOriginalConfigValue(properties, key); - String stringValue = decryptor != null ? decryptor.decrypt(key, originalValue) : originalValue; - - return JbootConfigKit.parseValue(this, stringValue); + String decryptValue = decryptor != null ? decryptor.decrypt(key, originalValue) : originalValue; + String parseValue = JbootConfigKit.parseValue(this, decryptValue); + return parseValue == null || parseValue.trim().length() == 0 ? null : parseValue.trim(); } -- Gitee From 1129af6bcbfcacff62d1585a5f6885debf5e9e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 31 Dec 2022 22:17:11 +0800 Subject: [PATCH 1918/1965] optimize --- src/main/java/io/jboot/app/config/JbootConfigKit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/app/config/JbootConfigKit.java b/src/main/java/io/jboot/app/config/JbootConfigKit.java index 67905113..6829752d 100644 --- a/src/main/java/io/jboot/app/config/JbootConfigKit.java +++ b/src/main/java/io/jboot/app/config/JbootConfigKit.java @@ -186,7 +186,7 @@ public class JbootConfigKit { private static String appendSuffixIfNecessary(String fileName) { fileName = fileName.trim(); - return fileName.endsWith(".properties") ? fileName : fileName + ".properties"; + return fileName.toLowerCase().endsWith(".properties") ? fileName : fileName + ".properties"; } -- Gitee From 9e9b986405088298f2151b64acf4a2ed9af50feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 31 Dec 2022 22:27:13 +0800 Subject: [PATCH 1919/1965] v4.0.6 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 12 +----------- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 7 insertions(+), 17 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 3e895bb9..b17b41cc 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 4.0.5 + 4.0.6 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 8aab1883..67587cbb 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 4.0.5 + 4.0.6 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 4ea3366b..1c8a0a37 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 4.0.5 + 4.0.6 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='4.0.5', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='4.0.6', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 0083f3ca..2d6f5024 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,20 +113,10 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='4.0.5', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='4.0.6', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 -ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes -ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-model/target/classes -ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-codegen/target/classes -ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-web/target/classes -ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-web/target/classes -ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-provider/target/classes -ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/module-cms/module-cms-web/target/classes -ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-commons/target/classes -ClassScanner scan classpath : /Users/michael/work/git/JbootAdmin/core-framework/core-framework-service/target/classes -ClassScanner scan jar : /Users/michael/.m2/repository/io/jboot/jboot/4.0.5/jboot-4.0.5.jar Starting Complete in 2.3 seconds. Welcome To The JFinal World (^_^) diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 9ec3e6e1..67ba3cdd 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 4.0.5 + 4.0.6 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 3af268ad..e65d74ae 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "4.0.5"; + public static String VERSION = "4.0.6"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From bcc5e8a6dd741cba47be61938f9fae3af76f4f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 31 Dec 2022 22:27:18 +0800 Subject: [PATCH 1920/1965] v4.0.6 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 8 ++++++++ pom.xml | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8846f474..4ae32b81 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 4.0.5 + 4.0.6 ``` diff --git a/changes.txt b/changes.txt index 064173d9..ec75ebe0 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,11 @@ +jboot v4.0.6 2022-12-31: +修复:缓存注解自动生成 key 无法支持集合参数的问题 +优化:ApplicationUtil.java 使其在 window 下输出正确的 classpath 路径 +优化:修改 ValueFilterInterceptor.java 里的错别字 +优化:JbootConfigManager.java 配置独立目录时,再次读取 jboot.properties 文件 + + + jboot v4.0.5 2022-12-29: 新增:jboot.cache.cacheSyncMqChannel 的配置,用于对分布式缓存的 channel 进行自定义 优化:JbootRedisCacheImpl 初始化的错误提示内容 diff --git a/pom.xml b/pom.xml index ec0c08e1..4b956db8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.6-SNAPSHOT + 4.0.6 jar jboot -- Gitee From a21240112dcceeefa6fde6d998d0735804565e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 5 Jan 2023 12:49:20 +0800 Subject: [PATCH 1921/1965] v4.0.7 snapshot --- pom.xml | 2 +- .../io/jboot/web/attachment/AttachmentManager.java | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 4b956db8..f07a226c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.6 + 4.0.7-SNAPSHOT jar jboot diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index 65621d2d..05bf8d13 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -230,7 +230,7 @@ public class AttachmentManager { //优先从 默认的 container 去获取 if (localFirst) { File localFile = defaultContainer.getFile(relativePath); - if (localFile.exists()) { + if (localFile.exists() && localFile.length() > 0) { return localFile; } } @@ -239,7 +239,7 @@ public class AttachmentManager { if (container != defaultContainer) { try { File file = container.getFile(relativePath); - if (file != null && file.exists()) { + if (file != null && file.exists() && file.length() > 0) { return file; } } catch (Exception ex) { @@ -287,7 +287,13 @@ public class AttachmentManager { public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { if (target.startsWith(defaultContainer.getTargetPrefix()) && target.lastIndexOf('.') != -1) { - Render render = getFileRender(getFile(target)); + Render render = null; + if (target.contains("..")) { + render = renderFactory.getErrorRender(404); + } else { + File file = getFile(target); + render = getFileRender(file); + } render.setContext(request, response).render(); return true; } else { -- Gitee From 3b17def481ca7b3130e2d86a9471895c05f29cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 5 Jan 2023 19:02:53 +0800 Subject: [PATCH 1922/1965] add StrUtil.isStartsWithAny --- src/main/java/io/jboot/utils/StrUtil.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/io/jboot/utils/StrUtil.java b/src/main/java/io/jboot/utils/StrUtil.java index 077d4157..cd7bf96a 100644 --- a/src/main/java/io/jboot/utils/StrUtil.java +++ b/src/main/java/io/jboot/utils/StrUtil.java @@ -136,6 +136,20 @@ public class StrUtil extends StrKit { return false; } + public static boolean isStartsWithAny(String str, String... prefixes) { + if (isBlank(str) || prefixes == null || prefixes.length == 0) { + return false; + } + + for (String prefix : prefixes) { + if (str.startsWith(prefix)) { + return true; + } + } + return false; + } + + public static String requireNonBlank(String str) { if (isBlank(str)) { throw new NullPointerException(); @@ -200,6 +214,7 @@ public class StrUtil extends StrKit { /** * null 或者 空内容字符串 * 使用 isBlank 代替 + * * @param str * @return */ -- Gitee From 59988aac089e49e51951e743fc21d2d1d2fa55e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 5 Jan 2023 19:21:14 +0800 Subject: [PATCH 1923/1965] add FileUtil.getFileMD5() --- src/main/java/io/jboot/utils/FileUtil.java | 53 ++++++++++++++++++- .../io/jboot/test/utils/FileUtilTest.java | 13 +++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 src/test/java/io/jboot/test/utils/FileUtilTest.java diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index df6e4273..909589ef 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -16,6 +16,8 @@ package io.jboot.utils; import com.jfinal.core.JFinal; +import com.jfinal.kit.Base64Kit; +import com.jfinal.kit.HashKit; import com.jfinal.kit.LogKit; import com.jfinal.kit.PathKit; import com.jfinal.upload.UploadFile; @@ -23,6 +25,7 @@ import com.jfinal.upload.UploadFile; import java.io.*; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; import java.util.Enumeration; import java.util.List; import java.util.zip.ZipEntry; @@ -31,6 +34,12 @@ import java.util.zip.ZipFile; public class FileUtil { + /** + * 获取文件后缀 + * + * @param fileName eg: jboot.jpg + * @return suffix .jpg + */ public static String getSuffix(String fileName) { if (fileName != null && fileName.contains(".")) { return fileName.substring(fileName.lastIndexOf(".")); @@ -100,10 +109,14 @@ public class FileUtil { public static void writeString(File file, String content, String charsetName) { + writeString(file, content, charsetName, false); + } + + public static void writeString(File file, String content, String charsetName, boolean append) { FileOutputStream fos = null; try { ensuresParentExists(file); - fos = new FileOutputStream(file, false); + fos = new FileOutputStream(file, append); fos.write(content.getBytes(charsetName)); } catch (Exception e) { LogKit.error(e.toString(), e); @@ -120,6 +133,44 @@ public class FileUtil { } + /** + * 获取文件的 md5 + * + * @param file + * @return + */ + public static String getFileMD5(File file) { + return getFileMD5(file, false); + } + + + /** + * 获取文件 md5 的 base64 编码 + * + * @param file + * @return + */ + public static String getFileMd5Base64(File file) { + return getFileMD5(file, true); + } + + + private static String getFileMD5(File file, boolean withBase64) { + try (FileInputStream fileInputStream = new FileInputStream(file)) { + MessageDigest digest = MessageDigest.getInstance("MD5"); + byte[] buffer = new byte[8192]; + int length; + while ((length = fileInputStream.read(buffer)) != -1) { + digest.update(buffer, 0, length); + } + return withBase64 ? Base64Kit.encode(digest.digest()) : HashKit.toHex(digest.digest()); + } catch (Exception e) { + LogKit.error(e.toString(), e); + } + return null; + } + + public static void close(Closeable... closeable) { QuietlyUtil.closeQuietly(closeable); } diff --git a/src/test/java/io/jboot/test/utils/FileUtilTest.java b/src/test/java/io/jboot/test/utils/FileUtilTest.java new file mode 100644 index 00000000..f1af92d9 --- /dev/null +++ b/src/test/java/io/jboot/test/utils/FileUtilTest.java @@ -0,0 +1,13 @@ +package io.jboot.test.utils; + +import io.jboot.utils.FileUtil; +import org.junit.Test; + +public class FileUtilTest { + + @Test + public void testGetSuffix(){ + String suffix = FileUtil.getSuffix("aaa/bbb/ccc.jpg"); + System.out.println(suffix); + } +} -- Gitee From a97a79361fe80999a8dd80d7c56ae905ae8b8b4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Thu, 5 Jan 2023 19:40:10 +0800 Subject: [PATCH 1924/1965] add CdnUtil.appendCdnDomain --- .../java/io/jboot/web/render/cdn/CdnUtil.java | 21 +++++++++++++++++-- .../web/render/cdn/JbootWebCdnConfig.java | 4 ++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/web/render/cdn/CdnUtil.java b/src/main/java/io/jboot/web/render/cdn/CdnUtil.java index 94d3ba45..f6cacf29 100644 --- a/src/main/java/io/jboot/web/render/cdn/CdnUtil.java +++ b/src/main/java/io/jboot/web/render/cdn/CdnUtil.java @@ -16,6 +16,7 @@ package io.jboot.web.render.cdn; import com.jfinal.core.JFinal; +import io.jboot.Jboot; import io.jboot.utils.StrUtil; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -32,11 +33,27 @@ import java.util.Iterator; */ public class CdnUtil { - private static String charSet = JFinal.me().getConstants().getEncoding(); + private static String charSet = JFinal.me().getConstants().getEncoding(); + private static JbootWebCdnConfig cdnConfig = Jboot.config(JbootWebCdnConfig.class); + + public static String appendCdnDomain(String path) { + if (StrUtil.isBlank(path)) { + return path; + } + + if (cdnConfig.isEnable() && StrUtil.isNotBlank(cdnConfig.getDomain())) { + if (!path.startsWith("/")) { + path = "/" + path; + } + return cdnConfig.getDomain() + path; + } + + return path; + } public static String toHtml(InputStream content, String domain) throws IOException { - Document doc = Jsoup.parse(content,charSet,""); + Document doc = Jsoup.parse(content, charSet, ""); Elements jsElements = doc.select("script[src]"); replace(jsElements, "src", domain); diff --git a/src/main/java/io/jboot/web/render/cdn/JbootWebCdnConfig.java b/src/main/java/io/jboot/web/render/cdn/JbootWebCdnConfig.java index f9e4f6d2..99e24db5 100644 --- a/src/main/java/io/jboot/web/render/cdn/JbootWebCdnConfig.java +++ b/src/main/java/io/jboot/web/render/cdn/JbootWebCdnConfig.java @@ -37,6 +37,10 @@ public class JbootWebCdnConfig { } public void setDomain(String domain) { + if (domain.endsWith("/")) { + domain = domain.substring(0, domain.length() - 1); + } this.domain = domain; } + } -- Gitee From 300f3965e47596fbfeed4707d297b3ab269d398e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 6 Jan 2023 13:12:42 +0800 Subject: [PATCH 1925/1965] optimize ... --- src/main/java/io/jboot/utils/FileUtil.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/utils/FileUtil.java b/src/main/java/io/jboot/utils/FileUtil.java index 909589ef..7adddaf8 100644 --- a/src/main/java/io/jboot/utils/FileUtil.java +++ b/src/main/java/io/jboot/utils/FileUtil.java @@ -38,7 +38,7 @@ public class FileUtil { * 获取文件后缀 * * @param fileName eg: jboot.jpg - * @return suffix .jpg + * @return suffix eg: .jpg */ public static String getSuffix(String fileName) { if (fileName != null && fileName.contains(".")) { @@ -156,11 +156,11 @@ public class FileUtil { private static String getFileMD5(File file, boolean withBase64) { - try (FileInputStream fileInputStream = new FileInputStream(file)) { + try (FileInputStream fiStream = new FileInputStream(file)) { MessageDigest digest = MessageDigest.getInstance("MD5"); byte[] buffer = new byte[8192]; int length; - while ((length = fileInputStream.read(buffer)) != -1) { + while ((length = fiStream.read(buffer)) != -1) { digest.update(buffer, 0, length); } return withBase64 ? Base64Kit.encode(digest.digest()) : HashKit.toHex(digest.digest()); -- Gitee From d2112b355ebff22e9e412f445fe50a93da6b42ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 6 Jan 2023 18:00:41 +0800 Subject: [PATCH 1926/1965] v4.0.7 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 +- doc/jbootadmin/start.md | 2 +- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- .../web/attachment/AttachmentManager.java | 39 ++++++++++++------- 7 files changed, 31 insertions(+), 22 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index b17b41cc..577f0418 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 4.0.6 + 4.0.7 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 67587cbb..f8f6ccbc 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 4.0.6 + 4.0.7 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 1c8a0a37..302005eb 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 4.0.6 + 4.0.7 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='4.0.6', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='4.0.7', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 2d6f5024..0cf7a103 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='4.0.6', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='4.0.7', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 67ba3cdd..341aa906 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 4.0.6 + 4.0.7 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index e65d74ae..7aa646c2 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "4.0.6"; + public static String VERSION = "4.0.7"; public static final String ATTR_CONTEXT_PATH = "CPATH"; diff --git a/src/main/java/io/jboot/web/attachment/AttachmentManager.java b/src/main/java/io/jboot/web/attachment/AttachmentManager.java index 05bf8d13..306525c6 100644 --- a/src/main/java/io/jboot/web/attachment/AttachmentManager.java +++ b/src/main/java/io/jboot/web/attachment/AttachmentManager.java @@ -37,7 +37,6 @@ public class AttachmentManager { private static final Log LOG = Log.getLog(AttachmentManager.class); - private static Map managers = new HashMap<>(); public static AttachmentManager me() { @@ -58,29 +57,39 @@ public class AttachmentManager { return manager; } - private AttachmentManager(String name) { + /** + * 通过这个方式可以来更改 manager 包括默认的 manager + * + * @param manager + */ + public static void setManager(AttachmentManager manager) { + managers.put(manager.name, manager); + } + + + public AttachmentManager(String name) { this.name = name; } /** * 默认的 附件容器 */ - private LocalAttachmentContainer defaultContainer = new LocalAttachmentContainer(); + protected LocalAttachmentContainer defaultContainer = new LocalAttachmentContainer(); /** * 其他附件容器 */ - private List containers = new CopyOnWriteArrayList<>(); + protected List containers = new CopyOnWriteArrayList<>(); /** * 自定义文件渲染器 */ - private IRenderFactory renderFactory = RenderManager.me().getRenderFactory(); + protected IRenderFactory renderFactory = RenderManager.me().getRenderFactory(); /** * manager 的名称 */ - private final String name; + protected final String name; public String getName() { @@ -135,7 +144,7 @@ public class AttachmentManager { container.saveFile(defaultContainerFile); } } catch (Exception ex) { - LOG.error("Save file error in container :" + container, ex); + LOG.error("Save file error in container: " + container, ex); } } return relativePath.replace("\\", "/"); @@ -159,7 +168,7 @@ public class AttachmentManager { container.saveFile(defaultContainerFile, toRelativePath); } } catch (Exception ex) { - LOG.error("Save file error in container :" + container, ex); + LOG.error("Save file error in container: " + container, ex); } } return relativePath.replace("\\", "/"); @@ -182,7 +191,7 @@ public class AttachmentManager { container.saveFile(defaultContainerFile, toRelativePath); } } catch (Exception ex) { - LOG.error("Save file error in container :" + container, ex); + LOG.error("Save file error in container: " + container, ex); } } return relativePath.replace("\\", "/"); @@ -200,7 +209,7 @@ public class AttachmentManager { try { container.deleteFile(relativePath); } catch (Exception ex) { - LOG.error("Delete file error in container :" + container, ex); + LOG.error("Delete file error in container: " + container, ex); } } return defaultContainer.deleteFile(relativePath); @@ -226,11 +235,10 @@ public class AttachmentManager { * @return */ public File getFile(String relativePath, boolean localFirst) { - //优先从 默认的 container 去获取 if (localFirst) { File localFile = defaultContainer.getFile(relativePath); - if (localFile.exists() && localFile.length() > 0) { + if (localFile.exists()) { return localFile; } } @@ -239,7 +247,7 @@ public class AttachmentManager { if (container != defaultContainer) { try { File file = container.getFile(relativePath); - if (file != null && file.exists() && file.length() > 0) { + if (file != null && file.exists()) { return file; } } catch (Exception ex) { @@ -287,7 +295,7 @@ public class AttachmentManager { public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) { if (target.startsWith(defaultContainer.getTargetPrefix()) && target.lastIndexOf('.') != -1) { - Render render = null; + Render render; if (target.contains("..")) { render = renderFactory.getErrorRender(404); } else { @@ -301,7 +309,8 @@ public class AttachmentManager { } } - private Render getFileRender(File file) { + + protected Render getFileRender(File file) { return file == null || !file.isFile() ? renderFactory.getErrorRender(404) : renderFactory.getFileRender(file); -- Gitee From 4d7e9c1fbd5bd67746902796c3e0e19e3feac2ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 6 Jan 2023 18:00:46 +0800 Subject: [PATCH 1927/1965] v4.0.7 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 8 ++++++++ pom.xml | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4ae32b81..464d26c6 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 4.0.6 + 4.0.7 ``` diff --git a/changes.txt b/changes.txt index ec75ebe0..6ea987fe 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,11 @@ +jboot v4.0.7 2023-01-06: +优化:重构 AttachmentManager 使之更加灵活易用 +新增:StrUtil.isStartsWithAny() 方法 +新增:FileUtil.getFileMD5() 等方法 +新增:CdnUtil.appendCdnDomain() 方法 + + + jboot v4.0.6 2022-12-31: 修复:缓存注解自动生成 key 无法支持集合参数的问题 优化:ApplicationUtil.java 使其在 window 下输出正确的 classpath 路径 diff --git a/pom.xml b/pom.xml index f07a226c..a2755f17 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.7-SNAPSHOT + 4.0.7 jar jboot -- Gitee From bb60396e7d8317e9841e036fc6a7cd219a5e3ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 4 Feb 2023 14:24:51 +0800 Subject: [PATCH 1928/1965] v4.0.8 snapshot --- pom.xml | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/pom.xml b/pom.xml index a2755f17..fd20500f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.7 + 4.0.8-snapshot jar jboot @@ -516,22 +516,14 @@ provided - - - - - - - - - org.apache.shardingsphere - sharding-jdbc-core - 4.1.1 + shardingsphere-jdbc-core + 5.3.1 provided + io.dropwizard.metrics metrics-core -- Gitee From 81d640b68a65fa8745b7b19c1dd1259d10b213f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 4 Feb 2023 14:25:32 +0800 Subject: [PATCH 1929/1965] upgrade ShardingJDBC --- src/main/java/io/jboot/db/datasource/DataSourceBuilder.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/db/datasource/DataSourceBuilder.java b/src/main/java/io/jboot/db/datasource/DataSourceBuilder.java index 25a6856b..0dad0705 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceBuilder.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceBuilder.java @@ -20,7 +20,7 @@ import io.jboot.core.spi.JbootSpiLoader; import io.jboot.exception.JbootException; import io.jboot.support.seata.JbootSeataManager; import io.jboot.utils.StrUtil; -import org.apache.shardingsphere.shardingjdbc.api.yaml.YamlShardingDataSourceFactory; +import org.apache.shardingsphere.driver.api.yaml.YamlShardingSphereDataSourceFactory; import javax.sql.DataSource; import java.io.File; @@ -50,7 +50,8 @@ public class DataSourceBuilder { : new File(PathKit.getRootClassPath(), shardingConfigYaml); try { - return YamlShardingDataSourceFactory.createDataSource(yamlFile); +// return YamlShardingDataSourceFactory.createDataSource(yamlFile); + return YamlShardingSphereDataSourceFactory.createDataSource(yamlFile); } catch (Exception e) { throw new JbootException(e); } -- Gitee From b8d7a36951034ed98bfd1653de50d86699c54383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sat, 4 Feb 2023 14:25:49 +0800 Subject: [PATCH 1930/1965] optimize Columns.in --- src/main/java/io/jboot/db/model/Columns.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 98e7ff0b..969826f1 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -318,6 +318,11 @@ public class Columns implements Serializable { */ public Columns in(String name, Object... arrays) { Util.checkNullParas(this, name, arrays); + + //忽略 columns.in("name", null) 达到情况 + if (arrays != null && arrays.length == 1 && arrays[0] == null) { + return this; + } return add(Column.create(name, arrays, Column.LOGIC_IN)); } @@ -346,6 +351,11 @@ public class Columns implements Serializable { */ public Columns notIn(String name, Object... arrays) { Util.checkNullParas(this, name, arrays); + + //忽略 columns.notIn("name", null) 达到情况 + if (arrays != null && arrays.length == 1 && arrays[0] == null) { + return this; + } return add(Column.create(name, arrays, Column.LOGIC_NOT_IN)); } @@ -419,7 +429,7 @@ public class Columns implements Serializable { if (columns == this) { throw new IllegalArgumentException("Columns.group(...) need a new Columns"); } - if ( conditon != null && conditon && !columns.isEmpty()) { + if (conditon != null && conditon && !columns.isEmpty()) { add(new Group(columns)); } return this; -- Gitee From 5fe3ab0dce27cecf6274d50ac4281d175be7e7bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 5 Feb 2023 15:24:23 +0800 Subject: [PATCH 1931/1965] upgrade dependencies --- pom.xml | 6 +++--- src/main/java/io/jboot/utils/ClassUtil.java | 2 +- src/test/java/io/jboot/test/utils/ClassUtilTest.java | 7 +++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index fd20500f..09660afc 100644 --- a/pom.xml +++ b/pom.xml @@ -43,11 +43,11 @@ 5.0.8 2022.2 - 3.3 + 3.4 3.4 3.3.0 4.0.3 - 2.14.1 + 2.14.2 5.1.49 2.2.19.Final 1.7.36 @@ -72,7 +72,7 @@ 3.14.0.Final 1.5.1 1.1.8 - 4.2.13 + 4.2.15 1.8 1.8 diff --git a/src/main/java/io/jboot/utils/ClassUtil.java b/src/main/java/io/jboot/utils/ClassUtil.java index 8faa1f43..e614219f 100644 --- a/src/main/java/io/jboot/utils/ClassUtil.java +++ b/src/main/java/io/jboot/utils/ClassUtil.java @@ -149,7 +149,7 @@ public class ClassUtil { } } - throw new IllegalArgumentException("Can not matched constructor by paras: " + Arrays.toString(paras) + " in class: " + clazz.getName()); + throw new IllegalArgumentException("Can not matched constructor by paras" + Arrays.toString(paras) + " for class: " + clazz.getName()); } catch (Exception e) { LOG.error("Can not new instance for class: " + clazz.getName() + "\n" + e, e); } diff --git a/src/test/java/io/jboot/test/utils/ClassUtilTest.java b/src/test/java/io/jboot/test/utils/ClassUtilTest.java index e2839ffc..7cb50c41 100644 --- a/src/test/java/io/jboot/test/utils/ClassUtilTest.java +++ b/src/test/java/io/jboot/test/utils/ClassUtilTest.java @@ -1,6 +1,5 @@ package io.jboot.test.utils; -import io.jboot.utils.ClassUtil; import org.junit.Test; public class ClassUtilTest { @@ -48,9 +47,9 @@ public class ClassUtilTest { // // TestObject testObject4 = ClassUtil.newInstance(TestObject.class,null,"bbb"); // System.out.println(testObject4); - - TestObject testObject5 = ClassUtil.newInstance(TestObject.class,null,"bbb","ccc"); - System.out.println(testObject5); +// +// TestObject testObject5 = ClassUtil.newInstance(TestObject.class,null,"bbb","ccc"); +// System.out.println(testObject5); } -- Gitee From c23963b9c691065ef5cd0b923aa0dd3718ee7f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 5 Feb 2023 15:37:29 +0800 Subject: [PATCH 1932/1965] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=94=99=E5=88=AB?= =?UTF-8?q?=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/model/Columns.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 969826f1..722da724 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -319,7 +319,7 @@ public class Columns implements Serializable { public Columns in(String name, Object... arrays) { Util.checkNullParas(this, name, arrays); - //忽略 columns.in("name", null) 达到情况 + //忽略 columns.in("name", null) 的情况 if (arrays != null && arrays.length == 1 && arrays[0] == null) { return this; } @@ -328,13 +328,13 @@ public class Columns implements Serializable { /** - * in list + * in Collection * * @param name * @param collection * @return */ - public Columns in(String name, Collection collection) { + public Columns in(String name, Collection collection) { Util.checkNullParas(this, collection); if (collection != null && !collection.isEmpty()) { in(name, collection.toArray()); @@ -352,7 +352,7 @@ public class Columns implements Serializable { public Columns notIn(String name, Object... arrays) { Util.checkNullParas(this, name, arrays); - //忽略 columns.notIn("name", null) 达到情况 + //忽略 columns.notIn("name", null) 的情况 if (arrays != null && arrays.length == 1 && arrays[0] == null) { return this; } @@ -361,13 +361,13 @@ public class Columns implements Serializable { /** - * not in list + * not in Collection * * @param name * @param collection * @return */ - public Columns notIn(String name, Collection collection) { + public Columns notIn(String name, Collection collection) { Util.checkNullParas(this, collection); if (collection != null && !collection.isEmpty()) { notIn(name, collection.toArray()); @@ -422,14 +422,14 @@ public class Columns implements Serializable { /** * @param columns - * @param conditon + * @param condition * @return */ - public Columns groupIf(Columns columns, Boolean conditon) { + public Columns groupIf(Columns columns, Boolean condition) { if (columns == this) { throw new IllegalArgumentException("Columns.group(...) need a new Columns"); } - if (conditon != null && conditon && !columns.isEmpty()) { + if (condition != null && condition && !columns.isEmpty()) { add(new Group(columns)); } return this; -- Gitee From 6b07fb3d687f263c4380a2442fc9a3c61d102887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 5 Feb 2023 15:44:46 +0800 Subject: [PATCH 1933/1965] v4.0.8 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 2 +- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 577f0418..409d59fc 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 4.0.7 + 4.0.8 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index f8f6ccbc..287be319 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 4.0.7 + 4.0.8 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 302005eb..f3543aed 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 4.0.7 + 4.0.8 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='4.0.7', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='4.0.8', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 0cf7a103..e7f43078 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='4.0.7', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='4.0.8', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index 341aa906..fd42528f 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 4.0.7 + 4.0.8 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 7aa646c2..9bc2a9cf 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "4.0.7"; + public static String VERSION = "4.0.8"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 7c0e2a414bfc5273234ae4c438a60367b3d98096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 5 Feb 2023 15:45:00 +0800 Subject: [PATCH 1934/1965] v4.0.8 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ pom.xml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 464d26c6..b6dd061a 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 4.0.7 + 4.0.8 ``` diff --git a/changes.txt b/changes.txt index 6ea987fe..2c879b3a 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v4.0.8 2023-02-05: +优化:升级 ShardingJDBC 到 5.x 最新版本 +优化:升级 Columns.in 和 notIn 等方法和错别字 +优化:升级 jfinal-undertow、jackson、metrics 等到最新版本 + + + jboot v4.0.7 2023-01-06: 优化:重构 AttachmentManager 使之更加灵活易用 新增:StrUtil.isStartsWithAny() 方法 diff --git a/pom.xml b/pom.xml index 09660afc..239b13a2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.8-snapshot + 4.0.8 jar jboot -- Gitee From 20c36c35f9ad8634ddba2dd892913d34b3e0f1b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 7 Feb 2023 14:20:12 +0800 Subject: [PATCH 1935/1965] v4.0.9 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 239b13a2..9a8a790e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.8 + 4.0.9-SNAPSHOT jar jboot -- Gitee From 42f6d3e638542076bb1b32dcd986f68b1b9b05eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 7 Feb 2023 14:21:25 +0800 Subject: [PATCH 1936/1965] rename "pulish" to "publish" --- src/main/java/io/jboot/Jboot.java | 2 +- src/main/java/io/jboot/components/event/JbootEventManager.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/Jboot.java b/src/main/java/io/jboot/Jboot.java index 6e2ea6d7..604833df 100644 --- a/src/main/java/io/jboot/Jboot.java +++ b/src/main/java/io/jboot/Jboot.java @@ -230,7 +230,7 @@ public class Jboot { * @param event */ public static void sendEvent(JbootEvent event) { - JbootEventManager.me().pulish(event); + JbootEventManager.me().publish(event); } /** diff --git a/src/main/java/io/jboot/components/event/JbootEventManager.java b/src/main/java/io/jboot/components/event/JbootEventManager.java index 1d29e9ee..b21332c7 100644 --- a/src/main/java/io/jboot/components/event/JbootEventManager.java +++ b/src/main/java/io/jboot/components/event/JbootEventManager.java @@ -180,7 +180,7 @@ public class JbootEventManager { return false; } - public void pulish(final JbootEvent event) { + public void publish(final JbootEvent event) { String action = event.getAction(); List syncListeners = listenerMap.get(action); -- Gitee From 3225bc6f5d4ccfb795697016d6ca64cba8764529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 7 Feb 2023 19:04:41 +0800 Subject: [PATCH 1937/1965] optimize JbootResourceLoader.java --- src/main/java/io/jboot/app/JbootResourceLoader.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/app/JbootResourceLoader.java b/src/main/java/io/jboot/app/JbootResourceLoader.java index ab43826f..cf415699 100644 --- a/src/main/java/io/jboot/app/JbootResourceLoader.java +++ b/src/main/java/io/jboot/app/JbootResourceLoader.java @@ -111,15 +111,15 @@ public class JbootResourceLoader { return; } - //忽略掉 windows 下的临时文件 - if(file.endsWith("~")){ + //忽略掉 windows 和 mac 下的临时文件 + if(file.endsWith("~") || file.endsWith(".DS_Store")){ return; } int indexOf = file.indexOf(path); File target = new File(classPath, resourcePathName + File.separator + file.substring(indexOf + path.length())); - System.err.println("JbootResourceLoader " + action + " : " + target); + System.err.println("JbootResourceLoader " + action + ": " + target); //文件删除 if (FileScanner.ACTION_DELETE.equals(action)) { -- Gitee From f467742d8bebce54871be023bf8219a8e87014b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 10 Feb 2023 18:54:40 +0800 Subject: [PATCH 1938/1965] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=94=99=E5=88=AB?= =?UTF-8?q?=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/db/model/SqlBuilder.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/io/jboot/db/model/SqlBuilder.java b/src/main/java/io/jboot/db/model/SqlBuilder.java index fa786eb4..7a3904a1 100644 --- a/src/main/java/io/jboot/db/model/SqlBuilder.java +++ b/src/main/java/io/jboot/db/model/SqlBuilder.java @@ -85,35 +85,35 @@ public class SqlBuilder { for (int i = 0; i < columns.size(); i++) { Column before = i > 0 ? columns.get(i - 1) : null; - Column curent = columns.get(i); + Column current = columns.get(i); - if (curent instanceof Or) { + if (current instanceof Or) { continue; } // sqlPart - else if (curent instanceof SqlPart) { - appendSqlPartLogic(sqlBuilder, before, (SqlPart) curent, separator); + else if (current instanceof SqlPart) { + appendSqlPartLogic(sqlBuilder, before, (SqlPart) current, separator); } // group - else if (curent instanceof Group) { - appendGroupLogic(sqlBuilder, before, (Group) curent, separator); + else if (current instanceof Group) { + appendGroupLogic(sqlBuilder, before, (Group) current, separator); } // in logic - else if (Column.LOGIC_IN.equals(curent.getLogic()) || Column.LOGIC_NOT_IN.equals(curent.getLogic())) { + else if (Column.LOGIC_IN.equals(current.getLogic()) || Column.LOGIC_NOT_IN.equals(current.getLogic())) { appendLinkString(sqlBuilder, before); - appendInLogic(sqlBuilder, curent, separator); + appendInLogic(sqlBuilder, current, separator); } // between logic - else if (Column.LOGIC_BETWEEN.equals(curent.getLogic()) || Column.LOGIC_NOT_BETWEEN.equals(curent.getLogic())) { + else if (Column.LOGIC_BETWEEN.equals(current.getLogic()) || Column.LOGIC_NOT_BETWEEN.equals(current.getLogic())) { appendLinkString(sqlBuilder, before); - appendBetweenLogic(sqlBuilder, curent, separator); + appendBetweenLogic(sqlBuilder, current, separator); } // others else { appendLinkString(sqlBuilder, before); - appendColumnName(sqlBuilder, curent, separator); + appendColumnName(sqlBuilder, current, separator); - if (curent.hasPara()) { + if (current.hasPara()) { sqlBuilder.append('?'); } } -- Gitee From 55726642b6d277f7de76b6b13ab3b7aaaca4fae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 8 Mar 2023 16:08:14 +0800 Subject: [PATCH 1939/1965] fixed orEqs --- src/main/java/io/jboot/db/model/Columns.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Columns.java b/src/main/java/io/jboot/db/model/Columns.java index 722da724..6c233deb 100644 --- a/src/main/java/io/jboot/db/model/Columns.java +++ b/src/main/java/io/jboot/db/model/Columns.java @@ -599,16 +599,19 @@ public class Columns implements Serializable { public Columns ors(String name, String logic, Object... values) { Util.checkNullParas(this, name, values); + + Columns columns = new Columns(); for (int i = 0; i < values.length; i++) { Object value = values[i]; if (value != null) { - add(Column.create(name, value, logic)); + columns.add(Column.create(name, value, logic)); if (i != values.length - 1) { - add(new Or()); + columns.add(new Or()); } } } - return this; + + return group(columns); } -- Gitee From 04bf73d2511844eb7fe99341132d9d258b1d6ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 8 Mar 2023 16:12:24 +0800 Subject: [PATCH 1940/1965] v4.0.9 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 2 +- simples/websocket/pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index 409d59fc..eb99e5a9 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 4.0.8 + 4.0.9 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 287be319..53fe552b 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 4.0.8 + 4.0.9 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index f3543aed..22570c4f 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 4.0.8 + 4.0.9 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='4.0.8', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='4.0.9', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index e7f43078..72f46b2b 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='4.0.8', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='4.0.9', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 diff --git a/simples/websocket/pom.xml b/simples/websocket/pom.xml index fd42528f..df3b8ac9 100644 --- a/simples/websocket/pom.xml +++ b/simples/websocket/pom.xml @@ -17,7 +17,7 @@ io.jboot jboot - 4.0.8 + 4.0.9 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 9bc2a9cf..5931cb00 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "4.0.8"; + public static String VERSION = "4.0.9"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From a96b2cd65a6ec255d031158b3d04d208b119c066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 8 Mar 2023 16:12:28 +0800 Subject: [PATCH 1941/1965] v4.0.9 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ pom.xml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b6dd061a..3895e1fb 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 4.0.8 + 4.0.9 ``` diff --git a/changes.txt b/changes.txt index 2c879b3a..3d5fe6a9 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v4.0.9 2023-03-08: +优化:JbootResourceLoader 忽略掉 windows 和 mac 下的临时文件 +优化:修改 SqlBuilder.java 的错别字 +修复:Columns.orEqs 没有添加括号导致 sql 逻辑不对的问题 + + + jboot v4.0.8 2023-02-05: 优化:升级 ShardingJDBC 到 5.x 最新版本 优化:升级 Columns.in 和 notIn 等方法和错别字 diff --git a/pom.xml b/pom.xml index 9a8a790e..d92c1d41 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.9-SNAPSHOT + 4.0.9 jar jboot -- Gitee From 87024d7f3847cbec7ba3b103a85d2ac0e866e1c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 10 Mar 2023 09:30:32 +0800 Subject: [PATCH 1942/1965] v4.1.0 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d92c1d41..64ea3d05 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.0.9 + 4.1.0-SNAPSHOT jar jboot -- Gitee From 16afcf269626597f72c92e5b2778da7aa5c35495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 10 Mar 2023 09:30:49 +0800 Subject: [PATCH 1943/1965] fixed WechatAccessTokenCache.java --- ...ssTokenCache.java => WechatAccessTokenCache.java} | 12 ++++++++---- src/main/java/io/jboot/wechat/WechatSupport.java | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) rename src/main/java/io/jboot/components/cache/support/{JbootAccessTokenCache.java => WechatAccessTokenCache.java} (66%) diff --git a/src/main/java/io/jboot/components/cache/support/JbootAccessTokenCache.java b/src/main/java/io/jboot/components/cache/support/WechatAccessTokenCache.java similarity index 66% rename from src/main/java/io/jboot/components/cache/support/JbootAccessTokenCache.java rename to src/main/java/io/jboot/components/cache/support/WechatAccessTokenCache.java index c4dafc04..c015192c 100644 --- a/src/main/java/io/jboot/components/cache/support/JbootAccessTokenCache.java +++ b/src/main/java/io/jboot/components/cache/support/WechatAccessTokenCache.java @@ -2,29 +2,33 @@ package io.jboot.components.cache.support; import com.jfinal.weixin.sdk.cache.IAccessTokenCache; import io.jboot.Jboot; -import io.jboot.components.cache.CacheTime; import io.jboot.components.cache.JbootCacheManager; -public class JbootAccessTokenCache implements IAccessTokenCache { +public class WechatAccessTokenCache implements IAccessTokenCache { static final String CACHE_NAME = "wechat_access_tokens"; - public JbootAccessTokenCache() { + public WechatAccessTokenCache() { JbootCacheManager.me().getCache() .addThreadCacheNamePrefixIngore(CACHE_NAME); } + @Override public String get(String key) { return Jboot.getCache().get(CACHE_NAME, key); } + @Override public void set(String key, String value) { // 微信相关 token 的有效期之多 2 个小时 - Jboot.getCache().put(CACHE_NAME, key, value,2 * CacheTime.HOUR); + // 如果设置为 7200,则有一定几率出现如下错误 + // {"errcode":40001,"errmsg":"invalid credential, access_token is invalid or not latest rid: **"} + Jboot.getCache().put(CACHE_NAME, key, value,7000); } + @Override public void remove(String key) { Jboot.getCache().remove(CACHE_NAME, key); diff --git a/src/main/java/io/jboot/wechat/WechatSupport.java b/src/main/java/io/jboot/wechat/WechatSupport.java index e32aa9ba..d8a375dc 100644 --- a/src/main/java/io/jboot/wechat/WechatSupport.java +++ b/src/main/java/io/jboot/wechat/WechatSupport.java @@ -16,11 +16,11 @@ package io.jboot.wechat; import com.jfinal.weixin.sdk.api.ApiConfigKit; -import io.jboot.components.cache.support.JbootAccessTokenCache; +import io.jboot.components.cache.support.WechatAccessTokenCache; public class WechatSupport { public void autoSupport() { - ApiConfigKit.setAccessTokenCache(new JbootAccessTokenCache()); + ApiConfigKit.setAccessTokenCache(new WechatAccessTokenCache()); } } -- Gitee From d50e7259e5aeb68ec72d6bdf90e161f5c83b9a24 Mon Sep 17 00:00:00 2001 From: "xin.yang" Date: Fri, 17 Mar 2023 15:50:17 +0800 Subject: [PATCH 1944/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=20JbootAppListener?= =?UTF-8?q?Manager=20=E4=B8=AD=E9=87=8D=E5=A4=8D=E7=9A=84=20foreach=20?= =?UTF-8?q?=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../listener/JbootAppListenerManager.java | 114 ++++++------------ 1 file changed, 40 insertions(+), 74 deletions(-) diff --git a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java index f7d71eee..94cd7d5b 100644 --- a/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java +++ b/src/main/java/io/jboot/core/listener/JbootAppListenerManager.java @@ -102,137 +102,103 @@ public class JbootAppListenerManager implements JbootAppListener { @Override public void onInit() { - for (JbootAppListener listener : listeners) { - try { + eachListeners(new TriggerThrowable() { + @Override + public void trigger(JbootAppListener listener) { listener.onInit(); - } catch (Throwable ex) { - //在 init 的时候, log 组件未初始化,无法使用 + } + + @Override + public void throwable(Throwable ex) { + //在 onConstantConfigBefore 的时候, log 组件未初始化,无法使用 ex.printStackTrace(); } - } + }); } @Override public void onConstantConfigBefore(Constants constants) { - for (JbootAppListener listener : listeners) { - try { + eachListeners(new TriggerThrowable() { + @Override + public void trigger(JbootAppListener listener) { listener.onConstantConfigBefore(constants); - } catch (Throwable ex) { + } + + @Override + public void throwable(Throwable ex) { //在 onConstantConfigBefore 的时候, log 组件未初始化,无法使用 ex.printStackTrace(); } - } + }); } @Override public void onConstantConfig(Constants constants) { - for (JbootAppListener listener : listeners) { - try { - listener.onConstantConfig(constants); - } catch (Throwable ex) { - LogKit.error(ex.toString(), ex); - } - } + eachListeners(listener -> listener.onConstantConfig(constants)); } @Override public void onRouteConfig(Routes routes) { - for (JbootAppListener listener : listeners) { - try { - listener.onRouteConfig(routes); - } catch (Throwable ex) { - LogKit.error(ex.toString(), ex); - } - } + eachListeners(listener -> listener.onRouteConfig(routes)); } @Override public void onEngineConfig(Engine engine) { - for (JbootAppListener listener : listeners) { - try { - listener.onEngineConfig(engine); - } catch (Throwable ex) { - LogKit.error(ex.toString(), ex); - } - } + eachListeners(listener -> listener.onEngineConfig(engine)); } @Override public void onPluginConfig(JfinalPlugins plugins) { - for (JbootAppListener listener : listeners) { - try { - listener.onPluginConfig(plugins); - } catch (Throwable ex) { - LogKit.error(ex.toString(), ex); - } - } + eachListeners(listener -> listener.onPluginConfig(plugins)); } @Override public void onInterceptorConfig(Interceptors interceptors) { - for (JbootAppListener listener : listeners) { - try { - listener.onInterceptorConfig(interceptors); - } catch (Throwable ex) { - LogKit.error(ex.toString(), ex); - } - } + eachListeners(listener -> listener.onInterceptorConfig(interceptors)); } @Override public void onHandlerConfig(JfinalHandlers handlers) { - for (JbootAppListener listener : listeners) { - try { - listener.onHandlerConfig(handlers); - } catch (Throwable ex) { - LogKit.error(ex.toString(), ex); - } - } + eachListeners(listener -> listener.onHandlerConfig(handlers)); } @Override public void onStartBefore() { - for (JbootAppListener listener : listeners) { - try { - listener.onStartBefore(); - } catch (Throwable ex) { - LogKit.error(ex.toString(), ex); - } - } + eachListeners(JbootAppListener::onStartBefore); } @Override public void onStart() { - for (JbootAppListener listener : listeners) { - try { - listener.onStart(); - } catch (Throwable ex) { - LogKit.error(ex.toString(), ex); - } - } + eachListeners(JbootAppListener::onStart); } @Override public void onStartFinish() { - for (JbootAppListener listener : listeners) { - try { - listener.onStartFinish(); - } catch (Throwable ex) { - LogKit.error(ex.toString(), ex); - } - } + eachListeners(JbootAppListener::onStartFinish); } @Override public void onStop() { + eachListeners(JbootAppListener::onStop); + } + + private void eachListeners(TriggerThrowable triggerThrowable) { for (JbootAppListener listener : listeners) { try { - listener.onStop(); + triggerThrowable.trigger(listener); } catch (Throwable ex) { - LogKit.error(ex.toString(), ex); + triggerThrowable.throwable(ex); } } } + @FunctionalInterface + private interface TriggerThrowable { + void trigger(JbootAppListener listener); + + default void throwable(Throwable ex) { + LogKit.error(ex.toString(), ex); + } + } } -- Gitee From 022e5fb80362df46c8b57fbd00038dfa6389c39b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 27 Mar 2023 19:13:40 +0800 Subject: [PATCH 1945/1965] optimize Util.java --- src/main/java/io/jboot/db/model/Util.java | 36 +++++++---------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/jboot/db/model/Util.java b/src/main/java/io/jboot/db/model/Util.java index 5bd120a2..1cd0b597 100644 --- a/src/main/java/io/jboot/db/model/Util.java +++ b/src/main/java/io/jboot/db/model/Util.java @@ -20,6 +20,7 @@ import com.jfinal.ext.kit.DateKit; import io.jboot.utils.CollectionUtil; import io.jboot.utils.StrUtil; +import java.lang.reflect.Array; import java.util.Collection; import java.util.Date; import java.util.LinkedList; @@ -47,12 +48,16 @@ class Util { if (value.getClass().isArray()) { Object[] values = (Object[]) value; for (Object v : values) { - if (v.getClass() == int[].class) { - addAll(paras, (int[]) v); - } else if (v.getClass() == long[].class) { - addAll(paras, (long[]) v); - } else if (v.getClass() == short[].class) { - addAll(paras, (short[]) v); + if (v != null && ( + v.getClass() == int[].class + || v.getClass() == long[].class + || v.getClass() == short[].class + || v.getClass() == float[].class + || v.getClass() == double[].class + )) { + for (int i = 0; i < Array.getLength(v); i++) { + paras.add(Array.get(v, i)); + } } else { paras.add(v); } @@ -67,25 +72,6 @@ class Util { } - private static void addAll(List paras, int[] ints) { - for (int i : ints) { - paras.add(i); - } - } - - private static void addAll(List paras, long[] longs) { - for (long i : longs) { - paras.add(i); - } - } - - - private static void addAll(List paras, short[] shorts) { - for (short i : shorts) { - paras.add(i); - } - } - static String replaceSqlPara(String sql, Object value) { // null -- Gitee From 7235211d012468e53bb3e5229003e418dfdd77f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 28 Mar 2023 10:25:00 +0800 Subject: [PATCH 1946/1965] optimize Utils --- src/main/java/io/jboot/utils/DateUtil.java | 6 +++--- src/main/java/io/jboot/utils/ObjectUtil.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/utils/DateUtil.java b/src/main/java/io/jboot/utils/DateUtil.java index 312df0f0..4f557579 100644 --- a/src/main/java/io/jboot/utils/DateUtil.java +++ b/src/main/java/io/jboot/utils/DateUtil.java @@ -44,13 +44,13 @@ public class DateUtil { private static final ThreadLocal> TL = ThreadLocal.withInitial(() -> new HashMap<>()); - private static final Map datetimeFormaters = new SyncWriteMap<>(); + private static final Map dateTimeFormatters = new SyncWriteMap<>(); public static DateTimeFormatter getDateTimeFormatter(String pattern) { - DateTimeFormatter ret = datetimeFormaters.get(pattern); + DateTimeFormatter ret = dateTimeFormatters.get(pattern); if (ret == null) { ret = DateTimeFormatter.ofPattern(pattern); - datetimeFormaters.put(pattern, ret); + dateTimeFormatters.put(pattern, ret); } return ret; } diff --git a/src/main/java/io/jboot/utils/ObjectUtil.java b/src/main/java/io/jboot/utils/ObjectUtil.java index 06b3d293..326d1770 100644 --- a/src/main/java/io/jboot/utils/ObjectUtil.java +++ b/src/main/java/io/jboot/utils/ObjectUtil.java @@ -135,7 +135,7 @@ public class ObjectUtil { } - public static Object convert(Object value, Class targetClass) { + public static Object convert(Object value, Class targetClass) { if (value == null || (value.getClass() == String.class && StrUtil.isBlank((String) value) && targetClass != String.class)) { return null; -- Gitee From dd7aa525c4517cc2879e3fbcd98711a4fb4d7c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 12 May 2023 14:57:17 +0800 Subject: [PATCH 1947/1965] upgrade jfinal version --- pom.xml | 8 +- .../io/jboot/db/dialect/JbootDmDialect.java | 242 +++++++++++++++++- 2 files changed, 233 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index 64ea3d05..2e525b52 100644 --- a/pom.xml +++ b/pom.xml @@ -41,9 +41,9 @@ - 5.0.8 + 5.1.0 2022.2 - 3.4 + 3.5 3.4 3.3.0 4.0.3 @@ -58,7 +58,7 @@ 31.1-jre 2.2.5 3.8.0 - 1.15.3 + 1.16.1 2.10.9.2 2.9.3 3.12.0 @@ -72,7 +72,7 @@ 3.14.0.Final 1.5.1 1.1.8 - 4.2.15 + 4.2.18 1.8 1.8 diff --git a/src/main/java/io/jboot/db/dialect/JbootDmDialect.java b/src/main/java/io/jboot/db/dialect/JbootDmDialect.java index bc407e98..23012007 100644 --- a/src/main/java/io/jboot/db/dialect/JbootDmDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootDmDialect.java @@ -15,60 +15,276 @@ */ package io.jboot.db.dialect; -import com.jfinal.plugin.activerecord.dialect.MysqlDialect; +import com.jfinal.plugin.activerecord.CPI; +import com.jfinal.plugin.activerecord.Record; +import com.jfinal.plugin.activerecord.Table; +import com.jfinal.plugin.activerecord.dialect.OracleDialect; import io.jboot.db.model.Column; import io.jboot.db.model.Join; import io.jboot.db.model.SqlBuilder; import java.util.List; +import java.util.Map; +import java.util.Set; /** * 达梦数据库的数据方言 */ -public class JbootDmDialect extends MysqlDialect implements JbootDialect { +public class JbootDmDialect extends OracleDialect implements JbootDialect { - public JbootDmDialect() { - throw new IllegalStateException("Not finished!"); + private static final char separator = '"'; + + private String wrap(String wrap) { + return "\"" + wrap.toUpperCase() + "\""; + } + + @Override + public String forTableBuilderDoBuild(String tableName) { + return toUpperCase("select * from " + wrap(tableName) + " where rownum < 1"); + } + + + @Override + // insert into table (id,name) values(seq.nextval, ?) + public void forModelSave(Table table, Map attrs, StringBuilder sql, List paras) { + sql.append("insert into ").append(wrap(table.getName())).append('('); + StringBuilder temp = new StringBuilder(") values("); + String[] pKeys = table.getPrimaryKey(); + int count = 0; + for (Map.Entry e : attrs.entrySet()) { + String colName = e.getKey(); + if (table.hasColumnLabel(colName)) { + Object value = e.getValue(); + if (isPrimaryKey(colName, pKeys) && value == null) { + continue; + } + + if (count++ > 0) { + sql.append(", "); + temp.append(", "); + } + + + if (isPrimaryKey(colName, pKeys) && value instanceof String && ((String) value).endsWith(".nextval")) { + sql.append(wrap(colName)); + temp.append(value); + } else { + sql.append(wrap(colName)); + temp.append('?'); + paras.add(value); + } + } + } + sql.append(temp).append(')'); + } + + @Override + public String forModelDeleteById(Table table) { + String[] pKeys = table.getPrimaryKey(); + StringBuilder sql = new StringBuilder(45); + sql.append("delete from "); + sql.append(wrap(table.getName())); + sql.append(" where "); + for (int i = 0; i < pKeys.length; i++) { + if (i > 0) { + sql.append(" and "); + } + sql.append(wrap(pKeys[i])).append(" = ?"); + } + return sql.toString(); } + @Override + public void forModelUpdate(Table table, Map attrs, Set modifyFlag, StringBuilder sql, List paras) { + sql.append("update ").append(wrap(table.getName())).append(" set "); + String[] pKeys = table.getPrimaryKey(); + for (Map.Entry e : attrs.entrySet()) { + String colName = e.getKey(); + if (modifyFlag.contains(colName) && !isPrimaryKey(colName, pKeys) && table.hasColumnLabel(colName)) { + if (paras.size() > 0) { + sql.append(", "); + } + sql.append(wrap(colName)).append(" = ? "); + paras.add(e.getValue()); + } + } + sql.append(" where "); + for (int i = 0; i < pKeys.length; i++) { + if (i > 0) { + sql.append(" and "); + } + sql.append(wrap(pKeys[i])).append(" = ?"); + paras.add(attrs.get(pKeys[i])); + } + } + + + @Override + public String forModelFindById(Table table, String columns) { + StringBuilder sql = new StringBuilder("select ").append(columns).append(" from "); + sql.append(wrap(table.getName())); + sql.append(" where "); + String[] pKeys = table.getPrimaryKey(); + for (int i = 0; i < pKeys.length; i++) { + if (i > 0) { + sql.append(" and "); + } + sql.append(wrap(pKeys[i])).append(" = ?"); + } + return sql.toString(); + } + + + @Override + public String forDbFindById(String tableName, String[] pKeys) { + tableName = tableName.trim(); + trimPrimaryKeys(pKeys); + + StringBuilder sql = new StringBuilder("select * from ").append(wrap(tableName)).append(" where "); + for (int i = 0; i < pKeys.length; i++) { + if (i > 0) { + sql.append(" and "); + } + sql.append(wrap(pKeys[i])).append(" = ?"); + } + return sql.toString(); + } + + @Override + public String forDbDeleteById(String tableName, String[] pKeys) { + tableName = tableName.trim(); + trimPrimaryKeys(pKeys); + + StringBuilder sql = new StringBuilder("delete from ").append(wrap(tableName)).append(" where "); + for (int i = 0; i < pKeys.length; i++) { + if (i > 0) { + sql.append(" and "); + } + sql.append(wrap(pKeys[i])).append(" = ?"); + } + return sql.toString(); + } + + + @Override + public void forDbSave(String tableName, String[] pKeys, Record record, StringBuilder sql, List paras) { + tableName = tableName.trim(); + trimPrimaryKeys(pKeys); + + sql.append("insert into "); + sql.append(wrap(tableName)).append('('); + StringBuilder temp = new StringBuilder(); + temp.append(") values("); + + int count = 0; + for (Map.Entry e : record.getColumns().entrySet()) { + + String colName = e.getKey(); + Object value = e.getValue(); + + if (isPrimaryKey(colName, pKeys) && value == null) { + continue; + } + + if (count++ > 0) { + sql.append(", "); + temp.append(", "); + } + sql.append(wrap(colName)); + + if (value instanceof String && isPrimaryKey(colName, pKeys) && ((String) value).endsWith(".nextval")) { + temp.append(value); + } else { + temp.append('?'); + paras.add(value); + } + } + sql.append(temp).append(')'); + } + + + @Override + public void forDbUpdate(String tableName, String[] pKeys, Object[] ids, Record record, StringBuilder sql, List paras) { + tableName = tableName.trim(); + trimPrimaryKeys(pKeys); + + // Record 新增支持 modifyFlag + Set modifyFlag = CPI.getModifyFlag(record); + + sql.append("update ").append(tableName).append(" set "); + for (Map.Entry e : record.getColumns().entrySet()) { + String colName = e.getKey(); + if (modifyFlag.contains(colName) && !isPrimaryKey(colName, pKeys)) { + if (paras.size() > 0) { + sql.append(", "); + } + sql.append(wrap(colName)).append(" = ? "); + paras.add(e.getValue()); + } + } + sql.append(" where "); + for (int i = 0; i < pKeys.length; i++) { + if (i > 0) { + sql.append(" and "); + } + sql.append(wrap(pKeys[i])).append(" = ?"); + paras.add(ids[i]); + } + } + + @Override + public String forPaginate(int pageNumber, int pageSize, StringBuilder findSql) { + int start = (pageNumber - 1) * pageSize; + int end = pageNumber * pageSize; + StringBuilder ret = new StringBuilder(); + ret.append("select * from ( select row_.*, rownum rownum_ from ( "); + ret.append(findSql); + ret.append(" ) row_ where rownum <= ").append(end).append(") table_alias"); + ret.append(" where table_alias.rownum_ > ").append(start); + return ret.toString(); + } + + + /////////////////jboot//////////// @Override public String forFindByColumns(String alias, List joins, String table, String loadColumns, List columns, String orderBy, Object limit) { - StringBuilder sqlBuilder = SqlBuilder.forFindByColumns(alias, joins, table, loadColumns, columns, orderBy, '`'); + StringBuilder sqlBuilder = SqlBuilder.forFindByColumns(alias, joins, table, loadColumns, columns, orderBy, separator); if (limit != null) { sqlBuilder.append(" LIMIT " + limit); } - - return sqlBuilder.toString(); + return toUpperCase(sqlBuilder.toString()); } @Override public String forFindCountByColumns(String alias, List joins, String table, String loadColumns, List columns) { - return SqlBuilder.forFindCountByColumns(alias, joins, table, loadColumns, columns, '`'); + return toUpperCase(SqlBuilder.forFindCountByColumns(alias, joins, table, loadColumns, columns, separator)); } @Override public String forDeleteByColumns(String alias, List joins, String table, List columns) { - return SqlBuilder.forDeleteByColumns(alias, joins, table, columns, '`'); + return toUpperCase(SqlBuilder.forDeleteByColumns(alias, joins, table, columns, separator)); } - @Override public String forPaginateSelect(String loadColumns) { - return "SELECT " + loadColumns; + return toUpperCase("SELECT " + loadColumns); } @Override public String forPaginateFrom(String alias, List joins, String table, List columns, String orderBy) { - return SqlBuilder.forPaginateFrom(alias, joins, table, columns, orderBy, '`'); + return toUpperCase(SqlBuilder.forPaginateFrom(alias, joins, table, columns, orderBy, separator)); } @Override public String forPaginateTotalRow(String select, String sqlExceptSelect, Object ext) { String distinctSql = SqlBuilder.forPaginateDistinctTotalRow(select, sqlExceptSelect, ext); - return distinctSql != null ? distinctSql : super.forPaginateTotalRow(select, sqlExceptSelect, ext); + return toUpperCase(distinctSql != null ? distinctSql : super.forPaginateTotalRow(select, sqlExceptSelect, ext)); } + private static String toUpperCase(String sql) { + return sql.toUpperCase(); + } } -- Gitee From 3e7c0f710ce80dd15d42ec6fb705c6774bbb0541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 12 May 2023 16:38:16 +0800 Subject: [PATCH 1948/1965] =?UTF-8?q?add:=20=E6=96=B0=E5=A2=9E=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=20JFinalFilter=20=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/undertow/JbootUndertowServer.java | 31 +++++++++++++------ src/main/java/io/jboot/test/MockMvc.java | 6 +++- .../test/web/MockHttpServletRequest.java | 12 +++---- .../test/web/MockHttpServletResponse.java | 13 ++++++++ 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/jboot/app/undertow/JbootUndertowServer.java b/src/main/java/io/jboot/app/undertow/JbootUndertowServer.java index 623de19c..d8412c2d 100644 --- a/src/main/java/io/jboot/app/undertow/JbootUndertowServer.java +++ b/src/main/java/io/jboot/app/undertow/JbootUndertowServer.java @@ -17,8 +17,11 @@ package io.jboot.app.undertow; import com.jfinal.server.undertow.UndertowConfig; import com.jfinal.server.undertow.UndertowServer; +import io.jboot.app.config.JbootConfigManager; +import io.undertow.servlet.Servlets; -import javax.servlet.ServletException; +import javax.servlet.DispatcherType; +import javax.servlet.Filter; public class JbootUndertowServer extends UndertowServer { @@ -27,17 +30,27 @@ public class JbootUndertowServer extends UndertowServer { super(undertowConfig); } + /** + * 添加 自定义 filter 的支持 + */ @Override - protected void init() { - super.init(); - - //让 undertow 支持 音视频在线播放 -// HttpContentTypes.init(deploymentInfo); + protected void configJFinalFilter() { + deploymentInfo.addFilter( + Servlets.filter("jfinal", getJFinalFilter()).addInitParam("configClass", config.getJFinalConfig()) + ).addFilterUrlMapping("jfinal", "/*", DispatcherType.REQUEST); } - @Override - protected void doStop() throws ServletException { - super.doStop(); + + private Class getJFinalFilter() { + try { + String jfinalFilter = JbootConfigManager.me().getConfigValue("undertow.jfinalFilter"); + if (jfinalFilter == null || jfinalFilter.trim().length() == 0){ + jfinalFilter = "com.jfinal.core.JFinalFilter"; + } + return (Class)config.getClassLoader().loadClass(jfinalFilter); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } } } diff --git a/src/main/java/io/jboot/test/MockMvc.java b/src/main/java/io/jboot/test/MockMvc.java index eda908c1..99654650 100644 --- a/src/main/java/io/jboot/test/MockMvc.java +++ b/src/main/java/io/jboot/test/MockMvc.java @@ -272,7 +272,7 @@ public class MockMvc { public MockMvcResult doMockRequest(MockHttpServletRequest request) { - MockHttpServletResponse response = new MockHttpServletResponse(); + MockHttpServletResponse response = createResponse(); try { if (requestStartListener != null) { requestStartListener.accept(request); @@ -317,4 +317,8 @@ public class MockMvc { MockApp.mockRequest(request, response); } + public MockHttpServletResponse createResponse() { + return new MockHttpServletResponse(); + } + } diff --git a/src/main/java/io/jboot/test/web/MockHttpServletRequest.java b/src/main/java/io/jboot/test/web/MockHttpServletRequest.java index 744f5822..10046ebd 100644 --- a/src/main/java/io/jboot/test/web/MockHttpServletRequest.java +++ b/src/main/java/io/jboot/test/web/MockHttpServletRequest.java @@ -55,7 +55,7 @@ public class MockHttpServletRequest implements HttpServletRequest { protected HttpSession session; protected ServletInputStream inputStream; - private byte[] content; + protected byte[] content; protected ServletContext servletContext = MockServletContext.DEFAULT; @@ -67,14 +67,14 @@ public class MockHttpServletRequest implements HttpServletRequest { protected Set cookies = new HashSet<>(); protected LinkedList locales = new LinkedList<>(); - private boolean requestedSessionIdValid = true; + protected boolean requestedSessionIdValid = true; - private boolean requestedSessionIdFromCookie = true; + protected boolean requestedSessionIdFromCookie = true; - private boolean requestedSessionIdFromURL = false; + protected boolean requestedSessionIdFromURL = false; - private final Set userRoles = new HashSet<>(); - private final Multimap parts = LinkedHashMultimap.create(); + protected final Set userRoles = new HashSet<>(); + protected final Multimap parts = LinkedHashMultimap.create(); public MockHttpServletRequest() { diff --git a/src/main/java/io/jboot/test/web/MockHttpServletResponse.java b/src/main/java/io/jboot/test/web/MockHttpServletResponse.java index ec690b2a..fc237db1 100644 --- a/src/main/java/io/jboot/test/web/MockHttpServletResponse.java +++ b/src/main/java/io/jboot/test/web/MockHttpServletResponse.java @@ -46,6 +46,19 @@ public class MockHttpServletResponse extends HttpServletResponseWrapper { super(MockProxy.create(HttpServletResponse.class)); } + public MockHttpServletResponse(ByteArrayOutputStream stream) { + super(MockProxy.create(HttpServletResponse.class)); + this.stream = stream; + } + + public MockHttpServletResponse(HttpServletResponse response) { + super(response); + } + + public MockHttpServletResponse(HttpServletResponse response, ByteArrayOutputStream stream) { + super(response); + this.stream = stream; + } @Override public void addCookie(Cookie cookie) { -- Gitee From 07887471e72db71c9056b57721515df7cb9ce46f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 12 May 2023 16:39:38 +0800 Subject: [PATCH 1949/1965] v4.1.0 release (^.^)YYa!! --- doc/docs/install.md | 2 +- doc/docs/quickstart.md | 2 +- doc/docs/start.md | 4 ++-- doc/jbootadmin/start.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/docs/install.md b/doc/docs/install.md index eb99e5a9..fa52c91d 100644 --- a/doc/docs/install.md +++ b/doc/docs/install.md @@ -9,7 +9,7 @@ io.jboot jboot - 4.0.9 + 4.1.0 ``` diff --git a/doc/docs/quickstart.md b/doc/docs/quickstart.md index 53fe552b..a72bf0ab 100644 --- a/doc/docs/quickstart.md +++ b/doc/docs/quickstart.md @@ -28,7 +28,7 @@ io.jboot jboot - 4.0.9 + 4.1.0 ``` diff --git a/doc/docs/start.md b/doc/docs/start.md index 22570c4f..1aa9efb9 100644 --- a/doc/docs/start.md +++ b/doc/docs/start.md @@ -51,7 +51,7 @@ IntelliJ IDEA 下载地址:https://www.jetbrains.com/idea/ ,下载完成后 io.jboot jboot - 4.0.9 + 4.1.0 ``` @@ -113,7 +113,7 @@ public class IndexController extends JbootController { -JbootApplication { mode='dev', version='4.0.9', jfinalConfig='io.jboot.core.JbootCoreConfig' } +JbootApplication { mode='dev', version='4.1.0', jfinalConfig='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/git/jboot/target/test-classes/ Starting JFinal 4.2 -> http://127.0.0.1:8080 Info: jfinal-undertow 1.6, undertow 2.0.19.Final, jvm 1.8.0_201 diff --git a/doc/jbootadmin/start.md b/doc/jbootadmin/start.md index 72f46b2b..286d67c5 100644 --- a/doc/jbootadmin/start.md +++ b/doc/jbootadmin/start.md @@ -113,7 +113,7 @@ starter-cms/src/main/io.jboot.cms.admin.CmsStarter \____||_____| \___/ \___/ |__| -JbootApplication { name='jboot', mode='dev', version='4.0.9', config='io.jboot.core.JbootCoreConfig' } +JbootApplication { name='jboot', mode='dev', version='4.1.0', config='io.jboot.core.JbootCoreConfig' } Classpath : /Users/michael/work/git/JbootAdmin/starter-cms/target/classes/ Starting JFinal 4.9.01 -> http://0.0.0.0:8003 Info: jfinal-undertow 2.1, undertow 2.0.30.Final, jvm 1.8.0_261 -- Gitee From b0fc87bf87e851ff0851105108fa478d46cfcd7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 12 May 2023 16:39:49 +0800 Subject: [PATCH 1950/1965] v4.1.0 release (^.^)YYa!! --- README.md | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3895e1fb..a01ebb03 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 4.0.9 + 4.1.0 ``` diff --git a/pom.xml b/pom.xml index 2e525b52..7fc3d87a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.1.0-SNAPSHOT + 4.1.0 jar jboot -- Gitee From dc3ac58742775f67eafb59d22a5637f827ab672e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Fri, 12 May 2023 16:39:52 +0800 Subject: [PATCH 1951/1965] v4.1.0 release (^.^)YYa!! --- changes.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/changes.txt b/changes.txt index 3d5fe6a9..df7c28ec 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,11 @@ +jboot v4.1.0 2023-05-12: +新增:自定义 JFinalFilter 的支持 +优化:重命名 JbootAccessTokenCache 为 WechatAccessTokenCache +优化:JbootAppListenerManager 中重复的 foreach 操作 感谢 @梦行 +优化:升级 Jfinal/jfinal-undertow/jsoup 等到最新版本 + + + jboot v4.0.9 2023-03-08: 优化:JbootResourceLoader 忽略掉 windows 和 mac 下的临时文件 优化:修改 SqlBuilder.java 的错别字 -- Gitee From b38d605a7c00b864ed97a300ee0e827f9a47b497 Mon Sep 17 00:00:00 2001 From: whpat124 <124653847@qq.com> Date: Sat, 3 Jun 2023 04:19:23 +0000 Subject: [PATCH 1952/1965] update src/main/java/io/jboot/JbootConsts.java. VERSION = "4.0.9"->"4.1.0" Signed-off-by: whpat124 <124653847@qq.com> --- src/main/java/io/jboot/JbootConsts.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 5931cb00..808e5f92 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "4.0.9"; + public static String VERSION = "4.1.0"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From d4645e961c219ac365bb5bf3d5f480902ade451f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Sun, 18 Jun 2023 15:09:55 +0800 Subject: [PATCH 1953/1965] optimize JbootmqBase.java --- .../io/jboot/components/mq/JbootmqBase.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index 4ed3093e..93c759e5 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -23,7 +23,10 @@ import io.jboot.utils.NamedThreadFactory; import io.jboot.utils.StrUtil; import java.util.*; -import java.util.concurrent.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public abstract class JbootmqBase implements Jbootmq { @@ -36,13 +39,11 @@ public abstract class JbootmqBase implements Jbootmq { private Map> channelListeners = new ConcurrentHashMap<>(); protected Set channels = new HashSet<>(); - protected Set syncRecevieMessageChannels = new HashSet<>(); + protected Set syncReceiveMessageChannels = new HashSet<>(); protected JbootSerializer serializer; - - private ExecutorService threadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, - 60L, TimeUnit.SECONDS, - new SynchronousQueue<>(), new NamedThreadFactory("jbootmq")); + private int executorThreads = Runtime.getRuntime().availableProcessors(); + private ExecutorService threadPool = Executors.newFixedThreadPool(executorThreads, new NamedThreadFactory("jbootmq")); public JbootmqBase(JbootmqConfig config) { @@ -55,7 +56,7 @@ public abstract class JbootmqBase implements Jbootmq { this.channels.addAll(StrUtil.splitToSet(channelString, ",")); if (StrUtil.isNotBlank(config.getSyncRecevieMessageChannel())) { - this.syncRecevieMessageChannels.addAll(StrUtil.splitToSet(config.getSyncRecevieMessageChannel(), ",")); + this.syncReceiveMessageChannels.addAll(StrUtil.splitToSet(config.getSyncRecevieMessageChannel(), ",")); } } @@ -120,7 +121,7 @@ public abstract class JbootmqBase implements Jbootmq { boolean channelResult = notifyListeners(channel, message, context, channelListeners.get(channel)); if (!globalResult && !channelResult) { - LOG.warn("Jboot has recevied mq message, But it has no listener to process. channel:" + + LOG.warn("Jboot has received mq message, But it has no listener to process. channel:" + channel + " message:" + message); } } @@ -131,7 +132,7 @@ public abstract class JbootmqBase implements Jbootmq { return false; } - if (syncRecevieMessageChannels.contains(channel)) { + if (syncReceiveMessageChannels.contains(channel)) { for (JbootmqMessageListener listener : listeners) { try { listener.onMessage(channel, message, context); -- Gitee From efa1eafe8eda054ae7be44d5d79bcefdd0cd367a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 19 Jun 2023 12:16:40 +0800 Subject: [PATCH 1954/1965] v4.1.1 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 6 ++++++ pom.xml | 10 +++++----- src/main/java/io/jboot/JbootConsts.java | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a01ebb03..0bfeb9da 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 4.1.0 + 4.1.1 ``` diff --git a/changes.txt b/changes.txt index df7c28ec..829c0771 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,9 @@ +jboot v4.1.1 2023-06-19: +修复:修复 MQ 通知线程在高并发场景下无法及时回收的问题 +优化:升级 JFinal 等依赖到最新版本 + + + jboot v4.1.0 2023-05-12: 新增:自定义 JFinalFilter 的支持 优化:重命名 JbootAccessTokenCache 为 WechatAccessTokenCache diff --git a/pom.xml b/pom.xml index 7fc3d87a..2e94d183 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.1.0 + 4.1.1 jar jboot @@ -41,13 +41,13 @@ - 5.1.0 + 5.1.1 2022.2 3.5 3.4 3.3.0 4.0.3 - 2.14.2 + 2.15.2 5.1.49 2.2.19.Final 1.7.36 @@ -55,7 +55,7 @@ 2.57 1.2.83 - 31.1-jre + 32.0.1-jre 2.2.5 3.8.0 1.16.1 @@ -68,7 +68,7 @@ 4.4.13 4.1.52.Final 1.8.3 - 2.7.15 + 2.7.23 3.14.0.Final 1.5.1 1.1.8 diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 808e5f92..c3938216 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "4.1.0"; + public static String VERSION = "4.1.1"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 0d76f3d89440d07d41634ba0e43d995f99a0d87c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 20 Jun 2023 15:51:09 +0800 Subject: [PATCH 1955/1965] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9AOracle=20?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E5=9C=A8=E6=9F=90=E4=BA=9B=E6=9E=81?= =?UTF-8?q?=E7=AB=AF=E6=83=85=E5=86=B5=E4=B8=8B=E5=87=BA=E9=94=99=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/dbpro/JbootDbPro.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java index 22d8cf1e..21307bbe 100644 --- a/src/main/java/io/jboot/db/dbpro/JbootDbPro.java +++ b/src/main/java/io/jboot/db/dbpro/JbootDbPro.java @@ -15,6 +15,7 @@ */ package io.jboot.db.dbpro; +import com.jfinal.kit.StrKit; import com.jfinal.plugin.activerecord.*; import com.jfinal.plugin.activerecord.dialect.Dialect; import io.jboot.db.SqlDebugger; @@ -31,6 +32,7 @@ import java.util.function.Function; * @version V1.0 */ public class JbootDbPro extends DbPro { + private static final String[] NO_PRIMARY_KEYS = new String[0]; public JbootDbPro() { } @@ -61,7 +63,7 @@ public class JbootDbPro extends DbPro { @Override protected boolean save(Config config, Connection conn, String tableName, String primaryKey, Record record) throws SQLException { - String[] pKeys = primaryKey.split(","); + String[] pKeys = StrKit.notBlank(primaryKey) ? primaryKey.split(",") : NO_PRIMARY_KEYS; List paras = new ArrayList(); StringBuilder sql = new StringBuilder(); -- Gitee From 7a6a7fe2c2829290db01f6faf319ce5277bc5237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 20 Jun 2023 16:00:58 +0800 Subject: [PATCH 1956/1965] v4.1.2 release (^.^)YYa!! --- README.md | 2 +- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0bfeb9da..8970858c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 4.1.1 + 4.1.2 ``` diff --git a/pom.xml b/pom.xml index 2e94d183..db1e449b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.1.1 + 4.1.2 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index c3938216..13ef6ed5 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "4.1.1"; + public static String VERSION = "4.1.2"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 4740a48c07d7a6f62f5f46a04b54bd64294d9703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Tue, 20 Jun 2023 16:01:02 +0800 Subject: [PATCH 1957/1965] v4.1.2 release (^.^)YYa!! --- changes.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changes.txt b/changes.txt index 829c0771..9278d456 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,8 @@ +jboot v4.1.2 2023-06-20: +修复:Oracle 数据库在某些极端情况下出错的问题 + + + jboot v4.1.1 2023-06-19: 修复:修复 MQ 通知线程在高并发场景下无法及时回收的问题 优化:升级 JFinal 等依赖到最新版本 -- Gitee From c9eeb360b4381b85068b7818916ac73253f530e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 21 Jun 2023 15:22:15 +0800 Subject: [PATCH 1958/1965] =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E6=B1=A0=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E5=90=8C=E4=B8=80=E4=BD=BF=E7=94=A8=20NamedT?= =?UTF-8?q?hreadPools.java=20=E8=BF=9B=E8=A1=8C=E6=9E=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/jboot/components/event/JbootEventManager.java | 5 +---- .../java/io/jboot/components/mq/JbootmqBase.java | 7 ++----- src/main/java/io/jboot/utils/NamedThreadPools.java | 12 +++++------- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/jboot/components/event/JbootEventManager.java b/src/main/java/io/jboot/components/event/JbootEventManager.java index b21332c7..272595dc 100644 --- a/src/main/java/io/jboot/components/event/JbootEventManager.java +++ b/src/main/java/io/jboot/components/event/JbootEventManager.java @@ -40,10 +40,7 @@ public class JbootEventManager { private JbootEventManager() { asyncListenerMap = new ConcurrentHashMap<>(); listenerMap = new ConcurrentHashMap<>(); - - threadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, - 60L, TimeUnit.SECONDS, - new SynchronousQueue<>(), new NamedThreadFactory("jboot-event")); + threadPool = NamedThreadPools.newFixedThreadPool("jboot-event"); initListeners(); } diff --git a/src/main/java/io/jboot/components/mq/JbootmqBase.java b/src/main/java/io/jboot/components/mq/JbootmqBase.java index 93c759e5..e8fe67bb 100644 --- a/src/main/java/io/jboot/components/mq/JbootmqBase.java +++ b/src/main/java/io/jboot/components/mq/JbootmqBase.java @@ -19,14 +19,13 @@ import com.jfinal.kit.LogKit; import com.jfinal.log.Log; import io.jboot.Jboot; import io.jboot.components.serializer.JbootSerializer; -import io.jboot.utils.NamedThreadFactory; +import io.jboot.utils.NamedThreadPools; import io.jboot.utils.StrUtil; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; public abstract class JbootmqBase implements Jbootmq { @@ -42,9 +41,7 @@ public abstract class JbootmqBase implements Jbootmq { protected Set syncReceiveMessageChannels = new HashSet<>(); protected JbootSerializer serializer; - private int executorThreads = Runtime.getRuntime().availableProcessors(); - private ExecutorService threadPool = Executors.newFixedThreadPool(executorThreads, new NamedThreadFactory("jbootmq")); - + private ExecutorService threadPool = NamedThreadPools.newFixedThreadPool("jbootmq"); public JbootmqBase(JbootmqConfig config) { this.config = config; diff --git a/src/main/java/io/jboot/utils/NamedThreadPools.java b/src/main/java/io/jboot/utils/NamedThreadPools.java index ce8ff773..212edcaa 100644 --- a/src/main/java/io/jboot/utils/NamedThreadPools.java +++ b/src/main/java/io/jboot/utils/NamedThreadPools.java @@ -23,16 +23,14 @@ import java.util.concurrent.*; */ public class NamedThreadPools { - public static ExecutorService newFixedThreadPool(int nThreads, String name) { - return newFixedThreadPool(nThreads, new NamedThreadFactory(name)); + public static ExecutorService newFixedThreadPool(String prefix) { + int nThreads = Runtime.getRuntime().availableProcessors(); + return newFixedThreadPool(nThreads, prefix); } - public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { - return new ThreadPoolExecutor(nThreads, nThreads, - 0L, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue(), - threadFactory); + public static ExecutorService newFixedThreadPool(int nThreads, String name) { + return Executors.newFixedThreadPool(nThreads, new NamedThreadFactory(name)); } -- Gitee From 25d40ae455789a5591788dbeecee926cf455e5f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 21 Jun 2023 16:42:56 +0800 Subject: [PATCH 1959/1965] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=94=99=E5=88=AB?= =?UTF-8?q?=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/SqlDebugger.java | 18 +++++++++--------- .../jboot/web/handler/JbootActionReporter.java | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/jboot/db/SqlDebugger.java b/src/main/java/io/jboot/db/SqlDebugger.java index a95fcbca..cc5287ce 100644 --- a/src/main/java/io/jboot/db/SqlDebugger.java +++ b/src/main/java/io/jboot/db/SqlDebugger.java @@ -56,7 +56,7 @@ public class SqlDebugger { } - private static void doDebug(Long takedTimeMillis, String sql, Object... paras) { + private static void doDebug(Long tookTimeMillis, String sql, Object... paras) { if (paras != null) { for (Object value : paras) { // null @@ -86,7 +86,7 @@ public class SqlDebugger { } } - printer.print(sql, takedTimeMillis); + printer.print(sql, tookTimeMillis); } @@ -107,9 +107,9 @@ public class SqlDebugger { } @Override - public void print(String sql, Long takedTimeMillis) { - if (takedTimeMillis != null) { - System.out.println("Jboot exec sql taked " + takedTimeMillis + " ms >>> " + sql); + public void print(String sql, Long tookTimeMillis) { + if (tookTimeMillis != null) { + System.out.println("Jboot exec sql took " + tookTimeMillis + " ms >>> " + sql); } else { System.out.println("Jboot exec sql >>> " + sql); } @@ -132,9 +132,9 @@ public class SqlDebugger { } @Override - public void print(String sql, Long takedTimeMillis) { - if (takedTimeMillis != null) { - log.debug("Jboot exec sql taked " + takedTimeMillis + " ms >>> " + sql); + public void print(String sql, Long tookTimeMillis) { + if (tookTimeMillis != null) { + log.debug("Jboot exec sql took " + tookTimeMillis + " ms >>> " + sql); } else { log.debug("Jboot exec sql >>> " + sql); } @@ -145,7 +145,7 @@ public class SqlDebugger { boolean isPrintEnable(Config config); - void print(String sql, Long takedTimeMillis); + void print(String sql, Long tookTimeMillis); } public interface SqlRunner { diff --git a/src/main/java/io/jboot/web/handler/JbootActionReporter.java b/src/main/java/io/jboot/web/handler/JbootActionReporter.java index afc4f776..54460879 100644 --- a/src/main/java/io/jboot/web/handler/JbootActionReporter.java +++ b/src/main/java/io/jboot/web/handler/JbootActionReporter.java @@ -231,7 +231,7 @@ public class JbootActionReporter { appendRenderMessage(controller.getRender(), sb); - sb.append("----------------------------------- taked " + (System.currentTimeMillis() - time) + " ms --------------------------------\n\n\n"); + sb.append("----------------------------------- took " + (System.currentTimeMillis() - time) + " ms --------------------------------\n\n\n"); writer.write(sb.toString()); } -- Gitee From ef5f560d8937dc61fa4af0224cd3f4e10d672655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 21 Jun 2023 16:43:10 +0800 Subject: [PATCH 1960/1965] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/utils/ClassScanner.java | 46 ++++++++++++++++++- src/test/resources/jboot.properties | 2 +- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/utils/ClassScanner.java b/src/main/java/io/jboot/utils/ClassScanner.java index 9765132c..b559164d 100644 --- a/src/main/java/io/jboot/utils/ClassScanner.java +++ b/src/main/java/io/jboot/utils/ClassScanner.java @@ -40,6 +40,7 @@ public class ClassScanner { public static final Set scanClasses = new HashSet<>(); public static final Set excludeClasses = new HashSet<>(); + // dev模式打开扫描信息打印 private static boolean printScannerInfoEnable = false; @@ -301,6 +302,49 @@ public class ClassScanner { excludeJars.add("ip2region-"); excludeJars.add("java-uuid-generator-"); excludeJars.add("quartz-"); + excludeJars.add("elasticjob-"); + excludeJars.add("reflections-"); + excludeJars.add("jts-"); + excludeJars.add("json-"); + excludeJars.add("httpclient5-"); + excludeJars.add("httpcore5-"); + excludeJars.add("jul-to-"); + excludeJars.add("calcite-"); + excludeJars.add("avatica-"); + excludeJars.add("encoder-"); + excludeJars.add("aggdesigner-"); + excludeJars.add("uzaygezen-"); + excludeJars.add("memory-"); + excludeJars.add("commons-"); + excludeJars.add("accessors-"); + excludeJars.add("sketches-"); + excludeJars.add("h2-"); + excludeJars.add("cosid-"); + excludeJars.add("mchange-"); + excludeJars.add("janino-"); + excludeJars.add("jnanoid-"); + excludeJars.add("proj4j-"); + excludeJars.add("sparsebitset-"); + excludeJars.add("captcha-"); + excludeJars.add("cryptokit"); + excludeJars.add("isec-"); + excludeJars.add("jurt-"); + excludeJars.add("minio-"); + excludeJars.add("logging-"); + excludeJars.add("simple-xml-"); + excludeJars.add("jodconverter-"); + excludeJars.add("credentials-"); + excludeJars.add("unoil-"); + excludeJars.add("endpoint-"); + excludeJars.add("ridl-"); + excludeJars.add("tencentcloud-"); + excludeJars.add("yauaa-"); + excludeJars.add("tea-"); + excludeJars.add("fr."); + excludeJars.add("vod20"); + excludeJars.add("juh-"); + excludeJars.add("prefixmap-"); + excludeJars.add("dmjdbcdriver"); } @@ -648,7 +692,7 @@ public class ClassScanner { } try (JarInputStream jarStream = new JarInputStream(jarFile - .getInputStream(jarEntry));){ + .getInputStream(jarEntry));) { JarEntry innerEntry = jarStream.getNextJarEntry(); while (innerEntry != null) { if (!innerEntry.isDirectory()) { diff --git a/src/test/resources/jboot.properties b/src/test/resources/jboot.properties index 8e026eef..2c788a17 100644 --- a/src/test/resources/jboot.properties +++ b/src/test/resources/jboot.properties @@ -59,7 +59,7 @@ jboot.rpc.type = local jboot.web.jwt.secret = 123 -jboot.sentinel.enable = true +jboot.sentinel.enable = false #jboot.sentinel.datasource = redis #jboot.sentinel.datasource.redis.host = 127.0.0.1 #jboot.sentinel.datasource.redis.database = 1 -- Gitee From 42f0d0738886007ebc62e8d0adc60eec0660ff36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Wed, 21 Jun 2023 16:52:04 +0800 Subject: [PATCH 1961/1965] v4.1.3 release (^.^)YYa!! --- README.md | 2 +- changes.txt | 7 +++++++ pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8970858c..8a30f267 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 4.1.2 + 4.1.3 ``` diff --git a/changes.txt b/changes.txt index 9278d456..39f2a440 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,10 @@ +jboot v4.1.3 2023-06-21: +优化:线程池同一使用 NamedThreadPools.java 进行构建 +优化:修改版错别字 taked 为 took +优化:添加更多的扫描 jar 排除,减少启动消耗时间 + + + jboot v4.1.2 2023-06-20: 修复:Oracle 数据库在某些极端情况下出错的问题 diff --git a/pom.xml b/pom.xml index db1e449b..49c5a239 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.1.2 + 4.1.3 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 13ef6ed5..6e37ea6b 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "4.1.2"; + public static String VERSION = "4.1.3"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 8a33edb3fb80d7da10e400bab7e2382e22a5feed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 10 Jul 2023 16:50:12 +0800 Subject: [PATCH 1962/1965] fix: use java Proxy to replace cglib for test mock. --- src/main/java/io/jboot/test/MockProxy.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/test/MockProxy.java b/src/main/java/io/jboot/test/MockProxy.java index ac8d91bd..f5d9e35e 100644 --- a/src/main/java/io/jboot/test/MockProxy.java +++ b/src/main/java/io/jboot/test/MockProxy.java @@ -15,14 +15,16 @@ */ package io.jboot.test; + import io.jboot.utils.ClassUtil; -import net.sf.cglib.proxy.MethodInterceptor; + +import java.lang.reflect.Proxy; public class MockProxy { public static T create(Class target) { - return (T) net.sf.cglib.proxy.Enhancer.create(target, (MethodInterceptor) (obj, method, args, proxy) -> { - throw new IllegalAccessException("Cant not invoke this mock method: " + ClassUtil.buildMethodString(method)); + return (T) Proxy.newProxyInstance(target.getClassLoader(), new Class[]{target}, (proxy, method, args) -> { + throw new IllegalAccessException("Can not invoke this mock method: " + ClassUtil.buildMethodString(method)); }); } } -- Gitee From e370811c881227142becc19fc983b6689cbaf2a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 10 Jul 2023 16:54:24 +0800 Subject: [PATCH 1963/1965] feat: upgrade dependencies --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 49c5a239..dca5d865 100644 --- a/pom.xml +++ b/pom.xml @@ -55,14 +55,14 @@ 2.57 1.2.83 - 32.0.1-jre + 32.1.1-jre 2.2.5 3.8.0 1.16.1 2.10.9.2 2.9.3 3.12.0 - 2.11.0 + 2.13.0 1.15 4.5.12 4.4.13 -- Gitee From 3351d94336f5ad86a59c2bd619249472ce62a7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=80=E6=BA=90=E6=B5=B7=E5=93=A5?= Date: Mon, 10 Jul 2023 18:56:15 +0800 Subject: [PATCH 1964/1965] build: v4.1.4 release (^.^)YYa!! --- README.md | 2 +- pom.xml | 2 +- src/main/java/io/jboot/JbootConsts.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8a30f267..f00db2a2 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Jboot 是一个基于 JFinal、Dubbo、Seata、Sentinel、ShardingSphere、Nacos io.jboot jboot - 4.1.3 + 4.1.4 ``` diff --git a/pom.xml b/pom.xml index dca5d865..5a68417f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.jboot jboot - 4.1.3 + 4.1.4 jar jboot diff --git a/src/main/java/io/jboot/JbootConsts.java b/src/main/java/io/jboot/JbootConsts.java index 6e37ea6b..cfe68ca3 100644 --- a/src/main/java/io/jboot/JbootConsts.java +++ b/src/main/java/io/jboot/JbootConsts.java @@ -22,7 +22,7 @@ package io.jboot; */ public class JbootConsts { - public static String VERSION = "4.1.3"; + public static String VERSION = "4.1.4"; public static final String ATTR_CONTEXT_PATH = "CPATH"; -- Gitee From 06f234a00843fddea6a177f47cd3c64d99a4a581 Mon Sep 17 00:00:00 2001 From: hinglo-c <13568939633@163.com> Date: Tue, 1 Aug 2023 13:59:36 +0800 Subject: [PATCH 1965/1965] =?UTF-8?q?1=E3=80=81=E4=BF=AE=E6=94=B9wrap?= =?UTF-8?q?=E4=B8=8EtoUpperCase=20=E6=96=B9=E6=B3=95=E4=B8=BApublic=20?= =?UTF-8?q?=EF=BC=8C=E6=96=B9=E4=BE=BF=E9=87=8D=E5=86=99=E6=96=B9=E8=A8=80?= =?UTF-8?q?=E5=86=B3=E5=AE=9ASQL=E6=98=AF=E5=90=A6=E9=9C=80=E8=A6=81?= =?UTF-8?q?=E5=8A=A0=E5=BC=95=E5=8F=B7=E4=B8=8E=E5=A4=A7=E5=B0=8F=E5=86=99?= =?UTF-8?q?=202=E3=80=81=E4=BF=AE=E6=94=B9forDbUpdate=20=E6=96=B9=E6=B3=95?= =?UTF-8?q?=EF=BC=8C=E5=B0=86tableName=20=E4=BD=BF=E7=94=A8warp=20?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E5=8C=85=E8=A3=B9=E3=80=82=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/io/jboot/db/dialect/JbootDmDialect.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/jboot/db/dialect/JbootDmDialect.java b/src/main/java/io/jboot/db/dialect/JbootDmDialect.java index 23012007..1395f52c 100644 --- a/src/main/java/io/jboot/db/dialect/JbootDmDialect.java +++ b/src/main/java/io/jboot/db/dialect/JbootDmDialect.java @@ -34,7 +34,7 @@ public class JbootDmDialect extends OracleDialect implements JbootDialect { private static final char separator = '"'; - private String wrap(String wrap) { + public String wrap(String wrap) { return "\"" + wrap.toUpperCase() + "\""; } @@ -211,7 +211,7 @@ public class JbootDmDialect extends OracleDialect implements JbootDialect { // Record 新增支持 modifyFlag Set modifyFlag = CPI.getModifyFlag(record); - sql.append("update ").append(tableName).append(" set "); + sql.append("update ").append(wrap(tableName)).append(" set "); for (Map.Entry e : record.getColumns().entrySet()) { String colName = e.getKey(); if (modifyFlag.contains(colName) && !isPrimaryKey(colName, pKeys)) { @@ -284,7 +284,7 @@ public class JbootDmDialect extends OracleDialect implements JbootDialect { return toUpperCase(distinctSql != null ? distinctSql : super.forPaginateTotalRow(select, sqlExceptSelect, ext)); } - private static String toUpperCase(String sql) { + public String toUpperCase(String sql) { return sql.toUpperCase(); } } -- Gitee

      dGOhd$saj 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%0DW3rRh4C}R6rUE5&++tFHd7(J{Slqf{ zv_;>0?TZ!*{RVu_42FKfZg%g?55h$HAkJo3OngTHPR>K+#dM+X&Ah4sr|KO0Ka#^w z@CO(zNt!o}3dw}(tAl9)E~1_xbV!QZ2y5$hYrBOU0t@a0>IOVHxJoKx-rG}%JP$@W z9-0`|KawgP)>71FU?kBm@nP@MD{^S`rqJ$*VrDsRoI;kZoX`!@a$d3uFtS3o%*IC? zUcw`hp+VC4Kj4_84!89%HqB=m1%o$<80;%)CP+s|4}7PU;m~(8*57_JOw;C-X@gyF&2+PuDzioW(HZa#(44C` zPizYrAX8AMQ7InhT^T>*fJPhPC_Inb(8Nhp2$ip13dKTNy4tecvk9|LI}_3k1nboe zitozo(i=I%9&8Es*<)Qa*B5Ip3Nk%GQ^I)sWdzR5qXsKEEh95)r?sc0){>m!+5`{-R?RHY9yqYg0y0WmFD-2-zl^?^XiFqMM$9Z}!B z@{XJepqk?mTYo@D*b{R|^)8h}8KD5)$h^;^aD~H+T&)B`PdMB$k&NWe;K3e^@z!tn zvz`T!xP0#(vYYP?6H|rIB;fUcbI}Y|#->IU2X(>EVG*yfTeTR~94(l7^NJfC11#Ft z(4szRs#K7BZ6Q3fXSxarM5sSeQUip{qu8_Zq+zl?k>LkiW$=N$! za?>(XL;sb@kP>uB-N^Z;n+L=*;jU1TXH=S=i|GC8Skm8s)nKUM0Mtb2>cTz5?Rkfs zJ=9y|ICjbHl`m=A36Z9`?s#t&(AGi@&YwFxEirzRV`~&H|&0{H@V*e+6&6%J3y z4O9QQi8KkLBb=%AGLu3?PnI?X3~-?uUJ(RVWzbcD3TQG=hhvi^xStLkT<*F!k{H#! z8v9OyFz=?&zB|2Enf`hbXZ;4_tUdAPQEb-A+0=_)m5=|G<8*1wk`Iz*qN%&ym+a1t z6W%u`d8vjB26fwLJ-j4{&k@*`6xBg|n+18zSd40I4N;)>kV{TT^7KCgc;}k`BXv&Wmt3C}7;{lRP>BP-$g+`xDz3qQ%Cq0Z5-t4U*u?`!# z<`zn_2;@kQcdJvx*pak>w35}M;Z-*R>jJ8|K-jIluljjd`@A7K2}X_YsC~#Lgk`)* ze1?);)*_s+Ntum{~*9ZEkzNQ)o z`NH8WZiR~GYT86XIaOJ0X#K@j{5-k`q_eVFq`sk%W+QacPwp}Eqf8}Hgs;pI+&h*! zUA#~^+ca0o+7~Khk|alxMD4z#zJ7|$s8JOtkJp`-6&oIX!J|delTVB#r2d9@ExYH7 z86Up~tT>Dmwxh@X#9b6(J9Xh%0Rp=VsFVVWpczj?I3WUoQl6fmsy$Dd8K(a5jB@<7 zEU|C-bxq#GG}mm3Z{%T(F825dXOfC!e-6p=$69X|O5{soYD$R<5{$CH3YUiv!l#v0 zso=Un3K_x6~ag83v zPBPS=Q}yjPXgCW$drgJf30ihmG~gsK_^&9*>#J~!&gmy}64~@N(p{EEYIRtm4>*zeeof!$Cqwxz^s?UEd-&+ksR)2asf) z5(YWEBPgM0E>+M9o%7BDbdwAix1&nSD{89|KmclA>e#V zH-9~Mn#Xj*nZTiD&T!)U#h5Uyh)HuS04Ee?(XoSl7>k@4t2H_f)u5Yyu^NIz3@LT| zn^bDp<{;!qQl`@D_?^=OP)BbJu45ax2>je+8DWKb8jNMq<_3q zA|eHf&xgv=;1Mp~A!;S=hqwz*w||$k@Tl5mEoyY+Wys7+KOQN-bi8Wi-7O^_tD(}F zZ>KLTfqh4|%L0G?8Y)xN6_8wTVU=1!7NIlS;YHFWX1-@GaSAS&!QQNo^Uv_exUd+k zZlv^`jLBZ1nvRw1Ezj!xoNBW=vbh3jX*1_V*(jT3leuGFwoI=fvSA!Vqk?U%+XlVF z!l;DKSTMZ$H6wZu)MI|3;t#1^6%CdOb4fzQJ{bad8hkrY{2@DrecDYojr9KcyiOz^^@TCL@a+15D$q9B1Re@Yh1WJqmaC07Bj1 ze>N{t2U+y!BPxtzhdL#9Rt3gC%uVB!$KvwdhKIN<>T$dJW$*_u`fFhp zMeVw`@v+-X?e}~{bP%;t3aV(J1A6cH)Z3sw0Bb5!rUgr>+9D=GsafG%&%FRn`GBbc zO)zy0RqdLs{|W;}$c6rTC>v!6^2U*0jGFx*`WtsIL z0iHG}AwfvSeK53orT;Dypz=uQILiCb7JnU0=iiPX&m?Vx=sntqmLgQpxs~gb6Kbae zhAr=DlZI*OY^((TIxq4W<~)4;ryk+h$m=)RZRDA; z@vSyURTpRAyAS(J`xjz*?p&?Tc$P!AU;m>juW`Gm$Nm*NXRlImMxcwk{`u^d0x>3i zfH3)hcWaj>&M2d3;ND*dVphUnFy_%lP+Pq}PMz#$07qlSI@2Q;=)VtTS)mZz7L6K7@>tX-98Gv`AUR?PEvq$|v_UPDP-|BC z4PdCL;xNYNMR+M#XLJYkKZFL~{L|9?C?K0}f-Sp|-?qu73QWwC89$0iqm5Iqd*4`d z%}0|Dac?=OTA`^twajv#eFGq+v~2zjs1>#>v;gMqr))C>tlY}=@NxCs9tcyBBV-(4 z)jeYh(F;F}g2It;E(*LeavON&gD>Bv58r%Sl6mN% zgxOGg=sB74wn(Vac~kr_NJ8q78R)=keOJHAn2d4PEBYnHlnX@u1b}HTGv2Z&ATcO+ zSn!6as+!*CJT1ylH}ex`h+0hwh7WN=+IfK6*#F{z9d2!~eSUzuw0GKWGDL`4K&V`ieOWqK;tZ zeL|?+^~rKcNXI<3E*ZjtN;KF606E7EDJ=!^u-9OOFaV2i=f@B6_P^U=31Y&^n{r<2 zJFpbwO&(bEW-z;2gMl4^=*1kbbnlWs*s#0+n3I^7ZI3XSmudkQ7xWgrIauO&ax@m+ zu>P?L#*>%0V6OI$g!wa27iHe!z!*$#0*hFgI-Fm}iMa?!o3Ok&w3eC)+MV%5+SJ_kz6gTpAsu3cE@7l zsiYu3B|ClbE-E2jm-d74_xLxf@a9PB@lm6iX+}0hSuV*U-$CP5-V}#%M?jZgP2hub zg?Bi*lIgao6{XcJtPSe6?SSdms!Mu2wA6(CWwaZWjZCIO*h)UXd}E{6j|4he^T`ZM9v%ut}8+pO1rPx5DVVv1vUjEKd&XV;!Cl5WEYw=sfcHUSzX@9zhoS>NnsWPqJsp zfKquu#Wv-Vzffe1xUEN4vX&oGRcGX&zCx#OGH24xth~h`qGqGO)sD@jEn1Ni7O2s9 zGPd-?RbJs@oH3q-P`Hr(YuTwvy=W(InAuZI@3{#CA$TpP`HtY(L?#hNq2z0dMQbY} z=5N6_g}k`=p0;%5M-tSHci~zj!2bTVSil3#vK)9xieGxkGkg1G8Z+AL@Jgn^&;^4r zuCbIRLF-3R_4SHPwgAKWWF^@Vxl@&v-IPxM=tGowjTCPZZ;_2{)A5_-SQN>aJeu#EqOqK{bS-ZKvWjftQ}+4QKq7KJf085H?-dmADQb*VRZ@JMcc+kObXU+ofH zWG(5NW%UkO7Q9MBY9&F^=k22^VT`c}t+YG#zX6>hQmgEBBI$zxbHN9{ z0nlJO{8#`OARG(~2nY{C0Q~vE0AR6zaM~C?YX*kWfBsJW6e_4bj z6%^L~{2N&Z76wKP@USO2QEg6BRBdUHLF;xpq6sO;+*K<;x{}!$$Vxw}Sej|Eanc5N z#nt(A8OdSe5F=(#Q=Vm8c|g*yb)|!HQg^i;`F3@zh^Yx2l8pFHQ$FuUYY7I>GU*wq zM@*C0L`nD{S6?!GMP@wplC|18%}lk2#bJmlhE(YC*u6+rtj<^7Pj=JELnoeh_p7%U zw5s{D?PW7R*0=@6XZRp!!x2bLz-&ZjswOHq@$fHS$Xu3SHHBo#?Yy}};3@;Y&ih%e z#F7nOCyYIbXu4c%b602&$P!I|yUaT!g3~VfY5w!d;l(eMO?G(^ zdf28eYm>6n0QrO}edbMP9t(aGAxiB05OiOA5S316-^PHQ>aie%9cmdi+T2mC#lJOs zE!$Y}i(fqR5PXv$+rcZCDRrQ104Wjosqh=^)SD@j+-w3Y0bUu}y8TM&LHZt~P(wH& zA`tVYsgfh>!i)JPRhT!E^6KTVx3`H*h4RqaJD1qE|6{eDewas)f2AE?n%Ia|i!qcMv+C<|Q8F*~@Tp*wQMUhA+`z`QpBBVW5Do}6`KHE~Ryixsu{Isye-u9O|IGOCRfT^k|K}WAA|`iZy;)RqLhBy6+jS`k4pJ*}&ne zRRz|t2Y4x2+JD8rGC>hm>e_NvPG0;M7WQre$S`BDA zl3Kw_PUInc957iN-LE4J)08Rc19gI?6aP)&y6bo$6pUt4op|=uh z`|>%$vla#LpUov5ZRSrcajgRs=)aEa&A9(ucj#@?<5L73n9Ya&+pn<@*Ao9Z`~wdK zJ}_Hj0i^@wqQWDHW#5F(^n%q0M_B+2#1aWms6Pn)!4TJ?zG^{fTKDJpFY0eD z8ByG|f7)=i9Al=(V=@01Xg)#*s0i~pLI+I`4T>My!awbRDIuJ(!1@YM_=^U5(~qyq zT9H(88baHQ$dXAV|CfRlHjXdgFSg0iY7MI5td4|<+I23$Fd!d?H%{l zlQcScjk5MMI`>wir*bA$&onV6bA=uEvhS~;mGT-|`Dr4vyB}MP?(dC$17^uL0jX?o z{cpqa!6z|#{6tq;A+-76%RWACiCAT-KG``RSjjW#3vf&+!kjOnhJ#%g#hok$l=ZP; zLF0_U!T^ClSa|rqtPdJ!3>s%l9TSHNjza?1>@6kxAI9efWqJSMc)&j#?`bbZn9}kZ zGk@D#USPY>LP0D4Bhk)#Z5qc#5ZBtGQ3QR6%LN?D?&z?9tH3h4H785Wj=Vho`6O(r(+0 zbBlpSP0_p$oNqYaAG}A_sH7-;?`5N;fTf`_$t#QE^*r=0U6A2I*bANpm(P<;p}BOt zMJp(=toy>MmmzMplUFovKJTl=)lFK`fFF-l5I5b0Dk{~5?5qsbz&WE-J>iAv10Ssf z58}sMDj|G@$U-fROP$x$OV0wlHePN@S3kV1e5_8JunAYwVs)bA`<9Ey&)HfNmhkp8 zU&YJyq`$l|*&m&`l-|K#ZsTI;_9H;tD%);3I{w+8cYG$?E)n8^oeK4u$0D}(6Opvtd2VSzLMsD+pNO!^t$qt&;IH=B`qqgKLr)J z`CL9RDaFW(|G;Ozx4qlmcI&*q6FfC*=2uCrHI=}ftY?Y?%HQb+KfW=qc50x_?A=Bw zciycHTKp>hm(biFKU61YtoWdFNqogqew|rMoQ?gSKABdk>RZ3<@?5SWvx$3ppX4i7 zN}ssr_Q@Q?$@^(uGaJr>O1*)|?gXoVE1#|g(gG6PX%1vRxfe70^yJE-a@CV}z*6FB z?UwpLfqXspS5ecJq}|M)T*+0mOZny6*m=wJh0Uh|ZH)oi1<{tT=lcYRpPmGnrd|{? z_aww52vhT9m!;&M-X&nep|X;9(%_cDb*g=`(>n_>MD5e>>Ia!O+Ap0~pLAM(snJZI zWx<+LPNf1F%YSJv6}{G#YPTg2Bsle2m&T%9$3ohlEb4q%{?A9^o=L?^`Fi#8pLLS2 zQl2_#PWjJ}wkt4JV_C4$rMly_;Tu!GEb7u;D*`esHbneRN!MSIuJ~mWJ(dJ%igdXu zff%pjmvw0@3DQ{91rpK&+VR&U&{brYN>``HcN>u)&C*5t^Co(H2y`u71T-6>=+Z>j zlJ&m~-u-7#QO)^PFJ}5vHumD4tbIQ7R3CqGPgac;4f_03R`z%tPv7&O%9Y~4TSqt^ H{=W$TgL}h+ literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/ketang8_index.jpeg b/doc/jbootadmin/features/ketang8_index.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..7b8e3f9389724083556d548109f0c0d2087b3c79 GIT binary patch literal 93475 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%0DgnnbRVP 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/apidoc_debug.jpeg b/doc/jbootadmin/features/apidoc_debug.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..adff7e65e044e6abb4b6b838de2563fb4b53aec0 GIT binary patch literal 57240 zcmeFa1z43^^C-SIn+_@IkZzFflJ0I0X%LVUq@=sMyQC$glkYS)doO?1+7Ahfqnv&b|-{ky*DZAu700`zUJ$}8=(f$qH{0jnrMMOmP z7A67!1Q@M}Z1X1h^m{57bjWD&uxW5TzjC-^GI)Zv{w9%|2sJ9*YB0WjJSORWuQA=1 zM&M?N=TUTz#8<>$`Vw(F@!8owy-a9+b4nN!latlP!IO#67vw$Lq0Yfh#^Cv7kk9Yt zN3cIKE=u%F#Gy&{{L1(bF_y9U0FXweNtJ!$+{|iavDr#Wg5YL&xX$MiH3nb6U%Nn| zPFBrXD+f;nGLPL(O4rYl@s(kmAcBYs;S}cUl<#7F)yJ*JI)#G&W{6xRy~|>!4Ap`J z`=2@)tADrN{A;e1{JPiuz{(Q_ zpY%6F$kFOP<;ddrS|yMB-*Bb^>|s5>5WP(n=Wl8!oYdu7g|is#RO%h3O-`#CBeeV& zaC?Qoegq%gKxu|4#$V2;$v!l0SVV=GOI#;glUb3DfQ6DOG;w+pGd#S-C&+)y!qO+3AZrpJijn_+lfZ=W!9XbKrk|IA~9uR<&xll?*ipH9p$`+ zA^kVkJ1Bhf;nzEr4x%9n%}O)?BdLkmh6|ko02U|#|3@|KC;7yWgtd9|G3?;Q3DAQCe?j5=N%HFuE~*AU)?R}ihQ* zU&wyxG7~nD_dDOihBn@I+nsQ?v_DUC?;hGYZhBy!u=rlQaPF;R!7-47A0jeqSutV9vsM>PrIWR*@to#B{?vUs+c#N{#H6`11m{PY9)-k8YndJ$MqZ_|Bq!N1siS^?3YcBlfov{1W_c9sY?7V*ZTR>msi?vL~5B1*KFZ zWo4$JRdYW+f&&Juq?oGCS5MtFoSBkTy(RUQ2;|(p=xJ`AcYZxJv>ZtmfvoROR=)^+ zQB~SygvdAOpK9f+x1H+A;=#5Fg5bLNq6&VAU>rsx5ZdSG?JkP}SnPt|;QuNJ?aOX- zG4t(Odg{WLTPaNiW&nK)>q%j04*GlP0jCv zBa;Ie$!y=yA(wYo$A4W;-&Fo4h>J24^Fb{0PpYpWsh94Xnm^lq75ee`HOXVcXumia z@C6|eB}nuY`n4~JElV&Wa>nPqAnlYuhzN@V_vfa?v*Tcs`H@P9q-GPDfmz;yh&TYS zx2KFBs6g}lwVwXK3XBE+wz7p?m3QpGf? z*c|PsWRyG~S~z2_{LNdz$E$V&4X#{d`ELxgTYg;v2(lxQ%s7z!;m-W-V7l%>S z1Xd9yJk-Z^^`D!X$9SJ-9p9mSy9M@rga67|zjFVYsE}1glA8zm7h{qLYmMMWw~YGB z;4oTg1@&&^D@snHXKVesAOKXd;ufg(d;k%Je9*Z(wRm-_NAX~Om39KIo#|V5^^tSr zxQ`!+t0*_a1pnj;+Gg4@j@FcbuQB?g62hr6^Uvm+AqbK$D<1AZz;wgR@zXN!;~w?a zQ|p0GTBw>oG}nCCwtq4Gmx0~f#a9ey@HbF7Apll^`Tp9*;(geX-m{!dLUW*zpYW;YuXgt*9xw|jNr2V-&kDdD{Rj)X z=pJ(``>`nP-ZtWbZHXrzqNM&XjoImGc@p{;@r2(_WBWj z$ufQvGN54x2R(UV7ETuhwl9j@<~_6z`4dJSyJi9y)#(4qj?t*qNAIsy*OaauBY5}z zs`Ke`0%yHRG6V}OJnXj&$nVRdd|UnrZ1w6o!_E8&)s0Qp`l}l$e}?dX+iFqpK#(mE z*_I*_T7R_-UoTb)Qid|Yp9%Y)w(ATA95_&^NXP*g@bP-RF86!?!j0=#Ow3I0tu!^& zfB5_i_(>m-mb8#90AN<#&*-mh`^lOH(}eyzX#R^n&Vg0%T2_B)0KVM*zdf6O>HoJ1 z*9EKJ-=cimE$cN&kDO#DHw^E)wDNC}e-FOSZBX}ek=t>{9^3pLyKk}o@2>19j4x^X zKfR!mOs-S11X{H^-9P0ErlX|mzb1p}6O1d4brAgLywT&0uUpPUQWFv{|2bzsY8Atm zjq48*K57WHOaLw}I>5{d2v?~0kNWtjP6_~!7+u5^q{NI5SVMBfb^cHE0C=uCh!`1- z*;g?_ozU`6dVsJSczMl+HWyCI6U-7N1r2=opMp&W4I36VFzJ7gXAQiE5`&97*|mQg z6~|lg9xk3I5^V1v64GD`l%bv8jbg}YCR~@}d41jPLinYd?Ucdy7Q!FH#oL%XZ937QZhX zgE^FVqcAtjAvk8iWy#er#`W7RuraD|uu@`Vc}jw`0=;Mnpi)1)U1Xx`z^I3?9cml0 zi^(*z8ycwX^MAu$hU)!MLWMq#hNRG`lz_S7=0$yjr2+tuWTNE&X#eHVUQ|7Bg3ZAS zy?o=~zD4-yD*Jl!vOXyRzljtk{f6vq9D;FstkKAL>oiSlFZ>(9w<7<$JHz*O0T~J_ zQ%Fi8d#f3mpzzzA<)6NXjqahDhk$c?1O*92&A?D&F5T)IZl z1sgU9=SfTfXoa#QRn$fo{FA}2KydxcKRJN;!ohBPGo>JK8?O@rJ~cG+ahR-(Pr8&2 z)ibB&UrP-CBgM^KnG33xB!)+!7E1YV`t=mh*s&-Zl8LL)V7B~+$u9vnFI_fND`>>v z*2EIM7eFMVc)$o=aA-8Z7wnx#$e01paTST+Fl1(F1U}r*>tPg;QD-M(|9RIpyRF`m zNOC)f-a_EKrks?yoHFU4Qz>_7@`8fmJOnBW8YSa|=<jUjq1&8A|QlmmfM#@UpS(PrQ z`BO;%H;Ljq4?aKoXV*=v0}CGiGsa-58$u%dhJEHg<$Y6xH$y6>K#c5KZjJmcfA}A9 zZm@n7{IwM0)j%HR9;K@E$Hs5bJ^w@b8^BHGUZPqWrVJye5hx)IjiC1%jQ@jrgS!9+ zTmUWvNy~rme+K|^ncTnT6ix}UR1iSKCAI;=hEjgD@+^%q za{mYSCL020iD)ShEYNU%X4Vks_P@W|x=DTjfQwDUj{zrsrcr^G%(u&1x0mxj()}uM z^C)m{_Y^}-PbcQ876$hAX1Dw*=RfgoSje!lga3f(AM9^p9ULI|ryRcr-K739b?)C| z_zm_AOBdQdjui>1M>b5-^BbaH2X9)pz^+oI)?o&$jd22$U+4M_=1o=uZ^TxiGykrG ztmPY`-vEE=KZ`aze}mO`s6U@oyW-;S@PDWH(^)NwyYKls%>IgdbK?6~{9i8JU@E`N z=WmEVSM=Y|f4u@iY5v;XIklAMuQU1{6JA`S`nr>m-^{bWV}0F8&Ua|PSG?}XJ`Y_< zPniK?HFiMnuowttr7o&F2n?-k%S zBClf%emH;#0R2T~rQhTEFYKQ$yfqcc0u2OTQ-ypD&h^~RwkVz5c%pyO&(D{k5z&Q+69)eH+URyKt0fE4NU2~5v&UiV|g!%e&&zWv+X{WM$@ zfDhRW6aWD8n@`Dp(DBb_-B#kA(Br@f>-e#r1kBgez)`L)-wkG#(Uu=@x~b~tnLzy; z=7BVf+(!fkrE${5Sp(8A_sr0uA?jy5Z{qud_MZ>x2L!)bb;B4`D{$bA%FWa@!v+Yj z*Cuj=l~yb_Q$J*jr8x`!Y8QXv{dCrg(-e|%B;m=uMMqkV>hWDdTMOOhvo%;r}Ar> z<2~zn4$6O{Pw;W?Pmkm5dMpE5sb?PWYcOgC4O*l+gxdngs4(F>KakrMj?kh--)2iD=?dQ%cBCILH$ z9BTeFm_Z5vaDK%{md)%n21AwF0N{Ei0T7e0zlD^}pUFR*5MB0qJoeZ7|6}Lm0e&fi z1qA?vgaAN7LP7rP%?%Lvtqe#gbQDyy+c3;5?9fcC3N{!-jCz=Y#H4IwB;???HDJN- zaR6ZfR{)Q8=jPKm3fa?2B{R_t;`yXKUxlI7CFEEa`zwHHyNCIh;^&PkfY-~n!CSxl zF>9Smc`OPuX1zDY*}aL=C4cu&tAdAok{j`$PtEjz-|B9t=8DR;&5H@!3VCb^VpQwS zE}FKOpm0q{QUsE5loWkRDWV8fZ}H6Nh)=qB=W59j2wbTS!abN4CKWaEC_X`r9Lq9k zb+c5x!9DYQEQ485JqaZHcngg0C*l%@1fH^6CK>VS%fYMRE=b@sC-4t@@I`bay@dfD zP_R0^xFdfa70L)L%F%fRK+_~{!-Z^)_Mf!&W@fk35KpV-&-YD7PN|iGvMeJUGMeF` z_XvxA?^9GU!^K-#1smlw#Ui1zjvx4jKU$t)Kx;lL5r2`%XFcEw0A*eeoaTXRb|B%$ zIcMX&G8J-)q48$@aC@o#d;iO0k#*;I;uJaBs^+On29)L2Jh>2%NJXo z3hZj8sAjo%(z&_VXbt*ShuTUzldg=sK?EATv)qGY755^RP&Tre-cIAqi=JQexFj6} z2uY?MpPfC-yIv087=THJjK^^ z_6T>D?S+Ne=DLnMegL{L&4_;uUw($53MJUc1oV0# zSn4)7J6A{N`3Y=h+m)$ShRZOEhtN~GSEfet^#!Bwdn91(h|$rIDTBSl1!#O%#h&=? zsN>m_V#|#tC1ZxYZV0_b zE%av99rYMNHuKbd^Lh=`=L&inPw(3$=Uf3EmS|~at z7tn{=8x|oy9w4qD+&JTBcB_;X`-Va0zy~hja(odE69t8)`r3eW*u7O3EP6+o;kDC~x3C zZe+u-z%sf5pus>XkQuS>pv-qq`0jpT-@c`Mhf_(o z6h_5(6LI%O<%cvXg;TXl2-kg&+xk13cz84JK1mPOoaLu%)#b`mq28Gkn>*om(5oiy zpVk=ybTKPN`Q8$!bW31mKmNF`+z_Dy1(&<4#}x|Wpw&P-5lAW(`S#u_2c+)!tv5k$ zMq4c1gm$H>AbXCU(cUgig~UrION2x5wSm*FnD;D8=ap@MVorxlqJ8!4l%3V7$bw~_4m8q$Q$lh`U| zs1-HT0_F}9$XZSH9*zgD3l1KxJhi>gC_<_zo|tZe0jF2gk{gt<%_IBoXFra6+&;~- zs@@9{)Vj~`^8VwOZyiS8=S@kMCH#6ZzCC`xNt(fak`U2n}4xcf;>Qq9>*@9Zx~ti{av)jkhmIX+RUXYy2H=fgd@2Wdmwfs?nygM91RRb7zSI_9zw=e+4)AYR@7o6 zKlWx$I@gAZ<(N00Uq!9#_uKckPIA87QX8K{(Dgf1Ep?no=AzQ*7J@Fo#MBZrXJOU! zr9Fl}(~Q>yi4+5O=Iv^BaL1nbQgUk)WWw6FE1>nQy@kOxfLlMhB(E^WobQg}m&0Pv z>U5}J4l}|omT|kEJ#QbI;f1t?D&DuG|BbxmM^Cg-=dtzthwb6k#Azv*O9dUa2M*kf zO}b|a5!kW}3IWHqY$b97n?MmF?5|4p!;_ae&w0S?QjJ>4wKtn>KUw zsfuPxmH}b0IlaQ!Tckg0GuA-1T8lEI4p#KCi3YIleHIwdx{L$9jJw%@p!}(E`H(?OC$t{Nj(Od< zr#~uz^#9Fb+Heiyeai1YI^mT9cpEh9J8A)Em-js~jHk|y!9N>QXP268Oo?ubwDrl~ zt9!-z?NR{yoBm^&a=g}OB;inj8{H}HlQKyh@a{@BN&d{C_Luut09E8whtw;;QV$qT z*8v|c_&1-zEPp>!ZA)X#2nOZWLENAS1VldUgP=i{yb=UbaQ zrBih2Bk#u>?P(zraV+D!*m6{B%M;|YcJHHZ-0L?bg{hJer4LcadCmbE0z&510r}Vr zL~;>c%+AH&(pJx5GW6f&u2Q+kT_H$daSWx=bWPj~c(M@C;irEeA1SKQ0u-Idglv4E z=jyR==Uryn8Ouv#S^-3*!FgBmUekW|;Sv1{hRGGN$O2%;E%r+ROT|5f{xwO(D8^M;I3J{s{lu zZ7Rc}_S&u50JvNN!>$SJMI@~%HB?emMMS2w&=P}pni-nN!THV&`EH_k4C);E>ZO}u z5k-&6#5%|IB&0vSOlHk7G*4$S5XO>Y3x?!1mH@O0b&la9VJP{z_$EOXo=N2HzK-ce zRdZl!_X6*^Qi+%-o>`@c|bNVQ|3rXOh1}bfOY*lL! zul&_HoLYT}f9riyfs;dC0gcAZd%bf21LA>>L^0~SNFMu^Hb1b9jmuI2YJtY)3E|TZ z8Rutr7FIea5rOw#H$qUIX>ap?*-2VQmu_(o0PojfSwYQtIWGSf_$Nw$JZgER$1sgh z1F_@z7XaSLQj_9C#+H-cCqiQPfWQY?t5{o3vBWK;`ayVKGPG~JsZHuj!$CWebxXq%HLQneXQj+~D_KJgi(PUni-;KGiOPS+x)=yZ?#+ylA1$u;j)MwVykEiD?t~SM=R9|M+YQ=5|ysO)HNBrLCmmiRN z02?9P@xE_ZVSf@q@p;`e@)_p)=>L9xM*|_RYA!u-KjqY;;-k5pDn;D18rwI>`me`e{bax_V;_FT?WQ#*L@Gwbi zb@L6-AmThbm}u9SqnoX!jgMs`XIBjt3sCB1Tn`9RojM!1HHw~?hYwgpCaL>d-o`tp z{@(Jo?S>du6s=?i&w1s~)^0O8Mnpip)C^Xs@FWo3=XmkZeJQxHI947ZE`?TWh~C@P zDG-E>CQpYiabWq^H;gj! z+kYEymEh4wDsXdo|w$ARazKL_LjzIQ`E>>CjY}CW$P7SrM~8W z%I1F}!v8*6#V3xNTZ>+|-Aa$|+-iI-%kgDTaqu@=SXI8+RNK_v^OAfbdT~$vV@Aey zFQ$H%CRO@K;s@MYf<~z%$y-J!7)|{z36`vqPuDpm|r_yB``Em_5SAE@D zf8)445St3zEIwJE+X*Ed)Id^Z-a|YBX_d^k%t#=ADqPJ}euYq=l)AbrU$FtO6ErF? z@4#>2RLQq(bgx0`%I1DVL*{?LP<0D3aE+NO{qE<;?+1A*^%=%D!wvJ4<}&8P=Y3z| ziPxus-y?m7V{&em|KF1_TOkj}9R9zNg7@*s-njxue6Ov4WH-jm-WMa9U6SQ+SBEYIgnj1<>)hnS*&nRDn*KEC+qckfXt|WP zKdA)hT#Z2XuM)};Mu*4l$_P9IweKH%@7w-n&sH!gp?4{!Rm+R@)!It^wBc(oENT8* z{E0S7#L4ay{w>?8{kYdQSTUIxaJPN@BKOC;p}Ul)@u8-tE*Y_@|2n?;Pjsu({`6$U zu~6EoG}4{$*#;%(JGWznd6{+vaF${W7rAS9D?ehJS`7OLE6~#OB9WqoaR&Cz{!;;J z&L2s2zB}PJYh0q!C=N6`j<0-Dv1eCEWAsQcUZ1;O8U9owe#7n51?$#XNS@b@CwZ@S z|C0V{*<4%;{E?-$;_dWU{NV#Ct>NWQ;2q%KvLNt&@sw`dFIp{id(}dd-Wzp)jjE(; zor!4=WYWDwJc-h^?JB;!A-aZjLy?8(&7G6K}}I{P>;;GIs}6Ct>#G7$yLSsE9A>7~cE9<^#Cg`yX!Et6Cu5xooU0TWXl7 zIOEyu7~{@0#Fi&r7Xz}*3c7Kc!almJmhY4M(Z2c$K%dE0lfPe@Gm$4I)X*Z3v}C z)txmjn*m{DWN0>}rE8sExvcKgk~#>L3-!is3yo_@a)sHjO{9uH<<)&VxV@>GFgF!pEOD!F|nr&N!LW2 zR2rnl2sVZ;CQ^tOee7DN{(FAH$V2+@GU65o0kI9MPS5bWz^=O*(0=D8lt;W|9TFR^Zj#1|vopHZ(M)RS`JI z5#!#lQTK3E%6j_ib@i(N^GS@D3FI}s!S5O(;R4QCBkZxeu0!wQqj3opMlXPgok{3cx%Dw~Iu#(kH z8?9FkzHatN9s(jwL-}?W-<{7X6SF?Av?b+(iuEQ<1XpK**Q@`q7AFo%Sv?)Kifz}Z z7u{YWs;LYKz^aO6-zH;A!CpXl>NiX<-@V<)AiN^%A&5fhs+qrh%ZwNax?SgF>- z#7VOkj}!I_r^0g%7*n-S-=27+UPB*u&m}Kf#v#w7;faG_4(VXP=f!Kn)TQQX8$p2I zU1Kk{l*~fWsLE%2br2|xv9-Kr#B`!5nu=)Ep+c{}Z_ zNwKhKPn1>xj~onns>XIpsDi;PH1&X`A!>0SD)3<0(@Znht$-Avz1w3{Z?-fzu*A0u z>0a><(Y_c-o|qs^8P z)^K1tK5?{AHy;a;nwtTs?GiRdYGKY!l#N$&brs()I8HBSi{xUfJ$ZMXCom0x#SZYp z!=X6UeI<^qt>z;YX4)K0&T03GWg$n%CZ0-!|4^>bvZ}fr@c8a36m(;xKM_|_Xg%%1 z$~)t`>6r2WWBG;gT4UUa?MJOr^N);ZCvx0&T=oz7IGFV65yKy@SgGY?+a!xCStaL+zCM^ZU0Bx`I zy(cKJLU=@zT99}Yix4M^(-m6%2_VGV0*4$8^t(x>oupyZRCR8-7*rTRQ!!?vga3de zW6vIp+v6v5RD;0}(_ujR#nlr2q#a&9=9m_%(`o^F`)$zN-R-y_h~l>HliBMu_Kok< zy|q2CdqFVMyfINc{cb$aZ3thlp$z#1;ukza*z>;~>sf9J2T2lxg!}84lA1 zy?|!)aNdR}NUhPFl^MP>D>}#Diwgy<&hC>gZP3R(=LAW8zQ}qf=uxmvaa7b!a>fN6EuA8N58yD5j%m>|DFR|g&w8oYn0#^JM`!WR- ztKv;P<&`u;(y(SZnAKYl;zZm^gHq#*5}3^IMl1JCJg2AZ6REXx@&<-Q)-T~!^VA;J z&IvE{v$GwdLRGzPHfh{9HrL=4AvUt?UzxvO%eyz%wmD>P6v5M3M5NQZRINaV@3&U1 zMf3WQwG;kBeN=srs~~;dY++(2Uy-Ior+Iw5#8lY@Q*=X%1zzrl23PVvpF3*vcbmj9 zNluQ7z4z&2?mlK?R=}9*Q})E^Phe?_gn2ymXw?(~cW`zX_1>PA>-0(tw>CSCiBOqx z&Dpl4DeoXtv_hPBy5L_(> z*cs}RKgiTIRU(c+(*fuW1QRn+Fn!lTS`X^Z6Xza`^b;_Zf;?Od(YD0Lj2W>E0;5WdT27^l+>pI=2Lc90GgH2u{ zn1&P4@tK_l-hO5~nF&8Bq346y!Y1Zb5O^YPG;S^wr&dPm&~2Agj8T51^_U`q5T;+8 zw;1CSzHnC!alLR~2@OoUh2g3k~)Y`pmD;AMaG3#}ETm7+h6fCe&QrT%Ur&yWA zSQfJI#86irpa5hb!TFit?cRO8l<6od4VS>_D z!WcJ=CCZ*da3m*cf!$em>?M>~FgbeX9r*Uq4|i;PFPtVJ!NClf?~e!knxA<@T5QHHh>z?GCa*bBgQEPg?3Wk4%@ z3);s4BC_@IF_aTD&*bYQmNa1>YQ*_j*X@*^9}Iw`VM2;4FTRU2CZ1n zs6dE}b(w8h69-ItgGES{%yp%px>Ck{w?|I~*d^~kpaYK5yNF;cbmm}m?J9UwZD%bj z26Z#vr}t)^5?(jO#}T+KQTbdQ*+DR;2G=4bGZ&boLmQ>vN=e!8yv8SPE2c+50e6j!U%*)!*3pbz@LjXi_XirX zEU=iaJvKVkC{kX3hjRr$zBZi-ylyRYT&JN`Ty!rGb=cnyoGOQjBvXtqd*~yziE8dy zde1;rBK)^_tu|~o-Ro%R*+KP+-yeki?V$GW_sHg%&rnvG*zGXwxC9N8Q<6E}_Prkq zF{0*M9ISNyguVEPqgl4BngipLN%=C?O8fUGig)k;lkP};5H;#6i7M-*{c`x86MlI6>X?(ZyKChz9IDlGdi z@PASXnNdNv+J4g&h}=H(zJo7cRKnLuih<^uZLdW*qEbjp zew1XxSFr9A%_GORZx7{IVz_bGO@U5gUc;=IY3a3y^Dw*@6lK}FI=<<$Xc zKAu8s?%eGXd$?cAEsKCtdY4LQQ>K$JhTrIEX-FvViw;d>atl(+rFhr~E%_(W233p1nDi%$ICS>& zwK#(%(Q7O3tDjy3%}1AhngVYZY1lzgu9TPzM{2nW>KzM)BF}nOf*dCj;zDsKl@@>? z*p9_67cSvy;GKQ)YeM>EiV$i{pMcLb!N?saz2+rMh1%9zC1OF#`F9X=3#HM#WuBOG z9Hfthvh2$kktQrCi8a(vO9dF&>TyGvYNQp;LQtxlR3)Q%yQoKyc2B5-7|Uk;xQ8_L z7N?w}{pRewK5Ue_uFTojhHT2ca(KR>Fj*Kg2KUsbH&SU@H^AFqpcGz2H4C0nXZk53pa}!=uvFN@N;L+I=ic@PzW|n=qHr zp0=z*<&=$J7{)!-0qke_-9@IHF7r5)P$hv;m5w|)5Gf|Q{lT2ILk^8ps@)5&u3ehL znRdy~V|W&zToccY&7JCbHt&^1kv?E}IP{|A67HIO_Efh}e>eDuuxsOlXFW%p#4#74 zIq%MeZ2?6$PJyvth@CT6}HS(Kb$?joPi!7ORPiiR({wlz}s) z47s0nO6ZTK$EKFCeBA-~XF}Oip=&a@6(@O%RU);-KpiV32OUiwzHy7`ZsQ$c)TXUN zKhttr@Lj8;SI>Q)<7zk(T&StExs}e1_9wrJ#(KAjs61wv!xrTxD_wSKsalFYXjC%P zWDrxeo>a6pRF0m+^i-q|xXe>~*RN!$4!5?Ltv@zBM(`aJ%(&Q$NpiwJo(cg z^_P7knZm2|av0(Jp3*Vwf@RX|+hgqy)bNkn%^Te;yLiulN|+@x!wNhmkI>TT@}UJC zDn>#IbU5Ab^2n2$ZJs7aLY$tmyJILUx8v(AEx!G+Yjnr5(6y|y8f9SKDbQhhyAnv3 z$D_!Ave9+yn-^Z8Ue9N0>cqoa&VC0k9HiB8d!w0CGs}aN3vGr9Q?Zl9P*(3q*Re!} z*lxU3ERVO20r&M?$atzmB0t(GhK@6$waPMHJ>;NbZk5NeHsB(@QdCGOvxVAx>A6eq!SMkWt{*O&=%*NU6FcP|z!a`mMfkYwgoPH;2iODr zikeEb;|(}(tIMPS_grg5`iFDS1{)wnrUOWp^++ah&n+&(tUFu?8B2fhDy4rs@9|}0 ze!M6%1b)N8>o4s&qF{@`|1SUiu8#cR@w}&8KL55 zYlaH+D8?720!xqIcrmBpQ|!HvMo~gN#Nz}%8oPwnGGyGQoaA9dDR8Kb@;Ud30fMiT z{9%%xo1xu=W6xHH33JS#qVLXTWvS&re+R9a`C3uq1Ai%DHx7~f%Z-1h{8o3s?*~hx z03g8sr40%a3Jw|y5&#YU?`{wv04gL}6gn|e^lOxRib~7|6_qPcw~0s;^lfZg8Cmqa zVzNk;RqVXU1cgSSF@z0$s#xQ)+y3?5Fq{DRz2WAg&&L<^>=5A^vw|;^Sc8XGs#y*r{&BIj+qZW(Jky6b2Nd*;k-kt6p*>eTE#a8jB5U@#B$ZsC3iwkvo zV&5%cmC<7COl<6}-#3pM zrL2)=Fj>rM5JzmwC&nr9XB|wju{glQ)R)1duI>h10gT_Dzp2HH>T6J$q$gBgZWiuv zbtO|#=|nkaNi8yhUJPGxxyx5Rl6aYav17B)R&lC?b-{ylmYTk@WbRB9J6L7WqWR#x z@uJ{7vTcbwg6W4A0eAWsgIfugN9{UOy9?ir3*G8{w#VFR*`=;B;bQ5Q*>!zm{ zGkBw<42QF}0oApjH4UO@t7*%yj@&E2DlH;OyPp!DvVI-m6(GBQkU@%1Rn0`#tG2K1?1`uiPn zS~$&SM!v8}c$^8ksJyJk(a+%C9@2T`SqpmEq9Pk7J3>0!*7~UBgengnu1T@QW5(t3 zY^TdD4tKY5m5=FFQ$+?@Pm=~8%h^+8)Sc~?Vf22kXj+Ne;C~ILvsn*aUJ0i)gT{3) z6j%pIXT>Yq`3NT+z1lR^*g&E(pvFR*!$DA{^>ykX(L$7uHEGwxp(A;2xIJsBSyMf6 zK*nA!lYSP>iX>K4UC1fGfmC(azXPwQwjyljEppw-XbT^GnHu=H<{dMllA zK4)5F4dMq{Zn@nnaU{EVhWb)EFo9ivgSDs%1E&qobK&?o`pzYD66yW2nGY!lRP2}Q zD!nw8fR}jSJ^$0(%SckYz52^l|2L^|Be%NxI;_}NG)_8J;f=xtlh}njfEr`Gh($1E zf_OU%AGMwF@8vjxf>_T8V*NZoR;;99j(5?cNG0|XwN6NC;EBYg{VBPq)Gjy_wS@!B zQa*rhpd_2^+Eli89N62682eL-T?{?=?4j2}y~`@VvUUY<_+r#BkkK5NPOWU--dtEd zbb6>CRqJJG?UL0tSWEjJYR8V&Qs-GIOU>uU{Z93>(*Y%XDHHIaqLO3Nkz11QrT3_x zDkcB{c#cV=94%lJmPh(fVpfSg_J0B>g2ql}bMKprPX71n{}VcpH5a*ND%@L+K7Tk> zg>EysN2K#^u1wrk;N-Ho$=#>uZV2A3nH*C@l>$8p{@d?DTRhIpgT9fsapV|TspeW$@o zU#?PY_1%^g?0JMCKxfANLsuf2|KJIpZbhPvcxZM7W6!0Ejl3Z=AnsCc`ZFdX2!s6q zFEATuZ+t!&^chK_Dcs}q^q6r-%;M?zyBejg&A9-N4dt$P-rK6J)r`ZIGuBOutjjz$ z(1|n*huxmrwhlz7a<>k1K0x|Rw|bI$B(k&dN-(HxFI(e_O5{pzp**GU*u4}=WP}Xs zZm?n_GSMY^Vof@W#5t4qfy1Wuj_JYFC-Re}Eiz7{loMfes?es@r=H1PS!tX(j&dXS zGeXI{F<9`Qd6BlvR*-d(+SwDSBSM~+z~SrheVpSWhm1|N6nzz3WuhkWHWOr4LYN+@9`~VHY+L zy7$Um(6-=#RR-xrwxc6gxO0!z;YGrpie&X%t7**@V6oJ#G{TQ5u{lL5(NZ?Lh2x~I zP)_0){Si6e94#hq93c)Po(A%e^rl^BXUU5U3WI0eNWPp6>U*C}KUxBwN4X0opy@Hk zFIpDA+_pPO{&)pAi+&JwE->DI1$f67jUDLNwFCAHjS`W#b&0Zt_o!uQ9ARA z?&$8oq zlyvt%{+<;aCjx~DJ)80x|VRh`;f#})R7sg zIUkRBW$oohj{#h_%~)>B`)j=pQ4)O|cqf}#p@N34bXCz-;Z2AS-*7s<9Ljcv@O0fq z25ys$Cx&@PM*4U3w0L#aCPy|fD0Cc3$Emew#279$D0*N_uJDDp$_e&U4xg)70dEFojti7m0MU1B7H-_ z6PkOBlPFK^&l&TY(&#L$Y2y}G5hW9=vP^W?mNEm~;#$C-V2KzOnNz)RcM$b?CEDg- z9njngOX+&}KRl`T-1!rv@P+ zeR4#93m2JNmr&FlG?*X`isnh_y^J~8$d?-qJQGD?(VDkR9XZI3!AaA$Q!*U|#uyUn z6@aU*;=BY<;w&Fuff!EVy+YKxMMUtnVe!Ho5%M8J=Yf3?9;1<72UUNboXxvKQl4jd z#(GcGcyRn%mrvRuZ=nr0?WNCv?@JK}TXGB>q}UA&W9-?uozEt~VfD7IIuk5Zm5DXR zYGt0$=?M&V5`*8gV@##pKn;!-EDj7I$sgB{O>B6&u#pf&Sn^HSE&& zKJCV{ZeicJh0(_&L`0!;4qd%^OOr$~7nUM>PRc1XqCR*9r&xj^*=mY7Zv!+h$s!rwx#s<1i@f)Am<6NZj#1$wy#o?UO$1p z&SwMq*?=z^sWqQxy}VZbrqj0|UG7D@Td$=91qV_DZ5A zAI<+b;4fo;-*gzUY;N=eyHdNv8>*%a{g#Y*R6=oIA5dQYO5S|=ZXxGb$+m5llUS4{uBzo2lyxKrtX z*Y16R;)kvqy-mifMvn*OvetbZ3-h=D zyayV!5&kL}=7Q4#n9UQW*nLg@+`L#Gg^tP+r`m@`GE)7wmb$D070Je!@d#%l_4zR0 z*jyj#_cgPGA1rQ#Vx;zV)FEU_v2J>1sK6-2^<(*X|5)zhHIB8MuVB>R;og&4@h2B7^c+!~2>-;&>`%aDUZ zC7i^LYS>9!vdTVvyNm>x_k+o!a6>tNBK+k)=BZvi1>l+w)IC^xl1nyffu!>z7iyjc zqd{GXfR$Y4)HLrw1*@^dj2v*X;Zhcrpm3E!$4c-W13-HK!{S&moThSIDA$+v|6QINNj+;H$g!LZNi0@0V;Hg13 znPS?){MXCGrB5IlA{GEyKefn0mkAk#7a+&-voKOpq3YZO~j-Q`6sDg`Xn1=;q`t|ucPug{2OftfhG>u&VK0){S#>Yd6CY2UK zIXj~6BeBP?brxdZysG6>FUQiT`92IZ>>`|bqe8*e8S>9L6!*bX+U{8Q-{p!>HdB~! zx~ldqnECFkubAe5zV&pRYOWgdCtyh*nxP$vtFBbYe9V-2w*sJ{`9vy7 z1YAy`eal*z6XC*OcILc@*#{FgkrRc#Y#>mIeD1f5sZ@rOz|2TX*ld1@ryB4j%VJxM0}2TM~&!!KME~={LXLSgrg+-8qJArFMOw>nhrlH~a6r zc&cwc)>{qExQRp&C=HV2-5Q6**HPgobi{*?Z! ze`-7E8cinzRMya3>X`|j6u!0Dy8C!w_}N5q#`jVt?mbnx|Do=Qgo@?RJwvs@XH%&s zjE&%c=o$?smcQTl|KohTJwC)(VL51CF;u$|oi0oM@dzag-`<<0$$u$q1P6b-`vm3E zSD*ytCqVmq729nNN=*KJ;JpZ5g6fbAmyO%qaO}aAD`Og6R4jMCxW_~C_7fIyYUkNY zp@78c(jnFsHS~tzpdJ*^o?8#BY#8mB_?C^%W@ptlTlC>$$UZ*WIT7boa%R8fbbWkN^rPzT#<$L?A}@d0%6-9q>Q8^!k*^Hd=_r?_ba@S(NqZsD zhLOLPNcE{giwm8QBjf1D>ry`UGL=Pit;tGL5Ychdv@$iM6qsfd{)dgFLF(nk0x*5>qa zc=lS1$TR6AANg`2HP?({IO`eeG*w6PT$8?Ptyo+02;rTzo~xKD?MX$x;PsHOdmx}X zU``9)!HR2`*1dxBCjf`-8ODv98fm#xrJwsf8+?4WmrCmCR|PycrD@tunn$Y6II5tU zb27;TN(@{MUwnG;4K=lZHUWTnfL-q(Yl}<$=gyJk+?;VHF%r!G?RyCr9U8Zh=_VL+L6fZ(W_Js6dE*ZV9DCeK1XP0&R)fIB9F&-i>kL@tY;tL8cQ9_%`c- z4UH+u+u$rq;HPL2IdO`&b1~f6DI!Urj;oJh6Jo`?GV;HXiuhXr?VNV={j>H}LXyc2?@#-1}JhO1#z{8{zf~ zgset$BHQ9i)gfLEuOml>^%B<4q+v45Ui4^y*|?ZguD3B@>za29Qy^n2$YA{swMqO8 z223?iUS3fw=50$1(ym+MWc$Dup1hC7HVV(%*|z4LMV+nWg6U`4ztDZjw#zc49cdL6rYC~Ax;uZO^(s2AP!o86ndWMcBeB63^}qq27$43tML)O-JW(H? zrpKHuSSk$upg*dxDEl~Eh8&FaX$3XHx=7b`Iq|OekA-2=&OZSVSGIQ@2CRO-m{HOU z^=jA8X|6$6xk%;1*j{?!*?SGNI_G)mo`%)q;R0UT&(WjyiZ-m+{M8-}Bd!^VU|LY9 za0YEVUwKM>_$pyQRZwzHAhB=SJGe}4H8dm>D-5P!eS z7BdYcl3rKxpEM2`hsYfrotwDd?lkVIprPUPQPiUk@RGVK+a_JALr7z>}o zMadcH4-E6rT&mn-;(BSbMM)6wch~#Qf4`6Sc~J zbUhfFRrPF32hI#-%YPQsycEU0!Te&yVU02$?Yx_#Oy8)>{}}+}VLiYr>GJ-I1l6Bh zJIp-a&~59a^V+)j_(rh5z%NK_Z{)b7jEHsm8gx=rhsq2ky2B5RC$0%&mNal*(!xMPU*WX6jzwq4sjG@}LEGd*+vYi@VewT@F%p_U>KvyT zF04liQ&E(ojqo>U)^AC>R{cR##e=QC-ap{`hI@Zlgt8?FKn0iZRiJ*4{0V4!k!yg1WuE|K#SXf; z9hq_!=$AcY4KWZ95bXG&Z~cKahk^+Fl1*^OKM3nDQ|I@5UIs55ig4|;wtRJPn(7+D zrBPa=Jfq9`)`)wa5UW_f4irMUbvFWwFM7{yv1`Hrp07T0G~DamNPxvfx9+t6FyvcM zO*NrYZ0Jyy^J|$AFX*zQ`P z2Zv%^Vb$B#cI9^Cf_6(D`Uk6xqA=#RtsdV*XWSD0;f4R3F7i!w-Iq#HQT~zCNWOb# zO7G>Kfm~_L-;TaMgDn?xse=?%5WCpTzo&%D7)$|4!2?P;}8_YN!DQ2=y0qw2SN^ z`P`xoWtj&NkGdCoGl6BaF2k=1&?WhO6%UHw`YkvYJ_aj!&w_8em`*CYyN;pR`IbWV zhRB39m9A0UAFD;&>tS;uE<@al?Vp%0H+>PMX{29RO#4Rg`c~HqowSSAk%a~5>aDx}3xpIEw{|4|1o(WbO0ZYv`qY+-iVxg`iw?!6%Vb@J0EiK2 zXZp+Xv47fwV4J9@fWo9t=;mhsut9Iqg@Qkr#j?iwvU2Imr~Dw530z-#f-7#>h>`oo zEKKJ?jUvbE>EiVJd-@u$zP|*2dNxJ&r2YTP0sGYdD}0N;zr+8aL^x>P4E#5A$A8cx z>>Lxk0{q@35uI z0YS&_J~7n8Y&}G`>oXl)4>usLOT%SD`H+|X2P@CQ9K7p=O=!a4b8WqnUpne8Bc4bE zTM*pH7pOfx31gBZUvpiz`cnbSMzQhYM{@S|L==5=$pGKxJuOz5ON?puy9o=;-gfjU z>Sp?L@D;X#+&4}XEwYKCNiK=a(&X-;D3p}{<=(Iy7yp~&``?}8R3<;i|BD}UOZF!c z{jb_*7!KCScVf|4;YAz)W$C4FlS7Ck07U%WPvR+<3ylb9n&UnUZ?u>d=a(Taj!G%{ zux;2^cq5*ezL67JMVhL*GlNN8Qm&G}xl0NgHX;V-co@f{N%G>W(_ny^p_7R=vc5%RR-i1AP)IHF6wbXY&s4M@UnACf|@dxNLVUCCffr558G8W>M!)L$&7#~ceR#A zIzkYZoTt?a@x+l&hZsj69@4=L_0S+$rkh5My?R)vRF0b_P@a*lcP~MmTH|u(17VDX zL+5Y6dh0wEOER*dq7yi}RX6c3lf+cVN5uH>)k51AZ)f&Iy4d>N)ddzM&CDZ7@t7it zj{^;YEc*9@R*TUfRmWsKG#nrKt5uR#0o5kpwbfMHC{RsG6=KCP^)eSZp6y^Oq7h}- zLQ}BwZk6%)S&LK!A+xL`*Ta=T6G2iDs826Nq*K&%dsoY%|CmgL7jFS!wpCl;cjmwZ zd+PaPJ9Zb>jqkx2vVX)G{)8z1V0xx~=Z35)q`+H?^FC8mP59XZ+4kuD%S_6s)sxyU zANK(cOBIdyRvIT_&SlS{8M4gf-OZbjXittNop)B=RP5;4IjJ-eZh8Rt!7&4$GFX}2 zjge{q5e+Y5IIoQC#uzl ziOq#f8ubHoSvISfQe+gw4RR^i$Utgi#lGxW;*#B%lKoL9K((?_u>^-Cq0Q{>Lj}I# zL%`7)9jDm)Q~>0Xl`4n)N=!lpY`a=rAnt4Ov2jHkoqyeZu0vt-v6|D3I1%LR9 zB{@UUI`U7(atOvy(UK6+t43n%spSR^CE0RxvtY$+WMTfamHlTDxECvlB|oqlLu*i z-d*?!Km~IEVIHftg!wTkv&(QDx{)vurylOQF@|Y)jP`6n)F4u1c2J)Q6j$UzqR6AJ z&)uK_BkWw5CEE+hQkcW+CTLoLY;ta4G$h8nRpaoljc~1o;ksRvN9fwL*s+*<5b@g> z%1)x=glTJoSS1pSeCrl@Dctqx#XgEp+#gaPY&K3`m)OKDk-}DvVf-G@hAQUQ=@XW; za;x^aX{Q>Zr#^s|=7RlVwh!CWq9e*M1?ks;QDS#Urw=Zs}bZd2@v6l+X%m zTpyPA-rv z@|h6CXHqmq-AY#N#BWd1tj7uo@(ilwgcWjj#U=F5j=m9dM!e!8=t6BXst#Sdlu}Pl zvAisVv--ka8+=dD8l1|b%w3!IT-)VXbsECl!hx4S@+A&SX4m$wkQZcKcBG1_>h%^D zg_28_>8$CO^A%~Fo|(BD%=9kC#}$&`eD#8cP9i+j197O>M=;k|8@G+))CM&OYMb#0 zF9AYH1;#f4iWw^K2`kJ7nN*1=qQ~ZLVoC=D?Z)(Cz`+JlR1QuW<7%gM`*~z;;~tSB z0@h@gTA+uat@qD-#pVRB=KJ2#dWlPb*YdaUY%RLjPqHLh(Y;G)(}h=Pn9Z&bH=p82 z)FRyJDipjEGla(i&z7=OiJH36Y48z-EXF|EL11KcZ+MQunbcpIgv(lH4CkEoPrx_%)=lotOS&xM%pKF-n%X1^fxToja|@?a25j!qHc4Y@WTCwml>gp$805Q!= z8m+B+xe-&7KR>f9K-k7_GRO*n>aiy780}L=L*I5M$SP#EMNmu9C5gurLZdr+q;WshToG4&+S{0+Fw(3=h|fZ6 z{ewhSk4e*N8uL(w?*V0Dv^sGqs7)B%l>xSD_+Z1fHUHr`j}Yv9u`1n!x)ofu_N1`FCA0Y$zG+0KXY^odq4N=(i0THgw3`-4K zkzD3;ygxyIMy@+wSe)MNL%A=gX(zAyCaV@}$ zZcc&ij~#Ht)1vDJF2c%>sibIddkMhVaE#fdr}!JFXK|$wEp2!>A{jKz!CESCwEDv` zseMIfN;YEC&?nE===F=$VHX(&Z;TaDTD2qikxI2~pP^9OzXF4Y6h7Us**7becG-TP=6AeOG& zCyB)-X8Vz)TY%}&-u9aRQDaqph})}`H1%SW)o42^nu>2ub1paMEvwwekXZ~e^{=S^ zJ(o$n=n-mTs28)4!V$B|K*sF<>Es9-ncEjN)V@kuYoa|ZbFp31VJ9K&HYqg4|C$zz z&(mNgD=Ito69BB}J!EYjp}&a9iL)KcAoce#QR!KifFq3YC=WCX(D8C1s(cz!zIFX3 zkZ=Hsy(Cd=p==xrM{dWdM)Wn(`p{S3g>j{&6f2k~Mj!7KePby67oB^z(C#dG!N%R) zdLfCH-mW=BVifzd%C<*~z@OdM;|HTTiAq(OAbML)Y>T(MJ-;5uqg?64m?m2)3fhPd zIHs^x?NlsfenhTRuz>}`k7;-v|maEw@2CT*c7$5vaiLflZCQj}7QkykXD zC$wjCs-fC?Cmr8w&|LgH&vItP@eIb^J4$YE##bq_eAa-jCoHT5E)})h>c9Fp0&njP z6AjP3N_?f(s}+q2OXIcRUguN9FltNKf4G^ll#}dN^$?m!bH{%1e6983^fw0MQB3pn zB7N}27l#!?mY7=W)q`eKx^6fdeiYVfa`A3V*dLItf}mOeM-n3t`#z2UvJm~S%3yf@ z5(2WYFg}hRrRH|`F9a6o3*(7`EDF&-gIIy10hm&XcblN&hAx7Rz8|aIU$#7w0K!Ri zUn8;?#otz~hYC7PN*O$}dkXcklf*ENr*~{?%|pB1_1)205ObligzFD~`NE@46IaLr zr_MUSQaryfdYvI{&fT62Vo*CC_*EX}p9{Q}X3CEs-+%KB|G>C@tQU3t zwK`>-Z>G=}wvmXQw^ypJ7kL7^!MUE)L9%GQ%ka z{6t?ctyP@huwIQt-f7ErQd)aqze+ia!FhrOSEEGh)e(J2X^{a~@Y0ZDb5Y1;3wf#@ zdiY?Zi;2vUs=8h~77SuWGYNR!37QsOmgGrjp|CvuSi7u~-L^123_K6)$vMq2w0gH& zuGW-)M?wNftJD2s9iFs7$aw(kd7jL*PN(JKx}!|5pNd6(lJ4<&?lt3;_8ki~B8p)_ z$De>0=XbY&II7EAi;W%``#?2Rh1g8y1$vl3Fj{6a9__YNgizlSTX;Z4u{Ph=D46cQF0^ElrYE*f`bgaRX8GpEmv0d08}DQ&cz{-sX;?KVSyaUTvWa>FLUi|?`q0q{k8*0;_cO?;JY z%bO_)aif-?7toQ-<^*uULBvtfFMuvQB30bSghe}&|1L{oU`;8CI_hRA$% zuaUEjCaO~+CXqkkJgxtt64hhCvvtT!+G$qkpA#|B5qMNb^`kK!sTbp$p!9$TkNac` zfO_sJjyO~b)CGFIPAy1Wt$j@x9${qp&gC_T26#J zoeg}+?|ShPrFhZ-tfbuLYVIzp0?zNY;~y0XSo;!`^nBt)uHT34PA?Lnpmy@Fer;nR z>x#LX`%ge}d1c_8w|1V`*zSyy6$A+a)fKDBjJ;0^g;rk8zpjdZ~l-&QsN8}H5=Ku3`9q5D^LKriAb6684c7mIoG;pElXCF=E{pX(LCX$ zBp5ABNm7`egzM)kY*2l$pR7#ifaQ3`Zv!`jyl>@EX)}L`2K#x4rjw70(7!%nEOlQkz?>4rMGoKcT$y%U&05-MZg z)z{_k^x+zj9aq<3py{hC(CRTDrP!LdoO+hext*Eg_tLt z)09~;j_atZwJ-dvBNW)R%j$^$i=;+61gWA@IqCvh&y*KQT#d$U{gB)K30HcIR0p6M z<;H086+_hF1tT}!9aacmJpLjg$V+;{zUHaZe0moKp|V;9MHG|_J!pqLiX+gnFT~)@ z9NJiN54xg5>QO-Aol6p4nk-_2EL<4+%ko%y2slXt!aJksT7-K+F(=f^?+W7w}&oNl>tWU{ibDEGf?MVH-H)3XuXh~muf+93OgAf@WgMN zj9Vf$lHCK{KWT54aqmp?q{SOr&_IH_D0m(C8wK@S$mmeVP_dMaU>5xR)fPX{5foj} z)d49tk@!L0zwZg*4f6pp5bgezbooK_GCW?muF~Y`w<(6b%d^2ad^?Ohi=*b9Z@9v6 zW+o8G>Wo~CAjpq~Yia_6VKupIjZMKcCutQs5ETCH;k`k11>Liyz@;QIG1?pyc{9Ju zsD;9!jcszV%?@>xvidJ1#@HgM_xtW)xq#I01E2WT58~d-4}>}%94~3p(maHK!sH`R z{Kzk?(f?C;N?mZ2Ry-dwMN1l%f>Uj~*ASrvKDHjM-An`2R{>lqrEIwJHNRpRcH?c? znXLIw3+}B2=_iq`i-Yvu&>H%^(%8)4YOST6zkdCMzyulS8^=I{ZkaFjxD2|ZfCepB z&!ZP}$3##$Fod3~Z&z#d^~rjWiC}UKwltd~LY`>sT=u@qBF=tS_GD@I(!WikQbxrz zY-jD3ZRZZD-mP>`8yQWrAga!`;O)|&A8 z7^T%pA#>Z>#=013PzgnP{IKW14YO=rrY(|COZNJofFwm$I1Hm_c%qL=;a(oJ2|M|GCdGd%~F$f!P-;A*9M;Tq_W}#wswzZGdpdD61 z7}t(Uz6Lt`L~UyWT35>BS|4j6nKp}oue4R+8|kEvH4NHCzTOSZDq|AreG62y_QYPE z$GT_r6-A1)%DrEKR}_@#j6OgimRqPZWQZ+J!-_RqVOZIT!hL@#7B6ut zcBSpYtdhn_atxZE5Sn|K=l!GaMH62Si!edCcdL-bhC3t*iA*UQUT3Nn=u@vx{N6mi zC<-kzs4_Q7Y+_ZXx(96&1vpY*kP;Yc;3T->X9IO$oV^@$Qs=$l4yO(L=d&QZlvVBQ zd*EKX@8MWYtDl6lwbnV}v=tOT4+O5dwJaG8fU^@cdkHRmB6&3U+64HV4HHahpke~M z6ye$IxeGNOgs>!Tw9Hs8iZC`=em&V%ABziUs9Q|pDr)&%At@}}JOWJ2O$wTeh0-tk z-7cx^0n%?)p5;MvIdV0W!lp3*nCI@|vgUzryR_qf0-|&!)^g!hZ^ND@x)V~JnE(z{ zK=b8k4%U!D>O#VF`h7(Vh(9kS-$X0C&{t^qJ>!}+dC=ZUAPSG$2&ZQwVJ>0k0W zE%Y7mIHcPoM4CM4bvo;M2bqf|aU{yl<7#^&(ywt{V^GcNlX3@}*o`E$qmrAEVZ9EA zup5wC6Hv16p{XcTv4?r}dWYJ`#)+@;YO7jgeQ}LKokK>LhCS|3=D@%2<; zJsyhG4``iT6j5$A;9-sT)mZZb-$tx7DOQ}~`F+4kSFs#3<+1gy)L?{5)pnl1$A%SP?5_{oXK77`+Wb_q3wi;}7C ztzB_hk6FU8Sd;8{^eQwkNNvh#HPvGMD)NNhXO{*B!Fc>Co|70k@Y;>pNdh1cym3{& zs$k=n0Mg<=Sojkp^gV~Z0dbT*ChVAe46}!t2Qvn~2%r>OV2PoL(28fneh+1NzsGp4IeqrY{}U0@*j1T&Iso1G^$&yITCdBb_? zV50m4TXI;o^nfxT?S+wx%r)=bBRms9bFYr(MEiwmb3v1tjmtyv{B8m3xuh#eMT9}h z^}q-4537UYWwh%ne5_t!+h(9<=1L8$GE7$Yay_Q@fkfkZe0(zg{TW#eDxEo{@~c8| zTxq@|7K>(CbqT~uM@3+`>dVwD1Y+HJ$w&i>m03xP@{J9W1m#%{PpYsDK8$Hg=s`9e z+PyNh$=T8fuc=LVonf}oQ;Af+h*V^QQ%lp-HSOUQLO9|sZIfo;>p0Bekk}crG_g{0 zuW{3M-h;;EEhFvi2V}CygspB2Mye&~(vzOjpRMe3!!ETm-{7#A(vPExOGIhj$(I?$YfY?IDio3&9D<~ zdmN3aYPfzbi+x&YOwg@rznlW5)jmUTTe-9Nwu!07cBtR`a_>CO~-`|BUi zVPU7$uoQ1(4bYwRRwnw4v;O)r)QW3oZiCf4Cakj}YrM>bd1hFIHzrhuKww%`jt`hp z0$0UO%_o5;j*}iN?3#9mJy5S33iZ(t$#ZpU(>RiO{rC+_oOTDMfLc=yKO?gi(a3Na z3E{2{j#t`)?2u6an_I`;(wDQbuz@b`n7k$3V}#FC4z!@7jJuz&GMq`g{|ERhy9431 z#x?P88~|L~IQ6_c8Vt1+NS{UHQ!NgbX|?PWCXogHYCeAG5@b}{L)MYLPsCEY87}|8 zec9KnJXbKc#kb5*6o2_zC270bgq4fa<5VHiqea$de*Na`ZrWdPY?eA_)RnYNW8X#M zY8asg?@+VFy6AAv!tR2-et%@XB|cT9w4tG^Z^3IKE9r>=#Q;@~Mp?mQJeE58q@g2vL>K z4ZAao1vmi(9-_89bqoG_S9OGs;Yw<@5?sK$NoB^T@A=jgb55f4D|Rs>BNJh8a?KC2 znAvYiraM;o4Ts3r)P*&(MsYBF;@#vZ!e*TnG1e-DV!bCnc4UhPy{lVQbE+z! zmf?oFb#NY3r6~byQbQMgnRbl&=_zTo;unvY-Zy^A6@Bza2y^8++3DC!0%u3+N3}2d zs!yDk8%-6DD@>B~g80|x`K-D1_*T_ zlvR%c+Lt9OndC3Gd&Q$)eA#Kf;ugVmQ)pS&5Lg(h1bI@pR~0?GQG}xWF>l|Mw6n91 z$wdL@)-}-2OmA{m_N#3Wy&6~9b>$nwQc-COd6yGSAGc`OTnGCz?IA}op9@>nADmXX zLlB&a5y>1VdbMc0*M0iA9Zp|=7w4vrjrC1m*Wr-xsK^GDD0UC-gM$`y1i677F0haGv|zKj&cONgKP(JKc^2Qs$JqM1 zYt)_}MEs-OTkm63>>6rs?}Y$%rp~3837nszi3MvQR`RNrt%a zTk}I&j&D-QL&CwEg>WvZ0H1ZO`klJH2$*3B=fGJzdh>P^kxxexuU8 z5~=;{C4iMl+d2i|#JV#hmkX_ppD0qAZJak}3H><}TG&0JF&5pKhRc<~WTJJTXa-(V zvK#%Y#b@AIrs1D}Fw6f+tBRea1!pv*-5~o{r%EZ-!D+Y*N{5ez>>DFPs}1`4?r8K; zn$kXy*z+NUq->DK^tMDZ_|Z5x^>(lQ&3+~5Y2rkJ zOvVnCM7!ugBv|l(C_m|;@@V&s^LeY1TI8jJ4eC4E`SYlIDuA@bWU96#;&?Qe5`%DS951i_ub!(|A~{JUIUzU8VPOU zM4mGr9?DTIZiMf7F}T4OW$b`^thh@wkM(|$5oT25viq$psG~QI(_v{!te1Ruoy)6A6?ZJ#8Md9 zllo>|vj#clv$G_rE@)3Ww@4^M;drB;WBb8AWA1~JL zmXhrBnumSo!dUD5(aYyWYX*P!|2;qd6Xin*2_pSZ`G7Z})bzo>v$)<`%%OE}zEhfp zI(@N$8jUkw=24-`nxEDl_rM|RDKsbOiO<_zQIJ|nO>PQ7Gv^<|*3n^Dh(nG~8zUh- zx!JI|BJl&MA%Vp$-&Ocf1-v2XiGnz4>z$Cj zukyG>X}Ak#U^`u(p2YIQgmqIbY<7)Nv~M~CUUC}ll+Ug-V{XX^fl)47~@xbC_iC_CFFFldgCS2W#@y zTZpQ-TxDT@D`^p?6$wS!nS&-QQk+H6k=p=#gM0KTU~8WxE%HM=Lqk8HW&qpze5mV= z2_e|25UkP6sGnVL#OzdJK>*N6(IjLO!) zQR9p1nU)Eq#)z9Mg)z_aDEEa4T;-(*xSFFelI*l;Yh>;#fJsCWcf5b3+wt5PqR8ra zVC4SpIX)3hYLl?~_$0v98i&?ZgFeN^gP_w*L2|0ZHesHYxWZ`WYG5_^zhz<>($`CJ zxo9dzNIs*XcY*X#^ur|Bl!@fP#n)U|{N(WhiD_uzU|!#Hta;N9su;u)UcM;miuq zopKt996U~;A=gSBq|E@$URYN4siF1Cm|>?pY~X*B)`6k+gBI(9 zxp?aUDg);<9e@S1#dSOc_~x0=X?SPjl#vWy2^ExPxPpt)5b$(wfP}g&r~jp#f)Jv@(OIXdY(h%bIg~($Hp$_hnod z`#w(ajj;ns=NwRW%NiSpvOUML{vK|R5VMXauD4B5+Da&kw|bUPk^my$Eu}RWa`14q zdhNW>)p4puT=(3eA!l(LEmm4{;jr;gkL0pP;kAd4Elek96>1-)aF?w}kdV;EZ8jmj z6b-wgTk#*xUyajY{^s1uhlBR3&x$*D+h;4hrrVZj_oI?E*u+2%~VKp&lTUV1B<=y)bQb^ju|5EIjfFME(PpV_6 z*NPuOAs*6YoujuL;Q2D2OH?^)>qR-k`q#SQJKOUBhEsT3KkfAQV$Ghr574})~=)0%O1 zUw)dWcsF%pq;$#vZ$H>Y0Nv$|8QeqH2v{H5#obmI!<15bQli0y21e?!Bt2*jb$!ef z`WpeJlg83cQxwfK>R1Z}%ucR(AMzRENWN8L@5@f@Sc_?YjT_P}I}xEA5_ryD#3jzE z5+B*ybJ)d*t*-;En|%=x(F;fZ5Ri0jPfYAniwGHrUubjVxR1`p1?=@(mdu?CSl~F2 zK3&l=OK<^F__BMva&;79W>_yv7@}fz*lGOgL)a3`I%O>VlueRwl#rr{zgt)3;SXaP zS?gq6dS+c+h^Y$dP_^Z6$D|Q)wgDZj+*Fgf_aAF;O{nXKskFZGPSdD?laKCq#4uUD z=w2@!M2iaSW*V*J7Mr-c>4R*wAT!(Cx_zXXt;*30R`2y^174xdgkZTxkh)ez_B?jT z0YbPCLmQ0#8(|6}`CayYt~wN=J(-=yD|dy7SjML78*~G*zNa6bq+ko|mihXmn?~rO z37;TOp?~wCC(+)meH{UUO=75gK0Z8;|75|pJs^KlF)XF}eIvRXHM>glH1Uoq_(4jJ z5?Y=wI&!|Q-_JhU>Hdm6sjKr&(A6|S#k5D78t)c3GWd1Z!IJ-noH=kT72i4LoJuCP z_bEgtVF+C})NZ^Ub6pT>1EC6|xBdyh(kbS+^fZn3-i=b%g^3LE`H%@J?jYqP!6fKQ z4LTI=9Ch;2Qf?9hPbh#YC$(RY;*TU+U=a;oE~Q<`goTo)Y`^6 z!tz@sHA8noA>>$06bezOuY{TqK`&I2847#<=a{EA&DrND2@MT=R&sv}H|8XXO7)U7 ztVrw75dbKjOrfPFBpPpbTYWJr`%Nh#apAmRyYO!9NAUxX1;?T}?HqVAo7$=<7w~&% zmyPxWYicZGI9Fr$unQNo%srX}1!?qW(`Y@yoj0fI#uCCjg$Xn(j(WM6t=yhul5Q4q zugVdGJTqpFjG|L#Nu)=#9>bhuF-3wW25E1P8|;>9oSJfy58gOh`7phkvPq^tjdeTq z1%%kSA`TJN*h90GeVBa0pfg>}#3Z9^2M8aQ%98EGBBjMgS zY=KYsi9n=-RpCldhlFPqp#GAz2gV*-YFesA*$x$wy5CDR&eAIOTuCsGt1{twQu_YB z?#XmxvvEkbm@@&I?*NDl^5iM7j)?;)benOkN|2V?>uvBHc_<(HD-$A*^BbY|*14l{ zYRbeX+UCRZaacu5jsv?_>^NF_Nizj69JwM?jT2{T^C-f(*Pbn|>Fa=5xa=Dl6`Thc zgA1y(zW8Q!0*GuIvX~d{920>??`kED`?liP^PcSZB}R60W*cdK?&pjm=JMavmv+Ri zOSe8%rm|S;NnAgp5)XaM(ocM3I}dbyE}reQJw%I?NXE5;)URVEkNT3Wr! z!GAfGHURHd;~Sxf5}9o5KWRO;tJWfn;(w9rH0>SZO2yXsEUsVc)Oe8U0f$AJ#m;^H z0k2Lr!?s%XOwU1={z)Oqm_n|jQMfWoMtPEcUn1hi!|&4Sz|L`R7n?_Ri;_v`?wov2sa7G+6u+vd?)>&V+8)8oRclOV zH>?MY)(fWQJEv-KDv6>+(v-^59++q~kk#_HdYXIfVV-!Cc_)K~k*%GQ*2ObqJdY>V zBcR6?&S3KfG()hz5q?)d4db!95{@QxD-N8bwAvbP&(V`3|9O+(C%|{E{$M(l^t6sv z5LSF3bB@C6;JB`1=^94|ed|YsCO3O93rgKBbdEJ68uq6wU`rnVL>yMqu4A6Aty;?& z8#h_5HDw4cMV1)-G1{bpL+13-6r$`^h4xd;@0kF5g@9KRL=P?|(ai##{4P zUFwc7R%I3pTqh#!vP{KL8Cr4=X9(lLFk)q|(+oB~>pLR6tTA$hTSn_X>t!yyh?BI@ z+)&YJr^)$&VFts zt5i!M7W`z`1 zXy^-g_mOo-UC)PqRO`Fy2p|-_+K_IA7nNT4$p7eIeUmSwa}VdB-GTZn#{2AJEms+wu9F(_4l9n zFFTl&N=c3V9`Y{SA9gr|wL4d7->;ZYwRnNh-g_Kd*ey0>mG*@x=kM_U&%z=b{Gc<<{UK zil(B<8_nIP$IT0*SMg$#h=qq8od}24r)PntN(nMsyg#1TomIV^S9uc}q*m5p|0d9; zuksT<;#Ik3mP|$^VVWjSyG!izMT4ME0~8HONsQ9_Ts0;Ysn4&?+O5*w5PS@v6pQ!% z(x^u$HnHy^TDD?))qSSE>whIHj~>_m(-!H! zCS>{T+ke~jruz7tzh*zNE#zzFCA&xe8M?z)f9F2cH}PlJ-mm>)^F#C=eciX!Wop99 zb?dZd#>s|VUiocdVrJ;7rQbv!W~_TIt#LTm^_+^1>e5@0o7S9iey9IqYt??0QW?L; zfk)n%(t&b&^!C=eE3a1m2Kznk>34BpK-TP!_~R8XxZ+XytUmQhY*fX^AJZ0he_g%t zV%fXfp(hvLzH?UY-edReJ1)L`m)4(OY*=RW9e4`;!J55x^G?P$FRiIJ_zq-1x96uzj+xif5t{QvZ|KTWl^BUl#<23YuwUjWPQD|Pp$NOx&6-Tckldr9$#Mkdv5LB z^Y4H{hTHD!|K6F^w`r@#no!=YcS6&av}Yc!X-z#mf5DqAG27nmsBd2JcSYa1q)oTt z_bfYIXgeu1^`q0LPgiS1wYst&PI{)?7jJAZsX%+n@=))+Qy*0<-@a$<$B_FEUTZ#n zJbBI1BY*j}P3K#`V4WV<{M%pe9!Pk3)M~0f-2Y)yyU(?L`7FJ#`t?8NV;6n%x~@33 z?JoQ4x9?S5yT_li^{ti77uNcwzOh~X@tB5};3av}_!(F3tm z6Su*U1-O`k{;2-dZT+wBfrX)wAOj;a3j+%?2O|qJq%34$Vipt$EMO5bGzON20-_2A z4vqmq%0?y=7Bno}04@Z9H=fxu{Q9`bZLNHS$?~=j7xykbFY4UZF#m-}OpQ-hlJw=5 z$FK3Ke~S~U?`gT{r7x^-{^d`@rBdIG7sl1h-{>-_wqDKt#`J~hnqUn;PmQuFxF$tsmq_3;ZIR5L&Kzpb&X>FEi7|5PrX!WY7ei``emEZgF` zbZ@|`1#f$`RM~&E2H%Uzs_mIPvGVv@;q+=(rq6R+xr8UYOS~%CS#N)r^A4BV@)s3y z2Yz1uc1pW#xm99D{vtjFiMb%WfREm zLfK7Sy?;9QIA$#Hojyf?J@mO%i`tn7=R(p{x44yGv~&6*&VO6V?HG@?gu$AGLmQj9 zsK@N>?OeDPS+!~a$R_uqxK^Z#1SC;v4}#{Lyh zN=8J&A^6WEUS55VPn1Z z=f#CykE)M4F;=WftUj-)xp7N?;8jmQ2itklxBtBOcG*$%$agPx?(>s1d_HJ~#0I|k z@b2Cr?M{|c&{VjdkH!7s~khySrQZsJ7dRr8S{X-IiPhrp=9WrQ@$gA8mc&-XeWU i!~RR0v`WszO>QTtMb)qVaZ@>^caoV0R_aIn|C<0%*jzXO literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/apidoc_info.jpeg b/doc/jbootadmin/features/apidoc_info.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..125dd3a9ec416d78c30597d6bb4399d37f64cdf1 GIT binary patch literal 51675 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 M7gZ;HmS3m-7pZ|Hx&QzG literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/apidoc_mock.jpeg b/doc/jbootadmin/features/apidoc_mock.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..cd9c032df9a695086c75066e1cbc67d2a54ce663 GIT binary patch literal 39419 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 literal 0 HcmV?d00001 diff --git a/doc/jbootadmin/features/demo_product.jpeg b/doc/jbootadmin/features/demo_product.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..adb998873fef6ddde251f200b4c445a6b7523d0d GIT binary patch literal 60013 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­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�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%0Dv z!UQ|HX@WhR3_ee?-v##l^?0qLTj2gD-Y8uPY0;YHRm&-@xNXlV7L>L;Y)5E`?1(tI za-($KM8;vP;VfTbLY{j6{l$lFhTwXC$ka6<<6Z&++Ics`>RKy+!H@;qPtdf zoK&yhE3OD4q+Tc>OJFW}zM{O>J70UsgK8Tiz_?ZyR4&i~Bc%p#H`0L!$8GMCA2G2J bq}xAiz` Date: Thu, 20 Aug 2020 17:35:29 +0800 Subject: [PATCH 0477/1965] update docs and vuepress configs --- .gitignore | 1 - doc/package.json | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 doc/package.json diff --git a/.gitignore b/.gitignore index ae405371..53f516c9 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,5 @@ logs *.patch ~$* node_modules -package.json yarn.lock dist \ No newline at end of file diff --git a/doc/package.json b/doc/package.json new file mode 100644 index 00000000..30312c19 --- /dev/null +++ b/doc/package.json @@ -0,0 +1,9 @@ +{ + "dependencies": { + "@vuepress-reco/vuepress-plugin-screenfull": "^1.0.1", + "@vuepress-reco/vuepress-plugin-back-to-top": "^1.5.5", + "vuepress-theme-reco": "^1.5.5" + }, + "devDependencies": { + } +} -- Gitee From adbbda72acbda164bdea2bee0a36fcd16a87ba3b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Thu, 20 Aug 2020 20:25:56 +0800 Subject: [PATCH 0478/1965] update docs and vuepress configs --- .gitignore | 3 +- doc/.vuepress/config.js | 173 ++++++++++++++++++++-------------------- doc/deploy.sh | 6 +- doc/package.json | 7 +- 4 files changed, 94 insertions(+), 95 deletions(-) diff --git a/.gitignore b/.gitignore index 53f516c9..f22055dd 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ logs ~$* node_modules yarn.lock -dist \ No newline at end of file +dist +package-lock.json diff --git a/doc/.vuepress/config.js b/doc/.vuepress/config.js index 7bb9793c..1af646cc 100644 --- a/doc/.vuepress/config.js +++ b/doc/.vuepress/config.js @@ -2,104 +2,101 @@ // https://github.com/vuejs/vuepress/blob/master/packages/docs/docs/.vuepress/config.js // https://vuepress-theme-reco.recoluan.com/views/1.x/ module.exports = { - theme: 'reco', - themeConfig: { - //腾讯 404 公益配置 - noFoundPageByTencent: false, + theme: 'vuepress-theme-reco', + themeConfig: { + //腾讯 404 公益配置 + noFoundPageByTencent: false, - mode: 'light', // 默认 auto,auto 跟随系统,dark 暗色模式,light 亮色模式 - modePicker: false, // 默认 true,false 不显示模式调节按钮,true 则显示 + mode: 'light', // 默认 auto,auto 跟随系统,dark 暗色模式,light 亮色模式 + modePicker: false, // 默认 true,false 不显示模式调节按钮,true 则显示 - // author - author: 'jboot', + // author + author: 'jboot', - // if your docs are in a different repo from your main project: - docsRepo: 'yangfuhai/jboot', - // if your docs are in a specific branch (defaults to 'master'): - docsBranch: 'master', - // if your docs are not at the root of the repo: - docsDir: 'doc', - // defaults to false, set to true to enable - editLinks: true, - // custom text for edit link. Defaults to "Edit this page" - editLinkText: '编辑此页面', + // if your docs are in a different repo from your main project: + docsRepo: 'yangfuhai/jboot', + // if your docs are in a specific branch (defaults to 'master'): + docsBranch: 'master', + // if your docs are not at the root of the repo: + docsDir: 'doc', + // defaults to false, set to true to enable + editLinks: true, + // custom text for edit link. Defaults to "Edit this page" + editLinkText: '编辑此页面', - lastUpdated: '更新时间', // string | boolean + lastUpdated: '更新时间', // string | boolean - nav: [ - {text: '首页', link: '/'}, - {text: '提问', link: 'https://gitee.com/JPressProjects/jpress/issues'}, - {text: 'JPress', link: 'http://www.jpress.io'}, - { - text: '源码下载', items: [ - {text: 'Gitee', link: 'https://gitee.com/JbootProjects/jboot'}, - {text: 'Github', link: 'https://github.com/yangfuhai/jboot'} - ] - }, - ], - - sidebar: { - '/': [{ - title: '认识 Jboot', - collapsable: false, - children: [ - {title: 'Jboot 简介', path: '/'}, - {title: '快速开始', path: '/docs/start'} - ], - }, + nav: [ + {text: '首页', link: '/'}, + {text: '提问', link: 'https://gitee.com/JPressProjects/jpress/issues'}, + {text: 'JPress', link: 'http://www.jpress.io'}, + { + text: '源码下载', items: [ + {text: 'Gitee', link: 'https://gitee.com/JbootProjects/jboot'}, + {text: 'Github', link: 'https://github.com/yangfuhai/jboot'} + ] + }, + ], - { - title: '开发文档', - collapsable: false, - children: [ - {title: '安装', path: '/docs/install'}, - {title: '配置', path: '/docs/config'}, - {title: 'MVC', path: '/docs/mvc'}, - {title: 'WebSocket', path: '/docs/websocket'}, - {title: 'Jwt', path: '/docs/jwt'}, - {title: 'Swagger', path: '/docs/swagger'}, - {title: 'Aop', path: '/docs/aop'}, - {title: '数据库', path: '/docs/db'}, - {title: '缓存', path: '/docs/cache'}, - {title: 'Redis', path: '/docs/redis'}, - {title: 'RPC 调用', path: '/docs/rpc'}, - {title: 'MQ 消息队列', path: '/docs/mq'}, - {title: 'Gateway 网关', path: '/docs/gateway'}, - {title: '任务调度', path: '/docs/schedule'}, - {title: '限流', path: '/docs/limit'}, - {title: '监控', path: '/docs/metrics'}, - {title: '事件机制', path: '/docs/event'}, - {title: '序列化', path: '/docs/serialize'}, - {title: 'SPI扩展', path: '/docs/spi'}, - {title: '代码生成器', path: '/docs/codegen'}, - {title: '项目构建', path: '/docs/build'}, - {title: '项目部署', path: '/docs/deploy'}, - {title: 'Docker', path: '/docs/docker'}, - ], - }, + sidebar: { + '/': [{ + title: '认识 Jboot', + collapsable: false, + children: [ + {title: 'Jboot 简介', path: '/'}, + {title: '快速开始', path: '/docs/start'} + ], + }, - { - title: '性能', - collapsable: false, - children: [ - {title: '性能测试', path: '/docs/benchmark'}, - ], - } - ], + { + title: '开发文档', + collapsable: false, + children: [ + {title: '安装', path: '/docs/install'}, + {title: '配置', path: '/docs/config'}, + {title: 'MVC', path: '/docs/mvc'}, + {title: 'WebSocket', path: '/docs/websocket'}, + {title: 'Jwt', path: '/docs/jwt'}, + {title: 'Swagger', path: '/docs/swagger'}, + {title: 'Aop', path: '/docs/aop'}, + {title: '数据库', path: '/docs/db'}, + {title: '缓存', path: '/docs/cache'}, + {title: 'Redis', path: '/docs/redis'}, + {title: 'RPC 调用', path: '/docs/rpc'}, + {title: 'MQ 消息队列', path: '/docs/mq'}, + {title: 'Gateway 网关', path: '/docs/gateway'}, + {title: '任务调度', path: '/docs/schedule'}, + {title: '限流', path: '/docs/limit'}, + {title: '监控', path: '/docs/metrics'}, + {title: '事件机制', path: '/docs/event'}, + {title: '序列化', path: '/docs/serialize'}, + {title: 'SPI扩展', path: '/docs/spi'}, + {title: '代码生成器', path: '/docs/codegen'}, + {title: '项目构建', path: '/docs/build'}, + {title: '项目部署', path: '/docs/deploy'}, + {title: 'Docker', path: '/docs/docker'}, + ], }, - sidebarDepth: 3 - + { + title: '性能', + collapsable: false, + children: [ + {title: '性能测试', path: '/docs/benchmark'}, + ], + } + ], }, - // "@vuepress-reco/vuepress-plugin-screenfull": "^1.0.1", - // "@vuepress-reco/vuepress-plugin-back-to-top": "^1.5.5", - plugins: ['@vuepress-reco/vuepress-plugin-back-to-top','@vuepress-reco/vuepress-plugin-screenfull'], - title: 'Jboot 官方网站', - description: 'Jboot 一个开源的分布式、商业级微服务框架。', - head: [ - ['link', {rel: 'icon', href: '/logo.png'}], - ['script', {}, ` + sidebarDepth: 1 + + + }, + title: 'Jboot 官方网站', + description: 'Jboot 一个开源的分布式、商业级微服务框架。', + head: [ + ['link', {rel: 'icon', href: '/logo.png'}], + ['script', {}, ` var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); @@ -108,5 +105,5 @@ module.exports = { s.parentNode.insertBefore(hm, s); })(); `] - ] + ] } diff --git a/doc/deploy.sh b/doc/deploy.sh index 894ae8da..a308b2fe 100755 --- a/doc/deploy.sh +++ b/doc/deploy.sh @@ -3,8 +3,12 @@ # abort on errors set -e +cd .. + # build -vuepress build . +vuepress build doc + +cd doc cp CNAME .vuepress/dist diff --git a/doc/package.json b/doc/package.json index 30312c19..40fe29da 100644 --- a/doc/package.json +++ b/doc/package.json @@ -1,9 +1,6 @@ { - "dependencies": { - "@vuepress-reco/vuepress-plugin-screenfull": "^1.0.1", - "@vuepress-reco/vuepress-plugin-back-to-top": "^1.5.5", - "vuepress-theme-reco": "^1.5.5" - }, + "dependencies": {}, "devDependencies": { + "vuepress-theme-reco": "1.5.4" } } -- Gitee From 1d66043ec61e47d9141fe79ee32053889c5282ab Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 21 Aug 2020 13:08:50 +0800 Subject: [PATCH 0479/1965] update docs and vuepress configs --- doc/deploy.sh | 5 +---- doc/package.json | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/deploy.sh b/doc/deploy.sh index a308b2fe..6dac71e1 100755 --- a/doc/deploy.sh +++ b/doc/deploy.sh @@ -3,12 +3,9 @@ # abort on errors set -e -cd .. - # build -vuepress build doc +vuepress build . -cd doc cp CNAME .vuepress/dist diff --git a/doc/package.json b/doc/package.json index 40fe29da..1d4e1db5 100644 --- a/doc/package.json +++ b/doc/package.json @@ -1,6 +1,10 @@ { "dependencies": {}, "devDependencies": { + "vuepress": "^1.5.3", + "chalk": "^2.4.2", + "commander": "^2.20.3", + "esm": "^3.2.25", "vuepress-theme-reco": "1.5.4" } } -- Gitee From ac7012713d916c180216b259361c4493afd298ad Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Fri, 21 Aug 2020 18:49:00 +0800 Subject: [PATCH 0480/1965] JbootHttpImpl remove default header attrs: Content-Type and User-Agent --- .../java/io/jboot/components/http/jboot/JbootHttpImpl.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java index 9f7cc06e..c9da94ac 100644 --- a/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java +++ b/src/main/java/io/jboot/components/http/jboot/JbootHttpImpl.java @@ -193,9 +193,6 @@ public class JbootHttpImpl implements JbootHttp { connection.setConnectTimeout(request.getConnectTimeOut()); connection.setRequestMethod(request.getMethod()); - - connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36"); if (request.getHeaders() != null && request.getHeaders().size() > 0) { for (Map.Entry entry : request.getHeaders().entrySet()) { connection.setRequestProperty(entry.getKey(), entry.getValue()); -- Gitee From 145065dc2856d53f0eb508943f10f4101b46d17b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 24 Aug 2020 16:25:06 +0800 Subject: [PATCH 0481/1965] fixed: the config 'jboot.datasource.validationQuery' can not work --- .../io/jboot/db/datasource/DataSourceConfig.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java index ba15d55f..e534763d 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java @@ -358,13 +358,20 @@ public class DataSourceConfig { } public String getValidationQuery() { - if(this.url.startsWith("jdbc:oracle")){ + if(validationQuery != null){ + return validationQuery; + } + if (this.url == null){ + return null; + } + String url = this.url.toLowerCase(); + if(url.startsWith("jdbc:oracle")){ return "select 1 from dual"; - }else if(this.url.startsWith("jdbc:db2")){ + }else if(url.startsWith("jdbc:db2")){ return "select 1 from sysibm.sysdummy1"; - }else if(this.url.startsWith("jdbc:hsqldb")){ + }else if(url.startsWith("jdbc:hsqldb")){ return "select 1 from INFORMATION_SCHEMA.SYSTEM_USERS"; - }else if(this.url.startsWith("jdbc:derby")){ + }else if(url.startsWith("jdbc:derby")){ return "select 1 from INFORMATION_SCHEMA.SYSTEM_USERS"; } return "select 1"; -- Gitee From a9213dead065d2aff040b103c4e8fdf92d27c7bd Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Mon, 24 Aug 2020 16:28:15 +0800 Subject: [PATCH 0482/1965] fixed: the config 'jboot.datasource.validationQuery' can not work --- .../io/jboot/db/datasource/DataSourceConfig.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java index e534763d..b380dd8d 100644 --- a/src/main/java/io/jboot/db/datasource/DataSourceConfig.java +++ b/src/main/java/io/jboot/db/datasource/DataSourceConfig.java @@ -55,7 +55,7 @@ public class DataSourceConfig { private long minEvictableIdleTimeMillis = 1000L * 60L * 30L; // 配置发生错误时多久重连 private long timeBetweenConnectErrorMillis = 500; - private String validationQuery = "select 1"; + private String validationQuery; private boolean testWhileIdle = true; private boolean testOnBorrow = false; private boolean testOnReturn = false; @@ -358,20 +358,20 @@ public class DataSourceConfig { } public String getValidationQuery() { - if(validationQuery != null){ + if (validationQuery != null) { return validationQuery; } - if (this.url == null){ + if (this.url == null) { return null; } String url = this.url.toLowerCase(); - if(url.startsWith("jdbc:oracle")){ + if (url.startsWith("jdbc:oracle")) { return "select 1 from dual"; - }else if(url.startsWith("jdbc:db2")){ + } else if (url.startsWith("jdbc:db2")) { return "select 1 from sysibm.sysdummy1"; - }else if(url.startsWith("jdbc:hsqldb")){ + } else if (url.startsWith("jdbc:hsqldb")) { return "select 1 from INFORMATION_SCHEMA.SYSTEM_USERS"; - }else if(url.startsWith("jdbc:derby")){ + } else if (url.startsWith("jdbc:derby")) { return "select 1 from INFORMATION_SCHEMA.SYSTEM_USERS"; } return "select 1"; -- Gitee From 96502fffb0c5e9c1580e51c5614e59d11f20646b Mon Sep 17 00:00:00 2001 From: yangfuhai Date: Tue, 25 Aug 2020 16:42:59 +0800 Subject: [PATCH 0483/1965] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20gateway=20?= =?UTF-8?q?=E9=97=A8=E6=88=B7=E7=BD=91=E5=85=B3=E7=9A=84=E8=B4=9F=E8=BD=BD?= =?UTF-8?q?=E5=9D=87=E8=A1=A1=E7=AD=96=E7=95=A5=E9=85=8D=E7=BD=AE=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/GatewayLoadBalanceStrategy.java | 42 ++++++++++++++++++ .../jboot/components/gateway/GatewayUtil.java | 7 ++- .../gateway/JbootGatewayConfig.java | 44 ++++++++++++++----- 3 files changed, 78 insertions(+), 15 deletions(-) create mode 100644 src/main/java/io/jboot/components/gateway/GatewayLoadBalanceStrategy.java diff --git a/src/main/java/io/jboot/components/gateway/GatewayLoadBalanceStrategy.java b/src/main/java/io/jboot/components/gateway/GatewayLoadBalanceStrategy.java new file mode 100644 index 00000000..411e5c35 --- /dev/null +++ b/src/main/java/io/jboot/components/gateway/GatewayLoadBalanceStrategy.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2015-2020, Michael Yang 杨福海 (fuhai999@gmail.com). + *