This is an automated email from the ASF dual-hosted git repository.

mthomsen 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 1b196ea  NIFI-5338: Added SimpleScriptedLookupService controller 
service
1b196ea is described below

commit 1b196ea2134e3fd5075c910cd43aef544f44cb3e
Author: Matthew Burgess <[email protected]>
AuthorDate: Mon Apr 1 13:26:16 2019 -0400

    NIFI-5338: Added SimpleScriptedLookupService controller service
    
    This closes #3397
    
    Signed-off-by: Mike Thomsen <[email protected]>
---
 ...Service.java => BaseScriptedLookupService.java} |  68 +---
 .../nifi/lookup/script/ScriptedLookupService.java  | 357 +--------------------
 .../lookup/script/SimpleScriptedLookupService.java |  61 ++++
 .../org.apache.nifi.controller.ControllerService   |   3 +-
 .../script/TestSimpleScriptedLookupService.groovy  | 112 +++++++
 .../groovy/test_simple_lookup_inline.groovy        |  73 +++++
 6 files changed, 268 insertions(+), 406 deletions(-)

diff --git 
a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/lookup/script/ScriptedLookupService.java
 
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/lookup/script/BaseScriptedLookupService.java
similarity index 84%
copy from 
nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/lookup/script/ScriptedLookupService.java
copy to 
nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/lookup/script/BaseScriptedLookupService.java
index 956a9ce..1f00ad3 100644
--- 
a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/lookup/script/ScriptedLookupService.java
+++ 
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/lookup/script/BaseScriptedLookupService.java
@@ -16,16 +16,11 @@
  */
 package org.apache.nifi.lookup.script;
 
-import org.apache.nifi.annotation.behavior.Restricted;
-import org.apache.nifi.annotation.behavior.Restriction;
-import org.apache.nifi.annotation.documentation.CapabilityDescription;
-import org.apache.nifi.annotation.documentation.Tags;
 import org.apache.nifi.annotation.lifecycle.OnDisabled;
 import org.apache.nifi.annotation.lifecycle.OnEnabled;
 import org.apache.nifi.components.AllowableValue;
 import org.apache.nifi.components.ConfigurableComponent;
 import org.apache.nifi.components.PropertyDescriptor;
-import org.apache.nifi.components.RequiredPermission;
 import org.apache.nifi.components.ValidationResult;
 import org.apache.nifi.components.state.StateManager;
 import org.apache.nifi.controller.ConfigurationContext;
@@ -33,7 +28,6 @@ import 
org.apache.nifi.controller.ControllerServiceInitializationContext;
 import org.apache.nifi.controller.ControllerServiceLookup;
 import org.apache.nifi.expression.ExpressionLanguageScope;
 import org.apache.nifi.logging.ComponentLog;
-import org.apache.nifi.lookup.LookupFailureException;
 import org.apache.nifi.lookup.LookupService;
 import org.apache.nifi.processor.exception.ProcessException;
 import org.apache.nifi.processor.util.StandardValidators;
