This is an automated email from the ASF dual-hosted git repository.
mattyb149 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/master by this push:
new c93ee5a NIFI-6186 Resolve handling of module paths in
JythonScriptEngineConfigurator
c93ee5a is described below
commit c93ee5ad5949858bd3f4eec2067ae30016ea76ac
Author: ambah <[email protected]>
AuthorDate: Wed Apr 3 18:15:58 2019 -0400
NIFI-6186 Resolve handling of module paths in JythonScriptEngineConfigurator
Signed-off-by: Matthew Burgess <[email protected]>
This closes #3406
---
.../impl/JythonScriptEngineConfigurator.java | 3 +-
.../nifi/processors/script/TestInvokeJython.java | 55 ++++++++++++++++
.../src/test/resources/jython/test_modules_path.py | 74 ++++++++++++++++++++++
3 files changed, 131 insertions(+), 1 deletion(-)
diff --git
a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/script/impl/JythonScriptEngineConfigurator.java
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/script/impl/JythonScriptEngineConfigurator.java
index 458658b..3b8271b 100644
---
a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/script/impl/JythonScriptEngineConfigurator.java
+++
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/script/impl/JythonScriptEngineConfigurator.java
@@ -18,6 +18,7 @@ package org.apache.nifi.script.impl;
import org.apache.nifi.logging.ComponentLog;
import org.apache.nifi.processors.script.ScriptEngineConfigurator;
+import org.python.core.PyString;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
@@ -47,7 +48,7 @@ public class JythonScriptEngineConfigurator implements
ScriptEngineConfigurator
engine.eval("import sys");
if (modulePaths != null) {
for (String modulePath : modulePaths) {
- engine.eval("sys.path.append('" + modulePath + "')");
+ engine.eval("sys.path.append(" +
PyString.encode_UnicodeEscape(modulePath, true) + ")");
}
}
}
diff --git
a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/java/org/apache/nifi/processors/script/TestInvokeJython.java
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/java/org/apache/nifi/processors/script/TestInvokeJython.java
index 5b17e04..345f4af 100755
---
a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/java/org/apache/nifi/processors/script/TestInvokeJython.java
+++
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/java/org/apache/nifi/processors/script/TestInvokeJython.java
@@ -16,6 +16,7 @@
*/
package org.apache.nifi.processors.script;
+import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.script.ScriptingComponentUtils;
import org.apache.nifi.util.MockFlowFile;
@@ -31,6 +32,9 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
public class TestInvokeJython extends BaseScriptTest {
@@ -95,6 +99,57 @@ public class TestInvokeJython extends BaseScriptTest {
}
/**
+ * Test a script that has a Jython processor that reads the system path as
controlled by the Module Directory property then stores it in the attributes of
the flowfile being routed.
+ * <p>
+ * This tests whether the JythonScriptEngineConfigurator successfully
translates the "Module Directory" property into Python system paths, even with
strings that contain Python escape sequences
+ *
+ * @throws Exception Any error encountered while testing
+ */
+ @Test
+ public void testUpdateAttributeFromProcessorModulePaths() throws Exception
{
+ // Prepare a set of easily identified paths for the Module Directory
property
+ final String moduleDirectoryTestPrefix = "test";
+ final String[] testModuleDirectoryValues = {
"abc","\\a\\b\\c","\\123","\\d\"e" };
+ final int numTestValues = testModuleDirectoryValues.length;
+ // Prepend each module directory value with a simple prefix and an
identifying number so we can identify it later.
+ final List<String> testModuleDirectoryFullValues =
IntStream.range(0,numTestValues)
+ .boxed()
+ .map(i ->
String.format("%s#%s#%s",moduleDirectoryTestPrefix,i,testModuleDirectoryValues[i]))
+ .collect(Collectors.toList());
+ final String testModuleDirectoryCombined =
String.join(",",testModuleDirectoryFullValues);
+
+ // Run the script that captures the system path resulting from the
Module Directory property
+ final TestRunner runner = TestRunners.newTestRunner(new
InvokeScriptedProcessor());
+
+ runner.setValidateExpressionUsage(false);
+
runner.setProperty(scriptingComponent.getScriptingComponentHelper().SCRIPT_ENGINE,
"python");
+ runner.setProperty(ScriptingComponentUtils.SCRIPT_FILE,
"target/test/resources/jython/test_modules_path.py");
+ runner.setProperty(ScriptingComponentUtils.MODULES,
testModuleDirectoryCombined);
+
+ final Map<String, String> attributes = new HashMap<>();
+
+ runner.assertValid();
+ runner.enqueue(new byte[0], attributes);
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred("success", 1);
+ final List<MockFlowFile> result =
runner.getFlowFilesForRelationship("success");
+
+ // verify successful processing of the module paths
+ result.get(0).assertAttributeExists("from-path");
+ final String[] effectivePaths =
result.get(0).getAttribute("from-path").split(","); // Extract the
comma-delimited paths from the script-produced attribute
+ Assert.assertTrue(effectivePaths.length >= numTestValues); // we
should have our test values, plus defaults
+ // Isolate only the paths with our identified prefix
+ final List<String> relevantPaths =
Arrays.stream(effectivePaths).filter(path ->
path.startsWith(moduleDirectoryTestPrefix)).collect(Collectors.toList());
+ Assert.assertEquals(testModuleDirectoryFullValues.size(),
relevantPaths.size());
+ relevantPaths.forEach(path -> {
+ final int resultIx =
Integer.valueOf(StringUtils.substringBetween(path,"#")); // extract the index
so we can relate it to the sources, despite potential mangling
+ final String expectedValue =
testModuleDirectoryFullValues.get(resultIx);
+ Assert.assertEquals(expectedValue, path); // Ensure our path was
passed through without mangling
+ });
+ }
+
+ /**
* Tests a script that has a Jython Processor that that reads the first
line of text from the flowfiles content and stores the value in an attribute of
the outgoing flowfile.
*
* @throws Exception Any error encountered while testing
diff --git
a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/resources/jython/test_modules_path.py
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/resources/jython/test_modules_path.py
new file mode 100644
index 0000000..bee52e6
--- /dev/null
+++
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/resources/jython/test_modules_path.py
@@ -0,0 +1,74 @@
+#! /usr/bin/python
+#
+# 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.
+#
+
+import sys
+import traceback
+from org.apache.nifi.processor import Processor
+from org.apache.nifi.processor import Relationship
+
+class ReadModulesPathAndStoreAsAttribute(Processor) :
+ __rel_success =
Relationship.Builder().description("Success").name("success").build()
+
+ def __init__(self) :
+ pass
+
+ def initialize(self, context) :
+ pass
+
+ def getRelationships(self) :
+ return set([self.__rel_success])
+
+ def validate(self, context) :
+ pass
+
+ def getPropertyDescriptors(self) :
+ pass
+
+ def onPropertyModified(self, descriptor, newValue, oldValue) :
+ pass
+
+ def onTrigger(self, context, sessionFactory) :
+ session = sessionFactory.createSession()
+ try :
+ # ensure there is work to do
+ flowfile = session.get()
+ if flowfile is None :
+ return
+
+ # Extract sys.path and encode as a comma-separated string
+ sysPathString = ','.join(sys.path)
+
+ # set an attribute with the captured contents of sys.path
+ flowfile = session.putAttribute(flowfile, "from-path",
sysPathString)
+
+ # transfer
+ session.transfer(flowfile, self.__rel_success)
+ session.commit()
+ except :
+ print sys.exc_info()[0]
+ print "Exception in TestReader:"
+ print '-' * 60
+ traceback.print_exc(file=sys.stdout)
+ print '-' * 60
+
+ session.rollback(True)
+ raise
+
+processor = ReadModulesPathAndStoreAsAttribute()
\ No newline at end of file