From ddc80f8e3e5a79e456baa4abbd33db8c32c1be31 Mon Sep 17 00:00:00 2001 From: fuzi1996 Date: Tue, 8 Mar 2022 18:53:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=8F=92=E4=BB=B6=E6=94=AF=E6=8C=81spi?= =?UTF-8?q?=E6=96=B9=E5=BC=8F=E5=8A=A0=E8=BD=BD,ui=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E9=80=89=E6=8B=A9,=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E8=84=9A=E6=9C=ACprofile=E5=AF=B9=E5=A4=96=E6=8A=9B=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dbapi-assembly/bin/dbapi-daemon.sh | 8 +- dbapi-assembly/bin/dbapi.sh | 13 +-- dbapi-assembly/docs/plugin development.md | 87 +++++++++++++++-- .../controller/DataSourceController.java | 10 +- .../basic/controller/PluginController.java | 32 +++++++ .../dbapi/plugin/PluginManager.java | 95 ++++++++++++++++++- .../basic/event/LoadPluginOnSpringReady.java | 14 +++ .../dbapi/basic/service/PluginService.java | 29 ++++++ dbapi-ui/src/components/api/common.vue | 44 +++++++-- dbapi-ui/src/components/common/MySelect.vue | 5 +- 10 files changed, 299 insertions(+), 38 deletions(-) create mode 100644 dbapi-controller/src/main/java/com/gitee/freakchicken/dbapi/basic/controller/PluginController.java create mode 100644 dbapi-service/src/main/java/com/gitee/freakchicken/dbapi/basic/event/LoadPluginOnSpringReady.java create mode 100644 dbapi-service/src/main/java/com/gitee/freakchicken/dbapi/basic/service/PluginService.java diff --git a/dbapi-assembly/bin/dbapi-daemon.sh b/dbapi-assembly/bin/dbapi-daemon.sh index c020052..8bac534 100644 --- a/dbapi-assembly/bin/dbapi-daemon.sh +++ b/dbapi-assembly/bin/dbapi-daemon.sh @@ -69,7 +69,7 @@ if [ "$command" = "standalone" ]; then generate_classpath CLASS=com.gitee.freakchicken.dbapi.DBApiStandalone HEAP_OPTS="-Xms4g -Xmx4g -Xmn2g" - PROFILES="-Dspring.profiles.active=standalone" + PROFILES="-Dspring.profiles.active=standalone $PROFILES" export DBAPI_OPTS="$HEAP_OPTS $DBAPI_OPTS" elif [ "$command" = "manager" ]; then @@ -77,7 +77,7 @@ elif [ "$command" = "manager" ]; then generate_classpath CLASS=com.gitee.freakchicken.dbapi.manager.DBApiManager HEAP_OPTS="-Xms1g -Xmx1g -Xmn512m" - PROFILES="-Dspring.profiles.active=manager" + PROFILES="-Dspring.profiles.active=manager $PROFILES" export DBAPI_OPTS="$HEAP_OPTS $DBAPI_OPTS" elif [ "$command" = "apiServer" ]; then @@ -85,7 +85,7 @@ elif [ "$command" = "apiServer" ]; then generate_classpath CLASS=com.gitee.freakchicken.dbapi.apiserver.DBApiApiServer HEAP_OPTS="-Xms4g -Xmx4g -Xmn2g" - PROFILES="-Dspring.profiles.active=apiServer" + PROFILES="-Dspring.profiles.active=apiServer $PROFILES" export DBAPI_OPTS="$HEAP_OPTS $DBAPI_OPTS" elif [ "$command" = "gateway" ]; then @@ -93,7 +93,7 @@ elif [ "$command" = "gateway" ]; then generate_classpath CLASS=com.gitee.freakchicken.dbapi.gateway.DBApiGateWay HEAP_OPTS="-Xms4g -Xmx4g -Xmn2g" - PROFILES="-Dspring.profiles.active=gateway -Dreactor.netty.http.server.accessLogEnabled=true " + PROFILES="-Dspring.profiles.active=gateway -Dreactor.netty.http.server.accessLogEnabled=true $PROFILES" export DBAPI_OPTS="$HEAP_OPTS $DBAPI_OPTS" else diff --git a/dbapi-assembly/bin/dbapi.sh b/dbapi-assembly/bin/dbapi.sh index 8c0b4a9..14f429d 100644 --- a/dbapi-assembly/bin/dbapi.sh +++ b/dbapi-assembly/bin/dbapi.sh @@ -69,7 +69,7 @@ if [ "$command" = "standalone" ]; then generate_classpath CLASS=com.gitee.freakchicken.dbapi.DBApiStandalone HEAP_OPTS="-Xms4g -Xmx4g -Xmn2g" - PROFILES="-Dspring.profiles.active=standalone" + PROFILES="-Dspring.profiles.active=standalone $PROFILES" export DBAPI_OPTS="$HEAP_OPTS $DBAPI_OPTS" elif [ "$command" = "manager" ]; then @@ -77,7 +77,7 @@ elif [ "$command" = "manager" ]; then generate_classpath CLASS=com.gitee.freakchicken.dbapi.manager.DBApiManager HEAP_OPTS="-Xms1g -Xmx1g -Xmn512m" - PROFILES="-Dspring.profiles.active=manager" + PROFILES="-Dspring.profiles.active=manager $PROFILES" export DBAPI_OPTS="$HEAP_OPTS $DBAPI_OPTS" elif [ "$command" = "apiServer" ]; then @@ -85,7 +85,7 @@ elif [ "$command" = "apiServer" ]; then generate_classpath CLASS=com.gitee.freakchicken.dbapi.apiserver.DBApiApiServer HEAP_OPTS="-Xms4g -Xmx4g -Xmn2g" - PROFILES="-Dspring.profiles.active=apiServer" + PROFILES="-Dspring.profiles.active=apiServer $PROFILES" export DBAPI_OPTS="$HEAP_OPTS $DBAPI_OPTS" elif [ "$command" = "gateway" ]; then @@ -93,7 +93,7 @@ elif [ "$command" = "gateway" ]; then generate_classpath CLASS=com.gitee.freakchicken.dbapi.gateway.DBApiGateWay HEAP_OPTS="-Xms4g -Xmx4g -Xmn2g" - PROFILES="-Dspring.profiles.active=gateway -Dreactor.netty.http.server.accessLogEnabled=true " + PROFILES="-Dspring.profiles.active=gateway -Dreactor.netty.http.server.accessLogEnabled=true $PROFILES" export DBAPI_OPTS="$HEAP_OPTS $DBAPI_OPTS" else @@ -107,7 +107,7 @@ case $startStop in echo start $command in docker export DBAPI_OPTS="$DBAPI_OPTS -XX:-UseContainerSupport" exec_command="$PROFILES -classpath $cp $CLASS" -# echo $exec_command + # echo $exec_command java $exec_command else [ -w "$DBAPI_PID_DIR" ] || mkdir -p "$DBAPI_PID_DIR" @@ -121,7 +121,8 @@ case $startStop in echo starting $command exec_command="$PROFILES -classpath $cp $CLASS" - # echo "nohup java $exec_command > $log 2>&1 &" + # echo "nohup java $exec_command > $log 2>&1 &" + # echo "java $exec_command" java $exec_command fi diff --git a/dbapi-assembly/docs/plugin development.md b/dbapi-assembly/docs/plugin development.md index 72dc269..01ba954 100644 --- a/dbapi-assembly/docs/plugin development.md +++ b/dbapi-assembly/docs/plugin development.md @@ -30,13 +30,29 @@ ### 2.2 缓存插件开发 - 新建java类实现`com.gitee.freakchicken.dbapi.plugin.CachePlugin` + ```java import com.gitee.freakchicken.dbapi.common.ApiConfig; import com.gitee.freakchicken.dbapi.plugin.CachePlugin; +import com.gitee.freakchicken.dbapi.plugin.PluginManager; import java.util.Map; public class Cdemo extends CachePlugin { + + /** + * 在静态代码中使用`PluginManager.registCachePlugin`注册插件 + */ + static { + PluginManager.registCachePlugin(new Cdemo()); + } + + /** + * 必须提供无参构造方法 + */ + public Cdemo() { + } + /** * 插件初始化方法,实例化插件的时候执行,永远只会执行一次, * 一般是用来创建连接池 @@ -96,21 +112,35 @@ public class Cdemo extends CachePlugin { ```java import com.alibaba.fastjson.JSONObject; import com.gitee.freakchicken.dbapi.common.ApiConfig; +import com.gitee.freakchicken.dbapi.plugin.PluginManager; import com.gitee.freakchicken.dbapi.plugin.TransformPlugin; import java.util.List; public class Tdemo extends TransformPlugin { - - /** - * 数据转换逻辑 - * @param data sql查询结果 - * @param params 缓存插件局部参数 - * @return - */ - public abstract Object transform(List data, String params){ - return null; + + /** + * 在静态代码中使用`PluginManager.registTransformPlugin`注册插件 + */ + static { + PluginManager.registTransformPlugin(new Tdemo()); } + + /** + * 必须提供无参构造方法 + */ + public Tdemo() { + } + + /** + * 数据转换逻辑 + * @param data sql查询结果 + * @param params 缓存插件局部参数 + * @return + */ + public abstract Object transform(List data, String params) { + return null; + } } ``` - 数据转换逻辑写在`transform`方法里,第一个参数就是sql查询结果,第二个参数是插件局部参数 @@ -159,7 +189,44 @@ public abstract Object transform(List data, String params){ ``` ![](https://freakchicken.gitee.io/images/dbApi/20211016/plugin_param.png) -### 2.6 插件使用 +### 2.6 插件注册 +提供两种插件注册方式 +- SPI注册 + - 在工程的resources中新建`META-INF/service`目录 + - 插件注册 + - 如果注册缓存插件,在`META-INF/service`目录下 + 新建一个名为`com.gitee.freakchicken.dbapi.plugin.CachePlugin`的**文件**,然后在文件中写入要注册插件的`类全限定名`,例如: + ```txt + com.gitee.freakchicken.demo.plugin.RedisCachePlugin + ``` + 如果有多个插件,换行填入 + + - 如果注册格式转化插件,在`META-INF/service`目录下 + 新建一个名为`com.gitee.freakchicken.dbapi.plugin.TransformPlugin`的**文件**,然后在文件中写入要注册插件的`类全限定名`,例如: + ```txt + com.gitee.freakchicken.demo.plugin.EncryptTransformerPlugin + ``` + 如果有多个插件,换行填入 + - 打包,确保jar包目录结构如下所示 + ![jar包目录结构](https://fuzi1996.pages.dev/assets/pictbed/dbapi-plugin-demo.png) + +- 环境变量注册 + - 在程序启动时,使用环境变量注入插件的`类全限定名` + - 插件注册 + - 如果注册缓存插件 + ```bash + export PROFILES="-Dcom.gitee.freakchicken.dbapi.plugin.CachePlugin=com.gitee.freakchicken.demo.plugin.RedisCachePlugin" + ``` + 如果是多个值,中间用":"隔开 + + - 如果注册格式转化插件 + ```bash + export PROFILES="-Dcom.gitee.freakchicken.dbapi.plugin.TransformPlugin=com.gitee.freakchicken.demo.plugin.EncryptTransformerPlugin" + ``` + 如果是多个值,中间用":"隔开 + + +### 2.7 插件使用 - 用户开发完插件后,请打包,将最后生成的jar包和插件依赖的jar包拷贝进DBApi的`lib`目录下, 再重启DBApi服务,就可以使用插件了 - 如果插件中使用了全局参数,还需要在`conf/plugin.properties`文件添加相应配置 diff --git a/dbapi-controller/src/main/java/com/gitee/freakchicken/dbapi/basic/controller/DataSourceController.java b/dbapi-controller/src/main/java/com/gitee/freakchicken/dbapi/basic/controller/DataSourceController.java index 30f3aeb..60da11e 100644 --- a/dbapi-controller/src/main/java/com/gitee/freakchicken/dbapi/basic/controller/DataSourceController.java +++ b/dbapi-controller/src/main/java/com/gitee/freakchicken/dbapi/basic/controller/DataSourceController.java @@ -22,12 +22,7 @@ import java.sql.SQLException; import java.util.Arrays; import java.util.List; -/** - * @program: dbApi - * @description: - * @author: jiangqiang - * @create: 2021-01-20 10:43 - **/ + @Slf4j @RestController @RequestMapping("/datasource") @@ -97,8 +92,9 @@ public class DataSourceController { e.printStackTrace(); } finally { try { - if (os != null) + if (os != null) { os.close(); + } } catch (IOException e) { e.printStackTrace(); } diff --git a/dbapi-controller/src/main/java/com/gitee/freakchicken/dbapi/basic/controller/PluginController.java b/dbapi-controller/src/main/java/com/gitee/freakchicken/dbapi/basic/controller/PluginController.java new file mode 100644 index 0000000..5734b6a --- /dev/null +++ b/dbapi-controller/src/main/java/com/gitee/freakchicken/dbapi/basic/controller/PluginController.java @@ -0,0 +1,32 @@ +package com.gitee.freakchicken.dbapi.basic.controller; + +import com.gitee.freakchicken.dbapi.basic.domain.DataSource; +import com.gitee.freakchicken.dbapi.basic.service.PluginService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; + +/** + * @program: dbApi + * @description: 插件接口 + * @author: kensan + * @create: 2022-03-09 21:12 + */ +@Slf4j +@RestController +@RequestMapping("/plugin") +public class PluginController { + + @Autowired + private PluginService pluginService; + + @RequestMapping("/listAll") + public Map> listAllPlugin() { + return pluginService.getAllPlugin(); + } +} diff --git a/dbapi-plugin/src/main/java/com/gitee/freakchicken/dbapi/plugin/PluginManager.java b/dbapi-plugin/src/main/java/com/gitee/freakchicken/dbapi/plugin/PluginManager.java index bdcd55b..f720898 100644 --- a/dbapi-plugin/src/main/java/com/gitee/freakchicken/dbapi/plugin/PluginManager.java +++ b/dbapi-plugin/src/main/java/com/gitee/freakchicken/dbapi/plugin/PluginManager.java @@ -2,8 +2,11 @@ package com.gitee.freakchicken.dbapi.plugin; import lombok.extern.slf4j.Slf4j; -import java.util.Map; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; @Slf4j public class PluginManager { @@ -11,6 +14,88 @@ public class PluginManager { private static Map cachePlugins = new ConcurrentHashMap<>(); private static Map transformPlugins = new ConcurrentHashMap<>(); + public static List getAllCachePlugin(){ + return new ArrayList<>(cachePlugins.keySet()); + } + + public static List getAllTransformPlugin(){ + return new ArrayList<>(transformPlugins.keySet()); + } + + /** + * 使用spi自动加载插件 + */ + public static void loadPlugins(){ + loadInitialPlugin("com.gitee.freakchicken.dbapi.plugin.CachePlugin",CachePlugin.class); + loadInitialPlugin("com.gitee.freakchicken.dbapi.plugin.TransformPlugin",TransformPlugin.class); + } + + private static void loadInitialPlugin(String property,Class clazz) { + String plugins; + try { + plugins = AccessController.doPrivileged((PrivilegedAction) () -> System.getProperty(property)); + log.info("property {} get value {}",property,plugins); + } catch (Exception ex) { + plugins = null; + } + // If the driver is packaged as a Service Provider, load it. + // Get all the drivers through the classloader + // exposed as a Plugin.class service. + // ServiceLoader.load() replaces the sun.misc.Providers() + + AccessController.doPrivileged((PrivilegedAction) () -> { + + ServiceLoader loadedPlugins = ServiceLoader.load(clazz); + Iterator pluginsIterator = loadedPlugins.iterator(); + + /* Load these drivers, so that they can be instantiated. + * It may be the case that the driver class may not be there + * i.e. there may be a packaged driver with the service class + * as implementation of Plugin but the actual class + * may be missing. In that case a java.util.ServiceConfigurationError + * will be thrown at runtime by the VM trying to locate + * and load the service. + * + * Adding a try catch block to catch those runtime errors + * if driver not available in classpath but it's + * packaged as service and that service is there in classpath. + */ + try{ + while(pluginsIterator.hasNext()) { + pluginsIterator.next(); + } + } catch(Throwable t) { + // Do nothing + } + return null; + }); + + log.info("{} initialize: {}",clazz.getCanonicalName(),plugins); + + if (plugins == null || plugins.equals("")) { + return; + } + String[] pluginsList = plugins.split(":"); + log.info("number of plugins: {}",pluginsList.length); + for (String aPlugin : pluginsList) { + try { + log.info("{} Initialize: {} loading ",clazz.getCanonicalName(),aPlugin); + Class.forName(aPlugin, true, + ClassLoader.getSystemClassLoader()); + } catch (Exception ex) { + log.warn("{} Initialize: {} load failed",clazz.getCanonicalName(),aPlugin,ex); + } + } + } + + public static void registCachePlugin(CachePlugin cachePlugin){ + if(null == cachePlugin){ + throw new IllegalArgumentException("can't regist a null transform plugin"); + } + Class aClass = cachePlugin.getClass(); + cachePlugins.put(aClass.getCanonicalName(),cachePlugin); + } + public static CachePlugin getCachePlugin(String className) { if (cachePlugins.containsKey(className)) { return cachePlugins.get(className); @@ -28,6 +113,14 @@ public class PluginManager { } } + public static void registTransformPlugin(TransformPlugin transformPlugin){ + if(null == transformPlugin){ + throw new IllegalArgumentException("can't regist a null transform plugin"); + } + Class aClass = transformPlugin.getClass(); + transformPlugins.put(aClass.getCanonicalName(),transformPlugin); + } + public static TransformPlugin getTransformPlugin(String className) { if (transformPlugins.containsKey(className)) { return transformPlugins.get(className); diff --git a/dbapi-service/src/main/java/com/gitee/freakchicken/dbapi/basic/event/LoadPluginOnSpringReady.java b/dbapi-service/src/main/java/com/gitee/freakchicken/dbapi/basic/event/LoadPluginOnSpringReady.java new file mode 100644 index 0000000..9d93703 --- /dev/null +++ b/dbapi-service/src/main/java/com/gitee/freakchicken/dbapi/basic/event/LoadPluginOnSpringReady.java @@ -0,0 +1,14 @@ +package com.gitee.freakchicken.dbapi.basic.event; + +import com.gitee.freakchicken.dbapi.plugin.PluginManager; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +@Component +public class LoadPluginOnSpringReady { + @EventListener + public void loadPlugins(ApplicationReadyEvent event){ + PluginManager.loadPlugins(); + } +} diff --git a/dbapi-service/src/main/java/com/gitee/freakchicken/dbapi/basic/service/PluginService.java b/dbapi-service/src/main/java/com/gitee/freakchicken/dbapi/basic/service/PluginService.java new file mode 100644 index 0000000..50560d2 --- /dev/null +++ b/dbapi-service/src/main/java/com/gitee/freakchicken/dbapi/basic/service/PluginService.java @@ -0,0 +1,29 @@ +package com.gitee.freakchicken.dbapi.basic.service; + +import com.gitee.freakchicken.dbapi.plugin.PluginManager; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @description: + * @program: dbApi + * @author: kensan + * @create: 2022-03-09 21:19 + */ +@Slf4j +@Service +public class PluginService { + + public Map> getAllPlugin(){ + Map> allPlugin = new HashMap<>(2); + allPlugin.put("cachePlugin",PluginManager.getAllCachePlugin()); + allPlugin.put("transformPlugin",PluginManager.getAllTransformPlugin()); + return allPlugin; + } + +} diff --git a/dbapi-ui/src/components/api/common.vue b/dbapi-ui/src/components/api/common.vue index 9510b10..a98124a 100644 --- a/dbapi-ui/src/components/api/common.vue +++ b/dbapi-ui/src/components/api/common.vue @@ -64,10 +64,10 @@ -
+
sql-{{ item.id }} : - +
@@ -83,8 +83,8 @@ - + 填写“插件类名”表示对结果数据开启缓存,不填写表示不开启缓存 @@ -148,12 +148,29 @@ export default { isFullScreen: false, mode: 'mini', contentType:'application/x-www-form-urlencoded', - types:['application/x-www-form-urlencoded','application/json'] + types:['application/x-www-form-urlencoded','application/json'], + cachePlugin:[], + transformPlugin:[] } }, props: ["id"], + computed:{ + cachePluginOptions(){ + return this.getPluginOptions(this.cachePlugin) + }, + transformPluginOptions(){ + return this.getPluginOptions(this.transformPlugin) + } + }, methods: { - + getPluginOptions(list){ + return list.map(plugin => { + return { + name: plugin, + value: plugin + } + }) + }, addRow() { this.detail.params.push({name: null, type: null}) }, @@ -207,16 +224,27 @@ export default { }) }) }, - getAllGroups() { this.axios.post("/group/getAll/").then((response) => { this.groups = response.data }).catch((error) => { }) + }, + getAllPlugin(){ + this.axios.get("/plugin/listAll").then((response)=>{ + const {cachePlugin,transformPlugin} = response.data + if(Array.isArray(cachePlugin)){ + this.cachePlugin = cachePlugin + } + if(Array.isArray(transformPlugin)){ + this.transformPlugin = transformPlugin + } + }).catch((error)=>{}) } }, mounted() { this.getAddress() + this.getAllPlugin() //编辑页面 if (this.id != undefined) { this.getDetail(this.id) diff --git a/dbapi-ui/src/components/common/MySelect.vue b/dbapi-ui/src/components/common/MySelect.vue index 8d8ebe1..18724d0 100644 --- a/dbapi-ui/src/components/common/MySelect.vue +++ b/dbapi-ui/src/components/common/MySelect.vue @@ -3,10 +3,9 @@ {{ (nullable ? '' : '*') + label }}
-
+
{{ item[option_label] }}
@@ -163,6 +162,8 @@ export default { padding: 0px 10px; line-height: 30px; cursor: pointer; + word-break: break-word; + white-space: pre-line; &:hover { //color: rgb(61, 192, 21); -- Gitee