@@ -50,50 +44,16 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 
-/**
- * A Controller service that allows the user to script the lookup operation to 
be performed (by LookupRecord, e.g.)
- */
-@Tags({"lookup", "record", "script", "invoke", "groovy", "python", "jython", 
"jruby", "ruby", "javascript", "js", "lua", "luaj"})
-@CapabilityDescription("Allows the user to provide a scripted LookupService 
instance in order to enrich records from " +
-        "an incoming flow file. Please note, that due to a bug in Jython that 
remains unresolved, it is not possible to use " +
-        "Jython to write a script for this service in Python.")
-@Restricted(
-        restrictions = {
-                @Restriction(
-                        requiredPermission = RequiredPermission.EXECUTE_CODE,
-                        explanation = "Provides operator the ability to 
execute arbitrary code assuming all permissions that NiFi has.")
-        }
-)
-public class ScriptedLookupService extends AbstractScriptedControllerService 
implements LookupService<Object> {
-
-    protected final AtomicReference<LookupService<Object>> lookupService = new 
AtomicReference<>();
+public class BaseScriptedLookupService extends 
AbstractScriptedControllerService {
 
-    private volatile String kerberosServicePrincipal = null;
-    private volatile File kerberosConfigFile = null;
-    private volatile File kerberosServiceKeytab = null;
-
-    @Override
-    public Optional<Object> lookup(Map<String, Object> coordinates) throws 
LookupFailureException {
-        // Delegate the lookup() call to the scripted LookupService
-        return lookupService.get().lookup(coordinates);
-    }
+    protected final AtomicReference<LookupService> lookupService = new 
AtomicReference<>();
 
-    @Override
-    public Set<String> getRequiredKeys() {
-        return lookupService.get().getRequiredKeys();
-    }
-
-    @Override
-    public Class<?> getValueType() {
-        // Delegate the getValueType() call to the scripted LookupService
-        return lookupService.get().getValueType();
-    }
+    protected volatile String kerberosServicePrincipal = null;
+    protected volatile File kerberosConfigFile = null;
+    protected volatile File kerberosServiceKeytab = null;
 
     @Override
     protected void init(final ControllerServiceInitializationContext context) {
@@ -125,10 +85,10 @@ public class ScriptedLookupService extends 
AbstractScriptedControllerService imp
         _temp.remove(scriptingComponentHelper.SCRIPT_ENGINE);
 
         PropertyDescriptor.Builder jythonLessEngineProp = new 
PropertyDescriptor
-            
.Builder().fromPropertyDescriptor(scriptingComponentHelper.SCRIPT_ENGINE);
+                
.Builder().fromPropertyDescriptor(scriptingComponentHelper.SCRIPT_ENGINE);
         List<AllowableValue> filtered = 
scriptingComponentHelper.getScriptEngineAllowableValues()
-            .stream().filter(allowableValue -> 
!allowableValue.getValue().contains("ython"))
-            .collect(Collectors.toList());
+                .stream().filter(allowableValue -> 
!allowableValue.getValue().contains("ython"))
+                .collect(Collectors.toList());
         jythonLessEngineProp.allowableValues(filtered.toArray(new 
AllowableValue[filtered.size()]));
 
         supportedPropertyDescriptors.add(jythonLessEngineProp.build());
@@ -341,7 +301,7 @@ public class ScriptedLookupService extends 
AbstractScriptedControllerService imp
                             scriptedLookupService.initialize(new 
ControllerServiceInitializationContext() {
                                 @Override
                                 public String getIdentifier() {
-                                    return 
ScriptedLookupService.this.getIdentifier();
+                                    return 
BaseScriptedLookupService.this.getIdentifier();
                                 }
 
                                 @Override
@@ -351,27 +311,27 @@ public class ScriptedLookupService extends 
AbstractScriptedControllerService imp
 
                                 @Override
                                 public StateManager getStateManager() {
-                                    return 
ScriptedLookupService.this.getStateManager();
+                                    return 
BaseScriptedLookupService.this.getStateManager();
                                 }
 
                                 @Override
                                 public ControllerServiceLookup 
getControllerServiceLookup() {
-                                    return 
ScriptedLookupService.super.getControllerServiceLookup();
+                                    return 
BaseScriptedLookupService.super.getControllerServiceLookup();
                                 }
 
                                 @Override
                                 public String getKerberosServicePrincipal() {
-                                    return 
ScriptedLookupService.this.kerberosServicePrincipal;
+                                    return 
BaseScriptedLookupService.this.kerberosServicePrincipal;
                                 }
 
                                 @Override
                                 public File getKerberosServiceKeytab() {
-                                    return 
ScriptedLookupService.this.kerberosServiceKeytab;
+                                    return 
BaseScriptedLookupService.this.kerberosServiceKeytab;
                                 }
 
                                 @Override
                                 public File getKerberosConfigurationFile() {
-                                    return 
ScriptedLookupService.this.kerberosConfigFile;
+                                    return 
BaseScriptedLookupService.this.kerberosConfigFile;
                                 }
                             });
                         } catch (final Exception e) {
diff --git 
a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/lookup/script/ScriptedLookupService.java
 
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/lookup/script/ScriptedLookupService.java
index 956a9ce..0ef8309 100644
--- 
a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/lookup/script/ScriptedLookupService.java
+++ 
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/lookup/script/ScriptedLookupService.java
@@ -20,41 +20,13 @@ import org.apache.nifi.annotation.behavior.Restricted;
 import org.apache.nifi.annotation.behavior.Restriction;
 import org.apache.nifi.annotation.documentation.CapabilityDescription;
 import org.apache.nifi.annotation.documentation.Tags;
-import org.apache.nifi.annotation.lifecycle.OnDisabled;
-import org.apache.nifi.annotation.lifecycle.OnEnabled;
-import org.apache.nifi.components.AllowableValue;
-import org.apache.nifi.components.ConfigurableComponent;
-import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.RequiredPermission;
-import org.apache.nifi.components.ValidationResult;
-import org.apache.nifi.components.state.StateManager;
-import org.apache.nifi.controller.ConfigurationContext;
-import org.apache.nifi.controller.ControllerServiceInitializationContext;
-import org.apache.nifi.controller.ControllerServiceLookup;
-import org.apache.nifi.expression.ExpressionLanguageScope;
-import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.lookup.LookupFailureException;
 import org.apache.nifi.lookup.LookupService;
-import org.apache.nifi.processor.exception.ProcessException;
-import org.apache.nifi.processor.util.StandardValidators;
-import org.apache.nifi.processors.script.ScriptEngineConfigurator;
-import org.apache.nifi.script.AbstractScriptedControllerService;
-import org.apache.nifi.script.ScriptingComponentHelper;
-import org.apache.nifi.script.ScriptingComponentUtils;
 
-import javax.script.Invocable;
-import javax.script.ScriptException;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.Collectors;
 
 /**
  * A Controller service that allows the user to script the lookup operation to 
be performed (by LookupRecord, e.g.)
@@ -70,21 +42,10 @@ import java.util.stream.Collectors;
                         explanation = "Provides operator the ability to 
execute arbitrary code assuming all permissions that NiFi has.")
         }
 )
-public class ScriptedLookupService extends AbstractScriptedControllerService 
implements LookupService<Object> {
-
-    protected final AtomicReference<LookupService<Object>> lookupService = new 
AtomicReference<>();
-
-    private volatile String kerberosServicePrincipal = null;
-    private volatile File kerberosConfigFile = null;
-    private volatile File kerberosServiceKeytab = null;
-
-    @Override
-    public Optional<Object> lookup(Map<String, Object> coordinates) throws 
LookupFailureException {
-        // Delegate the lookup() call to the scripted LookupService
-        return lookupService.get().lookup(coordinates);
-    }
+public class ScriptedLookupService extends BaseScriptedLookupService 
implements LookupService<Object> {
 
     @Override
+    @SuppressWarnings("unchecked")
     public Set<String> getRequiredKeys() {
         return lookupService.get().getRequiredKeys();
     }
@@ -96,315 +57,9 @@ public class ScriptedLookupService extends 
AbstractScriptedControllerService imp
     }
 
     @Override
-    protected void init(final ControllerServiceInitializationContext context) {
-        kerberosServicePrincipal = context.getKerberosServicePrincipal();
-        kerberosConfigFile = context.getKerberosConfigurationFile();
-        kerberosServiceKeytab = context.getKerberosServiceKeytab();
-    }
-
-    /**
-     * Returns a list of property descriptors supported by this processor. The
-     * list always includes properties such as script engine name, script file
-     * name, script body name, script arguments, and an external module path. 
If
-     * the scripted processor also defines supported properties, those are 
added
-     * to the list as well.
-     *
-     * @return a List of PropertyDescriptor objects supported by this processor
-     */
-    @Override
-    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
-
-        synchronized (scriptingComponentHelper.isInitialized) {
-            if (!scriptingComponentHelper.isInitialized.get()) {
-                scriptingComponentHelper.createResources();
-            }
-        }
-        List<PropertyDescriptor> supportedPropertyDescriptors = new 
ArrayList<>();
-        List<PropertyDescriptor> _temp = new ArrayList<>();
-        _temp.addAll(scriptingComponentHelper.getDescriptors());
-        _temp.remove(scriptingComponentHelper.SCRIPT_ENGINE);
-
-        PropertyDescriptor.Builder jythonLessEngineProp = new 
PropertyDescriptor
-            
.Builder().fromPropertyDescriptor(scriptingComponentHelper.SCRIPT_ENGINE);
-        List<AllowableValue> filtered = 
scriptingComponentHelper.getScriptEngineAllowableValues()
-            .stream().filter(allowableValue -> 
!allowableValue.getValue().contains("ython"))
-            .collect(Collectors.toList());
-        jythonLessEngineProp.allowableValues(filtered.toArray(new 
AllowableValue[filtered.size()]));
-
-        supportedPropertyDescriptors.add(jythonLessEngineProp.build());
-        supportedPropertyDescriptors.addAll(_temp);
-
-        final ConfigurableComponent instance = lookupService.get();
-        if (instance != null) {
-            try {
-                final List<PropertyDescriptor> instanceDescriptors = 
instance.getPropertyDescriptors();
-                if (instanceDescriptors != null) {
-                    supportedPropertyDescriptors.addAll(instanceDescriptors);
-                }
-            } catch (final Throwable t) {
-                final ComponentLog logger = getLogger();
-                final String message = "Unable to get property descriptors 
from Processor: " + t;
-
-                logger.error(message);
-                if (logger.isDebugEnabled()) {
-                    logger.error(message, t);
-                }
-            }
-        }
-
-        return Collections.unmodifiableList(supportedPropertyDescriptors);
-    }
-
-    /**
-     * Returns a PropertyDescriptor for the given name. This is for the user to
-     * be able to define their own properties which will be available as
-     * variables in the script
-     *
-     * @param propertyDescriptorName used to lookup if any property descriptors
-     *                               exist for that name
-     * @return a PropertyDescriptor object corresponding to the specified
-     * dynamic property name
-     */
-    @Override
-    protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final 
String propertyDescriptorName) {
-        return new PropertyDescriptor.Builder()
-                .name(propertyDescriptorName)
-                .required(false)
-                .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
-                
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
-                .dynamic(true)
-                .build();
-    }
-
-    /**
-     * Handles changes to this processor's properties. If changes are made to
-     * script- or engine-related properties, the script will be reloaded.
-     *
-     * @param descriptor of the modified property
-     * @param oldValue   non-null property value (previous)
-     * @param newValue   the new property value or if null indicates the 
property
-     */
-    @Override
-    public void onPropertyModified(final PropertyDescriptor descriptor, final 
String oldValue, final String newValue) {
-        final ComponentLog logger = getLogger();
-        final ConfigurableComponent instance = lookupService.get();
-
-        if (ScriptingComponentUtils.SCRIPT_FILE.equals(descriptor)
-                || ScriptingComponentUtils.SCRIPT_BODY.equals(descriptor)
-                || ScriptingComponentUtils.MODULES.equals(descriptor)
-                || scriptingComponentHelper.SCRIPT_ENGINE.equals(descriptor)) {
-            scriptNeedsReload.set(true);
-            // Need to reset scriptEngine if the value has changed
-            if (scriptingComponentHelper.SCRIPT_ENGINE.equals(descriptor)) {
-                scriptEngine = null;
-            }
-        } else if (instance != null) {
-            // If the script provides a ConfigurableComponent, call its 
onPropertyModified() method
-            try {
-                instance.onPropertyModified(descriptor, oldValue, newValue);
-            } catch (final Exception e) {
-                final String message = "Unable to invoke onPropertyModified 
from scripted LookupService: " + e;
-                logger.error(message, e);
-            }
-        }
-    }
-
-    @Override
-    @OnEnabled
-    public void onEnabled(final ConfigurationContext context) {
-        synchronized (scriptingComponentHelper.isInitialized) {
-            if (!scriptingComponentHelper.isInitialized.get()) {
-                scriptingComponentHelper.createResources();
-            }
-        }
-        super.onEnabled(context);
-
-        // Call an non-interface method onEnabled(context), to allow a 
scripted LookupService the chance to set up as necessary
-        final Invocable invocable = (Invocable) scriptEngine;
-        if (configurationContext != null) {
-            try {
-                // Get the actual object from the script engine, versus the 
proxy stored in lookupService. The object may have additional methods,
-                // where lookupService is a proxied interface
-                final Object obj = scriptEngine.get("lookupService");
-                if (obj != null) {
-                    try {
-                        invocable.invokeMethod(obj, "onEnabled", context);
-                    } catch (final NoSuchMethodException nsme) {
-                        if (getLogger().isDebugEnabled()) {
-                            getLogger().debug("Configured script LookupService 
does not contain an onEnabled() method.");
-                        }
-                    }
-                } else {
-                    throw new ScriptException("No LookupService was defined by 
the script.");
-                }
-            } catch (ScriptException se) {
-                throw new ProcessException("Error executing onEnabled(context) 
method", se);
-            }
-        }
-    }
-
-    @OnDisabled
-    public void onDisabled(final ConfigurationContext context) {
-        // Call an non-interface method onDisabled(context), to allow a 
scripted LookupService the chance to shut down as necessary
-        final Invocable invocable = (Invocable) scriptEngine;
-        if (configurationContext != null) {
-            try {
-                // Get the actual object from the script engine, versus the 
proxy stored in lookupService. The object may have additional methods,
-                // where lookupService is a proxied interface
-                final Object obj = scriptEngine.get("lookupService");
-                if (obj != null) {
-                    try {
-                        invocable.invokeMethod(obj, "onDisabled", context);
-                    } catch (final NoSuchMethodException nsme) {
-                        if (getLogger().isDebugEnabled()) {
-                            getLogger().debug("Configured script LookupService 
does not contain an onDisabled() method.");
-                        }
-                    }
-                } else {
-                    throw new ScriptException("No LookupService was defined by 
the script.");
-                }
-            } catch (ScriptException se) {
-                throw new ProcessException("Error executing 
onDisabled(context) method", se);
-            }
-        }
-    }
-
-    @Override
-    public void setup() {
-        // Create a single script engine, the Processor object is reused by 
each task
-        if (scriptEngine == null) {
-            scriptingComponentHelper.setup(1, getLogger());
-            scriptEngine = scriptingComponentHelper.engineQ.poll();
-        }
-
-        if (scriptEngine == null) {
-            throw new ProcessException("No script engine available!");
-        }
-
-        if (scriptNeedsReload.get() || lookupService.get() == null) {
-            if 
(ScriptingComponentHelper.isFile(scriptingComponentHelper.getScriptPath())) {
-                reloadScriptFile(scriptingComponentHelper.getScriptPath());
-            } else {
-                reloadScriptBody(scriptingComponentHelper.getScriptBody());
-            }
-            scriptNeedsReload.set(false);
-        }
-    }
-
-    /**
-     * Reloads the script RecordReaderFactory. This must be called within the 
lock.
-     *
-     * @param scriptBody An input stream associated with the script content
-     * @return Whether the script was successfully reloaded
-     */
-    @Override
-    protected boolean reloadScript(final String scriptBody) {
-        // note we are starting here with a fresh listing of validation
-        // results since we are (re)loading a new/updated script. any
-        // existing validation results are not relevant
-        final Collection<ValidationResult> results = new HashSet<>();
-
-        try {
-            // get the engine and ensure its invocable
-            if (scriptEngine instanceof Invocable) {
-                final Invocable invocable = (Invocable) scriptEngine;
-
-                // Find a custom configurator and invoke their eval() method
-                ScriptEngineConfigurator configurator = 
scriptingComponentHelper.scriptEngineConfiguratorMap.get(scriptingComponentHelper.getScriptEngineName().toLowerCase());
-                if (configurator != null) {
-                    configurator.eval(scriptEngine, scriptBody, 
scriptingComponentHelper.getModules());
-                } else {
-                    // evaluate the script
-                    scriptEngine.eval(scriptBody);
-                }
-
-                // get configured LookupService from the script (if it exists)
-                final Object obj = scriptEngine.get("lookupService");
-                if (obj != null) {
-                    final ComponentLog logger = getLogger();
-
-                    try {
-                        // set the logger if the processor wants it
-                        invocable.invokeMethod(obj, "setLogger", logger);
-                    } catch (final NoSuchMethodException nsme) {
-                        if (logger.isDebugEnabled()) {
-                            logger.debug("Scripted LookupService does not 
contain a setLogger method.");
-                        }
-                    }
-
-                    // record the processor for use later
-                    final LookupService<Object> scriptedLookupService = 
invocable.getInterface(obj, LookupService.class);
-                    lookupService.set(scriptedLookupService);
-
-                    if (scriptedLookupService != null) {
-                        try {
-                            scriptedLookupService.initialize(new 
ControllerServiceInitializationContext() {
-                                @Override
-                                public String getIdentifier() {
-                                    return 
ScriptedLookupService.this.getIdentifier();
-                                }
-
-                                @Override
-                                public ComponentLog getLogger() {
-                                    return logger;
-                                }
-
-                                @Override
-                                public StateManager getStateManager() {
-                                    return 
ScriptedLookupService.this.getStateManager();
-                                }
-
-                                @Override
-                                public ControllerServiceLookup 
getControllerServiceLookup() {
-                                    return 
ScriptedLookupService.super.getControllerServiceLookup();
-                                }
-
-                                @Override
-                                public String getKerberosServicePrincipal() {
-                                    return 
ScriptedLookupService.this.kerberosServicePrincipal;
-                                }
-
-                                @Override
-                                public File getKerberosServiceKeytab() {
-                                    return 
ScriptedLookupService.this.kerberosServiceKeytab;
-                                }
-
-                                @Override
-                                public File getKerberosConfigurationFile() {
-                                    return 
ScriptedLookupService.this.kerberosConfigFile;
-                                }
-                            });
-                        } catch (final Exception e) {
-                            logger.error("Unable to initialize scripted 
LookupService: " + e.getLocalizedMessage(), e);
-                            throw new ProcessException(e);
-                        }
-                    }
-
-                } else {
-                    throw new ScriptException("No LookupService was defined by 
the script.");
-                }
-            } else {
-                throw new ScriptException("Script engine is not Invocable, 
cannot be used for ScriptedLookupService");
-            }
-
-        } catch (final Exception ex) {
-            final ComponentLog logger = getLogger();
-            final String message = "Unable to load script: " + 
ex.getLocalizedMessage();
-
-            logger.error(message, ex);
-            results.add(new ValidationResult.Builder()
-                    .subject("ScriptedLookupServiceValidation")
-                    .valid(false)
-                    .explanation("Unable to load script due to " + 
ex.getLocalizedMessage())
-                    .input(scriptingComponentHelper.getScriptPath())
-                    .build());
-        }
-
-        // store the updated validation results
-        validationResults.set(results);
-
-        // return whether there was any issues loading the configured script
-        return results.isEmpty();
+    @SuppressWarnings("unchecked")
+    public Optional<Object> lookup(Map<String, Object> coordinates) throws 
LookupFailureException {
+        // Delegate the lookup() call to the scripted LookupService
+        return lookupService.get().lookup(coordinates);
     }
-
 }
diff --git 
a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/lookup/script/SimpleScriptedLookupService.java
 
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/lookup/script/SimpleScriptedLookupService.java
new file mode 100644
index 0000000..d4636db
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/lookup/script/SimpleScriptedLookupService.java
@@ -0,0 +1,61 @@
+/*
+ * 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.nifi.lookup.script;
+
+import org.apache.nifi.annotation.behavior.Restricted;
+import org.apache.nifi.annotation.behavior.Restriction;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.components.RequiredPermission;
+import org.apache.nifi.lookup.LookupFailureException;
+import org.apache.nifi.lookup.StringLookupService;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * A Controller service that allows the user to script the lookup operation to 
be performed (by LookupAttribute, e.g.)
+ */
+@Tags({"lookup", "script", "invoke", "groovy", "python", "jython", "jruby", 
"ruby", "javascript", "js", "lua", "luaj"})
+@CapabilityDescription("Allows the user to provide a scripted LookupService 
instance in order to enrich records from "
+        + "an incoming flow file. The script is expected to return an optional 
string value rather than an arbitrary object (record, e.g.). "
+        + "Also the scripted lookup service should implement 
StringLookupService, otherwise the getValueType() method must be implemented 
even "
+        + "though it will be ignored, as SimpleScriptedLookupService returns 
String as the value type on the script's behalf. Please note that due to "
+        + "a bug in Jython that remains unresolved, it is not possible to use 
Jython to write a script for this service in Python.")
+@Restricted(
+        restrictions = {
+                @Restriction(
+                        requiredPermission = RequiredPermission.EXECUTE_CODE,
+                        explanation = "Provides operator the ability to 
execute arbitrary code assuming all permissions that NiFi has.")
+        }
+)
+public class SimpleScriptedLookupService extends BaseScriptedLookupService 
implements StringLookupService {
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Set<String> getRequiredKeys() {
+        return lookupService.get().getRequiredKeys();
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Optional<String> lookup(Map<String, Object> coordinates) throws 
LookupFailureException {
+        // Delegate the lookup() call to the scripted LookupService
+        return lookupService.get().lookup(coordinates);
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService
 
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService
index a49f837..751e5ba 100644
--- 
a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService
+++ 
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService
@@ -15,4 +15,5 @@
 
 org.apache.nifi.record.script.ScriptedReader
 org.apache.nifi.record.script.ScriptedRecordSetWriter
-org.apache.nifi.lookup.script.ScriptedLookupService
\ No newline at end of file
+org.apache.nifi.lookup.script.ScriptedLookupService
+org.apache.nifi.lookup.script.SimpleScriptedLookupService
\ No newline at end of file
diff --git 
a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/groovy/org/apache/nifi/lookup/script/TestSimpleScriptedLookupService.groovy
 
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/groovy/org/apache/nifi/lookup/script/TestSimpleScriptedLookupService.groovy
new file mode 100644
index 0000000..01b2b2d
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/groovy/org/apache/nifi/lookup/script/TestSimpleScriptedLookupService.groovy
@@ -0,0 +1,112 @@
+/*
+ * 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.nifi.lookup.script
+
+import org.apache.commons.io.FileUtils
+import org.apache.nifi.components.PropertyDescriptor
+import org.apache.nifi.controller.ConfigurationContext
+import org.apache.nifi.controller.ControllerServiceInitializationContext
+import org.apache.nifi.logging.ComponentLog
+import org.apache.nifi.processors.script.AccessibleScriptingComponentHelper
+import org.apache.nifi.script.ScriptingComponentHelper
+import org.apache.nifi.script.ScriptingComponentUtils
+import org.apache.nifi.util.MockFlowFile
+import org.apache.nifi.util.MockPropertyValue
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Test
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+import static junit.framework.TestCase.assertEquals
+import static org.junit.Assert.assertFalse
+import static org.junit.Assert.assertTrue
+import static org.mockito.Mockito.mock
+import static org.mockito.Mockito.when
+
+/**
+ * Unit tests for the SimpleScriptedLookupService controller service
+ */
+class TestSimpleScriptedLookupService {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(TestSimpleScriptedLookupService)
+    SimpleScriptedLookupService scriptedLookupService
+    def scriptingComponent
+
+
+    @BeforeClass
+    static void setUpOnce() throws Exception {
+        logger.metaClass.methodMissing = {String name, args ->
+            logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
+        }
+        FileUtils.copyDirectory('src/test/resources' as File, 
'target/test/resources' as File)
+    }
+
+    @Before
+    void setUp() {
+        scriptedLookupService = new MockScriptedLookupService()
+        scriptingComponent = (AccessibleScriptingComponentHelper) 
scriptedLookupService
+    }
+
+    @Test
+    void testSimpleLookupServiceGroovyScript() {
+
+        def properties = [:] as Map<PropertyDescriptor, String>
+        scriptedLookupService.getSupportedPropertyDescriptors().each 
{PropertyDescriptor descriptor ->
+            properties.put(descriptor, descriptor.getDefaultValue())
+        }
+
+        // Mock the ConfigurationContext for setup(...)
+        def configurationContext = mock(ConfigurationContext)
+        
when(configurationContext.getProperty(scriptingComponent.getScriptingComponentHelper().SCRIPT_ENGINE))
+                .thenReturn(new MockPropertyValue('Groovy'))
+        
when(configurationContext.getProperty(ScriptingComponentUtils.SCRIPT_FILE))
+                .thenReturn(new 
MockPropertyValue('target/test/resources/groovy/test_simple_lookup_inline.groovy'))
+        
when(configurationContext.getProperty(ScriptingComponentUtils.SCRIPT_BODY))
+                .thenReturn(new MockPropertyValue(null))
+        when(configurationContext.getProperty(ScriptingComponentUtils.MODULES))
+                .thenReturn(new MockPropertyValue(null))
+
+        def logger = mock(ComponentLog)
+        def initContext = mock(ControllerServiceInitializationContext)
+        
when(initContext.getIdentifier()).thenReturn(UUID.randomUUID().toString())
+        when(initContext.getLogger()).thenReturn(logger)
+
+        scriptedLookupService.initialize initContext
+        scriptedLookupService.onEnabled configurationContext
+
+        def mockFlowFile = new MockFlowFile(1L)
+        def inStream = new ByteArrayInputStream('Flow file content not 
used'.bytes)
+
+        def opt = scriptedLookupService.lookup(['key':'Hello'])
+        assertTrue(opt.present)
+        assertEquals('Hi', opt.get())
+        opt = scriptedLookupService.lookup(['key':'World'])
+        assertTrue(opt.present)
+        assertEquals('there', opt.get())
+        opt = scriptedLookupService.lookup(['key':'Not There'])
+        assertFalse(opt.present)
+    }
+
+    class MockScriptedLookupService extends SimpleScriptedLookupService 
implements AccessibleScriptingComponentHelper {
+
+        @Override
+        ScriptingComponentHelper getScriptingComponentHelper() {
+            return this.@scriptingComponentHelper
+        }
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/resources/groovy/test_simple_lookup_inline.groovy
 
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/resources/groovy/test_simple_lookup_inline.groovy
new file mode 100644
index 0000000..ca10baa
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/resources/groovy/test_simple_lookup_inline.groovy
@@ -0,0 +1,73 @@
+/*
+ * 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 org.apache.nifi.lookup.StringLookupService
+
+import org.apache.nifi.controller.ControllerServiceInitializationContext
+import org.apache.nifi.reporting.InitializationException
+
+
+class SimpleGroovyLookupService implements StringLookupService {
+
+    def lookupTable = [
+            'Hello': 'Hi',
+            'World': 'there'
+    ]
+
+
+    @Override
+    Optional<String> lookup(Map<String, String> coordinates) {
+        final String key = coordinates.values().iterator().next()
+        Optional.ofNullable(lookupTable[key])
+    }
+    
+    Set<String> getRequiredKeys() {
+        return ['key'] as Set<String>
+    }
+
+    @Override
+    void initialize(ControllerServiceInitializationContext context) throws 
InitializationException {
+
+    }
+
+    @Override
+    Collection<ValidationResult> validate(ValidationContext context) {
+        return null
+    }
+
+    @Override
+    PropertyDescriptor getPropertyDescriptor(String name) {
+        return null
+    }
+
+    @Override
+    void onPropertyModified(PropertyDescriptor descriptor, String oldValue, 
String newValue) {
+
+    }
+
+    @Override
+    List<PropertyDescriptor> getPropertyDescriptors() {
+        return null
+    }
+
+    @Override
+    String getIdentifier() {
+        return null
+    }
+}
+
+lookupService = new SimpleGroovyLookupService()
\ No newline at end of file

Reply via email to