You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rocketmq.apache.org by ji...@apache.org on 2021/08/02 11:13:51 UTC
[rocketmq-streams] 06/18: add filter channel-http schedule modules
This is an automated email from the ASF dual-hosted git repository.
jinrongtong pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/rocketmq-streams.git
commit b3c42426876dfc7f4deaced0e64a43b00252d9a1
Author: arthur.lyj <wr...@vip.qq.com>
AuthorDate: Mon Aug 2 12:05:10 2021 +0800
add filter channel-http schedule modules
---
rocketmq-streams-channel-http/pom.xml | 30 ++
.../rocketmq-streams-channel-http.iml | 62 +++
.../rocketmq/streams/http/source/HttpSource.java | 157 ++++++
.../streams/http/source/server/HttpServer.java | 273 ++++++++++
.../http/source/server/HttpServerManager.java | 60 +++
.../streams/http/source/util/HttpUtil.java | 251 +++++++++
.../streams/http/source/http/HttpChannelTest.java | 45 ++
rocketmq-streams-filter/pom.xml | 31 ++
.../rocketmq-streams-filter.iml | 72 +++
.../rocketmq/streams/filter/FilterComponent.java | 115 ++++
.../streams/filter/builder/ExpressionBuilder.java | 409 ++++++++++++++
.../streams/filter/builder/RuleBuilder.java | 592 +++++++++++++++++++++
.../streams/filter/builder/RuleElementBuilder.java | 137 +++++
.../streams/filter/contants/RuleElementType.java | 43 ++
.../streams/filter/contants/RuleStatus.java | 98 ++++
.../streams/filter/context/ContextConfigure.java | 272 ++++++++++
.../streams/filter/context/RuleContext.java | 399 ++++++++++++++
.../streams/filter/context/RuleMessage.java | 48 ++
.../streams/filter/engine/IRuleEngine.java | 51 ++
.../rocketmq/streams/filter/engine/SplitFlow.java | 159 ++++++
.../filter/engine/impl/DefaultRuleEngine.java | 280 ++++++++++
.../filter/exception/RegexTimeoutException.java | 48 ++
.../filter/function/etl/RenameFunction.java | 37 ++
.../expression/AbstractExpressionFunction.java | 67 +++
.../function/expression/CompareFunction.java | 74 +++
.../ContainsCaseInsensitiveFunction.java | 74 +++
.../function/expression/ContainsFunction.java | 62 +++
.../streams/filter/function/expression/Equals.java | 166 ++++++
.../function/expression/ExpressionFunction.java | 33 ++
.../filter/function/expression/GreaterEquals.java | 163 ++++++
.../filter/function/expression/GreaterThan.java | 132 +++++
.../filter/function/expression/InFunction.java | 85 +++
.../filter/function/expression/InMetaData.java | 65 +++
.../function/expression/IpContainsFunction.java | 180 +++++++
.../filter/function/expression/IsNotNull.java | 50 ++
.../streams/filter/function/expression/IsNull.java | 49 ++
.../filter/function/expression/LessEquals.java | 118 ++++
.../filter/function/expression/LessThan.java | 119 +++++
.../filter/function/expression/LikeFunction.java | 167 ++++++
.../NotContainsCaseInsensitiveFunction.java | 76 +++
.../function/expression/NotContainsFunction.java | 63 +++
.../filter/function/expression/NotEquals.java | 169 ++++++
.../filter/function/expression/NotInFunction.java | 35 ++
.../function/expression/NotLikeFunction.java | 36 ++
.../NotRegexCaseInsensitiveFunction.java | 36 ++
.../function/expression/NotRegexFunction.java | 36 ++
.../expression/RegexCaseInsensitiveFunction.java | 41 ++
.../filter/function/expression/RegexFunction.java | 123 +++++
.../filter/function/expression/ScriptFunction.java | 145 +++++
.../filter/function/script/CaseFunction.java | 86 +++
.../rocketmq/streams/filter/monitor/Monitor.java | 160 ++++++
.../filter/monitor/contants/MonitorType.java | 40 ++
.../streams/filter/monitor/rule/ActionMonitor.java | 27 +
.../filter/monitor/rule/ExpressionMonitor.java | 27 +
.../filter/monitor/rule/MessageMonitor.java | 119 +++++
.../streams/filter/monitor/rule/NullMonitor.java | 55 ++
.../streams/filter/monitor/rule/RuleMonitor.java | 62 +++
.../streams/filter/monitor/rule/VarMonitor.java | 27 +
.../streams/filter/operator/FilterOperator.java | 31 ++
.../rocketmq/streams/filter/operator/Rule.java | 538 +++++++++++++++++++
.../streams/filter/operator/action/Action.java | 28 +
.../operator/action/IConfigurableAction.java | 41 ++
.../filter/operator/action/impl/ChannelAction.java | 69 +++
.../operator/action/impl/MetaDataAction.java | 185 +++++++
.../filter/operator/expression/Expression.java | 405 ++++++++++++++
.../operator/expression/ExpressionPerformance.java | 99 ++++
.../expression/ExpressionRelationParser.java | 106 ++++
.../expression/ExpressionRelationPaser.java | 107 ++++
.../operator/expression/GroupExpression.java | 146 +++++
.../expression/GroupExpressionManager.java | 86 +++
.../operator/expression/OptimizationRule.java | 28 +
.../operator/expression/RelationExpression.java | 347 ++++++++++++
.../operator/expression/SimpleExpression.java | 113 ++++
.../streams/filter/operator/var/ConstantVar.java | 118 ++++
.../streams/filter/operator/var/ContextVar.java | 85 +++
.../streams/filter/operator/var/InnerVar.java | 86 +++
.../rocketmq/streams/filter/operator/var/Var.java | 79 +++
.../optimization/EqualsExpressionOptimization.java | 39 ++
.../optimization/ExpressionOptimization.java | 144 +++++
.../optimization/IExpressionOptimization.java | 38 ++
.../optimization/LikeExpressionOptimization.java | 39 ++
.../optimization/OptimizationExpression.java | 71 +++
.../PiplineLogFingerprintAnalysis.java | 433 +++++++++++++++
.../optimization/RegexExpressionOptimization.java | 39 ++
.../streams/filter/service/IRuleEngineService.java | 50 ++
.../filter/service/impl/RuleEngineServiceImpl.java | 104 ++++
.../rocketmq/streams/filter/utils/IPUtil.java | 209 ++++++++
.../rocketmq/streams/filter/utils/RegexUtil.java | 211 ++++++++
.../streams/filter/FilterComponentTest.java | 36 ++
.../streams/filter/operator/ExpressionTest.java | 49 ++
.../rocketmq/streams/filter/operator/RuleTest.java | 40 ++
.../test/resources/credible/ChannelComponent.xml | 70 +++
.../resources/credible/credible-channel.properties | 12 +
.../test/resources/credible/credible.properties | 8 +
.../src/test/resources/log4j.xml | 20 +
rocketmq-streams-schedule/pom.xml | 33 ++
.../rocketmq-streams-schedule.iml | 65 +++
.../streams/schedule/ScheduleComponent.java | 65 +++
.../schedule/job/ConfigurableExecutorJob.java | 55 ++
.../streams/schedule/service/IScheduleService.java | 90 ++++
.../schedule/service/impl/ScheduleServiceImpl.java | 264 +++++++++
.../stream/schedule/ScheduleComponentTest.java | 62 +++
.../src/test/resources/log4j.xml | 20 +
103 files changed, 11599 insertions(+)
diff --git a/rocketmq-streams-channel-http/pom.xml b/rocketmq-streams-channel-http/pom.xml
new file mode 100644
index 0000000..05596b7
--- /dev/null
+++ b/rocketmq-streams-channel-http/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.rocketmq</groupId>
+ <artifactId>rocketmq-streams</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>rocketmq-streams-channel-http</artifactId>
+ <name>ROCKETMQ STREAMS :: channel-http</name>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.rocketmq</groupId>
+ <artifactId>rocketmq-streams-commons</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/rocketmq-streams-channel-http/rocketmq-streams-channel-http.iml b/rocketmq-streams-channel-http/rocketmq-streams-channel-http.iml
new file mode 100644
index 0000000..51a5150
--- /dev/null
+++ b/rocketmq-streams-channel-http/rocketmq-streams-channel-http.iml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
+ <output url="file://$MODULE_DIR$/target/classes" />
+ <output-test url="file://$MODULE_DIR$/target/test-classes" />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/target" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="rocketmq-streams-commons" />
+ <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.11" level="project" />
+ <orderEntry type="library" name="Maven: junit:junit:4.12" level="project" />
+ <orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
+ <orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.27" level="project" />
+ <orderEntry type="library" name="Maven: commons-logging:commons-logging:1.1" level="project" />
+ <orderEntry type="library" name="Maven: logkit:logkit:1.0.1" level="project" />
+ <orderEntry type="library" name="Maven: avalon-framework:avalon-framework:4.1.3" level="project" />
+ <orderEntry type="library" name="Maven: javax.servlet:servlet-api:2.3" level="project" />
+ <orderEntry type="library" name="Maven: log4j:log4j:1.2.17" level="project" />
+ <orderEntry type="library" name="Maven: com.google.code.gson:gson:2.8.5" level="project" />
+ <orderEntry type="library" name="Maven: com.google.auto.service:auto-service:1.0-rc5" level="project" />
+ <orderEntry type="library" name="Maven: com.google.auto.service:auto-service-annotations:1.0-rc5" level="project" />
+ <orderEntry type="library" name="Maven: com.google.auto:auto-common:0.10" level="project" />
+ <orderEntry type="library" name="Maven: com.google.guava:guava:25.1-jre" level="project" />
+ <orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
+ <orderEntry type="library" name="Maven: org.checkerframework:checker-qual:2.0.0" level="project" />
+ <orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.1.3" level="project" />
+ <orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.1" level="project" />
+ <orderEntry type="library" name="Maven: org.codehaus.mojo:animal-sniffer-annotations:1.14" level="project" />
+ <orderEntry type="library" name="Maven: com.lmax:disruptor:3.2.0" level="project" />
+ <orderEntry type="library" name="Maven: com.gliwka.hyperscan:hyperscan:5.4.0-2.0.0" level="project" />
+ <orderEntry type="library" name="Maven: com.gliwka.hyperscan:native:5.4.0-1.0.0" level="project" />
+ <orderEntry type="library" name="Maven: com.gliwka.hyperscan:native:linux-x86_64:5.4.0-1.0.0" level="project" />
+ <orderEntry type="library" name="Maven: com.gliwka.hyperscan:native:windows-x86_64:5.4.0-1.0.0" level="project" />
+ <orderEntry type="library" name="Maven: com.gliwka.hyperscan:native:macosx-x86_64:5.4.0-1.0.0" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp-platform:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:android-arm:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:android-arm64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:android-x86:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:android-x86_64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:ios-arm64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:ios-x86_64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:linux-armhf:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:linux-arm64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:linux-ppc64le:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:linux-x86:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:linux-x86_64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:macosx-x86_64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:windows-x86:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:windows-x86_64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: net.java.dev.jna:platform:3.5.2" level="project" />
+ <orderEntry type="library" name="Maven: net.java.dev.jna:jna:3.5.2" level="project" />
+ <orderEntry type="library" name="Maven: commons-io:commons-io:2.5" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.2" level="project" />
+ <orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.4" level="project" />
+ <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.9" level="project" />
+ </component>
+</module>
\ No newline at end of file
diff --git a/rocketmq-streams-channel-http/src/main/java/org/apache/rocketmq/streams/http/source/HttpSource.java b/rocketmq-streams-channel-http/src/main/java/org/apache/rocketmq/streams/http/source/HttpSource.java
new file mode 100644
index 0000000..6b7ea5e
--- /dev/null
+++ b/rocketmq-streams-channel-http/src/main/java/org/apache/rocketmq/streams/http/source/HttpSource.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.http.source;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.rocketmq.streams.http.source.server.HttpServerManager;
+import org.apache.rocketmq.streams.common.channel.source.AbstractUnreliableSource;
+import org.apache.rocketmq.streams.common.utils.IPUtil;
+import org.apache.rocketmq.streams.common.utils.StringUtil;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * receive http(s) post data
+ **/
+public class HttpSource extends AbstractUnreliableSource {
+ private static final Log LOG = LogFactory.getLog(HttpSource.class);
+ protected boolean isHttps = false;//发送的请求是否是https的
+
+ /**
+ * 下面三个可以都配置,或者只配置一个,只要有匹配上的请求,都会被这个channel处理
+ */
+
+ //消息发送源的ip或域名列表,支持ip范围-192.168.1.1-192.168.1.10,ip端-192.168.0.0/22,和正则表达式
+ protected List<String> ipList = new ArrayList<>();
+ protected List<String> rootPath = new ArrayList<>();//请求的跟path,支持正则和列表
+ protected String routeLabel;//可以在请求的参数中加入一个固定的参数,这个值是参数名称
+ protected String routeValue;
+
+ public boolean match(String hostOrIp, String query, String uri) {
+ String routePara = routeLabel + "=" + routeValue;
+ if (StringUtil.isNotEmpty(query) && query.contains(routePara)) {
+ return true;
+ }
+ if (StringUtil.isNotEmpty(uri)) {
+ for (String path : rootPath) {
+ if (uri.startsWith(path)) {
+ return true;
+ }
+ if (StringUtil.matchRegex(uri, path)) {
+ return true;
+ }
+ }
+ }
+ if (StringUtil.isNotEmpty(hostOrIp)) {
+ for (String ip : ipList) {
+ if (StringUtil.isEmpty(ip)) {
+ continue;
+ }
+ if (ip.equals(hostOrIp)) {
+ return true;
+ } else if (ip.contains("-")) {
+ boolean match = IPUtil.ipInSection(hostOrIp, ip);
+ if (match) {
+ return true;
+ }
+ } else if (ip.contains("/")) {
+ boolean match = IPUtil.isInRange(hostOrIp, ip);
+ if (match) {
+ return true;
+ }
+ } else {
+ boolean match = StringUtil.matchRegex(hostOrIp, ip);
+ if (match) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ protected boolean initConfigurable() {
+ boolean success = super.initConfigurable();
+ HttpServerManager.register(this, isHttps);
+ return success;
+ }
+
+ @Override
+ protected boolean startSource() {
+ HttpServerManager.startServer(isHttps);
+ return true;
+ }
+
+ public boolean isHttps() {
+ return isHttps;
+ }
+
+ public void setHttps(boolean https) {
+ isHttps = https;
+ }
+
+ public List<String> getIpList() {
+ return ipList;
+ }
+
+ public void setIpList(List<String> ipList) {
+ this.ipList = ipList;
+ }
+
+ public List<String> getRootPath() {
+ return rootPath;
+ }
+
+ public void setRootPath(List<String> rootPath) {
+ this.rootPath = rootPath;
+ }
+
+ public String getRouteLabel() {
+ return routeLabel;
+ }
+
+ public void setRouteLabel(String routeLabel) {
+ this.routeLabel = routeLabel;
+ }
+
+ public String getRouteValue() {
+ return routeValue;
+ }
+
+ public void setRouteValue(String routeValue) {
+ this.routeValue = routeValue;
+ }
+
+ public void addIps(String... ips) {
+ if (ips == null) {
+ return;
+ }
+ Collections.addAll(this.ipList, ips);
+ }
+
+ public void addPath(String... paths) {
+ if (paths == null) {
+ return;
+ }
+ Collections.addAll(this.rootPath, paths);
+ }
+}
\ No newline at end of file
diff --git a/rocketmq-streams-channel-http/src/main/java/org/apache/rocketmq/streams/http/source/server/HttpServer.java b/rocketmq-streams-channel-http/src/main/java/org/apache/rocketmq/streams/http/source/server/HttpServer.java
new file mode 100644
index 0000000..62a0ae8
--- /dev/null
+++ b/rocketmq-streams-channel-http/src/main/java/org/apache/rocketmq/streams/http/source/server/HttpServer.java
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.http.source.server;
+
+import com.alibaba.fastjson.JSONObject;
+
+import org.apache.rocketmq.streams.http.source.HttpSource;
+import org.apache.rocketmq.streams.common.channel.source.AbstractUnreliableSource;
+import org.apache.rocketmq.streams.common.configurable.annotation.ENVDependence;
+import org.apache.rocketmq.streams.common.interfaces.IStreamOperator;
+import org.apache.rocketmq.streams.common.context.AbstractContext;
+import org.apache.rocketmq.streams.common.context.IMessage;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsParameters;
+import com.sun.net.httpserver.HttpsServer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+public class HttpServer extends AbstractUnreliableSource {
+ private static final Log LOG = LogFactory.getLog(HttpServer.class);
+ private transient com.sun.net.httpserver.HttpServer server;
+ @ENVDependence
+ private int port = 8000;
+ @ENVDependence
+ private String serverIp = "localhost";
+ @ENVDependence
+ private int backlog = 10;
+ @ENVDependence
+ private int stopDelaySecond;
+ private boolean useHttps = false;
+
+ protected transient List<HttpSource> channels = new ArrayList<>();
+
+ public HttpServer() {
+ setJsonData(false);
+ setMsgIsJsonArray(false);
+ }
+
+ public void register(HttpSource channel) {
+ this.channels.add(channel);
+ }
+
+ @Override
+ protected boolean initConfigurable() {
+ try {
+
+ if (useHttps) {
+ HttpsServer httpsServer = HttpsServer.create(new InetSocketAddress(serverIp, port), backlog);
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ // initialise the keystore
+ char[] password = "password".toCharArray();
+ KeyStore ks = KeyStore.getInstance("JKS");
+ InputStream fis = this.getClass().getResourceAsStream("/testkey.jks");
+ ks.load(fis, password);
+ // setup the key manager factory
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(ks, password);
+ // setup the trust manager factory
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+ tmf.init(ks);
+ // setup the HTTPS context and parameters
+ sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext) {
+ @Override
+ public void configure(HttpsParameters params) {
+ try {
+ // initialise the SSL context
+ SSLContext context = getSSLContext();
+ SSLEngine engine = context.createSSLEngine();
+ params.setNeedClientAuth(false);
+ params.setCipherSuites(engine.getEnabledCipherSuites());
+ params.setProtocols(engine.getEnabledProtocols());
+
+ // Set the SSL parameters
+ SSLParameters sslParameters = context.getSupportedSSLParameters();
+ params.setSSLParameters(sslParameters);
+
+ } catch (Exception ex) {
+ LOG.error("Failed to create HTTPS port", ex);
+ }
+ }
+ });
+ server = httpsServer;
+ } else {
+ server = com.sun.net.httpserver.HttpServer.create(new InetSocketAddress(serverIp, port), backlog);
+ }
+
+ } catch (IOException e) {
+ // System.out.println(e);
+ LOG.error("http channel init get io exception", e);
+ return false;
+ } catch (NoSuchAlgorithmException e) {
+ // System.out.println(e);
+ LOG.error("http channel init https ssl context exception", e);
+ return false;
+ } catch (Exception e) {
+ // System.out.println(e);
+ LOG.error("http channel init http cert exception", e);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean startSource() {
+ ExecutorService cachedThreadPool = new ThreadPoolExecutor(maxThread, maxThread,
+ 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<Runnable>(1000));
+ server.setExecutor(cachedThreadPool);
+ server.createContext("/", new DataHandler());
+ setReceiver((message, context) -> {
+ String clientIp = message.getMessageBody().getString("clientIp");
+ String query = message.getMessageBody().getString("query");
+ String uri = message.getMessageBody().getString("uri");
+ List<HttpSource> destroyChannels = new ArrayList<>();
+ for (HttpSource channel : channels) {
+ if (channel.match(clientIp, query, uri)) {
+ if (channel.isDestroy()) {
+ destroyChannels.add(channel);
+ continue;
+ }
+ channel.doReceiveMessage(message.getMessageBody());
+ }
+ }
+ for (HttpSource httpChannel : destroyChannels) {
+ channels.remove(httpChannel);
+ }
+ return message;
+ });
+ server.start();
+ if (isUseHttps()) {
+ LOG.info("https server is start");
+ } else {
+ LOG.info("http server is start");
+ }
+
+ return true;
+ }
+
+ public boolean isUseHttps() {
+ return useHttps;
+ }
+
+ public void setUseHttps(boolean useHttps) {
+ this.useHttps = useHttps;
+ }
+
+ public String getServerIp() {
+ return serverIp;
+ }
+
+ public void setServerIp(String serverIp) {
+ this.serverIp = serverIp;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public int getBacklog() {
+ return backlog;
+ }
+
+ public void setBacklog(int backlog) {
+ this.backlog = backlog;
+ }
+
+ public int getStopDelaySecond() {
+ return stopDelaySecond;
+ }
+
+ public void setStopDelaySecond(int delaySecond) {
+ this.stopDelaySecond = delaySecond;
+ }
+
+ private class DataHandler implements HttpHandler {
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ OutputStream os = exchange.getResponseBody();
+ InputStream in = exchange.getRequestBody();
+ try {
+ JSONObject jsonObject = createHttpHeader(exchange);
+ jsonObject.put("query", exchange.getRequestURI().getQuery());
+ String data = getContent(in);
+ jsonObject.put(IMessage.DATA_KEY, data);
+
+ doReceiveMessage(jsonObject);
+ String response = "{\"code\": \"200\", \"data\" :\"received\", \"message\" :\"\"}";
+ exchange.sendResponseHeaders(200, 0);
+ os.write(response.getBytes());
+ os.flush();
+ } catch (Exception e) {
+ LOG.error("");
+ } finally {
+ if (os != null) {
+ os.close();
+ }
+ if (in != null) {
+ in.close();
+ }
+ }
+
+ }
+ }
+
+ protected JSONObject createHttpHeader(HttpExchange exchange) {
+ JSONObject header = new JSONObject();
+ header.put("clientIp", exchange.getRemoteAddress().getAddress().getHostAddress());
+ header.put("uri", exchange.getRequestURI().getPath());
+ header.put("User-agent", exchange.getRequestHeaders().getFirst("User-agent"));
+ header.put("Content-type", exchange.getRequestHeaders().getFirst("Content-type"));
+ header.put("Host", exchange.getRequestHeaders().getFirst("Host"));
+ header.put("method", exchange.getRequestMethod());
+ return header;
+ }
+
+ /**
+ * 从请求里获取内容
+ *
+ * @param in
+ * @return
+ * @throws IOException
+ */
+ protected String getContent(InputStream in) throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ String line;
+ StringBuilder stringBuilder = new StringBuilder();
+ while ((line = reader.readLine()) != null) {
+ stringBuilder.append(line);
+ }
+ return stringBuilder.toString();
+ }
+}
\ No newline at end of file
diff --git a/rocketmq-streams-channel-http/src/main/java/org/apache/rocketmq/streams/http/source/server/HttpServerManager.java b/rocketmq-streams-channel-http/src/main/java/org/apache/rocketmq/streams/http/source/server/HttpServerManager.java
new file mode 100644
index 0000000..ca878e3
--- /dev/null
+++ b/rocketmq-streams-channel-http/src/main/java/org/apache/rocketmq/streams/http/source/server/HttpServerManager.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.http.source.server;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.rocketmq.streams.http.source.HttpSource;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * receive http(s) post data
+ **/
+public class HttpServerManager {
+ private static final Log LOG = LogFactory.getLog(HttpServerManager.class);
+ protected transient static HttpServer HTTP_SERVER = new HttpServer();//http server,全局只有一个
+ protected transient static HttpServer HTTPS_SERVER = new HttpServer();//https server 全局只有一个
+ protected transient static AtomicBoolean isHttpStart = new AtomicBoolean(false);//只启动一次
+ protected transient static AtomicBoolean isHttpsStart = new AtomicBoolean(false);//只启动一次
+
+ public static void register(HttpSource channel, boolean isHttps) {
+ if (isHttps) {
+ HTTPS_SERVER.register(channel);
+ } else {
+ HTTP_SERVER.register(channel);
+ }
+ }
+
+ public static void startServer(boolean isHttps) {
+ if (isHttps) {
+ if (isHttpsStart.compareAndSet(false, true)) {
+ HTTPS_SERVER.setUseHttps(true);
+ HTTPS_SERVER.setPort(443);
+ HTTPS_SERVER.init();
+ HTTPS_SERVER.startSource();
+ }
+ } else {
+ if (isHttpStart.compareAndSet(false, true)) {
+ HTTP_SERVER.setPort(8000);
+ HTTP_SERVER.setUseHttps(false);
+ HTTP_SERVER.init();
+ HTTP_SERVER.startSource();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/rocketmq-streams-channel-http/src/main/java/org/apache/rocketmq/streams/http/source/util/HttpUtil.java b/rocketmq-streams-channel-http/src/main/java/org/apache/rocketmq/streams/http/source/util/HttpUtil.java
new file mode 100644
index 0000000..92dddf8
--- /dev/null
+++ b/rocketmq-streams-channel-http/src/main/java/org/apache/rocketmq/streams/http/source/util/HttpUtil.java
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.http.source.util;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.Consts;
+import org.apache.http.Header;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPatch;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.config.ConnectionConfig;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.ssl.SSLContexts;
+import org.apache.http.util.EntityUtils;
+
+public class HttpUtil {
+
+ public static final int NORMAL_STATUES = 200;
+ public static final String CHARSET = "UTF-8";
+ public static final int TIMOUT = 10000;
+ public static final int CONNECT_TIMOUT = 10000;
+
+ private static CloseableHttpClient httpclient;
+
+ static {
+ RequestConfig.Builder configBuilder = RequestConfig.custom();
+ configBuilder.setConnectionRequestTimeout(CONNECT_TIMOUT);
+ configBuilder.setConnectTimeout(CONNECT_TIMOUT);
+ configBuilder.setSocketTimeout(TIMOUT);
+ SSLConnectionSocketFactory sslsf = null;
+ try {
+ SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(new TrustStrategy() {
+ @Override
+ public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ return true;
+ }
+ }).build();
+ sslsf = new SSLConnectionSocketFactory(sslcontext, new HostnameVerifier() {
+ @Override
+ public boolean verify(String s, SSLSession sslSession) {
+ return true;
+ }
+ });
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ ConnectionConfig connectionConfig = ConnectionConfig.custom().setCharset(Consts.UTF_8).build();
+ Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create().register("https",
+ sslsf).register("http",
+ new PlainConnectionSocketFactory()).build();
+ PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
+ connManager.setDefaultConnectionConfig(connectionConfig);
+ connManager.setMaxTotal(500);
+ connManager.setDefaultMaxPerRoute(50);
+ HttpClientBuilder clientBuilder = HttpClients.custom();
+
+ clientBuilder.setDefaultRequestConfig(configBuilder.build());
+ clientBuilder.setSSLSocketFactory(sslsf);
+ clientBuilder.setConnectionManager(connManager);
+
+ httpclient = clientBuilder.build();
+ }
+
+ public static String getContent(String url) {
+ return getContent(url, null);
+ }
+
+ public static String getContent(String url, Header... headers) {
+ try {
+ String content = EntityUtils.toString(get(url, headers).getEntity(), CHARSET);
+ return content;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static String deleteContent(String url) {
+ return deleteContent(url, null);
+ }
+
+ public static String deleteContent(String url, Header... headers) {
+ try {
+ String content = EntityUtils.toString(delete(url, headers).getEntity(), CHARSET);
+ return content;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static String postContent(String url, String body) {
+ return postContent(url, body, null);
+ }
+
+ public static String patchContent(String url, String body) {
+ return patchContent(url, body, null);
+ }
+
+ public static String postContent(String url, String body, Header... headers) {
+ try {
+ String content = EntityUtils.toString(post(url, body, headers).getEntity(), CHARSET);
+ return content;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static String patchContent(String url, String body, Header... headers) {
+ try {
+ String content = EntityUtils.toString(patch(url, body, headers).getEntity(), CHARSET);
+ return content;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static CloseableHttpResponse get(String url) {
+ return get(url, null);
+ }
+
+ public static CloseableHttpResponse delete(String url) {
+ return delete(url, null);
+ }
+
+ public static CloseableHttpResponse get(String url, Header... headers) {
+ try {
+ HttpGet httpGet = new HttpGet(url);
+ if (headers != null && headers.length > 0) {
+ for (Header header : headers) {
+ httpGet.addHeader(header);
+ }
+ }
+ CloseableHttpResponse response = httpclient.execute(httpGet);
+ return response;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static CloseableHttpResponse delete(String url, Header... headers) {
+ try {
+ HttpDelete httpDelete = new HttpDelete(url);
+ if (headers != null && headers.length > 0) {
+ for (Header header : headers) {
+ httpDelete.addHeader(header);
+ }
+ }
+ CloseableHttpResponse response = httpclient.execute(httpDelete);
+ return response;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static CloseableHttpResponse post(String url, String body) {
+ return post(url, body, null);
+ }
+
+ public static CloseableHttpResponse patch(String url, String body) {
+ return patch(url, body, null);
+ }
+
+ public static CloseableHttpResponse post(String url, String body, Header... headers) {
+ try {
+ HttpPost httpPost = new HttpPost(url);
+ StringEntity stringEntity = new StringEntity(body, CHARSET);
+ stringEntity.setContentEncoding(CHARSET);
+ stringEntity.setContentType("application/json");
+ httpPost.setEntity(stringEntity);
+ if (headers != null && headers.length > 0) {
+ for (Header header : headers) {
+ httpPost.addHeader(header);
+ }
+ }
+ CloseableHttpResponse response = httpclient.execute(httpPost);
+ return response;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static CloseableHttpResponse patch(String url, String body, Header... headers) {
+ try {
+ HttpPatch httpPatch = new HttpPatch(url);
+ StringEntity stringEntity = new StringEntity(body, CHARSET);
+ stringEntity.setContentEncoding(CHARSET);
+ stringEntity.setContentType("application/json");
+ httpPatch.setEntity(stringEntity);
+ if (headers != null && headers.length > 0) {
+ for (Header header : headers) {
+ httpPatch.addHeader(header);
+ }
+ }
+ CloseableHttpResponse response = httpclient.execute(httpPatch);
+ return response;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static String replaceSpace(String str) {
+ if (StringUtils.isNotBlank(str)) {
+ return str.replace(" ", "").replace("\r", "").replace("\n", "").replace("\r\n", "");
+ }
+ return str;
+ }
+}
+
diff --git a/rocketmq-streams-channel-http/src/test/java/org/apache/rocketmq/streams/http/source/http/HttpChannelTest.java b/rocketmq-streams-channel-http/src/test/java/org/apache/rocketmq/streams/http/source/http/HttpChannelTest.java
new file mode 100644
index 0000000..1e6341f
--- /dev/null
+++ b/rocketmq-streams-channel-http/src/test/java/org/apache/rocketmq/streams/http/source/http/HttpChannelTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.http.source.http;
+
+import org.apache.rocketmq.streams.common.channel.source.ISource;
+import org.apache.rocketmq.streams.common.utils.ThreadUtil;
+import org.apache.rocketmq.streams.http.source.HttpSource;
+import org.junit.Test;
+
+public class HttpChannelTest {
+
+ @Test
+ public void startHttpChannel() {
+ ISource channel = create();
+ channel.start((message, context) -> {
+ System.out.println("receive message " + message.getMessageBody());
+ return message;
+ });
+ while (true) {
+ ThreadUtil.sleep(1000);
+ }
+ }
+
+ private ISource create() {
+ HttpSource httpChannel = new HttpSource();
+ httpChannel.addPath("/chris/yuanxiaodong");
+ httpChannel.setHttps(false);
+ httpChannel.init();
+ return httpChannel;
+ }
+}
\ No newline at end of file
diff --git a/rocketmq-streams-filter/pom.xml b/rocketmq-streams-filter/pom.xml
new file mode 100755
index 0000000..6e7d41d
--- /dev/null
+++ b/rocketmq-streams-filter/pom.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.rocketmq</groupId>
+ <artifactId>rocketmq-streams</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>rocketmq-streams-filter</artifactId>
+ <name>ROCKETMQ STREAMS :: filter</name>
+ <packaging>jar</packaging>
+ <properties>
+ <file_encoding>UTF-8</file_encoding>
+ <project.build.sourceEncoding>${file_encoding}</project.build.sourceEncoding>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.rocketmq</groupId>
+ <artifactId>rocketmq-streams-script</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.rocketmq</groupId>
+ <artifactId>rocketmq-streams-db-operator</artifactId>
+ </dependency>
+ </dependencies>
+
+
+</project>
diff --git a/rocketmq-streams-filter/rocketmq-streams-filter.iml b/rocketmq-streams-filter/rocketmq-streams-filter.iml
new file mode 100644
index 0000000..70f1781
--- /dev/null
+++ b/rocketmq-streams-filter/rocketmq-streams-filter.iml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
+ <output url="file://$MODULE_DIR$/target/classes" />
+ <output-test url="file://$MODULE_DIR$/target/test-classes" />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
+ <excludeFolder url="file://$MODULE_DIR$/target" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="rocketmq-streams-script" />
+ <orderEntry type="module" module-name="rocketmq-streams-commons" />
+ <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.11" level="project" />
+ <orderEntry type="library" name="Maven: junit:junit:4.12" level="project" />
+ <orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
+ <orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.27" level="project" />
+ <orderEntry type="library" name="Maven: commons-logging:commons-logging:1.1" level="project" />
+ <orderEntry type="library" name="Maven: logkit:logkit:1.0.1" level="project" />
+ <orderEntry type="library" name="Maven: avalon-framework:avalon-framework:4.1.3" level="project" />
+ <orderEntry type="library" name="Maven: javax.servlet:servlet-api:2.3" level="project" />
+ <orderEntry type="library" name="Maven: commons-io:commons-io:2.5" level="project" />
+ <orderEntry type="library" name="Maven: log4j:log4j:1.2.17" level="project" />
+ <orderEntry type="library" name="Maven: com.google.code.gson:gson:2.8.5" level="project" />
+ <orderEntry type="library" name="Maven: com.google.auto.service:auto-service:1.0-rc5" level="project" />
+ <orderEntry type="library" name="Maven: com.google.auto.service:auto-service-annotations:1.0-rc5" level="project" />
+ <orderEntry type="library" name="Maven: com.google.auto:auto-common:0.10" level="project" />
+ <orderEntry type="library" name="Maven: com.google.guava:guava:25.1-jre" level="project" />
+ <orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
+ <orderEntry type="library" name="Maven: org.checkerframework:checker-qual:2.0.0" level="project" />
+ <orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.1.3" level="project" />
+ <orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.1" level="project" />
+ <orderEntry type="library" name="Maven: org.codehaus.mojo:animal-sniffer-annotations:1.14" level="project" />
+ <orderEntry type="library" name="Maven: com.lmax:disruptor:3.2.0" level="project" />
+ <orderEntry type="library" name="Maven: com.gliwka.hyperscan:hyperscan:5.4.0-2.0.0" level="project" />
+ <orderEntry type="library" name="Maven: com.gliwka.hyperscan:native:5.4.0-1.0.0" level="project" />
+ <orderEntry type="library" name="Maven: com.gliwka.hyperscan:native:linux-x86_64:5.4.0-1.0.0" level="project" />
+ <orderEntry type="library" name="Maven: com.gliwka.hyperscan:native:windows-x86_64:5.4.0-1.0.0" level="project" />
+ <orderEntry type="library" name="Maven: com.gliwka.hyperscan:native:macosx-x86_64:5.4.0-1.0.0" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp-platform:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:android-arm:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:android-arm64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:android-x86:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:android-x86_64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:ios-arm64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:ios-x86_64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:linux-armhf:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:linux-arm64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:linux-ppc64le:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:linux-x86:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:linux-x86_64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:macosx-x86_64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:windows-x86:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bytedeco:javacpp:windows-x86_64:1.5.4" level="project" />
+ <orderEntry type="library" name="Maven: net.java.dev.jna:platform:3.5.2" level="project" />
+ <orderEntry type="library" name="Maven: net.java.dev.jna:jna:3.5.2" level="project" />
+ <orderEntry type="library" name="Maven: io.krakens:java-grok:0.1.9" level="project" />
+ <orderEntry type="library" name="Maven: org.codehaus.groovy:groovy-all:2.1.8" level="project" />
+ <orderEntry type="library" name="Maven: org.python:jython-standalone:2.7.0" level="project" />
+ <orderEntry type="module" module-name="rocketmq-streams-db-operator" />
+ <orderEntry type="module" module-name="rocketmq-streams-configurable" />
+ <orderEntry type="module" module-name="rocketmq-streams-serviceloader" />
+ <orderEntry type="library" name="Maven: org.springframework:spring-jdbc:3.2.13.RELEASE" level="project" />
+ <orderEntry type="library" name="Maven: org.springframework:spring-beans:3.2.13.RELEASE" level="project" />
+ <orderEntry type="library" name="Maven: org.springframework:spring-core:3.2.13.RELEASE" level="project" />
+ <orderEntry type="library" name="Maven: org.springframework:spring-tx:3.2.13.RELEASE" level="project" />
+ <orderEntry type="library" name="Maven: mysql:mysql-connector-java:5.1.40" level="project" />
+ </component>
+</module>
\ No newline at end of file
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/FilterComponent.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/FilterComponent.java
new file mode 100644
index 0000000..37384ef
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/FilterComponent.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter;
+
+import java.util.List;
+import java.util.Properties;
+
+import com.alibaba.fastjson.JSONObject;
+
+import org.apache.rocketmq.streams.filter.context.ContextConfigure;
+import org.apache.rocketmq.streams.filter.builder.RuleBuilder;
+import org.apache.rocketmq.streams.filter.context.RuleMessage;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.service.IRuleEngineService;
+import org.apache.rocketmq.streams.filter.service.impl.RuleEngineServiceImpl;
+import org.apache.rocketmq.streams.common.component.AbstractComponent;
+import org.apache.rocketmq.streams.common.component.ComponentCreator;
+import org.apache.rocketmq.streams.common.component.IgnoreNameSpace;
+import org.apache.rocketmq.streams.common.context.AbstractContext;
+import org.apache.rocketmq.streams.common.context.IMessage;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * 通过表达式,做过滤 表达式的表达形式(varName,functionName,value)&varName,functionName,datatypeName,value)
+ */
+public class FilterComponent extends AbstractComponent<IRuleEngineService> implements IRuleEngineService, IgnoreNameSpace {
+
+ private static final Log LOG = LogFactory.getLog(FilterComponent.class);
+
+ private static final FilterComponent filterComponent = ComponentCreator.getComponent(null, FilterComponent.class);
+
+ protected RuleEngineServiceImpl ruleEngineService;
+
+ public FilterComponent() {
+ }
+
+ public static FilterComponent getInstance() {
+ return filterComponent;
+ }
+
+ @Override
+ public boolean stop() {
+ return true;
+ }
+
+ @Override
+ public IRuleEngineService getService() {
+ return this.ruleEngineService;
+ }
+
+ @Override
+ protected boolean startComponent(String namespace) {
+ return true;
+ }
+
+ public static RuleEngineServiceImpl createRuleEngineService(Properties properties) {
+ RuleEngineServiceImpl ruleEngineService = new RuleEngineServiceImpl();
+ ContextConfigure config = new ContextConfigure(properties);
+ ruleEngineService.initRuleContext(config);
+ return ruleEngineService;
+ }
+
+ @Override
+ protected boolean initProperties(Properties properties) {
+ if (ruleEngineService != null) {
+ return true;
+ }
+ RuleEngineServiceImpl ruleEngineService = createRuleEngineService(properties);
+
+ this.ruleEngineService = ruleEngineService;
+ return true;
+ }
+
+ @Override
+ public Rule createRule(String namespace, String ruleName, String expressionStr, String... msgMetaInfo) {
+ RuleBuilder ruleCreator = new RuleBuilder(namespace, ruleName, expressionStr, msgMetaInfo);
+ Rule rule = ruleCreator.generateRule(null);
+ return rule;
+ }
+
+ @Override
+ public List<Rule> excuteRule(JSONObject message, Rule... rules) {
+ return ruleEngineService.excuteRule(message, rules);
+ }
+
+ @Override
+ public List<Rule> executeRule(IMessage message, AbstractContext context, Rule... rules) {
+ return ruleEngineService.executeRule(message, context, rules);
+ }
+
+ @Override
+ public List<Rule> excuteRule(RuleMessage message, Rule... rules) {
+ return ruleEngineService.excuteRule(message, rules);
+ }
+
+ @Override
+ public List<Rule> excuteRule(JSONObject message, List<Rule> rules) {
+ return ruleEngineService.excuteRule(message, rules);
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/builder/ExpressionBuilder.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/builder/ExpressionBuilder.java
new file mode 100644
index 0000000..c3356e4
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/builder/ExpressionBuilder.java
@@ -0,0 +1,409 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.builder;
+
+import com.alibaba.fastjson.JSONObject;
+
+import org.apache.rocketmq.streams.filter.optimization.ExpressionOptimization;
+import org.apache.rocketmq.streams.common.model.NameCreator;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.expression.ExpressionRelationParser;
+import org.apache.rocketmq.streams.filter.operator.expression.RelationExpression;
+import org.apache.rocketmq.streams.filter.operator.expression.SimpleExpression;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.common.cache.softreference.ICache;
+import org.apache.rocketmq.streams.common.cache.softreference.impl.SoftReferenceCache;
+import org.apache.rocketmq.streams.common.configurable.IConfigurable;
+import org.apache.rocketmq.streams.common.datatype.ListDataType;
+import org.apache.rocketmq.streams.common.utils.ContantsUtil;
+import org.apache.rocketmq.streams.common.utils.DataTypeUtil;
+import org.apache.rocketmq.streams.common.utils.MapKeyUtil;
+import org.apache.rocketmq.streams.common.utils.StringUtil;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 必须先初始化组件后才可以使用
+ */
+public class ExpressionBuilder {
+ private static ListDataType listDataType = new ListDataType(DataTypeUtil.getDataTypeFromClass(String.class));
+ private static ICache<String, Rule> cache = new SoftReferenceCache<>();
+
+ /**
+ * 根据表达式,创建一个规则,规则本身可执行
+ *
+ * @param namespace
+ * @param expressionStr
+ * @return
+ */
+ public static Rule createRule(String namespace, String ruleName, String expressionStr) {
+ Rule rule = null;
+ String key = MapKeyUtil.createKey(namespace, expressionStr);
+ //rule = sinkcache.get(key);
+ if (rule != null) {
+ return rule;
+
+ }
+ List<Expression> expressions = new ArrayList<>();
+ List<RelationExpression> relationExpressions = new ArrayList<>();
+ Expression expression = createExpression(namespace, ruleName, expressionStr, expressions, relationExpressions);
+ ExpressionOptimization expressionOptimization = new ExpressionOptimization(expression, expressions, relationExpressions);
+ List<Expression> expressionList = expressionOptimization.optimizate();
+ rule = createRule(namespace, ruleName, expression, expressionList);
+ cache.put(key, rule);
+ return rule;
+ }
+
+ /**
+ * 可以简化表达式格式(varName,function,value)&((varName,function,datatype,value)|(varName,function,datatype,value))
+ *
+ * @param expressionStr
+ * @param msg
+ * @return
+ */
+ public static boolean executeExecute(String namespace, String expressionStr, JSONObject msg) {
+ Rule rule = null;
+ String key = expressionStr;
+ rule = cache.get(key);
+ if (rule == null) {
+ rule = createRule(namespace, Rule.class.getSimpleName(), expressionStr);
+ cache.put(key, rule);
+ }
+ return rule.execute(msg);
+
+ }
+
+ public static boolean executeExecute(Expression expression, JSONObject msg) {
+ Rule rule = null;
+ if (expression.getConfigureName() == null) {
+ expression.setConfigureName(expression.getVarName());
+ }
+ String key = MapKeyUtil.createKey(expression.getNameSpace(), expression.toString());
+ rule = cache.get(key);
+ if (rule == null) {
+ rule = createRule(expression.getNameSpace(), Rule.class.getSimpleName(), expression);
+ cache.put(key, rule);
+ }
+ return rule.execute(msg);
+ }
+
+ /**
+ * 创建一个表达式,并返回最顶层的表达式,中间的子表达式,全部relation表达式
+ *
+ * @param expressionStr
+ * @return
+ */
+ public static Expression createExpression(String namespace, String ruleName, String expressionStr, List<Expression> expressions,
+ List<RelationExpression> relationExpressions) {
+ String relationStr = parseExpression(namespace, ruleName, expressionStr, expressions);
+ if (expressions != null && expressions.size() == 1) {
+ if (expressions.get(0).getConfigureName().equals(relationStr)) {
+ return expressions.get(0);
+ }
+ }
+ Expression relationExpression =
+ ExpressionRelationParser.createRelations(namespace, ruleName, relationStr, relationExpressions);
+ if (relationExpressions != null) {
+ for (RelationExpression relation : relationExpressions) {
+ relation.setDataType(listDataType);
+ }
+ }
+ return relationExpression;
+ }
+
+ /**
+ * 创建表达式,并做初步的优化,把同关系的多层relation,拉平。如((a,==,b)&(c,==,d))&(e,==,f)=>(a,==,b)&(c,==,d)&(e,==,f)
+ *
+ * @param expressionStr
+ * @param expressions
+ * @param relationExpressions
+ * @return
+ */
+ public static Expression createOptimizationExpression(String namespace, String ruleName, String expressionStr, List<Expression> expressions,
+ List<RelationExpression> relationExpressions) {
+ Expression expression = createExpression(namespace, ruleName, expressionStr, expressions, relationExpressions);
+ ExpressionOptimization expressionOptimization = new ExpressionOptimization(expression, expressions, relationExpressions);
+ List<Expression> list = expressionOptimization.optimizate();
+ if (list == null || list.size() == 0) {
+ return expression;
+ }
+ expressions.clear();
+ relationExpressions.clear();
+ for (Expression express : list) {
+ if (RelationExpression.class.isInstance(express)) {
+ relationExpressions.add((RelationExpression)express);
+ } else {
+ expressions.add(express);
+ }
+ }
+ return expression;
+ }
+
+ public static Rule createRule(String namespace, String ruleName, Expression expression,
+ List<? extends Expression>... expressionLists) {
+ RuleBuilder ruleCreator = new RuleBuilder(expression.getNameSpace(), ruleName);
+ //RuleContext ruleContext=new RuleContext(msg,ruleCreator.createRule());
+ Rule rule = ruleCreator.getRule();
+ if (expressionLists != null) {
+ for (List<? extends Expression> expressions : expressionLists) {
+ if (expressions == null || expressions.size() == 0) {
+ continue;
+ }
+ for (Expression expression1 : expressions) {
+ createExpression(expression1, ruleCreator);
+ }
+ addConfigurable2Map(rule.getExpressionMap(), ruleCreator.getExpressionList());
+ }
+ }
+ createExpression(expression, ruleCreator);
+
+ addConfigurable2Map(rule.getVarMap(), ruleCreator.getVarList());
+ addConfigurable2Map(rule.getExpressionMap(), ruleCreator.getExpressionList());
+ addConfigurable2Map(rule.getMetaDataMap(), ruleCreator.getMetaDataList());
+ addConfigurable2Map(rule.getDataSourceMap(), ruleCreator.getDataSourceList());
+ addConfigurable2Map(rule.getActionMap(), ruleCreator.getActionList());
+ ruleCreator.setRootExpression(expression);
+ rule = ruleCreator.createRule();
+ ruleCreator.getMetaData().toObject(ruleCreator.getMetaData().toJson());//metadata的一个bug,如果不做这步,map为空。后续版本修复后可以去掉
+ return rule;
+ }
+
+ /**
+ * 一个表达式,需要的metafield,变量,value
+ *
+ * @param expression
+ * @param ruleCreator
+ */
+ private static void createExpression(Expression expression, RuleBuilder ruleCreator) {
+ if (RelationExpression.class.isInstance(expression)) {
+ createRelationExpression(expression, ruleCreator);
+ } else {
+ createSingleExpression(expression, ruleCreator);
+ }
+ }
+
+ /**
+ * @param expression
+ * @param ruleCreator
+ */
+ private static void createSingleExpression(Expression expression, RuleBuilder ruleCreator) {
+ String varName = expression.getVarName();
+ ruleCreator.addVarAndMetaDataField(varName);
+ ruleCreator.addExpression(expression);
+ }
+
+ private static void createRelationExpression(Expression expression, RuleBuilder ruleCreator) {
+ if (!RelationExpression.class.isInstance(expression)) {
+ return;
+ }
+ ruleCreator.addExpression(expression);
+ }
+
+ private static <T extends IConfigurable> void addConfigurable2Map(Map<String, T> map, List<T> list) {
+ if (map == null || list == null) {
+ return;
+ }
+ for (T t : list) {
+ if (Var.class.isInstance(t)) {
+ map.put(((Var)t).getVarName(), t);
+ } else {
+ map.put(t.getConfigureName(), t);
+ }
+
+ }
+ }
+
+ /**
+ * 把一个表达式组合,进行解析,返回一个关系字符串和一组表达式子列表expressions
+ *
+ * @param namespace
+ * @param expressionStr
+ * @param expressions
+ * @return
+ */
+ private static String parseExpression(String namespace, String ruleName, String expressionStr, List<Expression> expressions) {
+ return parseExpression(namespace, ruleName, expressionStr, expressions, ruleExpressionCreator);
+ }
+
+ public static void main(String[] args) {
+
+ }
+
+ /**
+ * 把一个表达式组合,进行解析,返回一个关系字符串和一组表达式子列表expressions
+ *
+ * @param namespace
+ * @param expressionStr
+ * @param expressions
+ * @return
+ */
+ public static String parseExpression(String namespace, String ruleName, String expressionStr, List<Expression> expressions,
+ IRuleExpressionCreator creator) {
+ if (StringUtil.isEmpty(expressionStr)) {
+ return null;
+ }
+
+ expressionStr = expressionStr.replaceAll("\r\n", "");
+ expressionStr = expressionStr.replaceAll("\n", "");
+
+ expressionStr = ContantsUtil.replaceSpeciaSign(expressionStr);
+
+ Map<String, String> flag2ExpressionStr = new HashMap<>();
+ boolean containsContant = ContantsUtil.containContant(expressionStr);
+ if (containsContant) {
+ expressionStr = ContantsUtil.doConstantReplace(expressionStr, flag2ExpressionStr, 1);
+ }
+ String relationStr = expressionStr;
+ NameCreator nameCreator = NameCreator.createOrGet(ruleName);
+ if (expressionStr.indexOf("(") == -1) {
+ relationStr = creator.createExpression(namespace, ruleName, expressionStr, expressions, containsContant, flag2ExpressionStr, nameCreator, relationStr);
+ return relationStr;
+ }
+
+ Map<String, String> expressionFlags = new HashMap<>();
+ relationStr = parseExpression(namespace, ruleName, expressionStr, expressions, expressionFlags, creator, containsContant, nameCreator, flag2ExpressionStr);
+ boolean needReplace = true;
+ while (needReplace) {
+ String tmp = ContantsUtil.restore(relationStr, expressionFlags);
+ if (tmp.equals(relationStr)) {
+ relationStr = tmp;
+ break;
+ } else {
+ relationStr = tmp;
+ }
+ }
+ return relationStr;
+ // for (int i = 0; i < expressionStr.length(); i++) {
+ // String word = expressionStr.substring(i, i + 1);
+ //
+ // if ("(".equals(word) ) {
+ // startExpression = true;
+ // continue;
+ // }
+ // if (")".equals(word)) {
+ // if (startExpression) {
+ // String expresionStr = expressionSb.toString();
+ // relationStr=creator.createExpression(namespace,ruleName,expresionStr,expressions,containsContant,flag2ExpressionStr,nameCreator,relationStr);
+ // expressionSb = new StringBuilder();
+ // }
+ // startExpression = false;
+ // continue;
+ // }
+ // if (startExpression) {
+ // expressionSb.append(word);
+ // }
+ // }
+ // return relationStr;
+ }
+
+ /**
+ * 把表达式中(a,b,c)部分解析出来,只留下关系部分
+ *
+ * @return 返回的是关系串
+ */
+ protected static String parseExpression(String namespace, String ruleName, String relationStr, List<Expression> expressions, Map<String, String> flag2ExpressionStr, IRuleExpressionCreator creator, boolean containsContant, NameCreator nameCreator, Map<String, String> contantsFlags) {
+ int endIndex = relationStr.indexOf(")");
+ if (endIndex == -1) {
+ return relationStr;
+ }
+ if (relationStr.indexOf(",") == -1) {
+ return relationStr;
+ }
+ for (int i = endIndex; i > 0; i--) {
+ String word = relationStr.substring(i - 1, i);
+ if (word.equals("(")) {
+ String expressionStr = relationStr.substring(i, endIndex);
+ String oriStr = "(" + expressionStr + ")";
+ if (expressionStr.indexOf(",") != -1) {
+ relationStr = creator.createExpression(namespace, ruleName, expressionStr, expressions, containsContant, contantsFlags, nameCreator, relationStr);
+ relationStr = parseExpression(namespace, ruleName, relationStr, expressions, flag2ExpressionStr, creator, containsContant, nameCreator, contantsFlags);
+ return relationStr;
+ } else {
+
+ String relationFlag = NameCreator.createNewName("relation");
+ flag2ExpressionStr.put(relationFlag, oriStr);
+ relationStr = relationStr.replace(oriStr, relationFlag);
+ return parseExpression(namespace, ruleName, relationStr, expressions, flag2ExpressionStr, creator, containsContant, nameCreator, contantsFlags);
+ }
+ }
+ }
+ return relationStr;
+ }
+
+ interface IRuleExpressionCreator {
+
+ String createExpression(String namespace, String ruleName, String expresionStr, List<Expression> expressions,
+ boolean containsContant,
+ Map<String, String> flag2ExpressionStr, NameCreator nameCreator, String relationStr);
+ }
+
+ protected static IRuleExpressionCreator ruleExpressionCreator = new IRuleExpressionCreator() {
+ @Override
+ public String createExpression(String namespace, String ruleName, String expresionStr,
+ List<Expression> expressions, boolean containsContant, Map<String, String> flag2ExpressionStr,
+ NameCreator nameCreator, String relationStr) {
+ String[] values = ExpressionBuilder.createElement(expresionStr, containsContant, flag2ExpressionStr);
+ //expresionStr= ContantsUtil.restore(expresionStr,flag2ExpressionStr);
+ String expressionName = nameCreator.createName(ruleName);
+
+ SimpleExpression expression = null;
+ if (values.length == 3) {
+ String value = values[2];
+ expression = new SimpleExpression(values[0], values[1], value);
+
+ }
+ if (values.length == 4) {
+ String value = values[3];
+ expression =
+ new SimpleExpression(values[0], values[1], DataTypeUtil.getDataType(values[2]), value);
+ }
+ if (expression != null) {
+ expression.setNameSpace(namespace);
+ expression.setConfigureName(expressionName);
+ if (relationStr.indexOf("(") == -1) {
+ relationStr = relationStr.replace(expresionStr, expressionName);
+ } else {
+ relationStr = relationStr.replace("(" + expresionStr + ")", expressionName);
+ }
+
+ expressions.add(expression);
+ }
+ return relationStr;
+ }
+ };
+
+ public static String[] createElement(String expresionStr, boolean containsContant, Map<String, String> flag2ExpressionStr) {
+ String[] values = expresionStr.split(",");
+ int i = 0;
+ for (String value : values) {
+ if (containsContant) {
+ value = ContantsUtil.restore(value, flag2ExpressionStr);
+ }
+ if (value != null && ContantsUtil.isContant(value)) {
+ value = value.substring(1, value.length() - 1);
+ values[i] = value;
+ }
+ values[i] = ContantsUtil.restoreSpecialSign(values[i]);
+ i++;
+ }
+ return values;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/builder/RuleBuilder.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/builder/RuleBuilder.java
new file mode 100644
index 0000000..17ef312
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/builder/RuleBuilder.java
@@ -0,0 +1,592 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.builder;
+
+import org.apache.rocketmq.streams.common.channel.sink.ISink;
+import org.apache.rocketmq.streams.common.model.NameCreator;
+import org.apache.rocketmq.streams.db.driver.JDBCDriver;
+import org.apache.rocketmq.streams.filter.operator.action.Action;
+import org.apache.rocketmq.streams.filter.operator.action.impl.MetaDataAction;
+import org.apache.rocketmq.streams.filter.contants.RuleElementType;
+import org.apache.rocketmq.streams.filter.operator.expression.ExpressionRelationParser;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.expression.RelationExpression;
+import org.apache.rocketmq.streams.filter.operator.var.ConstantVar;
+import org.apache.rocketmq.streams.filter.operator.var.ContextVar;
+import org.apache.rocketmq.streams.filter.operator.var.InnerVar;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.common.configurable.IConfigurable;
+import org.apache.rocketmq.streams.common.configurable.IConfigurableService;
+import org.apache.rocketmq.streams.common.datatype.DataType;
+import org.apache.rocketmq.streams.common.metadata.MetaData;
+import org.apache.rocketmq.streams.common.metadata.MetaDataField;
+import org.apache.rocketmq.streams.common.utils.DataTypeUtil;
+import org.apache.rocketmq.streams.common.utils.MapKeyUtil;
+
+import java.util.*;
+
+/**
+ * 通过这个工具可以快速创建一条规则。这个工具默认消息流的字段名=metadata的字段名
+ */
+public class RuleBuilder {
+ private static final DataType STRING = DataTypeUtil.getDataTypeFromClass(String.class);
+ private Rule rule = new Rule();
+ private List<Var> varList = new ArrayList<>();//变量列表,包含这个规则用到的所有变量
+ private List<MetaData> metaDataList = new ArrayList<>();//包含所有的metadata
+ private List<Expression> expressionList = new ArrayList<>();//包含所有的表达式
+ private List<Action> actionList = new ArrayList<>();//包含所有的action
+ private List<JDBCDriver> dataSourceList = new ArrayList<>();//包含所有的datasource
+ private MetaData metaData;//输入消息的metadata
+ private String namespace;//规则的命名空间
+ private String ruleName;//规则的名字
+ private Expression rootExpression;//最外层的表达式子
+ private String ruleCode;
+ private String ruleTitle;
+ private String ruleDescription;
+ private transient NameCreator actionNameCreator = new NameCreator();
+ private transient NameCreator dataSourceNameCreator = new NameCreator();
+ private transient NameCreator metaDataNameCreator = new NameCreator();
+
+ /**
+ * 创建个一个规则creator,在这个阶段会创建消息的meta,变量,以及表达式,支持多个表达式的关系操作,适合表达式比较少的时候
+ *
+ * @param namespace 规则的命名空间
+ * @param ruleName 规则名字
+ * @param expressionStr 格式如下(varname,functionName,datatype,value)&((varname,functionName,value)|(varname, functionName,value))
+ * @param msgMetaInfo 主要式表述消息的格式。格式如下:msgFieldName;int;true。如果最后以为是false,或第二位是string,可以省略最后两位。 格式如下:msgFieldnName。第二位可以根据datatype.getDataTypeName获取
+ */
+ public RuleBuilder(String namespace, String ruleName, String expressionStr, String... msgMetaInfo) {
+ init(namespace, ruleName);
+ List<Expression> expressions = new ArrayList<>();
+ List<RelationExpression> relationExpressions = new ArrayList<>();
+ rule.setExpressionStr(expressionStr);
+ Expression expression =
+ ExpressionBuilder.createExpression(namespace, ruleName, expressionStr, expressions, relationExpressions);
+ this.rootExpression = expression;
+ expressionList.addAll(expressions);
+ expressionList.addAll(relationExpressions);
+ if (msgMetaInfo == null || msgMetaInfo.length == 0) {
+ for (Expression express : expressionList) {
+ Var var = RuleElementBuilder.createContextVar(namespace, ruleName, express.getVarName(),
+ null, express.getVarName());
+ var.setConfigureName(express.getVarName());
+ varList.add(var);
+ }
+ return;
+ }
+ MetaData metaData =
+ RuleElementBuilder.createMetaData(namespace, metaDataNameCreator.createName(), msgMetaInfo);
+ List<MetaDataField> metaDataFields = metaData.getMetaDataFields();
+ Set<String> existVarNames = new HashSet<>();//
+ for (MetaDataField metaDataField : metaDataFields) {
+ Var var = RuleElementBuilder.createContextVar(namespace, ruleName, metaDataField.getFieldName(),
+ metaData.getConfigureName(), metaDataField.getFieldName());
+ var.setConfigureName(metaDataField.getFieldName());
+ varList.add(var);
+ existVarNames.add(var.getConfigureName());
+ }
+ for (Expression express : expressionList) {
+ if (existVarNames.contains(express.getVarName())) {
+ continue;
+ }
+ if (RelationExpression.class.isInstance(express)) {
+ continue;
+ }
+ Var var = RuleElementBuilder.createContextVar(namespace, ruleName, express.getVarName(),
+ null, express.getVarName());
+ var.setConfigureName(express.getVarName());
+ varList.add(var);
+ }
+ metaData.toObject(metaData.toJson());
+ this.metaData = metaData;
+ if (metaData != null) {
+ this.rule.setMsgMetaDataName(metaData.getConfigureName());
+ }
+ metaDataList.add(this.metaData);
+ }
+
+ /**
+ * 创建规则
+ *
+ * @param namespace 规则的命名空间
+ * @param ruleName 规则名称
+ */
+ public RuleBuilder(String namespace, String ruleName) {
+ init(namespace, ruleName);
+ createChannelMetaData();
+ }
+
+ public Rule getRule() {
+ return rule;
+ }
+
+ public RuleBuilder(IConfigurableService ruleEngineConfigurableService, String namespace, String ruleName) {
+ Rule rule =
+ (Rule)ruleEngineConfigurableService.queryConfigurableByIdent(RuleElementType.RULE.getType(), ruleName);
+ if (rule == null) {
+ init(namespace, ruleName);
+ createChannelMetaData();
+ return;
+ }
+ this.metaData =
+ (MetaData)ruleEngineConfigurableService.queryConfigurableByIdent(RuleElementType.METADATA.getType(),
+ rule.getMsgMetaDataName());
+ this.rootExpression =
+ (Expression)ruleEngineConfigurableService.queryConfigurableByIdent(RuleElementType.EXPRESSION.getType(),
+ rule.getExpressionName());
+ this.rule = rule;
+ this.ruleName = rule.getConfigureName();
+ this.namespace = rule.getNameSpace();
+ this.ruleTitle = rule.getRuleTitle();
+ this.ruleCode = rule.getRuleCode();
+ this.ruleDescription = rule.getRuleDesc();
+ this.varList = ruleEngineConfigurableService.queryConfigurableByType(RuleElementType.VAR.getType());
+ this.expressionList =
+ ruleEngineConfigurableService.queryConfigurableByType(RuleElementType.EXPRESSION.getType());
+ this.metaDataList = ruleEngineConfigurableService.queryConfigurableByType(RuleElementType.METADATA.getType());
+ this.actionList = ruleEngineConfigurableService.queryConfigurableByType(RuleElementType.ACTION.getType());
+ this.dataSourceList =
+ ruleEngineConfigurableService.queryConfigurableByType(RuleElementType.DATASOURCE.getType());
+ }
+
+ /**
+ * 创建规则必须的信息。
+ *
+ * @param namespace
+ * @param ruleName
+ * @param rulecode_title_description 最多3个,依次是rulecode,ruletitle,ruledescription
+ */
+ private void init(String namespace, String ruleName, String... rulecode_title_description) {
+ rule.setNameSpace(namespace);
+ rule.setConfigureName(ruleName);
+ rule.setRuleStatus(3);
+ this.namespace = namespace;
+ this.ruleName = ruleName;
+
+ if (rulecode_title_description != null && rulecode_title_description.length > 0) {
+ rule.setRuleCode(rulecode_title_description[0]);
+ this.ruleCode = rule.getRuleCode();
+ if (rulecode_title_description.length > 1) {
+ rule.setRuleTitle(rulecode_title_description[1]);
+ this.ruleTitle = rule.getRuleTitle();
+ }
+ if (rulecode_title_description.length > 2) {
+ rule.setRuleDesc(rulecode_title_description[2]);
+ ruleDescription = rule.getRuleDesc();
+ }
+ }
+ }
+
+ /**
+ * 快速创建消息流的meta。每一个表述一个metafield
+ *
+ * @param fieldNameTypeIsRequireds 格式如下:msgkeyName:type;isRequired。 如果最后以为是false,或第二位是string,可以省略最后两位。 格式如下:msgFieldnName。第二位可以根据datatype.getDataTypeName获取
+ */
+ public RuleBuilder addChannelMetaDataField(String... fieldNameTypeIsRequireds) {
+ if (metaData == null) {
+ throw new RuntimeException("need create metaData first");
+ }
+ if (metaData.getMetaDataFields() == null) {
+ metaData.setMetaDataFields(new ArrayList<MetaDataField>());
+ }
+ this.metaData.getMetaDataFields().addAll(createMetaDataField(fieldNameTypeIsRequireds));
+ return this;
+ }
+
+ /**
+ * 快速创建消息流的meta。每一个表述一个metafield
+ *
+ * @param fieldNameTypeIsRequireds 格式如下:msgkeyName:type;isRequired。 如果最后以为是false,或第二位是string,可以省略最后两位。 格式如下:msgFieldnName。第二位可以根据datatype.getDataTypeName获取
+ */
+ private List<MetaDataField> createMetaDataField(String... fieldNameTypeIsRequireds) {
+ MetaData metaData = RuleElementBuilder.createMetaData("tmp", "tmp", fieldNameTypeIsRequireds);
+ return metaData.getMetaDataFields();
+ }
+
+ /**
+ * 可以同时创建变量和metadatafield。类型是字符,可空
+ *
+ * @param fieldNameTypeIsRequireds
+ */
+ public RuleBuilder addVarAndMetaDataField(String... fieldNameTypeIsRequireds) {
+ List<MetaDataField> metaDataFields = createMetaDataField(fieldNameTypeIsRequireds);
+ this.metaData.getMetaDataFields().addAll(metaDataFields);
+ for (MetaDataField metaDataField : metaDataFields) {
+ ContextVar var = RuleElementBuilder.createContextVar(namespace, ruleName, metaDataField.getFieldName(),
+ metaData.getConfigureName(), metaDataField.getFieldName());
+ varList.add(var);
+ }
+ return this;
+ }
+
+ public RuleBuilder addInnerVar(String varName) {
+ InnerVar innerVar = new InnerVar();
+ innerVar.setNameSpace(ruleName);
+ innerVar.setVarName(varName);
+ innerVar.setType(RuleElementType.VAR.getType());
+ varList.add(innerVar);
+ return this;
+ }
+
+ /**
+ * 支持增加常量,常量类型是字符
+ *
+ * @param varName
+ * @param value
+ * @return
+ */
+ public RuleBuilder addConstantAndMetaField(String varName, String value) {
+ return addConstantAndMetaField(varName, STRING, value);
+ }
+
+ /**
+ * 支持增加常量,常量类型可以指定
+ *
+ * @param varName
+ * @param value
+ * @return
+ */
+ public RuleBuilder addConstantAndMetaField(String varName, DataType dataType, String value) {
+ ConstantVar var = RuleElementBuilder.createConstantVar(namespace, ruleName, varName, dataType, value);
+ varList.add(var);
+ MetaDataField metaDataField = new MetaDataField();
+ metaDataField.setFieldName(varName);
+ metaDataField.setDataType(dataType);
+ metaDataField.setIsRequired(false);
+ this.metaData.getMetaDataFields().add(metaDataField);
+ return this;
+ }
+
+ public RuleBuilder setExpression(String expressionStr) {
+ List<Expression> expressions = new ArrayList<>();
+ List<RelationExpression> relationExpressions = new ArrayList<>();
+ Expression expression =
+ ExpressionBuilder.createExpression(namespace, ruleName, expressionStr, expressions, relationExpressions);
+ this.rootExpression = expression;
+ this.expressionList.addAll(expressions);
+ this.expressionList.addAll(relationExpressions);
+ return this;
+ }
+
+ /**
+ * 创建表达式,包括表达式名字,变量名,函数和值。值是字符类型
+ *
+ * @param expressionName
+ * @param varName
+ * @param functionName
+ * @param value
+ * @return
+ */
+ public RuleBuilder addExpression(String expressionName, String varName, String functionName, String value) {
+ addExpression(expressionName, varName, functionName, STRING, value);
+ return this;
+ }
+
+ public RuleBuilder addExpression(Expression expression) {
+ expressionList.add(expression);
+ return this;
+ }
+
+ /**
+ * 创建表达式,包括表达式名字,变量名,函数和值。值可以指定
+ *
+ * @param expressionName
+ * @param varName
+ * @param functionName
+ * @param value
+ * @return
+ */
+ public RuleBuilder addExpression(String expressionName, String varName, String functionName, DataType dataType,
+ String value) {
+ Expression expression =
+ RuleElementBuilder.createExpression(ruleName, expressionName, varName, functionName, dataType, value);
+ expressionList.add(expression);
+ return this;
+ }
+
+ /**
+ * 通过 表达式的名字,做关联关系。如表达式的名字为1,2,3。关系可以写为1&(2|3)
+ *
+ * @param relationStr
+ * @return
+ */
+ public RuleBuilder addRelationExpression(String relationStr) {
+ List<RelationExpression> relationExpressions = new ArrayList<>();
+ RelationExpression relationExpression =
+ ExpressionRelationParser.createRelations(ruleName, ruleName, relationStr, relationExpressions);
+ this.expressionList.addAll(relationExpressions);
+ this.rootExpression = relationExpression;
+ return this;
+ }
+ //
+ ///**
+ // * @param url
+ // * @param userName
+ // * @param password
+ // * @param kvs: varName:fieldName,varName:fieldName
+ // * @return
+ // */
+ //public RuleBuilder addDBAction(String url, String userName, String password, String table, String... kvs) {
+ // return addDBAction(url, userName, password, table, createVarName2FieldName(kvs));
+ //}
+ //
+ //public RuleBuilder addChannelAction(IConvertDataSource convertDataSource) {
+ // Map<String, String> varName2FieldName = new HashMap<>();
+ // List<MetaDataField> metaDataFields = metaData.getMetaDataFields();
+ // for (MetaDataField field : metaDataFields) {
+ // varName2FieldName.put(field.getFieldName(), field.getFieldName());
+ // }
+ // IDataOperator dataSource = convertDataSource.convert();
+ // dataSourceList.add(dataSource);
+ // String metaDataName = metaDataNameCreator.createName();
+ // String actionName = actionNameCreator.createName();
+ // MetaData metaData =
+ // RuleElementBuilder.createMetaData(namespace, metaDataName, createMetaDataFiledStr(varName2FieldName));
+ // metaData.setDataSourceName(dataSource.getConfigureName());
+ // metaDataList.add(metaData);
+ // MetaDataAction action =
+ // RuleElementBuilder.createAction(namespace, actionName, metaDataName, varName2FieldName);
+ // actionList.add(action);
+ // return this;
+ //}
+
+ public RuleBuilder addMetaDataAction(String namespace, String metaDataName, Map<String, String> varName2FieldName) {
+ initVarName2FieldName(varName2FieldName);
+ String actionName = actionNameCreator.createName();
+ MetaDataAction action =
+ RuleElementBuilder.createAction(namespace, actionName, metaDataName, varName2FieldName);
+ actionList.add(action);
+ return this;
+ }
+
+ /**
+ * 创建输出,可以把触发规则的数据写入到指定的为止,此方法主要是db
+ *
+ * @param varName2FieldName 消息中的字段名和数据表的名字映射
+ * @param url
+ * @param userName
+ * @param password
+ * @return
+ */
+ public RuleBuilder addDBAction(String url, String userName, String password, String tableName,
+ Map<String, String> varName2FieldName) {
+ initVarName2FieldName(varName2FieldName);
+
+ JDBCDriver dataSource = new JDBCDriver();
+ dataSource.setUrl(url);
+ dataSource.setNameSpace(namespace);
+ dataSource.setUserName(userName);
+ dataSource.setPassword(password);
+ dataSource.setJdbcDriver("com.mysql.jdbc.Driver");
+ dataSource.setType(ISink.TYPE);
+ dataSource.setConfigureName(dataSourceNameCreator.createName());
+ dataSourceList.add(dataSource);
+
+ String metaDataName = metaDataNameCreator.createName();
+ String actionName = actionNameCreator.createName();
+ MetaData metaData =
+ RuleElementBuilder.createMetaData(namespace, metaDataName, createMetaDataFiledStr(varName2FieldName));
+ metaData.setTableName(tableName);
+ metaData.setDataSourceName(dataSource.getConfigureName());
+ metaDataList.add(metaData);
+ MetaDataAction action =
+ RuleElementBuilder.createAction(namespace, actionName, metaDataName, varName2FieldName);
+ actionList.add(action);
+ return this;
+ }
+
+ /**
+ * 保存规则相关的对象
+ *
+ * @param ruleEngineConfigurableService
+ * @return
+ */
+ public Rule generateRule(IConfigurableService ruleEngineConfigurableService) {
+ Rule rule = createRule();
+ insertOrUpdate(ruleEngineConfigurableService);
+ return rule;
+ }
+
+ private void insertOrUpdate(IConfigurableService ruleEngineConfigurableService) {
+ insertOrUpdate(ruleEngineConfigurableService, metaDataList, MetaData.TYPE);
+ insertOrUpdate(ruleEngineConfigurableService, varList, Var.TYPE);
+ insertOrUpdate(ruleEngineConfigurableService, expressionList, Expression.TYPE);
+ insertOrUpdate(ruleEngineConfigurableService, actionList, Action.TYPE);
+ insertOrUpdate(ruleEngineConfigurableService, dataSourceList, ISink.TYPE);
+ if (ruleEngineConfigurableService != null) {
+ ruleEngineConfigurableService.insert(rule);
+ }
+ }
+
+ /**
+ * 完成规则创建,但还不能使用。
+ *
+ * @return
+ */
+ protected Rule createRule() {
+ if (rootExpression == null && expressionList != null && expressionList.size() > 0) {
+ rootExpression = expressionList.get(0);
+ }
+ rule.setExpressionName(rootExpression.getConfigureName());
+ rule.setActionNames(convert(actionList));
+ rule.setVarNames(convert(varList));
+ rule.setRuleCode(ruleCode);
+ rule.setRuleTitle(ruleTitle);
+ rule.setRuleDesc(ruleDescription);
+ if (this.metaData != null) {
+ rule.setMsgMetaDataName(metaData.getConfigureName());
+ }
+ return rule;
+ }
+
+ private Map<String, String> createVarName2FieldName(String... kvs) {
+ Map<String, String> varName2FieldNames = null;
+ if (kvs != null) {
+ varName2FieldNames = new HashMap<>();
+ for (String varName2FieldName : kvs) {
+ String[] values = varName2FieldName.split(":");
+ varName2FieldNames.put(values[0], values[1]);
+ }
+ return varName2FieldNames;
+ }
+ initVarName2FieldName(varName2FieldNames);
+ return varName2FieldNames;
+ }
+
+ private void initVarName2FieldName(Map<String, String> varName2FieldNames) {
+ if (varName2FieldNames == null) {
+ List<MetaDataField> metaDataFields = metaData.getMetaDataFields();
+ for (MetaDataField field : metaDataFields) {
+ varName2FieldNames.put(field.getFieldName(), field.getFieldName());
+ }
+ }
+ }
+
+ private String[] createMetaDataFiledStr(Map<String, String> varName2FieldName) {
+ if (varName2FieldName == null) {
+ return new String[0];
+ }
+ String[] filedStr = new String[varName2FieldName.size()];
+ Iterator<Map.Entry<String, String>> it = varName2FieldName.entrySet().iterator();
+ metaData.toObject(metaData.toJson());
+ int i = 0;
+ while (it.hasNext()) {
+ Map.Entry<String, String> entry = it.next();
+ String varName = entry.getKey();
+ String fieldName = entry.getValue();
+ DataType dataType = metaData.getMetaDataField(varName).getDataType();
+ boolean isRequired = metaData.getMetaDataField(varName).getIsRequired();
+ filedStr[i] = MapKeyUtil.createKey(fieldName, dataType.getDataTypeName(), String.valueOf(isRequired));
+ i++;
+ }
+ return filedStr;
+ }
+
+ private <T extends IConfigurable> void insertOrUpdate(IConfigurableService ruleEngineConfigurableService,
+ List<T> configurables, String type) {
+ if (configurables == null) {
+ return;
+ }
+ for (IConfigurable configurable : configurables) {
+ if (ruleEngineConfigurableService != null) {
+ ruleEngineConfigurableService.insert(configurable);
+ }
+ rule.putConfigurableMap(configurable, type);
+ }
+ }
+
+ private <T extends IConfigurable> List<String> convert(List<T> configurables) {
+ List<String> configurableNames = new ArrayList<>();
+ for (T t : configurables) {
+ configurableNames.add(t.getConfigureName());
+ }
+ return configurableNames;
+ }
+
+ /**
+ * 创建规则默认创建一个消息流对应的metadata
+ *
+ * @return
+ */
+ private MetaData createChannelMetaData() {
+ MetaData metaData = RuleElementBuilder.createMetaData(namespace, metaDataNameCreator.createName());
+ metaDataList.add(metaData);
+ this.metaData = metaData;
+ return metaData;
+ }
+
+ public MetaData getMetaData() {
+ return metaData;
+ }
+
+ public List<Var> getVarList() {
+ return varList;
+ }
+
+ public List<MetaData> getMetaDataList() {
+ return metaDataList;
+ }
+
+ public List<Expression> getExpressionList() {
+ return expressionList;
+ }
+
+ public List<Action> getActionList() {
+ return actionList;
+ }
+
+ public List<JDBCDriver> getDataSourceList() {
+ return dataSourceList;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public String getRuleName() {
+ return ruleName;
+ }
+
+ public Expression getRootExpression() {
+ return rootExpression;
+ }
+
+ public void setRootExpression(Expression rootExpression) {
+ this.rootExpression = rootExpression;
+ }
+
+ public void setRuleName(String ruleName) {
+ this.ruleName = ruleName;
+ }
+
+ public String getRuleCode() {
+ return ruleCode;
+ }
+
+ public void setRuleCode(String ruleCode) {
+ this.ruleCode = ruleCode;
+ }
+
+ public String getRuleTitle() {
+ return ruleTitle;
+ }
+
+ public void setRuleTitle(String ruleTitle) {
+ this.ruleTitle = ruleTitle;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/builder/RuleElementBuilder.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/builder/RuleElementBuilder.java
new file mode 100644
index 0000000..1d7edba
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/builder/RuleElementBuilder.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.builder;
+
+import org.apache.rocketmq.streams.filter.operator.action.impl.MetaDataAction;
+import org.apache.rocketmq.streams.filter.contants.RuleElementType;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.expression.RelationExpression;
+import org.apache.rocketmq.streams.filter.operator.var.ConstantVar;
+import org.apache.rocketmq.streams.filter.operator.var.ContextVar;
+import org.apache.rocketmq.streams.common.datatype.DataType;
+import org.apache.rocketmq.streams.common.metadata.MetaData;
+import org.apache.rocketmq.streams.common.metadata.MetaDataField;
+import org.apache.rocketmq.streams.common.utils.DataTypeUtil;
+import org.apache.rocketmq.streams.common.utils.MapKeyUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class RuleElementBuilder {
+ /**
+ * @param namespace
+ * @param name
+ * @param fields 格式:fieldname;datatypename;isRequired。如isRequired==false, 最后部分可以省略,如果datatypename==string,且isRequired==false,可以只写fieldname
+ * @return
+ */
+ public static MetaData createMetaData(String namespace, String name, String... fields) {
+ MetaData metaData = new MetaData();
+ metaData.setNameSpace(namespace);
+ metaData.setConfigureName(name);
+ if (fields == null || fields.length == 0) {
+ metaData.toObject(metaData.toJson());
+ return metaData;
+ }
+ List<MetaDataField> metaDataFieldList = new ArrayList<>();
+ for (String field : fields) {
+ MetaDataField metaDataField = new MetaDataField();
+ String sign = ":";
+ if (field.indexOf(sign) == -1) {
+ sign = ";";
+ }
+ String[] values = field.split(sign);
+ String fieldName = values[0];
+ metaDataField.setFieldName(fieldName);
+ DataType dataType = DataTypeUtil.getDataTypeFromClass(String.class);
+ boolean isRequired = false;
+ if (values.length > 1) {
+ String dataTypeName = values[1];
+ dataType = DataTypeUtil.getDataType(dataTypeName);
+
+ }
+ metaDataField.setDataType(dataType);
+ if (values.length > 2) {
+ isRequired = Boolean.valueOf(values[2]);
+
+ }
+ metaDataField.setIsRequired(isRequired);
+ metaDataFieldList.add(metaDataField);
+ }
+ metaData.getMetaDataFields().addAll(metaDataFieldList);
+ metaData.toObject(metaData.toJson());
+ return metaData;
+ }
+
+ public static ContextVar createContextVar(String namespace, String ruleName, String varName, String metaDataName, String fieldName) {
+ ContextVar contextVar = new ContextVar();
+ contextVar.setNameSpace(namespace);
+ contextVar.setType(RuleElementType.VAR.getType());
+ contextVar.setVarName(varName);
+ contextVar.setConfigureName(MapKeyUtil.createKeyBySign("_", ruleName, varName));
+ contextVar.setMetaDataName(metaDataName);
+ contextVar.setFieldName(fieldName);
+ return contextVar;
+ }
+
+ public static ConstantVar createConstantVar(String namespace, String ruleName, String varName, DataType dataType, String value) {
+ ConstantVar constantVar = new ConstantVar();
+ constantVar.setNameSpace(ruleName);
+ constantVar.setDataType(dataType);
+ constantVar.setVarName(varName);
+ constantVar.setConfigureName(MapKeyUtil.createKeyBySign("_", ruleName, varName));
+ constantVar.setValue(dataType.getData(value));
+ constantVar.setType(RuleElementType.VAR.getType());
+ return constantVar;
+ }
+
+ public static Expression createExpression(String ruleName, String expressionName, String varName,
+ String functionName, DataType dataType, String value) {
+ Expression expression = new Expression();
+ expression.setNameSpace(ruleName);
+ expression.setType(RuleElementType.EXPRESSION.getType());
+ expression.setConfigureName(expressionName);
+ expression.setVarName(varName);
+ expression.setValue(dataType.getData(value));
+ expression.setDataType(dataType);
+ expression.setFunctionName(functionName);
+ return expression;
+ }
+
+ public static RelationExpression createRelationExpression(String ruleName, String expressionName, String relation,
+ List<String> expressionNames) {
+ RelationExpression relationExpression = new RelationExpression();
+ relationExpression.setNameSpace(ruleName);
+ relationExpression.setType(RuleElementType.EXPRESSION.getType());
+ relationExpression.setConfigureName(expressionName);
+ relationExpression.setRelation(relation);
+ relationExpression.setValue(expressionNames);
+ return relationExpression;
+ }
+
+ public static MetaDataAction createAction(String namespace, String name, String metaDataName,
+ Map<String, String> varName2FieldName) {
+ MetaDataAction metaDataAction = new MetaDataAction();
+ metaDataAction.setNameSpace(namespace);
+ metaDataAction.setType(RuleElementType.ACTION.getType());
+ metaDataAction.setConfigureName(name);
+ metaDataAction.setMetaDataName(metaDataName);
+ metaDataAction.setVarName2FieldName(varName2FieldName);
+ return metaDataAction;
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/contants/RuleElementType.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/contants/RuleElementType.java
new file mode 100644
index 0000000..308e836
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/contants/RuleElementType.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.contants;
+
+import org.apache.rocketmq.streams.common.channel.sink.ISink;
+import org.apache.rocketmq.streams.filter.operator.action.Action;
+
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.common.metadata.MetaData;
+
+public enum RuleElementType {
+ METADATA(MetaData.TYPE),
+ VAR(Var.TYPE),
+ RULE(Rule.TYPE),
+ EXPRESSION(Expression.TYPE),
+ ACTION(Action.TYPE),
+ DATASOURCE(ISink.TYPE);
+ private String type;
+
+ private RuleElementType(String type) {
+ this.type = type;
+ }
+
+ public String getType() {
+ return this.type;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/contants/RuleStatus.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/contants/RuleStatus.java
new file mode 100644
index 0000000..9407116
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/contants/RuleStatus.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.contants;
+
+/**
+ * 规则运行状态
+ */
+public enum RuleStatus {
+ RULE_STATUS_TESTING(0, "测试中", "yundun-sasruleengine-CSZ-key-001"),
+ RULE_STATUS_OBSERVING(1, "观察中", "yundun-sasruleengine-GCZ-key-002"),
+ RULE_STATUS_AUDITING(2, "审核中", "yundun-sasruleengine-SHZ-key-003"),
+ RULE_STATUS_ONLINE(3, "已上线", "yundun-sasruleengine-YSX-key-004"),
+ RULE_STATUS_SELF(4, "自定义", "do-self"),
+ KILLCHAIN_DESC_RULES(5, "killchain说明规则", "killchain-desc");
+
+ private Integer ruleStatus;
+ private String desc;
+ private String key;
+
+ private RuleStatus(int ruleStatus, String desc, String key) {
+ this.ruleStatus = ruleStatus;
+ this.desc = desc;
+ this.key = key;
+ }
+
+ public Integer getRuleStatus() {
+ return ruleStatus;
+ }
+
+ public void setRuleStatus(Integer ruleStatus) {
+ this.ruleStatus = ruleStatus;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public static String getDescByRuleStatus(Integer ruleStatus) {
+ if (RuleStatus.RULE_STATUS_OBSERVING.getRuleStatus().equals(ruleStatus)) {
+ return RuleStatus.RULE_STATUS_OBSERVING.getDesc();
+ } else if (RuleStatus.RULE_STATUS_AUDITING.getRuleStatus().equals(ruleStatus)) {
+ return RuleStatus.RULE_STATUS_AUDITING.getDesc();
+ } else if (RuleStatus.RULE_STATUS_ONLINE.getRuleStatus().equals(ruleStatus)) {
+ return RuleStatus.RULE_STATUS_ONLINE.getDesc();
+ } else {
+ return RuleStatus.RULE_STATUS_TESTING.getDesc();
+ }
+ }
+
+ public static String getKeyByRuleStatus(Integer ruleStatus) {
+ if (RuleStatus.RULE_STATUS_OBSERVING.getRuleStatus().equals(ruleStatus)) {
+ return RuleStatus.RULE_STATUS_OBSERVING.getKey();
+ } else if (RuleStatus.RULE_STATUS_AUDITING.getRuleStatus().equals(ruleStatus)) {
+ return RuleStatus.RULE_STATUS_AUDITING.getKey();
+ } else if (RuleStatus.RULE_STATUS_ONLINE.getRuleStatus().equals(ruleStatus)) {
+ return RuleStatus.RULE_STATUS_ONLINE.getKey();
+ } else {
+ return RuleStatus.RULE_STATUS_TESTING.getKey();
+ }
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public static void main(String args[]) {
+ // System.out.println(SasRuleEnum.getDescByRuleStatus(new Integer(1)));
+ System.out.println(RuleStatus.RULE_STATUS_OBSERVING.getRuleStatus());
+
+ Integer a = new Integer(1);
+ int b = 1;
+ System.out.println(a == b);
+
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/context/ContextConfigure.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/context/ContextConfigure.java
new file mode 100644
index 0000000..e5c2145
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/context/ContextConfigure.java
@@ -0,0 +1,272 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.context;
+
+import java.util.Properties;
+
+public class ContextConfigure {
+
+ /**
+ * 正则表达式优化相关
+ */
+ protected volatile boolean isSupportRegexOptimization = true; // 是否支持正则表达式优化
+ protected volatile int regexTimeout = 1000; // 正则表达式超时时间,默认100ms
+
+ protected int actionPoolSize = 5;
+ /**
+ * 控制监控输出
+ */
+ protected int monitorPoolSize = 10000; // 监控对象池大小
+ private volatile String rulesLogsIps = "127.0.0.1";
+ protected volatile boolean fetchDataInThisHost = false; // 是否在本机采样,在采样模式下使用
+ protected volatile int longTimeAlert = 1000; // 规则超过500ms则打印输出
+ protected volatile int mode = MONITOR_STATISTICS; // 0:开启模式;1,采样模式;-1:关闭模式
+ public static final int MONITOR_OPEN = 1; // 开启模式
+ public static final int MONITOR_CLOSE = -1; // 关闭模式
+ public static final int MONITOR_SAMPLE = 0; // 关闭模式
+ public static final int MONITOR_STATISTICS = 2; // 只统计时间,不输出
+ /**
+ * 区分引擎运行的环境
+ */
+ protected String evnNamespace; // 区分压测环境和线上环境
+
+ /**
+ * 控制结果输出
+ */
+ protected boolean action2Online = true; // 是否执行在线的action,如果是false,不执行线上的action
+ protected boolean action2Observer = false; // 观察中的规则是否输出到观察表,如果为true,则写到观察表
+ protected boolean actionOnline2Observer = false; // 在线规则输出到观察表,如果为true,则写到观察表,此种情况是为了测试
+
+ /**
+ * 控制加载规则
+ */
+ protected String loadRuleStatuses = "3"; // 规则加载方式,根据规则的状态加载
+ protected String diyRuleCodes = null; // 如果规则状态选择自定义,则此处列出规则的状态,用逗号分隔
+ //
+ ///**
+ // * 分流用对象
+ // */
+ //protected transient SplitFlow splitFlow = new SplitFlow();
+
+ /**
+ * 分流用对象
+ */
+ protected boolean openUnRepeateScript = false;
+
+ protected boolean openObserverDatasource = true;
+
+ /**
+ * 是否使用缓存处理正则
+ */
+ protected boolean encacheFlag = false;
+
+ /**
+ * 升级中心 压测数据
+ */
+ protected int pressureTotalCount = 10000;
+
+ /**
+ * 恶意IP最大加载数
+ */
+ protected int maliciousIPMaxCount = 1000000;
+
+ /**
+ * 是否支持监控模块输出超时日志
+ *
+ * @return
+ */
+ public boolean canMonitor() {
+ if (mode == ContextConfigure.MONITOR_OPEN) {
+ return true;
+ }
+ if (mode == ContextConfigure.MONITOR_SAMPLE && isFetchDataInThisHost()) {
+ return true;
+ }
+ return false;
+ }
+
+ public ContextConfigure(Properties properties) {
+
+ }
+
+ /**
+ * 是否支持规则统计(统计慢规则)
+ *
+ * @return
+ */
+ public boolean canStatisticsMonitor() {
+ if (mode == ContextConfigure.MONITOR_STATISTICS) {
+ return true;
+ }
+ if (this.canMonitor()) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isSupportRegexOptimization() {
+ return isSupportRegexOptimization;
+ }
+
+ public void setSupportRegexOptimization(boolean supportRegexOptimization) {
+ isSupportRegexOptimization = supportRegexOptimization;
+ }
+
+ public int getRegexTimeout() {
+ return regexTimeout;
+ }
+
+ public void setRegexTimeout(int regexTimeout) {
+ this.regexTimeout = regexTimeout;
+ }
+
+ public boolean isFetchDataInThisHost() {
+ return fetchDataInThisHost;
+ }
+
+ public void setFetchDataInThisHost(boolean fetchDataInThisHost) {
+ this.fetchDataInThisHost = fetchDataInThisHost;
+ }
+
+ public int getLongTimeAlert() {
+ return longTimeAlert;
+ }
+
+ public void setLongTimeAlert(int longTimeAlert) {
+ this.longTimeAlert = longTimeAlert;
+ }
+
+ public String getEvnNamespace() {
+ return evnNamespace;
+ }
+
+ public void setEvnNamespace(String evnNamespace) {
+ this.evnNamespace = evnNamespace;
+ }
+
+ public boolean isAction2Online() {
+ return action2Online;
+ }
+
+ public void setAction2Online(boolean action2Online) {
+ this.action2Online = action2Online;
+ }
+
+ public boolean isAction2Observer() {
+ return action2Observer;
+ }
+
+ public void setAction2Observer(boolean action2Observer) {
+ this.action2Observer = action2Observer;
+ }
+
+ public boolean isActionOnline2Observer() {
+ return actionOnline2Observer;
+ }
+
+ public void setActionOnline2Observer(boolean actionOnline2Observer) {
+ this.actionOnline2Observer = actionOnline2Observer;
+ }
+
+ public String getLoadRuleStatuses() {
+ return loadRuleStatuses;
+ }
+
+ public void setLoadRuleStatuses(String loadRuleStatuses) {
+ this.loadRuleStatuses = loadRuleStatuses;
+ }
+
+ public String getDiyRuleCodes() {
+ return diyRuleCodes;
+ }
+
+ public void setDiyRuleCodes(String diyRuleCodes) {
+ this.diyRuleCodes = diyRuleCodes;
+ }
+
+ public String getRulesLogsIps() {
+ return rulesLogsIps;
+ }
+
+ public void setRulesLogsIps(String rulesLogsIps) {
+ this.rulesLogsIps = rulesLogsIps;
+ }
+
+ public int getMonitorPoolSize() {
+ return monitorPoolSize;
+ }
+
+ public void setMonitorPoolSize(int monitorPoolSize) {
+ this.monitorPoolSize = monitorPoolSize;
+ }
+
+ public int getMode() {
+ return mode;
+ }
+
+ public void setMode(int mode) {
+ this.mode = mode;
+ }
+
+ //public SplitFlow getSplitFlow() {
+ // return splitFlow;
+ //}
+ //
+ //public void setSplitFlow(SplitFlow splitFlow) {
+ // this.splitFlow = splitFlow;
+ //}
+
+ public boolean isOpenUnRepeateScript() {
+ return openUnRepeateScript;
+ }
+
+ public void setOpenUnRepeateScript(boolean openUnRepeateScript) {
+ this.openUnRepeateScript = openUnRepeateScript;
+ }
+
+ public boolean isOpenObserverDatasource() {
+ return openObserverDatasource;
+ }
+
+ public void setOpenObserverDatasource(boolean openObserverDatasource) {
+ this.openObserverDatasource = openObserverDatasource;
+ }
+
+ public boolean isEncacheFlag() {
+ return encacheFlag;
+ }
+
+ public void setEncacheFlag(boolean encacheFlag) {
+ this.encacheFlag = encacheFlag;
+ }
+
+ public int getPressureTotalCount() {
+ return pressureTotalCount;
+ }
+
+ public void setPressureTotalCount(int pressureTotalCount) {
+ this.pressureTotalCount = pressureTotalCount;
+ }
+
+ public int getActionPoolSize() {
+ return actionPoolSize;
+ }
+
+ public void setActionPoolSize(int actionPoolSize) {
+ this.actionPoolSize = actionPoolSize;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/context/RuleContext.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/context/RuleContext.java
new file mode 100644
index 0000000..26554b2
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/context/RuleContext.java
@@ -0,0 +1,399 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.context;
+
+import java.io.Serializable;
+import java.util.Properties;
+import java.util.Vector;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import com.alibaba.fastjson.JSONObject;
+
+import org.apache.rocketmq.streams.db.driver.JDBCDriver;
+import org.apache.rocketmq.streams.filter.operator.action.Action;
+import org.apache.rocketmq.streams.filter.function.expression.ExpressionFunction;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.common.configurable.IConfigurableService;
+import org.apache.rocketmq.streams.script.function.model.FunctionConfigure;
+import org.apache.rocketmq.streams.script.function.service.impl.ScanFunctionService;
+import org.apache.rocketmq.streams.common.context.AbstractContext;
+import org.apache.rocketmq.streams.common.context.IMessage;
+import org.apache.rocketmq.streams.common.context.Message;
+import org.apache.rocketmq.streams.common.monitor.IMonitor;
+import org.apache.rocketmq.streams.common.monitor.TopologyFilterMonitor;
+import org.apache.rocketmq.streams.common.metadata.MetaData;
+import org.apache.rocketmq.streams.common.metadata.MetaDataAdapter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class RuleContext extends AbstractContext<Message> implements Serializable {
+
+ private static final Log LOG = LogFactory.getLog(RuleContext.class);
+
+ /**
+ * 观察者对应的configure
+ */
+ public static final String OBSERVER_NAME = "observerDBAction";
+
+ private static RuleContext superRuleContext;
+
+ private ExecutorService actionExecutor = null;
+
+ /**
+ * 默认的命名空间
+ */
+ public static final String DEFALUT_NAME_SPACE = IConfigurableService.PARENT_CHANNEL_NAME_SPACE;
+
+ /**
+ * var的名称合值对应的map
+ */
+ private ConcurrentMap<String, Object> varValueMap = new ConcurrentHashMap<String, Object>();
+ private ConcurrentMap<String, Boolean> expressionValueMap = new ConcurrentHashMap<String, Boolean>();
+
+ /**
+ * 错误信息
+ */
+ private Vector<String> errorMessageList = new Vector<>();
+ /**
+ * 当前的命名空间
+ */
+ private String nameSpace;
+
+ /**
+ * 配置服务
+ */
+ // private transient IConfigurableService configureService;
+
+ private transient ScanFunctionService functionService = ScanFunctionService.getInstance();
+ private transient RuleContext parentContext;
+ private transient Rule rule;
+
+ /**
+ * 监控一条信息的运行情况
+ */
+ private transient volatile IMonitor ruleMonitor;
+
+ /**
+ * 一个规则对应的系统配置
+ */
+ private transient ContextConfigure contextConfigure;
+
+ // private transient static volatile AtomicBoolean initflag = new AtomicBoolean(false);
+
+ private static volatile boolean initflag = false;
+
+ protected TopologyFilterMonitor expressionMonitor = new TopologyFilterMonitor();
+
+ public static void initSuperRuleContext(ContextConfigure contextConfigure) {
+ if (!initflag) {
+ synchronized (RuleContext.class) {
+ if (!initflag) {
+ RuleContext staticRuleContext = new RuleContext(DEFALUT_NAME_SPACE, contextConfigure);
+ ExecutorService actionExecutor = Executors.newFixedThreadPool(contextConfigure.getActionPoolSize());
+ staticRuleContext.actionExecutor = actionExecutor;
+ staticRuleContext.functionService.scanePackage("org.apache.rocketmq.streams.filter.function");
+ superRuleContext = staticRuleContext;
+ initflag = true;
+ }
+ }
+ }
+
+ }
+
+ private RuleContext(String pnameSpace, ContextConfigure contextConfigure) {
+ this(pnameSpace, new JSONObject(), null, contextConfigure);
+ }
+
+ public RuleContext(String nameSpace, JSONObject pmessage, Rule rule, ContextConfigure contextConfigure) {
+ super(new Message(pmessage));
+ if (!DEFALUT_NAME_SPACE.equals(nameSpace)) {
+ this.parentContext = superRuleContext;
+ }
+ this.nameSpace = nameSpace;
+ this.rule = rule;
+ this.contextConfigure = contextConfigure;
+ if (contextConfigure == null && parentContext != null) {
+ this.contextConfigure = this.parentContext.getContextConfigure();
+ }
+ if (this.functionService == null && this.parentContext != null) {
+ this.functionService = this.parentContext.getFunctionService();
+ }
+
+ }
+
+ public RuleContext(JSONObject pmessage, Rule rule, Properties properties) {
+ this(rule.getNameSpace(), pmessage, rule, new ContextConfigure(properties));
+ }
+
+ public RuleContext(JSONObject pmessage, Rule rule) {
+ this(rule.getNameSpace(), pmessage, rule, new ContextConfigure(null));
+ }
+
+ public String getNameSpace() {
+ if (nameSpace != null) {
+ return nameSpace;
+ }
+ if (parentContext != null) {
+ return parentContext.getNameSpace();
+ }
+ return nameSpace;
+ }
+
+ /**
+ * 获取表达式值
+ *
+ * @param configureName
+ * @return
+ */
+ public Boolean getExpressionValue(String configureName) {
+ Boolean result = expressionValueMap.get(configureName);
+ return result;
+ }
+
+ /**
+ * 设置表达式值
+ *
+ * @param configureName
+ * @param result
+ */
+ public void putExpressionValue(String nameSpace, String configureName, Boolean result) {
+ if (rule.getExpressionMap().containsKey(configureName)) {
+ expressionValueMap.putIfAbsent(configureName, result);
+ }
+ }
+
+ /**
+ * 当规则产生错误时,记录错误信息
+ *
+ * @param message
+ */
+ public void addErrorMessage(Rule rule, String message) {
+ String messageInfo = message;
+ if (rule != null) {
+ messageInfo = rule.getRuleCode() + ":" + messageInfo;
+ }
+ errorMessageList.add(messageInfo);
+ }
+
+ public Var getVar(String ruleName, String name) {
+ return getVar(name);
+ }
+
+ public Var getVar(String name) {
+ Var var = rule.getVarMap().get(name);
+
+ return var;
+ }
+
+ public Expression getExpression(String name) {
+ Expression expression = rule.getExpressionMap().get(name);
+ return expression;
+ }
+
+ public Action getAction(String name) {
+ Action action = rule.getActionMap().get(name);
+ return action;
+ }
+
+ public ExpressionFunction getExpressionFunction(String name, Object... objects) {
+ try {
+ FunctionConfigure fc = functionService.getFunctionConfigure(name, objects);
+ if (fc == null) {
+ return null;
+ }
+ return (ExpressionFunction)fc.getBean();
+ } catch (Exception e) {
+ LOG.error("RuleContext getExpressionFunction error,name is: " + name, e);
+ return null;
+ }
+
+ }
+
+ public MetaData getMetaData(String name) {
+ MetaData metaData = rule.getMetaDataMap().get(name);
+ return metaData;
+ }
+
+ public JDBCDriver getDataSource(String name) {
+ return rule.getDataSourceMap().get(name);
+ }
+
+ /**
+ * @param name
+ * @return
+ */
+ public MetaDataAdapter getMetaDataAdapter(String name) {
+ MetaData md = getMetaData(name);
+ JDBCDriver dataSource = this.getDataSource(md.getDataSourceName());
+ MetaDataAdapter mda = new MetaDataAdapter(md, dataSource);
+ return mda;
+
+ }
+
+ public boolean containsVarName(String varName) {
+ if (varValueMap.containsKey(varName)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 获取变量值,内部使用,不能直接使用,获取变量的值需要用var.getVarValue()
+ *
+ * @param varName
+ * @return
+ */
+ public Object getVarCacheValue(String varName) {
+ Object value = varValueMap.get(varName);
+ if (value != null) {
+ return value;
+ }
+ if (parentContext != null) {
+ value = parentContext.getVarCacheValue(varName);
+ }
+ return value;
+
+ }
+
+ /**
+ * 设置变量值
+ *
+ * @param varName
+ * @param value
+ */
+ public void putVarValue(String nameSpace, String varName, Object value) {
+ if (varName == null || value == null) {
+ return;
+ }
+ if (rule.getVarMap().containsKey(varName)) {
+ varValueMap.putIfAbsent(varName, value);
+ }
+
+ }
+
+ public void setContextConfigure(ContextConfigure contextConfigure) {
+ this.contextConfigure = contextConfigure;
+ }
+
+ public ContextConfigure getContextConfigure() {
+ if (contextConfigure != null) {
+ return contextConfigure;
+ }
+ if (parentContext != null) {
+ return parentContext.getContextConfigure();
+ }
+ return null;
+ }
+
+ @Override
+ public IConfigurableService getConfigurableService() {
+ return rule.getConfigurableService();
+ }
+
+ @Override
+ public AbstractContext copy() {
+ IMessage message = this.message.copy();
+ RuleContext context = new RuleContext(nameSpace, message.getMessageBody(), rule, contextConfigure);
+ super.copyProperty(context);
+ context.actionExecutor = actionExecutor;
+ context.errorMessageList = errorMessageList;
+ context.expressionValueMap = expressionValueMap;
+ context.functionService = functionService;
+ context.ruleMonitor = ruleMonitor;
+ context.parentContext = parentContext;
+ context.varValueMap = varValueMap;
+ context.configurableService = configurableService;
+ return context;
+ }
+
+ public ExecutorService getActionExecutor() {
+ if (actionExecutor != null) {
+ return actionExecutor;
+ } else {
+ return parentContext.getActionExecutor();
+ }
+ }
+
+ public void setNameSpace(String nameSpace) {
+ this.nameSpace = nameSpace;
+ }
+
+ public ConcurrentMap<String, Object> getVarValueMap() {
+ return varValueMap;
+ }
+
+ public void setVarValueMap(ConcurrentMap<String, Object> varValueMap) {
+ this.varValueMap = varValueMap;
+ }
+
+ public ConcurrentMap<String, Boolean> getExpressionValueMap() {
+ return expressionValueMap;
+ }
+
+ public void setExpressionValueMap(ConcurrentMap<String, Boolean> expressionValueMap) {
+ this.expressionValueMap = expressionValueMap;
+ }
+
+ public Vector<String> getErrorMessageList() {
+ return errorMessageList;
+ }
+
+ public void setErrorMessageList(Vector<String> errorMessageList) {
+ this.errorMessageList = errorMessageList;
+ }
+
+ public ScanFunctionService getFunctionService() {
+ return functionService;
+ }
+
+ public RuleContext getParentContext() {
+ return parentContext;
+ }
+
+ public void setParentContext(RuleContext parentContext) {
+ this.parentContext = parentContext;
+ }
+
+ public Rule getRule() {
+ return rule;
+ }
+
+ public void setRule(Rule rule) {
+ this.rule = rule;
+ }
+
+ public IMonitor getRuleMonitor() {
+ return ruleMonitor;
+ }
+
+ public void setRuleMonitor(IMonitor ruleMonitor) {
+ this.ruleMonitor = ruleMonitor;
+ }
+
+ public TopologyFilterMonitor getExpressionMonitor() {
+ return expressionMonitor;
+ }
+
+ public void setExpressionMonitor(TopologyFilterMonitor expressionMonitor) {
+ this.expressionMonitor = expressionMonitor;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/context/RuleMessage.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/context/RuleMessage.java
new file mode 100644
index 0000000..d7831f1
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/context/RuleMessage.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.context;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.rocketmq.streams.filter.monitor.rule.MessageMonitor;
+import org.apache.rocketmq.streams.common.context.Message;
+
+public class RuleMessage extends Message {
+
+ protected MessageMonitor messageMonitor;
+
+ public RuleMessage(JSONObject message) {
+ super(message);
+ messageMonitor = createMessageMonitor(message.toJSONString());
+ }
+
+ public MessageMonitor getMessageMonitor() {
+ return messageMonitor;
+ }
+
+ /**
+ * 创建一条信息的监控对象
+ *
+ * @param message
+ * @return
+ */
+ protected MessageMonitor createMessageMonitor(String message) {
+ MessageMonitor messageMonitor = new MessageMonitor();
+ messageMonitor.begin(message);
+ return messageMonitor;
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/engine/IRuleEngine.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/engine/IRuleEngine.java
new file mode 100644
index 0000000..7db56a9
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/engine/IRuleEngine.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.engine;
+
+import java.util.List;
+
+import org.apache.rocketmq.streams.filter.context.RuleMessage;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.common.context.AbstractContext;
+
+public interface IRuleEngine {
+
+ /**
+ * 把消息相关的上下文传递给规则引擎,规则引擎进行变量封装,资源检查,规则执行等操作
+ *
+ * @param message 包含变量相关信息和原始的message信息
+ * @return 触发的规则
+ */
+ List<Rule> executeRule(RuleMessage message, List<Rule> rules);
+
+ /**
+ * 把消息相关的上下文传递给规则引擎,规则引擎进行变量封装,资源检查,规则执行等操作
+ *
+ * @param message 包含变量相关信息和原始的message信息
+ * @return 触发的规则
+ */
+ List<Rule> executeRule(AbstractContext context, RuleMessage message, List<Rule> rules);
+
+ /**
+ * 把消息相关的上下文传递给规则引擎,规则引擎进行变量封装,资源检查,规则执行等操作
+ *
+ * @param message 包含变量相关信息和原始的message信息
+ * @return 触发的规则
+ */
+ List<Rule> executeRuleWithoutAction(RuleMessage message, List<Rule> rules);
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/engine/SplitFlow.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/engine/SplitFlow.java
new file mode 100644
index 0000000..ce5807f
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/engine/SplitFlow.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.engine;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.alibaba.fastjson.JSONObject;
+
+public class SplitFlow {
+
+ private static final Log LOG = LogFactory.getLog(SplitFlow.class);
+ private volatile String nameSpace;
+ private JSONObject uuidNameMap = new JSONObject();
+
+ // uuid尾号
+ private volatile String uuidEndNum = NUMBERS_ALL;
+ private volatile String ageis_split_namespaces = "topic_aegis_detect_msg_proc";
+ private volatile String sas_split_namespaces;
+
+ // 全部尾号
+ private static final String NUMBERS_ALL = "all";
+ // 没有尾号
+ private static final String NUMBERS_NONE = "none";
+
+ private void paserConfigToField(String jsonValue) {
+ JSONObject jsonObject = JSONObject.parseObject(jsonValue);
+ this.uuidEndNum = jsonObject.getString("uuidEndNum");
+ this.ageis_split_namespaces = jsonObject.getString("ageis_split_namespaces");
+ this.sas_split_namespaces = jsonObject.getString("sas_split_namespaces");
+ }
+
+ /**
+ * 验证安骑士数据是否满足分流后的条件
+ *
+ * @return
+ */
+ public boolean aegisUuidMatch(String uuid) {
+ if (uuidEndNum == null || "".equals(uuidEndNum)) {
+ LOG.warn("SplitFlow aegisUuidMatch,uuidEndNum is null");
+ return true;
+ }
+ String numbers = uuidEndNum;
+
+ if (NUMBERS_ALL.equals(numbers)) {
+ return true;
+ } else if (NUMBERS_NONE.equals(numbers)) {
+ return false;
+ } else {
+ return this.getNumberResult(uuid, numbers);
+ }
+ }
+
+ /**
+ * 验证安骑士数据是否满足分流后的条件
+ *
+ * @param message
+ * @return
+ */
+ public boolean sasMatch(JSONObject message) {
+ return true;
+ }
+
+ public String getUuidNameByNameSpace(String nameSpace) {
+ String uuid = uuidNameMap.getString(nameSpace);
+ if (uuid == null) {
+ uuid = "uuid";
+ }
+ return uuid;
+ }
+
+ public void setUuidNameJson(String uuidNameJson) {
+ try {
+ this.uuidNameMap = JSONObject.parseObject(uuidNameJson);
+ } catch (Exception e) {
+ LOG.error("SplitFlow setUuidNameJson error" + uuidNameJson, e);
+ }
+
+ }
+
+ /**
+ * 判断尾号是否满足条件
+ *
+ * @return
+ */
+ private boolean getNumberResult(String aliuid, String userNumbers) {
+ String[] numbers = userNumbers.split(",");
+ for (String number : numbers) {
+ if (aliuid.endsWith(number)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String getNameSpace() {
+ return nameSpace;
+ }
+
+ public void setNameSpace(String nameSpace) {
+ this.nameSpace = nameSpace;
+ }
+
+ public String getAgeis_split_namespaces() {
+ return ageis_split_namespaces;
+ }
+
+ public void setAgeis_split_namespaces(String ageis_split_namespaces) {
+ this.ageis_split_namespaces = ageis_split_namespaces;
+ }
+
+ public String getSas_split_namespaces() {
+ return sas_split_namespaces;
+ }
+
+ public void setSas_split_namespaces(String sas_split_namespaces) {
+ this.sas_split_namespaces = sas_split_namespaces;
+ }
+
+ public void setSplitFlowJson(String splitFlowJson) {
+ try {
+ paserConfigToField(splitFlowJson);
+ } catch (Exception e) {
+ LOG.error("SplitFlow setSplitFlowJson error,splitFlowJson is:" + splitFlowJson, e);
+ }
+
+ }
+
+ public JSONObject getUuidNameMap() {
+ return uuidNameMap;
+ }
+
+ public void setUuidNameMap(JSONObject uuidNameMap) {
+ this.uuidNameMap = uuidNameMap;
+ }
+
+ public String getUuidEndNum() {
+ return uuidEndNum;
+ }
+
+ public void setUuidEndNum(String uuidEndNum) {
+ this.uuidEndNum = uuidEndNum;
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/engine/impl/DefaultRuleEngine.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/engine/impl/DefaultRuleEngine.java
new file mode 100644
index 0000000..6ec8f3a
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/engine/impl/DefaultRuleEngine.java
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.engine.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.engine.IRuleEngine;
+import org.apache.rocketmq.streams.filter.operator.action.Action;
+import org.apache.rocketmq.streams.filter.contants.RuleStatus;
+import org.apache.rocketmq.streams.filter.exception.RegexTimeoutException;
+import org.apache.rocketmq.streams.filter.context.RuleMessage;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.expression.RelationExpression;
+import org.apache.rocketmq.streams.common.context.AbstractContext;
+import org.apache.rocketmq.streams.common.monitor.IMonitor;
+import org.apache.rocketmq.streams.common.monitor.group.MonitorCommander;
+import org.apache.rocketmq.streams.common.monitor.TopologyFilterMonitor;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class DefaultRuleEngine implements IRuleEngine {
+
+ private static final Log LOG = LogFactory.getLog(DefaultRuleEngine.class);
+
+ private static final Log RULEENGINE_MESSAGE_LOG = LogFactory.getLog("ruleengine_message");
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ @Override
+ public List<Rule> executeRule(RuleMessage message, List<Rule> rules) {
+ return executeRule(null, message, rules, true);
+ }
+
+ @Override
+ public List<Rule> executeRule(AbstractContext context, RuleMessage message, List<Rule> rules) {
+ return executeRule(context, message, rules, true);
+ }
+
+ protected List<Rule> executeRule(AbstractContext context, RuleMessage message, List<Rule> rules, boolean isAction) {
+ IMonitor rulesMonitor = null;
+ IMonitor monitor = null;
+ if (context != null) {
+ monitor = context.getCurrentMonitorItem();
+ monitor = context.startMonitor("rules");
+ rulesMonitor = monitor;
+ }
+ List<Rule> fireRules = new ArrayList<>();
+ List<Rule> excuteRules = rules;
+ try {
+ if (excuteRules == null) {
+ return fireRules;
+ }
+ try {
+
+ for (Rule rule : excuteRules) {
+ RuleContext ruleContext = new RuleContext(message.getMessageBody(), rule);
+ if (context != null) {
+ context.syncSubContext(ruleContext);
+ }
+ IMonitor ruleMonitor = null;
+ if (monitor != null) {
+ ruleMonitor = monitor.createChildren(rule);
+ ruleContext.setRuleMonitor(ruleMonitor);
+ }
+
+ try {
+
+ boolean isFireRule = executeRule(ruleContext, rule);
+ if (isFireRule == false) {
+ TopologyFilterMonitor piplineExecutorMonitor = message.getHeader().getPiplineExecutorMonitor();
+ if (piplineExecutorMonitor != null) {
+ piplineExecutorMonitor.setNotFireRule(rule.getConfigureName());
+ piplineExecutorMonitor.setNotFireExpression2DependentFields(ruleContext.getExpressionMonitor().getNotFireExpression2DependentFields());
+ }
+ }
+ if (ruleMonitor != null) {
+ ruleMonitor.addContextMessage(isFireRule);
+
+ }
+
+ if (isFireRule) {
+ fireRules.add(rule);
+
+ //doUnRepeateScript(ruleContext, rule);// 执行规则去重脚本
+ if (isAction) {
+ fireAction(ruleContext, rule);
+ }
+ }
+ if (ruleMonitor != null) {
+ ruleMonitor.endMonitor();
+ if (ruleMonitor.isSlow()) {
+ ruleMonitor.setSampleData(ruleContext).put("rule_info", rule.toJsonObject());
+ }
+ }
+
+ } catch (Exception e) {
+ if (!RegexTimeoutException.class.isInstance(e)) {
+ LOG.error("DefaultRuleEngine executeRule for excuteRules error: rule name is: "
+ + rule.getConfigureName(), e);
+ }
+ if (ruleMonitor != null) {
+ ruleMonitor.occureError(e, e.getMessage());
+ ruleContext.addErrorMessage(rule, e.getMessage());
+ ruleMonitor.setSampleData(ruleContext).put("rule_info", rule.toJsonObject());
+ }
+
+ }
+ }
+ } catch (Exception e) {
+ LOG.error("DefaultRuleEngine executeRule error,excuteRules size is " + excuteRules.size()
+ + " ,fireRules size is :" + fireRules.size(), e);
+ }
+ } catch (Exception e) {
+ LOG.error(
+ "DefaultRuleEngine executeRule error: fireRules size is: " + fireRules.size() + "excuteRules size is : "
+ + excuteRules.size(), e);
+
+ }
+ if (rulesMonitor != null) {
+ rulesMonitor.setType(IMonitor.TYPE_DATAPROCESS);
+ rulesMonitor.endMonitor();
+ MonitorCommander.getInstance().finishMonitor(rulesMonitor.getName(), rulesMonitor);
+ }
+ return fireRules;
+
+ }
+
+ @Override
+ public List<Rule> executeRuleWithoutAction(RuleMessage message, List<Rule> rules) {
+ return executeRule(null, message, rules, false);
+ }
+
+ @SuppressWarnings("rawtypes")
+ private void fireAction(RuleContext context, Rule rule) {
+ if (rule == null) {
+ LOG.error("DefaultRuleEngine fireAction error: rules is null!");
+ return;
+ }
+ try {
+ /**
+ * 判断rule的status 如果是观察者模式,则写入到观察者库中的观察表里
+ */
+ if (rule.getRuleStatus().intValue() == RuleStatus.RULE_STATUS_ONLINE.getRuleStatus().intValue()) {// 上线状态
+ if (!context.getContextConfigure().isAction2Online()) {
+ // LOG.warn("DefaultRuleEngine fireAction ignore : configure action2Online false!");
+ if (context.getContextConfigure().isActionOnline2Observer()) {
+ Action action = context.getAction(RuleContext.OBSERVER_NAME);
+ if (action == null) {
+ return;
+ }
+ doAction(context, action, rule);
+ }
+ return;
+ }
+ try {
+ if (rule.getActionNames() == null || rule.getActionNames().size() == 0) {
+ return;
+ }
+ for (String actionName : rule.getActionNames()) {
+ Action action = context.getAction(actionName);
+ if (action == null) {
+ continue;
+ }
+ doAction(context, action, rule);
+ }
+ } catch (Exception e) {
+ LOG.error("DefaultRuleEngine fire atciton error: ruleName is" + rule.getConfigureName(), e);
+ context.addErrorMessage(rule, "DefaultRuleEngine fire atciton error: " + e.getMessage());
+ }
+ } else {
+ if (context.getContextConfigure() != null && !context.getContextConfigure().isAction2Observer()) {
+ return;
+ }
+ Action action = context.getAction(RuleContext.OBSERVER_NAME);
+ if (action == null) {
+ return;
+ }
+ doAction(context, action, rule);
+ }
+
+ } catch (Exception e) {
+ LOG.error("DefaultRuleEngine fireAction error: ruleName is" + rule.getConfigureName(), e);
+ }
+
+ }
+
+ /**
+ * 处理一个规则的一个action
+ *
+ * @param context
+ * @param rule
+ */
+ @SuppressWarnings("rawtypes")
+ protected void doAction(final RuleContext context, final Action action, final Rule rule) {
+ context.getActionExecutor().execute(new Runnable() {
+
+ @Override
+ public void run() {
+ IMonitor monitor = context.getRuleMonitor();
+ IMonitor actionMonitor = monitor.createChildren(action);
+ try {
+
+ action.doAction(context, rule);
+ if (monitor != null) {
+ actionMonitor.endMonitor();
+ if (actionMonitor.isSlow()) {
+ actionMonitor.setSampleData(context).put("action_info", action.toJsonObject());
+ }
+ }
+ } catch (Exception e) {
+ String errorMsg = "DefaultRuleEngine doAction error,rule: " + rule.getRuleCode() + " ,action: "
+ + action.getConfigureName();
+ // RULEENGINE_MESSAGE_LOG.warn(errorMsg
+ // , e);
+ actionMonitor.occureError(e, errorMsg, e.getMessage());
+ actionMonitor.setSampleData(context).put("action_info", action.toJsonObject());
+ }
+
+ }
+ });
+ }
+
+ protected boolean executeRule(RuleContext context, Rule rule) {
+ boolean match = processExpress(rule, context);
+ return match;
+ }
+
+ /**
+ * 处理Express
+ *
+ * @param rule
+ * @param context
+ * @return
+ */
+ @SuppressWarnings("rawtypes")
+ private boolean processExpress(Rule rule, RuleContext context) {
+ try {
+ if (rule.getExpressionName() == null) {
+ return false;
+ }
+ // rule.getGroupExpressionManager().matchAndSetResult(context,rule);
+ Expression expression = context.getExpression(rule.getExpressionName());
+ if (expression == null) {
+ return false;
+ }
+
+ boolean match = expression.getExpressionValue(context, rule);
+ if (!RelationExpression.class.isInstance(expression)) {
+ TopologyFilterMonitor piplineExecutorMonitor = new TopologyFilterMonitor();
+ piplineExecutorMonitor.addNotFireExpression(expression.toString(), expression.getDependentFields(rule.getExpressionMap()));
+ context.setExpressionMonitor(piplineExecutorMonitor);
+ }
+ if (!match) {
+ return false;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ LOG.error("DefaultRuleEngine processExpress error,rule is: " + rule.getConfigureName(), e);
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/exception/RegexTimeoutException.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/exception/RegexTimeoutException.java
new file mode 100644
index 0000000..c69aa2f
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/exception/RegexTimeoutException.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.exception;
+
+public class RegexTimeoutException extends RuntimeException {
+ private String regex;
+ private String context;
+ private long timeout;
+
+ public RegexTimeoutException(String message) {
+ super(message);
+ }
+
+ public RegexTimeoutException(String regex, String context, long timeout) {
+ // super("正则表达式执行超时,超时的正则为:"+regex+"。匹配的内容为:"+context+"。对应的超时时间为:"+timeout);
+ super("The regular expression executes timeout, and the timeout is:" + regex + ". The matching content is "
+ + context + ". The corresponding timeout time is: " + timeout);
+ this.regex = regex;
+ this.context = context;
+ this.timeout = timeout;
+ }
+
+ public String getRegex() {
+ return regex;
+ }
+
+ public String getContext() {
+ return context;
+ }
+
+ public long getTimeout() {
+ return timeout;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/etl/RenameFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/etl/RenameFunction.java
new file mode 100644
index 0000000..880af7b
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/etl/RenameFunction.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.etl;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.common.utils.ReflectUtil;
+import org.apache.rocketmq.streams.common.utils.StringUtil;
+
+@Function
+public class RenameFunction {
+
+ @FunctionMethod("rename")
+ public void rename(JSONObject msg, String newFieldName, String oldFieldName) {
+ String value = ReflectUtil.getBeanFieldOrJsonValue(msg, oldFieldName);
+ if (StringUtil.isEmpty(value)) {
+ return;
+ }
+ msg.remove(oldFieldName);
+ msg.put(newFieldName, value);
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/AbstractExpressionFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/AbstractExpressionFunction.java
new file mode 100644
index 0000000..74a3adc
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/AbstractExpressionFunction.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.exception.RegexTimeoutException;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.common.monitor.IMonitor;
+import org.apache.rocketmq.streams.common.utils.MapKeyUtil;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.rocketmq.streams.filter.operator.Rule;
+
+public abstract class AbstractExpressionFunction implements ExpressionFunction {
+
+ private static final Log LOG = LogFactory.getLog(AbstractExpressionFunction.class);
+ private static final Log RULEENGINE_MESSAGE_LOG = LogFactory.getLog("ruleengine_message");
+
+ @Override
+ public Boolean doFunction(Expression expression, RuleContext context, Rule rule) {
+ String name = MapKeyUtil.createKey(Expression.TYPE, expression.getConfigureName());
+ IMonitor monitor = null;
+ if (context != null && context.getRuleMonitor() != null) {
+ monitor = context.getRuleMonitor().createChildren(name);
+ }
+ try {
+ Boolean result = doExpressionFunction(expression, context, rule);
+ if (monitor != null) {
+ monitor.setResult(result);
+ monitor.endMonitor();
+ if (monitor.isSlow()) {
+ monitor.setSampleData(context).put("expression_info", expression.toJson());
+ }
+ }
+ return result;
+ } catch (RegexTimeoutException e) {
+ LOG.error("AbstractExpressionFunction RegexTimeoutException", e);
+ RULEENGINE_MESSAGE_LOG.warn("AbstractExpressionFunction doFunction error", e);
+ if (monitor != null) {
+ monitor.occureError(e, "AbstractExpressionFunction doFunction error", e.getMessage());
+ monitor.setSampleData(context).put("expression_info", expression.toJsonObject());
+ }
+
+ throw e;
+ }
+
+ }
+
+ @SuppressWarnings("rawtypes")
+ protected abstract Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule);
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/CompareFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/CompareFunction.java
new file mode 100644
index 0000000..a643882
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/CompareFunction.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.script.utils.FunctionUtils;
+import org.apache.rocketmq.streams.common.utils.ReflectUtil;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+
+public abstract class CompareFunction extends AbstractExpressionFunction {
+
+ private static final Log LOG = LogFactory.getLog(CompareFunction.class);
+
+ @Override
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ if (!expression.volidate()) {
+ return false;
+ }
+ Object varValue = null;
+ String varName = expression.getVarName();
+ Var var = context.getVar(varName);
+ varValue = var.getVarValue(context, rule);
+ /**
+ * 两个数字比较的情况
+ */
+ if ((FunctionUtils.isNumber(varName) || FunctionUtils.isConstant(varName)) && varValue == null) {
+ varValue = varName;
+ }
+
+ if (varValue == null || expression.getValue() == null) {
+ return false;
+ }
+ Object basicVarValue = expression.getDataType().getData(varValue.toString());
+ Object basicValue = expression.getDataType().getData(expression.getValue().toString());
+ if (varValue == null || expression.getValue() == null) {
+ return false;
+ }
+ boolean match = false;
+ if (basicValue == null || basicVarValue == null) {
+ return false;
+ }
+
+ Class varClass = basicVarValue == null ? expression.getDataType().getDataClass() : basicVarValue.getClass();
+ Class valueClass = basicValue == null ? expression.getDataType().getDataClass() : basicValue.getClass();
+ try {
+ match = (Boolean)ReflectUtil.invoke(this, "compare",
+ new Class[] {varClass, valueClass},
+ new Object[] {basicVarValue, basicValue});
+ } catch (Exception e) {
+ LOG.error("CompareFunction doFunction ReflectUtil.invoke error: ", e);
+ }
+
+ return match;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/ContainsCaseInsensitiveFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/ContainsCaseInsensitiveFunction.java
new file mode 100644
index 0000000..215ac8a
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/ContainsCaseInsensitiveFunction.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+@Function
+public class ContainsCaseInsensitiveFunction extends AbstractExpressionFunction {
+
+ private static final Log LOG = LogFactory.getLog(ContainsCaseInsensitiveFunction.class);
+
+ @Override
+ @FunctionMethod("containsCaseInsensitiveFunction")
+ @FunctionMethodAilas("包含(忽略大小写)")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+
+ try {
+ if (!expression.volidate()) {
+ return false;
+ }
+
+ Var var = context.getVar(rule.getConfigureName(), expression.getVarName());
+ if (var == null) {
+ return false;
+ }
+ Object varObject = null;
+ Object valueObject = null;
+ varObject = var.getVarValue(context, rule);
+ valueObject = expression.getValue();
+
+ if (varObject == null || valueObject == null) {
+ return false;
+ }
+
+ String varString = "";
+ String regex = "";
+ varString = String.valueOf(varObject).trim();
+ regex = String.valueOf(valueObject).trim();
+
+ if (varString.toLowerCase().contains(regex.toLowerCase())) {
+ return true;
+ } else {
+ return false;
+ }
+ } catch (Exception e) {
+ LOG.error("ContainsCaseInsensitiveFunction error: rule name is: " + rule.getConfigureName(), e);
+ return false;
+ }
+
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/ContainsFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/ContainsFunction.java
new file mode 100644
index 0000000..f3f36ed
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/ContainsFunction.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+
+@Function
+public class ContainsFunction extends AbstractExpressionFunction {
+
+ @Override
+ @FunctionMethod("contains")
+ @FunctionMethodAilas("包含")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ if (!expression.volidate()) {
+ return false;
+ }
+
+ Var var = context.getVar(rule.getConfigureName(), expression.getVarName());
+ if (var == null) {
+ return false;
+ }
+ Object varObject = null;
+ Object valueObject = null;
+ varObject = var.getVarValue(context, rule);
+ valueObject = expression.getValue();
+
+ if (varObject == null || valueObject == null) {
+ return false;
+ }
+
+ String varString = "";
+ String valueString = "";
+ varString = String.valueOf(varObject).trim();
+ valueString = String.valueOf(valueObject).trim();
+
+ if (varString.contains(valueString)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/Equals.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/Equals.java
new file mode 100644
index 0000000..4744c8a
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/Equals.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.common.datatype.DateDataType;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+import org.apache.rocketmq.streams.script.utils.FunctionUtils;
+
+import java.util.Date;
+
+@Function
+public class Equals extends CompareFunction {
+
+ private final static double MIN_VALUE = 0.000001;
+ private static final DateDataType dateDataType = new DateDataType(Date.class);
+
+ /**
+ * 是否是equals的函数名
+ *
+ * @param functionName
+ * @return
+ */
+ public static boolean isEqualFunction(String functionName) {
+ if ("=".equals(functionName) || "==".equals(functionName) || "等于".equals(functionName)) {
+ return true;
+ }
+ return false;
+ }
+
+ @FunctionMethod(value = "=", alias = "==")
+ @FunctionMethodAilas("等于")
+ @Override
+ public Boolean doFunction(Expression expression, RuleContext context, Rule rule) {
+ return super.doFunction(expression, context, rule);
+ }
+
+ public boolean compare(int a, int b) {
+ return a == b;
+ }
+
+ public boolean compare(String a, int b) {
+ try {
+ a = FunctionUtils.getConstant(a);
+ int aa = new Integer(a).intValue();
+ return aa == b;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Integer a, Integer b) {
+ try {
+ Integer aa = new Integer(a);
+ return aa.equals(b);
+ } catch (Exception e) {
+ return false;
+ }
+
+ }
+
+ public boolean compare(String a, String b) {
+ // a = StringUtils.defaultString(a);
+ // b = StringUtils.defaultString(b);
+ // return a.trim().equals(b.trim());
+ a = FunctionUtils.getConstant(a);
+ b = FunctionUtils.getConstant(b);
+ return a.equals(b);
+ }
+
+ public boolean compare(boolean a, boolean b) {
+ return a == b;
+ }
+
+ public boolean compare(Boolean a, Boolean b) {
+ return a.equals(b);
+ }
+
+ public boolean compare(long a, long b) {
+ return a == b;
+ }
+
+ public boolean compare(double a, double b) {
+ if (a - b > -MIN_VALUE && a - b < MIN_VALUE) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean compare(float a, float b) {
+ if (a - b > -MIN_VALUE && a - b < MIN_VALUE) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean compare(Long a, Long b) {
+ try {
+ Long aa = new Long(a);
+ return aa.equals(b);
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Double a, Double b) {
+ if (a - b > -MIN_VALUE && a - b < MIN_VALUE) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean compare(Float a, Float b) {
+ if (a - b > -MIN_VALUE && a - b < MIN_VALUE) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean compare(Date a, Date b) {
+ String ad = dateDataType.toDataJson(a);
+ String bd = dateDataType.toDataJson(b);
+ return compare(ad, bd);
+ }
+
+ public static void main(String args[]) {
+ Equals equals = new Equals();
+
+ String a = "/usr/sbin/sshd";
+ String b = "/usr/sbin/zabbix_agentd";
+
+ long startTime1 = System.currentTimeMillis();
+ for (int i = 0; i < 10000; i++) {
+ a.equals(b);
+ }
+
+ long endTime1 = System.currentTimeMillis() - startTime1;
+ System.out.println("time is:" + endTime1);
+
+ long startTime2 = System.currentTimeMillis();
+ for (int i = 0; i < 10000; i++) {
+ a.trim().equals(b.trim());
+ }
+ long endTime2 = System.currentTimeMillis() - startTime2;
+ System.out.println("time is:" + endTime2);
+
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/ExpressionFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/ExpressionFunction.java
new file mode 100644
index 0000000..1e5687d
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/ExpressionFunction.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+
+public interface ExpressionFunction {
+ /**
+ * FunctionServiceImpl 函数接口,通过集成此方法声明函数
+ *
+ * @param t
+ * @param context
+ * @param rule
+ * @return
+ */
+ Boolean doFunction(Expression expression, RuleContext context, Rule rule);
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/GreaterEquals.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/GreaterEquals.java
new file mode 100644
index 0000000..8a4fd86
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/GreaterEquals.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.common.datatype.DateDataType;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+import org.apache.rocketmq.streams.script.utils.FunctionUtils;
+
+import java.util.Date;
+
+@Function
+public class GreaterEquals extends CompareFunction {
+
+ private static final DateDataType dateDataType = new DateDataType(Date.class);
+
+ @FunctionMethod(value = ">=", alias = ">=")
+ @FunctionMethodAilas("大于等于")
+ @Override
+ public Boolean doFunction(Expression expression, RuleContext context, Rule rule) {
+ return super.doFunction(expression, context, rule);
+ }
+
+ public boolean compare(int a, int b) {
+ return a >= b;
+ }
+
+ public boolean compare(Integer a, Integer b) {
+ return a >= b;
+ }
+
+ public boolean compare(String a, int b) {
+ try {
+ a = FunctionUtils.getConstant(a);
+ int aa = new Integer(a).intValue();
+ return aa >= b;
+ } catch (Exception e) {
+ return false;
+ }
+
+ }
+
+ public boolean compare(String a, Integer b) {
+ try {
+ a = FunctionUtils.getConstant(a);
+ Integer aa = new Integer(a);
+ return aa >= b;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(String a, String b) {
+ a = FunctionUtils.getConstant(a);
+ b = FunctionUtils.getConstant(b);
+ return a.compareTo(b) >= 0;
+ }
+
+ public boolean compare(long a, long b) {
+ return a >= b;
+ }
+
+ public boolean compare(Long a, Long b) {
+ return a >= b;
+ }
+
+ public boolean compare(double a, double b) {
+ return a - b >= 0;
+ }
+
+ public boolean compare(Double a, Double b) {
+ return a - b >= 0;
+ }
+
+ public boolean compare(float a, float b) {
+ return a - b >= 0;
+ }
+
+ public boolean compare(Float a, Float b) {
+ return a - b >= 0;
+ }
+
+ public boolean compare(String a, long b) {
+ try {
+ a = FunctionUtils.getConstant(a);
+ long aa = new Long(a).longValue();
+ return aa >= b;
+ } catch (Exception e) {
+ return false;
+ }
+
+ }
+
+ public boolean compare(String a, Long b) {
+ try {
+ Long aa = new Long(a);
+ return aa >= b;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(String a, double b) {
+ try {
+ double aa = new Double(a).doubleValue();
+ return aa - b >= 0;
+ } catch (Exception e) {
+ return false;
+ }
+
+ }
+
+ public boolean compare(String a, Double b) {
+ try {
+ Double aa = new Double(a);
+ return aa - b >= 0;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(String a, float b) {
+ try {
+ float aa = new Float(a).floatValue();
+ return aa - b >= 0;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(String a, Float b) {
+ try {
+ Float aa = new Float(a);
+ return aa - b >= 0;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Date a, Date b) {
+ String ad = dateDataType.toDataJson(a);
+ String bd = dateDataType.toDataJson(b);
+ return compare(ad, bd);
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/GreaterThan.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/GreaterThan.java
new file mode 100644
index 0000000..185f91a
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/GreaterThan.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.common.datatype.DateDataType;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+import org.apache.rocketmq.streams.script.utils.FunctionUtils;
+
+import java.util.Date;
+
+@Function
+
+public class GreaterThan extends CompareFunction {
+
+ private static final DateDataType dateDataType = new DateDataType(Date.class);
+
+ @FunctionMethod(value = ">", alias = ">")
+ @FunctionMethodAilas("大于")
+ @Override
+ public Boolean doFunction(Expression expression, RuleContext context, Rule rule) {
+ return super.doFunction(expression, context, rule);
+ }
+
+ public boolean compare(int a, int b) {
+ return a > b;
+ }
+
+ public boolean compare(Integer a, Integer b) {
+
+ return a > b;
+ }
+
+ public boolean compare(String a, Integer b) {
+ try {
+ a = FunctionUtils.getConstant(a);
+ Integer aa = new Integer(a);
+ return aa > b;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(String a, String b) {
+ a = FunctionUtils.getConstant(a);
+ b = FunctionUtils.getConstant(b);
+ return a.compareTo(b) > 0;
+ }
+
+ public boolean compare(long a, long b) {
+ return a > b;
+ }
+
+ public boolean compare(double a, double b) {
+ return a - b > 0;
+ }
+
+ public boolean compare(float a, float b) {
+ return a - b > 0;
+ }
+
+ public boolean compare(Long a, Long b) {
+ return a > b;
+ }
+
+ public boolean compare(String a, Long b) {
+ try {
+ a = FunctionUtils.getConstant(a);
+ Long aa = new Long(a);
+ return aa > b;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Double a, Double b) {
+ return a - b > 0;
+ }
+
+ public boolean compare(String a, Double b) {
+ try {
+ a = FunctionUtils.getConstant(a);
+ Double aa = new Double(a);
+ return aa - b > 0;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Float a, Float b) {
+ return a - b > 0;
+ }
+
+ public boolean compare(String a, Float b) {
+ try {
+ a = FunctionUtils.getConstant(a);
+ Float aa = new Float(a);
+ return aa - b > 0;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Date a, Date b) {
+ String ad = dateDataType.toDataJson(a);
+ String bd = dateDataType.toDataJson(b);
+ return compare(ad, bd);
+ }
+
+ public static void main(String args[]) {
+ GreaterThan gt = new GreaterThan();
+ System.out.println(gt.compare("1201", "1200"));
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/InFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/InFunction.java
new file mode 100644
index 0000000..25e7e04
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/InFunction.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.common.cache.softreference.ICache;
+import org.apache.rocketmq.streams.common.cache.softreference.impl.SoftReferenceCache;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+import org.apache.rocketmq.streams.script.utils.FunctionUtils;
+import org.apache.rocketmq.streams.common.utils.StringUtil;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@Function
+public class InFunction extends AbstractExpressionFunction {
+
+ /**
+ * in 的字符串形成set,可以最快o(1)来提高效率
+ */
+ private ICache<Object, Set<String>> cache = new SoftReferenceCache();
+
+ @Override
+ @FunctionMethod(value = "in", alias = "~in")
+ @FunctionMethodAilas("包含")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ if (!expression.volidate()) {
+ return false;
+ }
+
+ Var var = context.getVar(rule.getConfigureName(), expression.getVarName());
+ if (var == null) {
+ return false;
+ }
+ Object varObject = null;
+ Object valueObject = null;
+ varObject = var.getVarValue(context, rule);
+ valueObject = expression.getValue();
+
+ if (varObject == null || valueObject == null) {
+ return false;
+ }
+ String varString = String.valueOf(varObject).trim();
+ Set<String> valueSet = cache.get(valueObject);
+ if (valueSet != null) {
+ return valueSet.contains(varString);
+ }
+
+ String valueString = "";
+
+ valueString = String.valueOf(valueObject).trim();
+ if (StringUtil.isEmpty(valueString)) {
+ return false;
+ }
+ String[] values = valueString.split(",");
+ Set<String> set = new HashSet<>();
+ for (String value : values) {
+ value = FunctionUtils.getConstant(value);
+ set.add(value);
+
+ }
+ cache.put(valueObject, set);
+
+ return set.contains(varString);
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/InMetaData.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/InMetaData.java
new file mode 100644
index 0000000..27aaed1
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/InMetaData.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+
+@Function
+public class InMetaData extends AbstractExpressionFunction {
+
+ @FunctionMethod("engine_in")
+ @FunctionMethodAilas("in(英文逗号分隔)")
+ @Override
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ if (!expression.volidate()) {
+ return false;
+ }
+
+ Var var = context.getVar(rule.getConfigureName(), expression.getVarName());
+ if (var == null) {
+ return false;
+ }
+ Object varObject = null;
+ Object valueObject = null;
+ varObject = var.getVarValue(context, rule);
+ valueObject = expression.getValue();
+
+ if (varObject == null || valueObject == null) {
+ return false;
+ }
+
+ String varString = "";
+ String inStrings = "";
+ varString = String.valueOf(varObject).trim();
+ inStrings = String.valueOf(valueObject).trim();
+ String[] instr = inStrings.split(",");
+ for (String str : instr) {
+ if (str.trim().equals(varString)) {
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/IpContainsFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/IpContainsFunction.java
new file mode 100644
index 0000000..d72cbff
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/IpContainsFunction.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.utils.IPUtil;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+import org.apache.rocketmq.streams.common.utils.StringUtil;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+@Function
+public class IpContainsFunction extends AbstractExpressionFunction {
+
+ private static final Log LOG = LogFactory.getLog(IpContainsFunction.class);
+
+ private String ip;
+ private long start;
+ private long end;
+ private String startIp;
+ private String endIp;
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ @FunctionMethod("ipContains")
+ @FunctionMethodAilas("ip包含")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ try {
+ if (!expression.volidate()) {
+ return false;
+ }
+
+ Var var = context.getVar(rule.getConfigureName(), expression.getVarName());
+ if (var == null) {
+ return false;
+ }
+ Object varObject = null;
+ Object valueObject = null;
+ varObject = var.getVarValue(context, rule);
+ valueObject = expression.getValue();
+
+ if (varObject == null || valueObject == null) {
+ return false;
+ }
+
+ String varString = "";
+ String regex = "";
+ varString = String.valueOf(varObject).trim();
+ regex = String.valueOf(valueObject).trim();
+
+ if (StringUtil.isEmpty(varString) || StringUtil.isEmpty(regex)) {
+ return false;
+ }
+ parseRegexIp(regex);
+ return contains(varString);
+ } catch (Exception e) {
+ LOG.error("IpContainsFunction doExpressionFunction error", e);
+ return false;
+ }
+
+ }
+
+ private void parseRegexIp(String ip) {
+ if (null == ip || "".equals(ip)) {
+ return;
+ }
+
+ int n = ip.indexOf("/");
+ String preIp = ip;
+ int mask = 32;
+ if (n > 0) {
+ preIp = ip.substring(0, n);
+ mask = Integer.parseInt(ip.substring(n + 1));
+ if (!IPUtil.checkMask(mask)) {
+ return;
+ }
+ }
+
+ if (!IPUtil.checkIpFormat(preIp)) {
+ return;
+ }
+ this.ip = ip;
+ this.start = getStart(preIp, mask);
+ this.end = getEnd(preIp, mask);
+ this.startIp = IPUtil.ipToString(this.start);
+ this.endIp = IPUtil.ipToString(this.end);
+ }
+
+ private long getStart(String ip, int mask) {
+ return IPUtil.ipToInt(ip) & IPUtil.getPrefixIp(mask);
+ }
+
+ private long getEnd(String ip, int mask) {
+ return getStart(ip, mask) | IPUtil.getSuffixIp(mask);
+ }
+
+ /*
+ * 判断IP是否包含
+ */
+ private boolean contains(String ip) {
+ // 先判断是否为ip段
+ int n = ip.indexOf("/");
+ if (n <= 0) {
+ if (!IPUtil.checkIpFormat(ip)) {
+ return false;
+ }
+
+ long intIp = IPUtil.ipToInt(ip);
+ return this.start <= intIp && this.end >= intIp;
+ }
+
+ String preIp = ip.substring(0, n);
+ int mask = Integer.parseInt(ip.substring(n + 1));
+ if (!IPUtil.checkMask(mask) || !IPUtil.checkIpFormat(preIp)) {
+ return false;
+ }
+
+ return this.start <= this.getStart(preIp, mask) && this.end >= this.getEnd(preIp, mask);
+ }
+
+ public String getIp() {
+ return ip;
+ }
+
+ public void setIp(String ip) {
+ this.ip = ip;
+ }
+
+ public long getStart() {
+ return start;
+ }
+
+ public void setStart(long start) {
+ this.start = start;
+ }
+
+ public long getEnd() {
+ return end;
+ }
+
+ public void setEnd(long end) {
+ this.end = end;
+ }
+
+ public String getStartIp() {
+ return startIp;
+ }
+
+ public void setStartIp(String startIp) {
+ this.startIp = startIp;
+ }
+
+ public String getEndIp() {
+ return endIp;
+ }
+
+ public void setEndIp(String endIp) {
+ this.endIp = endIp;
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/IsNotNull.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/IsNotNull.java
new file mode 100644
index 0000000..306fcd3
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/IsNotNull.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+
+@Function
+public class IsNotNull extends AbstractExpressionFunction {
+
+ @Override
+ @FunctionMethod(value = "isNotNull", alias = "notNull")
+ @FunctionMethodAilas("不为空")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ Var var = context.getVar(rule.getConfigureName(), expression.getVarName());
+ if (var == null) {
+ return false;
+ }
+ Object varObject = null;
+ varObject = var.getVarValue(context, rule);
+ if (varObject == null) {
+ return false;
+ }
+
+ String varString = String.valueOf(varObject).trim();
+ if ("".equals(varString)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/IsNull.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/IsNull.java
new file mode 100644
index 0000000..7c8a963
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/IsNull.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+
+@Function
+public class IsNull extends AbstractExpressionFunction {
+
+ @Override
+ @FunctionMethod(value = "isNull", alias = "null")
+ @FunctionMethodAilas("为空")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ Var var = context.getVar(rule.getConfigureName(), expression.getVarName());
+ if (var == null) {
+ return true;
+ }
+ Object varObject = null;
+ varObject = var.getVarValue(context, rule);
+ if (varObject == null) {
+ return true;
+ }
+ String varString = String.valueOf(varObject).trim();
+ if ("".equals(varString)) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/LessEquals.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/LessEquals.java
new file mode 100644
index 0000000..da0e5b0
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/LessEquals.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.common.datatype.DateDataType;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+
+import java.util.Date;
+
+@Function
+public class LessEquals extends CompareFunction {
+
+ private static final DateDataType dateDataType = new DateDataType(Date.class);
+
+ @FunctionMethod(value = "<=", alias = "<=")
+ @FunctionMethodAilas("小于等于")
+ @Override
+ public Boolean doFunction(Expression expression, RuleContext context, Rule rule) {
+ return super.doFunction(expression, context, rule);
+ }
+
+ public boolean compare(int a, int b) {
+ return a <= b;
+ }
+
+ public boolean compare(Integer a, Integer b) {
+ return a <= b;
+ }
+
+ public boolean compare(String a, Integer b) {
+ try {
+ Integer aa = new Integer(a);
+ return aa <= b;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(String a, String b) {
+ return a.compareTo(b) <= 0;
+ }
+
+ public boolean compare(long a, long b) {
+ return a <= b;
+ }
+
+ public boolean compare(double a, double b) {
+ return a - b <= 0;
+ }
+
+ public boolean compare(float a, float b) {
+ return a - b <= 0;
+ }
+
+ public boolean compare(Long a, Long b) {
+ return a <= b;
+ }
+
+ public boolean compare(String a, Long b) {
+ try {
+ Long aa = new Long(a);
+ return aa <= b;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Double a, Double b) {
+ return a - b <= 0;
+ }
+
+ public boolean compare(String a, Double b) {
+ try {
+ Double aa = new Double(a);
+ return aa - b <= 0;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Float a, Float b) {
+ return a - b <= 0;
+ }
+
+ public boolean compare(String a, Float b) {
+ try {
+ Float aa = new Float(a);
+ return aa - b <= 0;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Date a, Date b) {
+ String ad = dateDataType.toDataJson(a);
+ String bd = dateDataType.toDataJson(b);
+ return compare(ad, bd);
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/LessThan.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/LessThan.java
new file mode 100644
index 0000000..14683eb
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/LessThan.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.common.datatype.DateDataType;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+
+import java.util.Date;
+
+@Function
+
+public class LessThan extends CompareFunction {
+
+ private static final DateDataType dateDataType = new DateDataType(Date.class);
+
+ @Override
+ @FunctionMethod(value = "<", alias = "<")
+ @FunctionMethodAilas("小于")
+ public Boolean doFunction(Expression expression, RuleContext context, Rule rule) {
+ return super.doFunction(expression, context, rule);
+ }
+
+ public boolean compare(int a, int b) {
+ return a < b;
+ }
+
+ public boolean compare(Integer a, Integer b) {
+ return a < b;
+ }
+
+ public boolean compare(String a, Integer b) {
+ try {
+ Integer aa = new Integer(a);
+ return aa < b;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(String a, String b) {
+ return a.compareTo(b) < 0;
+ }
+
+ public boolean compare(long a, long b) {
+ return a < b;
+ }
+
+ public boolean compare(double a, double b) {
+ return a - b < 0;
+ }
+
+ public boolean compare(float a, float b) {
+ return a - b < 0;
+ }
+
+ public boolean compare(Long a, Long b) {
+ return a < b;
+ }
+
+ public boolean compare(String a, Long b) {
+ try {
+ Long aa = new Long(a);
+ return aa < b;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Double a, Double b) {
+ return a - b < 0;
+ }
+
+ public boolean compare(String a, Double b) {
+ try {
+ Double aa = new Double(a);
+ return aa - b < 0;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Float a, Float b) {
+ return a - b < 0;
+ }
+
+ public boolean compare(String a, Float b) {
+ try {
+ Float aa = new Float(a);
+ return aa - b < 0;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Date a, Date b) {
+ String ad = dateDataType.toDataJson(a);
+ String bd = dateDataType.toDataJson(b);
+ return compare(ad, bd);
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/LikeFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/LikeFunction.java
new file mode 100644
index 0000000..e864202
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/LikeFunction.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.common.cache.softreference.ICache;
+import org.apache.rocketmq.streams.common.cache.softreference.impl.SoftReferenceCache;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+import org.apache.rocketmq.streams.common.optimization.LikeRegex;
+import org.apache.rocketmq.streams.script.utils.FunctionUtils;
+import org.apache.rocketmq.streams.common.utils.StringUtil;
+
+@Function
+public class LikeFunction extends AbstractExpressionFunction {
+ private transient ICache<String, LikeRegex> likeCache = new SoftReferenceCache<>();
+
+ private transient ICache<String, LikeCache> cache = new SoftReferenceCache<>();
+
+ public static boolean isLikeFunciton(String functionName) {
+ return "like".equals(functionName);
+ }
+
+ private class LikeCache {
+ public LikeCache(String containStr, String regexStr, boolean isPrefix) {
+ this.containStr = containStr;
+ this.regexStr = regexStr;
+ this.isPrefix = isPrefix;
+
+ }
+
+ protected String containStr;//预处理like中一定包含的字符串
+ protected String regexStr;//把like转换成regex对应的字符串
+ protected boolean isPrefix = false;//是否是前缀匹配
+
+ }
+
+ @Override
+ @FunctionMethod("like")
+ @FunctionMethodAilas("包含")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ if (!expression.volidate()) {
+ return false;
+ }
+
+ Var var = context.getVar(rule.getConfigureName(), expression.getVarName());
+ if (var == null) {
+ return false;
+ }
+ Object varObject = null;
+ Object valueObject = null;
+ varObject = var.getVarValue(context, rule);
+ valueObject = expression.getValue();
+
+ if (varObject == null || valueObject == null) {
+ return false;
+ }
+
+ String varString = "";
+ String valueString = "";
+ varString = String.valueOf(varObject).trim();
+ valueString = String.valueOf(valueObject).trim();
+ if (StringUtil.isEmpty(valueString)) {
+ return false;
+ }
+ valueString = FunctionUtils.getConstant(valueString);
+ LikeRegex likeRegex = likeCache.get(valueString);
+ if (likeRegex == null) {
+ likeRegex = new LikeRegex(valueString);
+ likeCache.put(valueString, likeRegex);
+ }
+ boolean likeResult = likeRegex.match(varString);
+ // System.out.println(likeResult);
+ return likeResult;
+
+ //LikeCache likeCache=sinkcache.get(valueString);
+ //if(likeCache==null){
+ // String containStr=parseContainStr(valueString);
+ // String regexStr=convertRegex(valueString);
+ // likeCache=new LikeCache(containStr,regexStr,!valueString.startsWith("%"));
+ // sinkcache.put(valueString,likeCache);
+ //}
+ //if(likeCache.containStr!=null){
+ // if(likeCache.isPrefix&&!varString.startsWith(likeCache.containStr)){
+ // return false;
+ // }
+ // if(varString.indexOf(likeCache.containStr)==-1){
+ // return false;
+ // }
+ //}
+ //boolean result= StringUtil.matchRegex(varString,likeCache.regexStr);
+ //return result;
+ }
+
+ protected static String[] regexSpecialWords = {"\\", "$", "(", ")", "*", "+", ".", "[", "]", "?", "^", "{", "}", "|"};
+
+ /**
+ * 把like中一定包含的字符串抽取出来,再正则匹配前,先做预处理
+ *
+ * @param likeStr like的语句
+ * @return 如果能够抽取,返回抽取的字符串,否则返回null
+ */
+ protected static String parseContainStr(String likeStr) {
+ String tmp = likeStr;
+ if (tmp.startsWith("%")) {
+ tmp = tmp.substring(1);
+ }
+ if (tmp.endsWith("%")) {
+ tmp = tmp.substring(0, tmp.length() - 1);
+ }
+ for (String word : regexSpecialWords) {
+ if (tmp.contains(word)) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * regexWord.add("."); regexWord.add("*"); regexWord.add("?"); regexWord.add("{"); regexWord.add("}"); // regexWord.add("\\d"); regexWord.add(")"); // regexWord.add("("); regexWord.add("+"); regexWord.add("\\"); regexWord.add("["); regexWord.add("]"); regexWord.add("^"); regexWord.add("$");
+ *
+ * @param likeStr
+ * @return
+ */
+
+ protected static String convertRegex(String likeStr) {
+ likeStr = likeStr.replace("\\", "\\\\");
+ likeStr = likeStr.replace("[!", "[^");
+ likeStr = likeStr.replace(".", "\\.");
+ likeStr = likeStr.replace("*", "\\*");
+ likeStr = likeStr.replace("_", ".");
+ likeStr = likeStr.replace("+", "\\+");
+
+ if (!likeStr.trim().startsWith("%")) {
+ likeStr = "^" + likeStr;
+ }
+ if (!likeStr.trim().endsWith("%")) {
+ likeStr = likeStr + "$";
+ }
+ likeStr = likeStr.replace("%", ".*");
+ return likeStr;
+ }
+
+ public static void main(String[] args) {
+ String valueString = convertRegex("%/php%");
+ System.out.println(valueString);
+ System.out.println(StringUtil.matchRegex("/www/server/php/70/sbin/php-fpm", valueString));
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotContainsCaseInsensitiveFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotContainsCaseInsensitiveFunction.java
new file mode 100644
index 0000000..1a8d815
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotContainsCaseInsensitiveFunction.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+@Function
+
+public class NotContainsCaseInsensitiveFunction extends AbstractExpressionFunction {
+
+ private static final Log LOG = LogFactory.getLog(NotContainsCaseInsensitiveFunction.class);
+
+ @Override
+ @FunctionMethod(value = "notContainsCaseInsensitiveFunction", alias = "~notContains")
+ @FunctionMethodAilas("不包含(忽略大小写)")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+
+ try {
+ if (!expression.volidate()) {
+ return false;
+ }
+
+ Var var = context.getVar(rule.getConfigureName(), expression.getVarName());
+ if (var == null) {
+ return false;
+ }
+ Object varObject = null;
+ Object valueObject = null;
+ varObject = var.getVarValue(context, rule);
+ valueObject = expression.getValue();
+ if (varObject == null) {
+ return true;
+ }
+ if (valueObject == null) {
+ return false;
+ }
+
+ String varString = "";
+ String valueString = "";
+ varString = String.valueOf(varObject).trim();
+ valueString = String.valueOf(valueObject).trim();
+ if (varString.toLowerCase().contains(valueString.toLowerCase())) {
+ return false;
+ } else {
+ return true;
+ }
+ } catch (Exception e) {
+ LOG.error("ContainsCaseInsensitiveFunction error: rule name is: " + rule.getConfigureName(), e);
+ return false;
+ }
+
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotContainsFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotContainsFunction.java
new file mode 100644
index 0000000..1e4974c
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotContainsFunction.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+
+@Function
+public class NotContainsFunction extends AbstractExpressionFunction {
+
+ @Override
+ @FunctionMethod("notContains")
+ @FunctionMethodAilas("不包含")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ if (!expression.volidate()) {
+ return false;
+ }
+
+ Var var = context.getVar(rule.getConfigureName(), expression.getVarName());
+ if (var == null) {
+ return false;
+ }
+ Object varObject = null;
+ Object valueObject = null;
+ varObject = var.getVarValue(context, rule);
+ valueObject = expression.getValue();
+ if (varObject == null) {
+ return true;
+ }
+ if (valueObject == null) {
+ return false;
+ }
+ String varString = "";
+ String valueString = "";
+ varString = String.valueOf(varObject).trim();
+ valueString = String.valueOf(valueObject).trim();
+
+ if (varString.contains(valueString)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotEquals.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotEquals.java
new file mode 100644
index 0000000..8a57072
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotEquals.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.common.datatype.DateDataType;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+import org.apache.rocketmq.streams.script.utils.FunctionUtils;
+
+import java.util.Date;
+
+@Function
+
+public class NotEquals extends CompareFunction {
+
+ private final static double MIN_VALUE = 0.000001;
+ private static final DateDataType dateDataType = new DateDataType(Date.class);
+
+ @FunctionMethod(value = "!=", alias = "<>")
+ @FunctionMethodAilas("不等于")
+ @Override
+ public Boolean doFunction(Expression expression, RuleContext context, Rule rule) {
+ return super.doFunction(expression, context, rule);
+ }
+
+ public boolean compare(int a, int b) {
+ return a != b;
+ }
+
+ public boolean compare(Integer a, Integer b) {
+ return !a.equals(b);
+ }
+
+ public boolean compare(String a, Integer b) {
+ try {
+ Integer aa = new Integer(a);
+ return !aa.equals(b);
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(String a, String b) {
+ a = FunctionUtils.getConstant(a);
+ b = FunctionUtils.getConstant(b);
+ return !a.equals(b);
+ }
+
+ public boolean compare(boolean a, boolean b) {
+ return a != b;
+ }
+
+ public boolean compare(long a, long b) {
+ return a != b;
+ }
+
+ public boolean compare(double a, double b) {
+ if (a - b > -MIN_VALUE && a - b < MIN_VALUE) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+
+ public boolean compare(float a, float b) {
+ if (a - b > -MIN_VALUE && a - b < MIN_VALUE) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ public boolean compare(Boolean a, Boolean b) {
+ return !a.equals(b);
+ }
+
+ public boolean compare(Long a, Long b) {
+ return !a.equals(b);
+ }
+
+ public boolean compare(String a, Long b) {
+ try {
+ a = FunctionUtils.getConstant(a);
+ Long aa = new Long(a);
+ return !aa.equals(b);
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Double a, Double b) {
+ if (a - b > -MIN_VALUE && a - b < MIN_VALUE) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+
+ public boolean compare(String a, Double b) {
+ try {
+ a = FunctionUtils.getConstant(a);
+ Double aa = new Double(a);
+ if (aa - b > -MIN_VALUE && aa - b < MIN_VALUE) {
+ return false;
+ } else {
+ return true;
+ }
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Float a, Float b) {
+ if (a - b > -MIN_VALUE && a - b < MIN_VALUE) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ public boolean compare(String a, Float b) {
+ try {
+ a = FunctionUtils.getConstant(a);
+ Float aa = new Float(a);
+ if (aa - b > -MIN_VALUE && aa - b < MIN_VALUE) {
+ return false;
+ } else {
+ return true;
+ }
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean compare(Date a, Date b) {
+ String ad = dateDataType.toDataJson(a);
+ String bd = dateDataType.toDataJson(b);
+ return compare(ad, bd);
+ }
+
+ public static void main(String[] args) {
+ Boolean a = true;
+ Boolean b = false;
+ NotEquals not = new NotEquals();
+ System.out.println(not.compare(a, b));
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotInFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotInFunction.java
new file mode 100644
index 0000000..602fe30
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotInFunction.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+
+@Function
+public class NotInFunction extends InFunction {
+
+ @Override
+ @FunctionMethod(value = "!in", alias = "~!in")
+ @FunctionMethodAilas("包含")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ return !super.doExpressionFunction(expression, context, rule);
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotLikeFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotLikeFunction.java
new file mode 100644
index 0000000..e22f4a4
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotLikeFunction.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+
+@Function
+public class NotLikeFunction extends LikeFunction {
+
+ @Override
+ @FunctionMethod(value = "notLike", alias = "!like")
+ @FunctionMethodAilas("not like")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ return !super.doExpressionFunction(expression, context, rule);
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotRegexCaseInsensitiveFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotRegexCaseInsensitiveFunction.java
new file mode 100644
index 0000000..9422b90
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotRegexCaseInsensitiveFunction.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+
+@Function
+
+public class NotRegexCaseInsensitiveFunction extends RegexCaseInsensitiveFunction {
+
+ @Override
+ @FunctionMethod(value = "notRegexCaseInsensitive", alias = "~!regex")
+ @FunctionMethodAilas("正则不匹配(忽略大小写)")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ return !super.doExpressionFunction(expression, context, rule);
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotRegexFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotRegexFunction.java
new file mode 100644
index 0000000..6d28fdc
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/NotRegexFunction.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+
+@Function
+
+public class NotRegexFunction extends RegexFunction {
+
+ @Override
+ @FunctionMethod(value = "notRegex", alias = "!regex")
+ @FunctionMethodAilas("正则不匹配")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ return !super.doExpressionFunction(expression, context, rule);
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/RegexCaseInsensitiveFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/RegexCaseInsensitiveFunction.java
new file mode 100644
index 0000000..1b8003f
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/RegexCaseInsensitiveFunction.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+
+@Function
+
+public class RegexCaseInsensitiveFunction extends RegexFunction {
+
+ @Override
+ @FunctionMethod(value = "regexCaseInsensitive", alias = "~regex")
+ @FunctionMethodAilas("正则匹配(忽略大小写)")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ return super.doExpressionFunction(expression, context, rule);
+ }
+
+ @Override
+ protected boolean caseInsensitive() {
+ return true;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/RegexFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/RegexFunction.java
new file mode 100644
index 0000000..591dbc7
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/RegexFunction.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+import org.apache.rocketmq.streams.common.context.Message;
+import org.apache.rocketmq.streams.common.optimization.CalculationResultCache;
+import org.apache.rocketmq.streams.common.utils.StringUtil;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+@Function
+
+public class RegexFunction extends AbstractExpressionFunction {
+
+ protected static CalculationResultCache calculationResultCache = CalculationResultCache.getInstance();
+
+ private static final Log LOG = LogFactory.getLog(RegexFunction.class);
+ private static final int REGEX_TIME_OUT = -1;
+
+ public static boolean isRegex(String functionName) {
+ if ("regex".equals(functionName) || "regexCaseInsensitive".equals(functionName) || "~regex".equals(functionName)) {
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean isNotRegex(String functionName) {
+ if ("notRegex".equals(functionName) || "!regex".equals(functionName) || "notRegexCaseInsensitive".equals(functionName) || "~!regex".equals(functionName)) {
+ return true;
+ }
+ return false;
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ @FunctionMethod("regex")
+ @FunctionMethodAilas("正则匹配")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+ if (!expression.volidate()) {
+ return false;
+ }
+ Message message = context.getMessage();
+
+ Var var = context.getVar(rule.getConfigureName(), expression.getVarName());
+ if (var == null) {
+ return false;
+ }
+ Object varObject = null;
+ Object valueObject = null;
+ varObject = var.getVarValue(context, rule);
+
+ valueObject = expression.getValue();
+
+ if (varObject == null || valueObject == null) {
+ return false;
+ }
+
+ String varString = "";
+ String regex = "";
+ varString = String.valueOf(varObject).trim();
+ regex = String.valueOf(valueObject).trim();
+ Boolean isMatch = calculationResultCache.match(regex, varString);
+ if (isMatch != null) {
+ return isMatch;
+ }
+ boolean value = false;
+ if (caseInsensitive()) {
+ value = StringUtil.matchRegexCaseInsensitive(varString, regex);
+ } else {
+ value = StringUtil.matchRegex(varString, regex);
+ }
+ if (isMatch == null) {
+ calculationResultCache.registeRegex(regex, varString, value);
+ }
+ return value;
+ }
+
+ @SuppressWarnings({"rawtypes"})
+ protected boolean doPreProcess(RuleContext context, Expression expression, String varString) {
+
+ try {
+ String keyword = expression.getKeyword();
+ if (StringUtil.isEmpty(keyword) || StringUtil.isEmpty(varString)) {
+ return true;
+ }
+ boolean result = true;
+ if (varString.toLowerCase().indexOf(keyword) == -1) {
+ result = false;
+ }
+
+ return result;
+ } catch (Exception e) {
+ LOG.error("AbstractExpressionFunction doPreProcess error", e);
+ return true;
+ }
+
+ }
+
+ protected boolean caseInsensitive() {
+ return false;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/ScriptFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/ScriptFunction.java
new file mode 100644
index 0000000..e20ff2a
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/expression/ScriptFunction.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.expression;
+
+import com.alibaba.fastjson.JSONObject;
+
+import org.apache.rocketmq.streams.filter.builder.ExpressionBuilder;
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.expression.SimpleExpression;
+import org.apache.rocketmq.streams.script.ScriptComponent;
+import org.apache.rocketmq.streams.script.context.FunctionContext;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethodAilas;
+import org.apache.rocketmq.streams.common.context.Message;
+import org.apache.rocketmq.streams.script.utils.FunctionUtils;
+import org.apache.rocketmq.streams.common.utils.StringUtil;
+import org.apache.rocketmq.streams.script.parser.imp.FunctionParser;
+import org.apache.rocketmq.streams.script.service.IScriptExpression;
+
+import java.util.List;
+
+/**
+ * 可以把script脚本发布成规则引擎的表达式格式如下("随便写会被忽略","operator","equals(fieldname,'3232')"); 函数必须返回的是boolean值
+ */
+
+@Function
+public class ScriptFunction extends AbstractExpressionFunction {
+ ScriptComponent scriptComponent = ScriptComponent.getInstance();
+ public static final String SPLIT_SIGN = "######";//对参数进行分隔
+ public static final String QUOTATION_CONVERT = "^^^^";//单引号转换
+
+ @Override
+ @FunctionMethod("operator")
+ @FunctionMethodAilas("执行脚本")
+ public Boolean doExpressionFunction(Expression expression, RuleContext context, Rule rule) {
+
+ Object valueObject = expression.getValue();
+ String valueString = "";
+
+ valueString = String.valueOf(valueObject).trim();
+ if (StringUtil.isEmpty(valueString)) {
+ return false;
+ }
+
+ String script = createScript(valueString, context, expression);
+ List<IScriptExpression> expressions = FunctionParser.getInstance().parse(script);
+ if (expressions == null || expressions.size() == 0) {
+ throw new RuntimeException("execute operator function error,parse function error " + valueString);
+ }
+ IScriptExpression scriptExpression = expressions.get(0);
+ FunctionContext functionContext = new FunctionContext(context.getMessage());
+ context.syncSubContext(functionContext);
+ Object object = scriptExpression.executeExpression(context.getMessage(), functionContext);
+ if (object == null) {
+ throw new RuntimeException("execute scriptFunction error, expect return boolean value ,real is null");
+ }
+ if (Boolean.class.isInstance(object)) {
+ return (Boolean)object;
+ }
+ throw new RuntimeException("execute scriptFunction error, expect return boolean value ,real is " + object.getClass().getName());
+ }
+
+ /**
+ * 解析函数中的参数,如果参数是表达式,先进行表达式解析,得到参数值
+ *
+ * @param valueString
+ * @param context
+ * @param expression
+ * @return
+ */
+ private String createScript(String valueString, RuleContext context, Expression expression) {
+ valueString = FunctionUtils.getConstant(valueString);
+ String functionStr = valueString;
+
+ if (valueString.endsWith(";")) {
+ functionStr = functionStr.substring(0, functionStr.length() - 1);
+ }
+ int index = functionStr.indexOf("(");
+ String functionName = functionStr.substring(0, index);
+ functionStr = functionStr.substring(index + 1, valueString.length() - 1);
+ String[] values = functionStr.split(SPLIT_SIGN);
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append(functionName + "(");
+ boolean isFirst = true;
+ for (String value : values) {
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ stringBuilder.append(",");
+ }
+ if (isExpression(value)) {
+ boolean paramter = ExpressionBuilder.executeExecute(expression.getNameSpace(), value, context.getMessage().getMessageBody());
+ stringBuilder.append(paramter);
+ } else {
+ stringBuilder.append(value);
+ }
+ }
+ stringBuilder.append(");");
+ return stringBuilder.toString();
+ }
+
+ protected boolean isExpression(String paramter) {
+ if (StringUtil.isEmpty(paramter)) {
+ return false;
+ }
+ paramter = paramter.trim();
+ if (paramter.startsWith("(") && paramter.endsWith(")")) {
+ return true;
+ }
+ return false;
+ }
+
+ public static void main(String[] args) {
+ ScriptFunction scriptFunction = new ScriptFunction();
+ Expression expression = new SimpleExpression("inner_message", "operator", "'~((___lower_proc_path_1,regex,\'"
+ + "(rdesktop|filebeat|\\/tmp\\/go-build|headless_shell|dongxingrpc|流量|scheduler|check|gpg-agent"
+ + "|/fireball|magneticod|portainer|kube|/vfs|jingling|/netstat|busybox|/tomcat|server|ping|bitcoind"
+ + "|/desktop|docker|aliprobe|safe|update|service|query|svchost\\"
+ + ".exe|mozilla|firefox|ss-server|parity|aria2c|daemon|software|/perl|/redis|/gnome|/bin/ssh|msinfo\\"
+ + ".exe|chrome\\.exe|/bin/ping|/ali|/usr/local/cloudmonitor/|sock|w3wp\\"
+ + ".exe|/usr/local/aegis|/phantomjs|filenet|tunnel|probe)\'))'");
+
+ Message message = new Message(new JSONObject());
+ Rule rule = new Rule();
+ rule.setNameSpace("3r3");
+ scriptFunction.doExpressionFunction(expression, new RuleContext(new JSONObject(), rule), rule);
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/script/CaseFunction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/script/CaseFunction.java
new file mode 100644
index 0000000..c63b724
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/function/script/CaseFunction.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.function.script;
+
+import java.util.List;
+
+import com.alibaba.fastjson.JSONObject;
+
+import org.apache.rocketmq.streams.filter.builder.ExpressionBuilder;
+import org.apache.rocketmq.streams.script.ScriptComponent;
+import org.apache.rocketmq.streams.script.context.FunctionContext;
+import org.apache.rocketmq.streams.script.annotation.Function;
+import org.apache.rocketmq.streams.script.annotation.FunctionMethod;
+import org.apache.rocketmq.streams.common.context.IMessage;
+import org.apache.rocketmq.streams.script.utils.FunctionUtils;
+
+@Function
+public class CaseFunction {
+
+ /**
+ * if(((((___compare_1&___compare_2)&___compare_3)&___compare_4)&___in_1)){___case_1='可疑编码命令';};
+ *
+ * @param message
+ * @param context
+ * @param value
+ * @return
+ */
+ @FunctionMethod(value = "if", alias = "case", comment = "支持内嵌函数")
+ public Boolean match(IMessage message, FunctionContext context, String value) {
+ String tmp = value;
+ value = FunctionUtils.getValueString(message, context, value);
+ if (value == null) {
+ value = tmp;
+ }
+ if (value.length() <= 5) {
+ String lowValue = value.trim().toLowerCase();
+ if (lowValue.equals("true") || lowValue.equals("false")) {
+ return Boolean.valueOf(value);
+ }
+ }
+ if (value.startsWith("(") && value.endsWith(")")) {
+ String expression = value;
+ Boolean result = ExpressionBuilder.executeExecute(System.currentTimeMillis() + "", expression,
+ message.getMessageBody());
+ //message.getHeader().getRegex2Value().put(value,result);
+ return result;
+ }
+ return false;
+ }
+
+ @FunctionMethod(value = "!", alias = "!if", comment = "支持内嵌函数")
+ public Boolean notma(IMessage message, FunctionContext context, String value) {
+ return !match(message, context, value);
+ }
+
+ //(___in_2&((___compare_5|__fdfddf)|___compare_7))
+ //(___in_2&((___compare_5|(a,==,b))|___compare_7))
+ public static void main(String[] args) {
+ String scriptValue
+ = "if((___in_2&((___compare_5|___compare_6)|___compare_7))){___case_1=___cast_java应用执行可疑指令_1;};";
+ ScriptComponent scriptComponent = ScriptComponent.getInstance();
+ JSONObject msg = new JSONObject();
+ msg.put("___in_2", true);
+ msg.put("___compare_5", true);
+ msg.put("___compare_6", true);
+ msg.put("___compare_7", false);
+ msg.put("___cast_java应用执行可疑指令_1", "fdsfdsdsff");
+ List<IMessage> messages = scriptComponent.getService().executeScript(msg, scriptValue);
+ System.out.println(messages.get(0).getMessageBody().toJSONString());
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/Monitor.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/Monitor.java
new file mode 100644
index 0000000..25392e9
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/Monitor.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.monitor;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.rocketmq.streams.filter.exception.RegexTimeoutException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SuppressWarnings("rawtypes")
+public abstract class Monitor<T> {
+
+ protected long startTime;
+ protected long endTime;
+ protected String message;
+ protected T result;
+ protected boolean isException;
+ protected Exception e;
+
+ protected List<Monitor> children = new ArrayList<>();
+
+ public long costTime() {
+ // endTime=0时,Action还没结束,故不统计
+ if (startTime <= 0 || endTime <= 0) {
+ return 0;
+ }
+ return endTime - startTime;
+ }
+
+ public void begin(String message) {
+ this.message = message;
+ startTime = System.currentTimeMillis();
+ }
+
+ public void end() {
+ endTime = System.currentTimeMillis();
+ }
+
+ public void end(T result) {
+ end();
+ if (result != null) {
+ this.result = result;
+ }
+ }
+
+ public void endException(Exception e) {
+ end();
+ this.e = e;
+ this.isException = true;
+ }
+
+ public abstract String getType();
+
+ public void addChildren(Monitor monitor) {
+ children.add(monitor);
+ }
+
+ @Override
+ public String toString() {
+ JSONObject jsonObject = toJson();
+ if (jsonObject == null) {
+ return "";
+ }
+ return jsonObject.toJSONString();
+ }
+
+ public JSONObject toJson() {
+ if (message == null) {
+ return null;
+ }
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("message", message);
+ jsonObject.put("result", resultToString());
+ jsonObject.put("cost_time", costTime());
+ jsonObject.put("type", getType());
+ if (children != null) {
+ JSONArray jsonArray = new JSONArray();
+ for (Monitor monitor : children) {
+ jsonArray.add(monitor.toString());
+ }
+ jsonObject.put("children", jsonArray);
+ }
+
+ return jsonObject;
+ }
+
+ protected String resultToString() {
+ if (result == null) {
+ return "error";
+ }
+ return result.toString();
+ }
+
+ public long getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(long startTime) {
+ this.startTime = startTime;
+ }
+
+ public long getEndTime() {
+ return endTime;
+ }
+
+ public void setEndTime(long endTime) {
+ this.endTime = endTime;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public List<Monitor> getChildren() {
+ return children;
+ }
+
+ public boolean isRegexTimeout() {
+ if (!isException) {
+ return false;
+ }
+ if (RegexTimeoutException.class.isInstance(e)) {
+ return true;
+ }
+ return false;
+ }
+
+ public Exception getException() {
+ return e;
+ }
+
+ public boolean isException() {
+ return isException;
+ }
+
+ public T getResult() {
+ return result;
+ }
+
+ public void setResult(T result) {
+ this.result = result;
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/contants/MonitorType.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/contants/MonitorType.java
new file mode 100644
index 0000000..8228d9a
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/contants/MonitorType.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.monitor.contants;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public enum MonitorType {
+ MESSAGE,
+ RULE,
+ ACTION,
+ VAR,
+ EXPRESSION,
+ NULL;
+
+ public static List<MonitorType> getAllMonitorTypes() {
+ List<MonitorType> monitorTypes = new ArrayList<>();
+ monitorTypes.add(MESSAGE);
+ monitorTypes.add(RULE);
+ monitorTypes.add(ACTION);
+ monitorTypes.add(VAR);
+ monitorTypes.add(EXPRESSION);
+ monitorTypes.add(NULL);
+ return monitorTypes;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/ActionMonitor.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/ActionMonitor.java
new file mode 100644
index 0000000..91578b9
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/ActionMonitor.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.monitor.rule;
+
+import org.apache.rocketmq.streams.filter.monitor.Monitor;
+import org.apache.rocketmq.streams.filter.monitor.contants.MonitorType;
+
+public class ActionMonitor extends Monitor<Object> {
+ @Override
+ public String getType() {
+ return MonitorType.ACTION.name();
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/ExpressionMonitor.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/ExpressionMonitor.java
new file mode 100644
index 0000000..652a6ce
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/ExpressionMonitor.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.monitor.rule;
+
+import org.apache.rocketmq.streams.filter.monitor.Monitor;
+import org.apache.rocketmq.streams.filter.monitor.contants.MonitorType;
+
+public class ExpressionMonitor extends Monitor<Boolean> {
+ @Override
+ public String getType() {
+ return MonitorType.EXPRESSION.name();
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/MessageMonitor.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/MessageMonitor.java
new file mode 100644
index 0000000..4d20a92
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/MessageMonitor.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.monitor.rule;
+
+import org.apache.rocketmq.streams.filter.monitor.Monitor;
+import org.apache.rocketmq.streams.filter.monitor.contants.MonitorType;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@SuppressWarnings("rawtypes")
+public class MessageMonitor extends Monitor<List<Rule>> {
+ public static final String KEY = MessageMonitor.class.getName();
+ private static final NullMonitor NULL_MONITOR = new NullMonitor();
+ private Map<String, Monitor> ruleMonitors = new HashMap<String, Monitor>();
+ private volatile boolean monitorSwtich = true;
+
+ public MessageMonitor(boolean monitorSwtich) {
+ this.monitorSwtich = monitorSwtich;
+ }
+
+ public MessageMonitor() {
+ }
+
+ public Monitor createRuleMonitor(String ruleName) {
+ if (!monitorSwtich) {
+ return NULL_MONITOR;
+ }
+ RuleMonitor ruleMonitor = new RuleMonitor();
+ ruleMonitor.begin(ruleName);
+ this.addChildren(ruleMonitor);
+ return ruleMonitor;
+ }
+
+ public Monitor createExpressionMonitor(String expressionName, String ruleName) {
+ if (!monitorSwtich) {
+ return NULL_MONITOR;
+ }
+ Monitor ruleMonitor = getRuleMonitor(ruleName);
+ if (ruleMonitor == null) {
+ return null;
+ }
+ ExpressionMonitor expressionMonitor = new ExpressionMonitor();
+ expressionMonitor.begin(expressionName);
+ ruleMonitor.addChildren(expressionMonitor);
+ return expressionMonitor;
+ }
+
+ public Monitor createActionMonitor(String actionName, String ruleName) {
+ return NULL_MONITOR;
+ }
+
+ @Override
+ public void addChildren(Monitor monitor) {
+ if (!monitorSwtich) {
+ return;
+ }
+ super.addChildren(monitor);
+ ruleMonitors.put(monitor.getMessage(), monitor);
+ }
+
+ public Monitor getRuleMonitor(String ruleName) {
+ if (!monitorSwtich) {
+ return NULL_MONITOR;
+ }
+ Monitor monitor = ruleMonitors.get(ruleName);
+ if (monitor == null) {
+ return null;
+ }
+ return (RuleMonitor)monitor;
+ }
+
+ @Override
+ protected String resultToString() {
+ if (result == null) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder();
+ boolean isStart = false;
+ for (Rule rule : result) {
+ if (isStart) {
+ isStart = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append(rule.getConfigureName());
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String getType() {
+ return MonitorType.MESSAGE.name();
+ }
+
+ public boolean isMonitorSwtich() {
+ return monitorSwtich;
+ }
+
+ public void setMonitorSwtich(boolean monitorSwtich) {
+ this.monitorSwtich = monitorSwtich;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/NullMonitor.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/NullMonitor.java
new file mode 100644
index 0000000..fcaa652
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/NullMonitor.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.monitor.rule;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.rocketmq.streams.filter.monitor.Monitor;
+import org.apache.rocketmq.streams.filter.monitor.contants.MonitorType;
+
+@SuppressWarnings("rawtypes")
+public class NullMonitor extends Monitor {
+ @Override
+ public void begin(String message) {
+ }
+
+ @Override
+ public void end() {
+ }
+
+ @Override
+ public void end(Object result) {
+ }
+
+ @Override
+ public void addChildren(Monitor monitor) {
+ }
+
+ @Override
+ public String toString() {
+ return null;
+ }
+
+ @Override
+ public JSONObject toJson() {
+ return null;
+ }
+
+ @Override
+ public String getType() {
+ return MonitorType.NULL.name();
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/RuleMonitor.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/RuleMonitor.java
new file mode 100644
index 0000000..450fb99
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/RuleMonitor.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.monitor.rule;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.rocketmq.streams.filter.monitor.Monitor;
+import org.apache.rocketmq.streams.filter.monitor.contants.MonitorType;
+
+import java.util.*;
+
+@SuppressWarnings("rawtypes")
+public class RuleMonitor extends Monitor<Boolean> {
+
+ private Map<String, List<Monitor>> groupByMonitorClassName = new HashMap<>();
+
+ @Override
+ public void addChildren(Monitor monitor) {
+ super.addChildren(monitor);
+ List<Monitor> monitors = groupByMonitorClassName.get(monitor.getClass().getName());
+ if (monitors == null) {
+ monitors = new ArrayList<>();
+ groupByMonitorClassName.put(monitor.getClass().getName(), monitors);
+ }
+ monitors.add(monitor);
+ }
+
+ public List<Monitor> getExpressionMonitors() {
+ return groupByMonitorClassName.get(ExpressionMonitor.class.getName());
+ }
+
+ public List<Monitor> getActionMonitors() {
+ return groupByMonitorClassName.get(ActionMonitor.class.getName());
+ }
+
+ public List<Monitor> getVarMonitors() {
+ return groupByMonitorClassName.get(VarMonitor.class.getName());
+ }
+
+ @Override
+ public JSONObject toJson() {
+ return super.toJson();
+ }
+
+ @Override
+ public String getType() {
+ return MonitorType.RULE.name();
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/VarMonitor.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/VarMonitor.java
new file mode 100644
index 0000000..5156041
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/monitor/rule/VarMonitor.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.monitor.rule;
+
+import org.apache.rocketmq.streams.filter.monitor.Monitor;
+import org.apache.rocketmq.streams.filter.monitor.contants.MonitorType;
+
+public class VarMonitor extends Monitor<Object> {
+ @Override
+ public String getType() {
+ return MonitorType.VAR.name();
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/FilterOperator.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/FilterOperator.java
new file mode 100644
index 0000000..3f8d3e9
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/FilterOperator.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator;
+
+public class FilterOperator extends Rule {
+
+ public FilterOperator(String namespace, String name, String expression) {
+ super(namespace, name, expression);
+ }
+
+ public FilterOperator() {}
+
+ public FilterOperator(String expression) {
+ super(expression);
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/Rule.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/Rule.java
new file mode 100644
index 0000000..4fb2311
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/Rule.java
@@ -0,0 +1,538 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import org.apache.rocketmq.streams.common.channel.sink.ISink;
+import org.apache.rocketmq.streams.filter.operator.action.Action;
+import org.apache.rocketmq.streams.filter.operator.action.impl.ChannelAction;
+import org.apache.rocketmq.streams.filter.optimization.ExpressionOptimization;
+import org.apache.rocketmq.streams.filter.context.RuleMessage;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.expression.GroupExpression;
+import org.apache.rocketmq.streams.filter.operator.expression.GroupExpressionManager;
+import org.apache.rocketmq.streams.filter.operator.expression.RelationExpression;
+import org.apache.rocketmq.streams.filter.operator.var.ContextVar;
+import org.apache.rocketmq.streams.filter.operator.var.InnerVar;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.filter.FilterComponent;
+import org.apache.rocketmq.streams.common.configurable.IConfigurable;
+import org.apache.rocketmq.streams.common.configurable.IAfterConfiguableRefreshListerner;
+import org.apache.rocketmq.streams.common.configurable.IConfigurableService;
+import org.apache.rocketmq.streams.common.context.AbstractContext;
+import org.apache.rocketmq.streams.common.context.IMessage;
+import org.apache.rocketmq.streams.common.topology.ChainStage;
+import org.apache.rocketmq.streams.common.topology.stages.FilterChainStage;
+import org.apache.rocketmq.streams.common.topology.builder.IStageBuilder;
+import org.apache.rocketmq.streams.common.topology.builder.PipelineBuilder;
+import org.apache.rocketmq.streams.common.metadata.MetaData;
+import org.apache.rocketmq.streams.common.metadata.MetaDataField;
+import org.apache.rocketmq.streams.common.topology.model.AbstractRule;
+import org.apache.rocketmq.streams.db.driver.JDBCDriver;
+
+public class Rule extends AbstractRule implements IAfterConfiguableRefreshListerner,
+ IStageBuilder<ChainStage> {
+ private transient volatile Map<String, Var> varMap = new HashMap<>();
+ private transient volatile Map<String, Expression> expressionMap = new HashMap<>();
+ @Deprecated
+ private transient Map<String, Action> actionMap = new HashMap<>();
+ private transient Map<String, MetaData> metaDataMap = new HashMap<>();
+ private transient volatile Map<String, JDBCDriver> dataSourceMap = new HashMap<>();
+ private String expressionStr;//表达式
+
+ protected transient GroupExpressionManager groupExpressionManager;
+ /**
+ * 如果已经完成varmap和expressionmap的初始化,主要是用于兼容老版本规则数据,新规则可以忽略这个字段,值设置为true
+ */
+ private transient boolean isFinishVarAndExpression = false;
+
+ public Rule() {groupExpressionManager = new GroupExpressionManager(this);}
+
+ public Rule(String namespace, String name, String expression) {
+ this();
+ FilterComponent filterComponent = FilterComponent.getInstance();
+ Rule rule = filterComponent.createRule(namespace, name, expression);
+ this.varMap = rule.getVarMap();
+ this.actionMap = rule.getActionMap();
+ this.expressionMap = rule.getExpressionMap();
+ this.metaDataMap = rule.getMetaDataMap();
+ this.dataSourceMap = rule.getDataSourceMap();
+ this.setMsgMetaDataName(rule.getMsgMetaDataName());
+ this.setExpressionStr(rule.getExpressionStr());
+ this.setVarNames(rule.getVarNames());
+ this.setExpressionName(rule.getExpressionName());
+ }
+
+ public Rule(String expression) {
+ this(null, null, expression);
+ }
+
+ public Rule copy() {
+ Rule rule = new Rule();
+ rule.setNameSpace(getNameSpace());
+ rule.setType(getType());
+ rule.setConfigureName(getConfigureName());
+ rule.varMap = varMap;
+ rule.expressionMap = expressionMap;
+ rule.actionMap = actionMap;
+ rule.metaDataMap = metaDataMap;
+ rule.dataSourceMap = dataSourceMap;
+ rule.setActionNames(actionNames);
+ rule.setVarNames(varNames);
+ rule.setExpressionName(expressionName);
+ rule.setMsgMetaDataName(getMsgMetaDataName());
+ rule.setRuleCode(ruleCode);
+ rule.setRuleDesc(ruleDesc);
+ rule.setRuleStatus(ruleStatus);
+ rule.setRuleTitle(ruleTitle);
+ rule.setRuleStatus(ruleStatus);
+ rule.setConfigurableService(configurableService);
+ rule.setPrivateDatas(privateDatas);
+ return rule;
+ }
+
+ @Override
+ public void doProcessAfterRefreshConfigurable(IConfigurableService configurableService) {
+
+ if (isFinishVarAndExpression == false) {
+ this.dataSourceMap = configurableService.queryConfigurableMapByType(ISink.TYPE);
+
+ }
+ initExpression(configurableService);
+ initVar(configurableService);
+ initAction(configurableService);
+ initMetaData(configurableService);
+ //this.optimize();
+ // groupExpressionManager.compile();
+ }
+
+ public void addAction(ChannelAction action) {
+ actionMap.put(action.getConfigureName(), action);
+ this.getActionNames().add(action.getConfigureName());
+ }
+
+ private void initVar(IConfigurableService configurableService) {
+ Map<String, Var> varMap = new HashMap<>();
+ if (isFinishVarAndExpression == false) {
+ varMap = configurableService.queryConfigurableMapByType(Var.TYPE);
+ }
+ if (expressionMap != null) {
+ Iterator<Entry<String, Expression>> it = expressionMap.entrySet().iterator();
+ while (it.hasNext()) {
+ Entry<String, Expression> entry = it.next();
+ Expression expression = entry.getValue();
+ if (RelationExpression.class.isInstance(expression)) {
+ continue;
+ }
+ String varName = expression.getVarName();
+ Var var = varMap.get(varName);
+ if (var == null) {
+ ContextVar contextVar = new ContextVar();
+ contextVar.setNameSpace(expression.getNameSpace());
+ contextVar.setConfigureName(varName);
+ contextVar.setVarName(varName);
+ contextVar.setFieldName(varName);
+ varMap.put(varName, contextVar);
+ var = contextVar;
+ }
+ this.varMap.put(varName, var);
+ }
+ }
+ InnerVar innerVar = new InnerVar();
+ innerVar.setNameSpace(getNameSpace());
+ innerVar.setConfigureName(innerVar.getClass().getSimpleName());
+ innerVar.setVarName(InnerVar.ORIG_MESSAGE);
+ this.varMap.put(innerVar.getVarName(), innerVar);
+ }
+
+ @Override
+ public Set<String> getDependentFields() {
+ Expression expression = expressionMap.get(getExpressionName());
+ return expression.getDependentFields(expressionMap);
+ }
+
+ protected void initExpression(IConfigurableService configurableService) {
+ Map<String, Expression> expressionMap = new HashMap<>();
+ if (isFinishVarAndExpression == false) {
+ expressionMap = configurableService.queryConfigurableMapByType(Expression.TYPE);
+ }
+ String expressionName = getExpressionName();
+ fetchExpression(expressionName, expressionMap);
+ }
+
+ /**
+ * 递归获取所有依赖的expression
+ *
+ * @param expressionName
+ * @param expressionMap
+ */
+ protected void fetchExpression(String expressionName, Map<String, Expression> expressionMap) {
+ Expression expression = expressionMap.get(expressionName);
+ if (expression == null) {
+ return;
+ }
+ this.expressionMap.put(expression.getConfigureName(), expression);
+ if (!RelationExpression.class.isInstance(expression)) {
+ return;
+ }
+ RelationExpression relationExpression = (RelationExpression)expression;
+ List<String> expressionNames = relationExpression.getValue();
+ if (expressionNames != null) {
+ for (String name : expressionNames) {
+ fetchExpression(name, expressionMap);
+ }
+ }
+ }
+
+ protected void initMetaData(IConfigurableService configurableService) {
+ Map<String, MetaData> metaDataMap = new HashMap<>();
+ this.metaDataMap = configurableService.queryConfigurableMapByType(MetaData.TYPE);
+ // MetaData metaData=metaDataMap.get(getMsgMetaDataName());
+ // if(metaData!=null){
+ // this.metaDataMap.put(getMsgMetaDataName(),metaData);
+ // }
+ }
+
+ /**
+ * 赋值action给rule
+ *
+ * @param configurableService
+ */
+ protected void initAction(IConfigurableService configurableService) {
+ Map<String, Action> actionMap = new HashMap<>();
+ actionMap = configurableService.queryConfigurableMapByType(Action.TYPE);
+ if (actionNames != null) {
+ for (String actionName : actionNames) {
+ Action action = actionMap.get(actionName);
+ if (action != null) {
+ this.actionMap.put(actionName, action);
+ }
+ }
+ }
+ }
+
+ public void putConfigurableMap(IConfigurable configurable, String type) {
+ if (Var.TYPE.equals(type)) {
+ varMap.put(configurable.getConfigureName(), (Var)configurable);
+ } else if (Expression.TYPE.equals(type)) {
+ expressionMap.put(configurable.getConfigureName(), (Expression)configurable);
+ } else if (Action.TYPE.equals(type)) {
+ actionMap.put(configurable.getConfigureName(), (Action)configurable);
+ } else if (MetaData.TYPE.equals(type)) {
+ metaDataMap.put(configurable.getConfigureName(), (MetaData)configurable);
+ } else if (ISink.TYPE.equals(type)) {
+ dataSourceMap.put(configurable.getConfigureName(), (JDBCDriver)configurable);
+ }
+ }
+
+ public void insertElement(IConfigurableService ruleEngineConfigurableService) {
+ insertOrUpdate(ruleEngineConfigurableService, varMap.values());
+ insertOrUpdate(ruleEngineConfigurableService, expressionMap.values());
+ insertOrUpdate(ruleEngineConfigurableService, actionMap.values());
+ insertOrUpdate(ruleEngineConfigurableService, metaDataMap.values());
+ insertOrUpdate(ruleEngineConfigurableService, dataSourceMap.values());
+ }
+
+ private <T extends IConfigurable> void insertOrUpdate(IConfigurableService ruleEngineConfigurableService,
+ Collection<T> configurables) {
+ if (configurables == null) {
+ return;
+ }
+ for (IConfigurable configurable : configurables) {
+ ruleEngineConfigurableService.insert(configurable);
+ }
+ }
+
+ public List<IConfigurable> getDependConfigurables() {
+ List<IConfigurable> configurableList = new ArrayList<>();
+ if (varMap != null) {
+ configurableList.addAll(varMap.values());
+ }
+ if (expressionMap != null) {
+ configurableList.addAll(expressionMap.values());
+ }
+ if (actionMap != null) {
+ configurableList.addAll(actionMap.values());
+ }
+ if (metaDataMap != null) {
+ configurableList.addAll(metaDataMap.values());
+ }
+ if (dataSourceMap != null) {
+ configurableList.addAll(dataSourceMap.values());
+ }
+ return configurableList;
+ }
+
+ public Map<String, Var> getVarMap() {
+ return varMap;
+ }
+
+ public void setVarMap(Map<String, Var> varMap) {
+ this.varMap = varMap;
+ }
+
+ public Map<String, Expression> getExpressionMap() {
+ return expressionMap;
+ }
+
+ public void setExpressionMap(Map<String, Expression> expressionMap) {
+ this.expressionMap = expressionMap;
+ }
+
+ public Map<String, Action> getActionMap() {
+ return actionMap;
+ }
+
+ public void setActionMap(Map<String, Action> actionMap) {
+ this.actionMap = actionMap;
+ }
+
+ public Map<String, MetaData> getMetaDataMap() {
+ return metaDataMap;
+ }
+
+ public void setMetaDataMap(Map<String, MetaData> metaDataMap) {
+ this.metaDataMap = metaDataMap;
+ }
+
+ public Map<String, JDBCDriver> getDataSourceMap() {
+ return dataSourceMap;
+ }
+
+ public void setDataSourceMap(Map<String, JDBCDriver> dataSourceMap) {
+ this.dataSourceMap = dataSourceMap;
+ }
+
+ private transient static FilterComponent filterComponent = FilterComponent.getInstance();
+
+ @Override
+ public Boolean doMessage(IMessage message, AbstractContext context) {
+ return execute(message.getMessageBody());
+ }
+
+ public static final String FIRE_RULES = "fireRules";
+
+ public boolean execute(JSONObject msg) {
+ RuleMessage ruleMessage = new RuleMessage(msg);
+ List<Rule> fireRules = filterComponent.excuteRule(ruleMessage, this);
+ msg.put(FIRE_RULES, createJsonArray(fireRules));
+ if (fireRules == null || fireRules.size() == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ private JSONArray createJsonArray(List<Rule> fireRules) {
+ JSONArray jsonArray = new JSONArray();
+ if (fireRules == null) {
+ return jsonArray;
+ }
+ for (Rule rule : fireRules) {
+ jsonArray.add(rule.getNameSpace() + ":" + rule.getConfigureName());
+ }
+ return jsonArray;
+ }
+
+ public String toExpressionString() {
+ Expression rootExpression = expressionMap.get(expressionName);
+ return rootExpression.toExpressionString(expressionMap);
+ }
+
+ public String toMetaDataString() {
+ MetaData metaData = metaDataMap.get(getMsgMetaDataName());
+ if (metaData == null) {
+ return null;
+ }
+ List<MetaDataField> metaDataFields = metaData.getMetaDataFields();
+ StringBuilder sb = new StringBuilder();
+ boolean isFirst = true;
+ for (MetaDataField metaDataField : metaDataFields) {
+ if (metaDataField.getDataType().matchClass(String.class)) {
+ continue;
+ }
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ sb.append(",");
+ }
+ String line = metaDataField.getFieldName() + ";" + metaDataField.getDataType().getDataTypeName();
+ sb.append(line);
+ }
+ return sb.toString();
+ }
+
+ public boolean isFinishVarAndExpression() {
+ return isFinishVarAndExpression;
+ }
+
+ public void setFinishVarAndExpression(boolean finishVarAndExpression) {
+ isFinishVarAndExpression = finishVarAndExpression;
+ }
+
+ public String getExpressionStr() {
+ return expressionStr;
+ }
+
+ public void setExpressionStr(String expressionStr) {
+ this.expressionStr = expressionStr;
+ }
+
+ @Override
+ public ChainStage createStageChain(PipelineBuilder pipelineBuilder) {
+ FilterChainStage filterChainStage = new FilterChainStage();
+ pipelineBuilder.addConfigurables(this);
+ filterChainStage.setRule(this);
+ filterChainStage.setEntityName("filter");
+ filterChainStage.setLabel(this.getConfigureName());
+ return filterChainStage;
+ }
+
+ @Override
+ public void addConfigurables(PipelineBuilder pipelineBuilder) {
+ if (varMap.values() != null) {
+ pipelineBuilder.addConfigurables(varMap.values());
+ }
+ if (expressionMap.values() != null) {
+ pipelineBuilder.addConfigurables(expressionMap.values());
+ }
+ if (actionMap.values() != null) {
+ pipelineBuilder.addConfigurables(actionMap.values());
+ }
+ if (metaDataMap.values() != null) {
+ pipelineBuilder.addConfigurables(metaDataMap.values());
+ }
+ if (dataSourceMap.values() != null) {
+ pipelineBuilder.addConfigurables(dataSourceMap.values());
+ }
+ }
+
+ /**
+ * 做优化,把相同变量的表达式,用hyperscan执行
+ */
+ public void optimize() {
+ Expression root = createOptimizationRule();
+ if (!RelationExpression.class.isInstance(root)) {
+ return;
+ }
+ groupByChildrenExpression((RelationExpression)root);
+ }
+
+ /**
+ * 在同一层关系中,相同变量名的分到一组,统一做处理
+ *
+ * @param root
+ */
+ protected void groupByChildrenExpression(RelationExpression root) {
+ List<String> expressionNames = root.getValue();
+ if (expressionNames == null || expressionNames.size() == 0) {
+ return;
+ }
+ Map<String, GroupExpression> varName2ExpressionNames = new HashMap<>();
+ List<String> newExpressionNames = new ArrayList<>();
+ for (String name : expressionNames) {
+ Expression expression = expressionMap.get(name);
+ //是直接值,不需要再次计算
+ if (expression == null) {
+ newExpressionNames.add(name);
+ continue;
+ }
+
+ //递归分组
+ if (RelationExpression.class.isInstance(expression)) {
+ newExpressionNames.add(name);
+ RelationExpression relationExpression = (RelationExpression)expression;
+ groupByChildrenExpression(relationExpression);
+ continue;
+ }
+ //按变量名分组
+ String varName = expression.getVarName();
+ GroupExpression groupExpression = varName2ExpressionNames.get(varName);
+ if (groupExpression == null) {
+ groupExpression = new GroupExpression(this, varName, root.getRelation().equalsIgnoreCase("or"));
+ varName2ExpressionNames.put(varName, groupExpression);
+
+ }
+ groupExpression.addExpressionName(expression);
+ }
+
+ //如果某个变量的个数>5,去除掉原来多个表达式,换成分组表达式,否则保持不变
+ Iterator<Entry<String, GroupExpression>> it = varName2ExpressionNames.entrySet().iterator();
+ while (it.hasNext()) {
+ Entry<String, GroupExpression> entry = it.next();
+ String varName = entry.getKey();
+ GroupExpression groupExpression = entry.getValue();
+ if (groupExpression.size() < 2) {
+ newExpressionNames.addAll(groupExpression.getAllExpressionNames());
+ } else {
+ groupExpressionManager.addGroupExpression(groupExpression);
+ expressionMap.put(groupExpression.getConfigureName(), groupExpression);
+ newExpressionNames.add(groupExpression.getConfigureName());
+ }
+ }
+ root.setValue(newExpressionNames);
+ }
+
+ /**
+ * 默认规则的解析是多层嵌套,优化的方案是尽量展开
+ */
+ protected Expression createOptimizationRule() {
+ Expression root = expressionMap.get(expressionName);
+ if (!RelationExpression.class.isInstance(root)) {
+ return root;
+ }
+ List<Expression> expressions = new ArrayList<>();
+ List<RelationExpression> relationExpressions = new ArrayList<>();
+ Iterator<Expression> it = expressionMap.values().iterator();
+ while (it.hasNext()) {
+ Expression expression = it.next();
+ if (RelationExpression.class.isInstance(expression)) {
+ relationExpressions.add((RelationExpression)expression);
+ } else {
+ expressions.add(expression);
+ }
+ }
+ ExpressionOptimization expressionOptimization = new ExpressionOptimization(root, expressions, relationExpressions);
+ List<Expression> list = expressionOptimization.optimizate();
+ expressionMap.clear();
+ expressions.clear();
+ relationExpressions.clear();
+ for (Expression express : list) {
+ expressionMap.put(express.getConfigureName(), express);
+ if (RelationExpression.class.isInstance(express)) {
+ relationExpressions.add((RelationExpression)express);
+ } else {
+ expressions.add(express);
+ }
+ }
+ return root;
+ }
+
+ public GroupExpressionManager getGroupExpressionManager() {
+ return groupExpressionManager;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/action/Action.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/action/Action.java
new file mode 100644
index 0000000..5233c44
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/action/Action.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.action;
+
+import org.apache.rocketmq.streams.common.configurable.BasedConfigurable;
+import org.apache.rocketmq.streams.common.configurable.IConfigurable;
+
+public abstract class Action<T> extends BasedConfigurable implements IConfigurableAction<T>, IConfigurable {
+ public static final String TYPE = "action";
+
+ public Action() {
+ setType(TYPE);
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/action/IConfigurableAction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/action/IConfigurableAction.java
new file mode 100644
index 0000000..ddf76cd
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/action/IConfigurableAction.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.action;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+
+public interface IConfigurableAction<T> {
+
+ /**
+ * 根据规则上下文执行configurable组件的默认动作
+ *
+ * @param context 规则上下文,一条数据一个
+ * @param rule 当前正在执行的规则
+ * @return
+ */
+ T doAction(RuleContext context, Rule rule);
+
+ /**
+ * 验证configurable组件的输入参数是否有效
+ *
+ * @param context 规则上下文,一条数据一个
+ * @param rule 当前正在执行的规则
+ * @return
+ */
+ boolean volidate(RuleContext context, Rule rule);
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/action/impl/ChannelAction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/action/impl/ChannelAction.java
new file mode 100644
index 0000000..0f29dea
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/action/impl/ChannelAction.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.action.impl;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.common.configurable.IAfterConfiguableRefreshListerner;
+import org.apache.rocketmq.streams.common.configurable.IConfigurableService;
+import org.apache.rocketmq.streams.common.channel.IChannel;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.rocketmq.streams.filter.operator.action.Action;
+
+public class ChannelAction extends Action<Boolean> implements IAfterConfiguableRefreshListerner {
+
+ private static final Log LOG = LogFactory.getLog(ChannelAction.class);
+ protected String channelName;
+ protected transient IChannel channel;
+
+ public ChannelAction() {
+ setType(Action.TYPE);
+ }
+
+ public ChannelAction setChannel(IChannel channel) {
+ this.channelName = channel.getConfigureName();
+ this.channel = channel;
+ return this;
+ }
+
+ @Override
+ public Boolean doAction(RuleContext context, Rule rule) {
+ channel.batchAdd(context.getMessage());
+ return true;
+ }
+
+ @Override
+ public boolean volidate(RuleContext context, Rule rule) {
+ return true;
+ }
+
+ @Override
+ public void doProcessAfterRefreshConfigurable(IConfigurableService configurableService) {
+ IChannel channel = configurableService.queryConfigurable(IChannel.TYPE, channelName);
+ channel.openAutoFlush();
+ this.channel = channel;
+ }
+
+ public String getChannelName() {
+ return channelName;
+ }
+
+ public void setChannelName(String channelName) {
+ this.channelName = channelName;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/action/impl/MetaDataAction.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/action/impl/MetaDataAction.java
new file mode 100644
index 0000000..47620e2
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/action/impl/MetaDataAction.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.action.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.operator.action.Action;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.common.datatype.DataType;
+import org.apache.rocketmq.streams.common.metadata.MetaData;
+import org.apache.rocketmq.streams.common.metadata.MetaDataAdapter;
+import org.apache.rocketmq.streams.common.metadata.MetaDataField;
+import org.apache.rocketmq.streams.common.utils.DataTypeUtil;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.rocketmq.streams.common.utils.StringUtil;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class MetaDataAction extends Action<Boolean> {
+
+ private static final Log LOG = LogFactory.getLog(MetaDataAction.class);
+ protected String metaDataName;
+ protected Map<String, String> varName2FieldName;
+
+ @Override
+ public Boolean doAction(RuleContext context, Rule rule) {
+ if (!volidate(context, rule)) {
+ return null;
+ }
+ Map<String, Object> fieldName2Value = getQuery(context, rule);
+
+ // /**
+ // * 为开启killchain的规则设置killchainId
+ // */
+ // if (rule.getKillChainFlag() != null && rule.getKillChainFlag().intValue() == 1) {
+ // fieldName2Value.put("killchainId", System.currentTimeMillis() + Math.round(Math.random() * 9999));
+ // }
+ Boolean result = doAction(context, rule, fieldName2Value);
+ return result;
+ }
+
+ protected Boolean doAction(RuleContext context, Rule rule, Map<String, Object> fieldName2Value) {
+ MetaDataAdapter metaDataAdapter = context.getMetaDataAdapter(metaDataName);
+ if (metaDataAdapter == null) {
+ throw new RuntimeException("MetaDataAction doAction metaDataAdapter is null");
+ }
+ return metaDataAdapter.insert(fieldName2Value);
+ }
+
+ public String getMetaDataName() {
+ return metaDataName;
+ }
+
+ public void setMetaDataName(String metaDataName) {
+ this.metaDataName = metaDataName;
+ }
+
+ public Map<String, String> getVarName2FieldName() {
+ return varName2FieldName;
+ }
+
+ public void setVarName2FieldName(Map<String, String> varName2FieldName) {
+ this.varName2FieldName = varName2FieldName;
+ }
+
+ @Override
+ public boolean volidate(RuleContext context, Rule rule) {
+ if (StringUtil.isEmpty(metaDataName)) {
+ return false;
+ }
+ MetaDataAdapter adapter = context.getMetaDataAdapter(metaDataName);
+ if (adapter == null || adapter.getMetaData() == null) {
+ return false;
+ }
+ if (varName2FieldName == null || varName2FieldName.size() == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * 查询数据
+ *
+ * @param context
+ * @param rule
+ * @return
+ */
+ public Map<String, Object> getQuery(RuleContext context, Rule rule) {
+ Map<String, Object> query = new HashMap<String, Object>();
+ MetaData metaData = context.getMetaData(metaDataName);
+ Iterator<Map.Entry<String, String>> it = varName2FieldName.entrySet().iterator();
+ while (it.hasNext()) {
+ try {
+ Map.Entry<String, String> entry = it.next();
+ String varName = entry.getKey();
+ String fieldName = entry.getValue();
+ Var var = context.getVar(rule.getConfigureName(), varName);
+ List<String> values = new ArrayList<>();
+ /**
+ * 需要MetaDataField配置最终输出的字段
+ */
+ try {
+ MetaDataField field = metaData.getMetaDataField(fieldName);
+ if (field == null) {
+ continue;
+ }
+ DataType dataType = field.getDataType();
+ if (query.containsKey(fieldName)) {
+ Object value = query.get(fieldName);
+ if (DataTypeUtil.isList(value.getClass())) {
+ values = (List)value;
+ } else {
+ query.put(fieldName, values);
+ values.add(dataType.toDataJson(value));
+ }
+ values.add(dataType.toDataJson(var.getVarValue(context, rule)));
+ } else {
+ query.put(fieldName, var.getVarValue(context, rule));
+ }
+ } catch (Exception e) {
+ LOG.error("MetaDataAction getQuery error,rule is :" + rule.getRuleCode() + " ,action is: "
+ + rule.getActionNames() + " ,metaDataName is: " + metaDataName + " ,varName is: " + varName
+ + " ,fieldName is: " + fieldName, e);
+ }
+
+ } catch (Exception e) {
+ LOG.error("MetaDataAction getQuery error,rule :" + rule.getRuleCode() + " ,action is:"
+ + rule.getActionNames(), e);
+ }
+
+ }
+ return query;
+ }
+
+ /**
+ * 把action的结果打印出来
+ *
+ * @param context
+ * @param rule
+ * @return
+ */
+ public String toString(RuleContext context, Rule rule) {
+ Map<String, Object> fieldName2Value = getQuery(context, rule);
+
+ JSONObject jsonObject = new JSONObject();
+ Iterator<Map.Entry<String, Object>> it = fieldName2Value.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<String, Object> entry = it.next();
+ String value = String.valueOf(entry.getValue());
+ try {
+ if (value.contains(":")) {
+ jsonObject.put(entry.getKey(), JSONObject.parseObject(value));
+ } else {
+ jsonObject.put(entry.getKey(), entry.getValue());
+ }
+ } catch (Exception e) {
+ jsonObject.put(entry.getKey(), entry.getValue());
+ }
+
+ }
+ return jsonObject.toJSONString();
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/Expression.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/Expression.java
new file mode 100644
index 0000000..4a111c3
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/Expression.java
@@ -0,0 +1,405 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.expression;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.rocketmq.streams.common.configurable.BasedConfigurable;
+import org.apache.rocketmq.streams.common.configurable.IConfigurableService;
+import org.apache.rocketmq.streams.common.utils.AESUtil;
+import org.apache.rocketmq.streams.common.utils.ContantsUtil;
+import org.apache.rocketmq.streams.common.utils.StringUtil;
+import org.apache.rocketmq.streams.script.utils.FunctionUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.rocketmq.streams.filter.operator.action.IConfigurableAction;
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.filter.function.expression.ExpressionFunction;
+import org.apache.rocketmq.streams.filter.operator.var.Var;
+import org.apache.rocketmq.streams.common.configurable.IConfigurable;
+import org.apache.rocketmq.streams.common.datatype.DataType;
+import org.apache.rocketmq.streams.common.datatype.ListDataType;
+import org.apache.rocketmq.streams.common.datatype.StringDataType;
+import org.apache.rocketmq.streams.common.metadata.MetaDataField;
+import org.apache.rocketmq.streams.common.utils.DataTypeUtil;
+
+public class Expression<T> extends BasedConfigurable
+ implements IConfigurable, IConfigurableAction<Boolean>, Serializable {
+ public static final String TYPE = "express";
+ private static final long serialVersionUID = 4610495074511059465L;
+ private static final Log LOG = LogFactory.getLog(Expression.class);
+ private static final String FIELD_COMPARE = "field";
+ private static final String AES_KEY = "baicheng.cbc";
+ private String varName;
+ private String functionName;
+ @SuppressWarnings("rawtypes")
+ private DataType dataType = new StringDataType(String.class);
+ protected T value;
+
+ public Expression copy() {
+ Expression expression = new Expression();
+ expression.setNameSpace(getNameSpace());
+ expression.setType(getType());
+ expression.setConfigureName(getConfigureName());
+ expression.setVarName(varName);
+ expression.setFunctionName(functionName);
+ expression.setDataType(dataType);
+ expression.setValue(value);
+ expression.setPrivateDatas(privateDatas);
+ expression.setAesFlag(aesFlag);
+ return expression;
+ }
+
+ // 前端处理使用
+ private String dataTypestr;
+
+ // 表达式中的关键词 正则之前进行字符串判断
+ private String keyword;
+
+ // 表达式的值是否加密 0未加密 1加密
+ private int aesFlag = 0;
+
+ /**
+ * 表达式是否是字段之间的比较,dataTypestr为field时为true
+ */
+ private boolean fieldFlag = false;
+ private volatile boolean initFlag = false;
+
+ public Expression() {
+ setType(TYPE);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Boolean doAction(RuleContext context, Rule rule) {
+ ExpressionFunction function = context.getExpressionFunction(functionName, this, context, rule);
+ if (function == null) {
+ return false;
+ }
+ /**
+ * 表达式新增fieldFlag 如:src_port=desc_port fieldFlag为true时,设置value为对应的字段值
+ */
+ if (fieldFlag && !initFlag) {
+ synchronized (Expression.class) {
+ if (fieldFlag && !initFlag) {
+ Var var = context.getVar(rule.getConfigureName(), String.valueOf(value));
+ if (var == null) {
+ LOG.error("Expression context.getVar var is null,expressName is: " + this.getConfigureName()
+ + " ,varName is: " + String.valueOf(value));
+ return false;
+ }
+ Object varObject = null;
+ varObject = var.getVarValue(context, rule);
+ if (varObject == null) {
+ LOG.error(
+ "Expression context.getVar varObject is null,expressName is: " + this.getConfigureName()
+ + " ,varName is: " + String.valueOf(value));
+ return false;
+ }
+ this.value = (T)String.valueOf(varObject).trim();
+ }
+ }
+ }
+ boolean result = false;
+ try {
+ result = function.doFunction(this, context, rule);
+ } catch (Exception e) {
+ LOG.error(
+ "Expression doAction function.doFunction error,rule is: " + rule.getConfigureName() + " ,express is:"
+ + getConfigureName(), e);
+ }
+ return result;
+ }
+
+ public Boolean getExpressionValue(RuleContext context, Rule rule) {
+ Boolean result = context.getExpressionValue(getConfigureName());
+ if (result != null) {
+ return result;
+ }
+ try {
+ result = doAction(context, rule);
+ if (result != null) {
+ context.putExpressionValue(getNameSpace(), getConfigureName(), result);
+ }
+ } catch (Exception e) {
+ LOG.error("Expression getExpressionValue error,rule is:" + rule.getConfigureName() + " ,express is: "
+ + getConfigureName(), e);
+ return false;
+ }
+ //if(result==false){
+ // context.setNotFireExpression(this.toString());
+ //}
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public String toJson() {
+ String valueStr = String.valueOf(value);
+ try {
+ // 适配fastjson的bug,parseArray时会对"-"进行分割
+ if (ListDataType.class.isInstance(dataType) && valueStr.contains("-") && valueStr.contains(",")) {
+ this.value = (T)value;
+ } else {
+ // value很多时候会是String类型,这样dataType.toDataJson会报错,所以先转为dataType类型,
+ // list类型JsonArray.tojson会报错,不做处理([pfile_path_rule,proc_vul_exploit_rce_rule])
+ if (!ListDataType.class.isInstance(dataType)) {
+ this.value = (T)dataType.getData(valueStr);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put(IConfigurableService.CLASS_NAME, this.getClass().getName());
+ jsonObject.put("varName", varName);
+ jsonObject.put("functionName", functionName);
+
+ if (FIELD_COMPARE.equals(dataTypestr)) {
+ fieldFlag = true;
+ }
+
+ // 如果是String,且不是字段比较,则加密
+ if (!fieldFlag) {
+ if (StringDataType.class.isInstance(dataType)) {
+ try {
+ jsonObject.put("value", AESUtil.aesEncrypt(valueStr, AES_KEY));
+ jsonObject.put("aesFlag", 1);
+ } catch (Exception e) {
+ jsonObject.put("value", dataType.toDataJson(value));
+ }
+ } else {
+ jsonObject.put("value", dataType.toDataJson(value));
+ }
+ } else {
+ jsonObject.put("value", valueStr);
+ }
+ jsonObject.put("dataType", dataType.toJson());
+ jsonObject.put("keyword", keyword == null ? "" : keyword.toLowerCase().trim());
+ jsonObject.put("fieldFlag", fieldFlag);
+
+ getJsonValue(jsonObject);
+ return jsonObject.toJSONString();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void toObject(String jsonString) {
+ JSONObject jsonObject = JSONObject.parseObject(jsonString);
+ this.varName = jsonObject.getString("varName");
+ this.functionName = jsonObject.getString("functionName");
+ String dataTypeJson = jsonObject.getString("dataType");
+ this.dataType = DataTypeUtil.createDataType(dataTypeJson);
+ String valueJson = jsonObject.getString("value");
+ this.aesFlag = jsonObject.getIntValue("aesFlag");
+ this.fieldFlag = jsonObject.getBooleanValue("fieldFlag");
+
+ // 前端显示用
+ String dataTypestr = "";
+ try {
+ if (dataType != null) {
+ dataTypestr = MetaDataField.getDataTypeStrByType(dataType);
+ }
+ } catch (Exception e) {
+ dataTypestr = "String";
+ }
+
+ if (!fieldFlag) {
+ if (StringDataType.class.isInstance(dataType)) {
+ // value经过加密 需要解密
+ if (aesFlag == 1) {
+ try {
+ this.value = (T)AESUtil.aesDecrypt(valueJson, AES_KEY);
+ } catch (Exception e) {
+ this.value = (T)this.dataType.getData(valueJson);
+ }
+ } else {
+ this.value = (T)this.dataType.getData(valueJson);
+ }
+ } else {
+ this.value = (T)this.dataType.getData(valueJson);
+ }
+ } else {
+ dataTypestr = FIELD_COMPARE;
+ this.value = (T)valueJson;
+ }
+
+ this.dataTypestr = dataTypestr;
+ this.keyword =
+ jsonObject.getString("keyword") == null ? "" : jsonObject.getString("keyword").toLowerCase().trim();
+ setJsonValue(jsonObject);
+ }
+
+ protected void getJsonValue(JSONObject jsonObject) {
+ }
+
+ protected void setJsonValue(JSONObject jsonObject) {
+ }
+
+ public Set<String> getDependentFields(Map<String, Expression> expressionMap) {
+ Set<String> set = new HashSet<>();
+ if (StringUtil.isNotEmpty(varName) && !ContantsUtil.isContant(varName) && !FunctionUtils.isNumber(varName) && !FunctionUtils.isBoolean(varName)) {
+ set.add(varName);
+ }
+ return set;
+ }
+
+ public String getVarName() {
+ return varName;
+ }
+
+ public void setVarName(String varName) {
+ this.varName = varName;
+ }
+
+ public String getFunctionName() {
+ return functionName;
+ }
+
+ public void setFunctionName(String functionName) {
+ this.functionName = functionName;
+ }
+
+ public DataType getDataType() {
+ return dataType;
+ }
+
+ public void setDataType(DataType dataType) {
+ this.dataType = dataType;
+ }
+
+ public T getValue() {
+ return value;
+ }
+
+ public void setValue(T value) {
+ this.value = value;
+ }
+
+ @Override
+ public boolean init() {
+ return true;
+ }
+
+ /**
+ * 是否支持快速匹配,快速匹配的意思是var不需要io
+ *
+ * @param expression
+ * @param context
+ * @param rule
+ * @return
+ */
+ public boolean supportQuickMatch(Expression expression, RuleContext context, Rule rule) {
+ String varName = expression.getVarName();
+ Var var = context.getVar(rule.getConfigureName(), varName);
+ if (var == null || var.canLazyLoad()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * 合法性验证
+ *
+ * @return
+ */
+ public boolean volidate() {
+ if (StringUtil.isEmpty(varName) || StringUtil.isEmpty(functionName) || dataType == null) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean volidate(RuleContext context, Rule rule) {
+ return true;
+ }
+
+ public String getDataTypestr() {
+ return dataTypestr;
+ }
+
+ public void setDataTypestr(String dataTypestr) {
+ this.dataTypestr = dataTypestr;
+ if (FIELD_COMPARE.equals(dataTypestr)) {
+ fieldFlag = true;
+ }
+
+ DataType dt = MetaDataField.getDataTypeByStr(dataTypestr);
+ this.dataType = dt;
+ }
+
+ public String getKeyword() {
+ return keyword;
+ }
+
+ public void setKeyword(String keyword) {
+ if (keyword != null) {
+ keyword = keyword.toLowerCase().trim();
+ }
+ this.keyword = keyword;
+ }
+
+ public int getAesFlag() {
+ return aesFlag;
+ }
+
+ public void setAesFlag(int aesFlag) {
+ this.aesFlag = aesFlag;
+ }
+
+ /**
+ * 返回expressCode
+ *
+ * @return
+ */
+ @SuppressWarnings("rawtypes")
+ public String toExpressionString(Map<String, Expression> name2Expression,
+ String... expressionNamePrefixs) {
+ return toString();
+ }
+
+ @Override
+ public String toString() {
+ String dataType = null;
+ String result = "(" + varName + "," + getFunctionName() + ",";
+ if (!getDataType().matchClass(String.class)) {
+ dataType = getDataType().getDataTypeName();
+ result += dataType + ",";
+ }
+ String value = getDataType().toDataJson(getValue());
+ if (getDataType().matchClass(String.class) && needContants(value)) {
+ value = "'" + value + "'";
+ }
+ result += value + ")";
+
+ return result;
+ }
+
+ private boolean needContants(String value) {
+ if (value.indexOf("(") != -1 || value.indexOf(",") != -1) {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/ExpressionPerformance.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/ExpressionPerformance.java
new file mode 100644
index 0000000..b583cad
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/ExpressionPerformance.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.expression;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ExpressionPerformance implements Runnable {
+ protected static List<ExpressionPerformance> expressionPerformances = new ArrayList<>();
+
+ protected transient volatile List<String> expressionNames = new ArrayList<>();
+ protected transient Map<String, ExpressionStatistic> expressionStatisticMap = new HashMap<>();
+ protected transient long lastTime = System.currentTimeMillis();//最后一次的优化时间
+ protected transient final List<String> values;
+ private static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
+
+ static {
+
+ scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
+ @Override
+ public void run() {
+ for (ExpressionPerformance expressionPerformance : expressionPerformances) {
+ expressionPerformance.run();
+ }
+ }
+ }, 10, 3, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public void run() {
+ List<String> newExpressionNames = new ArrayList<>();
+ List<ExpressionStatistic> statistics = new ArrayList<>();
+ statistics.addAll(expressionStatisticMap.values());
+ Collections.sort(statistics, new Comparator<ExpressionStatistic>() {
+ @Override
+ public int compare(ExpressionStatistic o1, ExpressionStatistic o2) {
+ return o2.count.get() - o1.count.get();
+ }
+ });
+ for (ExpressionStatistic statistic : statistics) {
+ newExpressionNames.add(statistic.name);
+ }
+ this.expressionNames = newExpressionNames;
+ this.lastTime = System.currentTimeMillis();
+ }
+
+ protected static class ExpressionStatistic {
+ protected String name;
+ protected AtomicInteger count = new AtomicInteger(0);
+ }
+
+ public ExpressionPerformance(List<String> values) {
+ this.expressionNames = values;
+ this.values = values;
+ for (String name : expressionNames) {
+ ExpressionStatistic expressionStatistic = new ExpressionStatistic();
+ expressionStatistic.name = name;
+ expressionStatistic.count.set(0);
+ expressionStatisticMap.put(name, expressionStatistic);
+ }
+ expressionPerformances.add(this);
+ }
+
+ public Boolean optimizate(String expressionName, Boolean value) {
+ ExpressionStatistic expressionStatistic = expressionStatisticMap.get(expressionName);
+ expressionStatistic.count.incrementAndGet();
+ return value;
+ }
+
+ public Iterator<String> iterator() {
+ if (expressionNames == null) {
+ return values.iterator();
+ }
+ return expressionNames.iterator();//expressionNames 会做优化,会给快速失效的表达式,加权中
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/ExpressionRelationParser.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/ExpressionRelationParser.java
new file mode 100644
index 0000000..ff813e5
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/ExpressionRelationParser.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.expression;
+
+import org.apache.rocketmq.streams.common.model.NameCreator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ExpressionRelationParser {
+ public static final String OR = "|";
+ public static final String AND = "&";
+
+ /**
+ * 创建表达式
+ *
+ * @param namespace
+ * @param str
+ * @param groupList
+ * @return
+ */
+ public static RelationExpression createRelations(String namespace, String ruleName, String str, List<RelationExpression> groupList) {
+ if (str.indexOf(")") == -1) {
+ return createMixRelation(namespace, ruleName, str, groupList);
+ }
+ int endIndex = str.indexOf(")");
+ int startIndex = 0;
+ for (int i = endIndex; i > 0; i--) {
+ String word = str.substring(i - 1, i);
+ if ("(".equals(word)) {
+ startIndex = i;
+ break;
+ }
+ }
+ String expression = str.substring(startIndex, endIndex);
+ RelationExpression relationExpression = createMixRelation(namespace, ruleName, expression, groupList);
+ str = str.replace("(" + expression + ")", relationExpression.getConfigureName());
+ return createRelations(namespace, ruleName, str, groupList);
+ }
+
+ /**
+ * 混合表达关系处理
+ *
+ * @param namespace
+ * @param str
+ * @return
+ */
+ private static RelationExpression createMixRelation(String namespace, String ruleName, String str,
+ List<RelationExpression> groupList) {
+ if (str.indexOf(OR) == -1) {
+ return createSignleRelation(namespace, ruleName, str, AND, groupList);
+ }
+ String[] values = str.split("\\" + OR);
+ for (String value : values) {
+ String sign = AND;
+ if (value.indexOf(sign) != -1) {
+ RelationExpression relationExpression = createSignleRelation(namespace, ruleName, value, sign, groupList);
+ str = str.replace(value, relationExpression.getConfigureName());
+ }
+ }
+ str = str.replace(" ", "");
+ return createSignleRelation(namespace, ruleName, str, OR, groupList);
+ }
+
+ /**
+ * 纯粹表达关系处理
+ *
+ * @param namespace
+ * @param str
+ * @param sign
+ * @return
+ */
+ private static RelationExpression createSignleRelation(String namespace, String ruleName, String str, String sign,
+ List<RelationExpression> groupList) {
+ NameCreator nameCreator = NameCreator.createOrGet(ruleName);
+ String expressionName = nameCreator.createName(ruleName);
+ RelationExpression relationExpression = new RelationExpression();
+ relationExpression.setNameSpace(namespace);
+ relationExpression.setType(Expression.TYPE);
+ relationExpression.setValue(new ArrayList<String>());
+ relationExpression.setConfigureName(expressionName);
+
+ relationExpression.setRelation(OR.equals(sign) ? "or" : "and");
+ String[] values = str.split("\\" + sign);
+ for (String value : values) {
+ relationExpression.addExpression(value);
+ }
+ groupList.add(relationExpression);
+
+ return relationExpression;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/ExpressionRelationPaser.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/ExpressionRelationPaser.java
new file mode 100644
index 0000000..942ef67
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/ExpressionRelationPaser.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.streams.filter.operator.expression;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.rocketmq.streams.common.model.NameCreator;
+
+public class ExpressionRelationPaser {
+ public static final String OR = "|";
+ public static final String AND = "&";
+
+
+ /**
+ * 创建表达式
+ *
+ * @param namespace
+ * @param str
+ * @param groupList
+ * @return
+ */
+ public static RelationExpression createRelations(String namespace,String ruleName, String str, List<RelationExpression> groupList) {
+ if (str.indexOf(")") == -1) {
+ return createMixRelation(namespace, ruleName,str, groupList);
+ }
+ int endIndex = str.indexOf(")");
+ int startIndex = 0;
+ for (int i = endIndex; i > 0; i--) {
+ String word = str.substring(i - 1, i);
+ if ("(".equals(word)) {
+ startIndex = i;
+ break;
+ }
+ }
+ String expression = str.substring(startIndex, endIndex);
+ RelationExpression relationExpression = createMixRelation(namespace, ruleName,expression, groupList);
+ str = str.replace("(" + expression + ")", relationExpression.getConfigureName());
+ return createRelations(namespace,ruleName, str, groupList);
+ }
+
+ /**
+ * 混合表达关系处理
+ *
+ * @param namespace
+ * @param str
+ * @return
+ */
+ private static RelationExpression createMixRelation(String namespace,String ruleName, String str,
+ List<RelationExpression> groupList) {
+ if (str.indexOf(OR) == -1) {
+ return createSignleRelation(namespace,ruleName, str, AND, groupList);
+ }
+ String[] values = str.split("\\" + OR);
+ for (String value : values) {
+ String sign = AND;
+ if (value.indexOf(sign) != -1) {
+ RelationExpression relationExpression = createSignleRelation(namespace, ruleName, value, sign, groupList);
+ str = str.replace(value, relationExpression.getConfigureName());
+ }
+ }
+ str = str.replace(" ", "");
+ return createSignleRelation(namespace,ruleName, str, OR, groupList);
+ }
+
+ /**
+ * 纯粹表达关系处理
+ *
+ * @param namespace
+ * @param str
+ * @param sign
+ * @return
+ */
+ private static RelationExpression createSignleRelation(String namespace,String ruleName, String str, String sign,
+ List<RelationExpression> groupList) {
+ NameCreator nameCreator=NameCreator.createOrGet(ruleName);
+ String expressionName=nameCreator.createName(ruleName);
+ RelationExpression relationExpression = new RelationExpression();
+ relationExpression.setNameSpace(namespace);
+ relationExpression.setType(Expression.TYPE);
+ relationExpression.setValue(new ArrayList<String>());
+ relationExpression.setConfigureName(expressionName);
+
+ relationExpression.setRelation(OR.equals(sign) ? "or" : "and");
+ String[] values = str.split("\\" + sign);
+ for (String value : values) {
+ relationExpression.addExpression(value);
+ }
+ groupList.add(relationExpression);
+
+ return relationExpression;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/GroupExpression.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/GroupExpression.java
new file mode 100644
index 0000000..69a61e5
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/GroupExpression.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.expression;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.rocketmq.streams.common.model.NameCreator;
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.function.expression.RegexFunction;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.common.cache.compress.impl.IntValueKV;
+import org.apache.rocketmq.streams.common.utils.MapKeyUtil;
+import org.apache.rocketmq.streams.common.utils.StringUtil;
+
+public class GroupExpression extends Expression<List<Expression>> {
+ protected Rule rule;
+ protected String varName;
+ protected static IntValueKV cache = new IntValueKV(3000000);
+ protected boolean isOrRelation = true;//是否是or关系
+ protected Map<String, Boolean> expressionName2Result = new HashMap<>();//正则类表达式的名字和结果的映射
+ protected Set<String> regeExpressionNameSet = new HashSet<>();//正则类表达式的名字
+
+ public GroupExpression(Rule rule, String varName, boolean isOrRelation) {
+ this.rule = rule;
+ this.varName = varName;
+ this.isOrRelation = isOrRelation;
+ this.setConfigureName(NameCreator.createNewName("expression.group"));
+ value = new ArrayList<>();
+ this.setNameSpace(rule.getNameSpace());
+ }
+
+ @Override
+ public Boolean doAction(RuleContext context, Rule rule) {
+ String varValue = context.getMessage().getMessageBody().getString(varName);
+ String key = MapKeyUtil.createKey(getNameSpace(), rule.getConfigureName(), getConfigureName(), varValue);
+ Integer value = cache.get(key);
+ if (value == null) {
+ Boolean isMatch = executeMatch(context, rule);
+ if (isMatch) {
+ value = 1;
+ } else {
+ value = 0;
+ }
+ cache.put(key, value);
+ }
+ return value > 0;
+ }
+
+ @Override
+ public String toString() {
+ return "";
+ }
+
+ @Override
+ public Set<String> getDependentFields(Map<String, Expression> expressionMap) {
+ Set<String> set = new HashSet<>();
+ set.add(varName);
+
+ return set;
+ }
+
+ private Boolean executeMatch(RuleContext context, Rule rule) {
+ for (Expression expression : getValue()) {
+ Boolean result = expressionName2Result.get(expression.getConfigureName());
+ boolean isMatch = false;
+ if (result != null) {
+ isMatch = result;
+ } else {
+ isMatch = expression.doAction(context, rule);
+ }
+ if (isOrRelation) {
+ if (isMatch) {
+ return true;
+ }
+ } else {
+ if (!isMatch) {
+ return false;
+ }
+ }
+ }
+ if (isOrRelation) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ public void addExpressionName(Expression expression) {
+ if (RegexFunction.isRegex(expression.getFunctionName())) {
+ regeExpressionNameSet.add(expression.getConfigureName());
+ }
+ getValue().add(expression);
+ }
+
+ public void setRegexResult(Set<String> allRegexResult) {
+ Iterator<String> it = regeExpressionNameSet.iterator();
+ while (it.hasNext()) {
+ String name = it.next();
+ Boolean value = allRegexResult.contains(name);
+ if (value == null) {
+ value = false;
+ }
+ expressionName2Result.put(name, value);
+
+ }
+ }
+
+ public int size() {
+ return getValue().size();
+ }
+
+ public Collection<? extends String> getAllExpressionNames() {
+ Set<String> names = new HashSet<>();
+ for (Expression expression : getValue()) {
+ names.add(expression.getConfigureName());
+ }
+ return names;
+ }
+
+ public static void main(String[] args) {
+ String content = "abdfdfd";
+ String regex = "ab.*fd";
+ System.out.println(StringUtil.matchRegex(content, regex));
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/GroupExpressionManager.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/GroupExpressionManager.java
new file mode 100644
index 0000000..09e34e9
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/GroupExpressionManager.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.expression;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import com.alibaba.fastjson.JSONObject;
+
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.function.expression.RegexFunction;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.common.optimization.HyperscanRegex;
+
+/**
+ * hyperscan和规则的结合,目前暂不启用。
+ */
+public class GroupExpressionManager {
+ protected Rule rule;
+ protected Map<String, HyperscanRegex> hyperscanRegexMap = new HashMap<>();
+ protected List<GroupExpression> groupExpressions = new ArrayList<>();
+
+ public GroupExpressionManager(Rule rule) {
+ this.rule = rule;
+
+ }
+
+ public void compile() {
+ for (Expression expression : rule.getExpressionMap().values()) {
+ if (SimpleExpression.class.isInstance(expression) && RegexFunction.isRegex(expression.getFunctionName())) {
+ String varName = expression.getVarName();
+ HyperscanRegex hyperscanRegex = hyperscanRegexMap.get(varName);
+ if (hyperscanRegex == null) {
+ hyperscanRegex = new HyperscanRegex();
+ hyperscanRegexMap.put(varName, hyperscanRegex);
+ }
+ hyperscanRegex.addRegex((String)expression.getValue(), expression.getConfigureName());
+ }
+ }
+ for (HyperscanRegex hyperscanRegex : hyperscanRegexMap.values()) {
+ hyperscanRegex.compile();
+ }
+ }
+
+ public void matchAndSetResult(RuleContext context, Rule rule) {
+ Set<String> allRegexResult = new HashSet<>();
+ JSONObject msg = context.getMessage().getMessageBody();
+ Iterator<Entry<String, HyperscanRegex>> it = hyperscanRegexMap.entrySet().iterator();
+ while (it.hasNext()) {
+ Entry<String, HyperscanRegex> entry = it.next();
+ String varName = entry.getKey();
+ String varValue = msg.getString(varName);
+ HyperscanRegex hyperscanRegex = entry.getValue();
+ Set<String> expressionNames = hyperscanRegex.matchExpression(varValue);
+ allRegexResult.addAll(expressionNames);
+ }
+ for (GroupExpression groupExpression : groupExpressions) {
+ groupExpression.setRegexResult(allRegexResult);
+ }
+ }
+
+ public void addGroupExpression(GroupExpression groupExpression) {
+ groupExpressions.add(groupExpression);
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/OptimizationRule.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/OptimizationRule.java
new file mode 100644
index 0000000..28f4001
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/OptimizationRule.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.expression;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.rocketmq.streams.filter.operator.Rule;
+
+public class OptimizationRule {
+ protected Rule rule;
+ protected Map<String, Expression> expressionMap = new HashMap<>();
+ protected String rootExpressionName;
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/RelationExpression.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/RelationExpression.java
new file mode 100644
index 0000000..13b8589
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/RelationExpression.java
@@ -0,0 +1,347 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.expression;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.rocketmq.streams.common.context.Message;
+import org.apache.rocketmq.streams.common.monitor.TopologyFilterMonitor;
+import org.apache.rocketmq.streams.common.utils.StringUtil;
+import org.apache.rocketmq.streams.script.utils.FunctionUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class RelationExpression extends Expression<List<String>> {
+
+ private static final long serialVersionUID = -3213091464347965570L;
+ private static final Log LOG = LogFactory.getLog(RelationExpression.class);
+ private String relation = "and"; // and or
+ // 前端处理使用
+ private String expressions; // @隔开的表达式名称
+
+ protected volatile ExpressionPerformance expressionPerformance;
+
+ @Override
+ protected void getJsonValue(JSONObject jsonObject) {
+ jsonObject.put("relation", relation);
+ }
+
+ @Override
+ protected void setJsonValue(JSONObject jsonObject) {
+ this.relation = jsonObject.getString("relation");
+ }
+
+ public RelationExpression() {
+
+ }
+
+ @Override
+ protected boolean initConfigurable() {
+ boolean success = super.initConfigurable();
+ expressionPerformance = new ExpressionPerformance(getValue());
+ return success;
+ }
+
+ public void addExpression(String expressionName) {
+ value = this.getValue();
+ if (value == null) {
+ value = new ArrayList<String>();
+ }
+ value.add(expressionName);
+ }
+
+ public Iterator<String> iterator() {
+ if (value == null) {
+ return null;
+ }
+ if (expressionPerformance != null) {
+ return expressionPerformance.iterator();//expressionNames 会做优化,会给快速失效的表达式,加权中
+ } else {
+ return value.iterator();
+ }
+
+ }
+
+ public boolean isOrRelation() {
+ return "or".equals(relation);
+ }
+
+ @Override
+ public boolean supportQuickMatch(Expression expression, RuleContext context, Rule rule) {
+ Iterator<String> it = iterator();
+ if (it == null) {
+ return true;
+ }
+ while (it.hasNext()) {
+ String expressionName = it.next();
+ Expression e = context.getExpression(expressionName);
+ if (e == null) {
+ continue;
+ }
+ if (!e.supportQuickMatch(e, context, rule)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public Boolean doAction(RuleContext context, Rule rule) {
+
+ Iterator<String> it = iterator();
+ /**
+ * 如果表达式组的值为空,则返回false
+ */
+ if (!it.hasNext()) {
+ return false;
+ }
+
+ boolean flag = true;
+ if (StringUtil.isEmpty(relation)) {
+ return false;
+ }
+ if ("and".equals(this.relation)) {
+ while (it.hasNext()) {
+ if (flag) {
+ String expressionName = it.next();
+ Expression exp = context.getExpression(expressionName);
+ if (exp == null) {
+ Message message = context.getMessage();
+ Boolean result = message.getMessageBody().getBoolean(expressionName);
+ if (result != null) {
+ if (result == false) {
+ TopologyFilterMonitor piplineExecutorMonitor = new TopologyFilterMonitor();
+ piplineExecutorMonitor.addNotFireExpression(expressionName, expressionName);
+ context.setExpressionMonitor(piplineExecutorMonitor);
+ return optimizate(expressionName, false);
+ } else {
+ continue;
+ }
+ } else {
+ throw new RuntimeException("expect exist expression, but not " + expressionName);
+ }
+
+ }
+
+ if (RelationExpression.class.isInstance(exp)) {
+ Boolean foreachResult = exp.doAction(context, rule);
+ if (foreachResult != null && !foreachResult) {
+ // expressions.add(exp.toString());
+ return optimizate(expressionName, false);
+ }
+ } else {
+ try {
+ flag = exp.getExpressionValue(context, rule);
+ } catch (Exception e) {
+ LOG.error("RelationExpression and function.doFunction error,rule is: "
+ + rule.getConfigureName() + " ,express is: " + exp.getConfigureName(), e);
+ e.printStackTrace();
+ return false;
+ }
+ if (!flag) {
+ TopologyFilterMonitor piplineExecutorMonitor = new TopologyFilterMonitor();
+ piplineExecutorMonitor.addNotFireExpression(exp.toString(), exp.getDependentFields(rule.getExpressionMap()));
+ context.setExpressionMonitor(piplineExecutorMonitor);
+ return optimizate(expressionName, false);
+ }
+ }
+ }
+ }
+ return true;
+ } else {// or
+ flag = false;
+ TopologyFilterMonitor piplineExecutorMonitor = new TopologyFilterMonitor();
+ while (it.hasNext()) {
+ String expressionName = it.next();
+ Expression exp = context.getExpression(expressionName);
+ if (exp == null) {
+ Message message = context.getMessage();
+ Boolean result = message.getMessageBody().getBoolean(expressionName);
+ if (result != null) {
+ if (result == true) {
+ return optimizate(expressionName, true);
+ } else {
+ continue;
+ }
+ } else {
+ throw new RuntimeException("expect exist expression, but not " + expressionName);
+ }
+ }
+ if (RelationExpression.class.isInstance(exp)) {
+ Boolean foreachResult = exp.doAction(context, rule);
+ if (foreachResult != null && foreachResult) {
+ // expressions.add(exp.toString());
+ return optimizate(expressionName, true);
+ } else {
+ //如果关系表达式未触发,则检测context中,有没有因为and失败的条件
+ if (context.getExpressionMonitor() != null && context.getExpressionMonitor().getNotFireExpression2DependentFields().size() > 0) {
+ piplineExecutorMonitor.addNotFireExpression(context.getExpressionMonitor().getNotFireExpression2DependentFields());
+ }
+ }
+ } else {
+
+ try {
+ flag = exp.getExpressionValue(context, rule);
+ } catch (Exception e) {
+ LOG.error(
+ "RelationExpression or function.doFunction error,rule is: " + rule.getConfigureName()
+ + " ,express is: " + exp.getConfigureName(), e);
+ }
+ if (flag) {
+ return optimizate(expressionName, true);
+ }
+ }
+
+ }
+ context.setExpressionMonitor(piplineExecutorMonitor);
+ return false;
+ }
+
+ }
+
+ public Boolean optimizate(String exprssionName, Boolean value) {
+ if (expressionPerformance == null) {
+ synchronized (this) {
+ if (expressionPerformance == null) {
+ expressionPerformance = new ExpressionPerformance(getValue());
+ }
+ }
+
+ }
+ return expressionPerformance.optimizate(exprssionName, value);
+ }
+
+ public String getRelation() {
+ return relation;
+ }
+
+ public void setRelation(String relation) {
+ this.relation = relation;
+ }
+
+ /**
+ * 合法性验证
+ *
+ * @return
+ */
+ @Override
+ public boolean volidate() {
+ return true;
+ }
+
+ public String getExpressions() {
+ return expressions;
+ }
+
+ public void setExpressions(String expressions) {
+ if (expressions == null) {
+ return;
+ }
+ this.expressions = expressions.trim();
+
+ String[] exps = expressions.split("@");
+ if (exps.length > 0) {
+ for (String express : exps) {
+ // 防止表达式组里依赖自己,造成死循环
+ if (this.getConfigureName().equals(express)) {
+ continue;
+ }
+ this.addExpression(express.trim());
+ }
+ }
+
+ }
+
+ @Override
+ public Set<String> getDependentFields(Map<String, Expression> expressionMap) {
+ Set<String> set = new HashSet<>();
+ if (getValue() == null) {
+ return set;
+ }
+ for (String expressionName : value) {
+ Expression expression = expressionMap.get(expressionName);
+ if (expression == null) {
+ if (StringUtil.isNotEmpty(expressionName) && !FunctionUtils.isBoolean(expressionName)) {
+ set.add(expressionName);
+ }
+
+ } else {
+ set.addAll(expression.getDependentFields(expressionMap));
+ }
+ }
+ return set;
+ }
+
+ /**
+ * 前端展示用,不设置value值
+ *
+ * @param expressions
+ */
+ public void setExpressionsNotValue(String expressions) {
+ if (expressions == null) {
+ return;
+ }
+ this.expressions = expressions.trim();
+ }
+
+ @Override
+ public String toExpressionString(Map<String, Expression> name2Expression,
+ String... expressionNamePrefixs) {
+ String sign = "and".equals(relation) ? "&" : "|";
+ StringBuilder sb = new StringBuilder();
+ sb.append("(");
+ boolean isFirst = true;
+ for (String expressionName : this.getValue()) {
+ String expressKey = expressionName;
+ Expression expression = null;
+ if (expressionNamePrefixs != null && expressionNamePrefixs.length > 0) {
+ for (String prefix : expressionNamePrefixs) {
+ expressKey = prefix + expressionName;
+ expression = name2Expression.get(expressKey);
+ if (expression != null) {
+ break;
+ }
+ }
+ } else {
+ expression = name2Expression.get(expressKey);
+ }
+
+ if (expression == null) {
+ continue;
+ }
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ sb.append(sign);
+ }
+ sb.append(
+ expression.toExpressionString(name2Expression, expressionNamePrefixs));
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/SimpleExpression.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/SimpleExpression.java
new file mode 100644
index 0000000..df34a3b
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/expression/SimpleExpression.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.expression;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.rocketmq.streams.filter.contants.RuleElementType;
+import org.apache.rocketmq.streams.filter.builder.ExpressionBuilder;
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.optimization.EqualsExpressionOptimization;
+import org.apache.rocketmq.streams.filter.optimization.IExpressionOptimization;
+import org.apache.rocketmq.streams.filter.optimization.LikeExpressionOptimization;
+import org.apache.rocketmq.streams.filter.optimization.OptimizationExpression;
+import org.apache.rocketmq.streams.filter.optimization.RegexExpressionOptimization;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.common.datatype.DataType;
+import org.apache.rocketmq.streams.common.optimization.CalculationResultCache;
+import org.apache.rocketmq.streams.common.utils.DataTypeUtil;
+
+/**
+ * 变量名就是字段名,不需要声明meta
+ */
+public class SimpleExpression extends Expression {
+ protected static CalculationResultCache calculationResultCache = CalculationResultCache.getInstance();
+
+ public SimpleExpression() {
+ }
+
+ public SimpleExpression(String msgFieldName, String functionName, String value) {
+ this(msgFieldName, functionName, DataTypeUtil.getDataTypeFromClass(String.class), value);
+ }
+
+ public SimpleExpression(String msgFieldName, String functionName, DataType dataType, String value) {
+ setType(RuleElementType.EXPRESSION.getType());
+ setFunctionName(functionName);
+ setVarName(msgFieldName);
+ if (dataType == null) {
+ dataType = DataTypeUtil.getDataTypeFromClass(String.class);
+ }
+ setValue(dataType.getData(value));
+ setDataType(dataType);
+ }
+
+ @Override
+ public Boolean getExpressionValue(RuleContext context, Rule rule) {
+ IExpressionOptimization expressionOptimization = getOptimize();
+ Boolean isHitCache = null;
+ String regex = null;
+ String value = null;
+ if (expressionOptimization != null) {
+ OptimizationExpression expression = expressionOptimization.optimize(this);
+ regex = expression.getRegex();
+ String varName = expression.getVarName();
+ value = context.getMessage().getMessageBody().getString(varName);
+ isHitCache = calculationResultCache.match(regex, value);
+ if (isHitCache != null) {
+ return isHitCache;
+ }
+ }
+ boolean isMatch = super.getExpressionValue(context, rule);
+ if (isHitCache == null && expressionOptimization != null) {
+ calculationResultCache.registeRegex(regex, value, isMatch);
+ }
+ return isMatch;
+ }
+
+ /**
+ * 找到函数对应的优化
+ *
+ * @return
+ */
+ private IExpressionOptimization getOptimize() {
+ for (IExpressionOptimization expressionOptimization : expressionOptimizations) {
+ if (expressionOptimization.support(this)) {
+ return expressionOptimization;
+ }
+ }
+ return null;
+ }
+
+ private static List<IExpressionOptimization> expressionOptimizations = new ArrayList<>();
+
+ static {
+ expressionOptimizations.add(new EqualsExpressionOptimization());
+ expressionOptimizations.add(new RegexExpressionOptimization());
+ expressionOptimizations.add(new LikeExpressionOptimization());
+ }
+
+ public boolean doExecute(JSONObject msg) {
+ return ExpressionBuilder.executeExecute(this, msg);
+ }
+
+ public void setMsgFieldName(String msgFieldName) {
+ setVarName(msgFieldName);
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/var/ConstantVar.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/var/ConstantVar.java
new file mode 100644
index 0000000..8a418bc
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/var/ConstantVar.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.var;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.common.datatype.DataType;
+import org.apache.rocketmq.streams.common.datatype.StringDataType;
+import org.apache.rocketmq.streams.common.metadata.MetaDataField;
+import org.apache.rocketmq.streams.common.utils.DataTypeUtil;
+
+public class ConstantVar<T> extends Var<T> {
+
+ private static final long serialVersionUID = 8490929155047073802L;
+ protected T value;
+ protected DataType<T> dataType;
+
+ // 前端处理使用
+ private String dataTypestr;
+
+ @Override
+ public T doAction(RuleContext context, Rule rule) {
+ return value;
+ }
+
+ @Override
+ public boolean volidate(RuleContext context, Rule rule) {
+ if (value == null || dataType == null) {
+ return false;
+ }
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void getJsonObject(JSONObject jsonObject) {
+ // value很多时候会是String类型,这样dataType.toDataJson会报错,所以先转为dataType类型
+ if (dataType == null) {
+ dataType = (DataType<T>)new StringDataType(String.class);
+ }
+ try {
+ this.value = (T)dataType.getData(String.valueOf(value));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ jsonObject.put("value", dataType.toDataJson(value));
+ jsonObject.put("dataType", dataType.toJson());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void setJsonObject(JSONObject jsonObject) {
+ String dataTypeJson = jsonObject.getString("dataType");
+ dataType = (DataType<T>)DataTypeUtil.createDataType(dataTypeJson);
+ String valueString = jsonObject.getString("value");
+ this.value = (T)dataType.getData(valueString);
+
+ // 前端显示用
+ String dataTypestr = "";
+ try {
+ if (dataType != null) {
+ dataTypestr = MetaDataField.getDataTypeStrByType(dataType);
+ }
+ } catch (Exception e) {
+ dataTypestr = "String";
+ }
+ this.dataTypestr = dataTypestr;
+ }
+
+ @Override
+ public boolean canLazyLoad() {
+ return false;
+ }
+
+ public T getValue() {
+ return value;
+ }
+
+ public void setValue(T value) {
+ this.value = value;
+ }
+
+ public DataType<T> getDataType() {
+ return dataType;
+ }
+
+ public void setDataType(DataType<T> dataType) {
+ this.dataType = dataType;
+ }
+
+ public String getDataTypestr() {
+ return dataTypestr;
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public void setDataTypestr(String dataTypestr) {
+ this.dataTypestr = dataTypestr;
+ DataType dt = MetaDataField.getDataTypeByStr(dataTypestr);
+ this.dataType = dt;
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/var/ContextVar.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/var/ContextVar.java
new file mode 100644
index 0000000..be90dca
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/var/ContextVar.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.var;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.common.metadata.MetaData;
+import org.apache.rocketmq.streams.common.metadata.MetaDataField;
+import org.apache.rocketmq.streams.common.utils.ReflectUtil;
+
+public class ContextVar<T> extends Var<T> {
+
+ private static final long serialVersionUID = -7025012350292140132L;
+ private String fieldName;
+ private String metaDataName; // 消息队列对应的meta信息
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T doAction(RuleContext context, Rule rule) {
+ if (!volidate(context, rule)) {
+ return null;
+ }
+ JSONObject message = context.getMessage().getMessageBody();
+ Object object = null;
+ MetaData metaData = context.getMetaData(metaDataName);
+ if (metaData == null) {
+ object = message.get(fieldName);
+ if (object == null) {
+ object = ReflectUtil.getBeanFieldOrJsonValue(message, String.class, fieldName);
+ }
+ return (T)object;
+ }
+ Class dataClass = String.class;
+ MetaDataField field = metaData.getMetaDataField(fieldName);
+ if (field != null) {
+ dataClass = field.getDataType().getDataClass();
+ }
+ object = ReflectUtil.getBeanFieldOrJsonValue(message, dataClass, fieldName);
+ if (object == null) {
+ return null;
+ }
+ return (T)object;
+ }
+
+ @Override
+ public boolean volidate(RuleContext context, Rule rule) {
+ return true;
+ }
+
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ public void setFieldName(String fieldName) {
+ this.fieldName = fieldName;
+ }
+
+ public String getMetaDataName() {
+ return metaDataName;
+ }
+
+ public void setMetaDataName(String metaDataName) {
+ this.metaDataName = metaDataName;
+ }
+
+ @Override
+ public boolean canLazyLoad() {
+ return false;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/var/InnerVar.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/var/InnerVar.java
new file mode 100644
index 0000000..514917b
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/var/InnerVar.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.var;
+
+import com.alibaba.fastjson.JSONObject;
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+
+@SuppressWarnings("rawtypes")
+public class InnerVar extends Var {
+
+ private static final long serialVersionUID = -166963014761276615L;
+ public static final String ORIG_MESSAGE = "inner_message";
+ public static final String RULE = "inner_rule";
+ public static final String INNER_RULE_CODE = "inner_rule_code";
+ public static final String INNER_RULE_DESC = "inner_rule_desc";
+ public static final String INNER_RULE_TITEL = "inner_rule_title";
+ public static final String INNER_NAMESPACE = "inner_namespace";
+
+ @Override
+ public Object doAction(RuleContext context, Rule rule) {
+ String varName = getVarName();
+ if (varName.equals(ORIG_MESSAGE)) {
+ return context.getMessage().getMessageBody().toJSONString();
+ } else if (varName.equals(RULE)) {
+ return rule.toJson();
+ } else if (varName.equals(INNER_RULE_CODE)) {
+ return rule.getRuleCode();
+ } else if (varName.equals(INNER_NAMESPACE)) {
+ return context.getNameSpace();
+ } else if (varName.equals(INNER_RULE_TITEL)) {
+ return rule.getRuleTitle();
+ } else if (varName.equals(INNER_RULE_DESC)) {
+ return rule.getRuleDesc();
+ }
+ return null;
+ }
+
+ @Override
+ public boolean canLazyLoad() {
+ return false;
+ }
+
+ @Override
+ public boolean volidate(RuleContext context, Rule rule) {
+ return true;
+ }
+
+ public static void main(String args[]) {
+
+ // String cmdline="\"C:\\Windows\\SysWOW64\\cmd.exe\" /c REG add
+ // HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run /v svchost /t REG_SZ /d
+ // \"C:\\Windows\\svchost.exe\"";
+
+ String message =
+ "{\"uid\":\"N/A\",\"filepath\":\"N/A\",\"sid\":\"-1\",\"ppid\":\"4080\",\"cmdline\":\"REG add "
+ + "HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run /v svchost /t REG_SZ /d "
+ + "\\\"C:\\\\Windows\\\\svchost.exe\\\" /f\",\"egroupid\":\"N/A\",\"groupname\":\"N/A\","
+ + "\"cwd\":\"N/A\",\"pid\":\"2144\",\"pcmdline\":\"\\\"C:\\\\Windows\\\\SysWOW64\\\\cmd.exe\\\" /c "
+ + "REG add HKLM\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run /v svchost /t REG_SZ /d "
+ + "\\\"C:\\\\Windows\\\\svchost.exe\\\" /f\",\"safe_mode\":\"N/A\",\"username\":\"N/A\","
+ + "\"time\":\"2018-02-26 04:18:20\",\"seq\":\"8136606\",\"tty\":\"N/A\",\"filename\":\"\","
+ + "\"groupid\":\"N/A\",\"uuid\":\"4c8a102a-52ad-480d-92f3-a9a2cce98773\",\"pfilename\":\"N/A\","
+ + "\"messageId\":\"310347\",\"euid\":\"N/A\"}";
+
+ JSONObject obj = JSONObject.parseObject(message);
+ // System.out.println(obj.getString("cmdline"));
+ System.out.println(obj.toJSONString());
+
+ }
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/var/Var.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/var/Var.java
new file mode 100644
index 0000000..e6e6480
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/operator/var/Var.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.operator.var;
+
+import java.io.Serializable;
+
+import org.apache.rocketmq.streams.filter.operator.action.IConfigurableAction;
+import org.apache.rocketmq.streams.filter.context.RuleContext;
+import org.apache.rocketmq.streams.filter.operator.Rule;
+import org.apache.rocketmq.streams.common.configurable.BasedConfigurable;
+import org.apache.rocketmq.streams.common.configurable.IConfigurable;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public abstract class Var<T> extends BasedConfigurable implements IConfigurable, IConfigurableAction<T>, Serializable {
+
+ private static final Log LOG = LogFactory.getLog(Var.class);
+
+ public static final String TYPE = "var";
+
+ protected String varName;
+
+ public Var() {
+ setType(TYPE);
+ }
+
+ public String getVarName() {
+ return varName;
+ }
+
+ public void setVarName(String varName) {
+ this.varName = varName;
+ }
+
+ public Object getVarValue(RuleContext context, Rule rule) {
+ if (context.containsVarName(getVarName())) {
+ return context.getVarCacheValue(getVarName());
+ }
+ Object value = doMonitorAction(context, rule);
+ context.putVarValue(getNameSpace(), getVarName(), value);
+ return value;
+ }
+
+ protected Object doMonitorAction(RuleContext context, Rule rule) {
+ Object value = null;
+
+ try {
+ value = doAction(context, rule);
+ } catch (Exception e) {
+ LOG.error(
+ "Var doMonitorAction doAction error, var is: " + getVarName() + " ,rule is : " + rule.getRuleCode(), e);
+ return null;
+ }
+ return value;
+
+ }
+
+ @Override
+ public boolean init() {
+ return true;
+ }
+
+ public abstract boolean canLazyLoad();
+
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/optimization/EqualsExpressionOptimization.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/optimization/EqualsExpressionOptimization.java
new file mode 100644
index 0000000..d0e3301
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/optimization/EqualsExpressionOptimization.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.optimization;
+
+import org.apache.rocketmq.streams.filter.function.expression.Equals;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.common.utils.DataTypeUtil;
+
+public class EqualsExpressionOptimization implements IExpressionOptimization {
+ @Override
+ public boolean support(Expression expression) {
+ return Equals.isEqualFunction(expression.getFunctionName()) && DataTypeUtil.isString(expression.getDataType()) && expression.getValue() != null;
+ }
+
+ @Override
+ public OptimizationExpression optimize(Expression expression) {
+ Object value = expression.getValue();
+ String regex = null;
+ if (value != null) {
+ regex = (String)value;
+ }
+ OptimizationExpression optimizationExpression = new OptimizationExpression(expression, regex);
+ return optimizationExpression;
+ }
+}
diff --git a/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/optimization/ExpressionOptimization.java b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/optimization/ExpressionOptimization.java
new file mode 100644
index 0000000..d75e66c
--- /dev/null
+++ b/rocketmq-streams-filter/src/main/java/org/apache/rocketmq/streams/filter/optimization/ExpressionOptimization.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.streams.filter.optimization;
+
+import org.apache.rocketmq.streams.filter.builder.ExpressionBuilder;
+import org.apache.rocketmq.streams.filter.operator.expression.Expression;
+import org.apache.rocketmq.streams.filter.operator.expression.RelationExpression;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 对于表达式解析,为了方便,会产生很多附加的关系,如(a,==,b)&((c,>,0)&&(d,>,1)),每一层括号都会产生一层关系。可以通过优化,降低关系个数 优化思路,是如果顶层关系是&,则子表达式也是&,则可以拉平,变成如下这种(a,==,b)&(c,>,0)&(d,>,1)
+ */
+public class ExpressionOptimization {
+ protected Expression rootExpression;//最外层的expression
+ //protected List<Expression> simpleExpressions;//不是关系的表达式
+ //protected List<RelationExpression> relationExpressions;;//关系表达式
+ protected Map<String, Expression> expressionMap = new HashMap<>();
+
... 2339 lines suppressed ...