You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ma...@apache.org on 2015/12/04 22:39:29 UTC

[2/3] nifi git commit: NIFI-1249: Allow Processors to add their own variables to those referencable by Expression Language. Make ReplaceText allow users to reference back-references of regex matches

NIFI-1249: Allow Processors to add their own variables to those referencable by Expression Language. Make ReplaceText allow users to reference back-references of regex matches


Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/f378ee90
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/f378ee90
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/f378ee90

Branch: refs/heads/master
Commit: f378ee902127bd29b168c9bb15e991abe4eab0fa
Parents: dce039b
Author: Mark Payne <ma...@hotmail.com>
Authored: Fri Dec 4 13:17:37 2015 -0500
Committer: Mark Payne <ma...@hotmail.com>
Committed: Fri Dec 4 13:17:37 2015 -0500

----------------------------------------------------------------------
 .../apache/nifi/components/PropertyValue.java   |  99 ++++-
 .../apache/nifi/processor/ProcessContext.java   |   7 +
 .../bootstrap/NotificationServiceManager.java   |   5 +-
 .../NotificationServicePropertyValue.java       | 120 ------
 .../NotificationValidationContext.java          |   4 +-
 nifi-commons/nifi-expression-language/pom.xml   |   4 +
 .../expression/language/EmptyPreparedQuery.java |   5 +
 .../language/InvalidPreparedQuery.java          |   4 +
 .../expression/language/PreparedQuery.java      |   1 +
 .../attribute/expression/language/Query.java    |  14 +-
 .../language/StandardPreparedQuery.java         |   6 +
 .../language/StandardPropertyValue.java         | 177 +++++++++
 .../apache/nifi/util/MockProcessContext.java    |  11 +
 .../org/apache/nifi/util/MockPropertyValue.java |  71 ++--
 .../nifi/util/StandardProcessorTestRunner.java  |  12 +
 .../java/org/apache/nifi/util/TestRunner.java   |  19 +-
 .../documentation/mock/MockProcessContext.java  |   5 +-
 .../manager/impl/ClusteredReportingContext.java |   2 +-
 .../reporting/StandardReportingContext.java     |   2 +-
 .../scheduling/ConnectableProcessContext.java   |  28 +-
 .../service/StandardConfigurationContext.java   |   2 +-
 .../nifi/processor/StandardProcessContext.java  |  12 +
 .../nifi/processor/StandardPropertyValue.java   | 157 --------
 .../processor/StandardSchedulingContext.java    |  13 +
 .../processor/StandardValidationContext.java    |   1 +
 .../processor/TestStandardPropertyValue.java    |   3 +-
 .../web/controller/StandardSearchContext.java   |   2 +-
 .../nifi/processors/standard/ReplaceText.java   | 380 ++++++++++++-------
 .../processors/standard/TestReplaceText.java    | 103 ++++-
 .../TestReplaceTextLineByLine/Good.txt          |   1 -
 30 files changed, 801 insertions(+), 469 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-api/src/main/java/org/apache/nifi/components/PropertyValue.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/components/PropertyValue.java b/nifi-api/src/main/java/org/apache/nifi/components/PropertyValue.java
