From 861342f478b26959252ec18d62114253c02e79b1 Mon Sep 17 00:00:00 2001 From: gaibu <1016771049@qq.com> Date: Sun, 16 Jul 2023 15:41:57 +0800 Subject: [PATCH] =?UTF-8?q?enhancement=20#I7KZCZ=20=E5=9C=A8=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E6=95=B0=E6=8D=AE=E5=BA=93=E9=85=8D=E7=BD=AE=E7=9A=84?= =?UTF-8?q?=E6=97=B6=E5=80=99=EF=BC=8C=E4=BD=BF=E7=94=A8=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=AD=E5=B7=B2=E7=BB=8F=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E7=9A=84=E6=95=B0=E6=8D=AE=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yomahub/liteflow/spi/ContextAware.java | 25 +- .../liteflow/spi/local/LocalContextAware.java | 71 +-- .../liteflow/parser/sql/SQLXmlELParser.java | 3 + .../liteflow/parser/sql/util/JDBCHelper.java | 505 ++++++++---------- .../parser/sql/util/LiteFlowJdbcUtil.java | 134 +++++ .../liteflow/parser/sql/vo/SQLParserVO.java | 329 ++++++------ .../liteflow/spi/solon/SolonContextAware.java | 124 +++-- .../liteflow/spi/spring/SpringAware.java | 140 ++--- .../liteflow/spring/LiteFlowSpringUtil.java | 275 ++++++++++ ...LWithXmlELMultiLanguageSpringbootTest.java | 34 ++ .../test/def/SQLWithXmlELSpringbootTest.java | 101 ++++ .../application-data-source-xml.properties | 21 + 12 files changed, 1158 insertions(+), 604 deletions(-) create mode 100644 liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/LiteFlowJdbcUtil.java create mode 100644 liteflow-spring/src/main/java/com/yomahub/liteflow/spring/LiteFlowSpringUtil.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/def/SQLWithXmlELMultiLanguageSpringbootTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/def/SQLWithXmlELSpringbootTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/resources/application-data-source-xml.properties diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/spi/ContextAware.java b/liteflow-core/src/main/java/com/yomahub/liteflow/spi/ContextAware.java index ea8197429..824de84fc 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/spi/ContextAware.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/spi/ContextAware.java @@ -1,5 +1,7 @@ package com.yomahub.liteflow.spi; +import java.util.Map; + /** * 环境容器SPI接口 * @@ -8,18 +10,27 @@ package com.yomahub.liteflow.spi; */ public interface ContextAware extends SpiPriority { - T getBean(String name); + T getBean(String name); - T getBean(Class clazz); + T getBean(Class clazz); - T registerBean(String beanName, Class clazz); + T registerBean(String beanName, Class clazz); - T registerBean(Class clazz); + T registerBean(Class clazz); - T registerBean(String beanName, Object bean); + T registerBean(String beanName, Object bean); - T registerOrGet(String beanName, Class clazz); + T registerOrGet(String beanName, Class clazz); - boolean hasBean(String beanName); + /** + * 获取指定类型对应的所有Bean,包括子类 + * + * @param Bean类型 + * @param type 类、接口,null表示获取所有bean + * @return 类型对应的bean,key是bean注册的name,value是Bean + */ + Map getBeansOfType(Class type); + + boolean hasBean(String beanName); } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/spi/local/LocalContextAware.java b/liteflow-core/src/main/java/com/yomahub/liteflow/spi/local/LocalContextAware.java index 63d3cc806..8dd958324 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/spi/local/LocalContextAware.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/spi/local/LocalContextAware.java @@ -3,6 +3,8 @@ package com.yomahub.liteflow.spi.local; import cn.hutool.core.util.ReflectUtil; import com.yomahub.liteflow.spi.ContextAware; +import java.util.Map; + /** * 非Spring环境容器实现 其实非Spring没有环境容器,所以这是个空实现 * @@ -11,44 +13,49 @@ import com.yomahub.liteflow.spi.ContextAware; */ public class LocalContextAware implements ContextAware { - @Override - public T getBean(String name) { - return null; - } + @Override + public T getBean(String name) { + return null; + } - @Override - public T getBean(Class clazz) { - return null; - } + @Override + public T getBean(Class clazz) { + return null; + } - @Override - public T registerBean(String beanName, Class clazz) { - return ReflectUtil.newInstance(clazz); - } + @Override + public T registerBean(String beanName, Class clazz) { + return ReflectUtil.newInstance(clazz); + } - @Override - public T registerBean(Class clazz) { - return registerBean(null, clazz); - } + @Override + public T registerBean(Class clazz) { + return registerBean(null, clazz); + } - @Override - public T registerBean(String beanName, Object bean) { - return (T) bean; - } + @Override + public T registerBean(String beanName, Object bean) { + return (T) bean; + } - @Override - public T registerOrGet(String beanName, Class clazz) { - return registerBean(beanName, clazz); - } + @Override + public T registerOrGet(String beanName, Class clazz) { + return registerBean(beanName, clazz); + } - @Override - public boolean hasBean(String beanName) { - return false; - } + @Override + public Map getBeansOfType(Class type) { + return null; + } - @Override - public int priority() { - return 2; - } + @Override + public boolean hasBean(String beanName) { + return false; + } + + @Override + public int priority() { + return 2; + } } diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/SQLXmlELParser.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/SQLXmlELParser.java index 907818052..ce3bbe536 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/SQLXmlELParser.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/SQLXmlELParser.java @@ -71,6 +71,9 @@ public class SQLXmlELParser extends ClassXmlFlowELParser { * @param sqlParserVO sqlParserVO */ private void checkParserVO(SQLParserVO sqlParserVO) { + if (sqlParserVO.isDefaultDataSource()) { + return; + } if (StrUtil.isEmpty(sqlParserVO.getUrl())) { throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "url")); } diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/JDBCHelper.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/JDBCHelper.java index e7a8bc939..03435a21f 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/JDBCHelper.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/JDBCHelper.java @@ -9,7 +9,6 @@ import com.yomahub.liteflow.parser.sql.exception.ELSQLException; import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; import java.sql.Connection; -import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -25,331 +24,279 @@ import java.util.Objects; */ public class JDBCHelper { - private static final String SQL_PATTERN = "SELECT {},{} FROM {} WHERE {}=?"; + private static final String SQL_PATTERN = "SELECT {},{} FROM {} WHERE {}=?"; - private static final String SCRIPT_SQL_CHECK_PATTERN = "SELECT 1 FROM {} WHERE {}=?"; + private static final String SCRIPT_SQL_CHECK_PATTERN = "SELECT 1 FROM {} WHERE {}=?"; - private static final String SCRIPT_SQL_PATTERN = "SELECT {},{},{},{} FROM {} WHERE {}=?"; + private static final String SCRIPT_SQL_PATTERN = "SELECT {},{},{},{} FROM {} WHERE {}=?"; - private static final String SCRIPT_WITH_LANGUAG_SQL_PATTERN = "SELECT {},{},{},{},{} FROM {} WHERE {}=?"; + private static final String SCRIPT_WITH_LANGUAG_SQL_PATTERN = "SELECT {},{},{},{},{} FROM {} WHERE {}=?"; - private static final String CHAIN_XML_PATTERN = "{}"; + private static final String CHAIN_XML_PATTERN = "{}"; - private static final String NODE_XML_PATTERN = "{}"; + private static final String NODE_XML_PATTERN = "{}"; - private static final String NODE_ITEM_XML_PATTERN = ""; + private static final String NODE_ITEM_XML_PATTERN = ""; - private static final String NODE_ITEM_WITH_LANGUAGE_XML_PATTERN = ""; + private static final String NODE_ITEM_WITH_LANGUAGE_XML_PATTERN = ""; - private static final String XML_PATTERN = "{}{}"; + private static final String XML_PATTERN = "{}{}"; - private static final Integer FETCH_SIZE_MAX = 1000; + private static final Integer FETCH_SIZE_MAX = 1000; - private SQLParserVO sqlParserVO; + private SQLParserVO sqlParserVO; - private static JDBCHelper INSTANCE; + private static JDBCHelper INSTANCE; - /** - * 初始化 INSTANCE - */ - public static void init(SQLParserVO sqlParserVO) { - try { - INSTANCE = new JDBCHelper(); - Class.forName(sqlParserVO.getDriverClassName()); - INSTANCE.setSqlParserVO(sqlParserVO); - } - catch (ClassNotFoundException e) { - throw new ELSQLException(e.getMessage()); - } - } + /** + * 初始化 INSTANCE + */ + public static void init(SQLParserVO sqlParserVO) { + try { + INSTANCE = new JDBCHelper(); + if (StrUtil.isNotBlank(sqlParserVO.getDriverClassName())) { + Class.forName(sqlParserVO.getDriverClassName()); + } + INSTANCE.setSqlParserVO(sqlParserVO); + } catch (ClassNotFoundException e) { + throw new ELSQLException(e.getMessage()); + } + } - /** - * 获取 INSTANCE - */ - public static JDBCHelper getInstance() { - return INSTANCE; - } + /** + * 获取 INSTANCE + */ + public static JDBCHelper getInstance() { + return INSTANCE; + } - /** - * 获取链接 - */ - public Connection getConn() { - Connection connection; - try { - connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), - sqlParserVO.getPassword()); - } - catch (SQLException e) { - throw new ELSQLException(e.getMessage()); - } - return connection; - } + /** + * 获取 ElData 数据内容 + */ + public String getContent() { + Connection conn = null; + PreparedStatement stmt = null; + ResultSet rs = null; - /** - * 获取 ElData 数据内容 - */ - public String getContent() { - Connection conn = null; - PreparedStatement stmt = null; - ResultSet rs = null; + String chainTableName = sqlParserVO.getChainTableName(); + String elDataField = sqlParserVO.getElDataField(); + String chainNameField = sqlParserVO.getChainNameField(); + String chainApplicationNameField = sqlParserVO.getChainApplicationNameField(); + String applicationName = sqlParserVO.getApplicationName(); - String chainTableName = sqlParserVO.getChainTableName(); - String elDataField = sqlParserVO.getElDataField(); - String chainNameField = sqlParserVO.getChainNameField(); - String chainApplicationNameField = sqlParserVO.getChainApplicationNameField(); - String applicationName = sqlParserVO.getApplicationName(); + if (StrUtil.isBlank(chainTableName)) { + throw new ELSQLException("You did not define the chainTableName property"); + } - if (StrUtil.isBlank(chainTableName)) { - throw new ELSQLException("You did not define the chainTableName property"); - } + if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(chainApplicationNameField)) { + throw new ELSQLException("You did not define the applicationName or chainApplicationNameField property"); + } - if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(chainApplicationNameField)) { - throw new ELSQLException("You did not define the applicationName or chainApplicationNameField property"); - } + String sqlCmd = StrUtil.format(SQL_PATTERN, chainNameField, elDataField, chainTableName, + chainApplicationNameField); - String sqlCmd = StrUtil.format(SQL_PATTERN, chainNameField, elDataField, chainTableName, - chainApplicationNameField); + List result = new ArrayList<>(); + try { + conn = LiteFlowJdbcUtil.getConn(sqlParserVO); + stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + // 设置游标拉取数量 + stmt.setFetchSize(FETCH_SIZE_MAX); + stmt.setString(1, applicationName); + rs = stmt.executeQuery(); - List result = new ArrayList<>(); - try { - conn = getConn(); - stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - // 设置游标拉取数量 - stmt.setFetchSize(FETCH_SIZE_MAX); - stmt.setString(1, applicationName); - rs = stmt.executeQuery(); + while (rs.next()) { + String elData = getStringFromResultSet(rs, elDataField); + String chainName = getStringFromResultSet(rs, chainNameField); - while (rs.next()) { - String elData = getStringFromResultSet(rs, elDataField); - String chainName = getStringFromResultSet(rs, chainNameField); + result.add(StrUtil.format(CHAIN_XML_PATTERN, XmlUtil.escape(chainName), elData)); + } + } catch (Exception e) { + throw new ELSQLException(e.getMessage()); + } finally { + // 关闭连接 + LiteFlowJdbcUtil.close(conn, stmt, rs); + } - result.add(StrUtil.format(CHAIN_XML_PATTERN, XmlUtil.escape(chainName), elData)); - } - } - catch (Exception e) { - throw new ELSQLException(e.getMessage()); - } - finally { - // 关闭连接 - close(conn, stmt, rs); - } + String chainsContent = CollUtil.join(result, StrUtil.EMPTY); - String chainsContent = CollUtil.join(result, StrUtil.EMPTY); + String nodesContent; + if (hasScriptData()) { + nodesContent = getScriptNodes(); + } else { + nodesContent = StrUtil.EMPTY; + } - String nodesContent; - if (hasScriptData()) { - nodesContent = getScriptNodes(); - } - else { - nodesContent = StrUtil.EMPTY; - } + return StrUtil.format(XML_PATTERN, nodesContent, chainsContent); + } - return StrUtil.format(XML_PATTERN, nodesContent, chainsContent); - } + /** + * 获取脚本节点内容 + */ + public String getScriptNodes() { + String scriptLanguageField = sqlParserVO.getScriptLanguageField(); + if (StrUtil.isNotBlank(scriptLanguageField)) { + return getScriptNodesWithLanguage(); + } - public String getScriptNodes() { - String scriptLanguageField = sqlParserVO.getScriptLanguageField(); - if (StrUtil.isNotBlank(scriptLanguageField)) { - return getScriptNodesWithLanguage(); - } + List result = new ArrayList<>(); + Connection conn = null; + PreparedStatement stmt = null; + ResultSet rs = null; - List result = new ArrayList<>(); - Connection conn = null; - PreparedStatement stmt = null; - ResultSet rs = null; + String scriptTableName = sqlParserVO.getScriptTableName(); + String scriptIdField = sqlParserVO.getScriptIdField(); + String scriptDataField = sqlParserVO.getScriptDataField(); + String scriptNameField = sqlParserVO.getScriptNameField(); + String scriptTypeField = sqlParserVO.getScriptTypeField(); + String scriptApplicationNameField = sqlParserVO.getScriptApplicationNameField(); + String applicationName = sqlParserVO.getApplicationName(); - String scriptTableName = sqlParserVO.getScriptTableName(); - String scriptIdField = sqlParserVO.getScriptIdField(); - String scriptDataField = sqlParserVO.getScriptDataField(); - String scriptNameField = sqlParserVO.getScriptNameField(); - String scriptTypeField = sqlParserVO.getScriptTypeField(); - String scriptApplicationNameField = sqlParserVO.getScriptApplicationNameField(); - String applicationName = sqlParserVO.getApplicationName(); + if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(scriptApplicationNameField)) { + throw new ELSQLException("You did not define the applicationName or scriptApplicationNameField property"); + } - if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(scriptApplicationNameField)) { - throw new ELSQLException("You did not define the applicationName or scriptApplicationNameField property"); - } + String sqlCmd = StrUtil.format(SCRIPT_SQL_PATTERN, scriptIdField, scriptDataField, scriptNameField, + scriptTypeField, scriptTableName, scriptApplicationNameField); + try { + conn = LiteFlowJdbcUtil.getConn(sqlParserVO); + stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + // 设置游标拉取数量 + stmt.setFetchSize(FETCH_SIZE_MAX); + stmt.setString(1, applicationName); + rs = stmt.executeQuery(); - String sqlCmd = StrUtil.format(SCRIPT_SQL_PATTERN, scriptIdField, scriptDataField, scriptNameField, - scriptTypeField, scriptTableName, scriptApplicationNameField); - try { - conn = getConn(); - stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - // 设置游标拉取数量 - stmt.setFetchSize(FETCH_SIZE_MAX); - stmt.setString(1, applicationName); - rs = stmt.executeQuery(); + while (rs.next()) { + String id = getStringFromResultSet(rs, scriptIdField); + String data = getStringFromResultSet(rs, scriptDataField); + String name = getStringFromResultSet(rs, scriptNameField); + String type = getStringFromResultSet(rs, scriptTypeField); - while (rs.next()) { - String id = getStringFromResultSet(rs, scriptIdField); - String data = getStringFromResultSet(rs, scriptDataField); - String name = getStringFromResultSet(rs, scriptNameField); - String type = getStringFromResultSet(rs, scriptTypeField); + NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type); + if (Objects.isNull(nodeTypeEnum)) { + throw new ELSQLException(StrUtil.format("Invalid type value[{}]", type)); + } - NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type); - if (Objects.isNull(nodeTypeEnum)) { - throw new ELSQLException(StrUtil.format("Invalid type value[{}]", type)); - } + if (!nodeTypeEnum.isScript()) { + throw new ELSQLException(StrUtil.format("The type value[{}] is not a script type", type)); + } - if (!nodeTypeEnum.isScript()) { - throw new ELSQLException(StrUtil.format("The type value[{}] is not a script type", type)); - } + result.add(StrUtil.format(NODE_ITEM_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), type, data)); + } + } catch (Exception e) { + throw new ELSQLException(e.getMessage()); + } finally { + // 关闭连接 + LiteFlowJdbcUtil.close(conn, stmt, rs); + } + return StrUtil.format(NODE_XML_PATTERN, CollUtil.join(result, StrUtil.EMPTY)); + } - result.add(StrUtil.format(NODE_ITEM_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), type, data)); - } - } - catch (Exception e) { - throw new ELSQLException(e.getMessage()); - } - finally { - // 关闭连接 - close(conn, stmt, rs); - } - return StrUtil.format(NODE_XML_PATTERN, CollUtil.join(result, StrUtil.EMPTY)); - } + /** + * 获取脚本节点(带语言) + * + * @return + */ + public String getScriptNodesWithLanguage() { - public String getScriptNodesWithLanguage() { + List result = new ArrayList<>(); + Connection conn = null; + PreparedStatement stmt = null; + ResultSet rs = null; - List result = new ArrayList<>(); - Connection conn = null; - PreparedStatement stmt = null; - ResultSet rs = null; + String scriptTableName = sqlParserVO.getScriptTableName(); + String scriptIdField = sqlParserVO.getScriptIdField(); + String scriptDataField = sqlParserVO.getScriptDataField(); + String scriptNameField = sqlParserVO.getScriptNameField(); + String scriptTypeField = sqlParserVO.getScriptTypeField(); + String scriptApplicationNameField = sqlParserVO.getScriptApplicationNameField(); + String applicationName = sqlParserVO.getApplicationName(); + String scriptLanguageField = sqlParserVO.getScriptLanguageField(); - String scriptTableName = sqlParserVO.getScriptTableName(); - String scriptIdField = sqlParserVO.getScriptIdField(); - String scriptDataField = sqlParserVO.getScriptDataField(); - String scriptNameField = sqlParserVO.getScriptNameField(); - String scriptTypeField = sqlParserVO.getScriptTypeField(); - String scriptApplicationNameField = sqlParserVO.getScriptApplicationNameField(); - String applicationName = sqlParserVO.getApplicationName(); - String scriptLanguageField = sqlParserVO.getScriptLanguageField(); + if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(scriptApplicationNameField)) { + throw new ELSQLException("You did not define the applicationName or scriptApplicationNameField property"); + } - if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(scriptApplicationNameField)) { - throw new ELSQLException("You did not define the applicationName or scriptApplicationNameField property"); - } + String sqlCmd = StrUtil.format(SCRIPT_WITH_LANGUAG_SQL_PATTERN, scriptIdField, scriptDataField, scriptNameField, + scriptTypeField, scriptLanguageField, scriptTableName, scriptApplicationNameField); + try { + conn = LiteFlowJdbcUtil.getConn(sqlParserVO); + stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + // 设置游标拉取数量 + stmt.setFetchSize(FETCH_SIZE_MAX); + stmt.setString(1, applicationName); + rs = stmt.executeQuery(); - String sqlCmd = StrUtil.format(SCRIPT_WITH_LANGUAG_SQL_PATTERN, scriptIdField, scriptDataField, scriptNameField, - scriptTypeField, scriptLanguageField, scriptTableName, scriptApplicationNameField); - try { - conn = getConn(); - stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - // 设置游标拉取数量 - stmt.setFetchSize(FETCH_SIZE_MAX); - stmt.setString(1, applicationName); - rs = stmt.executeQuery(); + while (rs.next()) { + String id = getStringFromResultSet(rs, scriptIdField); + String data = getStringFromResultSet(rs, scriptDataField); + String name = getStringFromResultSet(rs, scriptNameField); + String type = getStringFromResultSet(rs, scriptTypeField); + String language = getStringFromResultSet(rs, scriptLanguageField); - while (rs.next()) { - String id = getStringFromResultSet(rs, scriptIdField); - String data = getStringFromResultSet(rs, scriptDataField); - String name = getStringFromResultSet(rs, scriptNameField); - String type = getStringFromResultSet(rs, scriptTypeField); - String language = getStringFromResultSet(rs, scriptLanguageField); + NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type); + if (Objects.isNull(nodeTypeEnum)) { + throw new ELSQLException(StrUtil.format("Invalid type value[{}]", type)); + } - NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type); - if (Objects.isNull(nodeTypeEnum)) { - throw new ELSQLException(StrUtil.format("Invalid type value[{}]", type)); - } + if (!nodeTypeEnum.isScript()) { + throw new ELSQLException(StrUtil.format("The type value[{}] is not a script type", type)); + } - if (!nodeTypeEnum.isScript()) { - throw new ELSQLException(StrUtil.format("The type value[{}] is not a script type", type)); - } + if (!ScriptTypeEnum.checkScriptType(language)) { + throw new ELSQLException(StrUtil.format("The language value[{}] is error", language)); + } - if (!ScriptTypeEnum.checkScriptType(language)) { - throw new ELSQLException(StrUtil.format("The language value[{}] is error", language)); - } + result.add(StrUtil.format(NODE_ITEM_WITH_LANGUAGE_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), + type, language, data)); + } + } catch (Exception e) { + throw new ELSQLException(e.getMessage()); + } finally { + // 关闭连接 + LiteFlowJdbcUtil.close(conn, stmt, rs); + } + return StrUtil.format(NODE_XML_PATTERN, CollUtil.join(result, StrUtil.EMPTY)); + } - result.add(StrUtil.format(NODE_ITEM_WITH_LANGUAGE_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), - type, language, data)); - } - } - catch (Exception e) { - throw new ELSQLException(e.getMessage()); - } - finally { - // 关闭连接 - close(conn, stmt, rs); - } - return StrUtil.format(NODE_XML_PATTERN, CollUtil.join(result, StrUtil.EMPTY)); - } + private boolean hasScriptData() { + if (StrUtil.isBlank(sqlParserVO.getScriptTableName())) { + return false; + } - /** - * 关闭连接 - * @param conn conn - * @param stmt stmt - * @param rs rs - */ - private void close(Connection conn, PreparedStatement stmt, ResultSet rs) { - // 关闭连接 - if (conn != null) { - try { - conn.close(); - } - catch (SQLException e) { - throw new ELSQLException(e.getMessage()); - } - } - // 关闭 statement - if (stmt != null) { - try { - stmt.close(); - } - catch (SQLException e) { - throw new ELSQLException(e.getMessage()); - } - } - // 关闭结果集 - if (rs != null) { - try { - rs.close(); - } - catch (SQLException e) { - throw new ELSQLException(e.getMessage()); - } - } - } + Connection conn = null; + PreparedStatement stmt = null; + ResultSet rs = null; + String sqlCmd = StrUtil.format(SCRIPT_SQL_CHECK_PATTERN, sqlParserVO.getScriptTableName(), + sqlParserVO.getScriptApplicationNameField()); + try { + conn = LiteFlowJdbcUtil.getConn(sqlParserVO); + stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + stmt.setFetchSize(1); + stmt.setString(1, sqlParserVO.getApplicationName()); + rs = stmt.executeQuery(); + return rs.next(); + } catch (Exception e) { + return false; + } finally { + // 关闭连接 + LiteFlowJdbcUtil.close(conn, stmt, rs); + } + } - private boolean hasScriptData() { - if (StrUtil.isBlank(sqlParserVO.getScriptTableName())) { - return false; - } + private String getStringFromResultSet(ResultSet rs, String field) throws SQLException { + String data = rs.getString(field); + if (StrUtil.isBlank(data)) { + throw new ELSQLException(StrUtil.format("exist {} field value is empty", field)); + } + return data; + } - Connection conn = null; - PreparedStatement stmt = null; - ResultSet rs = null; - String sqlCmd = StrUtil.format(SCRIPT_SQL_CHECK_PATTERN, sqlParserVO.getScriptTableName(), - sqlParserVO.getScriptApplicationNameField()); - try { - conn = getConn(); - stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - stmt.setFetchSize(1); - stmt.setString(1, sqlParserVO.getApplicationName()); - rs = stmt.executeQuery(); - return rs.next(); - } - catch (Exception e) { - return false; - } - finally { - // 关闭连接 - close(conn, stmt, rs); - } - } + private SQLParserVO getSqlParserVO() { + return sqlParserVO; + } - // #region get set method - private String getStringFromResultSet(ResultSet rs, String field) throws SQLException { - String data = rs.getString(field); - if (StrUtil.isBlank(data)) { - throw new ELSQLException(StrUtil.format("exist {} field value is empty", field)); - } - return data; - } - - private SQLParserVO getSqlParserVO() { - return sqlParserVO; - } - - private void setSqlParserVO(SQLParserVO sqlParserVO) { - this.sqlParserVO = sqlParserVO; - } + private void setSqlParserVO(SQLParserVO sqlParserVO) { + this.sqlParserVO = sqlParserVO; + } } diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/LiteFlowJdbcUtil.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/LiteFlowJdbcUtil.java new file mode 100644 index 000000000..4b2b640ed --- /dev/null +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/LiteFlowJdbcUtil.java @@ -0,0 +1,134 @@ +package com.yomahub.liteflow.parser.sql.util; + +import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.parser.sql.exception.ELSQLException; +import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; +import com.yomahub.liteflow.spi.holder.ContextAwareHolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; +import java.sql.*; +import java.util.Map; + +public class LiteFlowJdbcUtil { + private static final Logger LOG = LoggerFactory.getLogger(LiteFlowJdbcUtil.class); + private static final String CHECK_SQL_PATTERN = "SELECT {},{} FROM {} WHERE {}='{}'"; + + /** + * 获取链接 + * 此方法会根据配置,判读使用指定数据源,还是IOC容器中已有的数据源 + * + * @param sqlParserVO + * @return + */ + public static Connection getConn(SQLParserVO sqlParserVO) { + Connection connection = null; + String url = sqlParserVO.getUrl(); + String username = sqlParserVO.getUsername(); + String password = sqlParserVO.getPassword(); + + try { + // 如果不配置 jdbc 连接相关配置,代表使用项目数据源 + if (sqlParserVO.isDefaultDataSource()) { + String executeSql = buildCheckSql(sqlParserVO); + Map dataSourceMap = ContextAwareHolder.loadContextAware().getBeansOfType(DataSource.class); + // 遍历数据源,多数据源场景下,判断哪个数据源有 liteflow 配置 + for (Map.Entry entry : dataSourceMap.entrySet()) { + String dataSourceName = entry.getKey(); + DataSource dataSource = entry.getValue(); + + if (checkConnectionCanExecuteSql(dataSource.getConnection(), executeSql)) { + connection = dataSource.getConnection(); + LOG.info("use dataSourceName[{}],has found liteflow config", dataSourceName); + } else { + LOG.info("check dataSourceName[{}],but not has liteflow config", dataSourceName); + } + } + if (connection == null) { + throw new ELSQLException("can not found liteflow config in dataSourceName " + dataSourceMap.keySet()); + } + } + // 如果配置 jdbc 连接相关配置,代表使用指定链接信息 + else { + connection = DriverManager.getConnection(url, username, password); + } + + } catch (Exception e) { + throw new ELSQLException(e.getMessage()); + } + + return connection; + } + + /** + * 判断连接是否可以执行指定 sql + * + * @param conn 连接 + * @param sql 执行 sql + */ + public static boolean checkConnectionCanExecuteSql(Connection conn, String sql) { + PreparedStatement stmt = null; + ResultSet rs = null; + try { + stmt = conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + stmt.setFetchSize(1); + rs = stmt.executeQuery(); + return rs.next(); + } catch (Exception e) { + return false; + } finally { + // 关闭连接 + close(conn, stmt, rs); + } + } + + /** + * 关闭 + * + * @param conn conn + * @param conn conn + * @param rs rs + */ + public static void close(Connection conn, PreparedStatement stmt, ResultSet rs) { + // 关闭连接 + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + throw new ELSQLException(e.getMessage()); + } + } + // 关闭 statement + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException e) { + throw new ELSQLException(e.getMessage()); + } + } + // 关闭结果集 + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + throw new ELSQLException(e.getMessage()); + } + } + } + + /** + * 构建检查 sql + * + * @param sqlParserVO + * @return + */ + private static String buildCheckSql(SQLParserVO sqlParserVO) { + String chainTableName = sqlParserVO.getChainTableName(); + String elDataField = sqlParserVO.getElDataField(); + String chainNameField = sqlParserVO.getChainNameField(); + String chainApplicationNameField = sqlParserVO.getChainApplicationNameField(); + String applicationName = sqlParserVO.getApplicationName(); + return StrUtil.format(CHECK_SQL_PATTERN, chainNameField, elDataField, chainTableName, chainApplicationNameField, applicationName); + } +} diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/vo/SQLParserVO.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/vo/SQLParserVO.java index 4459d1922..b549bafdc 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/vo/SQLParserVO.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/vo/SQLParserVO.java @@ -1,5 +1,7 @@ package com.yomahub.liteflow.parser.sql.vo; +import cn.hutool.core.util.StrUtil; + /** * 用于解析 RuleSourceExtData 的 VO 类,用于 sql 模式中 * @@ -8,212 +10,219 @@ package com.yomahub.liteflow.parser.sql.vo; */ public class SQLParserVO { - /** - * 连接地址 - */ - private String url; + /** + * 连接地址 + */ + private String url; - /** - * 驱动 - */ - private String driverClassName; + /** + * 驱动 + */ + private String driverClassName; - /** - * 账号名 - */ - private String username; + /** + * 账号名 + */ + private String username; - /** - * 密码 - */ - private String password; + /** + * 密码 + */ + private String password; - /** - * 应用名 - */ - private String applicationName; + /** + * 应用名 + */ + private String applicationName; - /** - * chain表名 - */ - private String chainTableName; + /** + * chain表名 + */ + private String chainTableName; - /** - * chain表里的应用名字段 - */ - private String chainApplicationNameField = "application_name"; + /** + * chain表里的应用名字段 + */ + private String chainApplicationNameField = "application_name"; - /** - * chainName - */ - private String chainNameField = "chain_name"; + /** + * chainName + */ + private String chainNameField = "chain_name"; - /** - * el 表达式相关数据 - */ - private String elDataField = "el_data"; + /** + * el 表达式相关数据 + */ + private String elDataField = "el_data"; - /** - * 脚本 node 表名 - */ - private String scriptTableName; + /** + * 脚本 node 表名 + */ + private String scriptTableName; - /** - * script表里的应用名字段 - */ - private String scriptApplicationNameField = "application_name"; + /** + * script表里的应用名字段 + */ + private String scriptApplicationNameField = "application_name"; - /** - * 脚本 node id 字段 - */ - private String scriptIdField = "script_id"; + /** + * 脚本 node id 字段 + */ + private String scriptIdField = "script_id"; - /** - * 脚本 node name 字段 - */ - private String scriptNameField = "script_name"; + /** + * 脚本 node name 字段 + */ + private String scriptNameField = "script_name"; - /** - * 脚本 node data 字段 - */ - private String scriptDataField = "script_data"; + /** + * 脚本 node data 字段 + */ + private String scriptDataField = "script_data"; - /** - * 脚本 node type 字段 - */ - private String scriptTypeField = "script_type"; + /** + * 脚本 node type 字段 + */ + private String scriptTypeField = "script_type"; - /** - * 脚本 node language 字段 - */ - private String scriptLanguageField; + /** + * 脚本 node language 字段 + */ + private String scriptLanguageField; - public String getUrl() { - return url; - } + public String getUrl() { + return url; + } - public void setUrl(String url) { - this.url = url; - } + public void setUrl(String url) { + this.url = url; + } - public String getDriverClassName() { - return driverClassName; - } + public String getDriverClassName() { + return driverClassName; + } - public void setDriverClassName(String driverClassName) { - this.driverClassName = driverClassName; - } + public void setDriverClassName(String driverClassName) { + this.driverClassName = driverClassName; + } - public String getUsername() { - return username; - } + public String getUsername() { + return username; + } - public void setUsername(String username) { - this.username = username; - } + public void setUsername(String username) { + this.username = username; + } - public String getPassword() { - return password; - } + public String getPassword() { + return password; + } - public void setPassword(String password) { - this.password = password; - } + public void setPassword(String password) { + this.password = password; + } - public String getApplicationName() { - return applicationName; - } + public String getApplicationName() { + return applicationName; + } - public void setApplicationName(String applicationName) { - this.applicationName = applicationName; - } + public void setApplicationName(String applicationName) { + this.applicationName = applicationName; + } - public String getChainTableName() { - return chainTableName; - } + public String getChainTableName() { + return chainTableName; + } - public void setChainTableName(String chainTableName) { - this.chainTableName = chainTableName; - } + public void setChainTableName(String chainTableName) { + this.chainTableName = chainTableName; + } - public String getChainApplicationNameField() { - return chainApplicationNameField; - } + public String getChainApplicationNameField() { + return chainApplicationNameField; + } - public void setChainApplicationNameField(String chainApplicationNameField) { - this.chainApplicationNameField = chainApplicationNameField; - } + public void setChainApplicationNameField(String chainApplicationNameField) { + this.chainApplicationNameField = chainApplicationNameField; + } - public String getChainNameField() { - return chainNameField; - } + public String getChainNameField() { + return chainNameField; + } - public void setChainNameField(String chainNameField) { - this.chainNameField = chainNameField; - } + public void setChainNameField(String chainNameField) { + this.chainNameField = chainNameField; + } - public String getElDataField() { - return elDataField; - } + public String getElDataField() { + return elDataField; + } - public void setElDataField(String elDataField) { - this.elDataField = elDataField; - } + public void setElDataField(String elDataField) { + this.elDataField = elDataField; + } - public String getScriptTableName() { - return scriptTableName; - } + public String getScriptTableName() { + return scriptTableName; + } - public void setScriptTableName(String scriptTableName) { - this.scriptTableName = scriptTableName; - } + public void setScriptTableName(String scriptTableName) { + this.scriptTableName = scriptTableName; + } - public String getScriptApplicationNameField() { - return scriptApplicationNameField; - } + public String getScriptApplicationNameField() { + return scriptApplicationNameField; + } - public void setScriptApplicationNameField(String scriptApplicationNameField) { - this.scriptApplicationNameField = scriptApplicationNameField; - } + public void setScriptApplicationNameField(String scriptApplicationNameField) { + this.scriptApplicationNameField = scriptApplicationNameField; + } - public String getScriptIdField() { - return scriptIdField; - } + public String getScriptIdField() { + return scriptIdField; + } - public void setScriptIdField(String scriptIdField) { - this.scriptIdField = scriptIdField; - } + public void setScriptIdField(String scriptIdField) { + this.scriptIdField = scriptIdField; + } - public String getScriptNameField() { - return scriptNameField; - } + public String getScriptNameField() { + return scriptNameField; + } - public void setScriptNameField(String scriptNameField) { - this.scriptNameField = scriptNameField; - } + public void setScriptNameField(String scriptNameField) { + this.scriptNameField = scriptNameField; + } - public String getScriptDataField() { - return scriptDataField; - } + public String getScriptDataField() { + return scriptDataField; + } - public void setScriptDataField(String scriptDataField) { - this.scriptDataField = scriptDataField; - } + public void setScriptDataField(String scriptDataField) { + this.scriptDataField = scriptDataField; + } - public String getScriptTypeField() { - return scriptTypeField; - } + public String getScriptTypeField() { + return scriptTypeField; + } - public void setScriptTypeField(String scriptTypeField) { - this.scriptTypeField = scriptTypeField; - } + public void setScriptTypeField(String scriptTypeField) { + this.scriptTypeField = scriptTypeField; + } - public String getScriptLanguageField() { - return scriptLanguageField; - } + public String getScriptLanguageField() { + return scriptLanguageField; + } - public void setScriptLanguageField(String scriptLanguageField) { - this.scriptLanguageField = scriptLanguageField; - } + public void setScriptLanguageField(String scriptLanguageField) { + this.scriptLanguageField = scriptLanguageField; + } + + /** + * 判断配资是否使用 IOC 已有数据源 + */ + public boolean isDefaultDataSource() { + return StrUtil.isBlank(url) && StrUtil.isBlank(username) && StrUtil.isBlank(password) && StrUtil.isBlank(driverClassName); + } } diff --git a/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/spi/solon/SolonContextAware.java b/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/spi/solon/SolonContextAware.java index 8488019a5..4cb16d9e0 100644 --- a/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/spi/solon/SolonContextAware.java +++ b/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/spi/solon/SolonContextAware.java @@ -1,10 +1,15 @@ package com.yomahub.liteflow.spi.solon; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.yomahub.liteflow.spi.ContextAware; import org.noear.solon.Solon; import org.noear.solon.core.BeanWrap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * 基于代码形式的 Solon 上下文工具类 * @@ -12,73 +17,76 @@ import org.noear.solon.core.BeanWrap; */ public class SolonContextAware implements ContextAware { - @Override - public T getBean(String name) { - try { - return Solon.context().getBean(name); - } - catch (Exception e) { - return null; - } - } + @Override + public T getBean(String name) { + try { + return Solon.context().getBean(name); + } catch (Exception e) { + return null; + } + } - @Override - public T getBean(Class clazz) { - try { - return Solon.context().getBean(clazz); - } - catch (Exception e) { - return null; - } - } + @Override + public T getBean(Class clazz) { + try { + return Solon.context().getBean(clazz); + } catch (Exception e) { + return null; + } + } - private T getBean(String beanName, Class clazz) { - try { - return Solon.context().getBean(beanName); - } - catch (Exception e) { - return null; - } - } + private T getBean(String beanName, Class clazz) { + try { + return Solon.context().getBean(beanName); + } catch (Exception e) { + return null; + } + } - @Override - public T registerBean(String beanName, Class c) { - BeanWrap beanWrap = new BeanWrap(Solon.context(), c, null, beanName); - Solon.context().putWrap(beanName, beanWrap); + @Override + public T registerBean(String beanName, Class c) { + BeanWrap beanWrap = new BeanWrap(Solon.context(), c, null, beanName); + Solon.context().putWrap(beanName, beanWrap); - return beanWrap.get(); - } + return beanWrap.get(); + } - @Override - public T registerBean(Class c) { - return registerBean(c.getName(), c); - } + @Override + public T registerBean(Class c) { + return registerBean(c.getName(), c); + } - @Override - public T registerBean(String beanName, Object bean) { - BeanWrap beanWrap = new BeanWrap(Solon.context(), bean.getClass(), bean, beanName); - Solon.context().putWrap(beanName, beanWrap); + @Override + public T registerBean(String beanName, Object bean) { + BeanWrap beanWrap = new BeanWrap(Solon.context(), bean.getClass(), bean, beanName); + Solon.context().putWrap(beanName, beanWrap); - return beanWrap.get(); - } + return beanWrap.get(); + } - @Override - public T registerOrGet(String beanName, Class clazz) { - T t = getBean(beanName, clazz); - if (ObjectUtil.isNull(t)) { - t = registerBean(beanName, clazz); - } - return t; - } + @Override + public T registerOrGet(String beanName, Class clazz) { + T t = getBean(beanName, clazz); + if (ObjectUtil.isNull(t)) { + t = registerBean(beanName, clazz); + } + return t; + } - @Override - public boolean hasBean(String beanName) { - return Solon.context().hasWrap(beanName); - } + @Override + public Map getBeansOfType(Class type) { + List wrapsOfType = Solon.context().getWrapsOfType(type); + return CollUtil.toMap(wrapsOfType, new HashMap(), BeanWrap::name, BeanWrap::get); + } - @Override - public int priority() { - return 1; - } + @Override + public boolean hasBean(String beanName) { + return Solon.context().hasWrap(beanName); + } + + @Override + public int priority() { + return 1; + } } diff --git a/liteflow-spring/src/main/java/com/yomahub/liteflow/spi/spring/SpringAware.java b/liteflow-spring/src/main/java/com/yomahub/liteflow/spi/spring/SpringAware.java index 232adecbc..41e604f00 100644 --- a/liteflow-spring/src/main/java/com/yomahub/liteflow/spi/spring/SpringAware.java +++ b/liteflow-spring/src/main/java/com/yomahub/liteflow/spi/spring/SpringAware.java @@ -1,10 +1,8 @@ package com.yomahub.liteflow.spi.spring; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.ReflectUtil; import com.yomahub.liteflow.spi.ContextAware; import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.GenericBeanDefinition; @@ -12,6 +10,8 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; +import java.util.Map; + /** * 基于代码形式的spring上下文工具类 * @@ -19,83 +19,87 @@ import org.springframework.context.ConfigurableApplicationContext; */ public class SpringAware implements ApplicationContextAware, ContextAware { - private static ApplicationContext applicationContext = null; + private static ApplicationContext applicationContext = null; - public SpringAware() { - } + public SpringAware() { + } - @Override - public void setApplicationContext(ApplicationContext ac) throws BeansException { - applicationContext = ac; - } + @Override + public void setApplicationContext(ApplicationContext ac) throws BeansException { + applicationContext = ac; + } - public static ApplicationContext getApplicationContext() { - return applicationContext; - } + public static ApplicationContext getApplicationContext() { + return applicationContext; + } - @Override - public T getBean(String name) { - T t = (T) applicationContext.getBean(name); - return t; - } + @Override + public T getBean(String name) { + T t = (T) applicationContext.getBean(name); + return t; + } - @Override - public T getBean(Class clazz) { - T t = applicationContext.getBean(clazz); - return t; - } + @Override + public Map getBeansOfType(Class type) { + return applicationContext.getBeansOfType(type); + } - private T getBean(String beanName, Class clazz) { - T t = applicationContext.getBean(beanName, clazz); - return t; - } + @Override + public T getBean(Class clazz) { + T t = applicationContext.getBean(clazz); + return t; + } - @Override - public T registerBean(String beanName, Class c) { - DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext - .getAutowireCapableBeanFactory(); - BeanDefinition beanDefinition = new GenericBeanDefinition(); - beanDefinition.setBeanClassName(c.getName()); - beanFactory.setAllowBeanDefinitionOverriding(true); - beanFactory.registerBeanDefinition(beanName, beanDefinition); - return getBean(beanName); - } + private T getBean(String beanName, Class clazz) { + T t = applicationContext.getBean(beanName, clazz); + return t; + } - @Override - public T registerBean(Class c) { - return registerBean(c.getName(), c); - } + @Override + public T registerBean(String beanName, Class c) { + DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext + .getAutowireCapableBeanFactory(); + BeanDefinition beanDefinition = new GenericBeanDefinition(); + beanDefinition.setBeanClassName(c.getName()); + beanFactory.setAllowBeanDefinitionOverriding(true); + beanFactory.registerBeanDefinition(beanName, beanDefinition); + return getBean(beanName); + } - @Override - public T registerBean(String beanName, Object bean) { - ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext; - DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext - .getAutowireCapableBeanFactory(); - defaultListableBeanFactory.registerSingleton(beanName, bean); - return (T) configurableApplicationContext.getBean(beanName); - } + @Override + public T registerBean(Class c) { + return registerBean(c.getName(), c); + } - @Override - public T registerOrGet(String beanName, Class clazz) { - if (ObjectUtil.isNull(applicationContext)) { - return null; - } - try { - return getBean(beanName, clazz); - } - catch (Exception e) { - return registerBean(beanName, clazz); - } - } + @Override + public T registerBean(String beanName, Object bean) { + ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext; + DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext + .getAutowireCapableBeanFactory(); + defaultListableBeanFactory.registerSingleton(beanName, bean); + return (T) configurableApplicationContext.getBean(beanName); + } - @Override - public boolean hasBean(String beanName) { - return applicationContext.containsBean(beanName); - } + @Override + public T registerOrGet(String beanName, Class clazz) { + if (ObjectUtil.isNull(applicationContext)) { + return null; + } + try { + return getBean(beanName, clazz); + } catch (Exception e) { + return registerBean(beanName, clazz); + } + } - @Override - public int priority() { - return 1; - } + @Override + public boolean hasBean(String beanName) { + return applicationContext.containsBean(beanName); + } + + @Override + public int priority() { + return 1; + } } diff --git a/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/LiteFlowSpringUtil.java b/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/LiteFlowSpringUtil.java new file mode 100644 index 000000000..fe0197188 --- /dev/null +++ b/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/LiteFlowSpringUtil.java @@ -0,0 +1,275 @@ +package com.yomahub.liteflow.spring; + +import cn.hutool.core.exceptions.UtilException; +import cn.hutool.core.lang.TypeReference; +import cn.hutool.core.util.ArrayUtil; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.ResolvableType; + +import java.lang.reflect.ParameterizedType; +import java.util.Arrays; +import java.util.Map; + + +public class LiteFlowSpringUtil implements BeanFactoryPostProcessor, ApplicationContextAware { + + /** + * "@PostConstruct"注解标记的类中,由于ApplicationContext还未加载,导致空指针
+ * 因此实现BeanFactoryPostProcessor注入ConfigurableListableBeanFactory实现bean的操作 + */ + private static ConfigurableListableBeanFactory beanFactory; + /** + * Spring应用上下文环境 + */ + private static ApplicationContext applicationContext; + + @SuppressWarnings("NullableProblems") + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + LiteFlowSpringUtil.beanFactory = beanFactory; + } + + @SuppressWarnings("NullableProblems") + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + LiteFlowSpringUtil.applicationContext = applicationContext; + } + + /** + * 获取{@link ApplicationContext} + * + * @return {@link ApplicationContext} + */ + public static ApplicationContext getApplicationContext() { + return applicationContext; + } + + /** + * 获取{@link ListableBeanFactory},可能为{@link ConfigurableListableBeanFactory} 或 {@link ApplicationContextAware} + * + * @return {@link ListableBeanFactory} + * @since 5.7.0 + */ + public static ListableBeanFactory getBeanFactory() { + final ListableBeanFactory factory = null == beanFactory ? applicationContext : beanFactory; + if (null == factory) { + throw new UtilException("No ConfigurableListableBeanFactory or ApplicationContext injected, maybe not in the Spring environment?"); + } + return factory; + } + + /** + * 获取{@link ConfigurableListableBeanFactory} + * + * @return {@link ConfigurableListableBeanFactory} + * @throws UtilException 当上下文非ConfigurableListableBeanFactory抛出异常 + * @since 5.7.7 + */ + public static ConfigurableListableBeanFactory getConfigurableBeanFactory() throws UtilException { + final ConfigurableListableBeanFactory factory; + if (null != beanFactory) { + factory = beanFactory; + } else if (applicationContext instanceof ConfigurableApplicationContext) { + factory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory(); + } else { + throw new UtilException("No ConfigurableListableBeanFactory from context!"); + } + return factory; + } + + //通过name获取 Bean. + + /** + * 通过name获取 Bean + * + * @param Bean类型 + * @param name Bean名称 + * @return Bean + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) { + return (T) getBeanFactory().getBean(name); + } + + /** + * 通过class获取Bean + * + * @param Bean类型 + * @param clazz Bean类 + * @return Bean对象 + */ + public static T getBean(Class clazz) { + return getBeanFactory().getBean(clazz); + } + + /** + * 通过name,以及Clazz返回指定的Bean + * + * @param bean类型 + * @param name Bean名称 + * @param clazz bean类型 + * @return Bean对象 + */ + public static T getBean(String name, Class clazz) { + return getBeanFactory().getBean(name, clazz); + } + + /** + * 通过类型参考返回带泛型参数的Bean + * + * @param reference 类型参考,用于持有转换后的泛型类型 + * @param Bean类型 + * @return 带泛型参数的Bean + * @since 5.4.0 + */ + @SuppressWarnings("unchecked") + public static T getBean(TypeReference reference) { + final ParameterizedType parameterizedType = (ParameterizedType) reference.getType(); + final Class rawType = (Class) parameterizedType.getRawType(); + final Class[] genericTypes = Arrays.stream(parameterizedType.getActualTypeArguments()).map(type -> (Class) type).toArray(Class[]::new); + final String[] beanNames = getBeanFactory().getBeanNamesForType(ResolvableType.forClassWithGenerics(rawType, genericTypes)); + return getBean(beanNames[0], rawType); + } + + /** + * 获取指定类型对应的所有Bean,包括子类 + * + * @param Bean类型 + * @param type 类、接口,null表示获取所有bean + * @return 类型对应的bean,key是bean注册的name,value是Bean + * @since 5.3.3 + */ + public static Map getBeansOfType(Class type) { + return getBeanFactory().getBeansOfType(type); + } + + /** + * 获取指定类型对应的Bean名称,包括子类 + * + * @param type 类、接口,null表示获取所有bean名称 + * @return bean名称 + * @since 5.3.3 + */ + public static String[] getBeanNamesForType(Class type) { + return getBeanFactory().getBeanNamesForType(type); + } + + /** + * 获取配置文件配置项的值 + * + * @param key 配置项key + * @return 属性值 + * @since 5.3.3 + */ + public static String getProperty(String key) { + if (null == applicationContext) { + return null; + } + return applicationContext.getEnvironment().getProperty(key); + } + + /** + * 获取应用程序名称 + * + * @return 应用程序名称 + * @since 5.7.12 + */ + public static String getApplicationName() { + return getProperty("spring.application.name"); + } + + /** + * 获取当前的环境配置,无配置返回null + * + * @return 当前的环境配置 + * @since 5.3.3 + */ + public static String[] getActiveProfiles() { + if (null == applicationContext) { + return null; + } + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * 获取当前的环境配置,当有多个环境配置时,只获取第一个 + * + * @return 当前的环境配置 + * @since 5.3.3 + */ + public static String getActiveProfile() { + final String[] activeProfiles = getActiveProfiles(); + return ArrayUtil.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; + } + + /** + * 动态向Spring注册Bean + *

