This is an automated email from the ASF dual-hosted git repository.
zhaoqingran pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git
The following commit(s) were added to refs/heads/master by this push:
new 6c35f57b2 [improve] use apache jexl replace of aviator (#1859)
6c35f57b2 is described below
commit 6c35f57b2edb28b4b2bcfa0a0ac875e32454065a
Author: tomsun28 <[email protected]>
AuthorDate: Sat Apr 27 12:16:38 2024 +0800
[improve] use apache jexl replace of aviator (#1859)
---
README.md | 2 +-
README_CN.md | 2 +-
.../hertzbeat/alert/calculate/CalculateAlarm.java | 38 +-
.../alert/service/impl/AlertDefineServiceImpl.java | 4 +-
.../collector/dispatch/MetricsCollect.java | 71 +--
common/pom.xml | 10 +-
.../common/config/AviatorConfiguration.java | 200 --------
.../hertzbeat/common/entity/job/Metrics.java | 1 -
.../hertzbeat/common/util/JexlCommonFunction.java | 98 ++++
.../common/util/JexlExpressionRunner.java | 70 +++
.../common/config/AviatorConfigurationTest.java | 134 -----
.../org/apache/hertzbeat/common/util/JexlTest.java | 565 +++++++++++++++++++++
.../current/help/alert_threshold_expr.md | 2 +-
.../org/apache/hertzbeat/manager/ManagerTest.java | 8 +-
material/licenses/backend/LICENSE | 11 +-
material/licenses/backend/LICENSE-aviator.txt | 165 ------
pom.xml | 14 +-
17 files changed, 808 insertions(+), 587 deletions(-)
diff --git a/README.md b/README.md
index 649ec2046..90589b164 100644
--- a/README.md
+++ b/README.md
@@ -466,7 +466,7 @@ WeChat Public : Search `Apache HertzBeat` or `usthecom`.
HertzBeat is built on so many great open source projects, thanks to them!
-- `Java Spring SpringBoot Jpa Maven Assembly Netty Lombok Sureness Aviator
Protobuf HttpClient Guava SnakeYaml JsonPath ...`
+- `Java Spring SpringBoot Jpa Maven Assembly Netty Lombok Sureness Protobuf
HttpClient Guava SnakeYaml JsonPath ...`
- `TypeScript Angular NG-ZORRO NG-ALAIN NodeJs Npm Html Less Echarts Rxjs
ZoneJs MonacoEditor SlickCarousel Docusaurus ...`
## Landscape
diff --git a/README_CN.md b/README_CN.md
index a894b2098..b159b4039 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -466,7 +466,7 @@ Thanks these wonderful people, welcome to join us:
HertzBeat is built on so many great open source projects, thanks to them!
-- `Java Spring SpringBoot Jpa Maven Assembly Netty Lombok Sureness Aviator
Protobuf HttpClient Guava SnakeYaml JsonPath ...`
+- `Java Spring SpringBoot Jpa Maven Assembly Netty Lombok Sureness Protobuf
HttpClient Guava SnakeYaml JsonPath ...`
- `TypeScript Angular NG-ZORRO NG-ALAIN NodeJs Npm Html Less Echarts Rxjs
ZoneJs MonacoEditor SlickCarousel Docusaurus ...`
diff --git
a/alerter/src/main/java/org/apache/hertzbeat/alert/calculate/CalculateAlarm.java
b/alerter/src/main/java/org/apache/hertzbeat/alert/calculate/CalculateAlarm.java
index f18738b3c..fb60f50f8 100644
---
a/alerter/src/main/java/org/apache/hertzbeat/alert/calculate/CalculateAlarm.java
+++
b/alerter/src/main/java/org/apache/hertzbeat/alert/calculate/CalculateAlarm.java
@@ -26,11 +26,6 @@ import static
org.apache.hertzbeat.common.constants.CommonConstants.TAG_METRICS;
import static
org.apache.hertzbeat.common.constants.CommonConstants.TAG_MONITOR_APP;
import static
org.apache.hertzbeat.common.constants.CommonConstants.TAG_MONITOR_ID;
import static
org.apache.hertzbeat.common.constants.CommonConstants.TAG_MONITOR_NAME;
-import com.googlecode.aviator.AviatorEvaluator;
-import com.googlecode.aviator.Expression;
-import com.googlecode.aviator.exception.CompileExpressionErrorException;
-import com.googlecode.aviator.exception.ExpressionRuntimeException;
-import com.googlecode.aviator.exception.ExpressionSyntaxErrorException;
import jakarta.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.HashMap;
@@ -40,6 +35,8 @@ import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.jexl3.JexlException;
+import org.apache.commons.jexl3.JexlExpression;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.alert.AlerterWorkerPool;
import org.apache.hertzbeat.alert.dao.AlertMonitorDao;
@@ -57,6 +54,7 @@ import org.apache.hertzbeat.common.queue.CommonDataQueue;
import org.apache.hertzbeat.common.support.event.MonitorDeletedEvent;
import org.apache.hertzbeat.common.support.event.SystemConfigChangeEvent;
import org.apache.hertzbeat.common.util.CommonUtil;
+import org.apache.hertzbeat.common.util.JexlExpressionRunner;
import org.apache.hertzbeat.common.util.ResourceBundleUtil;
import org.springframework.context.event.EventListener;
import org.springframework.data.jpa.domain.Specification;
@@ -321,22 +319,24 @@ public class CalculateAlarm {
private boolean execAlertExpression(Map<String, Object> fieldValueMap,
String expr) {
Boolean match;
+ JexlExpression expression;
try {
- Expression expression = AviatorEvaluator.compile(expr, true);
- expression.getVariableNames().forEach(variable -> {
- if (!fieldValueMap.containsKey(variable)) {
- throw new ExpressionRuntimeException("metrics value not
contains expr field: " + variable);
- }
- });
- match = (Boolean) expression.execute(fieldValueMap);
- } catch (CompileExpressionErrorException |
ExpressionSyntaxErrorException compileException) {
- log.error("Alert Define Rule: {} Compile Error: {}.", expr,
compileException.getMessage());
- throw compileException;
- } catch (ExpressionRuntimeException expressionRuntimeException) {
- log.error("Alert Define Rule: {} Run Error: {}.", expr,
expressionRuntimeException.getMessage());
- throw expressionRuntimeException;
+ expression = JexlExpressionRunner.compile(expr);
+ } catch (JexlException jexlException) {
+ log.error("Alarm Rule: {} Compile Error: {}.", expr,
jexlException.getMessage());
+ throw jexlException;
+ } catch (Exception e) {
+ log.error("Alarm Rule: {} Unknown Error: {}.", expr,
e.getMessage());
+ throw e;
+ }
+
+ try {
+ match = (Boolean) JexlExpressionRunner.evaluate(expression,
fieldValueMap);
+ } catch (JexlException jexlException) {
+ log.error("Alarm Rule: {} Run Error: {}.", expr,
jexlException.getMessage());
+ throw jexlException;
} catch (Exception e) {
- log.error("Alert Define Rule: {} Unknown Error: {}.", expr,
e.getMessage());
+ log.error("Alarm Rule: {} Unknown Error: {}.", expr,
e.getMessage());
throw e;
}
return match != null && match;
diff --git
a/alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java
b/alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java
index b66ef9841..21d07b2e1 100644
---
a/alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java
+++
b/alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java
@@ -17,7 +17,6 @@
package org.apache.hertzbeat.alert.service.impl;
-import com.googlecode.aviator.AviatorEvaluator;
import jakarta.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@@ -35,6 +34,7 @@ import
org.apache.hertzbeat.alert.service.AlertDefineImExportService;
import org.apache.hertzbeat.alert.service.AlertDefineService;
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
import org.apache.hertzbeat.common.entity.alerter.AlertDefineMonitorBind;
+import org.apache.hertzbeat.common.util.JexlExpressionRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
@@ -70,7 +70,7 @@ public class AlertDefineServiceImpl implements
AlertDefineService {
// todo
if (StringUtils.hasText(alertDefine.getExpr())) {
try {
- AviatorEvaluator.compile(alertDefine.getExpr(), false);
+ JexlExpressionRunner.compile(alertDefine.getExpr());
} catch (Exception e) {
throw new IllegalArgumentException("alert expr error: " +
e.getMessage());
}
diff --git
a/collector/src/main/java/org/apache/hertzbeat/collector/dispatch/MetricsCollect.java
b/collector/src/main/java/org/apache/hertzbeat/collector/dispatch/MetricsCollect.java
index a32005499..f5b54556f 100644
---
a/collector/src/main/java/org/apache/hertzbeat/collector/dispatch/MetricsCollect.java
+++
b/collector/src/main/java/org/apache/hertzbeat/collector/dispatch/MetricsCollect.java
@@ -17,8 +17,6 @@
package org.apache.hertzbeat.collector.dispatch;
-import com.googlecode.aviator.AviatorEvaluator;
-import com.googlecode.aviator.Expression;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
@@ -28,6 +26,7 @@ import java.util.Objects;
import java.util.stream.Collectors;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.jexl3.JexlExpression;
import org.apache.hertzbeat.collector.collect.AbstractCollect;
import
org.apache.hertzbeat.collector.collect.prometheus.PrometheusAutoCollectImpl;
import org.apache.hertzbeat.collector.collect.strategy.CollectStrategyFactory;
@@ -40,6 +39,7 @@ import org.apache.hertzbeat.common.entity.job.Job;
import org.apache.hertzbeat.common.entity.job.Metrics;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.util.CommonUtil;
+import org.apache.hertzbeat.common.util.JexlExpressionRunner;
import org.apache.hertzbeat.common.util.Pair;
/**
@@ -203,11 +203,11 @@ public class MetricsCollect implements Runnable,
Comparable<MetricsCollect> {
}
// eg: database_pages=Database pages unconventional mapping
Map<String, String> fieldAliasMap = new HashMap<>(8);
- Map<String, Expression> fieldExpressionMap = metrics.getCalculates()
+ Map<String, JexlExpression> fieldExpressionMap =
metrics.getCalculates()
.stream()
.map(cal -> transformCal(cal, fieldAliasMap))
.filter(Objects::nonNull)
- .collect(Collectors.toMap(arr -> (String) arr[0], arr ->
(Expression) arr[1], (oldValue, newValue) -> newValue));
+ .collect(Collectors.toMap(arr -> (String) arr[0], arr ->
(JexlExpression) arr[1], (oldValue, newValue) -> newValue));
if (metrics.getUnits() == null) {
metrics.setUnits(Collections.emptyList());
@@ -220,46 +220,47 @@ public class MetricsCollect implements Runnable,
Comparable<MetricsCollect> {
List<Metrics.Field> fields = metrics.getFields();
List<String> aliasFields = metrics.getAliasFields();
- Map<String, String> aliasFieldValueMap = new HashMap<>(16);
- Map<String, Object> fieldValueMap = new HashMap<>(16);
+ Map<String, String> aliasFieldValueMap = new HashMap<>(8);
+ Map<String, Object> fieldValueMap = new HashMap<>(8);
+ Map<String, String> aliasFieldUnitMap = new HashMap<>(8);
CollectRep.ValueRow.Builder realValueRowBuilder =
CollectRep.ValueRow.newBuilder();
for (CollectRep.ValueRow aliasRow : aliasRowList) {
for (int aliasIndex = 0; aliasIndex < aliasFields.size();
aliasIndex++) {
String aliasFieldValue = aliasRow.getColumns(aliasIndex);
+ String aliasField = aliasFields.get(aliasIndex);
if (!CommonConstants.NULL_VALUE.equals(aliasFieldValue)) {
- aliasFieldValueMap.put(aliasFields.get(aliasIndex),
aliasFieldValue);
+ aliasFieldValueMap.put(aliasField, aliasFieldValue);
+ // whether the alias field is a number
+ CollectUtil.DoubleAndUnit doubleAndUnit = CollectUtil
+ .extractDoubleAndUnitFromStr(aliasFieldValue);
+ if (doubleAndUnit != null) {
+ fieldValueMap.put(aliasField,
doubleAndUnit.getValue());
+ if (doubleAndUnit.getUnit() != null) {
+ aliasFieldUnitMap.put(aliasField,
doubleAndUnit.getUnit());
+ }
+ } else {
+ fieldValueMap.put(aliasField, aliasFieldValue);
+ }
+ } else {
+ fieldValueMap.put(aliasField, null);
}
}
for (Metrics.Field field : fields) {
String realField = field.getField();
- Expression expression = fieldExpressionMap.get(realField);
+ JexlExpression expression = fieldExpressionMap.get(realField);
String value = null;
String aliasFieldUnit = null;
if (expression != null) {
- // If there is a calculation expression, calculate the
value
- if (CommonConstants.TYPE_NUMBER == field.getType()) {
- for (String variable :
expression.getVariableFullNames()) {
- // extract double value and unit from aliasField
value
- CollectUtil.DoubleAndUnit doubleAndUnit =
CollectUtil
-
.extractDoubleAndUnitFromStr(aliasFieldValueMap.get(variable));
- if (doubleAndUnit != null) {
- Double doubleValue = doubleAndUnit.getValue();
- aliasFieldUnit = doubleAndUnit.getUnit();
- fieldValueMap.put(variable, doubleValue);
- } else {
- fieldValueMap.put(variable, null);
+ try {
+ for (Map.Entry<String, String> unitEntry :
aliasFieldUnitMap.entrySet()) {
+ if
(expression.getSourceText().contains(unitEntry.getKey())) {
+ aliasFieldUnit = unitEntry.getValue();
+ break;
}
}
- } else {
- for (String variable :
expression.getVariableFullNames()) {
- String strValue = aliasFieldValueMap.get(variable);
- fieldValueMap.put(variable, strValue);
- }
- }
- try {
// valueList为空时也执行,涵盖纯字符串赋值表达式
- Object objValue = expression.execute(fieldValueMap);
+ Object objValue =
JexlExpressionRunner.evaluate(expression, fieldValueMap);
if (objValue != null) {
value = String.valueOf(objValue);
}
@@ -274,16 +275,15 @@ public class MetricsCollect implements Runnable,
Comparable<MetricsCollect> {
} else {
value = aliasFieldValueMap.get(realField);
}
-
+
if (value != null) {
final byte fieldType = field.getType();
-
if (fieldType == CommonConstants.TYPE_NUMBER) {
CollectUtil.DoubleAndUnit doubleAndUnit =
CollectUtil
.extractDoubleAndUnitFromStr(value);
- final Double tempValue = doubleAndUnit.getValue();
+ final Double tempValue = doubleAndUnit == null ?
null : doubleAndUnit.getValue();
value = tempValue == null ? null :
String.valueOf(tempValue);
- aliasFieldUnit = doubleAndUnit.getUnit();
+ aliasFieldUnit = doubleAndUnit == null ? null :
doubleAndUnit.getUnit();
} else if (fieldType == CommonConstants.TYPE_TIME) {
final int tempValue;
value = (tempValue =
CommonUtil.parseTimeStrToSecond(value)) == -1 ? null :
String.valueOf(tempValue);
@@ -314,9 +314,10 @@ public class MetricsCollect implements Runnable,
Comparable<MetricsCollect> {
value = CommonConstants.NULL_VALUE;
}
realValueRowBuilder.addColumns(value);
- fieldValueMap.clear();
}
aliasFieldValueMap.clear();
+ fieldValueMap.clear();
+ aliasFieldUnitMap.clear();
collectData.addValues(realValueRowBuilder.build());
realValueRowBuilder.clear();
}
@@ -332,9 +333,9 @@ public class MetricsCollect implements Runnable,
Comparable<MetricsCollect> {
int splitIndex = cal.indexOf("=");
String field = cal.substring(0, splitIndex).trim();
String expressionStr = cal.substring(splitIndex +
1).trim().replace("\\#", "#");
- Expression expression;
+ JexlExpression expression;
try {
- expression = AviatorEvaluator.compile(expressionStr, true);
+ expression = JexlExpressionRunner.compile(expressionStr);
} catch (Exception e) {
fieldAliasMap.put(field, expressionStr);
return null;
diff --git a/common/pom.xml b/common/pom.xml
index 20b6683ce..add11f7c3 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -78,11 +78,6 @@
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<scope>provided</scope>
</dependency>
- <!-- Expression evaluation -->
- <dependency>
- <groupId>com.googlecode.aviator</groupId>
- <artifactId>aviator</artifactId>
- </dependency>
<!-- Tool dependencies -->
<dependency>
<groupId>com.google.guava</groupId>
@@ -120,6 +115,11 @@
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
</dependency>
+ <!-- expr calculate -->
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-jexl3</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
diff --git
a/common/src/main/java/org/apache/hertzbeat/common/config/AviatorConfiguration.java
b/common/src/main/java/org/apache/hertzbeat/common/config/AviatorConfiguration.java
deleted file mode 100644
index c690ef0f0..000000000
---
a/common/src/main/java/org/apache/hertzbeat/common/config/AviatorConfiguration.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * 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.hertzbeat.common.config;
-
-import com.googlecode.aviator.AviatorEvaluator;
-import com.googlecode.aviator.AviatorEvaluatorInstance;
-import com.googlecode.aviator.Feature;
-import com.googlecode.aviator.Options;
-import com.googlecode.aviator.lexer.token.OperatorType;
-import com.googlecode.aviator.runtime.function.AbstractFunction;
-import com.googlecode.aviator.runtime.type.AviatorBoolean;
-import com.googlecode.aviator.runtime.type.AviatorDouble;
-import com.googlecode.aviator.runtime.type.AviatorObject;
-import com.googlecode.aviator.runtime.type.AviatorString;
-import com.googlecode.aviator.runtime.type.AviatorType;
-import java.util.Map;
-import java.util.Objects;
-import java.util.regex.Pattern;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-
-/**
- * aviator config
- */
-@Configuration
-@Slf4j
-public class AviatorConfiguration {
-
- private static final int AVIATOR_LRU_CACHE_SIZE = 1024;
-
- @Bean
- public AviatorEvaluatorInstance configAviatorEvaluator() {
- AviatorEvaluatorInstance instance = AviatorEvaluator.getInstance();
-
- // Configure AviatorEvaluator to cache compiled expressions using LRU
- instance
- .useLRUExpressionCache(AVIATOR_LRU_CACHE_SIZE)
- .addFunction(new StrEqualFunction());
-
- // limit loop Limit the number of loops
- instance.setOption(Options.MAX_LOOP_COUNT, 10);
-
- // Enables a partial Aviator syntax feature collection
- instance.setOption(Options.FEATURE_SET,
- Feature.asSet(Feature.If,
- Feature.Assignment,
- Feature.Let,
- Feature.StringInterpolation));
-
- // Configure the custom aviator function
- instance.addOpFunction(OperatorType.BIT_OR, new AbstractFunction() {
- @Override
- public AviatorObject call(final Map<String, Object> env, final
AviatorObject arg1,
- final AviatorObject arg2) {
- try {
- Object value1 = arg1.getValue(env);
- Object value2 = arg2.getValue(env);
- Object currentValue = value1 == null ? value2 : value1;
- if (arg1.getAviatorType() == AviatorType.String) {
- return new AviatorString(String.valueOf(currentValue));
- } else {
- return AviatorDouble.valueOf(currentValue);
- }
- } catch (Exception e) {
- log.error(e.getMessage());
- }
- return arg1.bitOr(arg2, env);
- }
-
- @Override
- public String getName() {
- return OperatorType.BIT_OR.getToken();
- }
- });
-
- instance.addFunction(new StrContainsFunction());
- instance.addFunction(new ObjectExistsFunction());
- instance.addFunction(new StrMatchesFunction());
- return instance;
- }
-
- /**
- * Define a custom aviator string equality function
- */
- private static class StrEqualFunction extends AbstractFunction {
- @Override
- public AviatorObject call(Map<String, Object> env, AviatorObject arg1,
AviatorObject arg2) {
- if (arg1 == null || arg2 == null) {
- return AviatorBoolean.FALSE;
- }
- Object leftTmp = arg1.getValue(env);
- Object rightTmp = arg2.getValue(env);
- if (leftTmp == null || rightTmp == null) {
- return AviatorBoolean.FALSE;
- }
- String left = String.valueOf(leftTmp);
- String right = String.valueOf(rightTmp);
- return AviatorBoolean.valueOf(left.equalsIgnoreCase(right));
- }
-
- @Override
- public String getName() {
- return "equals";
- }
- }
-
- /**
- * Custom aviator determines whether string 1 contains string 2
(case-insensitive)
- */
- private static class StrContainsFunction extends AbstractFunction {
- @Override
- public AviatorObject call(Map<String, Object> env, AviatorObject arg1,
AviatorObject arg2) {
- if (arg1 == null || arg2 == null) {
- return AviatorBoolean.FALSE;
- }
- Object leftTmp = arg1.getValue(env);
- Object rightTmp = arg2.getValue(env);
- if (leftTmp == null || rightTmp == null) {
- return AviatorBoolean.FALSE;
- }
- String left = String.valueOf(leftTmp);
- String right = String.valueOf(rightTmp);
- return AviatorBoolean.valueOf(StringUtils.containsIgnoreCase(left,
right));
- }
-
- @Override
- public String getName() {
- return "contains";
- }
- }
-
- /**
- * Custom aviator determines if a value exists for this object in the
environment
- */
- private static class ObjectExistsFunction extends AbstractFunction {
- @Override
- public AviatorObject call(Map<String, Object> env, AviatorObject arg) {
- if (arg == null) {
- return AviatorBoolean.FALSE;
- }
- Object keyTmp = arg.getValue(env);
- if (Objects.isNull(keyTmp)) {
- return AviatorBoolean.FALSE;
- } else {
- String key = String.valueOf(keyTmp);
- return AviatorBoolean.valueOf(StringUtils.isNotEmpty(key));
- }
- }
-
- @Override
- public String getName() {
- return "exists";
- }
- }
-
- /**
- * Custom aviator determines if a string matches a regex
- * - regex You need to add "" or ''
- */
- private static class StrMatchesFunction extends AbstractFunction {
- @Override
- public AviatorObject call(Map<String, Object> env, AviatorObject arg1,
AviatorObject arg2) {
- if (arg1 == null || arg2 == null) {
- return AviatorBoolean.FALSE;
- }
- Object strTmp = arg1.getValue(env);
- Object regexTmp = arg2.getValue(env);
- if (strTmp == null || regexTmp == null) {
- return AviatorBoolean.FALSE;
- }
- String str = String.valueOf(strTmp);
- String regex = String.valueOf(regexTmp);
- boolean isMatch = Pattern.compile(regex).matcher(str).matches();
- return AviatorBoolean.valueOf(isMatch);
- }
-
- @Override
- public String getName() {
- return "matches";
- }
- }
-}
diff --git
a/common/src/main/java/org/apache/hertzbeat/common/entity/job/Metrics.java
b/common/src/main/java/org/apache/hertzbeat/common/entity/job/Metrics.java
index 6bb96574e..1e517fd90 100644
--- a/common/src/main/java/org/apache/hertzbeat/common/entity/job/Metrics.java
+++ b/common/src/main/java/org/apache/hertzbeat/common/entity/job/Metrics.java
@@ -102,7 +102,6 @@ public class Metrics {
* Public attribute - expression calculation, map the pre-query attribute
(pre Fields)
* with the final attribute (fields), and calculate the final attribute
(fields) value
* eg: size = size1 + size2, speed = speedSize
- * <a
href="https://www.yuque.com/boyan-avfmj/aviatorscript/ban32m">www.yuque.com/boyan-avfmj/aviatorscript/ban32m</a>
*/
private List<String> calculates;
/**
diff --git
a/common/src/main/java/org/apache/hertzbeat/common/util/JexlCommonFunction.java
b/common/src/main/java/org/apache/hertzbeat/common/util/JexlCommonFunction.java
new file mode 100644
index 000000000..7ccdced22
--- /dev/null
+++
b/common/src/main/java/org/apache/hertzbeat/common/util/JexlCommonFunction.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.hertzbeat.common.util;
+
+import java.util.regex.Pattern;
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * the common function for jexl str equals, match, contains, etc.
+ * sys:now()
+ */
+@Data
+public class JexlCommonFunction {
+
+ /**
+ * Get the current time in milliseconds
+ * @return current time
+ */
+ public long now() {
+ return System.currentTimeMillis();
+ }
+
+
+ /**
+ * Define a custom string equality function
+ * @param left left
+ * @param right right
+ * @return true if equals
+ */
+ public boolean equals(String left, String right) {
+ if (left == null && right == null) {
+ return true;
+ }
+ if (left == null || right == null) {
+ return false;
+ }
+ return left.equals(right);
+ }
+
+ /**
+ * Custom determines whether string 1 contains string 2 (case-insensitive)
+ * @param left left
+ * @param right right
+ * @return true if contains
+ */
+ public boolean contains(String left, String right) {
+ if (left == null || right == null) {
+ return false;
+ }
+ return StringUtils.containsIgnoreCase(left, right);
+ }
+
+
+ /**
+ * Custom determines if a value exists for this object in the environment
+ * @param arg arg
+ * @return true if exists
+ */
+ public boolean exists(Object arg) {
+ if (arg == null) {
+ return false;
+ }
+ return StringUtils.isNotEmpty(String.valueOf(arg));
+ }
+
+ /**
+ * Custom determines if a string matches a regex
+ * - regex You need to add "" or ''
+ * @param str str
+ * @param regex regex
+ * @return true if matches
+ */
+ public boolean matches(String str, String regex) {
+ if (str == null || regex == null) {
+ return false;
+ }
+ return Pattern.compile(regex).matcher(str).matches();
+ }
+
+}
diff --git
a/common/src/main/java/org/apache/hertzbeat/common/util/JexlExpressionRunner.java
b/common/src/main/java/org/apache/hertzbeat/common/util/JexlExpressionRunner.java
new file mode 100644
index 000000000..6d13886ea
--- /dev/null
+++
b/common/src/main/java/org/apache/hertzbeat/common/util/JexlExpressionRunner.java
@@ -0,0 +1,70 @@
+/*
+ * 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.hertzbeat.common.util;
+
+import com.google.common.collect.Maps;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import org.apache.commons.jexl3.JexlBuilder;
+import org.apache.commons.jexl3.JexlContext;
+import org.apache.commons.jexl3.JexlEngine;
+import org.apache.commons.jexl3.JexlExpression;
+import org.apache.commons.jexl3.MapContext;
+
+/**
+ * jexl express runner
+ */
+public class JexlExpressionRunner {
+
+ private static final JexlEngine jexlEngine;
+
+ static {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ // set the root namespace function
+ functions.put(null, new JexlCommonFunction());
+ jexlEngine = new
JexlBuilder().charset(StandardCharsets.UTF_8).cache(256)
+ .strict(true).silent(false).namespaces(functions).create();
+ }
+
+ public static Object evaluate(String expression, Map<String, Object>
context) {
+ JexlContext jexlContext = new MapContext();
+ for (Map.Entry<String, Object> entry : context.entrySet()) {
+ jexlContext.set(entry.getKey(), entry.getValue());
+ }
+ return jexlEngine.createExpression(expression).evaluate(jexlContext);
+ }
+
+ public static Object evaluate(JexlExpression expression, Map<String,
Object> context) {
+ JexlContext jexlContext = new MapContext();
+ for (Map.Entry<String, Object> entry : context.entrySet()) {
+ jexlContext.set(entry.getKey(), entry.getValue());
+ }
+ return expression.evaluate(jexlContext);
+ }
+
+ public static Object evaluate(String expression) {
+ return jexlEngine.createExpression(expression).evaluate(new
MapContext());
+ }
+
+ public static JexlExpression compile(String expression) {
+ return jexlEngine.createExpression(expression);
+ }
+
+}
diff --git
a/common/src/test/java/org/apache/hertzbeat/common/config/AviatorConfigurationTest.java
b/common/src/test/java/org/apache/hertzbeat/common/config/AviatorConfigurationTest.java
deleted file mode 100644
index 4ca9dda79..000000000
---
a/common/src/test/java/org/apache/hertzbeat/common/config/AviatorConfigurationTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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.hertzbeat.common.config;
-
-import com.googlecode.aviator.AviatorEvaluator;
-import com.googlecode.aviator.exception.UnsupportedFeatureException;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- *
- *
- */
-class AviatorConfigurationTest {
-
- @BeforeAll
- static void setUp() {
- AviatorConfiguration aviatorConfig = new AviatorConfiguration();
- aviatorConfig.configAviatorEvaluator();
- }
-
- @Test
- void testCustomStringFunctions() {
- Map<String, Object> env = new HashMap<>();
- env.put("k1", "Intel");
- env.put("k2", "intel");
- env.put("k3", "Ubuntu 18.04.6 LTS");
- env.put("k4", "ubuntu");
- env.put("k5", "Ubntu");
- env.put("k6", null);
-
- // test StrEqualFunction
- String expr1 = "equals(k1,k2)"; // case-insensitive
- Boolean res1 = (Boolean) AviatorEvaluator.compile(expr1).execute(env);
- Assertions.assertTrue(res1);
-
- String expr2 = "equals(k1,k3)";
- Boolean res2 = (Boolean) AviatorEvaluator.compile(expr2).execute(env);
- Assertions.assertFalse(res2);
-
- // test StrContainsFunction
- String expr3 = "contains(k3,k4)"; // case-insensitive
- Boolean res3 = (Boolean) AviatorEvaluator.compile(expr3).execute(env);
- Assertions.assertTrue(res3);
-
- String expr4 = "contains(k4,k3)";
- Boolean res4 = (Boolean) AviatorEvaluator.compile(expr4).execute(env);
- Assertions.assertFalse(res4);
-
- String expr5 = "contains(k3,k5)"; // subsequence
- Boolean res5 = (Boolean) AviatorEvaluator.compile(expr5).execute(env);
- Assertions.assertFalse(res5);
-
- // test StrExistsFunction
- String expr6 = "exists('DNE_Key1')";
- Boolean res6 = (Boolean) AviatorEvaluator.compile(expr6).execute(env);
- Assertions.assertTrue(res6);
-
- String expr7 = "exists(k6)";
- Boolean res7 = (Boolean) AviatorEvaluator.compile(expr7).execute(env);
- Assertions.assertFalse(res7);
-
- String expr21 = "exists('k5')";
- Boolean res21 = (Boolean)
AviatorEvaluator.compile(expr21).execute(env);
- Assertions.assertTrue(res21);
-
- String expr22 = "exists(k5)";
- Boolean res22 = (Boolean)
AviatorEvaluator.compile(expr22).execute(env);
- Assertions.assertTrue(res22);
-
- // test StrMatchesFunction
- String regex1 = "'^[a-zA-Z0-9]+$'"; // only alphanumeric
- String expr8 = "matches(k6," + regex1 + ")";
- env.put("k6", "Ubntu50681269");
- Boolean res8 = (Boolean) AviatorEvaluator.compile(expr8).execute(env);
- Assertions.assertTrue(res8);
- env.put("k6", "Ubnt_u50681269");
- Boolean res9 = (Boolean) AviatorEvaluator.compile(expr8).execute(env);
- Assertions.assertFalse(res9);
-
- String regex2 = "'^Ubuntu.*'"; // starts with
- String expr9 = "matches(k3," + regex2 + ")";
- Boolean res10 = (Boolean) AviatorEvaluator.compile(expr9).execute(env);
- Assertions.assertTrue(res10);
- env.put("k3", "Ubunt_u50681269");
- Boolean res11 = (Boolean) AviatorEvaluator.compile(expr9).execute(env);
- Assertions.assertFalse(res11);
-
- String regex3 = "\"^\\\\[LOG\\\\].*error$\""; // starts & ends with
- String expr10 = "matches(k7," + regex3 + ")";
- env.put("k7", "[LOG] detected system error");
- Boolean res12 = (Boolean)
AviatorEvaluator.compile(expr10).execute(env);
- Assertions.assertTrue(res12);
- env.put("k7", "[LOG detected system error");
- Boolean res13 = (Boolean)
AviatorEvaluator.compile(expr10).execute(env);
- Assertions.assertFalse(res13);
- }
-
- @Test
- void testRCE() {
- // test if 'new' syntax is disabled to prevent RCE
- Assertions.assertThrows(UnsupportedFeatureException.class, () -> {
- String expr1 = "let d = new java.util.Date();\n" +
- "p(type(d));\n" +
- "p(d);";
- AviatorEvaluator.compile(expr1, true).execute();
- });
- // test allowed features
- String expr2 = "let a = 0;\n" +
- "if (\"#{a}\" == \"0\") { a = -1; }\n" +
- "a == -1";
- Boolean result = (Boolean) AviatorEvaluator.compile(expr2,
true).execute();
- Assertions.assertTrue(result);
- }
-}
diff --git
a/common/src/test/java/org/apache/hertzbeat/common/util/JexlTest.java
b/common/src/test/java/org/apache/hertzbeat/common/util/JexlTest.java
new file mode 100644
index 000000000..4cf0f4233
--- /dev/null
+++ b/common/src/test/java/org/apache/hertzbeat/common/util/JexlTest.java
@@ -0,0 +1,565 @@
+/*
+ * 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.hertzbeat.common.util;
+
+import com.google.common.collect.Maps;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import org.apache.commons.jexl3.JexlBuilder;
+import org.apache.commons.jexl3.JexlContext;
+import org.apache.commons.jexl3.JexlEngine;
+import org.apache.commons.jexl3.JexlException;
+import org.apache.commons.jexl3.JexlExpression;
+import org.apache.commons.jexl3.MapContext;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * test case for java expression language
+ */
+public class JexlTest {
+
+ private JexlBuilder jexlBuilder;
+
+ @BeforeEach
+ void setUp() {
+ jexlBuilder = new
JexlBuilder().charset(StandardCharsets.UTF_8).cache(256)
+ .strict(true).silent(false);
+ }
+
+
+ @Test
+ void testMultiExpression() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 1);
+ JexlExpression e = jexl.createExpression("x * y");
+ Object o = e.evaluate(context);
+ Assertions.assertEquals(8, o);
+ }
+
+ @Test
+ void testDivisionExpression() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 1);
+ JexlExpression e = jexl.createExpression("x / y");
+ Object o = e.evaluate(context);
+ Assertions.assertEquals(8, o);
+ }
+
+ @Test
+ void testAdditionExpression() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 1);
+ JexlExpression e = jexl.createExpression("x + y");
+ Object o = e.evaluate(context);
+ Assertions.assertEquals(9, o);
+ context.set("x", "hello");
+ context.set("y", 3.0);
+ e = jexl.createExpression("x + y");
+ o = e.evaluate(context);
+ Assertions.assertEquals("hello3.0", o);
+ }
+
+ @Test
+ void testSubtractionExpression() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 1);
+ JexlExpression e = jexl.createExpression("x - y");
+ Object o = e.evaluate(context);
+ Assertions.assertEquals(7, o);
+ }
+
+ @Test
+ void testModulusExpression() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 3);
+ JexlExpression e = jexl.createExpression("x % y");
+ Object o = e.evaluate(context);
+ Assertions.assertEquals(2, o);
+ }
+
+ @Test
+ void testComplicatedExpression() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 1);
+ JexlExpression e = jexl.createExpression("x * y + 2 * x - y");
+ Object o = e.evaluate(context);
+ Assertions.assertEquals(23, o);
+ }
+
+ @Test
+ void testComplicatedExpressionWithParentheses() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 1);
+ JexlExpression e = jexl.createExpression("(x * y) + (2 * x) - y");
+ Object o = e.evaluate(context);
+ Assertions.assertEquals(23, o);
+ }
+
+ @Test
+ void testComplicatedExpressionWithParenthesesAndSpaces() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 1);
+ JexlExpression e = jexl.createExpression(" ( x * y ) + ( 2 * x ) - y
");
+ Object o = e.evaluate(context);
+ Assertions.assertEquals(23, o);
+ }
+
+ @Test
+ void testComplicatedSpecialVariableNameExpression() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x.y", 8);
+ context.set("y", 1);
+ context.set("$.os.cpu.load_average.1m", 23);
+ context.set("$.os.load_average", 55);
+ context.set("$.fs.total.total_in_bytes", 20.0);
+ JexlExpression e = jexl.createExpression("x.y * y + 2 * x.y - y +
$.os.cpu.load_average.1m + $.os.load_average + $.fs.total.total_in_bytes");
+ Object o = e.evaluate(context);
+ Assertions.assertEquals(121.0, o);
+ }
+
+ @Test
+ void
testComplicatedSpecialVariableNameExpressionWithParenthesesAndSpaces() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x.y", 8);
+ context.set("y", 1);
+ context.set("$.os.cpu.load_average.1m", 23);
+ context.set("$.os.load_average", 55);
+ context.set("$.fs.total.total_in_bytes", 20.0);
+ JexlExpression e = jexl.createExpression(" ( x.y * y ) + ( 2 * x.y ) -
y + $.os.cpu.load_average.1m + $.os.load_average + $.fs.total.total_in_bytes ");
+ Object o = e.evaluate(context);
+ Assertions.assertEquals(121.0, o);
+ }
+
+ @Test
+ void testVariableAssignment() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("$.fs.total.total_in_bytes", 20.0);
+ JexlExpression e = jexl.createExpression(" $.fs.total.total_in_bytes
");
+ Object o = e.evaluate(context);
+ Assertions.assertEquals(20.0, o);
+ }
+
+ @Test
+ void testSpecialVariableNameWithoutSpacesExpression() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("$.fs.total.free_in_bytes", 12.0);
+ context.set("$.fs.total.total_in_bytes", 23.0);
+ JexlExpression e =
jexl.createExpression("(1-$.fs.total.free_in_bytes/$.fs.total.total_in_bytes)*100");
+ Object o = e.evaluate(context);
+ Assertions.assertEquals(47.82608695652174, o);
+ }
+
+ @Test
+ void testSpecialVariableNameWithSpacesExpression() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("$.fs.total.free_in_bytes", 12.0);
+ context.set("$.fs.total.total_in_bytes", 23.0);
+ JexlExpression e = jexl.createExpression("( 1 -
$.fs.total.free_in_bytes / $.fs.total.total_in_bytes ) * 100");
+ Object o = e.evaluate(context);
+ Assertions.assertEquals(47.82608695652174, o);
+ }
+
+ @Test
+ void testJudgmentExpression() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 1);
+ JexlExpression e = jexl.createExpression("x > y");
+ Object o = e.evaluate(context);
+ Assertions.assertTrue((Boolean) o);
+ }
+
+ @Test
+ void testJudgmentExpressionWithParentheses() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 1);
+ JexlExpression e = jexl.createExpression("(x > y)");
+ Object o = e.evaluate(context);
+ Assertions.assertTrue((Boolean) o);
+ }
+
+ @Test
+ void testJudgmentExpressionWithParenthesesAndSpaces() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 1);
+ JexlExpression e = jexl.createExpression(" ( x > y ) ");
+ Object o = e.evaluate(context);
+ Assertions.assertTrue((Boolean) o);
+ }
+
+ @Test
+ void testJudgmentExpressionWithAndOperator() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 1);
+ context.set("z", 2);
+ JexlExpression e = jexl.createExpression("x > y && x < z");
+ Object o = e.evaluate(context);
+ Assertions.assertFalse((Boolean) o);
+ }
+
+ @Test
+ void testJudgmentExpressionWithOrOperator() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 1);
+ context.set("z", 2);
+ JexlExpression e = jexl.createExpression("x > y || x < z");
+ Object o = e.evaluate(context);
+ Assertions.assertTrue((Boolean) o);
+ }
+
+ @Test
+ void testJudgmentExpressionWithNotOperator() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ JexlExpression e = jexl.createExpression("!(x == 8)");
+ Object o = e.evaluate(context);
+ Assertions.assertFalse((Boolean) o);
+ }
+
+ @Test
+ void testJudgmentExpressionWithNotOperatorAndParentheses() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("$.fs.total.free_in_bytes", 8);
+ JexlExpression e = jexl.createExpression("!($.fs.total.free_in_bytes
== 8)");
+ Object o = e.evaluate(context);
+ Assertions.assertFalse((Boolean) o);
+ }
+
+ @Test
+ void testCustomFunction() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put("date", new DateFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 1);
+ JexlExpression e = jexl.createExpression("date:now(x,y)");
+ Object o = e.evaluate(context);
+ String result = (String) o;
+ Assertions.assertTrue(result.endsWith("2"));
+ }
+
+ @Test
+ void testZeroThrowException() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", 0);
+ JexlExpression e = jexl.createExpression("x / y");
+ Assertions.assertThrows(JexlException.class, () ->
e.evaluate(context));
+ }
+
+ @Test
+ void testNullThrowException() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", null);
+ JexlExpression e = jexl.createExpression("x / y");
+ Assertions.assertThrows(JexlException.class, () ->
e.evaluate(context));
+ }
+
+ @Test
+ void testEmptyStringThrowException() {
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", 8);
+ context.set("y", "");
+ JexlExpression e = jexl.createExpression("x / y");
+ Assertions.assertThrows(JexlException.class, () ->
e.evaluate(context));
+ }
+
+ @Test
+ void testEqualsFunction() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put("sys", new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", "hello");
+ context.set("y", "hello");
+ JexlExpression e = jexl.createExpression("sys:equals(x, y)");
+ Object o = e.evaluate(context);
+ Assertions.assertTrue((Boolean) o);
+ }
+
+ @Test
+ void testNotEqualsFunction() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put("sys", new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", "hellos");
+ context.set("y", "hello");
+ JexlExpression e = jexl.createExpression("sys:equals(x, y)");
+ Object o = e.evaluate(context);
+ Assertions.assertFalse((Boolean) o);
+ }
+
+ @Test
+ void testContainsFunction() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put(null, new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", "hello");
+ context.set("y", "e");
+ JexlExpression e = jexl.createExpression("contains(x, y)");
+ Object o = e.evaluate(context);
+ Assertions.assertTrue((Boolean) o);
+ }
+
+ @Test
+ void testExistsFunction() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put("sys", new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", "hello");
+ JexlExpression e = jexl.createExpression("sys:exists(x)");
+ Object o = e.evaluate(context);
+ Assertions.assertTrue((Boolean) o);
+ }
+
+ @Test
+ void testExistsFunctionWithNull() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put("sys", new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ JexlExpression e = jexl.createExpression("sys:exists(x)");
+ Assertions.assertThrows(JexlException.class, () ->
e.evaluate(context));
+ }
+
+ @Test
+ void testExistsFunctionWithEmptyString() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put("sys", new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", "");
+ JexlExpression e = jexl.createExpression("sys:exists(x)");
+ Object o = e.evaluate(context);
+ Assertions.assertFalse((Boolean) o);
+ }
+
+ @Test
+ void testExistsFunctionWithEmptyStringAndSpace() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put("sys", new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", " ");
+ JexlExpression e = jexl.createExpression("sys:exists(x)");
+ Object o = e.evaluate(context);
+ Assertions.assertTrue((Boolean) o);
+ }
+
+ @Test
+ void testMatchesFunction() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put("sys", new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", "hello");
+ JexlExpression e = jexl.createExpression("sys:matches(x, '.*')");
+ Object o = e.evaluate(context);
+ Assertions.assertTrue((Boolean) o);
+ }
+
+ @Test
+ void testMatchesFunctionWithNull() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put("sys", new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", null);
+ JexlExpression e = jexl.createExpression("sys:matches(x, '.*')");
+ Object o = e.evaluate(context);
+ Assertions.assertFalse((Boolean) o);
+ }
+
+ @Test
+ void testMatchesFunctionWithEmptyString() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put("sys", new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", "");
+ JexlExpression e = jexl.createExpression("sys:matches(x, '.*')");
+ Object o = e.evaluate(context);
+ Assertions.assertTrue((Boolean) o);
+ }
+
+ @Test
+ void testMatchesFunctionWithEmptyStringAndSpace() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put("sys", new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", " ");
+ JexlExpression e = jexl.createExpression("sys:matches(x, '.*')");
+ Object o = e.evaluate(context);
+ Assertions.assertTrue((Boolean) o);
+ }
+
+ @Test
+ void testMatchesFunctionWithRegex() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put("sys", new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", "hello");
+ JexlExpression e = jexl.createExpression("sys:matches(x, 'he.*')");
+ Object o = e.evaluate(context);
+ Assertions.assertTrue((Boolean) o);
+ }
+
+ @Test
+ void testMatchesFunctionWithRegexNotMatch() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put("sys", new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", "hello");
+ JexlExpression e = jexl.createExpression("sys:matches(x, 'he')");
+ Object o = e.evaluate(context);
+ Assertions.assertFalse((Boolean) o);
+ }
+
+ @Test
+ void testMatchesFunctionWithRegexAndSpace() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put("sys", new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", "hello");
+ JexlExpression e = jexl.createExpression("sys:matches(x, 'he.* ')");
+ Object o = e.evaluate(context);
+ Assertions.assertFalse((Boolean) o);
+ }
+
+ @Test
+ void testMatchesFunctionWithRegexAndSpace2() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put(null, new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ context.set("x", "Ubuntu50681269");
+ JexlExpression e = jexl.createExpression("matches(x,
'^[a-zA-Z0-9]+$')");
+ Object o = e.evaluate(context);
+ Assertions.assertTrue((Boolean) o);
+ context.set("x", "Ubuntu_u50681269");
+ o = e.evaluate(context);
+ Assertions.assertFalse((Boolean) o);
+ }
+
+ @Test
+ void testNowFunction() {
+ Map<String, Object> functions = Maps.newLinkedHashMap();
+ functions.put(null, new JexlCommonFunction());
+ jexlBuilder.namespaces(functions);
+ JexlEngine jexl = jexlBuilder.create();
+ JexlContext context = new MapContext();
+ JexlExpression e = jexl.createExpression("now() + '-0'");
+ Object o = e.evaluate(context);
+ String result = (String) o;
+ Assertions.assertTrue(result.endsWith("-0"));
+ }
+
+ @Test
+ void testUnconventionalMapping() {
+ // database_pages=Database pages
+ // name=User Commits Per Sec
+ JexlEngine jexl = jexlBuilder.create();
+ Assertions.assertThrows(JexlException.class, () ->
jexl.createExpression("Database pages"));
+ Assertions.assertDoesNotThrow(() ->
jexl.createExpression("Database_pages"));
+ Assertions.assertThrows(JexlException.class, () ->
jexl.createExpression("User Commits Per Sec"));
+ Assertions.assertThrows(JexlException.class, () ->
jexl.createExpression("System I/O"));
+ Assertions.assertThrows(JexlException.class, () ->
jexl.createExpression("User I/O"));
+ Assertions.assertThrows(JexlException.class, () ->
jexl.createExpression("Library Cache Hit Ratio"));
+ Assertions.assertThrows(JexlException.class, () ->
jexl.createExpression("Buffer Cache Hit Ratio"));
+ Assertions.assertThrows(JexlException.class, () ->
jexl.createExpression("Page reads/sec"));
+ }
+
+ @Test
+ void testRecException() {
+ JexlEngine jexl = jexlBuilder.create();
+ Assertions.assertThrows(JexlException.class, () ->
jexl.createExpression("new java.util.Date()"));
+ }
+
+ /**
+ * custom function
+ */
+ public static class DateFunction {
+ public String now(Object... args) {
+ return System.currentTimeMillis() + "-" + args.length;
+ }
+ }
+}
diff --git
a/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/alert_threshold_expr.md
b/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/alert_threshold_expr.md
index 14fbcdb13..f7c8316c7 100644
---
a/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/alert_threshold_expr.md
+++
b/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/alert_threshold_expr.md
@@ -28,7 +28,7 @@ equals(str1,str2)
#### 表达式函数库列表
-参考: https://www.yuque.com/boyan-avfmj/aviatorscript/ashevw
+todo
#### 支持的环境变量
> 环境变量即指标值等支持的变量,用于在表达式中,阈值计算判断时会将变量替换成实际值进行计算
diff --git
a/manager/src/test/java/org/apache/hertzbeat/manager/ManagerTest.java
b/manager/src/test/java/org/apache/hertzbeat/manager/ManagerTest.java
index b29345046..22bbb369f 100644
--- a/manager/src/test/java/org/apache/hertzbeat/manager/ManagerTest.java
+++ b/manager/src/test/java/org/apache/hertzbeat/manager/ManagerTest.java
@@ -17,6 +17,8 @@
package org.apache.hertzbeat.manager;
+import javax.annotation.Resource;
+import javax.naming.NamingException;
import org.apache.hertzbeat.alert.AlerterProperties;
import org.apache.hertzbeat.alert.AlerterWorkerPool;
import org.apache.hertzbeat.alert.calculate.CalculateAlarm;
@@ -35,7 +37,6 @@ import org.apache.hertzbeat.collector.dispatch.WorkerPool;
import
org.apache.hertzbeat.collector.dispatch.entrance.internal.CollectJobService;
import org.apache.hertzbeat.collector.dispatch.timer.TimerDispatcher;
import org.apache.hertzbeat.collector.dispatch.unit.impl.DataSizeConvert;
-import org.apache.hertzbeat.common.config.AviatorConfiguration;
import org.apache.hertzbeat.common.config.CommonConfig;
import org.apache.hertzbeat.common.config.CommonProperties;
import org.apache.hertzbeat.common.queue.impl.InMemoryCommonDataQueue;
@@ -50,10 +51,6 @@ import
org.apache.hertzbeat.warehouse.store.RealTimeRedisDataStorage;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
-
-import javax.annotation.Resource;
-import javax.naming.NamingException;
-
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -93,7 +90,6 @@ class ManagerTest extends AbstractSpringIntegrationTest {
// test common module
assertNotNull(ctx.getBean(CommonProperties.class));
assertNotNull(ctx.getBean(CommonConfig.class));
- assertNotNull(ctx.getBean(AviatorConfiguration.class));
assertNotNull(ctx.getBean(InMemoryCommonDataQueue.class));
// condition on common.sms.tencent.app-id
assertThrows(NoSuchBeanDefinitionException.class, () ->
ctx.getBean(TencentSmsClient.class));
diff --git a/material/licenses/backend/LICENSE
b/material/licenses/backend/LICENSE
index 0116e2fc6..963e21187 100644
--- a/material/licenses/backend/LICENSE
+++ b/material/licenses/backend/LICENSE
@@ -409,6 +409,7 @@ The text of each license is the standard Apache 2.0 license.
https://mvnrepository.com/artifact/com.github.GreptimeTeam/greptime-proto/0.4.0
https://mvnrepository.com/artifact/org.webjars/swagger-ui/5.10.3
https://mvnrepository.com/artifact/com.google.flatbuffers/flatbuffers-java/1.12.0
+ https://mvnrepository.com/artifact/org.apache.commons/commons-jexl3/3.2.1
========================================================================
@@ -527,13 +528,3 @@ The following components are provided under the
https://spdx.org/licenses/MIT-0.
The text of each license is also included in licenses/LICENSE-[project].txt.
https://mvnrepository.com/artifact/org.reactivestreams/reactive-streams/1.0.4
https://spdx.org/licenses/MIT-0.html
-
-
-========================================================================
-LGPL-2.1 licenses
-========================================================================
-The following components are provided under the LGPL-2.1 License. See project
link for details.
-The text of each license is also included in licenses/LICENSE-[project].txt.
-
- https://mvnrepository.com/artifact/com.googlecode.aviator/aviator/5.4.1
http://www.gnu.org/licenses/lgpl.html
-
diff --git a/material/licenses/backend/LICENSE-aviator.txt
b/material/licenses/backend/LICENSE-aviator.txt
deleted file mode 100644
index 02bbb60bc..000000000
--- a/material/licenses/backend/LICENSE-aviator.txt
+++ /dev/null
@@ -1,165 +0,0 @@
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
- 0. Additional Definitions.
-
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index a95019724..cfb442ab2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -107,7 +107,6 @@
<nekohtml.version>1.9.22</nekohtml.version>
<json-path.version>2.9.0</json-path.version>
- <aviator.version>5.4.1</aviator.version>
<gson.version>2.10.1</gson.version>
<guava.version>32.1.2-jre</guava.version>
<protobuf.version>3.19.6</protobuf.version>
@@ -134,6 +133,7 @@
<taos-jdbcdriver.version>3.0.0</taos-jdbcdriver.version>
<iotdb-session.version>0.13.3</iotdb-session.version>
<commons-collections4.version>4.4</commons-collections4.version>
+ <commons-jexl3>3.2.1</commons-jexl3>
<commons-net>3.10.0</commons-net>
</properties>
@@ -229,6 +229,11 @@
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-jexl3</artifactId>
+ <version>${commons-jexl3}</version>
+ </dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
@@ -318,12 +323,7 @@
<artifactId>json-path</artifactId>
<version>${json-path.version}</version>
</dependency>
- <!-- 表达式计算 -->
- <dependency>
- <groupId>com.googlecode.aviator</groupId>
- <artifactId>aviator</artifactId>
- <version>${aviator.version}</version>
- </dependency>
+ <!-- protobuf -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]