index 1845ed2..72dcc2a 100644
--- a/nifi-api/src/main/java/org/apache/nifi/components/PropertyValue.java
+++ b/nifi-api/src/main/java/org/apache/nifi/components/PropertyValue.java
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.components;
 
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.nifi.controller.ControllerService;
@@ -120,7 +121,7 @@ public interface PropertyValue {
 
     /**
      * <p>
-     * Replaces values in the Property Value using the Attribute Expression
+     * Replaces values in the Property Value using the NiFi Expression
      * Language; a PropertyValue with the new value is then returned, supporting
      * call chaining.
      * </p>
@@ -128,14 +129,49 @@ public interface PropertyValue {
      * @return a PropertyValue with the new value is returned, supporting call
      * chaining
      *
-     * @throws ProcessException if the Query cannot be compiled or evaluating
-     * the query against the given attributes causes an Exception to be thrown
+     * @throws ProcessException if the Expression cannot be compiled or evaluating
+     * the Expression against the given attributes causes an Exception to be thrown
      */
     public PropertyValue evaluateAttributeExpressions() throws ProcessException;
 
     /**
      * <p>
-     * Replaces values in the Property Value using the Attribute Expression
+     * Replaces values in the Property Value using the NiFi Expression Language;
+     * a PropertyValue with the new value is then returned, supporting call chaining.
+     * </p>
+     *
+     * @param attributes a Map of attributes that the Expression can reference, in addition
+     * to JVM System Properties and Environmental Properties.
+     *
+     * @return a PropertyValue with the new value
+     *
+     * @throws ProcessException if the Expression cannot be compiled or evaluating the Expression against
+     * the given attributes causes an Exception to be thrown
+     */
+    public PropertyValue evaluateAttributeExpressions(Map<String, String> attributes) throws ProcessException;
+
+    /**
+     * <p>
+     * Replaces values in the Property Value using the NiFi Expression Language.
+     * The supplied decorator is then given a chance to decorate the
+     * value, and a PropertyValue with the new value is then returned,
+     * supporting call chaining.
+     * </p>
+     *
+     * @param attributes a Map of attributes that the Expression can reference, in addition
+     * to JVM System Properties and Environmental Properties.
+     * @param decorator the decorator to use in order to update the values returned by the Expression Language
+     *
+     * @return a PropertyValue with the new value
+     *
+     * @throws ProcessException if the Expression cannot be compiled or evaluating the Expression against
+     * the given attributes causes an Exception to be thrown
+     */
+    public PropertyValue evaluateAttributeExpressions(Map<String, String> attributes, AttributeValueDecorator decorator) throws ProcessException;
+
+    /**
+     * <p>
+     * Replaces values in the Property Value using the NiFi Expression
      * Language; a PropertyValue with the new value is then returned, supporting
      * call chaining.
      * </p>
@@ -144,14 +180,53 @@ public interface PropertyValue {
      * @return a PropertyValue with the new value is returned, supporting call
      * chaining
      *
-     * @throws ProcessException if the Query cannot be compiled or evaluating
-     * the query against the given attributes causes an Exception to be thrown
+     * @throws ProcessException if the Expression cannot be compiled or evaluating
+     * the Expression against the given attributes causes an Exception to be thrown
      */
     public PropertyValue evaluateAttributeExpressions(FlowFile flowFile) throws ProcessException;
 
     /**
      * <p>
-     * Replaces values in the Property Value using the Attribute Expression
+     * Replaces values in the Property Value using the NiFi Expression
+     * Language; a PropertyValue with the new value is then returned, supporting
+     * call chaining.
+     * </p>
+     *
+     * @param flowFile to evaluate attributes of
+     * @param additionalAttributes a Map of additional attributes that the Expression can reference. If entries in
+     * this Map conflict with entries in the FlowFile's attributes, the entries in this Map are given a higher priority.
+     *
+     * @return a PropertyValue with the new value is returned, supporting call
+     * chaining
+     *
+     * @throws ProcessException if the Expression cannot be compiled or evaluating
+     * the Expression against the given attributes causes an Exception to be thrown
+     */
+    public PropertyValue evaluateAttributeExpressions(FlowFile flowFile, Map<String, String> additionalAttributes) throws ProcessException;
+
+    /**
+     * <p>
+     * Replaces values in the Property Value using the NiFi Expression
+     * Language; a PropertyValue with the new value is then returned, supporting
+     * call chaining.
+     * </p>
+     *
+     * @param flowFile to evaluate attributes of
+     * @param additionalAttributes a Map of additional attributes that the Expression can reference. If entries in
+     * this Map conflict with entries in the FlowFile's attributes, the entries in this Map are given a higher priority.
+     * @param decorator the decorator to use in order to update the values returned by the Expression Language
+     *
+     * @return a PropertyValue with the new value is returned, supporting call
+     * chaining
+     *
+     * @throws ProcessException if the Expression cannot be compiled or evaluating
+     * the Expression against the given attributes causes an Exception to be thrown
+     */
+    public PropertyValue evaluateAttributeExpressions(FlowFile flowFile, Map<String, String> additionalAttributes, AttributeValueDecorator decorator) throws ProcessException;
+
+    /**
+     * <p>
+     * Replaces values in the Property Value using the NiFi Expression
      * Language. The supplied decorator is then given a chance to decorate the
      * value, and a PropertyValue with the new value is then returned,
      * supporting call chaining.
@@ -162,14 +237,14 @@ public interface PropertyValue {
      * @return a PropertyValue with the new value is then returned, supporting
      * call chaining
      *
-     * @throws ProcessException if the Query cannot be compiled or evaluating
-     * the query against the given attributes causes an Exception to be thrown
+     * @throws ProcessException if the Expression cannot be compiled or evaluating
+     * the Expression against the given attributes causes an Exception to be thrown
      */
     public PropertyValue evaluateAttributeExpressions(AttributeValueDecorator decorator) throws ProcessException;
 
     /**
      * <p>
-     * Replaces values in the Property Value using the Attribute Expression
+     * Replaces values in the Property Value using the NiFi Expression
      * Language. The supplied decorator is then given a chance to decorate the
      * value, and a PropertyValue with the new value is then returned,
      * supporting call chaining.
@@ -182,8 +257,8 @@ public interface PropertyValue {
      * @return a PropertyValue with the new value is then returned, supporting
      * call chaining
      *
-     * @throws ProcessException if the Query cannot be compiled or evaluating
-     * the query against the given attributes causes an Exception to be thrown
+     * @throws ProcessException if the Expression cannot be compiled or evaluating
+     * the Expression against the given attributes causes an Exception to be thrown
      */
     public PropertyValue evaluateAttributeExpressions(FlowFile flowFile, AttributeValueDecorator decorator) throws ProcessException;
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-api/src/main/java/org/apache/nifi/processor/ProcessContext.java
----------------------------------------------------------------------
diff --git a/nifi-api/src/main/java/org/apache/nifi/processor/ProcessContext.java b/nifi-api/src/main/java/org/apache/nifi/processor/ProcessContext.java
index 7488b2d..cf1bb6c 100644
--- a/nifi-api/src/main/java/org/apache/nifi/processor/ProcessContext.java
+++ b/nifi-api/src/main/java/org/apache/nifi/processor/ProcessContext.java
@@ -147,4 +147,11 @@ public interface ProcessContext {
      */
     boolean hasConnection(Relationship relationship);
 
+    /**
+     * @param property the Property whose value should be inspected to determined if it contains an Expression Language Expression
+     * @return <code>true</code> if the value of the given Property contains a NiFi Expression
+     * Language Expression, <code>false</code> if it does not. Note that <code>false</code> will be returned if the Property Descriptor
+     * does not allow the Expression Language, even if a seemingly valid Expression is present in the value.
+     */
+    boolean isExpressionLanguagePresent(PropertyDescriptor property);
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServiceManager.java
----------------------------------------------------------------------
diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServiceManager.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServiceManager.java
index 2fda022..21d8e82 100644
--- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServiceManager.java
+++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServiceManager.java
@@ -36,6 +36,7 @@ import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
+import org.apache.nifi.attribute.expression.language.StandardPropertyValue;
 import org.apache.nifi.bootstrap.notification.NotificationContext;
 import org.apache.nifi.bootstrap.notification.NotificationInitializationContext;
 import org.apache.nifi.bootstrap.notification.NotificationService;
@@ -246,7 +247,7 @@ public class NotificationServiceManager {
                     configuredValue = fullPropDescriptor.getDefaultValue();
                 }
 
-                return new NotificationServicePropertyValue(configuredValue);
+                return new StandardPropertyValue(configuredValue, null);
             }
 
             @Override
@@ -363,7 +364,7 @@ public class NotificationServiceManager {
                         value = descriptor.getDefaultValue();
                     }
 
-                    return new NotificationServicePropertyValue(value);
+                    return new StandardPropertyValue(value, null);
                 }
 
                 @Override

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServicePropertyValue.java
----------------------------------------------------------------------
diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServicePropertyValue.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServicePropertyValue.java
deleted file mode 100644
index 582b342..0000000
--- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServicePropertyValue.java
+++ /dev/null
@@ -1,120 +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.nifi.bootstrap;
-
-import java.util.concurrent.TimeUnit;
-
-import org.apache.nifi.attribute.expression.language.PreparedQuery;
-import org.apache.nifi.attribute.expression.language.Query;
-import org.apache.nifi.components.PropertyValue;
-import org.apache.nifi.controller.ControllerService;
-import org.apache.nifi.expression.AttributeValueDecorator;
-import org.apache.nifi.flowfile.FlowFile;
-import org.apache.nifi.processor.DataUnit;
-import org.apache.nifi.processor.exception.ProcessException;
-import org.apache.nifi.util.FormatUtils;
-
-public class NotificationServicePropertyValue implements PropertyValue {
-    private final String rawValue;
-    private final PreparedQuery preparedQuery;
-
-    public NotificationServicePropertyValue(final String rawValue) {
-        this.rawValue = rawValue;
-        this.preparedQuery = Query.prepare(rawValue);
-    }
-
-    @Override
-    public String getValue() {
-        return rawValue;
-    }
-
-    @Override
-    public Integer asInteger() {
-        return (rawValue == null) ? null : Integer.parseInt(rawValue.trim());
-    }
-
-    @Override
-    public Long asLong() {
-        return (rawValue == null) ? null : Long.parseLong(rawValue.trim());
-    }
-
-    @Override
-    public Boolean asBoolean() {
-        return (rawValue == null) ? null : Boolean.parseBoolean(rawValue.trim());
-    }
-
-    @Override
-    public Float asFloat() {
-        return (rawValue == null) ? null : Float.parseFloat(rawValue.trim());
-    }
-
-    @Override
-    public Double asDouble() {
-        return (rawValue == null) ? null : Double.parseDouble(rawValue.trim());
-    }
-
-    @Override
-    public Long asTimePeriod(final TimeUnit timeUnit) {
-        return (rawValue == null) ? null : FormatUtils.getTimeDuration(rawValue.trim(), timeUnit);
-    }
-
-    @Override
-    public Double asDataSize(final DataUnit dataUnit) {
-        return rawValue == null ? null : DataUnit.parseDataSize(rawValue.trim(), dataUnit);
-    }
-
-    @Override
-    public PropertyValue evaluateAttributeExpressions() throws ProcessException {
-        return evaluateAttributeExpressions((AttributeValueDecorator) null);
-    }
-
-    @Override
-    public PropertyValue evaluateAttributeExpressions(final AttributeValueDecorator decorator) throws ProcessException {
-        return new NotificationServicePropertyValue(preparedQuery.evaluateExpressions((FlowFile) null, decorator));
-    }
-
-    @Override
-    public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile) throws ProcessException {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile, final AttributeValueDecorator decorator) throws ProcessException {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public String toString() {
-        return rawValue;
-    }
-
-    @Override
-    public ControllerService asControllerService() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public <T extends ControllerService> T asControllerService(final Class<T> serviceType) throws IllegalArgumentException {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean isSet() {
-        return rawValue != null;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationValidationContext.java
----------------------------------------------------------------------
diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationValidationContext.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationValidationContext.java
index a02b108..49afc16 100644
--- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationValidationContext.java
+++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationValidationContext.java
@@ -23,7 +23,7 @@ import java.util.Map;
 import org.apache.nifi.attribute.expression.language.Query;
 import org.apache.nifi.attribute.expression.language.Query.Range;
 import org.apache.nifi.attribute.expression.language.StandardExpressionLanguageCompiler;
-import org.apache.nifi.bootstrap.NotificationServicePropertyValue;
+import org.apache.nifi.attribute.expression.language.StandardPropertyValue;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.PropertyValue;
 import org.apache.nifi.components.ValidationContext;
@@ -48,7 +48,7 @@ public class NotificationValidationContext implements ValidationContext {
 
     @Override
     public PropertyValue newPropertyValue(final String rawValue) {
-        return new NotificationServicePropertyValue(rawValue);
+        return new StandardPropertyValue(rawValue, null);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-commons/nifi-expression-language/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-expression-language/pom.xml b/nifi-commons/nifi-expression-language/pom.xml
index 2dc8839..fc3a4a9 100644
--- a/nifi-commons/nifi-expression-language/pom.xml
+++ b/nifi-commons/nifi-expression-language/pom.xml
@@ -52,5 +52,9 @@
             <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+        </dependency>
     </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/EmptyPreparedQuery.java
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/EmptyPreparedQuery.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/EmptyPreparedQuery.java
index 81da47e..d85c9ef 100644
--- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/EmptyPreparedQuery.java
+++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/EmptyPreparedQuery.java
@@ -59,4 +59,9 @@ public class EmptyPreparedQuery implements PreparedQuery {
     public String evaluateExpressions(Map<String, String> attributes, AttributeValueDecorator decorator) throws ProcessException {
         return value;
     }
+
+    @Override
+    public String evaluateExpressions(FlowFile flowFile, Map<String, String> additionalAttributes, AttributeValueDecorator decorator) throws ProcessException {
+        return value;
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/InvalidPreparedQuery.java
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/InvalidPreparedQuery.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/InvalidPreparedQuery.java
index a29e792..aa2428d 100644
--- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/InvalidPreparedQuery.java
+++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/InvalidPreparedQuery.java
@@ -69,4 +69,8 @@ public class InvalidPreparedQuery implements PreparedQuery {
         throw new AttributeExpressionLanguageException("Invalid Expression: " + query + " due to " + explanation);
     }
 
+    @Override
+    public String evaluateExpressions(FlowFile flowFile, Map<String, String> additionalAttributes, AttributeValueDecorator decorator) throws ProcessException {
+        throw new AttributeExpressionLanguageException("Invalid Expression: " + query + " due to " + explanation);
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/PreparedQuery.java
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/PreparedQuery.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/PreparedQuery.java
index 0d1b2c7..ad9225d 100644
--- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/PreparedQuery.java
+++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/PreparedQuery.java
@@ -36,4 +36,5 @@ public interface PreparedQuery {
 
     String evaluateExpressions(Map<String, String> attributes, AttributeValueDecorator decorator) throws ProcessException;
 
+    String evaluateExpressions(FlowFile flowFile, Map<String, String> additionalAttributes, AttributeValueDecorator decorator) throws ProcessException;
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/Query.java
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/Query.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/Query.java
index 496d80c..2c27e4d 100644
--- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/Query.java
+++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/Query.java
@@ -19,6 +19,7 @@ package org.apache.nifi.attribute.expression.language;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -416,7 +417,12 @@ public class Query {
     }
 
     static Map<String, String> createExpressionMap(final FlowFile flowFile) {
-        final Map<String, String> attributeMap = flowFile == null ? new HashMap<String, String>() : flowFile.getAttributes();
+        return createExpressionMap(flowFile, null);
+    }
+
+    static Map<String, String> createExpressionMap(final FlowFile flowFile, final Map<String, String> additionalAttributes) {
+        final Map<String, String> attributeMap = flowFile == null ? Collections.<String, String> emptyMap() : flowFile.getAttributes();
+        final Map<String, String> additionalOrEmpty = additionalAttributes == null ? Collections.<String, String> emptyMap() : additionalAttributes;
         final Map<String, String> envMap = System.getenv();
         final Map<?, ?> sysProps = System.getProperties();
 
@@ -428,13 +434,13 @@ public class Query {
             flowFileProps.put("lineageStartDate", String.valueOf(flowFile.getLineageStartDate()));
         }
 
-        return wrap(attributeMap, flowFileProps, envMap, sysProps);
+        return wrap(additionalOrEmpty, attributeMap, flowFileProps, envMap, sysProps);
     }
 
-    private static Map<String, String> wrap(final Map<String, String> attributes, final Map<String, String> flowFileProps,
+    private static Map<String, String> wrap(final Map<String, String> additional, final Map<String, String> attributes, final Map<String, String> flowFileProps,
         final Map<String, String> env, final Map<?, ?> sysProps) {
         @SuppressWarnings("rawtypes")
-        final Map[] maps = new Map[]{attributes, flowFileProps, env, sysProps};
+        final Map[] maps = new Map[] {additional, attributes, flowFileProps, env, sysProps};
 
         return new Map<String, String>() {
             @Override

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPreparedQuery.java
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPreparedQuery.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPreparedQuery.java
index 0affb7f..b81a583 100644
--- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPreparedQuery.java
+++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPreparedQuery.java
@@ -60,6 +60,12 @@ public class StandardPreparedQuery implements PreparedQuery {
     }
 
     @Override
+    public String evaluateExpressions(final FlowFile flowFile, final Map<String, String> additionalAttributes, final AttributeValueDecorator decorator) throws ProcessException {
+        final Map<String, String> expressionMap = Query.createExpressionMap(flowFile, additionalAttributes);
+        return evaluateExpressions(expressionMap, decorator);
+    }
+
+    @Override
     public String evaluateExpressions(final FlowFile flowFile, final AttributeValueDecorator decorator) throws ProcessException {
         final Map<String, String> expressionMap = Query.createExpressionMap(flowFile);
         return evaluateExpressions(expressionMap, decorator);

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPropertyValue.java
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPropertyValue.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPropertyValue.java
new file mode 100644
index 0000000..a5c336a
--- /dev/null
+++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPropertyValue.java
@@ -0,0 +1,177 @@
+/*
+ * 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.attribute.expression.language;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.expression.AttributeValueDecorator;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.DataUnit;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.util.FormatUtils;
+
+public class StandardPropertyValue implements PropertyValue {
+
+    private final String rawValue;
+    private final ControllerServiceLookup serviceLookup;
+    private final PreparedQuery preparedQuery;
+
+    public StandardPropertyValue(final String rawValue, final ControllerServiceLookup serviceLookup) {
+        this(rawValue, serviceLookup, Query.prepare(rawValue));
+    }
+
+    /**
+     * Constructs a new StandardPropertyValue with the given value & service lookup and indicates whether or not the rawValue contains any NiFi Expressions. If it is unknown whether or not the value
+     * contains any NiFi Expressions, the {@link #StandardPropertyValue(String, ControllerServiceLookup)} constructor should be used or <code>true</code> should be passed. However, if it is known that
+     * the value contains no NiFi Expression, that information should be provided so that calls to {@link #evaluateAttributeExpressions()} are much more efficient
+     *
+     * @param rawValue value
+     * @param serviceLookup lookup
+     * @param preparedQuery query
+     */
+    public StandardPropertyValue(final String rawValue, final ControllerServiceLookup serviceLookup, final PreparedQuery preparedQuery) {
+        this.rawValue = rawValue;
+        this.serviceLookup = serviceLookup;
+        this.preparedQuery = preparedQuery;
+    }
+
+    @Override
+    public String getValue() {
+        return rawValue;
+    }
+
+    @Override
+    public Integer asInteger() {
+        return (rawValue == null) ? null : Integer.parseInt(rawValue.trim());
+    }
+
+    @Override
+    public Long asLong() {
+        return (rawValue == null) ? null : Long.parseLong(rawValue.trim());
+    }
+
+    @Override
+    public Boolean asBoolean() {
+        return (rawValue == null) ? null : Boolean.parseBoolean(rawValue.trim());
+    }
+
+    @Override
+    public Float asFloat() {
+        return (rawValue == null) ? null : Float.parseFloat(rawValue.trim());
+    }
+
+    @Override
+    public Double asDouble() {
+        return (rawValue == null) ? null : Double.parseDouble(rawValue.trim());
+    }
+
+    @Override
+    public Long asTimePeriod(final TimeUnit timeUnit) {
+        return (rawValue == null) ? null : FormatUtils.getTimeDuration(rawValue.trim(), timeUnit);
+    }
+
+    @Override
+    public Double asDataSize(final DataUnit dataUnit) {
+        return rawValue == null ? null : DataUnit.parseDataSize(rawValue.trim(), dataUnit);
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions() throws ProcessException {
+        return evaluateAttributeExpressions(null, null, null);
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions(final Map<String, String> attributes) throws ProcessException {
+        return evaluateAttributeExpressions(null, attributes, null);
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions(final Map<String, String> attributes, final AttributeValueDecorator decorator) throws ProcessException {
+        return evaluateAttributeExpressions(null, attributes, decorator);
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions(final AttributeValueDecorator decorator) throws ProcessException {
+        return evaluateAttributeExpressions(null, null, decorator);
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile) throws ProcessException {
+        return evaluateAttributeExpressions(flowFile, null, null);
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile, final Map<String, String> additionalAttributes) throws ProcessException {
+        return evaluateAttributeExpressions(flowFile, additionalAttributes, null);
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile, final AttributeValueDecorator decorator) throws ProcessException {
+        return evaluateAttributeExpressions(flowFile, null, decorator);
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile, final Map<String, String> additionalAttributes, final AttributeValueDecorator decorator) throws ProcessException {
+        if (rawValue == null || preparedQuery == null) {
+            return this;
+        }
+
+        return new StandardPropertyValue(preparedQuery.evaluateExpressions(flowFile, additionalAttributes, decorator), serviceLookup, null);
+    }
+
+    @Override
+    public String toString() {
+        return rawValue;
+    }
+
+    @Override
+    public ControllerService asControllerService() {
+        if (rawValue == null || rawValue.equals("") || serviceLookup == null) {
+            return null;
+        }
+
+        return serviceLookup.getControllerService(rawValue);
+    }
+
+    @Override
+    public <T extends ControllerService> T asControllerService(final Class<T> serviceType) throws IllegalArgumentException {
+        if (!serviceType.isInterface()) {
+            throw new IllegalArgumentException("ControllerServices may be referenced only via their interfaces; " + serviceType + " is not an interface");
+        }
+        if (rawValue == null || rawValue.equals("") || serviceLookup == null) {
+            return null;
+        }
+
+        final ControllerService service = serviceLookup.getControllerService(rawValue);
+        if (service == null) {
+            return null;
+        }
+        if (serviceType.isAssignableFrom(service.getClass())) {
+            return serviceType.cast(service);
+        }
+        throw new IllegalArgumentException("Controller Service with identifier " + rawValue + " is of type " + service.getClass() + " and cannot be cast to " + serviceType);
+    }
+
+    @Override
+    public boolean isSet() {
+        return rawValue != null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessContext.java
----------------------------------------------------------------------
diff --git a/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessContext.java b/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessContext.java
index 49021d1..e8e4dd5 100644
--- a/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessContext.java
+++ b/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessContext.java
@@ -28,6 +28,8 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
+import org.apache.nifi.attribute.expression.language.Query;
+import org.apache.nifi.attribute.expression.language.Query.Range;
 import org.apache.nifi.components.ConfigurableComponent;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.PropertyValue;
@@ -331,4 +333,13 @@ public class MockProcessContext extends MockControllerServiceLookup implements S
         }
     }
 
+    @Override
+    public boolean isExpressionLanguagePresent(final PropertyDescriptor property) {
+        if (property == null || !property.isExpressionLanguageSupported()) {
+            return false;
+        }
+
+        final List<Range> elRanges = Query.extractExpressionRanges(getProperty(property).getValue());
+        return (elRanges != null && !elRanges.isEmpty());
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-mock/src/main/java/org/apache/nifi/util/MockPropertyValue.java
----------------------------------------------------------------------
diff --git a/nifi-mock/src/main/java/org/apache/nifi/util/MockPropertyValue.java b/nifi-mock/src/main/java/org/apache/nifi/util/MockPropertyValue.java
index 12436d4..090a8eb 100644
--- a/nifi-mock/src/main/java/org/apache/nifi/util/MockPropertyValue.java
+++ b/nifi-mock/src/main/java/org/apache/nifi/util/MockPropertyValue.java
@@ -16,9 +16,10 @@
  */
 package org.apache.nifi.util;
 
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
-import org.apache.nifi.attribute.expression.language.Query;
+import org.apache.nifi.attribute.expression.language.StandardPropertyValue;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.PropertyValue;
 import org.apache.nifi.controller.ControllerService;
@@ -29,11 +30,11 @@ import org.apache.nifi.processor.DataUnit;
 import org.apache.nifi.processor.exception.ProcessException;
 
 public class MockPropertyValue implements PropertyValue {
-
     private final String rawValue;
     private final Boolean expectExpressions;
     private final ControllerServiceLookup serviceLookup;
     private final PropertyDescriptor propertyDescriptor;
+    private final PropertyValue stdPropValue;
     private boolean expressionsEvaluated = false;
 
     public MockPropertyValue(final String rawValue, final ControllerServiceLookup serviceLookup) {
@@ -41,12 +42,20 @@ public class MockPropertyValue implements PropertyValue {
     }
 
     public MockPropertyValue(final String rawValue, final ControllerServiceLookup serviceLookup, final PropertyDescriptor propertyDescriptor) {
+        this(rawValue, serviceLookup, propertyDescriptor, false);
+    }
+
+    private MockPropertyValue(final String rawValue, final ControllerServiceLookup serviceLookup, final PropertyDescriptor propertyDescriptor, final boolean alreadyEvaluated) {
+        this.stdPropValue = new StandardPropertyValue(rawValue, serviceLookup);
+
         this.rawValue = rawValue;
         this.serviceLookup = serviceLookup;
         this.expectExpressions = propertyDescriptor == null ? null : propertyDescriptor.isExpressionLanguageSupported();
         this.propertyDescriptor = propertyDescriptor;
+        this.expressionsEvaluated = alreadyEvaluated;
     }
 
+
     private void ensureExpressionsEvaluated() {
         if (Boolean.TRUE.equals(expectExpressions) && !expressionsEvaluated) {
             throw new IllegalStateException("Attempting to retrieve value of " + propertyDescriptor
@@ -59,49 +68,49 @@ public class MockPropertyValue implements PropertyValue {
     @Override
     public String getValue() {
         ensureExpressionsEvaluated();
-        return rawValue;
+        return stdPropValue.getValue();
     }
 
     @Override
     public Integer asInteger() {
         ensureExpressionsEvaluated();
-        return (rawValue == null) ? null : Integer.parseInt(rawValue.trim());
+        return stdPropValue.asInteger();
     }
 
     @Override
     public Long asLong() {
         ensureExpressionsEvaluated();
-        return (rawValue == null) ? null : Long.parseLong(rawValue.trim());
+        return stdPropValue.asLong();
     }
 
     @Override
     public Boolean asBoolean() {
         ensureExpressionsEvaluated();
-        return (rawValue == null) ? null : Boolean.parseBoolean(rawValue.trim());
+        return stdPropValue.asBoolean();
     }
 
     @Override
     public Float asFloat() {
         ensureExpressionsEvaluated();
-        return (rawValue == null) ? null : Float.parseFloat(rawValue.trim());
+        return stdPropValue.asFloat();
     }
 
     @Override
     public Double asDouble() {
         ensureExpressionsEvaluated();
-        return (rawValue == null) ? null : Double.parseDouble(rawValue.trim());
+        return stdPropValue.asDouble();
     }
 
     @Override
     public Long asTimePeriod(final TimeUnit timeUnit) {
         ensureExpressionsEvaluated();
-        return (rawValue == null) ? null : FormatUtils.getTimeDuration(rawValue.trim(), timeUnit);
+        return stdPropValue.asTimePeriod(timeUnit);
     }
 
     @Override
     public Double asDataSize(final DataUnit dataUnit) {
         ensureExpressionsEvaluated();
-        return rawValue == null ? null : DataUnit.parseDataSize(rawValue.trim(), dataUnit);
+        return stdPropValue.asDataSize(dataUnit);
     }
 
     private void markEvaluated() {
@@ -115,38 +124,48 @@ public class MockPropertyValue implements PropertyValue {
 
     @Override
     public PropertyValue evaluateAttributeExpressions() throws ProcessException {
-        markEvaluated();
-        if (rawValue == null) {
-            return this;
-        }
-        return evaluateAttributeExpressions(null, null);
+        return evaluateAttributeExpressions(null, null, null);
     }
 
     @Override
     public PropertyValue evaluateAttributeExpressions(final AttributeValueDecorator decorator) throws ProcessException {
-        markEvaluated();
-        if (rawValue == null) {
-            return this;
-        }
-        return evaluateAttributeExpressions(null, decorator);
+        return evaluateAttributeExpressions(null, null, decorator);
     }
 
     @Override
     public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile) throws ProcessException {
-        markEvaluated();
-        if (rawValue == null) {
-            return this;
-        }
-        return evaluateAttributeExpressions(flowFile, null);
+        return evaluateAttributeExpressions(flowFile, null, null);
     }
 
     @Override
     public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile, final AttributeValueDecorator decorator) throws ProcessException {
+        return evaluateAttributeExpressions(flowFile, null, decorator);
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile, final Map<String, String> additionalAttributes) throws ProcessException {
+        return evaluateAttributeExpressions(flowFile, additionalAttributes, null);
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions(final Map<String, String> attributes) throws ProcessException {
+        return evaluateAttributeExpressions(null, attributes, null);
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions(final Map<String, String> attributes, final AttributeValueDecorator decorator) throws ProcessException {
+        return evaluateAttributeExpressions(null, attributes, decorator);
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile, final Map<String, String> additionalAttributes, final AttributeValueDecorator decorator) throws ProcessException {
         markEvaluated();
         if (rawValue == null) {
             return this;
         }
-        return new MockPropertyValue(Query.evaluateExpressions(rawValue, flowFile, decorator), serviceLookup);
+
+        final PropertyValue newValue = stdPropValue.evaluateAttributeExpressions(flowFile, additionalAttributes, decorator);
+        return new MockPropertyValue(newValue.getValue(), serviceLookup, propertyDescriptor, true);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-mock/src/main/java/org/apache/nifi/util/StandardProcessorTestRunner.java
----------------------------------------------------------------------
diff --git a/nifi-mock/src/main/java/org/apache/nifi/util/StandardProcessorTestRunner.java b/nifi-mock/src/main/java/org/apache/nifi/util/StandardProcessorTestRunner.java
index 2f384ba..925f0d8 100644
--- a/nifi-mock/src/main/java/org/apache/nifi/util/StandardProcessorTestRunner.java
+++ b/nifi-mock/src/main/java/org/apache/nifi/util/StandardProcessorTestRunner.java
@@ -24,6 +24,7 @@ import java.io.InputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -385,11 +386,22 @@ public class StandardProcessorTestRunner implements TestRunner {
     }
 
     @Override
+    public void enqueue(final String data) {
+        enqueue(data.getBytes(StandardCharsets.UTF_8), Collections.<String, String> emptyMap());
+    }
+
+    @Override
     public void enqueue(final byte[] data, final Map<String, String> attributes) {
         enqueue(new ByteArrayInputStream(data), attributes);
     }
 
     @Override
+    public void enqueue(final String data, final Map<String, String> attributes) {
+        enqueue(data.getBytes(StandardCharsets.UTF_8), attributes);
+    }
+
+
+    @Override
     public void enqueue(final InputStream data) {
         enqueue(data, new HashMap<String, String>());
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-mock/src/main/java/org/apache/nifi/util/TestRunner.java
----------------------------------------------------------------------
diff --git a/nifi-mock/src/main/java/org/apache/nifi/util/TestRunner.java b/nifi-mock/src/main/java/org/apache/nifi/util/TestRunner.java
index b1e7c8c..6c8f192 100644
--- a/nifi-mock/src/main/java/org/apache/nifi/util/TestRunner.java
+++ b/nifi-mock/src/main/java/org/apache/nifi/util/TestRunner.java
@@ -364,16 +364,33 @@ public interface TestRunner {
     void enqueue(byte[] data);
 
     /**
+     * Creates a FlowFile with the content set to the given string (in UTF-8 format), with no attributes,
+     * and adds this FlowFile to the Processor's Input Queue
+     *
+     * @param data to enqueue
+     */
+    void enqueue(String data);
+
+    /**
      * Copies the content from the given byte array into memory and creates a
      * FlowFile from this content with the given attributes and adds this
      * FlowFile to the Processor's Input Queue
      *
      * @param data to enqueue
-     * @param attributes to use for enqueued items
+     * @param attributes to use for enqueued item
      */
     void enqueue(byte[] data, Map<String, String> attributes);
 
     /**
+     * Creates a FlowFile with the content set to the given string (in UTF-8 format), with the given attributes,
+     * and adds this FlowFile to the Processor's Input Queue
+     *
+     * @param data to enqueue
+     * @param attributes to use for enqueued item
+     */
+    void enqueue(String data, Map<String, String> attributes);
+
+    /**
      * Reads the content from the given {@link InputStream} into memory and
      * creates a FlowFile from this content with no attributes and adds this
      * FlowFile to the Processor's Input Queue

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockProcessContext.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockProcessContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockProcessContext.java
index c5ec0e2..edf0475 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockProcessContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockProcessContext.java
@@ -98,5 +98,8 @@ public class MockProcessContext implements ProcessContext {
         return false;
     }
 
-
+    @Override
+    public boolean isExpressionLanguagePresent(PropertyDescriptor property) {
+        return false;
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/ClusteredReportingContext.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/ClusteredReportingContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/ClusteredReportingContext.java
index c6624cc..4a85b5b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/ClusteredReportingContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/ClusteredReportingContext.java
@@ -22,6 +22,7 @@ import java.util.Map;
 
 import org.apache.nifi.attribute.expression.language.PreparedQuery;
 import org.apache.nifi.attribute.expression.language.Query;
+import org.apache.nifi.attribute.expression.language.StandardPropertyValue;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.PropertyValue;
 import org.apache.nifi.controller.ControllerService;
@@ -32,7 +33,6 @@ import org.apache.nifi.controller.status.ProcessGroupStatus;
 import org.apache.nifi.controller.status.ProcessorStatus;
 import org.apache.nifi.controller.status.RemoteProcessGroupStatus;
 import org.apache.nifi.events.BulletinFactory;
-import org.apache.nifi.processor.StandardPropertyValue;
 import org.apache.nifi.reporting.Bulletin;
 import org.apache.nifi.reporting.BulletinRepository;
 import org.apache.nifi.reporting.ComponentType;

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingContext.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingContext.java
index 2536d6f..a4d337f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingContext.java
@@ -23,6 +23,7 @@ import java.util.Set;
 
 import org.apache.nifi.attribute.expression.language.PreparedQuery;
 import org.apache.nifi.attribute.expression.language.Query;
+import org.apache.nifi.attribute.expression.language.StandardPropertyValue;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.PropertyValue;
 import org.apache.nifi.connectable.Connectable;
@@ -32,7 +33,6 @@ import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.controller.service.ControllerServiceProvider;
 import org.apache.nifi.events.BulletinFactory;
 import org.apache.nifi.groups.ProcessGroup;
-import org.apache.nifi.processor.StandardPropertyValue;
 import org.apache.nifi.reporting.Bulletin;
 import org.apache.nifi.reporting.BulletinRepository;
 import org.apache.nifi.reporting.EventAccess;

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/ConnectableProcessContext.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/ConnectableProcessContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/ConnectableProcessContext.java
index 08e2504..c68c78d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/ConnectableProcessContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/ConnectableProcessContext.java
@@ -59,6 +59,9 @@ public class ConnectableProcessContext implements ProcessContext {
 
     @Override
     public PropertyValue getProperty(final String propertyName) {
+        // None of the connectable components other than Processor's will ever need to evaluate these.
+        // Since Processors use a different implementation of ProcessContext all together, we will just
+        // return null for all values
         return new PropertyValue() {
             @Override
             public String getValue() {
@@ -134,6 +137,26 @@ public class ConnectableProcessContext implements ProcessContext {
             public boolean isSet() {
                 return false;
             }
+
+            @Override
+            public PropertyValue evaluateAttributeExpressions(Map<String, String> attributes) throws ProcessException {
+                return null;
+            }
+
+            @Override
+            public PropertyValue evaluateAttributeExpressions(FlowFile flowFile, Map<String, String> additionalAttributes) throws ProcessException {
+                return null;
+            }
+
+            @Override
+            public PropertyValue evaluateAttributeExpressions(Map<String, String> attributes, AttributeValueDecorator decorator) throws ProcessException {
+                return null;
+            }
+
+            @Override
+            public PropertyValue evaluateAttributeExpressions(FlowFile flowFile, Map<String, String> additionalAttributes, AttributeValueDecorator decorator) throws ProcessException {
+                return null;
+            }
         };
     }
 
@@ -208,5 +231,8 @@ public class ConnectableProcessContext implements ProcessContext {
         return connections != null && !connections.isEmpty();
     }
 
-
+    @Override
+    public boolean isExpressionLanguagePresent(PropertyDescriptor property) {
+        return false;
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardConfigurationContext.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardConfigurationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardConfigurationContext.java
index f57dc7c..d57e61f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardConfigurationContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardConfigurationContext.java
@@ -22,12 +22,12 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.nifi.attribute.expression.language.PreparedQuery;
 import org.apache.nifi.attribute.expression.language.Query;
+import org.apache.nifi.attribute.expression.language.StandardPropertyValue;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.PropertyValue;
 import org.apache.nifi.controller.ConfigurationContext;
 import org.apache.nifi.controller.ConfiguredComponent;
 import org.apache.nifi.controller.ControllerServiceLookup;
-import org.apache.nifi.processor.StandardPropertyValue;
 import org.apache.nifi.util.FormatUtils;
 
 public class StandardConfigurationContext implements ConfigurationContext {

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardProcessContext.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardProcessContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardProcessContext.java
index a6302ca..8e52a46 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardProcessContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardProcessContext.java
@@ -19,11 +19,14 @@ package org.apache.nifi.processor;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import org.apache.nifi.attribute.expression.language.PreparedQuery;
 import org.apache.nifi.attribute.expression.language.Query;
+import org.apache.nifi.attribute.expression.language.StandardPropertyValue;
+import org.apache.nifi.attribute.expression.language.Query.Range;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.PropertyValue;
 import org.apache.nifi.connectable.Connection;
@@ -196,4 +199,13 @@ public class StandardProcessContext implements ProcessContext, ControllerService
         return connections != null && !connections.isEmpty();
     }
 
+    @Override
+    public boolean isExpressionLanguagePresent(final PropertyDescriptor property) {
+        if (property == null || !property.isExpressionLanguageSupported()) {
+            return false;
+        }
+
+        final List<Range> elRanges = Query.extractExpressionRanges(getProperty(property).getValue());
+        return (elRanges != null && !elRanges.isEmpty());
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardPropertyValue.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardPropertyValue.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardPropertyValue.java
deleted file mode 100644
index acb86aa..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardPropertyValue.java
+++ /dev/null
@@ -1,157 +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.nifi.processor;
-
-import java.util.concurrent.TimeUnit;
-
-import org.apache.nifi.attribute.expression.language.PreparedQuery;
-import org.apache.nifi.attribute.expression.language.Query;
-import org.apache.nifi.components.PropertyValue;
-import org.apache.nifi.controller.ControllerService;
-import org.apache.nifi.controller.ControllerServiceLookup;
-import org.apache.nifi.expression.AttributeValueDecorator;
-import org.apache.nifi.flowfile.FlowFile;
-import org.apache.nifi.processor.exception.ProcessException;
-import org.apache.nifi.util.FormatUtils;
-
-public final class StandardPropertyValue implements PropertyValue {
-
-    private final String rawValue;
-    private final ControllerServiceLookup serviceLookup;
-    private final PreparedQuery preparedQuery;
-
-    public StandardPropertyValue(final String rawValue, final ControllerServiceLookup serviceLookup) {
-        this(rawValue, serviceLookup, Query.prepare(rawValue));
-    }
-
-    /**
-     * Constructs a new StandardPropertyValue with the given value & service lookup and indicates whether or not the rawValue contains any NiFi Expressions. If it is unknown whether or not the value
-     * contains any NiFi Expressions, the {@link #StandardPropertyValue(String, ControllerServiceLookup)} constructor should be used or <code>true</code> should be passed. However, if it is known that
-     * the value contains no NiFi Expression, that information should be provided so that calls to {@link #evaluateAttributeExpressions()} are much more efficient
-     *
-     * @param rawValue value
-     * @param serviceLookup lookup
-     * @param preparedQuery query
-     */
-    public StandardPropertyValue(final String rawValue, final ControllerServiceLookup serviceLookup, final PreparedQuery preparedQuery) {
-        this.rawValue = rawValue;
-        this.serviceLookup = serviceLookup;
-        this.preparedQuery = preparedQuery;
-    }
-
-    @Override
-    public String getValue() {
-        return rawValue;
-    }
-
-    @Override
-    public Integer asInteger() {
-        return (rawValue == null) ? null : Integer.parseInt(rawValue.trim());
-    }
-
-    @Override
-    public Long asLong() {
-        return (rawValue == null) ? null : Long.parseLong(rawValue.trim());
-    }
-
-    @Override
-    public Boolean asBoolean() {
-        return (rawValue == null) ? null : Boolean.parseBoolean(rawValue.trim());
-    }
-
-    @Override
-    public Float asFloat() {
-        return (rawValue == null) ? null : Float.parseFloat(rawValue.trim());
-    }
-
-    @Override
-    public Double asDouble() {
-        return (rawValue == null) ? null : Double.parseDouble(rawValue.trim());
-    }
-
-    @Override
-    public Long asTimePeriod(final TimeUnit timeUnit) {
-        return (rawValue == null) ? null : FormatUtils.getTimeDuration(rawValue.trim(), timeUnit);
-    }
-
-    @Override
-    public Double asDataSize(final DataUnit dataUnit) {
-        return rawValue == null ? null : DataUnit.parseDataSize(rawValue.trim(), dataUnit);
-    }
-
-    @Override
-    public PropertyValue evaluateAttributeExpressions() throws ProcessException {
-        return evaluateAttributeExpressions(null, null);
-    }
-
-    @Override
-    public PropertyValue evaluateAttributeExpressions(final AttributeValueDecorator decorator) throws ProcessException {
-        return evaluateAttributeExpressions(null, decorator);
-    }
-
-    @Override
-    public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile) throws ProcessException {
-        return evaluateAttributeExpressions(flowFile, null);
-    }
-
-    @Override
-    public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile, final AttributeValueDecorator decorator) throws ProcessException {
-        if (rawValue == null || preparedQuery == null) {
-            return this;
-        }
-
-        return new StandardPropertyValue(preparedQuery.evaluateExpressions(flowFile, decorator), serviceLookup, null);
-    }
-
-    @Override
-    public String toString() {
-        return rawValue;
-    }
-
-    @Override
-    public ControllerService asControllerService() {
-        if (rawValue == null || rawValue.equals("")) {
-            return null;
-        }
-
-        return serviceLookup.getControllerService(rawValue);
-    }
-
-    @Override
-    public <T extends ControllerService> T asControllerService(final Class<T> serviceType) throws IllegalArgumentException {
-        if (!serviceType.isInterface()) {
-            throw new IllegalArgumentException("ControllerServices may be referenced only via their interfaces; " + serviceType + " is not an interface");
-        }
-        if (rawValue == null || rawValue.equals("")) {
-            return null;
-        }
-
-        final ControllerService service = serviceLookup.getControllerService(rawValue);
-        if (service == null) {
-            return null;
-        }
-        if (serviceType.isAssignableFrom(service.getClass())) {
-            return serviceType.cast(service);
-        }
-        throw new IllegalArgumentException("Controller Service with identifier " + rawValue + " is of type " + service.getClass() + " and cannot be cast to " + serviceType);
-    }
-
-    @Override
-    public boolean isSet() {
-        return rawValue != null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardSchedulingContext.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardSchedulingContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardSchedulingContext.java
index a3c2a5d..8c903ee 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardSchedulingContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardSchedulingContext.java
@@ -16,9 +16,12 @@
  */
 package org.apache.nifi.processor;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.nifi.attribute.expression.language.Query;
+import org.apache.nifi.attribute.expression.language.Query.Range;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.PropertyValue;
 import org.apache.nifi.controller.ControllerServiceLookup;
@@ -126,4 +129,14 @@ public class StandardSchedulingContext implements SchedulingContext {
     public boolean hasConnection(Relationship relationship) {
         return processContext.hasConnection(relationship);
     }
+
+    @Override
+    public boolean isExpressionLanguagePresent(PropertyDescriptor property) {
+        if (property == null || !property.isExpressionLanguageSupported()) {
+            return false;
+        }
+
+        final List<Range> elRanges = Query.extractExpressionRanges(getProperty(property).getValue());
+        return (elRanges != null && !elRanges.isEmpty());
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java
index e627dd3..c24e146 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java
@@ -26,6 +26,7 @@ import org.apache.nifi.attribute.expression.language.PreparedQuery;
 import org.apache.nifi.attribute.expression.language.Query;
 import org.apache.nifi.attribute.expression.language.Query.Range;
 import org.apache.nifi.attribute.expression.language.StandardExpressionLanguageCompiler;
+import org.apache.nifi.attribute.expression.language.StandardPropertyValue;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.PropertyValue;
 import org.apache.nifi.components.ValidationContext;

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/processor/TestStandardPropertyValue.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/processor/TestStandardPropertyValue.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/processor/TestStandardPropertyValue.java
index be40e90..cebedaf 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/processor/TestStandardPropertyValue.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/processor/TestStandardPropertyValue.java
@@ -16,8 +16,6 @@
  */
 package org.apache.nifi.processor;
 
-import org.apache.nifi.processor.StandardPropertyValue;
-
 import static org.junit.Assert.assertEquals;
 
 import java.util.Calendar;
@@ -25,6 +23,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.nifi.attribute.expression.language.StandardPropertyValue;
 import org.apache.nifi.components.PropertyValue;
 import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.controller.ControllerServiceLookup;

http://git-wip-us.apache.org/repos/asf/nifi/blob/f378ee90/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/StandardSearchContext.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/StandardSearchContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/StandardSearchContext.java
index ddb5350..f673075 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/StandardSearchContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/StandardSearchContext.java
@@ -18,11 +18,11 @@ package org.apache.nifi.web.controller;
 
 import java.util.Map;
 
+import org.apache.nifi.attribute.expression.language.StandardPropertyValue;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.PropertyValue;
 import org.apache.nifi.controller.ControllerServiceLookup;
 import org.apache.nifi.controller.ProcessorNode;
-import org.apache.nifi.processor.StandardPropertyValue;
 import org.apache.nifi.search.SearchContext;
 
 /**