azkaban-aplcache
Changes
src/main/java/azkaban/utils/PropsUtils.java 45(+42 -3)
Details
src/main/java/azkaban/utils/PropsUtils.java 45(+42 -3)
diff --git a/src/main/java/azkaban/utils/PropsUtils.java b/src/main/java/azkaban/utils/PropsUtils.java
index 37cd003..45501c6 100644
--- a/src/main/java/azkaban/utils/PropsUtils.java
+++ b/src/main/java/azkaban/utils/PropsUtils.java
@@ -31,6 +31,9 @@ import java.util.regex.Pattern;
import azkaban.executor.ExecutableFlowBase;
import azkaban.flow.CommonJobProperties;
+import org.apache.commons.jexl2.Expression;
+import org.apache.commons.jexl2.JexlEngine;
+import org.apache.commons.jexl2.MapContext;
import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;
@@ -144,7 +147,7 @@ public class PropsUtils {
return false;
}
- private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{([a-zA-Z_.0-9]+)\\}");
+ private static final Pattern VARIABLE_REPLACEMENT_PATTERN = Pattern.compile("\\$\\{([a-zA-Z_.0-9]+)\\}");
public static Props resolveProps(Props props) {
if (props == null) return null;
@@ -157,9 +160,16 @@ public class PropsUtils {
visitedVariables.add(key);
String replacedValue = resolveVariableReplacement(value, props, visitedVariables);
+ String expressedValue = resolveVariableExpression(replacedValue);
visitedVariables.clear();
- resolvedProps.put(key, replacedValue);
+ resolvedProps.put(key, expressedValue);
+ }
+
+ for (String key : resolvedProps.getKeySet()) {
+ String value = resolvedProps.get(key);
+ String expressedValue = resolveVariableExpression(value);
+ resolvedProps.put(key, expressedValue);
}
return resolvedProps;
@@ -169,7 +179,7 @@ public class PropsUtils {
StringBuffer buffer = new StringBuffer();
int startIndex = 0;
- Matcher matcher = VARIABLE_PATTERN.matcher(value);
+ Matcher matcher = VARIABLE_REPLACEMENT_PATTERN.matcher(value);
while (matcher.find(startIndex)) {
if (startIndex < matcher.start()) {
// Copy everything up front to the buffer
@@ -207,6 +217,35 @@ public class PropsUtils {
return buffer.toString();
}
+
+ private static final Pattern VARIABLE_EXPRESSION_PATTERN = Pattern.compile("\\$\\(([a-zA-Z_.0-9\\s\\+\\-\\*\\/\"']+)\\)");
+ private static String resolveVariableExpression(String value) {
+ Matcher matcher = VARIABLE_EXPRESSION_PATTERN.matcher(value);
+ JexlEngine jexl = new JexlEngine();
+
+ if (!matcher.find()) {
+ System.out.println("No matches found");
+ return value;
+ }
+
+ StringBuilder builder = new StringBuilder();
+
+ int startIndex = 0;
+ while (matcher.find(startIndex)) {
+ int start = matcher.start();
+ int end = matcher.end();
+ builder.append(value.substring(startIndex, start));
+
+ String group = matcher.group(1);
+
+ Expression e = jexl.createExpression(group);
+ Object result = e.evaluate(new MapContext());
+ builder.append(result);
+ startIndex = end;
+ }
+ builder.append(value.substring(startIndex));
+ return resolveVariableExpression(builder.toString());
+ }
public static Props addCommonFlowProperties(Props parentProps, final ExecutableFlowBase flow) {
Props props = new Props(parentProps);
diff --git a/unit/java/azkaban/test/utils/PropsUtilsTest.java b/unit/java/azkaban/test/utils/PropsUtilsTest.java
index 09edb89..5676e95 100644
--- a/unit/java/azkaban/test/utils/PropsUtilsTest.java
+++ b/unit/java/azkaban/test/utils/PropsUtilsTest.java
@@ -51,6 +51,36 @@ public class PropsUtilsTest {
}
@Test
+ public void testExpressionResolution() throws IOException {
+ Props props = Props.of(
+ "normkey", "normal",
+ "num1", "1",
+ "num2", "2",
+ "num3", "3",
+ "variablereplaced", "${num1}",
+ "expression1", "$(1+10)",
+ "expression2", "$(1+10)*2",
+ "expression3", "$((${num1} + ${num3})*10)",
+ "expression4", "$(${num1} + ${expression3})",
+ "expression5", "$($($(2+3)) + 3) + $(${expression3} + 1))",
+ "expression6", "$(1 + ${normkey}))"
+ );
+
+ Props resolved = PropsUtils.resolveProps(props);
+ Assert.assertEquals("normal", resolved.get("normkey"));
+ Assert.assertEquals("1", resolved.get("num1"));
+ Assert.assertEquals("2", resolved.get("num2"));
+ Assert.assertEquals("3", resolved.get("num3"));
+ Assert.assertEquals("1", resolved.get("variablereplaced"));
+ Assert.assertEquals("11", resolved.get("expression1"));
+ Assert.assertEquals("11*2", resolved.get("expression2"));
+ Assert.assertEquals("40", resolved.get("expression3"));
+ Assert.assertEquals("41", resolved.get("expression4"));
+ Assert.assertEquals("11 + 41", resolved.get("expression5"));
+ Assert.assertEquals("1 + normal", resolved.get("expression6"));
+ }
+
+ @Test
public void testCyclesResolveProps() throws IOException {
Props propsGrandParent = new Props();
Props propsParent = new Props(propsGrandParent);