+ * 由{@link org.springframework.beans.factory.BeanFactory} 实现,通过工具开放API + *

+ * 更新: shadow 2021-07-29 17:20:44 增加自动注入,修复注册bean无法反向注入的问题 + * + * @param Bean类型 + * @param beanName 名称 + * @param bean bean + * @author shadow + * @since 5.4.2 + */ + public static void registerBean(String beanName, T bean) { + final ConfigurableListableBeanFactory factory = getConfigurableBeanFactory(); + factory.autowireBean(bean); + factory.registerSingleton(beanName, bean); + } + + /** + * 注销bean + *

+ * 将Spring中的bean注销,请谨慎使用 + * + * @param beanName bean名称 + * @author shadow + * @since 5.7.7 + */ + public static void unregisterBean(String beanName) { + final ConfigurableListableBeanFactory factory = getConfigurableBeanFactory(); + if (factory instanceof DefaultSingletonBeanRegistry) { + DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) factory; + registry.destroySingleton(beanName); + } else { + throw new UtilException("Can not unregister bean, the factory is not a DefaultSingletonBeanRegistry!"); + } + } + + /** + * 发布事件 + * + * @param event 待发布的事件,事件必须是{@link ApplicationEvent}的子类 + * @since 5.7.12 + */ + public static void publishEvent(ApplicationEvent event) { + if (null != applicationContext) { + applicationContext.publishEvent(event); + } + } + + /** + * 发布事件 + * Spring 4.2+ 版本事件可以不再是{@link ApplicationEvent}的子类 + * + * @param event 待发布的事件 + * @since 5.7.21 + */ + public static void publishEvent(Object event) { + if (null != applicationContext) { + applicationContext.publishEvent(event); + } + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/def/SQLWithXmlELMultiLanguageSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/def/SQLWithXmlELMultiLanguageSpringbootTest.java new file mode 100644 index 000000000..c573fd243 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/def/SQLWithXmlELMultiLanguageSpringbootTest.java @@ -0,0 +1,34 @@ +package com.yomahub.liteflow.test.def; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import javax.annotation.Resource; + +@ExtendWith(SpringExtension.class) +@TestPropertySource(value = "classpath:/application-xml.properties") +@SpringBootTest(classes = SQLWithXmlELMultiLanguageSpringbootTest.class) +@EnableAutoConfiguration +@ComponentScan({"com.yomahub.liteflow.test.sql.cmp"}) +public class SQLWithXmlELMultiLanguageSpringbootTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + @Test + public void testMultiLanguage1() { + LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("x2[python脚本]==>x0[if 脚本]==>a==>b", response.getExecuteStepStrWithoutTime()); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/def/SQLWithXmlELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/def/SQLWithXmlELSpringbootTest.java new file mode 100644 index 000000000..87edf48cd --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/def/SQLWithXmlELSpringbootTest.java @@ -0,0 +1,101 @@ +package com.yomahub.liteflow.test.def; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.parser.sql.exception.ELSQLException; +import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.test.BaseTest; +import com.yomahub.liteflow.util.JsonUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import javax.annotation.Resource; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * @author tangkc + * @since 2.9.0 + */ +@ExtendWith(SpringExtension.class) +@TestPropertySource(value = "classpath:/application-xml.properties") +@SpringBootTest(classes = SQLWithXmlELSpringbootTest.class) +@EnableAutoConfiguration +@ComponentScan({"com.yomahub.liteflow.test.sql.cmp"}) +public class SQLWithXmlELSpringbootTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + @Test + public void testSQLWithXml() { + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + Assertions.assertEquals("a==>b==>c", response.getExecuteStepStr()); + + // 修改数据库 + changeData(); + + // 重新加载规则 + flowExecutor.reloadRule(); + Assertions.assertEquals("a==>c==>b", flowExecutor.execute2Resp("chain1", "arg").getExecuteStepStr()); + } + + @Test + public void testSQLWithScriptXml() { + LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("x0[if 脚本]==>a==>b", response.getExecuteStepStrWithoutTime()); + + // 修改数据库 + changeScriptData(); + // 重新加载规则 + flowExecutor.reloadRule(); + Assertions.assertEquals("x0[if 脚本]", flowExecutor.execute2Resp("chain3", "arg").getExecuteStepStr()); + } + + /** + * 修改数据库数据 + */ + private void changeData() { + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + SQLParserVO sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class); + Connection connection; + try { + connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), + sqlParserVO.getPassword()); + Statement statement = connection.createStatement(); + statement.executeUpdate("UPDATE EL_TABLE SET EL_DATA='THEN(a, c, b);' WHERE chain_name='chain1'"); + } catch (SQLException e) { + throw new ELSQLException(e.getMessage()); + } + } + + /** + * 修改数据库数据 + */ + private void changeScriptData() { + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + SQLParserVO sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class); + Connection connection; + try { + connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), + sqlParserVO.getPassword()); + Statement statement = connection.createStatement(); + statement.executeUpdate( + "UPDATE SCRIPT_NODE_TABLE SET SCRIPT_NODE_DATA='return false;' WHERE SCRIPT_NODE_ID='x0'"); + } catch (SQLException e) { + throw new ELSQLException(e.getMessage()); + } + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/resources/application-data-source-xml.properties b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/resources/application-data-source-xml.properties new file mode 100644 index 000000000..4458be232 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/resources/application-data-source-xml.properties @@ -0,0 +1,21 @@ +liteflow.rule-source-ext-data={\ + "applicationName":"demo",\ + "chainTableName":"EL_TABLE",\ + "chainApplicationNameField":"application_name",\ + "chainNameField":"chain_name",\ + "elDataField":"EL_DATA",\ + "scriptTableName":"script_node_table",\ + "scriptApplicationNameField":"application_name",\ + "scriptIdField":"script_node_id",\ + "scriptNameField":"script_node_name",\ + "scriptDataField":"script_node_data",\ + "scriptLanguageField":"script_language",\ + "scriptTypeField":"script_node_type"\ + } +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.url=jdbc:h2:mem:test_db;MODE=MySQL +spring.datasource.username=root +spring.datasource.password=123456 +spring.datasource.schema=classpath:/sql/schema.sql +spring.datasource.data=classpath:/sql/data.sql +spring.datasource.platform=h2 \ No newline at end of file