NIFI-8730: Invalidate (don't evaluate) empty script in scripting components

Signed-off-by: Pierre Villard <pierre.villard.fr@gmail.com>

This closes #5184.
This commit is contained in:
Matthew Burgess 2021-06-24 11:09:58 -04:00 committed by Pierre Villard
parent b27c2b500e
commit 632fbcca7d
No known key found for this signature in database
GPG Key ID: F92A93B30C07C6D5
9 changed files with 45 additions and 29 deletions

View File

@ -241,11 +241,10 @@ public class BaseScriptedLookupService extends AbstractScriptedControllerService
public void setup() { public void setup() {
if (scriptNeedsReload.get() || lookupService.get() == null) { if (scriptNeedsReload.get() || lookupService.get() == null) {
if (ScriptingComponentHelper.isFile(scriptingComponentHelper.getScriptPath())) { if (ScriptingComponentHelper.isFile(scriptingComponentHelper.getScriptPath())) {
reloadScriptFile(scriptingComponentHelper.getScriptPath()); scriptNeedsReload.set(reloadScriptFile(scriptingComponentHelper.getScriptPath()));
} else { } else {
reloadScriptBody(scriptingComponentHelper.getScriptBody()); scriptNeedsReload.set(reloadScriptBody(scriptingComponentHelper.getScriptBody()));
} }
scriptNeedsReload.set(false);
} }
} }

View File

@ -215,11 +215,10 @@ public class InvokeScriptedProcessor extends AbstractSessionFactoryProcessor {
public void setup() { public void setup() {
if (scriptNeedsReload.get() || processor.get() == null) { if (scriptNeedsReload.get() || processor.get() == null) {
if (ScriptingComponentHelper.isFile(scriptingComponentHelper.getScriptPath())) { if (ScriptingComponentHelper.isFile(scriptingComponentHelper.getScriptPath())) {
reloadScriptFile(scriptingComponentHelper.getScriptPath()); scriptNeedsReload.set(reloadScriptFile(scriptingComponentHelper.getScriptPath()));
} else { } else {
reloadScriptBody(scriptingComponentHelper.getScriptBody()); scriptNeedsReload.set(reloadScriptBody(scriptingComponentHelper.getScriptBody()));
} }
scriptNeedsReload.set(false);
} }
} }
@ -326,6 +325,10 @@ public class InvokeScriptedProcessor extends AbstractSessionFactoryProcessor {
* @return Whether the script was successfully reloaded * @return Whether the script was successfully reloaded
*/ */
private boolean reloadScript(final String scriptBody) { private boolean reloadScript(final String scriptBody) {
if (StringUtils.isEmpty(scriptBody)) {
return true;
}
// note we are starting here with a fresh listing of validation // note we are starting here with a fresh listing of validation
// results since we are (re)loading a new/updated script. any // results since we are (re)loading a new/updated script. any
// existing validation results are not relevant // existing validation results are not relevant
@ -431,15 +434,14 @@ public class InvokeScriptedProcessor extends AbstractSessionFactoryProcessor {
// store the updated validation results // store the updated validation results
validationResults.set(results); validationResults.set(results);
// return whether there was any issues loading the configured script // return whether there were any issues loading the configured script
return results.isEmpty(); return !results.isEmpty();
} }
/** /**
* Invokes the validate() routine provided by the script, allowing for * Invokes the validate() routine provided by the script, allowing for
* custom validation code. This method assumes there is a valid Processor * custom validation code, if there is a valid Processor
* defined in the script and it has been loaded by the * defined in the script and it has been loaded by the processor
* InvokeScriptedProcessor processor
* *
* @param context The validation context to be passed into the custom * @param context The validation context to be passed into the custom
* validate method * validate method
@ -460,6 +462,12 @@ public class InvokeScriptedProcessor extends AbstractSessionFactoryProcessor {
return validationResults.get(); return validationResults.get();
} }
Collection<ValidationResult> scriptingComponentHelperResults = scriptingComponentHelper.customValidate(context);
if (scriptingComponentHelperResults != null && !scriptingComponentHelperResults.isEmpty()) {
validationResults.set(scriptingComponentHelperResults);
return scriptingComponentHelperResults;
}
scriptingComponentHelper.setScriptEngineName(context.getProperty(scriptingComponentHelper.SCRIPT_ENGINE).getValue()); scriptingComponentHelper.setScriptEngineName(context.getProperty(scriptingComponentHelper.SCRIPT_ENGINE).getValue());
scriptingComponentHelper.setScriptPath(context.getProperty(ScriptingComponentUtils.SCRIPT_FILE).evaluateAttributeExpressions().getValue()); scriptingComponentHelper.setScriptPath(context.getProperty(ScriptingComponentUtils.SCRIPT_FILE).evaluateAttributeExpressions().getValue());
scriptingComponentHelper.setScriptBody(context.getProperty(ScriptingComponentUtils.SCRIPT_BODY).getValue()); scriptingComponentHelper.setScriptBody(context.getProperty(ScriptingComponentUtils.SCRIPT_BODY).getValue());
@ -476,7 +484,7 @@ public class InvokeScriptedProcessor extends AbstractSessionFactoryProcessor {
try { try {
// defer to the underlying processor for validation, without the // defer to the underlying processor for validation, without the
// invokescriptedprocessor properties // invokescriptedprocessor properties
final Set<PropertyDescriptor> innerPropertyDescriptor = new HashSet<PropertyDescriptor>(scriptingComponentHelper.getDescriptors()); final Set<PropertyDescriptor> innerPropertyDescriptor = new HashSet<>(scriptingComponentHelper.getDescriptors());
ValidationContext innerValidationContext = new FilteredPropertiesValidationContextAdapter(context, innerPropertyDescriptor); ValidationContext innerValidationContext = new FilteredPropertiesValidationContextAdapter(context, innerPropertyDescriptor);
final Collection<ValidationResult> instanceResults = instance.validate(innerValidationContext); final Collection<ValidationResult> instanceResults = instance.validate(innerValidationContext);
@ -568,7 +576,10 @@ public class InvokeScriptedProcessor extends AbstractSessionFactoryProcessor {
@OnStopped @OnStopped
public void stop(ProcessContext context) { public void stop(ProcessContext context) {
invokeScriptedProcessorMethod("onStopped", context); // If the script needs to be reloaded at this point, it is because it was empty
if (!scriptNeedsReload.get()) {
invokeScriptedProcessorMethod("onStopped", context);
}
scriptingComponentHelper.stop(); scriptingComponentHelper.stop();
processor.set(null); processor.set(null);
scriptRunner = null; scriptRunner = null;

View File

@ -168,7 +168,7 @@ public class ScriptedTransformRecord extends AbstractProcessor implements Search
@OnScheduled @OnScheduled
public void setup(final ProcessContext context) throws IOException { public void setup(final ProcessContext context) throws IOException {
if (!scriptingComponentHelper.isInitialized.get()) { if (!scriptingComponentHelper.isInitialized.get()) {
scriptingComponentHelper.createResources(); scriptingComponentHelper.createResources(false);
} }
scriptingComponentHelper.setupVariables(context); scriptingComponentHelper.setupVariables(context);

View File

@ -107,11 +107,10 @@ public class ScriptedRecordSink extends AbstractScriptedControllerService implem
public void setup() { public void setup() {
if (scriptNeedsReload.get() || recordSink.get() == null) { if (scriptNeedsReload.get() || recordSink.get() == null) {
if (ScriptingComponentHelper.isFile(scriptingComponentHelper.getScriptPath())) { if (ScriptingComponentHelper.isFile(scriptingComponentHelper.getScriptPath())) {
reloadScriptFile(scriptingComponentHelper.getScriptPath()); scriptNeedsReload.set(reloadScriptFile(scriptingComponentHelper.getScriptPath()));
} else { } else {
reloadScriptBody(scriptingComponentHelper.getScriptBody()); scriptNeedsReload.set(reloadScriptBody(scriptingComponentHelper.getScriptBody()));
} }
scriptNeedsReload.set(false);
} }
} }

View File

@ -78,11 +78,10 @@ public class ScriptedRulesEngine extends AbstractScriptedControllerService imple
public void setup() { public void setup() {
if (scriptNeedsReload.get() || rulesEngine.get() == null) { if (scriptNeedsReload.get() || rulesEngine.get() == null) {
if (ScriptingComponentHelper.isFile(scriptingComponentHelper.getScriptPath())) { if (ScriptingComponentHelper.isFile(scriptingComponentHelper.getScriptPath())) {
reloadScriptFile(scriptingComponentHelper.getScriptPath()); scriptNeedsReload.set(reloadScriptFile(scriptingComponentHelper.getScriptPath()));
} else { } else {
reloadScriptBody(scriptingComponentHelper.getScriptBody()); scriptNeedsReload.set(reloadScriptBody(scriptingComponentHelper.getScriptBody()));
} }
scriptNeedsReload.set(false);
} }
} }

View File

@ -80,11 +80,10 @@ public class ScriptedActionHandler extends AbstractScriptedControllerService imp
public void setup() { public void setup() {
if (scriptNeedsReload.get() || actionHandler.get() == null) { if (scriptNeedsReload.get() || actionHandler.get() == null) {
if (ScriptingComponentHelper.isFile(scriptingComponentHelper.getScriptPath())) { if (ScriptingComponentHelper.isFile(scriptingComponentHelper.getScriptPath())) {
reloadScriptFile(scriptingComponentHelper.getScriptPath()); scriptNeedsReload.set(reloadScriptFile(scriptingComponentHelper.getScriptPath()));
} else { } else {
reloadScriptBody(scriptingComponentHelper.getScriptBody()); scriptNeedsReload.set(reloadScriptBody(scriptingComponentHelper.getScriptBody()));
} }
scriptNeedsReload.set(false);
} }
} }

View File

@ -119,7 +119,7 @@ public abstract class AbstractScriptedControllerService extends AbstractControll
@Override @Override
protected Collection<ValidationResult> customValidate(ValidationContext validationContext) { protected Collection<ValidationResult> customValidate(ValidationContext validationContext) {
Collection<ValidationResult> commonValidationResults = super.customValidate(validationContext); Collection<ValidationResult> commonValidationResults = new ArrayList<>(super.customValidate(validationContext));
commonValidationResults.addAll(scriptingComponentHelper.customValidate(validationContext)); commonValidationResults.addAll(scriptingComponentHelper.customValidate(validationContext));
if (!commonValidationResults.isEmpty()) { if (!commonValidationResults.isEmpty()) {
@ -173,7 +173,7 @@ public abstract class AbstractScriptedControllerService extends AbstractControll
validationResults.set(results); validationResults.set(results);
// return whether there was any issues loading the configured script // return whether there was any issues loading the configured script
return results.isEmpty(); return !results.isEmpty();
} }
/** /**

View File

@ -52,7 +52,6 @@ public class TestInvokeJavascript extends BaseScriptTest {
*/ */
@Test @Test
public void testReadFlowFileContentAndStoreInFlowFileAttribute() throws Exception { public void testReadFlowFileContentAndStoreInFlowFileAttribute() throws Exception {
runner.setValidateExpressionUsage(false);
runner.setProperty(scriptingComponent.getScriptingComponentHelper().SCRIPT_ENGINE, "ECMAScript"); runner.setProperty(scriptingComponent.getScriptingComponentHelper().SCRIPT_ENGINE, "ECMAScript");
runner.setProperty(ScriptingComponentUtils.SCRIPT_FILE, "target/test/resources/javascript/test_reader.js"); runner.setProperty(ScriptingComponentUtils.SCRIPT_FILE, "target/test/resources/javascript/test_reader.js");
runner.setProperty(ScriptingComponentUtils.MODULES, "target/test/resources/javascript"); runner.setProperty(ScriptingComponentUtils.MODULES, "target/test/resources/javascript");
@ -146,7 +145,6 @@ public class TestInvokeJavascript extends BaseScriptTest {
@Test(expected = AssertionError.class) @Test(expected = AssertionError.class)
public void testInvokeScriptCausesException() throws Exception { public void testInvokeScriptCausesException() throws Exception {
final TestRunner runner = TestRunners.newTestRunner(new InvokeScriptedProcessor()); final TestRunner runner = TestRunners.newTestRunner(new InvokeScriptedProcessor());
runner.setValidateExpressionUsage(false);
runner.setProperty(scriptingComponent.getScriptingComponentHelper().SCRIPT_ENGINE, "ECMAScript"); runner.setProperty(scriptingComponent.getScriptingComponentHelper().SCRIPT_ENGINE, "ECMAScript");
runner.setProperty(ScriptingComponentUtils.SCRIPT_BODY, getFileContentsAsString( runner.setProperty(ScriptingComponentUtils.SCRIPT_BODY, getFileContentsAsString(
TEST_RESOURCE_LOCATION + "javascript/testInvokeScriptCausesException.js") TEST_RESOURCE_LOCATION + "javascript/testInvokeScriptCausesException.js")
@ -164,7 +162,6 @@ public class TestInvokeJavascript extends BaseScriptTest {
*/ */
@Test @Test
public void testScriptRoutesToFailure() throws Exception { public void testScriptRoutesToFailure() throws Exception {
runner.setValidateExpressionUsage(false);
runner.setProperty(scriptingComponent.getScriptingComponentHelper().SCRIPT_ENGINE, "ECMAScript"); runner.setProperty(scriptingComponent.getScriptingComponentHelper().SCRIPT_ENGINE, "ECMAScript");
runner.setProperty(ScriptingComponentUtils.SCRIPT_BODY, getFileContentsAsString( runner.setProperty(ScriptingComponentUtils.SCRIPT_BODY, getFileContentsAsString(
TEST_RESOURCE_LOCATION + "javascript/testScriptRoutesToFailure.js") TEST_RESOURCE_LOCATION + "javascript/testScriptRoutesToFailure.js")
@ -177,4 +174,16 @@ public class TestInvokeJavascript extends BaseScriptTest {
final List<MockFlowFile> result = runner.getFlowFilesForRelationship("FAILURE"); final List<MockFlowFile> result = runner.getFlowFilesForRelationship("FAILURE");
assertFalse(result.isEmpty()); assertFalse(result.isEmpty());
} }
/**
* Tests an empty script with Nashorn (which throws an NPE if it is loaded), this test verifies an empty script is not attempted to be loaded.
*
* @throws Exception Any error encountered while testing
*/
@Test
public void testEmptyScript() throws Exception {
runner.setProperty(scriptingComponent.getScriptingComponentHelper().SCRIPT_ENGINE, "ECMAScript");
runner.setProperty(ScriptingComponentUtils.SCRIPT_BODY, "");
runner.assertNotValid();
}
} }

View File

@ -57,7 +57,7 @@ class GroovyProcessor implements Processor {
@Override @Override
void onTrigger(ProcessContext context, ProcessSessionFactory sessionFactory) throws ProcessException { void onTrigger(ProcessContext context, ProcessSessionFactory sessionFactory) throws ProcessException {
def session = sessionFactory.createSession() def session = sessionFactory.createSession()
def flowFile = session.get(); def flowFile = session.get()
if (flowFile == null) { if (flowFile == null) {
return return
} }