You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by al...@apache.org on 2019/03/01 02:09:30 UTC

[nifi-minifi-cpp] branch master updated (c3c47b6 -> 8a4c2ac)

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

aldrin pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git.


    from c3c47b6  MINIFICPP-745: update readme with table
     new f01953b  MINIFICPP-738 - EL should be able to access global properties
     new 8a4c2ac  MINIFICPP-740: Add ability to run NiFi processors from Java and Python

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .gitignore                                         |    2 +
 CMakeLists.txt                                     |   66 +-
 CMakeSettings.json                                 |    9 +-
 LICENSE                                            |   32 +-
 NOTICE                                             |   14 +
 bootstrap.sh                                       |    4 +
 bstrp_functions.sh                                 |    6 +-
 centos.sh                                          |    2 +-
 conf/minifi.properties                             |    9 +
 debian.sh                                          |    2 +
 extensions/coap/CMakeLists.txt                     |    2 +-
 extensions/coap/nanofi/coap_connection.c           |   13 +-
 extensions/coap/nanofi/coap_functions.c            |   88 +-
 extensions/coap/nanofi/coap_functions.h            |    2 +-
 extensions/coap/nanofi/coap_server.c               |   47 +-
 extensions/coap/server/CoapServer.h                |    5 +-
 extensions/coap/tests/CoapC2VerifyHeartbeat.cpp    |   15 +-
 extensions/expression-language/Expression.cpp      | 1282 ++++----------------
 .../expression-language/ProcessContextExpr.cpp     |   15 +-
 extensions/expression-language/common/Value.h      |    6 +-
 .../impl/expression/Expression.h                   |   32 +-
 .../noop/expression/Expression.h                   |    3 +-
 extensions/http-curl/client/HTTPClient.cpp         |   20 +-
 extensions/http-curl/client/HTTPClient.h           |   13 +
 extensions/http-curl/protocols/RESTSender.cpp      |   16 +-
 .../http-curl/tests/unit/InvokeHTTPTests.cpp       |   16 +-
 extensions/jni/CMakeLists.txt                      |   75 ++
 extensions/jni/ExecuteJavaControllerService.cpp    |  104 ++
 extensions/jni/ExecuteJavaControllerService.h      |  135 +++
 extensions/jni/ExecuteJavaProcessor.cpp            |  219 ++++
 extensions/jni/ExecuteJavaProcessor.h              |  296 +++++
 .../expression/Expression.h => jni/JNILoader.cpp}  |   37 +-
 extensions/jni/JNILoader.h                         |   79 ++
 .../expression/Expression.h => jni/JVMCreator.cpp} |   38 +-
 extensions/jni/JVMCreator.h                        |  113 ++
 extensions/jni/JavaException.h                     |  117 ++
 extensions/jni/README.md                           |   49 +
 .../Expression.h => jni/jvm/JVMLoader.cpp}         |   34 +-
 extensions/jni/jvm/JVMLoader.h                     |  518 ++++++++
 extensions/jni/jvm/JavaClass.h                     |  139 +++
 extensions/jni/jvm/JavaControllerService.cpp       |  117 ++
 extensions/jni/jvm/JavaControllerService.h         |  170 +++
 .../expression/Expression.h => jni/jvm/JavaDefs.h} |   34 +-
 .../Expression.h => jni/jvm/JavaServicer.h}        |   31 +-
 extensions/jni/jvm/JniBundle.h                     |   87 ++
 extensions/jni/jvm/JniFlowFile.cpp                 |  153 +++
 extensions/jni/jvm/JniFlowFile.h                   |   53 +
 extensions/jni/jvm/JniLogger.cpp                   |  107 ++
 extensions/jni/jvm/JniLogger.h                     |   71 ++
 extensions/jni/jvm/JniMethod.h                     |  129 ++
 extensions/jni/jvm/JniProcessContext.cpp           |   79 ++
 extensions/jni/jvm/JniProcessContext.h             |   80 ++
 extensions/jni/jvm/JniProcessSession.cpp           |  478 ++++++++
 extensions/jni/jvm/JniProcessSession.h             |   90 ++
 .../jvm/JniReferenceObjects.cpp}                   |   31 +-
 extensions/jni/jvm/JniReferenceObjects.h           |  374 ++++++
 extensions/jni/jvm/NarClassLoader.h                |  443 +++++++
 extensions/jni/nifi-framework-jni/pom.xml          |   96 ++
 .../main/java/org/apache/nifi/nar/JniUnpacker.java |  267 ++++
 .../java/org/apache/nifi/processor/JniBundle.java  |   29 +
 .../org/apache/nifi/processor/JniClassLoader.java  |  498 ++++++++
 .../org/apache/nifi/processor/JniComponent.java    |  118 ++
 .../apache/nifi/processor/JniComponentLogger.java  |  358 ++++++
 .../org/apache/nifi/processor/JniFlowFile.java     |   59 +
 .../nifi/processor/JniInitializationContext.java   |   61 +
 .../org/apache/nifi/processor/JniInputStream.java  |   19 +
 .../java/org/apache/nifi/processor/JniLogger.java  |   21 +
 .../apache/nifi/processor/JniProcessContext.java   |  169 +++
 .../apache/nifi/processor/JniProcessSession.java   |  323 +++++
 .../nifi/processor/JniProcessSessionFactory.java   |    9 +
 .../org/apache/nifi/processor/JniProperty.java     |    9 +
 .../nifi/processor/JniProvenanceReporter.java      |  208 ++++
 extensions/librdkafka/PublishKafka.cpp             |  251 ++--
 extensions/librdkafka/PublishKafka.h               |  267 +++-
 extensions/script/CMakeLists.txt                   |    7 +-
 extensions/script/ExampleProcessor.py              |   11 +
 extensions/script/README.md                        |   61 +
 .../script/python/ExecutePythonProcessor.cpp       |  204 ++++
 extensions/script/python/ExecutePythonProcessor.h  |  155 +++
 .../python/PyProcCreator.cpp}                      |   38 +-
 extensions/script/python/PyProcCreator.h           |  107 ++
 extensions/script/python/PythonBindings.h          |    8 +-
 .../python/PythonCreator.cpp}                      |   43 +-
 extensions/script/python/PythonCreator.h           |  139 +++
 extensions/script/python/PythonProcessor.cpp       |   65 +
 extensions/script/python/PythonProcessor.h         |   71 ++
 extensions/script/python/PythonScriptEngine.cpp    |    2 +-
 extensions/script/python/PythonScriptEngine.h      |  104 +-
 .../pythonloader/PyProcLoader.cpp}                 |   38 +-
 extensions/script/pythonloader/PyProcLoader.h      |   69 ++
 fedora.sh                                          |    2 +
 libminifi/include/FlowController.h                 |    2 +
 libminifi/include/agent/build_description.h        |   71 +-
 libminifi/include/core/ClassLoader.h               |   33 +
 libminifi/include/core/ConfigurableComponent.h     |    8 +
 libminifi/include/core/ContentRepository.h         |    1 -
 libminifi/include/core/Core.h                      |    9 +-
 libminifi/include/core/FlowConfiguration.h         |    1 +
 libminifi/include/core/FlowFile.h                  |    8 +-
 libminifi/include/core/ProcessContext.h            |   23 +-
 libminifi/include/core/ProcessSession.h            |   16 +-
 libminifi/include/core/Processor.h                 |    2 +-
 libminifi/include/core/Property.h                  |   20 +-
 libminifi/include/core/VariableRegistry.h          |  105 ++
 libminifi/include/core/WeakReference.h             |  103 ++
 .../include/core/controller/ControllerService.h    |   11 +
 .../include/core/logging/LoggerConfiguration.h     |    5 +
 .../include/core/state/nodes/AgentInformation.h    |  103 +-
 libminifi/include/processors/RouteOnAttribute.h    |   12 +-
 libminifi/include/processors/UpdateAttribute.h     |    2 +-
 libminifi/include/properties/Configure.h           |    1 +
 libminifi/include/provenance/Provenance.h          |   15 +-
 libminifi/include/utils/Id.h                       |    3 +
 libminifi/include/utils/file/FileManager.h         |   34 +-
 libminifi/include/utils/file/FileUtils.h           |  180 ++-
 libminifi/src/Configure.cpp                        |    1 +
 libminifi/src/FlowController.cpp                   |   16 +-
 libminifi/src/Properties.cpp                       |   15 +-
 libminifi/src/ThreadedSchedulingAgent.cpp          |    2 +-
 libminifi/src/c2/C2Agent.cpp                       |   35 +-
 libminifi/src/core/ConfigurableComponent.cpp       |   66 +-
 libminifi/src/core/FlowConfiguration.cpp           |   36 +
 libminifi/src/core/FlowFile.cpp                    |   19 +-
 libminifi/src/core/ProcessSession.cpp              |   24 +-
 libminifi/src/core/yaml/YamlConfiguration.cpp      |    6 +-
 libminifi/src/io/FileStream.cpp                    |    3 +
 libminifi/src/processors/RouteOnAttribute.cpp      |    4 +
 libminifi/src/provenance/Provenance.cpp            |   25 +-
 libminifi/test/TestBase.cpp                        |    9 +-
 libminifi/test/TestBase.h                          |   25 +-
 .../test/archive-tests/CompressContentTests.cpp    |   20 +-
 libminifi/test/archive-tests/MergeFileTests.cpp    |   14 +-
 .../ExpressionLanguageTests.cpp                    |   17 +-
 libminifi/test/gps-tests/GPSTests.cpp              |    2 +-
 .../test/{mqtt-tests => jni-tests}/CMakeLists.txt  |    0
 .../test/script-tests/LuaExecuteScriptTests.cpp    |    4 +-
 .../test/script-tests/PythonExecuteScriptTests.cpp |    4 +-
 libminifi/test/unit/GetTCPTests.cpp                |   14 +-
 libminifi/test/unit/ProcessorTests.cpp             |   23 +-
 libminifi/test/unit/PutFileTests.cpp               |    8 +-
 libminifi/test/unit/TailFileTests.cpp              |    4 +-
 main/CMakeLists.txt                                |   11 +-
 main/Main.h                                        |   71 +-
 main/MiNiFiMain.cpp                                |  387 +++---
 nanofi/include/cxx/Instance.h                      |   12 +-
 nanofi/include/cxx/Plan.h                          |    2 +-
 nanofi/src/cxx/Plan.cpp                            |    2 +-
 thirdparty/pybind11/include/pybind11/pytypes.h     |    2 +-
 148 files changed, 9753 insertions(+), 1985 deletions(-)
 create mode 100644 extensions/jni/CMakeLists.txt
 create mode 100644 extensions/jni/ExecuteJavaControllerService.cpp
 create mode 100644 extensions/jni/ExecuteJavaControllerService.h
 create mode 100644 extensions/jni/ExecuteJavaProcessor.cpp
 create mode 100644 extensions/jni/ExecuteJavaProcessor.h
 copy extensions/{expression-language/noop/expression/Expression.h => jni/JNILoader.cpp} (55%)
 create mode 100644 extensions/jni/JNILoader.h
 copy extensions/{expression-language/noop/expression/Expression.h => jni/JVMCreator.cpp} (67%)
 create mode 100644 extensions/jni/JVMCreator.h
 create mode 100644 extensions/jni/JavaException.h
 create mode 100644 extensions/jni/README.md
 copy extensions/{expression-language/noop/expression/Expression.h => jni/jvm/JVMLoader.cpp} (55%)
 create mode 100644 extensions/jni/jvm/JVMLoader.h
 create mode 100644 extensions/jni/jvm/JavaClass.h
 create mode 100644 extensions/jni/jvm/JavaControllerService.cpp
 create mode 100644 extensions/jni/jvm/JavaControllerService.h
 copy extensions/{expression-language/noop/expression/Expression.h => jni/jvm/JavaDefs.h} (55%)
 copy extensions/{expression-language/noop/expression/Expression.h => jni/jvm/JavaServicer.h} (63%)
 create mode 100644 extensions/jni/jvm/JniBundle.h
 create mode 100644 extensions/jni/jvm/JniFlowFile.cpp
 create mode 100644 extensions/jni/jvm/JniFlowFile.h
 create mode 100644 extensions/jni/jvm/JniLogger.cpp
 create mode 100644 extensions/jni/jvm/JniLogger.h
 create mode 100644 extensions/jni/jvm/JniMethod.h
 create mode 100644 extensions/jni/jvm/JniProcessContext.cpp
 create mode 100644 extensions/jni/jvm/JniProcessContext.h
 create mode 100644 extensions/jni/jvm/JniProcessSession.cpp
 create mode 100644 extensions/jni/jvm/JniProcessSession.h
 copy extensions/{expression-language/noop/expression/Expression.h => jni/jvm/JniReferenceObjects.cpp} (67%)
 create mode 100644 extensions/jni/jvm/JniReferenceObjects.h
 create mode 100644 extensions/jni/jvm/NarClassLoader.h
 create mode 100644 extensions/jni/nifi-framework-jni/pom.xml
 create mode 100644 extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/nar/JniUnpacker.java
 create mode 100644 extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniBundle.java
 create mode 100644 extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniClassLoader.java
 create mode 100644 extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniComponent.java
 create mode 100644 extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniComponentLogger.java
 create mode 100644 extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniFlowFile.java
 create mode 100644 extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniInitializationContext.java
 create mode 100644 extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniInputStream.java
 create mode 100644 extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniLogger.java
 create mode 100644 extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProcessContext.java
 create mode 100644 extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProcessSession.java
 create mode 100644 extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProcessSessionFactory.java
 create mode 100644 extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProperty.java
 create mode 100644 extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProvenanceReporter.java
 create mode 100644 extensions/script/ExampleProcessor.py
 create mode 100644 extensions/script/README.md
 create mode 100644 extensions/script/python/ExecutePythonProcessor.cpp
 create mode 100644 extensions/script/python/ExecutePythonProcessor.h
 copy extensions/{expression-language/noop/expression/Expression.h => script/python/PyProcCreator.cpp} (55%)
 create mode 100644 extensions/script/python/PyProcCreator.h
 copy extensions/{expression-language/noop/expression/Expression.h => script/python/PythonCreator.cpp} (67%)
 create mode 100644 extensions/script/python/PythonCreator.h
 create mode 100644 extensions/script/python/PythonProcessor.cpp
 create mode 100644 extensions/script/python/PythonProcessor.h
 copy extensions/{expression-language/noop/expression/Expression.h => script/pythonloader/PyProcLoader.cpp} (55%)
 create mode 100644 extensions/script/pythonloader/PyProcLoader.h
 create mode 100644 libminifi/include/core/VariableRegistry.h
 create mode 100644 libminifi/include/core/WeakReference.h
 copy libminifi/test/{mqtt-tests => jni-tests}/CMakeLists.txt (100%)


[nifi-minifi-cpp] 02/02: MINIFICPP-740: Add ability to run NiFi processors from Java and Python

Posted by al...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

aldrin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git

commit 8a4c2ac04693d56ba201adae3b5207ea96e92b50
Author: Marc Parisi <ph...@apache.org>
AuthorDate: Thu Jan 3 16:41:33 2019 -0500

    MINIFICPP-740: Add ability to run NiFi processors from Java and Python
    
    This closes #489.
    
    Signed-off-by: Aldrin Piri <al...@apache.org>
---
 .gitignore                                         |    2 +
 CMakeLists.txt                                     |   66 +-
 CMakeSettings.json                                 |    9 +-
 LICENSE                                            |   32 +-
 NOTICE                                             |   14 +
 bootstrap.sh                                       |    4 +
 bstrp_functions.sh                                 |    6 +-
 centos.sh                                          |    2 +-
 conf/minifi.properties                             |    9 +
 debian.sh                                          |    2 +
 extensions/coap/CMakeLists.txt                     |    2 +-
 extensions/coap/nanofi/coap_connection.c           |   13 +-
 extensions/coap/nanofi/coap_functions.c            |   88 +-
 extensions/coap/nanofi/coap_functions.h            |    2 +-
 extensions/coap/nanofi/coap_server.c               |   47 +-
 extensions/coap/server/CoapServer.h                |    5 +-
 extensions/coap/tests/CoapC2VerifyHeartbeat.cpp    |   15 +-
 extensions/expression-language/Expression.cpp      | 1282 ++++----------------
 .../expression-language/ProcessContextExpr.cpp     |   21 +-
 extensions/expression-language/common/Value.h      |    6 +-
 .../impl/expression/Expression.h                   |   25 +-
 .../noop/expression/Expression.h                   |    3 +-
 extensions/http-curl/client/HTTPClient.cpp         |   20 +-
 extensions/http-curl/client/HTTPClient.h           |   13 +
 extensions/http-curl/protocols/RESTSender.cpp      |   16 +-
 .../http-curl/tests/unit/InvokeHTTPTests.cpp       |   16 +-
 extensions/jni/CMakeLists.txt                      |   75 ++
 extensions/jni/ExecuteJavaControllerService.cpp    |  104 ++
 extensions/jni/ExecuteJavaControllerService.h      |  135 +++
 extensions/jni/ExecuteJavaProcessor.cpp            |  219 ++++
 extensions/jni/ExecuteJavaProcessor.h              |  296 +++++
 .../expression/Expression.h => jni/JNILoader.cpp}  |   37 +-
 extensions/jni/JNILoader.h                         |   79 ++
 .../expression/Expression.h => jni/JVMCreator.cpp} |   38 +-
 extensions/jni/JVMCreator.h                        |  113 ++
 extensions/jni/JavaException.h                     |  117 ++
 extensions/jni/README.md                           |   49 +
 .../Expression.h => jni/jvm/JVMLoader.cpp}         |   34 +-
 extensions/jni/jvm/JVMLoader.h                     |  518 ++++++++
 extensions/jni/jvm/JavaClass.h                     |  139 +++
 extensions/jni/jvm/JavaControllerService.cpp       |  117 ++
 extensions/jni/jvm/JavaControllerService.h         |  170 +++
 .../expression/Expression.h => jni/jvm/JavaDefs.h} |   34 +-
 .../Expression.h => jni/jvm/JavaServicer.h}        |   31 +-
 extensions/jni/jvm/JniBundle.h                     |   87 ++
 extensions/jni/jvm/JniFlowFile.cpp                 |  153 +++
 extensions/jni/jvm/JniFlowFile.h                   |   53 +
 extensions/jni/jvm/JniLogger.cpp                   |  107 ++
 extensions/jni/jvm/JniLogger.h                     |   71 ++
 extensions/jni/jvm/JniMethod.h                     |  129 ++
 extensions/jni/jvm/JniProcessContext.cpp           |   79 ++
 extensions/jni/jvm/JniProcessContext.h             |   80 ++
 extensions/jni/jvm/JniProcessSession.cpp           |  478 ++++++++
 extensions/jni/jvm/JniProcessSession.h             |   90 ++
 .../jvm/JniReferenceObjects.cpp}                   |   31 +-
 extensions/jni/jvm/JniReferenceObjects.h           |  374 ++++++
 extensions/jni/jvm/NarClassLoader.h                |  443 +++++++
 extensions/jni/nifi-framework-jni/pom.xml          |   96 ++
 .../main/java/org/apache/nifi/nar/JniUnpacker.java |  267 ++++
 .../java/org/apache/nifi/processor/JniBundle.java  |   29 +
 .../org/apache/nifi/processor/JniClassLoader.java  |  498 ++++++++
 .../org/apache/nifi/processor/JniComponent.java    |  118 ++
 .../apache/nifi/processor/JniComponentLogger.java  |  358 ++++++
 .../org/apache/nifi/processor/JniFlowFile.java     |   59 +
 .../nifi/processor/JniInitializationContext.java   |   61 +
 .../org/apache/nifi/processor/JniInputStream.java  |   19 +
 .../java/org/apache/nifi/processor/JniLogger.java  |   21 +
 .../apache/nifi/processor/JniProcessContext.java   |  169 +++
 .../apache/nifi/processor/JniProcessSession.java   |  323 +++++
 .../nifi/processor/JniProcessSessionFactory.java   |    9 +
 .../org/apache/nifi/processor/JniProperty.java     |    9 +
 .../nifi/processor/JniProvenanceReporter.java      |  208 ++++
 extensions/librdkafka/PublishKafka.cpp             |  251 ++--
 extensions/librdkafka/PublishKafka.h               |  267 +++-
 .../rocksdb-repos/DatabaseContentRepository.cpp    |    1 -
 extensions/script/CMakeLists.txt                   |    7 +-
 extensions/script/ExampleProcessor.py              |   11 +
 extensions/script/README.md                        |   61 +
 .../script/python/ExecutePythonProcessor.cpp       |  204 ++++
 extensions/script/python/ExecutePythonProcessor.h  |  155 +++
 .../python/PyProcCreator.cpp}                      |   38 +-
 extensions/script/python/PyProcCreator.h           |  107 ++
 extensions/script/python/PythonBindings.h          |    8 +-
 .../python/PythonCreator.cpp}                      |   43 +-
 extensions/script/python/PythonCreator.h           |  139 +++
 extensions/script/python/PythonProcessor.cpp       |   65 +
 extensions/script/python/PythonProcessor.h         |   71 ++
 extensions/script/python/PythonScriptEngine.cpp    |    2 +-
 extensions/script/python/PythonScriptEngine.h      |  104 +-
 .../pythonloader/PyProcLoader.cpp}                 |   38 +-
 extensions/script/pythonloader/PyProcLoader.h      |   69 ++
 fedora.sh                                          |    2 +
 libminifi/include/FlowController.h                 |    2 +
 libminifi/include/agent/build_description.h        |   71 +-
 libminifi/include/core/ClassLoader.h               |   33 +
 libminifi/include/core/ConfigurableComponent.h     |    8 +
 libminifi/include/core/ContentRepository.h         |    6 -
 libminifi/include/core/Core.h                      |    9 +-
 libminifi/include/core/FlowConfiguration.h         |    1 +
 libminifi/include/core/FlowFile.h                  |    8 +-
 libminifi/include/core/ProcessContext.h            |   23 +-
 libminifi/include/core/ProcessSession.h            |   16 +-
 libminifi/include/core/Processor.h                 |    2 +-
 libminifi/include/core/Property.h                  |   20 +-
 libminifi/include/core/VariableRegistry.h          |  105 ++
 libminifi/include/core/WeakReference.h             |  103 ++
 .../include/core/controller/ControllerService.h    |   11 +
 .../include/core/logging/LoggerConfiguration.h     |    5 +
 .../include/core/state/nodes/AgentInformation.h    |  103 +-
 libminifi/include/processors/RouteOnAttribute.h    |   12 +-
 libminifi/include/processors/UpdateAttribute.h     |    2 +-
 libminifi/include/properties/Configure.h           |    1 +
 libminifi/include/provenance/Provenance.h          |   15 +-
 libminifi/include/utils/Id.h                       |    3 +
 libminifi/include/utils/file/FileManager.h         |   34 +-
 libminifi/include/utils/file/FileUtils.h           |  180 ++-
 libminifi/src/Configure.cpp                        |    1 +
 libminifi/src/FlowController.cpp                   |   16 +-
 libminifi/src/Properties.cpp                       |   15 +-
 libminifi/src/ThreadedSchedulingAgent.cpp          |    2 +-
 libminifi/src/c2/C2Agent.cpp                       |   35 +-
 libminifi/src/core/ConfigurableComponent.cpp       |   66 +-
 libminifi/src/core/FlowConfiguration.cpp           |   36 +
 libminifi/src/core/FlowFile.cpp                    |   19 +-
 libminifi/src/core/ProcessSession.cpp              |   24 +-
 .../src/core/repository/FileSystemRepository.cpp   |    1 -
 .../core/repository/VolatileContentRepository.cpp  |    1 -
 libminifi/src/core/yaml/YamlConfiguration.cpp      |    6 +-
 libminifi/src/io/FileStream.cpp                    |    3 +
 libminifi/src/processors/RouteOnAttribute.cpp      |    4 +
 libminifi/src/provenance/Provenance.cpp            |   25 +-
 libminifi/test/TestBase.cpp                        |    9 +-
 libminifi/test/TestBase.h                          |   21 +-
 .../test/archive-tests/CompressContentTests.cpp    |   20 +-
 libminifi/test/archive-tests/MergeFileTests.cpp    |   14 +-
 libminifi/test/gps-tests/GPSTests.cpp              |    2 +-
 libminifi/test/jni-tests/CMakeLists.txt            |   39 +
 .../test/script-tests/LuaExecuteScriptTests.cpp    |    4 +-
 .../test/script-tests/PythonExecuteScriptTests.cpp |    4 +-
 libminifi/test/unit/GetTCPTests.cpp                |   14 +-
 libminifi/test/unit/ProcessorTests.cpp             |   23 +-
 libminifi/test/unit/PutFileTests.cpp               |    8 +-
 libminifi/test/unit/TailFileTests.cpp              |    4 +-
 main/CMakeLists.txt                                |   11 +-
 main/Main.h                                        |   71 +-
 main/MiNiFiMain.cpp                                |  387 +++---
 nanofi/include/cxx/Instance.h                      |   12 +-
 nanofi/include/cxx/Plan.h                          |    2 +-
 nanofi/src/cxx/Plan.cpp                            |    2 +-
 thirdparty/pybind11/include/pybind11/pytypes.h     |    2 +-
 150 files changed, 9765 insertions(+), 1998 deletions(-)

diff --git a/.gitignore b/.gitignore
index eb28306..a7345ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,7 +21,9 @@
 
 # Ignore JetBrains project files
 .idea
+# Ignore intellij project files
 
+.iml
 # Ignore JetBrains cLion project files.
 .project
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a00d4c1..45e49cd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,6 +29,7 @@ option(USE_SYSTEM_OPENSSL "Instructs the build system to search for and use an S
 option(OPENSSL_OFF "Disables OpenSSL" OFF)
 option(ENABLE_OPS "Enable Operations/zlib Tools" ON)
 option(USE_SYSTEM_UUID "Instructs the build system to search for and use an UUID library available in the host system" OFF)
+option(ENABLE_JNI "Instructs the build system to enable the JNI extension" OFF)
 option(USE_SYSTEM_CURL "Instructs the build system to search for and use a cURL library available in the host system" ON)
 option(BUILD_SHARED_LIBS "Build yaml cpp shared lib" OFF)
 if (WIN32)
@@ -41,14 +42,56 @@ option(USE_SYSTEM_BZIP2 "Instructs the build system to search for and use a bzip
 option(BUILD_ROCKSDB "Instructs the build system to use RocksDB from the third party directory" ON)
 option(FORCE_WINDOWS "Instructs the build system to force Windows builds when WIN32 is specified" OFF)
 
+
 include(CheckIncludeFile)
 include(FeatureSummary)
 include(ExternalProject)
 
+
+
 if (OPENSSL_ROOT_DIR )
 	set(OPENSSL_PASSTHROUGH "-DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR}")
 endif()
 
+if(NOT WIN32)
+	if (ENABLE_JNI)
+	  set(BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}/thirdparty/jemalloc")
+	  
+	  set(DIR "${BASE_DIR}/extensions/jemalloc-src")
+	  set(JE_BYPRODUCT "${DIR}/lib/libjemalloc.a")
+	  ExternalProject_Add(
+	    jemalloc-external
+	    GIT_REPOSITORY "https://github.com/jemalloc/jemalloc.git"
+	    GIT_TAG "61efbda7098de6fe64c362d309824864308c36d4" 
+	    PREFIX "${BASE_DIR}/extensions/jemalloc"
+	    BUILD_IN_SOURCE true
+	    SOURCE_DIR "${DIR}"
+	    BUILD_COMMAND make
+	    CMAKE_COMMAND ""
+	    UPDATE_COMMAND ""
+	    BUILD_BYPRODUCTS ${JE_BYPRODUCT} 
+	    INSTALL_COMMAND ${CMAKE_COMMAND}  -E echo "Skipping install step."
+	    CONFIGURE_COMMAND ""
+	    PATCH_COMMAND ./autogen.sh && ./configure 
+	    STEP_TARGETS build
+	    EXCLUDE_FROM_ALL TRUE
+	  )
+	  
+	  add_library(jemalloc STATIC IMPORTED)
+	  set_target_properties(jemalloc PROPERTIES IMPORTED_LOCATION "${JE_BYPRODUCT}")
+	  add_dependencies(jemalloc jemalloc-external)
+	  set(JEMALLOC_FOUND "YES" CACHE STRING "" FORCE)
+	  set(JEMALLOC_INCLUDE_DIRS "${DIR}/include" CACHE STRING "" FORCE)
+	  set(JEMALLOC_LIBRARIES jemalloc CACHE STRING "" FORCE)
+	  set(JEMALLOC_LIBRARY jemalloc CACHE STRING "" FORCE)
+	  set(JEMALLOC_LIBRARY jemalloc CACHE STRING "" FORCE)
+
+	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_JNI")
+	set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_JNI")
+
+	endif()
+endif()
+
 set(PASSTHROUGH_CMAKE_ARGS -DANDROID_ABI=${ANDROID_ABI}
                            -DANDROID_PLATFORM=${ANDROID_PLATFORM}
                            -DANDROID_STL=${ANDROID_STL}
@@ -86,9 +129,6 @@ endif()
 # Enable usage of the VERSION specifier
 include(CheckCXXCompilerFlag)
 if (WIN32)
-	if (NOT FORCE_WINDOWS)
-	  message(FATAL_ERROR "Windows builds can only be built with FORCE_WINDOWS")
-	endif()
 	add_definitions(-DWIN32_LEAN_AND_MEAN)
   if ((MSVC_VERSION GREATER "1900") OR (MSVC_VERSION EQUAL "1900"))
     CHECK_CXX_COMPILER_FLAG("/std:c++14" _cpp_latest_flag_supported)
@@ -199,7 +239,7 @@ if(USE_SYSTEM_ZLIB STREQUAL "OFF")
   message("Using bundled zlib")
 
   if (WIN32)
-  IF(CMAKE_BUILD_TYPE MATCHES RELEASE)
+  IF(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)
   set(BYPRODUCT "thirdparty/zlib-install/lib/zlibstatic.lib")
   else()
   set(BYPRODUCT "thirdparty/zlib-install/lib/zlibstaticd.lib")
@@ -261,7 +301,7 @@ if(NOT DISABLE_CURL AND (NOT USE_SYSTEM_CURL))
   set(CURL_CXX_FLAGS "${CURL_C_FLAGS}")
 
   if (WIN32)
-  IF(CMAKE_BUILD_TYPE MATCHES RELEASE)
+  IF(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)
 	set(BYPRODUCT "thirdparty/curl-install/lib/libcurl.lib")
   else()
   	set(BYPRODUCT "thirdparty/curl-install/lib/libcurl-d.lib")
@@ -437,6 +477,11 @@ if(ENABLE_ALL OR ENABLE_MQTT)
         createExtension(MQTT-EXTENSIONS "MQTT EXTENSIONS" "This Enables MQTT functionality including PublishMQTT/ConsumeMQTT" "extensions/mqtt" "${TEST_DIR}/mqtt-tests" "TRUE" "thirdparty/paho.mqtt.c")
 endif()
 
+if(ENABLE_ALL OR ENABLE_JNI)
+	createExtension(JNI-EXTENSION "JNI EXTENSIONS" "Enables JNI capabilities to support loading Java Classes." "extensions/jni" "${TEST_DIR}/jni-tests")
+endif()
+
+
 option(ENABLE_PCAP "Enables the PCAP extension." OFF)
 if(ENABLE_ALL OR ENABLE_PCAP)
 	createExtension(PCAP-EXTENSION "PCAP EXTENSIONS" "Enables libPCAP Functionality and the PacketCapture processor." "extensions/pcap" "${TEST_DIR}/pcap-tests")
@@ -496,7 +541,7 @@ if (NOT DISABLE_CURL)
   if (ENABLE_PYTHON)
   	if (NOT WIN32)
   		add_subdirectory(python/library)
-  	endif()
+  	endif()	
   endif(ENABLE_PYTHON)
 endif()
 
@@ -569,7 +614,11 @@ install(PROGRAMS bin/minifi.sh
 install(FILES LICENSE README.md NOTICE
         DESTINATION .
         COMPONENT bin)
-
+        
+install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/main/minifi
+        DESTINATION bin
+        COMPONENT bin)
+        
 
 set(CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY 1)
 set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Apache NiFi MiNiFi C++ version ${VERSION}")
@@ -587,9 +636,8 @@ set(CPACK_MQTT_COMPONENT_INSTALL ON)
 set(CPACK_COMPONENTS_ALL bin)
 
 
-### include modules
-
 include(CPack)
+### include modules
 
 if (NOT SKIP_TESTS)
 	include(BuildTests)
diff --git a/CMakeSettings.json b/CMakeSettings.json
index 486eff5..f9082be 100644
--- a/CMakeSettings.json
+++ b/CMakeSettings.json
@@ -36,6 +36,10 @@
           "value": "OFF"
         },
         {
+          "name": "ENABLE_JNI",
+          "value": "ON"
+        },
+        {
           "name": "FORCE_WINDOWS",
           "value": "ON"
         },
@@ -91,7 +95,6 @@
       "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
       "cmakeCommandArgs": "",
       "buildCommandArgs": "-v -j8",
-      "$" { "env.USERPROFILE" },
       "variables": [
         {
           "name": "OPENSSL_OFF",
@@ -118,6 +121,10 @@
           "value": "OFF"
         },
         {
+          "name": "ENABLE_JNI",
+          "value": "ON"
+        },
+        {
           "name": "USE_SYSTEM_UUID",
           "value": "OFF"
         },
diff --git a/LICENSE b/LICENSE
index d762e8f..9f5b0bd 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1630,4 +1630,34 @@ This product bundles 'bsdiff' which is available under a "2-clause BSD" license.
       of your accepting any such warranty or additional liability.
 
    END OF TERMS AND CONDITIONS
- 
\ No newline at end of file
+
+This project bundles jemalloc, which is available under a "2-clause BSD" License:
+
+Unless otherwise specified, files in the jemalloc source distribution are
+subject to the following license:
+--------------------------------------------------------------------------------
+Copyright (C) 2002-present Jason Evans <ja...@canonware.com>.
+All rights reserved.
+Copyright (C) 2007-2012 Mozilla Foundation.  All rights reserved.
+Copyright (C) 2009-present Facebook, Inc.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the above copyright notice(s),
+   this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice(s),
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
+EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--------------------------------------------------------------------------------
+
diff --git a/NOTICE b/NOTICE
index 16a5be2..04d03e2 100644
--- a/NOTICE
+++ b/NOTICE
@@ -9,3 +9,17 @@ Copyright 2015 The Apache Software Foundation
 The derived work is adapted from
   celix/cmake/FindUUID.cmake
 and can be found in cmake/FindUUID.cmake
+
+This includes derived works from the Apache NiFi (ALv2 Licensed ) project (https://github.com/apache/nifi):
+Copyright 2019 The Apache Software Foundation
+The derived and original works are listed below:
+    JniUnpacker.java is based on NarUnpacker.java.
+    JniClassLoader is based, predominately, on NarClassdLoader with major variations.
+    JniComponentLogger extends ComponentLog
+    JniFlowFile extends and is based on FlowFile
+    JniInitializationContext extends and is based on ProcessorInitializationContext
+    JniProcessContext extends and is based on ProcessContext and ControllerServiceLookup
+    JniProcessSession extends and is based on ProcessSession
+    JniProcessSessionFactory extends and is based on ProcessSessionFactory
+    JniProvenanceReporter extends and is based on ProvenanceReporter
+
diff --git a/bootstrap.sh b/bootstrap.sh
index 2acd303..2a0ef15 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -273,9 +273,13 @@ add_dependency PYTHON_ENABLED "python"
 
 add_disabled_option COAP_ENABLED ${FALSE} "ENABLE_COAP"
 add_dependency COAP_ENABLED "automake"
+add_dependency COAP_ENABLED "autoconf"
 add_dependency COAP_ENABLED "libtool"
 
+add_disabled_option JNI_ENABLED ${FALSE} "ENABLE_JNI"
+
 TESTS_DISABLED=${FALSE}
+
 add_disabled_option SQLITE_ENABLED ${FALSE} "ENABLE_SQLITE"
 
 # Since the following extensions have limitations on
diff --git a/bstrp_functions.sh b/bstrp_functions.sh
index 0938ed0..e7e0255 100755
--- a/bstrp_functions.sh
+++ b/bstrp_functions.sh
@@ -263,6 +263,7 @@ show_supported_features() {
   echo "****************************************"
   echo "1. Disable Tests ...............$(print_feature_status TESTS_DISABLED)"
   echo "2. Enable all extensions"
+  echo "3. Enable JNI Support ..........$(print_feature_status JNI_ENABLED)"
   echo "P. Continue with these options"
   if [ "$GUIDED_INSTALL" = "${TRUE}" ]; then
     echo "R. Return to Main Menu"
@@ -274,7 +275,7 @@ show_supported_features() {
 
 read_feature_options(){
   local choice
-  read -p "Enter choice [ A - P or 1-2 ] " choice
+  read -p "Enter choice [ A - P or 1-3 ] " choice
   choice=$(echo ${choice} | tr '[:upper:]' '[:lower:]')
   case $choice in
     a) ToggleFeature ROCKSDB_ENABLED ;;
@@ -294,13 +295,14 @@ read_feature_options(){
     o) ToggleFeature COAP_ENABLED ;;
     1) ToggleFeature TESTS_DISABLED ;;
     2) EnableAllFeatures ;;
+    3) ToggleFeature JNI_ENABLED;;
     p) FEATURES_SELECTED="true" ;;
     r) if [ "$GUIDED_INSTALL" = "${TRUE}" ]; then
         MENU="main"
       fi
       ;;
     q) exit 0;;
-    *) echo -e "${RED}Please enter an option A-P or 1-2...${NO_COLOR}" && sleep 2
+    *) echo -e "${RED}Please enter an option A-P or 1-3...${NO_COLOR}" && sleep 2
   esac
 }
 
diff --git a/centos.sh b/centos.sh
index 06c77d6..70d9bfb 100644
--- a/centos.sh
+++ b/centos.sh
@@ -154,7 +154,7 @@ build_deps(){
 
     fi
   done
-
+  INSTALLED+=("autoconf")
   for option in "${INSTALLED[@]}" ; do
     COMMAND="${COMMAND} $option"
   done
diff --git a/conf/minifi.properties b/conf/minifi.properties
index 568669a..705bb16 100644
--- a/conf/minifi.properties
+++ b/conf/minifi.properties
@@ -41,3 +41,12 @@ nifi.database.content.repository.directory.default=${MINIFI_HOME}/content_reposi
 ## off by default. C2 must be enabled to support these
 #controller.socket.host=localhost
 #controller.socket.port=9998
+
+
+#JNI properties
+nifi.framework.dir=${MINIFI_HOME}/minifi-jni/lib
+nifi.nar.directory=${MINIFI_HOME}/minifi-jni/nars
+nifi.nar.deploy.directory=${MINIFI_HOME}/minifi-jni/nardeploy
+nifi.nar.deploy.directory=${MINIFI_HOME}/minifi-jni/nardocs
+# must be comma separated 
+nifi.jvm.options=-Xmx1G
\ No newline at end of file
diff --git a/debian.sh b/debian.sh
index ba1ec7d..170d822 100644
--- a/debian.sh
+++ b/debian.sh
@@ -81,6 +81,8 @@ build_deps(){
 
     fi
   done
+  
+  INSTALLED+=("autoconf")
 
   for option in "${INSTALLED[@]}" ; do
     COMMAND="${COMMAND} $option"
diff --git a/extensions/coap/CMakeLists.txt b/extensions/coap/CMakeLists.txt
index 3f38328..1f0d03e 100644
--- a/extensions/coap/CMakeLists.txt
+++ b/extensions/coap/CMakeLists.txt
@@ -52,7 +52,7 @@ endif()
     CMAKE_COMMAND ""
     UPDATE_COMMAND ""
     BUILD_BYPRODUCTS ${BYPRODUCT} 
-    INSTALL_COMMAND cmake -E echo "Skipping install step."
+    INSTALL_COMMAND ${CMAKE_COMMAND}  -E echo "Skipping install step."
     CONFIGURE_COMMAND ""
     PATCH_COMMAND ./autogen.sh && ./configure --disable-examples --disable-tests --disable-documentation
     STEP_TARGETS build
diff --git a/extensions/coap/nanofi/coap_connection.c b/extensions/coap/nanofi/coap_connection.c
index c49c3e2..eaa6c36 100644
--- a/extensions/coap/nanofi/coap_connection.c
+++ b/extensions/coap/nanofi/coap_connection.c
@@ -17,17 +17,16 @@
  */
 #include "coap_connection.h"
 
-
 CoapPDU *create_connection(uint8_t type, const char * const server, const char * const endpoint, int port, const CoapMessage * const message) {
-  CoapPDU *pdu = (CoapPDU*)malloc(sizeof(CoapPDU));
+  CoapPDU *pdu = (CoapPDU*) malloc(sizeof(CoapPDU));
 
   pdu->ctx = NULL;
   pdu->session = NULL;
 
   coap_uri_t uri;
-  uri.host.s = (uint8_t*)server; // may be a loss in resolution, but hostnames should be char *
+  uri.host.s = (uint8_t*) server;  // may be a loss in resolution, but hostnames should be char *
   uri.host.length = strlen(server);
-  uri.path.s = (uint8_t*)endpoint; // ^ same as above for paths.
+  uri.path.s = (uint8_t*) endpoint;  // ^ same as above for paths.
   uri.path.length = strlen(endpoint);
   uri.port = port;
   uri.scheme = COAP_URI_SCHEME_COAP;
@@ -40,25 +39,23 @@ CoapPDU *create_connection(uint8_t type, const char * const server, const char *
   if (res < 0) {
     return NULL;
   }
-
   pdu->dst_addr.size = res;
   pdu->dst_addr.addr.sin.sin_port = htons(uri.port);
 
   void *addrptr = NULL;
   char port_str[NI_MAXSERV] = "0";
 
-  char node_str[NI_MAXHOST] = "";
   switch (pdu->dst_addr.addr.sa.sa_family) {
     case AF_INET:
       addrptr = &pdu->dst_addr.addr.sin.sin_addr;
-      if (!create_session(&pdu->ctx, &pdu->session, node_str[0] == 0 ? "0.0.0.0" : node_str, port_str, &pdu->dst_addr)) {
+      if (!create_session(&pdu->ctx, &pdu->session, 0x00, port_str, &pdu->dst_addr)) {
         break;
       } else {
         return NULL;
       }
     case AF_INET6:
       addrptr = &pdu->dst_addr.addr.sin6.sin6_addr;
-      if (!create_session(&pdu->ctx, &pdu->session, node_str[0] == 0 ? "::" : node_str, port_str, &pdu->dst_addr)) {
+      if (!create_session(&pdu->ctx, &pdu->session, 0x00, port_str, &pdu->dst_addr)) {
         break;
       } else {
         return NULL;
diff --git a/extensions/coap/nanofi/coap_functions.c b/extensions/coap/nanofi/coap_functions.c
index c66cf3d..7ec25c0 100644
--- a/extensions/coap/nanofi/coap_functions.c
+++ b/extensions/coap/nanofi/coap_functions.c
@@ -34,62 +34,70 @@ int create_session(coap_context_t **ctx, coap_session_t **session, const char *n
   int getaddrres;
   struct addrinfo hints;
   coap_proto_t proto = COAP_PROTO_UDP;
-  struct addrinfo *result, *rp;
+  struct addrinfo *result, *interface_itr;
 
   memset(&hints, 0, sizeof(struct addrinfo));
   hints.ai_family = AF_UNSPEC;  // ipv4 or ipv6
   hints.ai_socktype = COAP_PROTO_RELIABLE(proto) ? SOCK_STREAM : SOCK_DGRAM;
   hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV | AI_ALL;
 
-  getaddrres = getaddrinfo(node, port, &hints, &result);
-  if (getaddrres != 0) {
-    return -1;
-  }
-
-  for (rp = result; rp != NULL; rp = rp->ai_next) {
-    coap_address_t addr;
-
-    if (rp->ai_addrlen <= sizeof(addr.addr)) {
-      coap_address_init(&addr);
-      addr.size = rp->ai_addrlen;
-      memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
-
-      *ctx = coap_new_context(0x00);
-
-      *session = coap_new_client_session(*ctx, &addr, dst_addr, proto);
-      if (*ctx && *session) {
-        freeaddrinfo(result);
-        return 0;
+  if (node) {
+    getaddrres = getaddrinfo(node, port, &hints, &result);
+    if (getaddrres != 0) {
+      perror("getaddrinfo");
+      return -1;
+    }
+    int skip = 1, count = 0;
+    for (interface_itr = result; interface_itr != NULL; interface_itr = interface_itr->ai_next) {
+      coap_address_t addr;
+
+      if (interface_itr->ai_addrlen <= sizeof(addr.addr)) {
+        coap_address_init(&addr);
+        addr.size = interface_itr->ai_addrlen;
+        memcpy(&addr.addr, interface_itr->ai_addr, interface_itr->ai_addrlen);
+
+        *ctx = coap_new_context(0x00);
+
+        *session = coap_new_client_session(*ctx, &addr, dst_addr, proto);
+        if (*ctx && *session) {
+          freeaddrinfo(result);
+          return 0;
+        }
       }
     }
+    freeaddrinfo(result);
+    return -2;
+  } else {
+    *ctx = coap_new_context(0x00);
+
+    *session = coap_new_client_session(*ctx, 0x00, dst_addr, proto);
+    return 0;
   }
 
-  freeaddrinfo(result);
-  return -2;
 }
 
 int create_endpoint_context(coap_context_t **ctx, const char *node, const char *port) {
   struct addrinfo hints;
   coap_proto_t proto = COAP_PROTO_UDP;
-  struct addrinfo *result, *rp;
+  struct addrinfo *result, *interface_itr;
 
   memset(&hints, 0, sizeof(struct addrinfo));
   hints.ai_family = AF_UNSPEC;  // ipv4 or ipv6
   hints.ai_socktype = SOCK_DGRAM;
-  hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST ;
-
+  hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
   int getaddrres = getaddrinfo(node, port, &hints, &result);
   if (getaddrres != 0) {
+    perror("getaddrinfo");
     return -1;
   }
 
-  for (rp = result; rp != NULL; rp = rp->ai_next) {
+  for (interface_itr = result; interface_itr != NULL; interface_itr = interface_itr->ai_next) {
     coap_address_t addr;
 
-    if (rp->ai_addrlen <= sizeof(addr.addr)) {
+    if (interface_itr->ai_addrlen <= sizeof(addr.addr)) {
       coap_address_init(&addr);
-      addr.size = rp->ai_addrlen;
-      memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
+      addr.size = interface_itr->ai_addrlen;
+      memcpy(&addr.addr, interface_itr->ai_addr, interface_itr->ai_addrlen);
 
       *ctx = coap_new_context(0x00);
 
@@ -163,8 +171,8 @@ void response_handler(struct coap_context_t *ctx, struct coap_session_t *session
 
 }
 
-int resolve_address(const struct coap_str_const_t *server, struct sockaddr *dst) {
-  struct addrinfo *res, *ainfo;
+int resolve_address(const struct coap_str_const_t *server, struct sockaddr *destination) {
+  struct addrinfo *result, *iterative_obj;
   struct addrinfo hints;
   static char addrstr[256];
   int error, len = -1;
@@ -175,30 +183,32 @@ int resolve_address(const struct coap_str_const_t *server, struct sockaddr *dst)
   else
     memcpy(addrstr, "127.0.0.1", 9);
 
-  memset((char * ) &hints, 0, sizeof(hints));
+  memset((char *) &hints, 0, sizeof(hints));
   hints.ai_socktype = SOCK_DGRAM;
   hints.ai_family = AF_UNSPEC;
 
-  error = getaddrinfo(addrstr, NULL, &hints, &res);
+  error = getaddrinfo(addrstr, NULL, &hints, &result);
 
   if (error != 0) {
+    perror("getaddrinfo");
+
     return error;
   }
 
-  for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {
-    switch (ainfo->ai_family) {
+  for (iterative_obj = result; iterative_obj != NULL; iterative_obj = iterative_obj->ai_next) {
+    switch (iterative_obj->ai_family) {
       case AF_INET6:
       case AF_INET:
-        len = ainfo->ai_addrlen;
-        memcpy(dst, ainfo->ai_addr, len);
-        freeaddrinfo(res);
+        len = iterative_obj->ai_addrlen;
+        memcpy(destination, iterative_obj->ai_addr, len);
+        freeaddrinfo(result);
         return len;
       default:
         ;
     }
   }
 
-  freeaddrinfo(res);
+  freeaddrinfo(result);
   return len;
 }
 
diff --git a/extensions/coap/nanofi/coap_functions.h b/extensions/coap/nanofi/coap_functions.h
index c86362d..5061ac5 100644
--- a/extensions/coap/nanofi/coap_functions.h
+++ b/extensions/coap/nanofi/coap_functions.h
@@ -116,7 +116,7 @@ void response_handler(struct coap_context_t *ctx, struct coap_session_t *session
  * @param dst destination pointer
  * @return 0 if sucess -1 otherwise
  */
-int resolve_address(const struct coap_str_const_t *server, struct sockaddr *dst);
+int resolve_address(const struct coap_str_const_t *server, struct sockaddr *destination);
 
 #ifdef __cplusplus
 }
diff --git a/extensions/coap/nanofi/coap_server.c b/extensions/coap/nanofi/coap_server.c
index cd94bef..d8badbb 100644
--- a/extensions/coap/nanofi/coap_server.c
+++ b/extensions/coap/nanofi/coap_server.c
@@ -20,37 +20,39 @@
 /**
  * Create a new CoAPServer
  */
-CoapServerContext * const create_server(const char *const server_hostname, const char * const port){
-  CoapServerContext *server = (CoapServerContext*)malloc(sizeof(CoapServerContext));
-  memset(server,0x00, sizeof(CoapServerContext));
-  if ( create_endpoint_context(&server->ctx,server_hostname,port) ) {
+CoapServerContext * const create_server(const char * const server_hostname, const char * const port) {
+  CoapServerContext *server = (CoapServerContext*) malloc(sizeof(CoapServerContext));
+  memset(server, 0x00, sizeof(CoapServerContext));
+  if (create_endpoint_context(&server->ctx, server_hostname, port)) {
     free_server(server);
   }
 
   return server;
 }
 
-CoapEndpoint *const create_endpoint(CoapServerContext * const server, const char * const resource_path, uint8_t method, coap_method_handler_t handler){
-  CoapEndpoint *endpoint = (CoapEndpoint*)malloc(sizeof(CoapEndpoint));
-  memset(endpoint,0x00, sizeof(CoapEndpoint));
+CoapEndpoint * const create_endpoint(CoapServerContext * const server, const char * const resource_path, uint8_t method, coap_method_handler_t handler) {
+  CoapEndpoint *endpoint = (CoapEndpoint*) malloc(sizeof(CoapEndpoint));
+  memset(endpoint, 0x00, sizeof(CoapEndpoint));
   endpoint->server = server;
   int8_t flags = COAP_RESOURCE_FLAGS_NOTIFY_CON;
   coap_str_const_t *path = NULL;
-  if (NULL != resource_path){
-      path = coap_new_str_const((const uint8_t *)resource_path,strlen(resource_path));
+  if (NULL != resource_path) {
+    path = coap_new_str_const((const uint8_t *) resource_path, strlen(resource_path));
   }
   endpoint->resource = coap_resource_init(path, flags);
-  coap_add_attr(endpoint->resource, coap_make_str_const("title"), coap_make_str_const("\"Internal Clock\""), 0);
-  assert( !add_endpoint(endpoint,method,handler) );
-  coap_add_resource(server->ctx,endpoint->resource);
-  if (path){
+  coap_add_attr(endpoint->resource, coap_make_str_const("title"), coap_make_str_const("\"Created CoapEndpoint\""), 0);
+  if ( add_endpoint(endpoint, method, handler) ){
+    return 0x00;
+  }
+  coap_add_resource(server->ctx, endpoint->resource);
+  if (path) {
     coap_delete_str_const(path);
   }
   return endpoint;
 
 }
 
-int8_t add_endpoint(CoapEndpoint * const endpoint, uint8_t method, coap_method_handler_t handler){
+int8_t add_endpoint(CoapEndpoint * const endpoint, uint8_t method, coap_method_handler_t handler) {
   if (endpoint == NULL || handler == NULL)
     return -1;
 
@@ -58,19 +60,20 @@ int8_t add_endpoint(CoapEndpoint * const endpoint, uint8_t method, coap_method_h
   return 0;
 }
 
-
 /**
  * FRee the CoAP messages that are provided.
  */
-void free_endpoint(CoapEndpoint * const endpoint){
-  if (endpoint){
-    free((void*)endpoint);
+void free_endpoint(CoapEndpoint * const endpoint) {
+  if (endpoint) {
+    free((void*) endpoint);
   }
 }
-void free_server(CoapServerContext * const server){
-  if (server){
-    coap_delete_all_resources( server->ctx );
-    coap_free_context( server->ctx );
+void free_server(CoapServerContext * const server) {
+  if (server) {
+    if (server->ctx) {
+      coap_delete_all_resources(server->ctx);
+      coap_free_context(server->ctx);
+    }
     free(server);
   }
 }
diff --git a/extensions/coap/server/CoapServer.h b/extensions/coap/server/CoapServer.h
index e929925..3717eff 100644
--- a/extensions/coap/server/CoapServer.h
+++ b/extensions/coap/server/CoapServer.h
@@ -112,7 +112,6 @@ class CoapServer : public core::Connectable {
         port_(port) {
     coap_startup();
     auto port_str = std::to_string(port_);
-    //coap_set_log_level(coap_log_t::LOG_DEBUG);
     server_ = create_server(hostname_.c_str(), port_str.c_str());
   }
 
@@ -135,7 +134,7 @@ class CoapServer : public core::Connectable {
   }
 
   void add_endpoint(const std::string &path, METHOD method, std::function<CoapResponse(CoapQuery)> functor) {
-    unsigned char mthd = 0;
+    unsigned char mthd = COAP_REQUEST_POST;
     switch (method) {
       case GET:
         mthd = COAP_REQUEST_GET;
@@ -161,7 +160,7 @@ class CoapServer : public core::Connectable {
   }
 
   void add_endpoint(METHOD method, std::function<CoapResponse(CoapQuery)> functor) {
-    unsigned char mthd = 0;
+    unsigned char mthd = COAP_REQUEST_POST;
     switch (method) {
       case GET:
         mthd = COAP_REQUEST_GET;
diff --git a/extensions/coap/tests/CoapC2VerifyHeartbeat.cpp b/extensions/coap/tests/CoapC2VerifyHeartbeat.cpp
index e363195..af73de9 100644
--- a/extensions/coap/tests/CoapC2VerifyHeartbeat.cpp
+++ b/extensions/coap/tests/CoapC2VerifyHeartbeat.cpp
@@ -122,9 +122,11 @@ class VerifyCoAPServer : public CoapIntegrationBase {
     std::string port, scheme, path;
 
     parse_http_components(url, port, scheme, path);
-    auto new_port_str = std::to_string(std::stoi(port) + 2);
+    uint16_t newport = std::stoi(port) + 2;
+    auto new_port_str = std::to_string(newport);
+
+    server = std::unique_ptr<minifi::coap::CoapServer>(new minifi::coap::CoapServer("127.0.0.1", newport));
 
-    server = std::unique_ptr<minifi::coap::CoapServer>(new minifi::coap::CoapServer("127.0.0.1", std::stoi(port) + 2));
 
     server->add_endpoint(minifi::coap::METHOD::POST, [](minifi::coap::CoapQuery)->minifi::coap::CoapResponse {
       minifi::coap::CoapResponse response(205,0x00,0);
@@ -149,16 +151,16 @@ class VerifyCoAPServer : public CoapIntegrationBase {
     {
       // should result in valid operation
       minifi::io::BaseStream stream;
-      uint16_t version=0, size=1;
+      uint16_t version = 0, size = 1;
       uint8_t operation = 1;
       stream.write(version);
       stream.write(size);
-      stream.write(&operation,1);
+      stream.write(&operation, 1);
       stream.writeUTF("id");
       stream.writeUTF("operand");
 
-      uint8_t *data = new uint8_t[ stream.getSize() ];
-      memcpy(data,stream.getBuffer(), stream.getSize() );
+      uint8_t *data = new uint8_t[stream.getSize()];
+      memcpy(data, stream.getBuffer(), stream.getSize());
       minifi::coap::CoapResponse response(205, std::unique_ptr<uint8_t>(data), stream.getSize());
       responses.enqueue(std::move(response));
     }
@@ -175,7 +177,6 @@ class VerifyCoAPServer : public CoapIntegrationBase {
       }
 
     });
-
     server->start();
     configuration->set("c2.enable", "true");
     configuration->set("c2.agent.class", "test");
diff --git a/extensions/expression-language/Expression.cpp b/extensions/expression-language/Expression.cpp
index 07fd509..2b60c4c 100644
--- a/extensions/expression-language/Expression.cpp
+++ b/extensions/expression-language/Expression.cpp
@@ -58,20 +58,20 @@ Expression make_static(std::string val) {
   return Expression(Value(val));
 }
 
-Expression make_dynamic(const std::function<Value(const Parameters &params,
-                                                  const std::vector<Expression> &sub_exprs)> &val_fn) {
+Expression make_dynamic(const std::function<Value(const Parameters &params, const std::vector<Expression> &sub_exprs)> &val_fn) {
   return Expression(Value(), val_fn);
 }
 
 Expression make_dynamic_attr(const std::string &attribute_id) {
   return make_dynamic([attribute_id](const Parameters &params, const std::vector<Expression> &sub_exprs) -> Value {
+
     std::string result;
     const auto cur_flow_file = params.flow_file.lock();
     if (cur_flow_file && cur_flow_file->getAttribute(attribute_id, result)) {
       return Value(result);
-    } else if (attribute_id.rfind("nifi.", 0) == 0) {
-      const auto config = params.configuration.lock();
-      if (config && config->get(attribute_id, result)) {
+    } else {
+      auto registry = params.registry_.lock();
+      if ( registry && registry->getConfigurationProperty( attribute_id , result) ) {
         return Value(result);
       }
     }
@@ -355,9 +355,9 @@ Value expr_indexOf(const std::vector<Value> &args) {
   auto pos = args[0].asString().find(args[1].asString());
 
   if (pos == std::string::npos) {
-    return Value(static_cast<int64_t >(-1));
+    return Value(static_cast<int64_t>(-1));
   } else {
-    return Value(static_cast<int64_t >(pos));
+    return Value(static_cast<int64_t>(pos));
   }
 }
 
@@ -373,9 +373,9 @@ Value expr_lastIndexOf(const std::vector<Value> &args) {
   }
 
   if (pos == std::string::npos) {
-    return Value(static_cast<int64_t >(-1));
+    return Value(static_cast<int64_t>(-1));
   } else {
-    return Value(static_cast<int64_t >(pos));
+    return Value(static_cast<int64_t>(pos));
   }
 }
 
@@ -402,868 +402,154 @@ Value expr_unescapeJson(const std::vector<Value> &args) {
 }
 
 Value expr_escapeHtml3(const std::vector<Value> &args) {
-  return Value(utils::StringUtils::replaceMap(
-      args[0].asString(),
-      {
-          {"!", "&excl;"},
-          {"\"", "&quot;"},
-          {"#", "&num;"},
-          {"$", "&dollar;"},
-          {"%", "&percnt;"},
-          {"&", "&amp;"},
-          {"'", "&apos;"},
-          {"(", "&lpar;"},
-          {")", "&rpar;"},
-          {"*", "&ast;"},
-          {"+", "&plus;"},
-          {",", "&comma;"},
-          {"-", "&minus;"},
-          {".", "&period;"},
-          {"/", "&sol;"},
-          {":", "&colon;"},
-          {";", "&semi;"},
-          {"<", "&lt;"},
-          {"=", "&equals;"},
-          {">", "&gt;"},
-          {"?", "&quest;"},
-          {"@", "&commat;"},
-          {"[", "&lsqb;"},
-          {"\\", "&bsol;"},
-          {"]", "&rsqb;"},
-          {"^", "&circ;"},
-          {"_", "&lowbar;"},
-          {"`", "&grave;"},
-          {"{", "&lcub;"},
-          {"|", "&verbar;"},
-          {"}", "&rcub;"},
-          {"~", "&tilde;"},
-          {"¡", "&iexcl;"},
-          {"¢", "&cent;"},
-          {"£", "&pound;"},
-          {"¤", "&curren;"},
-          {"¥", "&yen;"},
-          {"¦", "&brkbar;"},
-          {"§", "&sect;"},
-          {"¨", "&uml;"},
-          {"©", "&copy;"},
-          {"ª", "&ordf;"},
-          {"«", "&laquo;"},
-          {"¬", "&not;"},
-          {"®", "&reg;"},
-          {"¯", "&macr;"},
-          {"°", "&deg;"},
-          {"±", "&plusmn;"},
-          {"²", "&sup2;"},
-          {"³", "&sup3;"},
-          {"´", "&acute;"},
-          {"µ", "&micro;"},
-          {"¶", "&para;"},
-          {"·", "&middot;"},
-          {"¸", "&cedil;"},
-          {"¹", "&sup1;"},
-          {"º", "&ordm;"},
-          {"»", "&raquo;;"},
-          {"¼", "&frac14;"},
-          {"½", "&frac12;"},
-          {"¾", "&frac34;"},
-          {"¿", "&iquest;"},
-          {"À", "&Agrave;"},
-          {"Á", "&Aacute;"},
-          {"Â", "&Acirc;"},
-          {"Ã", "&Atilde;"},
-          {"Ä", "&Auml;"},
-          {"Å", "&Aring;"},
-          {"Æ", "&AElig;"},
-          {"Ç", "&Ccedil;"},
-          {"È", "&Egrave;"},
-          {"É", "&Eacute;"},
-          {"Ê", "&Ecirc;"},
-          {"Ë", "&Euml;"},
-          {"Ì", "&Igrave;"},
-          {"Í", "&Iacute;"},
-          {"Î", "&Icirc;"},
-          {"Ï", "&Iuml;"},
-          {"Ð", "&ETH;"},
-          {"Ñ", "&Ntilde;"},
-          {"Ò", "&Ograve;"},
-          {"Ó", "&Oacute;"},
-          {"Ô", "&Ocirc;"},
-          {"Õ", "&Otilde;"},
-          {"Ö", "&Ouml;"},
-          {"×", "&times;"},
-          {"Ø", "&Oslash;"},
-          {"Ù", "&Ugrave;;"},
-          {"Ú", "&Uacute;"},
-          {"Û", "&Ucirc;"},
-          {"Ü", "&Uuml;"},
-          {"Ý", "&Yacute;"},
-          {"Þ", "&THORN;"},
-          {"ß", "&szlig;"},
-          {"à", "&agrave;"},
-          {"á", "&aacute;"},
-          {"â", "&acirc;"},
-          {"ã", "&atilde;"},
-          {"ä", "&auml;"},
-          {"å", "&aring;"},
-          {"æ", "&aelig;"},
-          {"ç", "&ccedil;"},
-          {"è", "&egrave;"},
-          {"é", "&eacute;"},
-          {"ê", "&ecirc;"},
-          {"ë", "&euml;"},
-          {"ì", "&igrave;"},
-          {"í", "&iacute;"},
-          {"î", "&icirc;"},
-          {"ï", "&iuml;"},
-          {"ð", "&eth;"},
-          {"ñ", "&ntilde;"},
-          {"ò", "&ograve;"},
-          {"ó", "&oacute;"},
-          {"ô", "&ocirc;"},
-          {"õ", "&otilde;"},
-          {"ö", "&ouml;"},
-          {"÷", "&divide;"},
-          {"ø", "&oslash;"},
-          {"ù", "&ugrave;"},
-          {"ú", "&uacute;"},
-          {"û", "&ucirc;"},
-          {"ü", "&uuml;"},
-          {"ý", "&yacute;"},
-          {"þ", "&thorn;"},
-          {"ÿ", "&yuml;"}
-      }));
+  return Value(utils::StringUtils::replaceMap(args[0].asString(), { { "!", "&excl;" }, { "\"", "&quot;" }, { "#", "&num;" }, { "$", "&dollar;" }, { "%", "&percnt;" }, { "&", "&amp;" },
+                                                  { "'", "&apos;" }, { "(", "&lpar;" }, { ")", "&rpar;" }, { "*", "&ast;" }, { "+", "&plus;" }, { ",", "&comma;" }, { "-", "&minus;" }, { ".",
+                                                      "&period;" }, { "/", "&sol;" }, { ":", "&colon;" }, { ";", "&semi;" }, { "<", "&lt;" }, { "=", "&equals;" }, { ">", "&gt;" }, { "?", "&quest;" },
+                                                  { "@", "&commat;" }, { "[", "&lsqb;" }, { "\\", "&bsol;" }, { "]", "&rsqb;" }, { "^", "&circ;" }, { "_", "&lowbar;" }, { "`", "&grave;" }, { "{",
+                                                      "&lcub;" }, { "|", "&verbar;" }, { "}", "&rcub;" }, { "~", "&tilde;" }, { "¡", "&iexcl;" }, { "¢", "&cent;" }, { "£", "&pound;" }, { "¤",
+                                                      "&curren;" }, { "¥", "&yen;" }, { "¦", "&brkbar;" }, { "§", "&sect;" }, { "¨", "&uml;" }, { "©", "&copy;" }, { "ª", "&ordf;" },
+                                                  { "«", "&laquo;" }, { "¬", "&not;" }, { "®", "&reg;" }, { "¯", "&macr;" }, { "°", "&deg;" }, { "±", "&plusmn;" }, { "²", "&sup2;" },
+                                                  { "³", "&sup3;" }, { "´", "&acute;" }, { "µ", "&micro;" }, { "¶", "&para;" }, { "·", "&middot;" }, { "¸", "&cedil;" }, { "¹", "&sup1;" }, { "º",
+                                                      "&ordm;" }, { "»", "&raquo;;" }, { "¼", "&frac14;" }, { "½", "&frac12;" }, { "¾", "&frac34;" }, { "¿", "&iquest;" }, { "À", "&Agrave;" }, { "Á",
+                                                      "&Aacute;" }, { "Â", "&Acirc;" }, { "Ã", "&Atilde;" }, { "Ä", "&Auml;" }, { "Å", "&Aring;" }, { "Æ", "&AElig;" }, { "Ç", "&Ccedil;" }, { "È",
+                                                      "&Egrave;" }, { "É", "&Eacute;" }, { "Ê", "&Ecirc;" }, { "Ë", "&Euml;" }, { "Ì", "&Igrave;" }, { "Í", "&Iacute;" }, { "Î", "&Icirc;" }, { "Ï",
+                                                      "&Iuml;" }, { "Ð", "&ETH;" }, { "Ñ", "&Ntilde;" }, { "Ò", "&Ograve;" }, { "Ó", "&Oacute;" }, { "Ô", "&Ocirc;" }, { "Õ", "&Otilde;" }, { "Ö",
+                                                      "&Ouml;" }, { "×", "&times;" }, { "Ø", "&Oslash;" }, { "Ù", "&Ugrave;;" }, { "Ú", "&Uacute;" }, { "Û", "&Ucirc;" }, { "Ü", "&Uuml;" }, { "Ý",
+                                                      "&Yacute;" }, { "Þ", "&THORN;" }, { "ß", "&szlig;" }, { "à", "&agrave;" }, { "á", "&aacute;" }, { "â", "&acirc;" }, { "ã", "&atilde;" }, { "ä",
+                                                      "&auml;" }, { "å", "&aring;" }, { "æ", "&aelig;" }, { "ç", "&ccedil;" }, { "è", "&egrave;" }, { "é", "&eacute;" }, { "ê", "&ecirc;" }, { "ë",
+                                                      "&euml;" }, { "ì", "&igrave;" }, { "í", "&iacute;" }, { "î", "&icirc;" }, { "ï", "&iuml;" }, { "ð", "&eth;" }, { "ñ", "&ntilde;" }, { "ò",
+                                                      "&ograve;" }, { "ó", "&oacute;" }, { "ô", "&ocirc;" }, { "õ", "&otilde;" }, { "ö", "&ouml;" }, { "÷", "&divide;" }, { "ø", "&oslash;" }, { "ù",
+                                                      "&ugrave;" }, { "ú", "&uacute;" }, { "û", "&ucirc;" }, { "ü", "&uuml;" }, { "ý", "&yacute;" }, { "þ", "&thorn;" }, { "ÿ", "&yuml;" } }));
 }
 
 Value expr_escapeHtml4(const std::vector<Value> &args) {
-  return Value(utils::StringUtils::replaceMap(
-      args[0].asString(),
-      {
-          {"!", "&excl;"},
-          {"\"", "&quot;"},
-          {"#", "&num;"},
-          {"$", "&dollar;"},
-          {"%", "&percnt;"},
-          {"&", "&amp;"},
-          {"'", "&apos;"},
-          {"(", "&lpar;"},
-          {")", "&rpar;"},
-          {"*", "&ast;"},
-          {"+", "&plus;"},
-          {",", "&comma;"},
-          {"-", "&minus;"},
-          {".", "&period;"},
-          {"/", "&sol;"},
-          {":", "&colon;"},
-          {";", "&semi;"},
-          {"<", "&lt;"},
-          {"=", "&equals;"},
-          {">", "&gt;"},
-          {"?", "&quest;"},
-          {"@", "&commat;"},
-          {"[", "&lsqb;"},
-          {"\\", "&bsol;"},
-          {"]", "&rsqb;"},
-          {"^", "&circ;"},
-          {"_", "&lowbar;"},
-          {"`", "&grave;"},
-          {"{", "&lcub;"},
-          {"|", "&verbar;"},
-          {"}", "&rcub;"},
-          {"~", "&tilde;"},
-          {"¡", "&iexcl;"},
-          {"¢", "&cent;"},
-          {"£", "&pound;"},
-          {"¤", "&curren;"},
-          {"¥", "&yen;"},
-          {"¦", "&brkbar;"},
-          {"§", "&sect;"},
-          {"¨", "&uml;"},
-          {"©", "&copy;"},
-          {"ª", "&ordf;"},
-          {"«", "&laquo;"},
-          {"¬", "&not;"},
-          {"®", "&reg;"},
-          {"¯", "&macr;"},
-          {"°", "&deg;"},
-          {"±", "&plusmn;"},
-          {"²", "&sup2;"},
-          {"³", "&sup3;"},
-          {"´", "&acute;"},
-          {"µ", "&micro;"},
-          {"¶", "&para;"},
-          {"·", "&middot;"},
-          {"¸", "&cedil;"},
-          {"¹", "&sup1;"},
-          {"º", "&ordm;"},
-          {"»", "&raquo;;"},
-          {"¼", "&frac14;"},
-          {"½", "&frac12;"},
-          {"¾", "&frac34;"},
-          {"¿", "&iquest;"},
-          {"À", "&Agrave;"},
-          {"Á", "&Aacute;"},
-          {"Â", "&Acirc;"},
-          {"Ã", "&Atilde;"},
-          {"Ä", "&Auml;"},
-          {"Å", "&Aring;"},
-          {"Æ", "&AElig;"},
-          {"Ç", "&Ccedil;"},
-          {"È", "&Egrave;"},
-          {"É", "&Eacute;"},
-          {"Ê", "&Ecirc;"},
-          {"Ë", "&Euml;"},
-          {"Ì", "&Igrave;"},
-          {"Í", "&Iacute;"},
-          {"Î", "&Icirc;"},
-          {"Ï", "&Iuml;"},
-          {"Ð", "&ETH;"},
-          {"Ñ", "&Ntilde;"},
-          {"Ò", "&Ograve;"},
-          {"Ó", "&Oacute;"},
-          {"Ô", "&Ocirc;"},
-          {"Õ", "&Otilde;"},
-          {"Ö", "&Ouml;"},
-          {"×", "&times;"},
-          {"Ø", "&Oslash;"},
-          {"Ù", "&Ugrave;;"},
-          {"Ú", "&Uacute;"},
-          {"Û", "&Ucirc;"},
-          {"Ü", "&Uuml;"},
-          {"Ý", "&Yacute;"},
-          {"Þ", "&THORN;"},
-          {"ß", "&szlig;"},
-          {"à", "&agrave;"},
-          {"á", "&aacute;"},
-          {"â", "&acirc;"},
-          {"ã", "&atilde;"},
-          {"ä", "&auml;"},
-          {"å", "&aring;"},
-          {"æ", "&aelig;"},
-          {"ç", "&ccedil;"},
-          {"è", "&egrave;"},
-          {"é", "&eacute;"},
-          {"ê", "&ecirc;"},
-          {"ë", "&euml;"},
-          {"ì", "&igrave;"},
-          {"í", "&iacute;"},
-          {"î", "&icirc;"},
-          {"ï", "&iuml;"},
-          {"ð", "&eth;"},
-          {"ñ", "&ntilde;"},
-          {"ò", "&ograve;"},
-          {"ó", "&oacute;"},
-          {"ô", "&ocirc;"},
-          {"õ", "&otilde;"},
-          {"ö", "&ouml;"},
-          {"÷", "&divide;"},
-          {"ø", "&oslash;"},
-          {"ù", "&ugrave;"},
-          {"ú", "&uacute;"},
-          {"û", "&ucirc;"},
-          {"ü", "&uuml;"},
-          {"ý", "&yacute;"},
-          {"þ", "&thorn;"},
-          {"ÿ", "&yuml;"},
-          {"\u0192", "&fnof;"},
-          {"\u0391", "&Alpha;"},
-          {"\u0392", "&Beta;"},
-          {"\u0393", "&Gamma;"},
-          {"\u0394", "&Delta;"},
-          {"\u0395", "&Epsilon;"},
-          {"\u0396", "&Zeta;"},
-          {"\u0397", "&Eta;"},
-          {"\u0398", "&Theta;"},
-          {"\u0399", "&Iota;"},
-          {"\u039A", "&Kappa;"},
-          {"\u039B", "&Lambda;"},
-          {"\u039C", "&Mu;"},
-          {"\u039D", "&Nu;"},
-          {"\u039E", "&Xi;"},
-          {"\u039F", "&Omicron;"},
-          {"\u03A0", "&Pi;"},
-          {"\u03A1", "&Rho;"},
-          {"\u03A3", "&Sigma;"},
-          {"\u03A4", "&Tau;"},
-          {"\u03A5", "&Upsilon;"},
-          {"\u03A6", "&Phi;"},
-          {"\u03A7", "&Chi;"},
-          {"\u03A8", "&Psi;"},
-          {"\u03A9", "&Omega;"},
-          {"\u03B1", "&alpha;"},
-          {"\u03B2", "&beta;"},
-          {"\u03B3", "&gamma;"},
-          {"\u03B4", "&delta;"},
-          {"\u03B5", "&epsilon;"},
-          {"\u03B6", "&zeta;"},
-          {"\u03B7", "&eta;"},
-          {"\u03B8", "&theta;"},
-          {"\u03B9", "&iota;"},
-          {"\u03BA", "&kappa;"},
-          {"\u03BB", "&lambda;"},
-          {"\u03BC", "&mu;"},
-          {"\u03BD", "&nu;"},
-          {"\u03BE", "&xi;"},
-          {"\u03BF", "&omicron;"},
-          {"\u03C0", "&pi;"},
-          {"\u03C1", "&rho;"},
-          {"\u03C2", "&sigmaf;"},
-          {"\u03C3", "&sigma;"},
-          {"\u03C4", "&tau;"},
-          {"\u03C5", "&upsilon;"},
-          {"\u03C6", "&phi;"},
-          {"\u03C7", "&chi;"},
-          {"\u03C8", "&psi;"},
-          {"\u03C9", "&omega;"},
-          {"\u03D1", "&thetasym;"},
-          {"\u03D2", "&upsih;"},
-          {"\u03D6", "&piv;"},
-          {"\u2022", "&bull;"},
-          {"\u2026", "&hellip;"},
-          {"\u2032", "&prime;"},
-          {"\u2033", "&Prime;"},
-          {"\u203E", "&oline;"},
-          {"\u2044", "&frasl;"},
-          {"\u2118", "&weierp;"},
-          {"\u2111", "&image;"},
-          {"\u211C", "&real;"},
-          {"\u2122", "&trade;"},
-          {"\u2135", "&alefsym;"},
-          {"\u2190", "&larr;"},
-          {"\u2191", "&uarr;"},
-          {"\u2192", "&rarr;"},
-          {"\u2193", "&darr;"},
-          {"\u2194", "&harr;"},
-          {"\u21B5", "&crarr;"},
-          {"\u21D0", "&lArr;"},
-          {"\u21D1", "&uArr;"},
-          {"\u21D2", "&rArr;"},
-          {"\u21D3", "&dArr;"},
-          {"\u21D4", "&hArr;"},
-          {"\u2200", "&forall;"},
-          {"\u2202", "&part;"},
-          {"\u2203", "&exist;"},
-          {"\u2205", "&empty;"},
-          {"\u2207", "&nabla;"},
-          {"\u2208", "&isin;"},
-          {"\u2209", "&notin;"},
-          {"\u220B", "&ni;"},
-          {"\u220F", "&prod;"},
-          {"\u2211", "&sum;"},
-          {"\u2212", "&minus;"},
-          {"\u2217", "&lowast;"},
-          {"\u221A", "&radic;"},
-          {"\u221D", "&prop;"},
-          {"\u221E", "&infin;"},
-          {"\u2220", "&ang;"},
-          {"\u2227", "&and;"},
-          {"\u2228", "&or;"},
-          {"\u2229", "&cap;"},
-          {"\u222A", "&cup;"},
-          {"\u222B", "&int;"},
-          {"\u2234", "&there4;"},
-          {"\u223C", "&sim;"},
-          {"\u2245", "&cong;"},
-          {"\u2248", "&asymp;"},
-          {"\u2260", "&ne;"},
-          {"\u2261", "&equiv;"},
-          {"\u2264", "&le;"},
-          {"\u2265", "&ge;"},
-          {"\u2282", "&sub;"},
-          {"\u2283", "&sup;"},
-          {"\u2284", "&nsub;"},
-          {"\u2286", "&sube;"},
-          {"\u2287", "&supe;"},
-          {"\u2295", "&oplus;"},
-          {"\u2297", "&otimes;"},
-          {"\u22A5", "&perp;"},
-          {"\u22C5", "&sdot;"},
-          {"\u2308", "&lceil;"},
-          {"\u2309", "&rceil;"},
-          {"\u230A", "&lfloor;"},
-          {"\u230B", "&rfloor;"},
-          {"\u2329", "&lang;"},
-          {"\u232A", "&rang;"},
-          {"\u25CA", "&loz;"},
-          {"\u2660", "&spades;"},
-          {"\u2663", "&clubs;"},
-          {"\u2665", "&hearts;"},
-          {"\u2666", "&diams;"},
-          {"\u0152", "&OElig;"},
-          {"\u0153", "&oelig;"},
-          {"\u0160", "&Scaron;"},
-          {"\u0161", "&scaron;"},
-          {"\u0178", "&Yuml;"},
-          {"\u02C6", "&circ;"},
-          {"\u02DC", "&tilde;"},
-          {"\u2002", "&ensp;"},
-          {"\u2003", "&emsp;"},
-          {"\u2009", "&thinsp;"},
-          {"\u200C", "&zwnj;"},
-          {"\u200D", "&zwj;"},
-          {"\u200E", "&lrm;"},
-          {"\u200F", "&rlm;"},
-          {"\u2013", "&ndash;"},
-          {"\u2014", "&mdash;"},
-          {"\u2018", "&lsquo;"},
-          {"\u2019", "&rsquo;"},
-          {"\u201A", "&sbquo;"},
-          {"\u201C", "&ldquo;"},
-          {"\u201D", "&rdquo;"},
-          {"\u201E", "&bdquo;"},
-          {"\u2020", "&dagger;"},
-          {"\u2021", "&Dagger;"},
-          {"\u2030", "&permil;"},
-          {"\u2039", "&lsaquo;"},
-          {"\u203A", "&rsaquo;"},
-          {"\u20AC", "&euro;"}
-      }));
+  return Value(utils::StringUtils::replaceMap(args[0].asString(), { { "!", "&excl;" }, { "\"", "&quot;" }, { "#", "&num;" }, { "$", "&dollar;" }, { "%", "&percnt;" }, { "&", "&amp;" },
+                                                  { "'", "&apos;" }, { "(", "&lpar;" }, { ")", "&rpar;" }, { "*", "&ast;" }, { "+", "&plus;" }, { ",", "&comma;" }, { "-", "&minus;" }, { ".",
+                                                      "&period;" }, { "/", "&sol;" }, { ":", "&colon;" }, { ";", "&semi;" }, { "<", "&lt;" }, { "=", "&equals;" }, { ">", "&gt;" }, { "?", "&quest;" },
+                                                  { "@", "&commat;" }, { "[", "&lsqb;" }, { "\\", "&bsol;" }, { "]", "&rsqb;" }, { "^", "&circ;" }, { "_", "&lowbar;" }, { "`", "&grave;" }, { "{",
+                                                      "&lcub;" }, { "|", "&verbar;" }, { "}", "&rcub;" }, { "~", "&tilde;" }, { "¡", "&iexcl;" }, { "¢", "&cent;" }, { "£", "&pound;" }, { "¤",
+                                                      "&curren;" }, { "¥", "&yen;" }, { "¦", "&brkbar;" }, { "§", "&sect;" }, { "¨", "&uml;" }, { "©", "&copy;" }, { "ª", "&ordf;" },
+                                                  { "«", "&laquo;" }, { "¬", "&not;" }, { "®", "&reg;" }, { "¯", "&macr;" }, { "°", "&deg;" }, { "±", "&plusmn;" }, { "²", "&sup2;" },
+                                                  { "³", "&sup3;" }, { "´", "&acute;" }, { "µ", "&micro;" }, { "¶", "&para;" }, { "·", "&middot;" }, { "¸", "&cedil;" }, { "¹", "&sup1;" }, { "º",
+                                                      "&ordm;" }, { "»", "&raquo;;" }, { "¼", "&frac14;" }, { "½", "&frac12;" }, { "¾", "&frac34;" }, { "¿", "&iquest;" }, { "À", "&Agrave;" }, { "Á",
+                                                      "&Aacute;" }, { "Â", "&Acirc;" }, { "Ã", "&Atilde;" }, { "Ä", "&Auml;" }, { "Å", "&Aring;" }, { "Æ", "&AElig;" }, { "Ç", "&Ccedil;" }, { "È",
+                                                      "&Egrave;" }, { "É", "&Eacute;" }, { "Ê", "&Ecirc;" }, { "Ë", "&Euml;" }, { "Ì", "&Igrave;" }, { "Í", "&Iacute;" }, { "Î", "&Icirc;" }, { "Ï",
+                                                      "&Iuml;" }, { "Ð", "&ETH;" }, { "Ñ", "&Ntilde;" }, { "Ò", "&Ograve;" }, { "Ó", "&Oacute;" }, { "Ô", "&Ocirc;" }, { "Õ", "&Otilde;" }, { "Ö",
+                                                      "&Ouml;" }, { "×", "&times;" }, { "Ø", "&Oslash;" }, { "Ù", "&Ugrave;;" }, { "Ú", "&Uacute;" }, { "Û", "&Ucirc;" }, { "Ü", "&Uuml;" }, { "Ý",
+                                                      "&Yacute;" }, { "Þ", "&THORN;" }, { "ß", "&szlig;" }, { "à", "&agrave;" }, { "á", "&aacute;" }, { "â", "&acirc;" }, { "ã", "&atilde;" }, { "ä",
+                                                      "&auml;" }, { "å", "&aring;" }, { "æ", "&aelig;" }, { "ç", "&ccedil;" }, { "è", "&egrave;" }, { "é", "&eacute;" }, { "ê", "&ecirc;" }, { "ë",
+                                                      "&euml;" }, { "ì", "&igrave;" }, { "í", "&iacute;" }, { "î", "&icirc;" }, { "ï", "&iuml;" }, { "ð", "&eth;" }, { "ñ", "&ntilde;" }, { "ò",
+                                                      "&ograve;" }, { "ó", "&oacute;" }, { "ô", "&ocirc;" }, { "õ", "&otilde;" }, { "ö", "&ouml;" }, { "÷", "&divide;" }, { "ø", "&oslash;" }, { "ù",
+                                                      "&ugrave;" }, { "ú", "&uacute;" }, { "û", "&ucirc;" }, { "ü", "&uuml;" }, { "ý", "&yacute;" }, { "þ", "&thorn;" }, { "ÿ", "&yuml;" }, { "\u0192",
+                                                      "&fnof;" }, { "\u0391", "&Alpha;" }, { "\u0392", "&Beta;" }, { "\u0393", "&Gamma;" }, { "\u0394", "&Delta;" }, { "\u0395", "&Epsilon;" }, {
+                                                      "\u0396", "&Zeta;" }, { "\u0397", "&Eta;" }, { "\u0398", "&Theta;" }, { "\u0399", "&Iota;" }, { "\u039A", "&Kappa;" }, { "\u039B", "&Lambda;" }, {
+                                                      "\u039C", "&Mu;" }, { "\u039D", "&Nu;" }, { "\u039E", "&Xi;" }, { "\u039F", "&Omicron;" }, { "\u03A0", "&Pi;" }, { "\u03A1", "&Rho;" }, {
+                                                      "\u03A3", "&Sigma;" }, { "\u03A4", "&Tau;" }, { "\u03A5", "&Upsilon;" }, { "\u03A6", "&Phi;" }, { "\u03A7", "&Chi;" }, { "\u03A8", "&Psi;" }, {
+                                                      "\u03A9", "&Omega;" }, { "\u03B1", "&alpha;" }, { "\u03B2", "&beta;" }, { "\u03B3", "&gamma;" }, { "\u03B4", "&delta;" },
+                                                  { "\u03B5", "&epsilon;" }, { "\u03B6", "&zeta;" }, { "\u03B7", "&eta;" }, { "\u03B8", "&theta;" }, { "\u03B9", "&iota;" }, { "\u03BA", "&kappa;" }, {
+                                                      "\u03BB", "&lambda;" }, { "\u03BC", "&mu;" }, { "\u03BD", "&nu;" }, { "\u03BE", "&xi;" }, { "\u03BF", "&omicron;" }, { "\u03C0", "&pi;" }, {
+                                                      "\u03C1", "&rho;" }, { "\u03C2", "&sigmaf;" }, { "\u03C3", "&sigma;" }, { "\u03C4", "&tau;" }, { "\u03C5", "&upsilon;" }, { "\u03C6", "&phi;" }, {
+                                                      "\u03C7", "&chi;" }, { "\u03C8", "&psi;" }, { "\u03C9", "&omega;" }, { "\u03D1", "&thetasym;" }, { "\u03D2", "&upsih;" }, { "\u03D6", "&piv;" }, {
+                                                      "\u2022", "&bull;" }, { "\u2026", "&hellip;" }, { "\u2032", "&prime;" }, { "\u2033", "&Prime;" }, { "\u203E", "&oline;" },
+                                                  { "\u2044", "&frasl;" }, { "\u2118", "&weierp;" }, { "\u2111", "&image;" }, { "\u211C", "&real;" }, { "\u2122", "&trade;" },
+                                                  { "\u2135", "&alefsym;" }, { "\u2190", "&larr;" }, { "\u2191", "&uarr;" }, { "\u2192", "&rarr;" }, { "\u2193", "&darr;" }, { "\u2194", "&harr;" }, {
+                                                      "\u21B5", "&crarr;" }, { "\u21D0", "&lArr;" }, { "\u21D1", "&uArr;" }, { "\u21D2", "&rArr;" }, { "\u21D3", "&dArr;" }, { "\u21D4", "&hArr;" }, {
+                                                      "\u2200", "&forall;" }, { "\u2202", "&part;" }, { "\u2203", "&exist;" }, { "\u2205", "&empty;" }, { "\u2207", "&nabla;" }, { "\u2208", "&isin;" },
+                                                  { "\u2209", "&notin;" }, { "\u220B", "&ni;" }, { "\u220F", "&prod;" }, { "\u2211", "&sum;" }, { "\u2212", "&minus;" }, { "\u2217", "&lowast;" }, {
+                                                      "\u221A", "&radic;" }, { "\u221D", "&prop;" }, { "\u221E", "&infin;" }, { "\u2220", "&ang;" }, { "\u2227", "&and;" }, { "\u2228", "&or;" }, {
+                                                      "\u2229", "&cap;" }, { "\u222A", "&cup;" }, { "\u222B", "&int;" }, { "\u2234", "&there4;" }, { "\u223C", "&sim;" }, { "\u2245", "&cong;" }, {
+                                                      "\u2248", "&asymp;" }, { "\u2260", "&ne;" }, { "\u2261", "&equiv;" }, { "\u2264", "&le;" }, { "\u2265", "&ge;" }, { "\u2282", "&sub;" }, {
+                                                      "\u2283", "&sup;" }, { "\u2284", "&nsub;" }, { "\u2286", "&sube;" }, { "\u2287", "&supe;" }, { "\u2295", "&oplus;" }, { "\u2297", "&otimes;" }, {
+                                                      "\u22A5", "&perp;" }, { "\u22C5", "&sdot;" }, { "\u2308", "&lceil;" }, { "\u2309", "&rceil;" }, { "\u230A", "&lfloor;" },
+                                                  { "\u230B", "&rfloor;" }, { "\u2329", "&lang;" }, { "\u232A", "&rang;" }, { "\u25CA", "&loz;" }, { "\u2660", "&spades;" }, { "\u2663", "&clubs;" }, {
+                                                      "\u2665", "&hearts;" }, { "\u2666", "&diams;" }, { "\u0152", "&OElig;" }, { "\u0153", "&oelig;" }, { "\u0160", "&Scaron;" }, { "\u0161",
+                                                      "&scaron;" }, { "\u0178", "&Yuml;" }, { "\u02C6", "&circ;" }, { "\u02DC", "&tilde;" }, { "\u2002", "&ensp;" }, { "\u2003", "&emsp;" }, { "\u2009",
+                                                      "&thinsp;" }, { "\u200C", "&zwnj;" }, { "\u200D", "&zwj;" }, { "\u200E", "&lrm;" }, { "\u200F", "&rlm;" }, { "\u2013", "&ndash;" }, { "\u2014",
+                                                      "&mdash;" }, { "\u2018", "&lsquo;" }, { "\u2019", "&rsquo;" }, { "\u201A", "&sbquo;" }, { "\u201C", "&ldquo;" }, { "\u201D", "&rdquo;" }, {
+                                                      "\u201E", "&bdquo;" }, { "\u2020", "&dagger;" }, { "\u2021", "&Dagger;" }, { "\u2030", "&permil;" }, { "\u2039", "&lsaquo;" }, { "\u203A",
+                                                      "&rsaquo;" }, { "\u20AC", "&euro;" } }));
 }
 
 Value expr_unescapeHtml3(const std::vector<Value> &args) {
-  return Value(utils::StringUtils::replaceMap(
-      args[0].asString(),
-      {
-          {"&excl;", "!"},
-          {"&quot;", "\""},
-          {"&num;", "#"},
-          {"&dollar;", "$"},
-          {"&percnt;", "%"},
-          {"&amp;", "&"},
-          {"&apos;", "'"},
-          {"&lpar;", "("},
-          {"&rpar;", ")"},
-          {"&ast;", "*"},
-          {"&plus;", "+"},
-          {"&comma;", ","},
-          {"&minus;", "-"},
-          {"&period;", "."},
-          {"&sol;", "/"},
-          {"&colon;", ":"},
-          {"&semi;", ";"},
-          {"&lt;", "<"},
-          {"&equals;", "="},
-          {"&gt;", ">"},
-          {"&quest;", "?"},
-          {"&commat;", "@"},
-          {"&lsqb;", "["},
-          {"&bsol;", "\\"},
-          {"&rsqb;", "]"},
-          {"&circ;", "^"},
-          {"&lowbar;", "_"},
-          {"&grave;", "`"},
-          {"&lcub;", "{"},
-          {"&verbar;", "|"},
-          {"&rcub;", "}"},
-          {"&tilde;", "~"},
-          {"&iexcl;", "¡"},
-          {"&cent;", "¢"},
-          {"&pound;", "£"},
-          {"&curren;", "¤"},
-          {"&yen;", "¥"},
-          {"&brkbar;", "¦"},
-          {"&sect;", "§"},
-          {"&uml;", "¨"},
-          {"&copy;", "©"},
-          {"&ordf;", "ª"},
-          {"&laquo;", "«"},
-          {"&not;", "¬"},
-          {"&reg;", "®"},
-          {"&macr;", "¯"},
-          {"&deg;", "°"},
-          {"&plusmn;", "±"},
-          {"&sup2;", "²"},
-          {"&sup3;", "³"},
-          {"&acute;", "´"},
-          {"&micro;", "µ"},
-          {"&para;", "¶"},
-          {"&middot;", "·"},
-          {"&cedil;", "¸"},
-          {"&sup1;", "¹"},
-          {"&ordm;", "º"},
-          {"&raquo;;", "»"},
-          {"&frac14;", "¼"},
-          {"&frac12;", "½"},
-          {"&frac34;", "¾"},
-          {"&iquest;", "¿"},
-          {"&Agrave;", "À"},
-          {"&Aacute;", "Á"},
-          {"&Acirc;", "Â"},
-          {"&Atilde;", "Ã"},
-          {"&Auml;", "Ä"},
-          {"&Aring;", "Å"},
-          {"&AElig;", "Æ"},
-          {"&Ccedil;", "Ç"},
-          {"&Egrave;", "È"},
-          {"&Eacute;", "É"},
-          {"&Ecirc;", "Ê"},
-          {"&Euml;", "Ë"},
-          {"&Igrave;", "Ì"},
-          {"&Iacute;", "Í"},
-          {"&Icirc;", "Î"},
-          {"&Iuml;", "Ï"},
-          {"&ETH;", "Ð"},
-          {"&Ntilde;", "Ñ"},
-          {"&Ograve;", "Ò"},
-          {"&Oacute;", "Ó"},
-          {"&Ocirc;", "Ô"},
-          {"&Otilde;", "Õ"},
-          {"&Ouml;", "Ö"},
-          {"&times;", "×"},
-          {"&Oslash;", "Ø"},
-          {"&Ugrave;;", "Ù"},
-          {"&Uacute;", "Ú"},
-          {"&Ucirc;", "Û"},
-          {"&Uuml;", "Ü"},
-          {"&Yacute;", "Ý"},
-          {"&THORN;", "Þ"},
-          {"&szlig;", "ß"},
-          {"&agrave;", "à"},
-          {"&aacute;", "á"},
-          {"&acirc;", "â"},
-          {"&atilde;", "ã"},
-          {"&auml;", "ä"},
-          {"&aring;", "å"},
-          {"&aelig;", "æ"},
-          {"&ccedil;", "ç"},
-          {"&egrave;", "è"},
-          {"&eacute;", "é"},
-          {"&ecirc;", "ê"},
-          {"&euml;", "ë"},
-          {"&igrave;", "ì"},
-          {"&iacute;", "í"},
-          {"&icirc;", "î"},
-          {"&iuml;", "ï"},
-          {"&eth;", "ð"},
-          {"&ntilde;", "ñ"},
-          {"&ograve;", "ò"},
-          {"&oacute;", "ó"},
-          {"&ocirc;", "ô"},
-          {"&otilde;", "õ"},
-          {"&ouml;", "ö"},
-          {"&divide;", "÷"},
-          {"&oslash;", "ø"},
-          {"&ugrave;", "ù"},
-          {"&uacute;", "ú"},
-          {"&ucirc;", "û"},
-          {"&uuml;", "ü"},
-          {"&yacute;", "ý"},
-          {"&thorn;", "þ"},
-          {"&yuml;", "ÿ"}
-      }));
+  return Value(utils::StringUtils::replaceMap(args[0].asString(), { { "&excl;", "!" }, { "&quot;", "\"" }, { "&num;", "#" }, { "&dollar;", "$" }, { "&percnt;", "%" }, { "&amp;", "&" },
+                                                  { "&apos;", "'" }, { "&lpar;", "(" }, { "&rpar;", ")" }, { "&ast;", "*" }, { "&plus;", "+" }, { "&comma;", "," }, { "&minus;", "-" }, { "&period;",
+                                                      "." }, { "&sol;", "/" }, { "&colon;", ":" }, { "&semi;", ";" }, { "&lt;", "<" }, { "&equals;", "=" }, { "&gt;", ">" }, { "&quest;", "?" }, {
+                                                      "&commat;", "@" }, { "&lsqb;", "[" }, { "&bsol;", "\\" }, { "&rsqb;", "]" }, { "&circ;", "^" }, { "&lowbar;", "_" }, { "&grave;", "`" }, {
+                                                      "&lcub;", "{" }, { "&verbar;", "|" }, { "&rcub;", "}" }, { "&tilde;", "~" }, { "&iexcl;", "¡" }, { "&cent;", "¢" }, { "&pound;", "£" }, {
+                                                      "&curren;", "¤" }, { "&yen;", "¥" }, { "&brkbar;", "¦" }, { "&sect;", "§" }, { "&uml;", "¨" }, { "&copy;", "©" }, { "&ordf;", "ª" }, { "&laquo;",
+                                                      "«" }, { "&not;", "¬" }, { "&reg;", "®" }, { "&macr;", "¯" }, { "&deg;", "°" }, { "&plusmn;", "±" }, { "&sup2;", "²" }, { "&sup3;", "³" }, {
+                                                      "&acute;", "´" }, { "&micro;", "µ" }, { "&para;", "¶" }, { "&middot;", "·" }, { "&cedil;", "¸" }, { "&sup1;", "¹" }, { "&ordm;", "º" }, {
+                                                      "&raquo;;", "»" }, { "&frac14;", "¼" }, { "&frac12;", "½" }, { "&frac34;", "¾" }, { "&iquest;", "¿" }, { "&Agrave;", "À" }, { "&Aacute;", "Á" }, {
+                                                      "&Acirc;", "Â" }, { "&Atilde;", "Ã" }, { "&Auml;", "Ä" }, { "&Aring;", "Å" }, { "&AElig;", "Æ" }, { "&Ccedil;", "Ç" }, { "&Egrave;", "È" }, {
+                                                      "&Eacute;", "É" }, { "&Ecirc;", "Ê" }, { "&Euml;", "Ë" }, { "&Igrave;", "Ì" }, { "&Iacute;", "Í" }, { "&Icirc;", "Î" }, { "&Iuml;", "Ï" }, {
+                                                      "&ETH;", "Ð" }, { "&Ntilde;", "Ñ" }, { "&Ograve;", "Ò" }, { "&Oacute;", "Ó" }, { "&Ocirc;", "Ô" }, { "&Otilde;", "Õ" }, { "&Ouml;", "Ö" }, {
+                                                      "&times;", "×" }, { "&Oslash;", "Ø" }, { "&Ugrave;;", "Ù" }, { "&Uacute;", "Ú" }, { "&Ucirc;", "Û" }, { "&Uuml;", "Ü" }, { "&Yacute;", "Ý" }, {
+                                                      "&THORN;", "Þ" }, { "&szlig;", "ß" }, { "&agrave;", "à" }, { "&aacute;", "á" }, { "&acirc;", "â" }, { "&atilde;", "ã" }, { "&auml;", "ä" }, {
+                                                      "&aring;", "å" }, { "&aelig;", "æ" }, { "&ccedil;", "ç" }, { "&egrave;", "è" }, { "&eacute;", "é" }, { "&ecirc;", "ê" }, { "&euml;", "ë" }, {
+                                                      "&igrave;", "ì" }, { "&iacute;", "í" }, { "&icirc;", "î" }, { "&iuml;", "ï" }, { "&eth;", "ð" }, { "&ntilde;", "ñ" }, { "&ograve;", "ò" }, {
+                                                      "&oacute;", "ó" }, { "&ocirc;", "ô" }, { "&otilde;", "õ" }, { "&ouml;", "ö" }, { "&divide;", "÷" }, { "&oslash;", "ø" }, { "&ugrave;", "ù" }, {
+                                                      "&uacute;", "ú" }, { "&ucirc;", "û" }, { "&uuml;", "ü" }, { "&yacute;", "ý" }, { "&thorn;", "þ" }, { "&yuml;", "ÿ" } }));
 }
 
 Value expr_unescapeHtml4(const std::vector<Value> &args) {
-  return Value(utils::StringUtils::replaceMap(
-      args[0].asString(),
-      {
-          {"&excl;", "!"},
-          {"&quot;", "\""},
-          {"&num;", "#"},
-          {"&dollar;", "$"},
-          {"&percnt;", "%"},
-          {"&amp;", "&"},
-          {"&apos;", "'"},
-          {"&lpar;", "("},
-          {"&rpar;", ")"},
-          {"&ast;", "*"},
-          {"&plus;", "+"},
-          {"&comma;", ","},
-          {"&minus;", "-"},
-          {"&period;", "."},
-          {"&sol;", "/"},
-          {"&colon;", ":"},
-          {"&semi;", ";"},
-          {"&lt;", "<"},
-          {"&equals;", "="},
-          {"&gt;", ">"},
-          {"&quest;", "?"},
-          {"&commat;", "@"},
-          {"&lsqb;", "["},
-          {"&bsol;", "\\"},
-          {"&rsqb;", "]"},
-          {"&circ;", "^"},
-          {"&lowbar;", "_"},
-          {"&grave;", "`"},
-          {"&lcub;", "{"},
-          {"&verbar;", "|"},
-          {"&rcub;", "}"},
-          {"&tilde;", "~"},
-          {"&iexcl;", "¡"},
-          {"&cent;", "¢"},
-          {"&pound;", "£"},
-          {"&curren;", "¤"},
-          {"&yen;", "¥"},
-          {"&brkbar;", "¦"},
-          {"&sect;", "§"},
-          {"&uml;", "¨"},
-          {"&copy;", "©"},
-          {"&ordf;", "ª"},
-          {"&laquo;", "«"},
-          {"&not;", "¬"},
-          {"&reg;", "®"},
-          {"&macr;", "¯"},
-          {"&deg;", "°"},
-          {"&plusmn;", "±"},
-          {"&sup2;", "²"},
-          {"&sup3;", "³"},
-          {"&acute;", "´"},
-          {"&micro;", "µ"},
-          {"&para;", "¶"},
-          {"&middot;", "·"},
-          {"&cedil;", "¸"},
-          {"&sup1;", "¹"},
-          {"&ordm;", "º"},
-          {"&raquo;;", "»"},
-          {"&frac14;", "¼"},
-          {"&frac12;", "½"},
-          {"&frac34;", "¾"},
-          {"&iquest;", "¿"},
-          {"&Agrave;", "À"},
-          {"&Aacute;", "Á"},
-          {"&Acirc;", "Â"},
-          {"&Atilde;", "Ã"},
-          {"&Auml;", "Ä"},
-          {"&Aring;", "Å"},
-          {"&AElig;", "Æ"},
-          {"&Ccedil;", "Ç"},
-          {"&Egrave;", "È"},
-          {"&Eacute;", "É"},
-          {"&Ecirc;", "Ê"},
-          {"&Euml;", "Ë"},
-          {"&Igrave;", "Ì"},
-          {"&Iacute;", "Í"},
-          {"&Icirc;", "Î"},
-          {"&Iuml;", "Ï"},
-          {"&ETH;", "Ð"},
-          {"&Ntilde;", "Ñ"},
-          {"&Ograve;", "Ò"},
-          {"&Oacute;", "Ó"},
-          {"&Ocirc;", "Ô"},
-          {"&Otilde;", "Õ"},
-          {"&Ouml;", "Ö"},
-          {"&times;", "×"},
-          {"&Oslash;", "Ø"},
-          {"&Ugrave;;", "Ù"},
-          {"&Uacute;", "Ú"},
-          {"&Ucirc;", "Û"},
-          {"&Uuml;", "Ü"},
-          {"&Yacute;", "Ý"},
-          {"&THORN;", "Þ"},
-          {"&szlig;", "ß"},
-          {"&agrave;", "à"},
-          {"&aacute;", "á"},
-          {"&acirc;", "â"},
-          {"&atilde;", "ã"},
-          {"&auml;", "ä"},
-          {"&aring;", "å"},
-          {"&aelig;", "æ"},
-          {"&ccedil;", "ç"},
-          {"&egrave;", "è"},
-          {"&eacute;", "é"},
-          {"&ecirc;", "ê"},
-          {"&euml;", "ë"},
-          {"&igrave;", "ì"},
-          {"&iacute;", "í"},
-          {"&icirc;", "î"},
-          {"&iuml;", "ï"},
-          {"&eth;", "ð"},
-          {"&ntilde;", "ñ"},
-          {"&ograve;", "ò"},
-          {"&oacute;", "ó"},
-          {"&ocirc;", "ô"},
-          {"&otilde;", "õ"},
-          {"&ouml;", "ö"},
-          {"&divide;", "÷"},
-          {"&oslash;", "ø"},
-          {"&ugrave;", "ù"},
-          {"&uacute;", "ú"},
-          {"&ucirc;", "û"},
-          {"&uuml;", "ü"},
-          {"&yacute;", "ý"},
-          {"&thorn;", "þ"},
-          {"&yuml;", "ÿ"},
-          {"&fnof;", "\u0192"},
-          {"&Alpha;", "\u0391"},
-          {"&Beta;", "\u0392"},
-          {"&Gamma;", "\u0393"},
-          {"&Delta;", "\u0394"},
-          {"&Epsilon;", "\u0395"},
-          {"&Zeta;", "\u0396"},
-          {"&Eta;", "\u0397"},
-          {"&Theta;", "\u0398"},
-          {"&Iota;", "\u0399"},
-          {"&Kappa;", "\u039A"},
-          {"&Lambda;", "\u039B"},
-          {"&Mu;", "\u039C"},
-          {"&Nu;", "\u039D"},
-          {"&Xi;", "\u039E"},
-          {"&Omicron;", "\u039F"},
-          {"&Pi;", "\u03A0"},
-          {"&Rho;", "\u03A1"},
-          {"&Sigma;", "\u03A3"},
-          {"&Tau;", "\u03A4"},
-          {"&Upsilon;", "\u03A5"},
-          {"&Phi;", "\u03A6"},
-          {"&Chi;", "\u03A7"},
-          {"&Psi;", "\u03A8"},
-          {"&Omega;", "\u03A9"},
-          {"&alpha;", "\u03B1"},
-          {"&beta;", "\u03B2"},
-          {"&gamma;", "\u03B3"},
-          {"&delta;", "\u03B4"},
-          {"&epsilon;", "\u03B5"},
-          {"&zeta;", "\u03B6"},
-          {"&eta;", "\u03B7"},
-          {"&theta;", "\u03B8"},
-          {"&iota;", "\u03B9"},
-          {"&kappa;", "\u03BA"},
-          {"&lambda;", "\u03BB"},
-          {"&mu;", "\u03BC"},
-          {"&nu;", "\u03BD"},
-          {"&xi;", "\u03BE"},
-          {"&omicron;", "\u03BF"},
-          {"&pi;", "\u03C0"},
-          {"&rho;", "\u03C1"},
-          {"&sigmaf;", "\u03C2"},
-          {"&sigma;", "\u03C3"},
-          {"&tau;", "\u03C4"},
-          {"&upsilon;", "\u03C5"},
-          {"&phi;", "\u03C6"},
-          {"&chi;", "\u03C7"},
-          {"&psi;", "\u03C8"},
-          {"&omega;", "\u03C9"},
-          {"&thetasym;", "\u03D1"},
-          {"&upsih;", "\u03D2"},
-          {"&piv;", "\u03D6"},
-          {"&bull;", "\u2022"},
-          {"&hellip;", "\u2026"},
-          {"&prime;", "\u2032"},
-          {"&Prime;", "\u2033"},
-          {"&oline;", "\u203E"},
-          {"&frasl;", "\u2044"},
-          {"&weierp;", "\u2118"},
-          {"&image;", "\u2111"},
-          {"&real;", "\u211C"},
-          {"&trade;", "\u2122"},
-          {"&alefsym;", "\u2135"},
-          {"&larr;", "\u2190"},
-          {"&uarr;", "\u2191"},
-          {"&rarr;", "\u2192"},
-          {"&darr;", "\u2193"},
-          {"&harr;", "\u2194"},
-          {"&crarr;", "\u21B5"},
-          {"&lArr;", "\u21D0"},
-          {"&uArr;", "\u21D1"},
-          {"&rArr;", "\u21D2"},
-          {"&dArr;", "\u21D3"},
-          {"&hArr;", "\u21D4"},
-          {"&forall;", "\u2200"},
-          {"&part;", "\u2202"},
-          {"&exist;", "\u2203"},
-          {"&empty;", "\u2205"},
-          {"&nabla;", "\u2207"},
-          {"&isin;", "\u2208"},
-          {"&notin;", "\u2209"},
-          {"&ni;", "\u220B"},
-          {"&prod;", "\u220F"},
-          {"&sum;", "\u2211"},
-          {"&minus;", "\u2212"},
-          {"&lowast;", "\u2217"},
-          {"&radic;", "\u221A"},
-          {"&prop;", "\u221D"},
-          {"&infin;", "\u221E"},
-          {"&ang;", "\u2220"},
-          {"&and;", "\u2227"},
-          {"&or;", "\u2228"},
-          {"&cap;", "\u2229"},
-          {"&cup;", "\u222A"},
-          {"&int;", "\u222B"},
-          {"&there4;", "\u2234"},
-          {"&sim;", "\u223C"},
-          {"&cong;", "\u2245"},
-          {"&asymp;", "\u2248"},
-          {"&ne;", "\u2260"},
-          {"&equiv;", "\u2261"},
-          {"&le;", "\u2264"},
-          {"&ge;", "\u2265"},
-          {"&sub;", "\u2282"},
-          {"&sup;", "\u2283"},
-          {"&nsub;", "\u2284"},
-          {"&sube;", "\u2286"},
-          {"&supe;", "\u2287"},
-          {"&oplus;", "\u2295"},
-          {"&otimes;", "\u2297"},
-          {"&perp;", "\u22A5"},
-          {"&sdot;", "\u22C5"},
-          {"&lceil;", "\u2308"},
-          {"&rceil;", "\u2309"},
-          {"&lfloor;", "\u230A"},
-          {"&rfloor;", "\u230B"},
-          {"&lang;", "\u2329"},
-          {"&rang;", "\u232A"},
-          {"&loz;", "\u25CA"},
-          {"&spades;", "\u2660"},
-          {"&clubs;", "\u2663"},
-          {"&hearts;", "\u2665"},
-          {"&diams;", "\u2666"},
-          {"&OElig;", "\u0152"},
-          {"&oelig;", "\u0153"},
-          {"&Scaron;", "\u0160"},
-          {"&scaron;", "\u0161"},
-          {"&Yuml;", "\u0178"},
-          {"&circ;", "\u02C6"},
-          {"&tilde;", "\u02DC"},
-          {"&ensp;", "\u2002"},
-          {"&emsp;", "\u2003"},
-          {"&thinsp;", "\u2009"},
-          {"&zwnj;", "\u200C"},
-          {"&zwj;", "\u200D"},
-          {"&lrm;", "\u200E"},
-          {"&rlm;", "\u200F"},
-          {"&ndash;", "\u2013"},
-          {"&mdash;", "\u2014"},
-          {"&lsquo;", "\u2018"},
-          {"&rsquo;", "\u2019"},
-          {"&sbquo;", "\u201A"},
-          {"&ldquo;", "\u201C"},
-          {"&rdquo;", "\u201D"},
-          {"&bdquo;", "\u201E"},
-          {"&dagger;", "\u2020"},
-          {"&Dagger;", "\u2021"},
-          {"&permil;", "\u2030"},
-          {"&lsaquo;", "\u2039"},
-          {"&rsaquo;", "\u203A"},
-          {"&euro;", "\u20AC"}
-      }));
+  return Value(utils::StringUtils::replaceMap(args[0].asString(), { { "&excl;", "!" }, { "&quot;", "\"" }, { "&num;", "#" }, { "&dollar;", "$" }, { "&percnt;", "%" }, { "&amp;", "&" },
+                                                  { "&apos;", "'" }, { "&lpar;", "(" }, { "&rpar;", ")" }, { "&ast;", "*" }, { "&plus;", "+" }, { "&comma;", "," }, { "&minus;", "-" }, { "&period;",
+                                                      "." }, { "&sol;", "/" }, { "&colon;", ":" }, { "&semi;", ";" }, { "&lt;", "<" }, { "&equals;", "=" }, { "&gt;", ">" }, { "&quest;", "?" }, {
+                                                      "&commat;", "@" }, { "&lsqb;", "[" }, { "&bsol;", "\\" }, { "&rsqb;", "]" }, { "&circ;", "^" }, { "&lowbar;", "_" }, { "&grave;", "`" }, {
+                                                      "&lcub;", "{" }, { "&verbar;", "|" }, { "&rcub;", "}" }, { "&tilde;", "~" }, { "&iexcl;", "¡" }, { "&cent;", "¢" }, { "&pound;", "£" }, {
+                                                      "&curren;", "¤" }, { "&yen;", "¥" }, { "&brkbar;", "¦" }, { "&sect;", "§" }, { "&uml;", "¨" }, { "&copy;", "©" }, { "&ordf;", "ª" }, { "&laquo;",
+                                                      "«" }, { "&not;", "¬" }, { "&reg;", "®" }, { "&macr;", "¯" }, { "&deg;", "°" }, { "&plusmn;", "±" }, { "&sup2;", "²" }, { "&sup3;", "³" }, {
+                                                      "&acute;", "´" }, { "&micro;", "µ" }, { "&para;", "¶" }, { "&middot;", "·" }, { "&cedil;", "¸" }, { "&sup1;", "¹" }, { "&ordm;", "º" }, {
+                                                      "&raquo;;", "»" }, { "&frac14;", "¼" }, { "&frac12;", "½" }, { "&frac34;", "¾" }, { "&iquest;", "¿" }, { "&Agrave;", "À" }, { "&Aacute;", "Á" }, {
+                                                      "&Acirc;", "Â" }, { "&Atilde;", "Ã" }, { "&Auml;", "Ä" }, { "&Aring;", "Å" }, { "&AElig;", "Æ" }, { "&Ccedil;", "Ç" }, { "&Egrave;", "È" }, {
+                                                      "&Eacute;", "É" }, { "&Ecirc;", "Ê" }, { "&Euml;", "Ë" }, { "&Igrave;", "Ì" }, { "&Iacute;", "Í" }, { "&Icirc;", "Î" }, { "&Iuml;", "Ï" }, {
+                                                      "&ETH;", "Ð" }, { "&Ntilde;", "Ñ" }, { "&Ograve;", "Ò" }, { "&Oacute;", "Ó" }, { "&Ocirc;", "Ô" }, { "&Otilde;", "Õ" }, { "&Ouml;", "Ö" }, {
+                                                      "&times;", "×" }, { "&Oslash;", "Ø" }, { "&Ugrave;;", "Ù" }, { "&Uacute;", "Ú" }, { "&Ucirc;", "Û" }, { "&Uuml;", "Ü" }, { "&Yacute;", "Ý" }, {
+                                                      "&THORN;", "Þ" }, { "&szlig;", "ß" }, { "&agrave;", "à" }, { "&aacute;", "á" }, { "&acirc;", "â" }, { "&atilde;", "ã" }, { "&auml;", "ä" }, {
+                                                      "&aring;", "å" }, { "&aelig;", "æ" }, { "&ccedil;", "ç" }, { "&egrave;", "è" }, { "&eacute;", "é" }, { "&ecirc;", "ê" }, { "&euml;", "ë" }, {
+                                                      "&igrave;", "ì" }, { "&iacute;", "í" }, { "&icirc;", "î" }, { "&iuml;", "ï" }, { "&eth;", "ð" }, { "&ntilde;", "ñ" }, { "&ograve;", "ò" }, {
+                                                      "&oacute;", "ó" }, { "&ocirc;", "ô" }, { "&otilde;", "õ" }, { "&ouml;", "ö" }, { "&divide;", "÷" }, { "&oslash;", "ø" }, { "&ugrave;", "ù" }, {
+                                                      "&uacute;", "ú" }, { "&ucirc;", "û" }, { "&uuml;", "ü" }, { "&yacute;", "ý" }, { "&thorn;", "þ" }, { "&yuml;", "ÿ" }, { "&fnof;", "\u0192" }, {
+                                                      "&Alpha;", "\u0391" }, { "&Beta;", "\u0392" }, { "&Gamma;", "\u0393" }, { "&Delta;", "\u0394" }, { "&Epsilon;", "\u0395" },
+                                                  { "&Zeta;", "\u0396" }, { "&Eta;", "\u0397" }, { "&Theta;", "\u0398" }, { "&Iota;", "\u0399" }, { "&Kappa;", "\u039A" }, { "&Lambda;", "\u039B" }, {
+                                                      "&Mu;", "\u039C" }, { "&Nu;", "\u039D" }, { "&Xi;", "\u039E" }, { "&Omicron;", "\u039F" }, { "&Pi;", "\u03A0" }, { "&Rho;", "\u03A1" }, {
+                                                      "&Sigma;", "\u03A3" }, { "&Tau;", "\u03A4" }, { "&Upsilon;", "\u03A5" }, { "&Phi;", "\u03A6" }, { "&Chi;", "\u03A7" }, { "&Psi;", "\u03A8" }, {
+                                                      "&Omega;", "\u03A9" }, { "&alpha;", "\u03B1" }, { "&beta;", "\u03B2" }, { "&gamma;", "\u03B3" }, { "&delta;", "\u03B4" },
+                                                  { "&epsilon;", "\u03B5" }, { "&zeta;", "\u03B6" }, { "&eta;", "\u03B7" }, { "&theta;", "\u03B8" }, { "&iota;", "\u03B9" }, { "&kappa;", "\u03BA" }, {
+                                                      "&lambda;", "\u03BB" }, { "&mu;", "\u03BC" }, { "&nu;", "\u03BD" }, { "&xi;", "\u03BE" }, { "&omicron;", "\u03BF" }, { "&pi;", "\u03C0" }, {
+                                                      "&rho;", "\u03C1" }, { "&sigmaf;", "\u03C2" }, { "&sigma;", "\u03C3" }, { "&tau;", "\u03C4" }, { "&upsilon;", "\u03C5" }, { "&phi;", "\u03C6" }, {
+                                                      "&chi;", "\u03C7" }, { "&psi;", "\u03C8" }, { "&omega;", "\u03C9" }, { "&thetasym;", "\u03D1" }, { "&upsih;", "\u03D2" }, { "&piv;", "\u03D6" }, {
+                                                      "&bull;", "\u2022" }, { "&hellip;", "\u2026" }, { "&prime;", "\u2032" }, { "&Prime;", "\u2033" }, { "&oline;", "\u203E" },
+                                                  { "&frasl;", "\u2044" }, { "&weierp;", "\u2118" }, { "&image;", "\u2111" }, { "&real;", "\u211C" }, { "&trade;", "\u2122" },
+                                                  { "&alefsym;", "\u2135" }, { "&larr;", "\u2190" }, { "&uarr;", "\u2191" }, { "&rarr;", "\u2192" }, { "&darr;", "\u2193" }, { "&harr;", "\u2194" }, {
+                                                      "&crarr;", "\u21B5" }, { "&lArr;", "\u21D0" }, { "&uArr;", "\u21D1" }, { "&rArr;", "\u21D2" }, { "&dArr;", "\u21D3" }, { "&hArr;", "\u21D4" }, {
+                                                      "&forall;", "\u2200" }, { "&part;", "\u2202" }, { "&exist;", "\u2203" }, { "&empty;", "\u2205" }, { "&nabla;", "\u2207" }, { "&isin;", "\u2208" },
+                                                  { "&notin;", "\u2209" }, { "&ni;", "\u220B" }, { "&prod;", "\u220F" }, { "&sum;", "\u2211" }, { "&minus;", "\u2212" }, { "&lowast;", "\u2217" }, {
+                                                      "&radic;", "\u221A" }, { "&prop;", "\u221D" }, { "&infin;", "\u221E" }, { "&ang;", "\u2220" }, { "&and;", "\u2227" }, { "&or;", "\u2228" }, {
+                                                      "&cap;", "\u2229" }, { "&cup;", "\u222A" }, { "&int;", "\u222B" }, { "&there4;", "\u2234" }, { "&sim;", "\u223C" }, { "&cong;", "\u2245" }, {
+                                                      "&asymp;", "\u2248" }, { "&ne;", "\u2260" }, { "&equiv;", "\u2261" }, { "&le;", "\u2264" }, { "&ge;", "\u2265" }, { "&sub;", "\u2282" }, {
+                                                      "&sup;", "\u2283" }, { "&nsub;", "\u2284" }, { "&sube;", "\u2286" }, { "&supe;", "\u2287" }, { "&oplus;", "\u2295" }, { "&otimes;", "\u2297" }, {
+                                                      "&perp;", "\u22A5" }, { "&sdot;", "\u22C5" }, { "&lceil;", "\u2308" }, { "&rceil;", "\u2309" }, { "&lfloor;", "\u230A" },
+                                                  { "&rfloor;", "\u230B" }, { "&lang;", "\u2329" }, { "&rang;", "\u232A" }, { "&loz;", "\u25CA" }, { "&spades;", "\u2660" }, { "&clubs;", "\u2663" }, {
+                                                      "&hearts;", "\u2665" }, { "&diams;", "\u2666" }, { "&OElig;", "\u0152" }, { "&oelig;", "\u0153" }, { "&Scaron;", "\u0160" }, { "&scaron;",
+                                                      "\u0161" }, { "&Yuml;", "\u0178" }, { "&circ;", "\u02C6" }, { "&tilde;", "\u02DC" }, { "&ensp;", "\u2002" }, { "&emsp;", "\u2003" }, { "&thinsp;",
+                                                      "\u2009" }, { "&zwnj;", "\u200C" }, { "&zwj;", "\u200D" }, { "&lrm;", "\u200E" }, { "&rlm;", "\u200F" }, { "&ndash;", "\u2013" }, { "&mdash;",
+                                                      "\u2014" }, { "&lsquo;", "\u2018" }, { "&rsquo;", "\u2019" }, { "&sbquo;", "\u201A" }, { "&ldquo;", "\u201C" }, { "&rdquo;", "\u201D" }, {
+                                                      "&bdquo;", "\u201E" }, { "&dagger;", "\u2020" }, { "&Dagger;", "\u2021" }, { "&permil;", "\u2030" }, { "&lsaquo;", "\u2039" }, { "&rsaquo;",
+                                                      "\u203A" }, { "&euro;", "\u20AC" } }));
 }
 
 Value expr_escapeXml(const std::vector<Value> &args) {
-  return Value(utils::StringUtils::replaceMap(
-      args[0].asString(),
-      {
-          {"\"", "&quot;"},
-          {"'", "&apos;"},
-          {"<", "&lt;"},
-          {">", "&gt;"},
-          {"&", "&amp;"}
-      }));
+  return Value(utils::StringUtils::replaceMap(args[0].asString(), { { "\"", "&quot;" }, { "'", "&apos;" }, { "<", "&lt;" }, { ">", "&gt;" }, { "&", "&amp;" } }));
 }
 
 Value expr_unescapeXml(const std::vector<Value> &args) {
-  return Value(utils::StringUtils::replaceMap(
-      args[0].asString(),
-      {
-          {"&quot;", "\""},
-          {"&apos;", "'"},
-          {"&lt;", "<"},
-          {"&gt;", ">"},
-          {"&amp;", "&"}
-      }));
+  return Value(utils::StringUtils::replaceMap(args[0].asString(), { { "&quot;", "\"" }, { "&apos;", "'" }, { "&lt;", "<" }, { "&gt;", ">" }, { "&amp;", "&" } }));
 }
 
 Value expr_escapeCsv(const std::vector<Value> &args) {
   auto result = args[0].asString();
-  const char quote_req_chars[] = {'"', '\r', '\n', ','};
+  const char quote_req_chars[] = { '"', '\r', '\n', ',' };
   bool quote_required = false;
 
   for (const auto &c : quote_req_chars) {
@@ -1275,7 +561,7 @@ Value expr_escapeCsv(const std::vector<Value> &args) {
 
   if (quote_required) {
     std::string quoted_result = "\"";
-    quoted_result.append(utils::StringUtils::replaceMap(result, {{"\"", "\"\""}}));
+    quoted_result.append(utils::StringUtils::replaceMap(result, { { "\"", "\"\"" } }));
     quoted_result.append("\"");
     return Value(quoted_result);
   }
@@ -1300,7 +586,7 @@ Value expr_format(const std::vector<Value> &args) {
 
 Value expr_toDate(const std::vector<Value> &args) {
   auto arg_0 = args[0].asString();
-  std::istringstream arg_s{arg_0};
+  std::istringstream arg_s { arg_0 };
   date::sys_time<std::chrono::milliseconds> t;
   arg_s >> date::parse(args[1].asString(), t);
   auto zone = date::current_zone();
@@ -1330,7 +616,7 @@ Value expr_unescapeCsv(const std::vector<Value> &args) {
     if (quote_pos != result.length() - 1) {
       quote_required = true;
     } else {
-      const char quote_req_chars[] = {'\r', '\n', ','};
+      const char quote_req_chars[] = { '\r', '\n', ',' };
 
       for (const auto &c : quote_req_chars) {
         if (result.find(c) != std::string::npos) {
@@ -1341,7 +627,7 @@ Value expr_unescapeCsv(const std::vector<Value> &args) {
     }
 
     if (quote_required) {
-      return Value(utils::StringUtils::replaceMap(result.substr(1, result.size() - 2), {{"\"\"", "\""}}));
+      return Value(utils::StringUtils::replaceMap(result.substr(1, result.size() - 2), { { "\"\"", "\"" } }));
     }
   }
 
@@ -1353,9 +639,7 @@ Value expr_urlEncode(const std::vector<Value> &args) {
   auto arg_0 = args[0].asString();
   CURL *curl = curl_easy_init();
   if (curl != nullptr) {
-    char *output = curl_easy_escape(curl,
-                                    arg_0.c_str(),
-                                    static_cast<int>(arg_0.length()));
+    char *output = curl_easy_escape(curl, arg_0.c_str(), static_cast<int>(arg_0.length()));
     if (output != nullptr) {
       auto result = std::string(output);
       curl_free(output);
@@ -1379,10 +663,7 @@ Value expr_urlDecode(const std::vector<Value> &args) {
   CURL *curl = curl_easy_init();
   if (curl != nullptr) {
     int out_len;
-    char *output = curl_easy_unescape(curl,
-                                      arg_0.c_str(),
-                                      static_cast<int>(arg_0.length()),
-                                      &out_len);
+    char *output = curl_easy_unescape(curl, arg_0.c_str(), static_cast<int>(arg_0.length()), &out_len);
     if (output != nullptr) {
       auto result = std::string(output, static_cast<unsigned long>(out_len));
       curl_free(output);
@@ -1492,8 +773,8 @@ Value expr_find(const std::vector<Value> &args) {
 
 Value expr_trim(const std::vector<Value> &args) {
   std::string result = args[0].asString();
-  auto ws_front = std::find_if_not(result.begin(), result.end(), [](int c) { return std::isspace(c); });
-  auto ws_back = std::find_if_not(result.rbegin(), result.rend(), [](int c) { return std::isspace(c); }).base();
+  auto ws_front = std::find_if_not(result.begin(), result.end(), [](int c) {return std::isspace(c);});
+  auto ws_back = std::find_if_not(result.rbegin(), result.rend(), [](int c) {return std::isspace(c);}).base();
   return (ws_back <= ws_front ? Value(std::string()) : Value(std::string(ws_front, ws_back)));
 }
 
@@ -1512,10 +793,7 @@ Value expr_length(const std::vector<Value> &args) {
   return Value(len);
 }
 
-Value expr_binary_op(const std::vector<Value> &args,
-                     long double (*ldop)(long double, long double),
-                     int64_t (*iop)(int64_t, int64_t),
-                     bool long_only = false) {
+Value expr_binary_op(const std::vector<Value> &args, long double (*ldop)(long double, long double), int64_t (*iop)(int64_t, int64_t), bool long_only = false) {
   try {
     if (!long_only && !args[0].isDecimal() && !args[1].isDecimal()) {
       return Value(iop(args[0].asSignedLong(), args[1].asSignedLong()));
@@ -1528,34 +806,23 @@ Value expr_binary_op(const std::vector<Value> &args,
 }
 
 Value expr_plus(const std::vector<Value> &args) {
-  return expr_binary_op(args,
-                        [](long double a, long double b) { return a + b; },
-                        [](int64_t a, int64_t b) { return a + b; });
+  return expr_binary_op(args, [](long double a, long double b) {return a + b;}, [](int64_t a, int64_t b) {return a + b;});
 }
 
 Value expr_minus(const std::vector<Value> &args) {
-  return expr_binary_op(args,
-                        [](long double a, long double b) { return a - b; },
-                        [](int64_t a, int64_t b) { return a - b; });
+  return expr_binary_op(args, [](long double a, long double b) {return a - b;}, [](int64_t a, int64_t b) {return a - b;});
 }
 
 Value expr_multiply(const std::vector<Value> &args) {
-  return expr_binary_op(args,
-                        [](long double a, long double b) { return a * b; },
-                        [](int64_t a, int64_t b) { return a * b; });
+  return expr_binary_op(args, [](long double a, long double b) {return a * b;}, [](int64_t a, int64_t b) {return a * b;});
 }
 
 Value expr_divide(const std::vector<Value> &args) {
-  return expr_binary_op(args,
-                        [](long double a, long double b) { return a / b; },
-                        [](int64_t a, int64_t b) { return a / b; },
-                        true);
+  return expr_binary_op(args, [](long double a, long double b) {return a / b;}, [](int64_t a, int64_t b) {return a / b;}, true);
 }
 
 Value expr_mod(const std::vector<Value> &args) {
-  return expr_binary_op(args,
-                        [](long double a, long double b) { return std::fmod(a, b); },
-                        [](int64_t a, int64_t b) { return a % b; });
+  return expr_binary_op(args, [](long double a, long double b) {return std::fmod(a, b);}, [](int64_t a, int64_t b) {return a % b;});
 }
 
 Value expr_toRadix(const std::vector<Value> &args) {
@@ -1580,8 +847,7 @@ Value expr_toRadix(const std::vector<Value> &args) {
     sign = "-";
   }
 
-  const char chars[] =
-      "0123456789ab"
+  const char chars[] = "0123456789ab"
       "cdefghijklmn"
       "opqrstuvwxyz";
   std::string str_num;
@@ -1616,19 +882,11 @@ Value expr_random(const std::vector<Value> &args) {
 }
 
 template<Value T(const std::vector<Value> &)>
-Expression make_dynamic_function_incomplete(const std::string &function_name,
-                                            const std::vector<Expression> &args,
-                                            std::size_t num_args) {
+Expression make_dynamic_function_incomplete(const std::string &function_name, const std::vector<Expression> &args, std::size_t num_args) {
 
   if (args.size() < num_args) {
     std::stringstream message_ss;
-    message_ss << "Expression language function "
-               << function_name
-               << " called with "
-               << args.size()
-               << " argument(s), but "
-               << num_args
-               << " are required";
+    message_ss << "Expression language function " << function_name << " called with " << args.size() << " argument(s), but " << num_args << " are required";
     throw std::runtime_error(message_ss.str());
   }
 
@@ -1641,7 +899,8 @@ Expression make_dynamic_function_incomplete(const std::string &function_name,
 
     return args[0].compose_multi([=](const std::vector<Value> &args) -> Value {
       return T(args);
-    }, multi_args);
+    },
+                                 multi_args);
   } else {
     return make_dynamic([=](const Parameters &params, const std::vector<Expression> &sub_exprs) -> Value {
       std::vector<Value> evaluated_args;
@@ -1675,12 +934,7 @@ Value expr_isEmpty(const std::vector<Value> &args) {
   std::string arg_0 = args[0].asString();
 
   for (char c : arg_0) {
-    if (c != ' '
-        && c != '\f'
-        && c != '\n'
-        && c != '\r'
-        && c != '\t'
-        && c != '\v') {
+    if (c != ' ' && c != '\f' && c != '\n' && c != '\r' && c != '\t' && c != '\v') {
       return Value(false);
     }
   }
@@ -1757,13 +1011,7 @@ Value expr_ifElse(const std::vector<Value> &args) {
 Expression make_allAttributes(const std::string &function_name, const std::vector<Expression> &args) {
   if (args.size() < 1) {
     std::stringstream message_ss;
-    message_ss << "Expression language function "
-               << function_name
-               << " called with "
-               << args.size()
-               << " argument(s), but "
-               << 1
-               << " are required";
+    message_ss << "Expression language function " << function_name << " called with " << args.size() << " argument(s), but " << 1 << " are required";
     throw std::runtime_error(message_ss.str());
   }
 
@@ -1784,19 +1032,19 @@ Expression make_allAttributes(const std::string &function_name, const std::vecto
 
     for (const auto &arg : args) {
       out_exprs.emplace_back(make_dynamic([=](const Parameters &params,
-                                              const std::vector<Expression> &sub_exprs) -> Value {
-        std::string attr_id;
-        attr_id = arg(params).asString();
-        std::string attr_val;
-
-        const auto cur_flow_file = params.flow_file.lock();
-
-        if (cur_flow_file && cur_flow_file->getAttribute(attr_id, attr_val)) {
-          return Value(attr_val);
-        } else {
-          return Value();
-        }
-      }));
+                  const std::vector<Expression> &sub_exprs) -> Value {
+                std::string attr_id;
+                attr_id = arg(params).asString();
+                std::string attr_val;
+
+                const auto cur_flow_file = params.flow_file.lock();
+
+                if (cur_flow_file && cur_flow_file->getAttribute(attr_id, attr_val)) {
+                  return Value(attr_val);
+                } else {
+                  return Value();
+                }
+              }));
     }
 
     return out_exprs;
@@ -1808,13 +1056,7 @@ Expression make_allAttributes(const std::string &function_name, const std::vecto
 Expression make_anyAttribute(const std::string &function_name, const std::vector<Expression> &args) {
   if (args.size() < 1) {
     std::stringstream message_ss;
-    message_ss << "Expression language function "
-               << function_name
-               << " called with "
-               << args.size()
-               << " argument(s), but "
-               << 1
-               << " are required";
+    message_ss << "Expression language function " << function_name << " called with " << args.size() << " argument(s), but " << 1 << " are required";
     throw std::runtime_error(message_ss.str());
   }
 
@@ -1835,19 +1077,19 @@ Expression make_anyAttribute(const std::string &function_name, const std::vector
 
     for (const auto &arg : args) {
       out_exprs.emplace_back(make_dynamic([=](const Parameters &params,
-                                              const std::vector<Expression> &sub_exprs) -> Value {
-        std::string attr_id;
-        attr_id = arg(params).asString();
-        std::string attr_val;
-
-        const auto cur_flow_file = params.flow_file.lock();
-
-        if (cur_flow_file && cur_flow_file->getAttribute(attr_id, attr_val)) {
-          return Value(attr_val);
-        } else {
-          return Value();
-        }
-      }));
+                  const std::vector<Expression> &sub_exprs) -> Value {
+                std::string attr_id;
+                attr_id = arg(params).asString();
+                std::string attr_val;
+
+                const auto cur_flow_file = params.flow_file.lock();
+
+                if (cur_flow_file && cur_flow_file->getAttribute(attr_id, attr_val)) {
+                  return Value(attr_val);
+                } else {
+                  return Value();
+                }
+              }));
     }
 
     return out_exprs;
@@ -1861,13 +1103,7 @@ Expression make_anyAttribute(const std::string &function_name, const std::vector
 Expression make_allMatchingAttributes(const std::string &function_name, const std::vector<Expression> &args) {
   if (args.size() < 1) {
     std::stringstream message_ss;
-    message_ss << "Expression language function "
-               << function_name
-               << " called with "
-               << args.size()
-               << " argument(s), but "
-               << 1
-               << " are required";
+    message_ss << "Expression language function " << function_name << " called with " << args.size() << " argument(s), but " << 1 << " are required";
     throw std::runtime_error(message_ss.str());
   }
 
@@ -1898,15 +1134,15 @@ Expression make_allMatchingAttributes(const std::string &function_name, const st
       for (const auto &attr : attrs) {
         if (std::regex_match(attr.first.begin(), attr.first.end(), attr_regex)) {
           out_exprs.emplace_back(make_dynamic([=](const Parameters &params,
-                                                  const std::vector<Expression> &sub_exprs) -> Value {
-            std::string attr_val;
-
-            if (cur_flow_file && cur_flow_file->getAttribute(attr.first, attr_val)) {
-              return Value(attr_val);
-            } else {
-              return Value();
-            }
-          }));
+                      const std::vector<Expression> &sub_exprs) -> Value {
+                    std::string attr_val;
+
+                    if (cur_flow_file && cur_flow_file->getAttribute(attr.first, attr_val)) {
+                      return Value(attr_val);
+                    } else {
+                      return Value();
+                    }
+                  }));
         }
       }
     }
@@ -1920,13 +1156,7 @@ Expression make_allMatchingAttributes(const std::string &function_name, const st
 Expression make_anyMatchingAttribute(const std::string &function_name, const std::vector<Expression> &args) {
   if (args.size() < 1) {
     std::stringstream message_ss;
-    message_ss << "Expression language function "
-               << function_name
-               << " called with "
-               << args.size()
-               << " argument(s), but "
-               << 1
-               << " are required";
+    message_ss << "Expression language function " << function_name << " called with " << args.size() << " argument(s), but " << 1 << " are required";
     throw std::runtime_error(message_ss.str());
   }
 
@@ -1957,15 +1187,15 @@ Expression make_anyMatchingAttribute(const std::string &function_name, const std
       for (const auto &attr : attrs) {
         if (std::regex_match(attr.first.begin(), attr.first.end(), attr_regex)) {
           out_exprs.emplace_back(make_dynamic([=](const Parameters &params,
-                                                  const std::vector<Expression> &sub_exprs) -> Value {
-            std::string attr_val;
-
-            if (cur_flow_file && cur_flow_file->getAttribute(attr.first, attr_val)) {
-              return Value(attr_val);
-            } else {
-              return Value();
-            }
-          }));
+                      const std::vector<Expression> &sub_exprs) -> Value {
+                    std::string attr_val;
+
+                    if (cur_flow_file && cur_flow_file->getAttribute(attr.first, attr_val)) {
+                      return Value(attr_val);
+                    } else {
+                      return Value();
+                    }
+                  }));
         }
       }
     }
@@ -1981,13 +1211,7 @@ Expression make_anyMatchingAttribute(const std::string &function_name, const std
 Expression make_allDelineatedValues(const std::string &function_name, const std::vector<Expression> &args) {
   if (args.size() != 2) {
     std::stringstream message_ss;
-    message_ss << "Expression language function "
-               << function_name
-               << " called with "
-               << args.size()
-               << " argument(s), but "
-               << 2
-               << " are required";
+    message_ss << "Expression language function " << function_name << " called with " << args.size() << " argument(s), but " << 2 << " are required";
     throw std::runtime_error(message_ss.str());
   }
 
@@ -2019,13 +1243,7 @@ Expression make_allDelineatedValues(const std::string &function_name, const std:
 Expression make_anyDelineatedValue(const std::string &function_name, const std::vector<Expression> &args) {
   if (args.size() != 2) {
     std::stringstream message_ss;
-    message_ss << "Expression language function "
-               << function_name
-               << " called with "
-               << args.size()
-               << " argument(s), but "
-               << 2
-               << " are required";
+    message_ss << "Expression language function " << function_name << " called with " << args.size() << " argument(s), but " << 2 << " are required";
     throw std::runtime_error(message_ss.str());
   }
 
@@ -2057,26 +1275,18 @@ Expression make_anyDelineatedValue(const std::string &function_name, const std::
 Expression make_count(const std::string &function_name, const std::vector<Expression> &args) {
   if (args.size() != 1) {
     std::stringstream message_ss;
-    message_ss << "Expression language function "
-               << function_name
-               << " called with "
-               << args.size()
-               << " argument(s), but "
-               << 1
-               << " are required";
+    message_ss << "Expression language function " << function_name << " called with " << args.size() << " argument(s), but " << 1 << " are required";
     throw std::runtime_error(message_ss.str());
   }
 
   if (!args[0].is_multi()) {
     std::stringstream message_ss;
-    message_ss << "Expression language function "
-               << function_name
-               << " called against singular expression.";
+    message_ss << "Expression language function " << function_name << " called against singular expression.";
     throw std::runtime_error(message_ss.str());
   }
 
   return args[0].make_aggregate([](const Parameters &params,
-                                   const std::vector<Expression> &sub_exprs) -> Value {
+      const std::vector<Expression> &sub_exprs) -> Value {
     uint64_t count = 0;
     for (const auto &sub_expr : sub_exprs) {
       if (sub_expr(params).asBoolean()) {
@@ -2090,28 +1300,20 @@ Expression make_count(const std::string &function_name, const std::vector<Expres
 Expression make_join(const std::string &function_name, const std::vector<Expression> &args) {
   if (args.size() != 2) {
     std::stringstream message_ss;
-    message_ss << "Expression language function "
-               << function_name
-               << " called with "
-               << args.size()
-               << " argument(s), but "
-               << 2
-               << " are required";
+    message_ss << "Expression language function " << function_name << " called with " << args.size() << " argument(s), but " << 2 << " are required";
     throw std::runtime_error(message_ss.str());
   }
 
   if (!args[0].is_multi()) {
     std::stringstream message_ss;
-    message_ss << "Expression language function "
-               << function_name
-               << " called against singular expression.";
+    message_ss << "Expression language function " << function_name << " called against singular expression.";
     throw std::runtime_error(message_ss.str());
   }
 
   auto delim_expr = args[1];
 
   return args[0].make_aggregate([delim_expr](const Parameters &params,
-                                             const std::vector<Expression> &sub_exprs) -> Value {
+      const std::vector<Expression> &sub_exprs) -> Value {
     std::string delim = delim_expr(params).asString();
     std::stringstream out_ss;
     bool first = true;
@@ -2128,8 +1330,7 @@ Expression make_join(const std::string &function_name, const std::vector<Express
   });
 }
 
-Expression make_dynamic_function(const std::string &function_name,
-                                 const std::vector<Expression> &args) {
+Expression make_dynamic_function(const std::string &function_name, const std::vector<Expression> &args) {
   if (function_name == "hostname") {
     return make_dynamic_function_incomplete<expr_hostname>(function_name, args, 0);
   } else if (function_name == "ip") {
@@ -2291,13 +1492,12 @@ Expression make_dynamic_function(const std::string &function_name,
   }
 }
 
-Expression make_function_composition(const Expression &arg,
-                                     const std::vector<std::pair<std::string, std::vector<Expression>>> &chain) {
+Expression make_function_composition(const Expression &arg, const std::vector<std::pair<std::string, std::vector<Expression>>> &chain) {
 
   auto expr = arg;
 
   for (const auto &chain_part : chain) {
-    std::vector<Expression> complete_args = {expr};
+    std::vector<Expression> complete_args = { expr };
     complete_args.insert(complete_args.end(), chain_part.second.begin(), chain_part.second.end());
     expr = make_dynamic_function(chain_part.first, complete_args);
   }
@@ -2320,10 +1520,10 @@ Expression Expression::operator+(const Expression &other_expr) const {
     auto sub_expr_generator = sub_expr_generator_;
     auto other_sub_expr_generator = other_expr.sub_expr_generator_;
     return make_dynamic([val_fn,
-                            other_val_fn,
-                            sub_expr_generator,
-                            other_sub_expr_generator](const Parameters &params,
-                                                      const std::vector<Expression> &sub_exprs) -> Value {
+    other_val_fn,
+    sub_expr_generator,
+    other_sub_expr_generator](const Parameters &params,
+        const std::vector<Expression> &sub_exprs) -> Value {
       Value result = val_fn(params, sub_expr_generator(params));
       return Value(result.asString().append(other_val_fn(params, other_sub_expr_generator(params)).asString()));
     });
@@ -2332,9 +1532,9 @@ Expression Expression::operator+(const Expression &other_expr) const {
     auto other_val = other_expr.val_;
     auto sub_expr_generator = sub_expr_generator_;
     return make_dynamic([val_fn,
-                            other_val,
-                            sub_expr_generator](const Parameters &params,
-                                                const std::vector<Expression> &sub_exprs) -> Value {
+    other_val,
+    sub_expr_generator](const Parameters &params,
+        const std::vector<Expression> &sub_exprs) -> Value {
       Value result = val_fn(params, sub_expr_generator(params));
       return Value(result.asString().append(other_val.asString()));
     });
@@ -2343,9 +1543,9 @@ Expression Expression::operator+(const Expression &other_expr) const {
     auto other_val_fn = other_expr.val_fn_;
     auto other_sub_expr_generator = other_expr.sub_expr_generator_;
     return make_dynamic([val,
-                            other_val_fn,
-                            other_sub_expr_generator](const Parameters &params,
-                                                      const std::vector<Expression> &sub_exprs) -> Value {
+    other_val_fn,
+    other_sub_expr_generator](const Parameters &params,
+        const std::vector<Expression> &sub_exprs) -> Value {
       Value result(val);
       return Value(result.asString().append(other_val_fn(params, other_sub_expr_generator(params)).asString()));
     });
@@ -2366,27 +1566,26 @@ Value Expression::operator()(const Parameters &params) const {
   }
 }
 
-Expression Expression::compose_multi(const std::function<Value(const std::vector<Value> &)> fn,
-                                     const std::vector<Expression> &args) const {
+Expression Expression::compose_multi(const std::function<Value(const std::vector<Value> &)> fn, const std::vector<Expression> &args) const {
   auto result = make_dynamic(val_fn_);
   auto compose_expr_generator = sub_expr_generator_;
 
   result.sub_expr_generator_ = [=](const Parameters &params) -> std::vector<Expression> {
     auto sub_exprs = compose_expr_generator(params);
-    std::vector<Expression> out_exprs{};
+    std::vector<Expression> out_exprs {};
     for (const auto &sub_expr : sub_exprs) {
       out_exprs.emplace_back(make_dynamic([=](const Parameters &params,
-                                              const std::vector<Expression> &sub_exprs) {
-        std::vector<Value> evaluated_args;
+                  const std::vector<Expression> &sub_exprs) {
+                std::vector<Value> evaluated_args;
 
-        evaluated_args.emplace_back(sub_expr(params));
+                evaluated_args.emplace_back(sub_expr(params));
 
-        for (const auto &arg : args) {
-          evaluated_args.emplace_back(arg(params));
-        }
+                for (const auto &arg : args) {
+                  evaluated_args.emplace_back(arg(params));
+                }
 
-        return fn(evaluated_args);
-      }));
+                return fn(evaluated_args);
+              }));
     }
     return out_exprs;
   };
@@ -2396,12 +1595,11 @@ Expression Expression::compose_multi(const std::function<Value(const std::vector
   return result;
 }
 
-Expression Expression::make_aggregate(std::function<Value(const Parameters &params,
-                                                          const std::vector<Expression> &sub_exprs)> val_fn) const {
+Expression Expression::make_aggregate(std::function<Value(const Parameters &params, const std::vector<Expression> &sub_exprs)> val_fn) const {
   auto sub_expr_generator = sub_expr_generator_;
   return make_dynamic([sub_expr_generator,
-                          val_fn](const Parameters &params,
-                                  const std::vector<Expression> &sub_exprs) -> Value {
+  val_fn](const Parameters &params,
+      const std::vector<Expression> &sub_exprs) -> Value {
     return val_fn(params, sub_expr_generator(params));
   });
 }
diff --git a/extensions/expression-language/ProcessContextExpr.cpp b/extensions/expression-language/ProcessContextExpr.cpp
index 71c0b36..928029f 100644
--- a/extensions/expression-language/ProcessContextExpr.cpp
+++ b/extensions/expression-language/ProcessContextExpr.cpp
@@ -16,7 +16,7 @@
  */
 
 #include <core/ProcessContext.h>
-
+#include <memory>
 namespace org {
 namespace apache {
 namespace nifi {
@@ -24,8 +24,9 @@ namespace minifi {
 namespace core {
 
 bool ProcessContext::getProperty(const Property &property, std::string &value, const std::shared_ptr<FlowFile> &flow_file) {
-  if (!property.supportsExpressionLangauge())
+  if (!property.supportsExpressionLangauge()) {
     return getProperty(property.getName(), value);
+  }
   auto name = property.getName();
   if (expressions_.find(name) == expressions_.end()) {
     std::string expression_str;
@@ -34,17 +35,16 @@ bool ProcessContext::getProperty(const Property &property, std::string &value, c
     expressions_.emplace(name, expression::compile(expression_str));
   }
 
-  minifi::expression::Parameters p(flow_file);
-  p.configuration = content_repo_->getConfig();
-
-  value = expressions_[name]( p ).asString();
+  minifi::expression::Parameters p(shared_from_this(), flow_file);
+  value = expressions_[name](p).asString();
   return true;
 }
 
 bool ProcessContext::getDynamicProperty(const Property &property, std::string &value, const std::shared_ptr<FlowFile> &flow_file) {
 
-  if (!property.supportsExpressionLangauge())
+  if (!property.supportsExpressionLangauge()) {
     return getDynamicProperty(property.getName(), value);
+  }
   auto name = property.getName();
   if (dynamic_property_expressions_.find(name) == dynamic_property_expressions_.end()) {
     std::string expression_str;
@@ -52,11 +52,8 @@ bool ProcessContext::getDynamicProperty(const Property &property, std::string &v
     logger_->log_debug("Compiling expression for %s/%s: %s", getProcessorNode()->getName(), name, expression_str);
     dynamic_property_expressions_.emplace(name, expression::compile(expression_str));
   }
-
-  minifi::expression::Parameters p(flow_file);
-  p.configuration = content_repo_->getConfig();
-
-  value = dynamic_property_expressions_[name]( p ).asString();
+  minifi::expression::Parameters p(shared_from_this(), flow_file);
+  value = dynamic_property_expressions_[name](p).asString();
   return true;
 }
 
diff --git a/extensions/expression-language/common/Value.h b/extensions/expression-language/common/Value.h
index d71517b..1666637 100644
--- a/extensions/expression-language/common/Value.h
+++ b/extensions/expression-language/common/Value.h
@@ -182,7 +182,7 @@ class Value {
     if (is_unsigned_long_) {
       return unsigned_long_val_;
     } else if (is_string_) {
-      return std::stoul(string_val_);
+      return string_val_.empty() ? 0 : std::stoul(string_val_);
     } else if (is_signed_long_) {
       return signed_long_val_;
     } else if (is_long_double_) {
@@ -198,7 +198,7 @@ class Value {
     } else if (is_unsigned_long_) {
       return unsigned_long_val_;
     } else if (is_string_) {
-      return std::stoul(string_val_);
+      return string_val_.empty() ? 0 : std::stol(string_val_);
     } else if (is_long_double_) {
       return long_double_val_;
     } else {
@@ -214,7 +214,7 @@ class Value {
     } else if (is_long_double_) {
       return long_double_val_;
     } else if (is_string_) {
-      return std::stold(string_val_);
+      return string_val_.empty() ? 0 : std::stold(string_val_);
     } else {
       return 0.0;
     }
diff --git a/extensions/expression-language/impl/expression/Expression.h b/extensions/expression-language/impl/expression/Expression.h
index 826f588..ac987f7 100644
--- a/extensions/expression-language/impl/expression/Expression.h
+++ b/extensions/expression-language/impl/expression/Expression.h
@@ -34,6 +34,7 @@
 
 #include <Value.h>
 #include <core/FlowFile.h>
+#include <core/VariableRegistry.h>
 
 #include <string>
 #include <memory>
@@ -47,7 +48,12 @@ namespace expression {
 
 struct Parameters {
   std::weak_ptr<core::FlowFile> flow_file;
-  std::weak_ptr<minifi::Configure> configuration;
+  std::weak_ptr<core::VariableRegistry> registry_;
+  Parameters(std::shared_ptr<core::VariableRegistry> reg, std::shared_ptr<core::FlowFile> ff = nullptr)
+      : registry_(reg) {
+    flow_file = ff;
+  }
+
   Parameters(std::shared_ptr<core::FlowFile> ff = nullptr) {
     flow_file = ff;
   }
@@ -68,13 +74,12 @@ class Expression {
     val_fn_ = NOOP_FN;
   }
 
-  explicit Expression(Value val,
-                      std::function<Value(const Parameters &, const std::vector<Expression> &)> val_fn = NOOP_FN)
+  explicit Expression(Value val, std::function<Value(const Parameters &, const std::vector<Expression> &)> val_fn = NOOP_FN)
       : val_fn_(std::move(val_fn)),
         fn_args_(),
         is_multi_(false) {
     val_ = val;
-    sub_expr_generator_ = [](const Parameters &params) -> std::vector<Expression> { return {}; };
+    sub_expr_generator_ = [](const Parameters &params) -> std::vector<Expression> {return {};};
   }
 
   /**
@@ -127,11 +132,9 @@ class Expression {
    * @param args function arguments
    * @return composed multi-expression
    */
-  Expression compose_multi(const std::function<Value(const std::vector<Value> &)> fn,
-                           const std::vector<Expression> &args) const;
+  Expression compose_multi(const std::function<Value(const std::vector<Value> &)> fn, const std::vector<Expression> &args) const;
 
-  Expression make_aggregate(std::function<Value(const Parameters &params,
-                                                const std::vector<Expression> &sub_exprs)> val_fn) const;
+  Expression make_aggregate(std::function<Value(const Parameters &params, const std::vector<Expression> &sub_exprs)> val_fn) const;
 
  protected:
   Value val_;
@@ -163,8 +166,7 @@ Expression make_static(std::string val);
  * @param val_fn
  * @return
  */
-Expression make_dynamic(const std::function<Value(const Parameters &params,
-                                                  const std::vector<Expression> &sub_exprs)> &val_fn);
+Expression make_dynamic(const std::function<Value(const Parameters &params, const std::vector<Expression> &sub_exprs)> &val_fn);
 
 /**
  * Creates a dynamic expression which evaluates the given flow file attribute.
@@ -191,8 +193,7 @@ Expression make_dynamic_function(const std::string &function_name, const std::ve
  * @param chain
  * @return
  */
-Expression make_function_composition(const Expression &arg,
-                                     const std::vector<std::pair<std::string, std::vector<Expression>>> &chain);
+Expression make_function_composition(const Expression &arg, const std::vector<std::pair<std::string, std::vector<Expression>>> &chain);
 
 } /* namespace expression */
 } /* namespace minifi */
diff --git a/extensions/expression-language/noop/expression/Expression.h b/extensions/expression-language/noop/expression/Expression.h
index 5cbebfe..0716d35 100644
--- a/extensions/expression-language/noop/expression/Expression.h
+++ b/extensions/expression-language/noop/expression/Expression.h
@@ -19,6 +19,7 @@
 #define NIFI_MINIFI_CPP_EXPRESSION_H
 
 #include <core/FlowFile.h>
+#include <core/VariableRegistry.h>
 #include <Value.h>
 
 namespace org {
@@ -29,6 +30,7 @@ namespace expression {
 
 typedef struct {
   std::weak_ptr<core::FlowFile> flow_file;
+  std::shared_ptr<core::VariableRegistry> registry_;
 } Parameters;
 
 /**
@@ -36,7 +38,6 @@ typedef struct {
  */
 class Expression {
  public:
-
   explicit Expression(std::string, std::function<std::string(const Parameters &)>);
 };
 
diff --git a/extensions/http-curl/client/HTTPClient.cpp b/extensions/http-curl/client/HTTPClient.cpp
index 3940918..6ef19f2 100644
--- a/extensions/http-curl/client/HTTPClient.cpp
+++ b/extensions/http-curl/client/HTTPClient.cpp
@@ -44,6 +44,8 @@ HTTPClient::HTTPClient(const std::string &url, const std::shared_ptr<minifi::con
       read_callback_(INT_MAX),
       header_response_(-1),
       res(CURLE_OK),
+      keep_alive_probe_(-1),
+      keep_alive_idle_(-1),
       logger_(logging::LoggerFactory<HTTPClient>::getLogger()) {
   HTTPClientInitializer *initializer = HTTPClientInitializer::getInstance();
   initializer->initialize();
@@ -64,6 +66,8 @@ HTTPClient::HTTPClient(std::string name, utils::Identifier uuid)
       read_callback_(INT_MAX),
       header_response_(-1),
       res(CURLE_OK),
+      keep_alive_probe_(-1),
+      keep_alive_idle_(-1),
       logger_(logging::LoggerFactory<HTTPClient>::getLogger()) {
   HTTPClientInitializer *initializer = HTTPClientInitializer::getInstance();
   initializer->initialize();
@@ -84,6 +88,8 @@ HTTPClient::HTTPClient()
       read_callback_(INT_MAX),
       header_response_(-1),
       res(CURLE_OK),
+      keep_alive_probe_(-1),
+      keep_alive_idle_(-1),
       logger_(logging::LoggerFactory<HTTPClient>::getLogger()) {
   HTTPClientInitializer *initializer = HTTPClientInitializer::getInstance();
   initializer->initialize();
@@ -240,9 +246,17 @@ bool HTTPClient::submit() {
   }
   curl_easy_setopt(http_session_, CURLOPT_HEADERFUNCTION, &utils::HTTPHeaderResponse::receive_headers);
   curl_easy_setopt(http_session_, CURLOPT_HEADERDATA, static_cast<void*>(&header_response_));
-#ifdef CURLOPT_TCP_KEEPALIVE
-  curl_easy_setopt(http_session_, CURLOPT_TCP_KEEPALIVE, 0L);
-#endif
+  if (keep_alive_probe_ > 0){
+    logger_->log_debug("Setting keep alive to %d",keep_alive_probe_);
+    curl_easy_setopt(http_session_, CURLOPT_TCP_KEEPALIVE, 1L);
+    curl_easy_setopt(http_session_, CURLOPT_TCP_KEEPINTVL, keep_alive_probe_);
+    curl_easy_setopt(http_session_, CURLOPT_TCP_KEEPIDLE, keep_alive_idle_);
+
+  }
+  else{
+    logger_->log_debug("Not using keep alive");
+    curl_easy_setopt(http_session_, CURLOPT_TCP_KEEPALIVE, 0L);
+  }
   res = curl_easy_perform(http_session_);
   if (callback == nullptr) {
     read_callback_.close();
diff --git a/extensions/http-curl/client/HTTPClient.h b/extensions/http-curl/client/HTTPClient.h
index f83df10..84cb1a6 100644
--- a/extensions/http-curl/client/HTTPClient.h
+++ b/extensions/http-curl/client/HTTPClient.h
@@ -131,6 +131,15 @@ class HTTPClient : public BaseHTTPClient, public core::Connectable {
 
   void setDisableHostVerification() override;
 
+  void setKeepAliveProbe(long probe){
+    keep_alive_probe_ = probe;
+  }
+
+  void setKeepAliveIdle(long idle){
+    keep_alive_idle_= idle;
+  }
+
+
   std::string getURL() const {
     return url_;
   }
@@ -246,6 +255,10 @@ class HTTPClient : public BaseHTTPClient, public core::Connectable {
 
   std::string method_;
 
+  long keep_alive_probe_;
+
+  long keep_alive_idle_;
+
   std::shared_ptr<logging::Logger> logger_;
 
 };
diff --git a/extensions/http-curl/protocols/RESTSender.cpp b/extensions/http-curl/protocols/RESTSender.cpp
index 11d53e2..bef5aa0 100644
--- a/extensions/http-curl/protocols/RESTSender.cpp
+++ b/extensions/http-curl/protocols/RESTSender.cpp
@@ -46,15 +46,15 @@ void RESTSender::initialize(const std::shared_ptr<core::controller::ControllerSe
   // base URL when one is not specified.
   if (nullptr != configure) {
     std::string update_str, ssl_context_service_str;
-    configure->get("nifi.c2.rest.url","c2.rest.url", rest_uri_);
-    configure->get("nifi.c2.rest.url.ack","c2.rest.url.ack", ack_uri_);
-    if (configure->get("nifi.c2.rest.ssl.context.service","c2.rest.ssl.context.service", ssl_context_service_str)) {
+    configure->get("nifi.c2.rest.url", "c2.rest.url", rest_uri_);
+    configure->get("nifi.c2.rest.url.ack", "c2.rest.url.ack", ack_uri_);
+    if (configure->get("nifi.c2.rest.ssl.context.service", "c2.rest.ssl.context.service", ssl_context_service_str)) {
       auto service = controller->getControllerService(ssl_context_service_str);
       if (nullptr != service) {
         ssl_context_service_ = std::static_pointer_cast<minifi::controllers::SSLContextService>(service);
       }
     }
-    configure->get("nifi.c2.rest.heartbeat.minimize.updates","c2.rest.heartbeat.minimize.updates", update_str);
+    configure->get("nifi.c2.rest.heartbeat.minimize.updates", "c2.rest.heartbeat.minimize.updates", update_str);
     utils::StringUtils::StringToBool(update_str, minimize_updates_);
   }
   logger_->log_debug("Submitting to %s", rest_uri_);
@@ -78,15 +78,17 @@ C2Payload RESTSender::consumePayload(const C2Payload &payload, Direction directi
 
 void RESTSender::update(const std::shared_ptr<Configure> &configure) {
   std::string url;
-  configure->get("nifi.c2.rest.url","c2.rest.url", url);
-  configure->get("nifi.c2.rest.url.ack","c2.rest.url.ack", url);
+  configure->get("nifi.c2.rest.url", "c2.rest.url", url);
+  configure->get("nifi.c2.rest.url.ack", "c2.rest.url.ack", url);
 }
 
 const C2Payload RESTSender::sendPayload(const std::string url, const Direction direction, const C2Payload &payload, const std::string outputConfig) {
-  if (url.empty()){
+  if (url.empty()) {
     return C2Payload(payload.getOperation(), state::UpdateState::READ_ERROR, true);
   }
   utils::HTTPClient client(url, ssl_context_service_);
+  client.setKeepAliveProbe(2);
+  client.setKeepAliveIdle(2);
   client.setConnectionTimeout(2);
   std::unique_ptr<utils::ByteInputCallBack> input = nullptr;
   std::unique_ptr<utils::HTTPUploadCallback> callback = nullptr;
diff --git a/extensions/http-curl/tests/unit/InvokeHTTPTests.cpp b/extensions/http-curl/tests/unit/InvokeHTTPTests.cpp
index 68671cf..db12530 100644
--- a/extensions/http-curl/tests/unit/InvokeHTTPTests.cpp
+++ b/extensions/http-curl/tests/unit/InvokeHTTPTests.cpp
@@ -116,8 +116,8 @@ TEST_CASE("HTTPTestsWithNoResourceClaimPOST", "[httptest1]") {
   invokehttp->onSchedule(context2, factory2);
   invokehttp->onTrigger(context2, session2);
 
-  provenance::ProvenanceReporter *reporter = session->getProvenanceReporter();
-  std::set<provenance::ProvenanceEventRecord*> records = reporter->getEvents();
+  auto reporter = session->getProvenanceReporter();
+  auto records = reporter->getEvents();
   record = session->get();
   REQUIRE(record == nullptr);
   REQUIRE(records.size() == 0);
@@ -138,7 +138,7 @@ TEST_CASE("HTTPTestsWithNoResourceClaimPOST", "[httptest1]") {
   session2->commit();
   records = reporter->getEvents();
 
-  for (provenance::ProvenanceEventRecord *provEventRecord : records) {
+  for (auto provEventRecord : records) {
     REQUIRE(provEventRecord->getComponentType() == listenhttp->getName());
   }
   std::shared_ptr<core::FlowFile> ffr = session2->get();
@@ -247,8 +247,8 @@ TEST_CASE("HTTPTestsWithResourceClaimPOST", "[httptest1]") {
   listenhttp->onSchedule(context, factory);
   listenhttp->onTrigger(context, session);
 
-  provenance::ProvenanceReporter *reporter = session->getProvenanceReporter();
-  std::set<provenance::ProvenanceEventRecord*> records = reporter->getEvents();
+  auto reporter = session->getProvenanceReporter();
+  auto records = reporter->getEvents();
   record = session->get();
   REQUIRE(record == nullptr);
   REQUIRE(records.size() == 0);
@@ -269,7 +269,7 @@ TEST_CASE("HTTPTestsWithResourceClaimPOST", "[httptest1]") {
   session2->commit();
   records = reporter->getEvents();
 
-  for (provenance::ProvenanceEventRecord *provEventRecord : records) {
+  for (auto provEventRecord : records) {
     REQUIRE(provEventRecord->getComponentType() == listenhttp->getName());
   }
   std::shared_ptr<core::FlowFile> ffr = session2->get();
@@ -295,7 +295,7 @@ TEST_CASE("HTTPTestsPostNoResourceClaim", "[httptest1]") {
   plan->reset();
   testController.runSession(plan, true);
 
-  std::set<provenance::ProvenanceEventRecord*> records = plan->getProvenanceRecords();
+  auto records = plan->getProvenanceRecords();
   std::shared_ptr<core::FlowFile> record = plan->getCurrentFlowFile();
   REQUIRE(record == nullptr);
   REQUIRE(records.size() == 0);
@@ -306,7 +306,7 @@ TEST_CASE("HTTPTestsPostNoResourceClaim", "[httptest1]") {
   records = plan->getProvenanceRecords();
   record = plan->getCurrentFlowFile();
 
-  for (provenance::ProvenanceEventRecord *provEventRecord : records) {
+  for (auto provEventRecord : records) {
     REQUIRE(provEventRecord->getComponentType() == processor->getName());
   }
   std::shared_ptr<core::FlowFile> ffr = plan->getCurrentFlowFile();
diff --git a/extensions/jni/CMakeLists.txt b/extensions/jni/CMakeLists.txt
new file mode 100644
index 0000000..9b2bf8d
--- /dev/null
+++ b/extensions/jni/CMakeLists.txt
@@ -0,0 +1,75 @@
+#
+# 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.
+#
+
+include(${CMAKE_SOURCE_DIR}/extensions/ExtensionHeader.txt) 
+
+message(STATUS "JAVA_HOME: '$ENV{JAVA_HOME}'")
+find_package(JNI REQUIRED)
+find_package(Java REQUIRED)
+
+message(STATUS "JAVA_HOME: '$ENV{JAVA_HOME}'")
+include_directories(${JNI_INCLUDE_DIRS})
+
+file(GLOB SOURCES  "jvm/*.cpp" "*.cpp") 
+
+add_library(minifi-jni STATIC ${SOURCES})
+set_property(TARGET minifi-jni PROPERTY POSITION_INDEPENDENT_CODE ON)
+
+
+set(JNI_FRAMEWORK_JAR_SOURCE "${CMAKE_SOURCE_DIR}/extensions/jni/nifi-framework-jni")
+set(JNI_FRAMEWORK_JAR_BIN "${CMAKE_CURRENT_BINARY_DIR}/" )
+
+include_directories(${PCAPPLUSPLUS_HEADER_DIR})
+
+file(COPY ${JNI_FRAMEWORK_JAR_SOURCE} DESTINATION ${JNI_FRAMEWORK_JAR_BIN})
+
+execute_process(COMMAND "mvn" "package"
+	WORKING_DIRECTORY "${JNI_FRAMEWORK_JAR_BIN}/nifi-framework-jni"
+	RESULT_VARIABLE mvn_result
+  	OUTPUT_VARIABLE mvn_output)
+
+if("${mvn_result}" STREQUAL "0")
+
+	SET (JNI-FRAMEWORK-JAR "${JNI_FRAMEWORK_JAR_BIN}/nifi-framework-jni/target/nifi-framework-jni-1.10.0-SNAPSHOT.jar")
+	message("Produced ${JNI-FRAMEWORK-JAR}")
+	message("${mvn_output}")
+	install(FILES ${JNI-FRAMEWORK-JAR}
+	        DESTINATION minifi-jni/lib
+	        COMPONENT bin)	
+else()
+	message("Maven could not be invoked to build the framework jar")
+endif()
+
+
+if (APPLE)
+	target_link_libraries (minifi-jni  -Wl,-all_load ${JAVA_JVM_LIBRARY})
+else ()
+	if (WIN32)
+		target_link_libraries (minifi-jni  ${JAVA_JVM_LIBRARY})
+		target_link_libraries (minifi-jni  ${Java_LIBRARIES})
+	else()
+		target_link_libraries (minifi-jni -Wl,--whole-archive ${JAVA_JVM_LIBRARY} -Wl,--no-whole-archive)
+	endif()
+endif ()
+
+
+
+SET (JNI-EXTENSION minifi-jni PARENT_SCOPE)
+register_extension(minifi-jni)
+
diff --git a/extensions/jni/ExecuteJavaControllerService.cpp b/extensions/jni/ExecuteJavaControllerService.cpp
new file mode 100644
index 0000000..cafb0e4
--- /dev/null
+++ b/extensions/jni/ExecuteJavaControllerService.cpp
@@ -0,0 +1,104 @@
+/**
+ *
+ * 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.
+ */
+
+#include "ExecuteJavaControllerService.h"
+
+#include <regex>
+#include <uuid/uuid.h>
+#include <memory>
+#include <algorithm>
+#include <cctype>
+#include <cstdint>
+#include <cstring>
+#include <iostream>
+#include <iterator>
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "core/FlowFile.h"
+#include "core/logging/Logger.h"
+#include "core/ProcessContext.h"
+#include "core/Relationship.h"
+#include "ResourceClaim.h"
+#include "utils/StringUtils.h"
+#include "utils/ByteArrayCallback.h"
+#include "jvm/JniMethod.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+namespace controllers {
+
+core::Property ExecuteJavaControllerService::JVMControllerService(
+    core::PropertyBuilder::createProperty("JVM Controller Service")->withDescription("Name of controller service defined within this flow")->isRequired(false)->withDefaultValue<std::string>("")->build());
+core::Property ExecuteJavaControllerService::NiFiControllerService(
+    core::PropertyBuilder::createProperty("NiFi Controller Service")->withDescription("Name of NiFi Controller Service to load and run")->isRequired(true)->withDefaultValue<std::string>("")->build());
+
+const char *ExecuteJavaControllerService::ProcessorName = "ExecuteJavaControllerService";
+
+void ExecuteJavaControllerService::initialize() {
+  logger_->log_info("Initializing ExecuteJavaControllerService");
+  // Set the supported properties
+  std::set<core::Property> properties;
+  properties.insert(JVMControllerService);
+  properties.insert(NiFiControllerService);
+  setSupportedProperties(properties);
+  setAcceptAllProperties();
+
+}
+
+ExecuteJavaControllerService::~ExecuteJavaControllerService() {
+}
+
+void ExecuteJavaControllerService::onEnable() {
+  std::string controller_service_name;
+
+  auto env = java_servicer_->attach();
+
+  auto serv_cs = JVMLoader::getInstance()->getBaseServicer();
+  java_servicer_ = std::static_pointer_cast<controllers::JavaControllerService>(serv_cs);
+
+  if (!getProperty(NiFiControllerService.getName(), class_name_)) {
+    throw std::runtime_error("NiFi Controller Service must be defined");
+  }
+
+  clazzInstance = java_servicer_->newInstance(class_name_);
+
+  auto onEnabledName = java_servicer_->getAnnotation(class_name_, "OnEnabled");
+  current_cs_class = java_servicer_->getObjectClass(class_name_, clazzInstance);
+  // attempt to schedule here
+  try {
+    current_cs_class.callVoidMethod(env, clazzInstance, onEnabledName.first.c_str(), onEnabledName.second);
+  } catch (std::runtime_error &re) {
+    // this can be ignored.
+  }
+
+}
+
+} /* namespace controllers */
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
diff --git a/extensions/jni/ExecuteJavaControllerService.h b/extensions/jni/ExecuteJavaControllerService.h
new file mode 100644
index 0000000..85a7d93
--- /dev/null
+++ b/extensions/jni/ExecuteJavaControllerService.h
@@ -0,0 +1,135 @@
+/**
+ * ExecuteJavaClass class declaration
+ *
+ * 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.
+ */
+#ifndef __EXECUTE_JAVA_CS_
+#define __EXECUTE_JAVA_CS_
+
+#include <memory>
+#include <regex>
+
+#include "FlowFileRecord.h"
+#include "core/controller/ControllerService.h"
+#include "core/ProcessSession.h"
+#include "core/Core.h"
+#include "core/Property.h"
+#include "core/Resource.h"
+#include "concurrentqueue.h"
+#include "core/logging/LoggerConfiguration.h"
+#include "jvm/JavaControllerService.h"
+#include "jvm/JniProcessContext.h"
+#include "utils/Id.h"
+#include "jvm/NarClassLoader.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+namespace controllers {
+
+/**
+ * Purpose: Enables isolated java loading through the use of controller services
+ *
+ * While we do allow the nifi properties to define the classes, we also allow run
+ * time  loading of java classes
+ *
+ * In the case where we load via properties, we effectively instantiate this
+ * controller service within the execute java process.
+ *
+ */
+class ExecuteJavaControllerService : public core::controller::ControllerService {
+ public:
+
+  // Constructor
+  /*!
+   * Create a new processor
+   */
+  explicit ExecuteJavaControllerService(std::string name, utils::Identifier uuid = utils::Identifier())
+      : core::controller::ControllerService(name, uuid),
+        clazzInstance(nullptr),
+        logger_(logging::LoggerFactory<ExecuteJavaControllerService>::getLogger()) {
+  }
+
+  explicit ExecuteJavaControllerService(const std::string &name, const std::string &id)
+      : core::controller::ControllerService(name, id),
+        clazzInstance(nullptr),
+        logger_(logging::LoggerFactory<ExecuteJavaControllerService>::getLogger()) {
+  }
+  // Destructor
+  virtual ~ExecuteJavaControllerService();
+  // Processor Name
+  static const char *ProcessorName;
+  static core::Property JVMControllerService;
+  static core::Property NiFiControllerService;
+  // Supported Relationships
+
+  virtual void onEnable() override;
+  virtual void initialize() override;
+  virtual bool supportsDynamicProperties() override {
+    return true;
+  }
+
+  virtual void yield() override {
+  }
+
+  virtual bool isRunning() override {
+    return getState() == core::controller::ControllerServiceState::ENABLED;
+  }
+
+  virtual bool isWorkAvailable() override {
+    return false;
+  }
+
+  virtual void notifyStop() override {
+    auto env = java_servicer_->attach();
+    auto onEnabledName = java_servicer_->getAnnotation(class_name_, "OnDisabled");
+    current_cs_class = java_servicer_->getObjectClass(class_name_, clazzInstance);
+    // attempt to schedule here
+
+    try {
+      current_cs_class.callVoidMethod(env, clazzInstance, onEnabledName.first.c_str(), onEnabledName.second);
+    } catch (std::runtime_error &re) {
+      // this is avoidable.
+    }
+  }
+
+ protected:
+
+ private:
+
+  JavaClass current_cs_class;
+
+  jobject clazzInstance;
+
+  std::shared_ptr<controllers::JavaControllerService> java_servicer_;
+
+  std::string class_name_;
+
+  std::shared_ptr<logging::Logger> logger_;
+};
+
+REGISTER_RESOURCE(ExecuteJavaControllerService, "ExecuteJavaClass runs NiFi Controller services given a provided system path ")
+
+} /* namespace controllers */
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
+#endif
diff --git a/extensions/jni/ExecuteJavaProcessor.cpp b/extensions/jni/ExecuteJavaProcessor.cpp
new file mode 100644
index 0000000..eb6e5da
--- /dev/null
+++ b/extensions/jni/ExecuteJavaProcessor.cpp
@@ -0,0 +1,219 @@
+/**
+ *
+ * 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.
+ */
+
+#include "ExecuteJavaProcessor.h"
+
+#include <regex>
+#include <uuid/uuid.h>
+#include <memory>
+#include <algorithm>
+#include <cctype>
+#include <cstdint>
+#include <cstring>
+#include <iostream>
+#include <iterator>
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "core/FlowFile.h"
+#include "core/logging/Logger.h"
+#include "core/ProcessContext.h"
+#include "core/Relationship.h"
+#include "ResourceClaim.h"
+#include "utils/StringUtils.h"
+#include "utils/ByteArrayCallback.h"
+#include "jvm/JniMethod.h"
+#include "jvm/JniLogger.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+namespace processors {
+
+core::Property ExecuteJavaProcessor::JVMControllerService(
+    core::PropertyBuilder::createProperty("JVM Controller Service")->withDescription("Name of controller service defined within this flow")->isRequired(false)->withDefaultValue<std::string>("")->build());
+core::Property ExecuteJavaProcessor::NiFiProcessor(
+    core::PropertyBuilder::createProperty("NiFi Processor")->withDescription("Name of NiFi processor to load and run")->isRequired(true)->withDefaultValue<std::string>("")->build());
+
+const char *ExecuteJavaProcessor::ProcessorName = "ExecuteJavaClass";
+
+core::Relationship ExecuteJavaProcessor::Success("success", "All files are routed to success");
+void ExecuteJavaProcessor::initialize() {
+  logger_->log_info("Initializing ExecuteJavaClass");
+  // Set the supported properties
+  std::set<core::Property> properties;
+  properties.insert(JVMControllerService);
+  properties.insert(NiFiProcessor);
+  setSupportedProperties(properties);
+  setAcceptAllProperties();
+  // Set the supported relationships
+  std::set<core::Relationship> relationships;
+  relationships.insert(Success);
+  setSupportedRelationships(relationships);
+}
+
+void ExecuteJavaProcessor::onSchedule(const std::shared_ptr<core::ProcessContext> &context, const std::shared_ptr<core::ProcessSessionFactory> &sessionFactory) {
+  std::string controller_service_name;
+  if (getProperty(JVMControllerService.getName(), controller_service_name)) {
+    auto cs = context->getControllerService(controller_service_name);
+    if (cs == nullptr) {
+      auto serv_cs = JVMLoader::getInstance()->getBaseServicer();
+      java_servicer_ = std::static_pointer_cast<controllers::JavaControllerService>(serv_cs);
+      if (serv_cs == nullptr)
+        throw std::runtime_error("Could not load controller service");
+    } else {
+      java_servicer_ = std::static_pointer_cast<controllers::JavaControllerService>(cs);
+    }
+
+  } else {
+    auto serv_cs = JVMLoader::getInstance()->getBaseServicer();
+    java_servicer_ = std::static_pointer_cast<controllers::JavaControllerService>(serv_cs);
+    if (serv_cs == nullptr)
+      throw std::runtime_error("Could not load controller service");
+  }
+
+  if (!getProperty(NiFiProcessor.getName(), class_name_)) {
+    throw std::runtime_error("NiFi Processor must be defined");
+  }
+
+  nifi_logger_ = logging::LoggerFactory<ExecuteJavaProcessor>::getAliasedLogger(class_name_);
+
+  jni_logger_ref_.logger_reference_ = nifi_logger_;
+
+  jni_logger_class_ = java_servicer_->loadClass("org/apache/nifi/processor/JniLogger");
+
+  spn = java_servicer_->loadClass("org/apache/nifi/processor/JniProcessContext");
+  auto env = java_servicer_->attach();
+  java_servicer_->putNativeFunctionMapping<minifi::jni::JniProcessContext>(env, spn);
+
+  init = java_servicer_->loadClass("org/apache/nifi/processor/JniInitializationContext");
+
+  if (context_instance_ != nullptr) {
+    java_servicer_->attach()->DeleteGlobalRef(context_instance_);
+  }
+  context_instance_ = spn.newInstance(env);
+
+  auto initializer = init.newInstance(env);
+
+  ClassRegistrar::getRegistrar().registerClasses(env, java_servicer_, "org/apache/nifi/processor/JniLogger", getLoggerSignatures());
+
+  jni_logger_ref_.clazz_ = jni_logger_class_.getReference();
+
+  logger_instance_ = jni_logger_class_.newInstance(env);
+  java_servicer_->setReference<minifi::jni::JniLogger>(env, logger_instance_, &jni_logger_ref_);
+  java_servicer_->putNativeFunctionMapping<minifi::jni::JniLogger>(env, jni_logger_class_);
+  // create provided class
+
+  clazzInstance = java_servicer_->newInstance(class_name_);
+  auto onScheduledName = java_servicer_->getAnnotation(class_name_, "OnScheduled");
+  current_processor_class = java_servicer_->getObjectClass(class_name_, clazzInstance);
+  // attempt to schedule here
+  ClassRegistrar::getRegistrar().registerClasses(env, java_servicer_, "org/apache/nifi/processor/JniProcessContext", getProcessContextSignatures());
+
+  init.callVoidMethod(env, initializer, "setLogger", "(Lorg/apache/nifi/processor/JniLogger;)V", logger_instance_);
+
+  current_processor_class.callVoidMethod(env, clazzInstance, "initialize", "(Lorg/apache/nifi/processor/ProcessorInitializationContext;)V", initializer);
+
+  jpc.nifi_processor_ = clazzInstance;
+  jpc.context_ = context;
+  jpc.clazz_ = spn.getReference();
+  jpc.processor_ = shared_from_this();
+
+  java_servicer_->setReference<minifi::jni::JniProcessContext>(env, context_instance_, &jpc);
+
+  try {
+    current_processor_class.callVoidMethod(env, clazzInstance, onScheduledName.first.c_str(), onScheduledName.second, context_instance_);
+  } catch (std::runtime_error &re) {
+    // this can be ignored.
+  }
+
+  java_servicer_->attach()->DeleteGlobalRef(initializer);
+}
+
+ExecuteJavaProcessor::~ExecuteJavaProcessor() {
+}
+
+JNINativeMethod ExecuteJavaProcessor::registerNativeMethod(const std::string &name, const std::string &params, const void *ptr) {
+  JNINativeMethod mthd;
+
+  return mthd;
+}
+
+void ExecuteJavaProcessor::onTrigger(const std::shared_ptr<core::ProcessContext> &context, const std::shared_ptr<core::ProcessSessionFactory> &sessionFactory) {
+
+  assert(context == jpc.context_);
+  auto env = java_servicer_->attach();
+
+  if (ClassRegistrar::getRegistrar().registerClasses(env, java_servicer_, "org/apache/nifi/processor/JniFlowFile", getFlowFileSignatures())) {
+    static auto ffc = java_servicer_->loadClass("org/apache/nifi/processor/JniFlowFile");
+    java_servicer_->putNativeFunctionMapping<JniFlowFile>(env, ffc);
+
+  }
+  if (ClassRegistrar::getRegistrar().registerClasses(env, java_servicer_, "org/apache/nifi/processor/JniInputStream", getInputStreamSignatures())) {
+    static auto jin = java_servicer_->loadClass("org/apache/nifi/processor/JniInputStream");
+    java_servicer_->putNativeFunctionMapping<JniInputStream>(env, jin);
+  }
+  static auto sessioncls = java_servicer_->loadClass("org/apache/nifi/processor/JniProcessSession");
+
+  if (ClassRegistrar::getRegistrar().registerClasses(env, java_servicer_, "org/apache/nifi/processor/JniProcessSession", getProcessSessionSignatures())) {
+    java_servicer_->putNativeFunctionMapping<minifi::jni::JniSession>(env, sessioncls);
+  }
+  ClassRegistrar::getRegistrar().registerClasses(env, java_servicer_, "org/apache/nifi/processor/JniProcessSessionFactory", getProcessSessionFactorySignatures());
+
+  static auto sessionFactoryCls = java_servicer_->loadClass("org/apache/nifi/processor/JniProcessSessionFactory");
+
+  try {
+    // it is possible that java classes will maintain a reference to the session factory, so we cannot remove the global references
+    // in this function.
+    jobject java_process_session_factory = nullptr;
+
+    JniSessionFactory *jniSessionFactory = getFactory(sessionFactory);
+
+    if (!jniSessionFactory) {
+      java_process_session_factory = sessionFactoryCls.newInstance(env);
+      jniSessionFactory = setFactory(sessionFactory, java_process_session_factory);
+      java_servicer_->putNativeFunctionMapping<JniSessionFactory>(env, sessionFactoryCls);
+      java_servicer_->setReference<JniSessionFactory>(env, java_process_session_factory, jniSessionFactory);
+    } else {
+      java_process_session_factory = jniSessionFactory->getJavaReference();
+    }
+    current_processor_class.callVoidMethod(env, clazzInstance, "onTrigger", "(Lorg/apache/nifi/processor/ProcessContext;Lorg/apache/nifi/processor/ProcessSessionFactory;)V", context_instance_,
+                                           java_process_session_factory);
+  } catch (const JavaException &je) {
+    logger_->log_error(" Java occurred during onTrigger, reason: %s", je.what());
+  } catch (const std::exception &e) {
+    logger_->log_error(" Exception occurred during onTrigger, reason: %s", e.what());
+  }
+}
+
+void ExecuteJavaProcessor::onTrigger(const std::shared_ptr<core::ProcessContext> &context, const std::shared_ptr<core::ProcessSession> &session) {
+  // do nothing.
+}
+
+} /* namespace processors */
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
diff --git a/extensions/jni/ExecuteJavaProcessor.h b/extensions/jni/ExecuteJavaProcessor.h
new file mode 100644
index 0000000..308c48d
--- /dev/null
+++ b/extensions/jni/ExecuteJavaProcessor.h
@@ -0,0 +1,296 @@
+/**
+ * ExecuteJavaClass class declaration
+ *
+ * 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.
+ */
+#ifndef __EXECUTE_JAVA_CLASS__
+#define __EXECUTE_JAVA_CLASS__
+
+#include <memory>
+
+#include "FlowFileRecord.h"
+#include "core/Processor.h"
+#include "core/ProcessSession.h"
+#include "core/Core.h"
+#include "core/Property.h"
+#include "core/Resource.h"
+#include "concurrentqueue.h"
+#include "core/logging/LoggerConfiguration.h"
+#include "jvm/JavaControllerService.h"
+#include "jvm/JniProcessContext.h"
+#include "utils/Id.h"
+#include "jvm/NarClassLoader.h"
+#include "jvm/JniLogger.h"
+#include "jvm/JniReferenceObjects.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+namespace processors {
+
+class ClassRegistrar {
+ public:
+  static ClassRegistrar &getRegistrar() {
+    static ClassRegistrar registrar;
+    // do nothing.
+    return registrar;
+  }
+
+  bool registerClasses(JNIEnv *env, std::shared_ptr<controllers::JavaControllerService> servicer, const std::string &className, JavaSignatures &signatures) {
+    std::lock_guard<std::mutex> lock(mutex_);
+    // load class before insertion.
+    if (registered_classes_.find(className) == std::end(registered_classes_)) {
+      auto cls = servicer->loadClass(className);
+      cls.registerMethods(env, signatures);
+      registered_classes_.insert(className);
+      return true;
+    }
+    return false;
+  }
+ private:
+  ClassRegistrar() {
+  }
+
+  std::mutex mutex_;
+  std::set<std::string> registered_classes_;
+};
+
+/**
+ * Purpose and Justification: Executes a java NiFi Processor
+ *
+ * Design: Extends Processor to provide basic processor support capabilities.
+ */
+class ExecuteJavaProcessor : public core::Processor {
+ public:
+
+  // Constructor
+  /*!
+   * Create a new processor
+   */
+  explicit ExecuteJavaProcessor(std::string name, utils::Identifier uuid = utils::Identifier())
+      : Processor(name, uuid),
+        context_instance_(nullptr),
+        logger_instance_(nullptr),
+        logger_(logging::LoggerFactory<ExecuteJavaProcessor>::getLogger()),
+        nifi_logger_(nullptr) {
+  }
+  // Destructor
+  virtual ~ExecuteJavaProcessor();
+  // Processor Name
+  static const char *ProcessorName;
+  static core::Property JVMControllerService;
+  static core::Property NiFiProcessor;
+  // Supported Relationships
+  static core::Relationship Success;
+
+  virtual void onTrigger(const std::shared_ptr<core::ProcessContext> &context, const std::shared_ptr<core::ProcessSessionFactory> &sessionFactory) override;
+  virtual void onTrigger(const std::shared_ptr<core::ProcessContext> &context, const std::shared_ptr<core::ProcessSession> &session) override;
+  virtual void initialize() override;
+  virtual void onSchedule(const std::shared_ptr<core::ProcessContext> &context, const std::shared_ptr<core::ProcessSessionFactory> &sessionFactory) override;
+  virtual bool supportsDynamicProperties() override {
+    return true;
+  }
+
+ protected:
+
+  static JavaSignatures &getLoggerSignatures() {
+    static JavaSignatures loggersignatures;
+    if (loggersignatures.empty()) {
+      loggersignatures.addSignature( { "isWarnEnabled", "()Z", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniLogger_isWarnEnabled) });
+      loggersignatures.addSignature( { "isTraceEnabled", "()Z", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniLogger_isTraceEnabled) });
+      loggersignatures.addSignature( { "isInfoEnabled", "()Z", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniLogger_isInfoEnabled) });
+      loggersignatures.addSignature( { "isErrorEnabled", "()Z", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniLogger_isErrorEnabled) });
+      loggersignatures.addSignature( { "isDebugEnabled", "()Z", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniLogger_isDebugEnabled) });
+
+      loggersignatures.addSignature( { "info", "(Ljava/lang/String;)V", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniLogger_info) });
+      loggersignatures.addSignature( { "warn", "(Ljava/lang/String;)V", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniLogger_warn) });
+      loggersignatures.addSignature( { "error", "(Ljava/lang/String;)V", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniLogger_error) });
+      loggersignatures.addSignature( { "debug", "(Ljava/lang/String;)V", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniLogger_debug) });
+      loggersignatures.addSignature( { "trace", "(Ljava/lang/String;)V", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniLogger_trace) });
+    }
+    return loggersignatures;
+  }
+
+  static JavaSignatures &getProcessContextSignatures() {
+    static JavaSignatures methodSignatures;
+    if (methodSignatures.empty()) {
+      methodSignatures.addSignature( { "getProcessor", "()Lorg/apache/nifi/processor/Processor;", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessContext_getProcessor) });
+      methodSignatures.addSignature( { "getPropertyNames", "()Ljava/util/List;", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessContext_getPropertyNames) });
+      methodSignatures.addSignature( { "getPropertyValue", "(Ljava/lang/String;)Ljava/lang/String;", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessContext_getPropertyValue) });
+    }
+    return methodSignatures;
+  }
+
+  static JavaSignatures &getInputStreamSignatures() {
+    static JavaSignatures methodSignatures;
+    if (methodSignatures.empty()) {
+      methodSignatures.addSignature( { "read", "()I", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniInputStream_read) });
+      methodSignatures.addSignature( { "readWithOffset", "([BII)I", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniInputStream_readWithOffset) });
+
+    }
+    return methodSignatures;
+  }
+
+  static JavaSignatures &getFlowFileSignatures() {
+    static JavaSignatures methodSignatures;
+    if (methodSignatures.empty()) {
+      methodSignatures.addSignature( { "getAttributes", "()Ljava/util/Map;", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniFlowFile_getAttributes) });
+      methodSignatures.addSignature( { "getAttribute", "(Ljava/lang/String;)Ljava/lang/String;", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniFlowFile_getAttribute) });
+      methodSignatures.addSignature( { "getSize", "()J", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniFlowFile_getSize) });
+      methodSignatures.addSignature( { "getEntryDate", "()J", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniFlowFile_getEntryDate) });
+      methodSignatures.addSignature( { "getLineageStartDate", "()J", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniFlowFile_getLineageStartDate) });
+      methodSignatures.addSignature( { "getLastQueueDatePrim", "()J", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniFlowFile_getLastQueueDatePrim) });
+      methodSignatures.addSignature( { "getQueueDateIndex", "()J", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniFlowFile_getQueueDateIndex) });
+      methodSignatures.addSignature( { "getId", "()J", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniFlowFile_getId) });
+      methodSignatures.addSignature( { "getUUIDStr", "()Ljava/lang/String;", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniFlowFile_getUUIDStr) });
+    }
+    return methodSignatures;
+  }
+
+  static JavaSignatures &getProcessSessionSignatures() {
+    static JavaSignatures methodSignatures;
+    if (methodSignatures.empty()) {
+      methodSignatures.addSignature( { "remove", "(Lorg/apache/nifi/flowfile/FlowFile;)V", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_remove) });
+      methodSignatures.addSignature( { "create", "()Lorg/apache/nifi/flowfile/FlowFile;", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_create) });
+      methodSignatures.addSignature( { "penalize", "(Lorg/apache/nifi/flowfile/FlowFile;)Lorg/apache/nifi/flowfile/FlowFile;",
+          reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_penalize) });
+      methodSignatures.addSignature( { "createWithParent", "(Lorg/apache/nifi/flowfile/FlowFile;)Lorg/apache/nifi/flowfile/FlowFile;",
+          reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_createWithParent) });
+      methodSignatures.addSignature( { "rollback", "()V", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_rollback) });
+      methodSignatures.addSignature( { "commit", "()V", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_commit) });
+      methodSignatures.addSignature( { "get", "()Lorg/apache/nifi/flowfile/FlowFile;", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_get) });
+      methodSignatures.addSignature( { "write", "(Lorg/apache/nifi/flowfile/FlowFile;[B)Z", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_write) });
+      methodSignatures.addSignature( { "append", "(Lorg/apache/nifi/flowfile/FlowFile;[B)Z", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_append) });
+      methodSignatures.addSignature( { "putAttribute", "(Lorg/apache/nifi/flowfile/FlowFile;Ljava/lang/String;Ljava/lang/String;)Lorg/apache/nifi/flowfile/FlowFile;",
+          reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_putAttribute) });
+      methodSignatures.addSignature( { "removeAttribute", "(Lorg/apache/nifi/flowfile/FlowFile;Ljava/lang/String;)Lorg/apache/nifi/flowfile/FlowFile;",
+          reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_removeAttribute) });
+      methodSignatures.addSignature( { "clone", "(Lorg/apache/nifi/flowfile/FlowFile;)Lorg/apache/nifi/flowfile/FlowFile;",
+          reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_clone) });
+      methodSignatures.addSignature( { "clonePortion", "(Lorg/apache/nifi/flowfile/FlowFile;JJ)Lorg/apache/nifi/flowfile/FlowFile;",
+          reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_clonePortion) });
+      methodSignatures.addSignature( { "readFlowFile", "(Lorg/apache/nifi/flowfile/FlowFile;)Lorg/apache/nifi/processor/JniInputStream;",
+          reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_readFlowFile) });
+      methodSignatures.addSignature( { "transfer", "(Lorg/apache/nifi/flowfile/FlowFile;Ljava/lang/String;)V", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSession_transfer) });
+    }
+    return methodSignatures;
+  }
+
+  static JavaSignatures &getProcessSessionFactorySignatures() {
+    static JavaSignatures methodSignatures;
+    if (methodSignatures.empty()) {
+      methodSignatures.addSignature(
+          { "createSession", "()Lorg/apache/nifi/processor/ProcessSession;", reinterpret_cast<void*>(&Java_org_apache_nifi_processor_JniProcessSessionFactory_createSession) });
+    }
+    return methodSignatures;
+  }
+
+  virtual void notifyStop() override {
+
+    auto localEnv = java_servicer_->attach();
+
+    auto onStoppedName = java_servicer_->getAnnotation(class_name_, "OnStopped");
+
+    try {
+      if (!onStoppedName.first.empty() && !onStoppedName.second.empty())
+        current_processor_class.callVoidMethod(localEnv, clazzInstance, onStoppedName.first.c_str(), onStoppedName.second);
+    } catch (std::runtime_error &re) {
+      // this is something that we can ignore.
+    }
+
+    std::lock_guard<std::mutex> lock(local_mutex_);
+
+    for (auto &factory : session_factories_) {
+      factory->remove();
+      delete factory;
+    }
+
+    // delete the reference to the jni process session
+
+    if (logger_instance_) {
+      localEnv->DeleteLocalRef(logger_instance_);
+      logger_instance_ = nullptr;
+    }
+
+  }
+
+ private:
+
+  JniSessionFactory *getFactory(const std::shared_ptr<core::ProcessSessionFactory> &ptr) {
+    std::lock_guard<std::mutex> lock(local_mutex_);
+    for (const auto &factory : session_factories_) {
+      if (factory->getFactory() == ptr) {
+        return factory;
+      }
+    }
+    return nullptr;
+
+  }
+
+  JniSessionFactory *setFactory(const std::shared_ptr<core::ProcessSessionFactory> &ptr, jobject obj) {
+    std::lock_guard<std::mutex> lock(local_mutex_);
+    JniSessionFactory *factory = new JniSessionFactory(ptr, java_servicer_, obj);
+    session_factories_.push_back(factory);
+    return factory;
+
+  }
+
+  JNINativeMethod registerNativeMethod(const std::string &name, const std::string &params, const void *ptr);
+
+  JavaClass jni_logger_class_;
+
+  jobject logger_instance_;
+
+  std::mutex local_mutex_;
+
+  std::vector<JniSessionFactory*> session_factories_;
+
+  minifi::jni::JniLogger jni_logger_ref_;
+
+  JavaClass spn;
+
+  JavaClass init;
+
+  minifi::jni::JniProcessContext jpc;
+
+  JavaClass current_processor_class;
+
+  jobject context_instance_;
+
+  jobject clazzInstance;
+
+  std::shared_ptr<controllers::JavaControllerService> java_servicer_;
+
+  std::string class_name_;
+
+  std::shared_ptr<logging::Logger> logger_;
+
+  std::shared_ptr<logging::Logger> nifi_logger_;
+  ;
+};
+
+REGISTER_RESOURCE(ExecuteJavaProcessor, "ExecuteJavaClass runs NiFi processors given a provided system path ")
+
+} /* namespace processors */
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
+#endif
diff --git a/extensions/expression-language/noop/expression/Expression.h b/extensions/jni/JNILoader.cpp
similarity index 55%
copy from extensions/expression-language/noop/expression/Expression.h
copy to extensions/jni/JNILoader.cpp
index 5cbebfe..2177298 100644
--- a/extensions/expression-language/noop/expression/Expression.h
+++ b/extensions/jni/JNILoader.cpp
@@ -1,4 +1,5 @@
 /**
+ *
  * 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.
@@ -14,36 +15,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include "JNILoader.h"
 
-#ifndef NIFI_MINIFI_CPP_EXPRESSION_H
-#define NIFI_MINIFI_CPP_EXPRESSION_H
-
-#include <core/FlowFile.h>
-#include <Value.h>
-
-namespace org {
-namespace apache {
-namespace nifi {
-namespace minifi {
-namespace expression {
+#include "core/FlowConfiguration.h"
 
-typedef struct {
-  std::weak_ptr<core::FlowFile> flow_file;
-} Parameters;
-
-/**
- * A minimal definition of an Expression with a NoOp implementation.
- */
-class Expression {
- public:
+bool JNIFactory::added = core::FlowConfiguration::add_static_func("createJNIFactory");
 
-  explicit Expression(std::string, std::function<std::string(const Parameters &)>);
-};
+extern "C" {
 
-} /* namespace expression */
-} /* namespace minifi */
-} /* namespace nifi */
-} /* namespace apache */
-} /* namespace org */
+void *createJNIFactory(void) {
+  return new JNIFactory();
+}
 
-#endif //NIFI_MINIFI_CPP_EXPRESSION_H
+}
diff --git a/extensions/jni/JNILoader.h b/extensions/jni/JNILoader.h
new file mode 100644
index 0000000..69d6ef9
--- /dev/null
+++ b/extensions/jni/JNILoader.h
@@ -0,0 +1,79 @@
+/**
+ *
+ * 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.
+ */
+#ifndef EXTENSIONS_JNILOADER_H
+#define EXTENSIONS_JNILOADER_H
+
+#include "core/ClassLoader.h"
+#include "ExecuteJavaProcessor.h"
+#include "JVMCreator.h"
+#include "jvm/JavaControllerService.h"
+#include "utils/StringUtils.h"
+
+class JNIFactory : public core::ObjectFactory {
+ public:
+  JNIFactory() {
+
+  }
+
+  /**
+   * Gets the name of the object.
+   * @return class name of processor
+   */
+  virtual std::string getName() {
+    return "JNIFactory";
+  }
+
+  virtual std::string getClassName() {
+    return "JNIFactory";
+  }
+  /**
+   * Gets the class name for the object
+   * @return class name for the processor.
+   */
+  virtual std::vector<std::string> getClassNames() {
+    static std::vector<std::string> class_names;
+    if (class_names.empty()) {
+      class_names.push_back("ExecuteJavaClass");
+      class_names.push_back("JavaControllerService");
+      class_names.push_back("JVMCreator");
+    }
+    return class_names;
+  }
+
+  virtual std::unique_ptr<ObjectFactory> assign(const std::string &class_name) {
+    if (utils::StringUtils::equalsIgnoreCase(class_name, "ExecuteJavaClass")) {
+      return std::unique_ptr<ObjectFactory>(new core::DefautObjectFactory<minifi::jni::processors::ExecuteJavaProcessor>());
+    } else if (utils::StringUtils::equalsIgnoreCase(class_name, "JavaControllerService")) {
+      return std::unique_ptr<ObjectFactory>(new core::DefautObjectFactory<minifi::jni::controllers::JavaControllerService>());
+    } else if (utils::StringUtils::equalsIgnoreCase(class_name, "JVMCreator")) {
+      return std::unique_ptr<ObjectFactory>(new core::DefautObjectFactory<minifi::jni::JVMCreator>());
+    }
+    return nullptr;
+  }
+
+  static minifi::jni::JVMLoader jvm;
+
+  static bool added;
+
+}
+;
+
+extern "C" {
+DLL_EXPORT void *createJNIFactory(void);
+}
+#endif /* EXTENSIONS_JNILOADER_H */
diff --git a/extensions/expression-language/noop/expression/Expression.h b/extensions/jni/JVMCreator.cpp
similarity index 67%
copy from extensions/expression-language/noop/expression/Expression.h
copy to extensions/jni/JVMCreator.cpp
index 5cbebfe..045b2bf 100644
--- a/extensions/expression-language/noop/expression/Expression.h
+++ b/extensions/jni/JVMCreator.cpp
@@ -1,4 +1,5 @@
 /**
+ *
  * 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.
@@ -15,35 +16,34 @@
  * limitations under the License.
  */
 
-#ifndef NIFI_MINIFI_CPP_EXPRESSION_H
-#define NIFI_MINIFI_CPP_EXPRESSION_H
+#include "JVMCreator.h"
+
+#ifndef WIN32
 
-#include <core/FlowFile.h>
-#include <Value.h>
+#include <sys/types.h>
+#include <dirent.h>
+#endif
 
 namespace org {
 namespace apache {
 namespace nifi {
 namespace minifi {
-namespace expression {
-
-typedef struct {
-  std::weak_ptr<core::FlowFile> flow_file;
-} Parameters;
+namespace jni {
 
-/**
- * A minimal definition of an Expression with a NoOp implementation.
- */
-class Expression {
- public:
+#ifndef S_ISDIR
+#define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR)
+#endif
+#ifndef R_OK
+#define R_OK    4       /* Test for read permission.  */
+#define W_OK    2       /* Test for write permission.  */
+#define F_OK    0       /* Test for existence.  */
+#endif
+JVMCreator::~JVMCreator() {
 
-  explicit Expression(std::string, std::function<std::string(const Parameters &)>);
-};
+}
 
-} /* namespace expression */
+} /* namespace jni */
 } /* namespace minifi */
 } /* namespace nifi */
 } /* namespace apache */
 } /* namespace org */
-
-#endif //NIFI_MINIFI_CPP_EXPRESSION_H
diff --git a/extensions/jni/JVMCreator.h b/extensions/jni/JVMCreator.h
new file mode 100644
index 0000000..8981701
--- /dev/null
+++ b/extensions/jni/JVMCreator.h
@@ -0,0 +1,113 @@
+/**
+ *
+ * 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.
+ */
+
+#ifndef EXTENSIONS_JNI_JVMCREATOR_H_
+#define EXTENSIONS_JNI_JVMCREATOR_H_
+
+#include <vector>
+#include <string>
+#include <memory>
+#include "jvm/JVMLoader.h"
+#include "jvm/JavaControllerService.h"
+#include "utils/file/FileUtils.h"
+#include "core/Core.h"
+#include "core/logging/LoggerConfiguration.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+
+/**
+ * Can be used to load the JVM from NiFi properties.
+ */
+class JVMCreator : public minifi::core::CoreComponent {
+ public:
+
+  explicit JVMCreator(const std::string &name, utils::Identifier uuid = utils::Identifier())
+      : minifi::core::CoreComponent(name, uuid),
+        loader_(nullptr),
+        logger_(logging::LoggerFactory<JVMCreator>::getLogger()) {
+  }
+
+  virtual ~JVMCreator();
+
+  void configure(const std::vector<std::string> &jarFileListings) {
+    std::vector<std::string> pathOrFiles;
+    for (const auto &path : jarFileListings) {
+      const auto vec = utils::StringUtils::split(path, ",");
+      pathOrFiles.insert(pathOrFiles.end(), vec.begin(), vec.end());
+    }
+
+    for (const auto &path : pathOrFiles) {
+      minifi::utils::file::FileUtils::addFilesMatchingExtension(logger_, path, ".jar", classpaths_);
+    }
+
+  }
+
+  virtual void configure(const std::shared_ptr<Configure> &configuration) override {
+    std::string pathListings, jvmOptionsStr;
+
+    // assuming we have the options set and can access the JVMCreator
+
+    if (configuration->get("nifi.framework.dir", pathListings)) {
+      std::vector<std::string> paths;
+      paths.emplace_back(pathListings);
+      configure(paths);
+
+      if (configuration->get("nifi.jvm.options", jvmOptionsStr)) {
+        jvm_options_ = utils::StringUtils::split(jvmOptionsStr, ",");
+      }
+
+      initializeJVM();
+    }
+    std::string nar_dir, nar_dep, nar_docs;
+    if (loader_ && configuration->get("nifi.nar.directory", nar_dir) && configuration->get("nifi.nar.deploy.directory", nar_dep) && configuration->get("nifi.nar.docs.directory", nar_docs)) {
+      std::shared_ptr<jni::controllers::JavaControllerService> servicer = std::make_shared<jni::controllers::JavaControllerService>("BaseService");
+      servicer->initialize();
+      servicer->setProperty(jni::controllers::JavaControllerService::NarDirectory, nar_dir);
+      servicer->setProperty(jni::controllers::JavaControllerService::NarDeploymentDirectory, nar_dep);
+      servicer->setProperty(jni::controllers::JavaControllerService::NarDocumentDirectory, nar_docs);
+      servicer->onEnable();
+      loader_->setBaseServicer(servicer);
+    }
+  }
+
+  void initializeJVM() {
+    loader_ = minifi::jni::JVMLoader::getInstance(classpaths_, jvm_options_);
+  }
+
+ private:
+
+  minifi::jni::JVMLoader *loader_;
+
+  std::vector<std::string> jvm_options_;
+
+  std::vector<std::string> classpaths_;
+
+  std::shared_ptr<logging::Logger> logger_;
+};
+
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
+#endif /* EXTENSIONS_JNI_JVMCREATOR_H_ */
diff --git a/extensions/jni/JavaException.h b/extensions/jni/JavaException.h
new file mode 100644
index 0000000..9fd3bd6
--- /dev/null
+++ b/extensions/jni/JavaException.h
@@ -0,0 +1,117 @@
+/**
+ * @file JavaException.h
+ * JavaException class declaration
+ *
+ * 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.
+ */
+#ifndef __JAVA_EXCEPTION_H__
+#define __JAVA_EXCEPTION_H__
+
+#include <sstream>
+#include <stdexcept>
+#include <errno.h>
+#include <string.h>
+#include "core/expect.h"
+#include <jni.h>
+#include "jvm/JavaDefs.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+
+/**
+ * Purpose: Java Exception. Can gather the message from the JVM if need be.
+ */
+class JavaException : public std::exception {
+ public:
+  // Constructor
+  /*!
+   * Create a new JavaException
+   */
+  JavaException(std::string errorMsg)
+      : message_(std::move(errorMsg)) {
+  }
+
+  // Destructor
+  virtual ~JavaException() noexcept {
+  }
+  virtual const char * what() const noexcept {
+    return message_.c_str();
+  }
+
+ private:
+  // JavaException detailed information
+  std::string message_;
+
+};
+
+static std::string getMessage(JNIEnv *env, jthrowable throwable) {
+
+  jclass clazz = env->FindClass("java/lang/Throwable");
+
+  jmethodID getMessage = env->GetMethodID(clazz, "toString", "()Ljava/lang/String;");
+  if (getMessage == nullptr) {
+    return "";
+  }
+  jstring message = (jstring) env->CallObjectMethod(throwable, getMessage);
+  if (message) {
+    const char *mstr = env->GetStringUTFChars(message, NULL);
+    // do whatever with mstr
+    std::string excp = mstr;
+    env->ReleaseStringUTFChars(message, mstr);
+    env->DeleteLocalRef(message);
+    env->DeleteLocalRef(clazz);
+    return excp;
+  }
+  return "";
+}
+
+/**
+ * Static function to throw the exception if one exists within the ENV.
+ * @param env Requires an environment to check
+ */
+static inline void ThrowIf(JNIEnv *env) {
+  jthrowable throwable = env->ExceptionOccurred();
+  if (UNLIKELY(throwable != nullptr)) {  // we have faith maybe it won't happen!
+    env->ExceptionClear();
+    auto message = getMessage(env, throwable);
+    env->ThrowNew(env->FindClass(EXCEPTION_CLASS), message.c_str());
+    throw JavaException(message);
+  }
+}
+
+static inline void ThrowJava(JNIEnv *env, const char *message) {
+  env->ExceptionClear();
+  env->ThrowNew(env->FindClass(EXCEPTION_CLASS), message);
+}
+
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
+/**
+ * MACROS can make code look worse and more difficult to develop -- but this is a simple
+ * if that has no contrary result path.
+ */
+#define THROW_IF_NULL(expr, env, message) if (UNLIKELY(expr == nullptr)) minifi::jni::ThrowJava(env,message)
+
+#define THROW_IF(expr, env, message) if (UNLIKELY(expr)) minifi::jni::ThrowJava(env,message)
+
+#endif
diff --git a/extensions/jni/README.md b/extensions/jni/README.md
new file mode 100644
index 0000000..894bfbc
--- /dev/null
+++ b/extensions/jni/README.md
@@ -0,0 +1,49 @@
+<!--
+  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.
+-->
+
+# Apache NiFi - MiNiFi - JNI Readme
+
+
+This readme defines the configuration parameters to use JNI functionality within MiNiFi C++
+
+## Table of Contents
+
+- [Description](#description)
+- [Configuration](#configuration)
+
+## Description
+
+JNI provides the ability to access NiFi processors within MiNiFi C++. By exploding NARs, and coupling the framework
+JNI jar that exist within the JNI extension, we can replicate the behavior of NiFi processors.
+
+The subdirectory nifi-framework-jni contains the corresponding JNI library that is needed. Place that into the API directory
+
+## Configuration
+
+To enable JNI capabilities, the following options need to be provided in minifi.properties
+
+    in minifi.properties
+	#directory where base API exists.
+	nifi.framework.dir=./minifi-jni/api
+	
+	# directory where NARs are located
+	nifi.nar.directory=<nar directory>
+	# directory where nars will be deployed
+	nifi.nar.deploy.directory=<deploy directory>
+	
+Optionally, you can specify JVM options in a comma separated list
+	
+	# must be comma separated 
+	nifi.jvm.options=-Xmx1G
diff --git a/extensions/expression-language/noop/expression/Expression.h b/extensions/jni/jvm/JVMLoader.cpp
similarity index 55%
copy from extensions/expression-language/noop/expression/Expression.h
copy to extensions/jni/jvm/JVMLoader.cpp
index 5cbebfe..7e56e86 100644
--- a/extensions/expression-language/noop/expression/Expression.h
+++ b/extensions/jni/jvm/JVMLoader.cpp
@@ -1,4 +1,5 @@
 /**
+ *
  * 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.
@@ -14,36 +15,5 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include "JVMLoader.h"
 
-#ifndef NIFI_MINIFI_CPP_EXPRESSION_H
-#define NIFI_MINIFI_CPP_EXPRESSION_H
-
-#include <core/FlowFile.h>
-#include <Value.h>
-
-namespace org {
-namespace apache {
-namespace nifi {
-namespace minifi {
-namespace expression {
-
-typedef struct {
-  std::weak_ptr<core::FlowFile> flow_file;
-} Parameters;
-
-/**
- * A minimal definition of an Expression with a NoOp implementation.
- */
-class Expression {
- public:
-
-  explicit Expression(std::string, std::function<std::string(const Parameters &)>);
-};
-
-} /* namespace expression */
-} /* namespace minifi */
-} /* namespace nifi */
-} /* namespace apache */
-} /* namespace org */
-
-#endif //NIFI_MINIFI_CPP_EXPRESSION_H
diff --git a/extensions/jni/jvm/JVMLoader.h b/extensions/jni/jvm/JVMLoader.h
new file mode 100644
index 0000000..326d313
--- /dev/null
+++ b/extensions/jni/jvm/JVMLoader.h
@@ -0,0 +1,518 @@
+/**
+ *
+ * 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.
+ */
+#ifndef EXTENSIONS_JVMLOADER_H
+#define EXTENSIONS_JVMLOADER_H
+
+#include <string>
+#include <map>
+#include <vector>
+#include <sstream>
+#include <iterator>
+#include <algorithm>
+#include "JavaClass.h"
+#include "JavaServicer.h"
+#include "../JavaException.h"
+#include "core/Core.h"
+#include <jni.h>
+#ifndef WIN32
+#include <dlfcn.h>
+#endif
+#include "Core.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+
+/**
+ * Purpose and Justification: Provides a mapping function for jfields.
+ * Note that jFieldIDs aren't local references, so we don't need to worry
+ * about keeping them around. Thus this class provides us a caching mechanism.
+ */
+class FieldMapping {
+ public:
+  jfieldID getField(const std::string &clazz, const std::string &fnArg) {
+    std::lock_guard<std::mutex> guard(mutex_);
+    auto group = map_.find(clazz);
+    if (group != map_.end()) {
+      auto match = group->second.find(fnArg);
+      if (match != group->second.end()) {
+        return match->second;
+      }
+    }
+    return nullptr;
+  }
+
+  void putField(const std::string &clazz, const std::string &fnArg, jfieldID field) {
+    std::lock_guard<std::mutex> guard(mutex_);
+    map_[clazz].insert(std::make_pair(fnArg, field));
+  }
+
+ private:
+  std::mutex mutex_;
+  std::map<std::string, std::map<std::string, jfieldID>> map_;
+};
+
+typedef jint (*registerNatives_t)(JNIEnv* env, jclass clazz);
+
+jfieldID getPtrField(JNIEnv *env, jobject obj);
+
+template<typename T>
+T *getPtr(JNIEnv *env, jobject obj);
+
+template<typename T>
+void setPtr(JNIEnv *env, jobject obj, T *t);
+
+/**
+ * Purpose and Justification: Provides a singleton reference to a JVM.
+ *
+ * Since JVMs are singular in reference ( meaning that there cannot be more than one
+ * at any given time ), we need to create a singleton access pattern.
+ *
+ */
+class JVMLoader {
+ public:
+
+  bool initialized() {
+    return initialized_;
+  }
+
+  /**
+   * Attach the current thread
+   * @return JNIEnv reference.
+   */
+  JNIEnv *attach(const std::string &name = "") {
+    JNIEnv* jenv;
+    jint ret = jvm_->GetEnv((void**) &jenv, JNI_VERSION_1_8);
+    if (ret == JNI_EDETACHED) {
+      ret = jvm_->AttachCurrentThread((void**) &jenv, NULL);
+      if (ret != JNI_OK || jenv == NULL) {
+        throw std::runtime_error("Could not find class");
+      }
+    }
+
+    return jenv;
+  }
+
+  /**
+   * Returns a reference to an instantiated class loader
+   * @return class loader.
+   */
+  jobject getClassLoader() {
+    return gClassLoader;
+  }
+
+  /**
+   * Removes a class reference
+   * @param name class name
+   */
+  void remove_class(const std::string &name) {
+    std::lock_guard<std::mutex> lock(internal_mutex_);
+    auto finder = objects_.find(name);
+    if (finder != objects_.end()) {
+      auto oldClzz = finder->second;
+      JavaClass clazz(oldClzz);
+      attach(name)->DeleteGlobalRef(clazz.getReference());
+    }
+    objects_.erase(name);
+  }
+
+  /**
+   * Loads a class, creating a global reference to the jclass
+   * @param class name.
+   * @return JavaClass
+   */
+  JavaClass load_class(const std::string &clazz_name, JNIEnv *lenv = nullptr) {
+    // names should come with package/package/name, so we must normalize the name
+
+    JNIEnv *env = lenv;
+
+    // when we do the lookup here we will be using a boostrap CL so we'll need
+    // to ensure class names are not in their canonical form. Since we want to
+    // support both we will do the transition from . to / and later on from
+    // / to . to normalize. Forcing all classes that enter this method
+    // to be a certain way isn't very defensible.
+    std::string name = clazz_name;
+    name = utils::StringUtils::replaceAll(name, ".", "/");
+
+    if (env == nullptr) {
+      env = attach(name);
+    }
+
+    std::lock_guard<std::mutex> lock(internal_mutex_);
+    auto finder = objects_.find(name);
+    if (finder != objects_.end()) {
+      auto oldClzz = finder->second;
+      JavaClass clazz(oldClzz);
+      return clazz;
+    }
+
+    std::string modifiedName = name;
+    modifiedName = utils::StringUtils::replaceAll(modifiedName, "/", ".");
+
+    auto jstringclass = env->NewStringUTF(modifiedName.c_str());
+
+    auto preclass = env->CallObjectMethod(gClassLoader, gFindClassMethod, jstringclass);
+
+    minifi::jni::ThrowIf(env);
+
+    auto obj = (jclass) env->NewGlobalRef(preclass);
+
+    minifi::jni::ThrowIf(env);
+
+    auto clazzobj = static_cast<jclass>(obj);
+
+    JavaClass clazz(name, clazzobj, env);
+    objects_.insert(std::make_pair(name, clazz));
+    return clazz;
+  }
+
+  JavaClass getObjectClass(const std::string &name, jobject jobj) {
+    auto env = attach();
+    auto jcls = (jclass) env->NewGlobalRef(env->GetObjectClass(jobj));
+    return JavaClass(name, jcls, env);
+  }
+
+  static JVMLoader *getInstance() {
+    static JVMLoader jvm;
+    return &jvm;
+  }
+
+  JNIEnv *getEnv() {
+    return env_;
+  }
+
+  /**
+   * Returns an instance to the JVMLoader
+   * @param pathVector vector of paths
+   * @param otherOptions jvm options.
+   */
+  static JVMLoader *getInstance(const std::vector<std::string> &pathVector, const std::vector<std::string> &otherOptions = std::vector<std::string>()) {
+    JVMLoader *jvm = getInstance();
+    if (!jvm->initialized()) {
+      std::stringstream str;
+      std::vector<std::string> options;
+      for (const auto &path : pathVector) {
+        if (str.str().length() > 0) {
+#ifdef WIN32
+          str << ";" << path;
+#else
+          str << ":" << path;
+#endif
+        } else
+          str << path;
+      }
+      options.insert(options.end(), otherOptions.begin(), otherOptions.end());
+      std::string classpath = "-Djava.class.path=" + str.str();
+      options.push_back(classpath);
+      jvm->initialize(options);
+    }
+    return jvm;
+  }
+
+  template<typename T>
+  void setReference(jobject obj, T *t) {
+    setPtr(env_, obj, t);
+  }
+
+  template<typename T>
+  void setReference(jobject obj, JNIEnv *env, T *t) {
+    setPtr(env, obj, t);
+  }
+
+  template<typename T>
+  T *getReference(JNIEnv *env, jobject obj) {
+    return getPtr<T>(env, obj);
+  }
+
+  /**
+   * Get the pointer field to the
+   * This expects nativePtr to exist. I've always used nativePtr because I know others use it across
+   * stack overflow. It's a common field, so the hope is that we can potentially access any class
+   * in which the native pointer is stored in nativePtr.
+   */
+  static jfieldID getPtrField(const std::string &className, JNIEnv *env, jobject obj) {
+    static std::string fn = "nativePtr", args = "J", lookup = "nativePtrJ";
+    auto field = getClassMapping().getField(className, lookup);
+    if (field != nullptr) {
+      return field;
+    }
+
+    jclass c = env->GetObjectClass(obj);
+    return env->GetFieldID(c, "nativePtr", "J");
+  }
+
+  template<typename T>
+  static T *getPtr(JNIEnv *env, jobject obj) {
+    jlong handle = env->GetLongField(obj, getPtrField(minifi::core::getClassName<T>(), env, obj));
+    return reinterpret_cast<T *>(handle);
+  }
+
+  template<typename T>
+  static void setPtr(JNIEnv *env, jobject obj, T *t) {
+    jlong handle = reinterpret_cast<jlong>(t);
+    env->SetLongField(obj, getPtrField(minifi::core::getClassName<T>(), env, obj), handle);
+  }
+
+  void setBaseServicer(std::shared_ptr<JavaServicer> servicer) {
+    java_servicer_ = servicer;
+  }
+
+  std::shared_ptr<JavaServicer> getBaseServicer() const {
+    std::lock_guard<std::mutex> lock(internal_mutex_);
+    return java_servicer_;
+  }
+
+  template<typename T>
+  static void putClassMapping(JNIEnv *env, JavaClass &clazz, const std::string &fieldStr, const std::string &arg) {
+    auto classref = clazz.getReference();
+    auto name = minifi::core::getClassName<T>();
+    auto field = env->GetFieldID(classref, fieldStr.c_str(), arg.c_str());
+    auto fieldName = fieldStr + arg;
+    getClassMapping().putField(name, fieldName, field);
+
+  }
+
+ protected:
+
+  static FieldMapping &getClassMapping() {
+    static FieldMapping map;
+    return map;
+  }
+
+  mutable std::mutex internal_mutex_;
+
+#ifdef WIN32
+
+// base_object doesn't have a handle
+  std::map< HMODULE, std::string > resource_mapping_;
+
+  std::string error_str_;
+  std::string current_error_;
+
+  void store_error() {
+    auto error = GetLastError();
+
+    if (error == 0) {
+      error_str_ = "";
+      return;
+    }
+
+    LPSTR messageBuffer = nullptr;
+    size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+        NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
+
+    current_error_ = std::string(messageBuffer, size);
+
+    //Free the buffer.
+    LocalFree(messageBuffer);
+  }
+
+  void *dlsym(void *handle, const char *name)
+  {
+    FARPROC symbol;
+    HMODULE hModule;
+
+    symbol = GetProcAddress((HMODULE)handle, name);
+
+    if (symbol == nullptr) {
+      store_error();
+
+      for (auto hndl : resource_mapping_)
+      {
+        symbol = GetProcAddress((HMODULE)hndl.first, name);
+        if (symbol != nullptr) {
+          break;
+        }
+      }
+    }
+
+#ifdef _MSC_VER
+#pragma warning( suppress: 4054 )
+#endif
+    return (void*)symbol;
+  }
+
+  const char *dlerror(void)
+  {
+    std::lock_guard<std::mutex> lock(internal_mutex_);
+
+    error_str_ = current_error_;
+
+    current_error_ = "";
+
+    return error_str_.c_str();
+  }
+
+  void *dlopen(const char *file, int mode) {
+    std::lock_guard<std::mutex> lock(internal_mutex_);
+    HMODULE object;
+    char * current_error = NULL;
+    uint32_t uMode = SetErrorMode(SEM_FAILCRITICALERRORS);
+    if (nullptr == file)
+    {
+      HMODULE allModules[1024];
+      HANDLE current_process_id = GetCurrentProcess();
+      DWORD cbNeeded;
+      object = GetModuleHandle(NULL);
+
+      if (!object)
+      store_error();
+      if (EnumProcessModules(current_process_id, allModules,
+              sizeof(allModules), &cbNeeded) != 0)
+      {
+
+        for (uint32_t i = 0; i < cbNeeded / sizeof(HMODULE); i++)
+        {
+          TCHAR szModName[MAX_PATH];
+
+          // Get the full path to the module's file.
+          resource_mapping_.insert(std::make_pair(allModules[i], "minifi-system"));
+        }
+      }
+    }
+    else
+    {
+      char lpFileName[MAX_PATH];
+      int i;
+
+      for (i = 0; i < sizeof(lpFileName) - 1; i++)
+      {
+        if (!file[i])
+        break;
+        else if (file[i] == '/')
+        lpFileName[i] = '\\';
+        else
+        lpFileName[i] = file[i];
+      }
+      lpFileName[i] = '\0';
+      object = LoadLibraryEx(lpFileName, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
+      if (!object)
+      store_error();
+      else if ((mode & RTLD_GLOBAL))
+      resource_mapping_.insert(std::make_pair(object, lpFileName));
+    }
+
+    /* Return to previous state of the error-mode bit flags. */
+    SetErrorMode(uMode);
+
+    return (void *)object;
+
+  }
+
+  int dlclose(void *handle)
+  {
+    std::lock_guard<std::mutex> lock(internal_mutex_);
+
+    HMODULE object = (HMODULE)handle;
+    BOOL ret;
+
+    current_error_ = "";
+    ret = FreeLibrary(object);
+
+    resource_mapping_.erase(object);
+
+    ret = !ret;
+
+    return (int)ret;
+  }
+
+#endif
+
+  inline jclass find_class_global(JNIEnv* env, const char *name) {
+    jclass c = env->FindClass(name);
+    jclass c_global = (jclass) env->NewGlobalRef(c);
+    if (!c) {
+      throw std::runtime_error("Could not find ");
+    }
+    return c_global;
+  }
+
+  void initialize(const std::vector<std::string> &opts) {
+    string_options_ = opts;
+    java_options_ = new JavaVMOption[opts.size()];
+    int i = 0;
+    for (const auto &opt : string_options_) {
+      java_options_[i++].optionString = const_cast<char*>(opt.c_str());
+    }
+
+    JavaVMInitArgs vm_args;
+    // rely on 1.8 and above
+    vm_args.version = JNI_VERSION_1_8;
+    vm_args.nOptions = opts.size();
+    vm_args.options = java_options_;
+    // if we see an unrecognized option fail.
+    vm_args.ignoreUnrecognized = JNI_FALSE;
+    // load and initialize a Java VM, return a JNI interface
+    // pointer in env
+    JNI_CreateJavaVM(&jvm_, (void**) &env_, &vm_args);
+    // we're actually using a known class to locate the class loader and provide it
+    // to referentially perform lookups.
+    auto randomClass = find_class_global(env_, "org/apache/nifi/processor/ProcessContext");
+    jclass classClass = env_->GetObjectClass(randomClass);
+    auto classLoaderClass = find_class_global(env_, "java/lang/ClassLoader");
+    auto getClassLoaderMethod = env_->GetMethodID(classClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
+    gClassLoader = env_->NewGlobalRef(env_->CallObjectMethod(randomClass, getClassLoaderMethod));
+    gFindClassMethod = env_->GetMethodID(classLoaderClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
+    minifi::jni::ThrowIf(env_);
+    initialized_ = true;
+  }
+
+ private:
+
+  std::atomic<bool> initialized_;
+
+  std::map<std::string, JavaClass> objects_;
+
+  std::vector<std::string> string_options_;
+
+  JavaVMOption* java_options_;
+
+  JavaVM *jvm_;
+  JNIEnv *env_;
+
+  jobject gClassLoader;
+  jmethodID gFindClassMethod;
+
+  std::shared_ptr<JavaServicer> java_servicer_;
+
+  JVMLoader()
+      : java_options_(nullptr),
+        jvm_(nullptr),
+        env_(nullptr),
+        java_servicer_(nullptr),
+        gFindClassMethod(nullptr),
+        gClassLoader(nullptr) {
+    initialized_ = false;
+  }
+
+  ~JVMLoader() {
+    if (java_options_) {
+      delete[] java_options_;
+    }
+  }
+};
+
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
+#endif /* EXTENSIONS_JVMLOADER_H */
diff --git a/extensions/jni/jvm/JavaClass.h b/extensions/jni/jvm/JavaClass.h
new file mode 100644
index 0000000..32d43b9
--- /dev/null
+++ b/extensions/jni/jvm/JavaClass.h
@@ -0,0 +1,139 @@
+/**
+ *
+ * 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.
+ */
+#ifndef EXTENSIONS_JAVACLASS_H
+#define EXTENSIONS_JAVACLASS_H
+
+#include <string>
+#include <vector>
+#include <sstream>
+#include <iterator>
+#include <algorithm>
+#include <jni.h>
+#include "JniProcessContext.h"
+#include "JniFlowFile.h"
+#include "JniProcessSession.h"
+#include "JniMethod.h"
+#include "../JavaException.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+
+/**
+ * Purpose and Justification: Represents a java class that can be used
+ * for caching access to Java objects and classes.
+ *
+ */
+class JavaClass {
+
+ public:
+
+  JavaClass()
+      : class_ref_(nullptr) {
+
+  }
+
+  /**
+   * Initializes the java class with the name ( package + class name )
+   * @param name class name
+   * @param classref class reference
+   * @param jenv Java environment -- should be thread associated
+   */
+  explicit JavaClass(const std::string &name, jclass classref, JNIEnv* jenv)
+      : name_(name) {
+    class_ref_ = classref;
+    cnstrctr = jenv->GetMethodID(class_ref_, "<init>", "()V");
+  }
+
+  ~JavaClass() {
+  }
+
+  std::string getName() const {
+    return name_;
+  }
+
+  jclass getReference() const {
+    return class_ref_;
+  }
+
+  /**
+   * Though this is implicitly created, we'd like to make this aspect
+   * clear that we're likely to copy JavaClasses around.
+   */
+  JavaClass &operator=(const JavaClass &o) = default;
+
+  /**
+   * Call empty constructor
+   * @param env if supplied
+   * @return jobject that is a global reference.
+   */
+  JNIEXPORT
+  jobject newInstance(JNIEnv *env) {
+    std::string instanceName = "(L" + name_ + ";)V";
+    JNIEnv *lenv = env;
+
+    ThrowIf(lenv);
+
+    auto rezobj = lenv->NewObject(class_ref_, cnstrctr);
+
+    ThrowIf(lenv);
+
+    return lenv->NewGlobalRef(rezobj);
+  }
+
+  jmethodID getClassMethod(JNIEnv *env, const std::string &methodName, const std::string &type) {
+    jmethodID mid = env->GetMethodID(class_ref_, methodName.c_str(), type.c_str());
+    return mid;
+  }
+
+  void registerMethods(JNIEnv *env, JNINativeMethod *methods, size_t size) {
+    env->RegisterNatives(class_ref_, methods, size);
+    ThrowIf(env);
+
+  }
+
+  void registerMethods(JNIEnv *env, JavaSignatures &signatures) {
+    auto methods = signatures.getSignatures();
+    env->RegisterNatives(class_ref_, methods, signatures.getSize());
+    ThrowIf(env);
+
+  }
+
+  template<typename ... Args>
+  void callVoidMethod(JNIEnv* env, jobject obj, const std::string &methodName, const std::string &type, Args ... args) {
+    jmethodID method = getClassMethod(env, methodName, type);
+    ThrowIf(env);
+    env->CallVoidMethod(obj, method, std::forward<Args>(args)...);
+    ThrowIf(env);
+  }
+
+ private:
+  jmethodID cnstrctr;
+  std::string name_;
+  jclass class_ref_;
+};
+
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
+#endif /* EXTENSIONS_JAVACLASS_H */
diff --git a/extensions/jni/jvm/JavaControllerService.cpp b/extensions/jni/jvm/JavaControllerService.cpp
new file mode 100644
index 0000000..bccc4a8
--- /dev/null
+++ b/extensions/jni/jvm/JavaControllerService.cpp
@@ -0,0 +1,117 @@
+/**
+ *
+ * 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.
+ */
+
+#include "../jvm/JavaControllerService.h"
+
+#include <string>
+#include <memory>
+#include <algorithm>
+#include <iterator>
+#include <set>
+#include "core/Property.h"
+#include "io/validation.h"
+#include "utils/StringUtils.h"
+#include "utils/file/FileUtils.h"
+#include "properties/Configure.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+namespace controllers {
+
+#ifndef S_ISDIR
+#define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR)
+#endif
+#ifndef R_OK
+#define R_OK    4       /* Test for read permission.  */
+#define W_OK    2       /* Test for write permission.  */
+#define F_OK    0       /* Test for existence.  */
+#endif
+static core::Property NarDirectory;
+static core::Property NarDeploymentDirectory;
+static core::Property NarDocumentDirectory;
+
+core::Property JavaControllerService::NarDirectory(
+    core::PropertyBuilder::createProperty("Nar Directory")->withDescription("Directory containing the nars to deploy")->isRequired(true)->supportsExpressionLanguage(false)->build());
+
+core::Property JavaControllerService::NarDeploymentDirectory(
+    core::PropertyBuilder::createProperty("Nar Deployment Directory")->withDescription("Directory in which nars will be deployed")->isRequired(true)->supportsExpressionLanguage(false)->build());
+
+core::Property JavaControllerService::NarDocumentDirectory(
+    core::PropertyBuilder::createProperty("Nar Document Directory")->withDescription("Directory in which documents will be deployed")->isRequired(true)->supportsExpressionLanguage(false)->build());
+
+void JavaControllerService::initialize() {
+  if (initialized_)
+    return;
+
+  std::lock_guard<std::mutex> lock(initialization_mutex_);
+
+  ControllerService::initialize();
+
+  std::set<core::Property> supportedProperties;
+  supportedProperties.insert(NarDirectory);
+  supportedProperties.insert(NarDeploymentDirectory);
+  supportedProperties.insert(NarDocumentDirectory);
+
+  setSupportedProperties(supportedProperties);
+
+  initialized_ = true;
+}
+
+void JavaControllerService::onEnable() {
+  std::vector<std::string> pathOrFiles;
+
+  core::Property prop = NarDirectory;
+
+  std::string nardir, narscratch, nardocs;
+  if (getProperty(NarDirectory.getName(), prop)) {
+    nardir = prop.getValue().to_string();
+  }
+
+  prop = NarDeploymentDirectory;
+
+  if (getProperty(NarDeploymentDirectory.getName(), prop)) {
+    narscratch = prop.getValue().to_string();
+  }
+
+  prop = NarDocumentDirectory;
+
+  if (getProperty(NarDocumentDirectory.getName(), prop)) {
+    nardocs = prop.getValue().to_string();
+  }
+
+  for (const auto &path : pathOrFiles) {
+    utils::file::FileUtils::addFilesMatchingExtension(logger_, path, ".jar", classpaths_);
+  }
+
+  loader = JVMLoader::getInstance();
+
+  narClassLoaderClazz = loadClass("org/apache/nifi/processor/JniClassLoader");
+
+  nar_loader_ = std::unique_ptr<NarClassLoader>(new NarClassLoader(shared_from_this(), narClassLoaderClazz, nardir, narscratch, nardocs));
+
+}
+
+} /* namespace controllers */
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
diff --git a/extensions/jni/jvm/JavaControllerService.h b/extensions/jni/jvm/JavaControllerService.h
new file mode 100644
index 0000000..c497bc0
--- /dev/null
+++ b/extensions/jni/jvm/JavaControllerService.h
@@ -0,0 +1,170 @@
+/**
+ *
+ * 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.
+ */
+#ifndef LIBMINIFI_INCLUDE_CONTROLLERS_JAVACONTROLLERSERVICE_H_
+#define LIBMINIFI_INCLUDE_CONTROLLERS_JAVACONTROLLERSERVICE_H_
+
+#include <iostream>
+#include <memory>
+#include <vector>
+#include <string>
+
+#include "../jvm/JVMLoader.h"
+#include "NarClassLoader.h"
+#include "core/Resource.h"
+#include "utils/StringUtils.h"
+#include "JavaServicer.h"
+#include "io/validation.h"
+#include "core/controller/ControllerService.h"
+#include "core/logging/LoggerConfiguration.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+namespace controllers {
+
+/**
+ * Purpose and Justification: Java Controller Service is intended to be used either within the flow or
+ * based on a static load in JVM Creator. The static load simply loads via minifi properties.
+ *
+ */
+class JavaControllerService : public core::controller::ControllerService, public std::enable_shared_from_this<JavaControllerService>, public JavaServicer {
+ public:
+  explicit JavaControllerService(const std::string &name, const std::string &id)
+      : ControllerService(name, id),
+        loader(nullptr),
+        logger_(logging::LoggerFactory<JavaControllerService>::getLogger()) {
+    initialized_ = false;
+  }
+
+  explicit JavaControllerService(const std::string &name, utils::Identifier uuid = utils::Identifier())
+      : ControllerService(name, uuid),
+        loader(nullptr),
+        logger_(logging::LoggerFactory<JavaControllerService>::getLogger()) {
+    initialized_ = false;
+  }
+
+  explicit JavaControllerService(const std::string &name, const std::shared_ptr<Configure> &configuration)
+      : ControllerService(name),
+        loader(nullptr),
+        logger_(logging::LoggerFactory<JavaControllerService>::getLogger()) {
+    initialized_ = false;
+    setConfiguration(configuration);
+    initialize();
+  }
+
+  static core::Property NarDirectory;
+  static core::Property NarDeploymentDirectory;
+  static core::Property NarDocumentDirectory;
+
+  virtual void initialize() override;
+
+  void yield() override {
+  }
+
+  bool isRunning() override {
+    return getState() == core::controller::ControllerServiceState::ENABLED;
+  }
+
+  bool isWorkAvailable() override {
+    return false;
+  }
+
+  virtual void onEnable() override;
+
+  std::vector<std::string> getPaths() const {
+    return classpaths_;
+  }
+
+  JavaClass getObjectClass(const std::string &name, jobject jobj) {
+    return loader->getObjectClass(name, jobj);
+  }
+
+  virtual JavaClass loadClass(const std::string &class_name_) override {
+    std::string modifiedName = class_name_;
+    modifiedName = utils::StringUtils::replaceAll(modifiedName, ".", "/");
+    return loader->load_class(modifiedName);
+  }
+
+  virtual JNIEnv *attach() override {
+    return loader->attach();
+  }
+
+  virtual jobject getClassLoader() override {
+    return loader->getClassLoader();
+  }
+
+  template<typename T>
+  void setReference(JNIEnv *env, jobject obj, T *t) {
+    loader->setReference(obj, env, t);
+  }
+
+  template<typename T>
+  void putNativeFunctionMapping(JNIEnv *env, JavaClass &clazz, const std::string &fieldStr = "nativePtr", const std::string &arg = "J") {
+    JVMLoader::putClassMapping<T>(env, clazz, fieldStr, arg);
+  }
+
+  /**
+   * Retrieves a matching annotation
+   * @param requested class name,
+   * @param method_name method name to obtain
+   */
+  std::pair<std::string, std::string> getAnnotation(const std::string &requested_name, const std::string &method_name) {
+    return nar_loader_->getAnnotation(requested_name, method_name);
+  }
+  /**
+   * creates a new instance
+   * @param requested_name reqeusted class name
+   */
+  jobject newInstance(const std::string &requested_name) {
+    return nar_loader_->newInstance(requested_name);
+  }
+
+ protected:
+
+ // void addPath(std::vector<std::string> &jarFiles, const std::string &dir);
+
+ private:
+
+  JavaClass narClassLoaderClazz;
+
+  std::mutex initialization_mutex_;
+
+  std::atomic<bool> initialized_;
+
+  std::vector<std::string> classpaths_;
+
+  std::unique_ptr<NarClassLoader> nar_loader_;
+
+  JVMLoader *loader;
+
+  std::shared_ptr<logging::Logger> logger_;
+
+};
+
+REGISTER_RESOURCE(JavaControllerService, "Allows specification of nars to be used within referenced processors. ");
+
+} /* namespace controllers */
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
+#endif /* LIBMINIFI_INCLUDE_CONTROLLERS_JAVACONTROLLERSERVICE_H_ */
diff --git a/extensions/expression-language/noop/expression/Expression.h b/extensions/jni/jvm/JavaDefs.h
similarity index 55%
copy from extensions/expression-language/noop/expression/Expression.h
copy to extensions/jni/jvm/JavaDefs.h
index 5cbebfe..d9dadde 100644
--- a/extensions/expression-language/noop/expression/Expression.h
+++ b/extensions/jni/jvm/JavaDefs.h
@@ -1,4 +1,5 @@
 /**
+ *
  * 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.
@@ -15,35 +16,12 @@
  * limitations under the License.
  */
 
-#ifndef NIFI_MINIFI_CPP_EXPRESSION_H
-#define NIFI_MINIFI_CPP_EXPRESSION_H
-
-#include <core/FlowFile.h>
-#include <Value.h>
-
-namespace org {
-namespace apache {
-namespace nifi {
-namespace minifi {
-namespace expression {
+#ifndef EXTENSIONS_JNI_JVM_JAVADEFS_H_
+#define EXTENSIONS_JNI_JVM_JAVADEFS_H_
 
-typedef struct {
-  std::weak_ptr<core::FlowFile> flow_file;
-} Parameters;
-
-/**
- * A minimal definition of an Expression with a NoOp implementation.
- */
-class Expression {
- public:
+#define EXCEPTION_CLASS "java/lang/Exception"
 
-  explicit Expression(std::string, std::function<std::string(const Parameters &)>);
-};
+#define NO_FF_OBJECT "Calling function on null flow file"
 
-} /* namespace expression */
-} /* namespace minifi */
-} /* namespace nifi */
-} /* namespace apache */
-} /* namespace org */
 
-#endif //NIFI_MINIFI_CPP_EXPRESSION_H
+#endif /* EXTENSIONS_JNI_JVM_JAVADEFS_H_ */
diff --git a/extensions/expression-language/noop/expression/Expression.h b/extensions/jni/jvm/JavaServicer.h
similarity index 63%
copy from extensions/expression-language/noop/expression/Expression.h
copy to extensions/jni/jvm/JavaServicer.h
index 5cbebfe..1e80973 100644
--- a/extensions/expression-language/noop/expression/Expression.h
+++ b/extensions/jni/jvm/JavaServicer.h
@@ -1,4 +1,5 @@
 /**
+ *
  * 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.
@@ -15,35 +16,37 @@
  * limitations under the License.
  */
 
-#ifndef NIFI_MINIFI_CPP_EXPRESSION_H
-#define NIFI_MINIFI_CPP_EXPRESSION_H
+#ifndef EXTENSIONS_JNI_JVM_JAVASERVICER_H_
+#define EXTENSIONS_JNI_JVM_JAVASERVICER_H_
 
-#include <core/FlowFile.h>
-#include <Value.h>
+#include <jni.h>
+#include "JavaClass.h"
 
 namespace org {
 namespace apache {
 namespace nifi {
 namespace minifi {
-namespace expression {
-
-typedef struct {
-  std::weak_ptr<core::FlowFile> flow_file;
-} Parameters;
+namespace jni {
 
 /**
- * A minimal definition of an Expression with a NoOp implementation.
+ * Purpose: Java servicer provides a shared construct to be used by classes
+ * within the JNI extension to attach the JNI environment, get the class loader
+ * and load a class.
  */
-class Expression {
+class JavaServicer {
  public:
+  virtual ~JavaServicer() {
 
-  explicit Expression(std::string, std::function<std::string(const Parameters &)>);
+  }
+  virtual JNIEnv *attach() = 0;
+  virtual jobject getClassLoader() = 0;
+  virtual JavaClass loadClass(const std::string &class_name_) = 0;
 };
 
-} /* namespace expression */
+} /* namespace jni */
 } /* namespace minifi */
 } /* namespace nifi */
 } /* namespace apache */
 } /* namespace org */
 
-#endif //NIFI_MINIFI_CPP_EXPRESSION_H
+#endif /* EXTENSIONS_JNI_JVM_JAVASERVICER_H_ */
diff --git a/extensions/jni/jvm/JniBundle.h b/extensions/jni/jvm/JniBundle.h
new file mode 100644
index 0000000..0c2801d
--- /dev/null
+++ b/extensions/jni/jvm/JniBundle.h
@@ -0,0 +1,87 @@
+/**
+ *
+ * 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.
+ */
+#ifndef EXTENSIONS_JNIBUNDLE_H
+#define EXTENSIONS_JNIBUNDLE_H
+
+#include <string>
+#include <vector>
+#include <sstream>
+#include <iterator>
+#include <algorithm>
+#include <jni.h>
+#include "JniProcessContext.h"
+#include "JniFlowFile.h"
+#include "JniProcessSession.h"
+#include "agent/build_description.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+
+/**
+ * Purpose and Justification: JniBundle represents the interconnect between NiFi Java
+ * bundles and MiNiFi C++ bundles.
+ */
+class JniBundle {
+
+ public:
+
+  explicit JniBundle(struct BundleDetails details)
+      : details_(details) {
+  }
+
+  JniBundle() {
+  }
+
+  /**
+   * Add a description to this bundle
+   * @param description
+   */
+  void addDescription(ClassDescription description) {
+    descriptions_.push_back(description);
+  }
+
+  /**
+   * Retrives a copy of the descriptions.
+   */
+  std::vector<ClassDescription> getDescriptions() const {
+    return descriptions_;
+  }
+
+  /**
+   * Returns a copy of BundleDetails.
+   */
+  struct BundleDetails getDetails() const {
+    return details_;
+  }
+
+ private:
+  std::vector<ClassDescription> descriptions_;
+  struct BundleDetails details_;
+
+};
+
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
+#endif /* EXTENSIONS_JNIBUNDLE_H */
diff --git a/extensions/jni/jvm/JniFlowFile.cpp b/extensions/jni/jvm/JniFlowFile.cpp
new file mode 100644
index 0000000..84bf55b
--- /dev/null
+++ b/extensions/jni/jvm/JniFlowFile.cpp
@@ -0,0 +1,153 @@
+/**
+ *
+ * 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.
+ */
+
+#include "JniFlowFile.h"
+
+#include <string>
+#include <memory>
+#include <algorithm>
+#include <iterator>
+#include <set>
+#include "core/Property.h"
+#include "io/validation.h"
+#include "core/FlowFile.h"
+#include "utils/StringUtils.h"
+#include "utils/file/FileUtils.h"
+#include "properties/Configure.h"
+#include "JVMLoader.h"
+#include "../JavaException.h"
+#include "JniReferenceObjects.h"
+#include "JavaDefs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+jlong Java_org_apache_nifi_processor_JniFlowFile_getId(JNIEnv *env, jobject obj) {
+
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, obj);
+
+  auto ff = ptr->get();
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  jlong id = ff->getId();
+  return id;
+
+}
+jlong Java_org_apache_nifi_processor_JniFlowFile_getEntryDate(JNIEnv *env, jobject obj) {
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, obj);
+
+  auto ff = ptr->get();
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  jlong entryDate = ff->getEntryDate();
+  return entryDate;
+}
+jlong Java_org_apache_nifi_processor_JniFlowFile_getLineageStartDate(JNIEnv *env, jobject obj) {
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, obj);
+
+  auto ff = ptr->get();
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  jlong val = ff->getlineageStartDate();
+  return val;
+}
+jlong Java_org_apache_nifi_processor_JniFlowFile_getLineageStartIndex(JNIEnv *env, jobject obj) {
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, obj);
+
+  auto ff = ptr->get();
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  jlong val = ff->getlineageStartDate();
+  return val;
+}
+jlong Java_org_apache_nifi_processor_JniFlowFile_getLastQueueDatePrim(JNIEnv *env, jobject obj) {
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, obj);
+
+  auto ff = ptr->get();
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  jlong val = 0;
+  return val;
+}
+jlong Java_org_apache_nifi_processor_JniFlowFile_getQueueDateIndex(JNIEnv *env, jobject obj) {
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, obj);
+
+  auto ff = ptr->get();
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  jlong val = 0;
+  return val;
+}
+jboolean Java_org_apache_nifi_processor_JniFlowFile_isPenalized(JNIEnv *env, jobject obj) {
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, obj);
+
+  auto ff = ptr->get();
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  jboolean val = ff->isPenalized();
+  return val;
+}
+jstring Java_org_apache_nifi_processor_JniFlowFile_getAttribute(JNIEnv *env, jobject obj, jstring key) {
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, obj);
+
+  auto ff = ptr->get();
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  const char *kstr = env->GetStringUTFChars(key, 0);
+  std::string value;
+  std::string keystr = kstr;
+  ff->getAttribute(keystr, value);
+  env->ReleaseStringUTFChars(key, kstr);
+  return env->NewStringUTF(value.c_str());
+}
+jlong Java_org_apache_nifi_processor_JniFlowFile_getSize(JNIEnv *env, jobject obj) {
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, obj);
+  auto ff = ptr->get();
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  jlong val = ff->getSize();
+  return val;
+}
+jstring Java_org_apache_nifi_processor_JniFlowFile_getUUIDStr(JNIEnv *env, jobject obj) {
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, obj);
+
+  auto ff = ptr->get();
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  return env->NewStringUTF(ff->getUUIDStr().c_str());
+}
+
+jobject Java_org_apache_nifi_processor_JniFlowFile_getAttributes(JNIEnv *env, jobject obj) {
+
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, obj);
+
+  auto ff = ptr->get();
+  jclass mapClass = env->FindClass("java/util/HashMap");
+  if (mapClass == nullptr) {
+    return nullptr;
+  }
+
+  jsize map_len = ff->getAttributes().size();
+
+  jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V");
+  jobject hashMap = env->NewObject(mapClass, init, map_len);
+
+  jmethodID put = env->GetMethodID(mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+
+  for (auto kf : ff->getAttributes()) {
+    env->CallObjectMethod(hashMap, put, env->NewStringUTF(kf.first.c_str()), env->NewStringUTF(kf.second.c_str()));
+    minifi::jni::ThrowIf(env);
+  }
+
+  return hashMap;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/extensions/jni/jvm/JniFlowFile.h b/extensions/jni/jvm/JniFlowFile.h
new file mode 100644
index 0000000..fa6b31c
--- /dev/null
+++ b/extensions/jni/jvm/JniFlowFile.h
@@ -0,0 +1,53 @@
+/**
+ *
+ * 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.
+ */
+
+#ifndef EXTENSIONS_JNI_JVM_JNIFLOWFILE_H_
+#define EXTENSIONS_JNI_JVM_JNIFLOWFILE_H_
+
+#include <string>
+#include <vector>
+#include <sstream>
+#include <iterator>
+#include <algorithm>
+#include <jni.h>
+#include "core/Processor.h"
+#include "core/ProcessSession.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT void JNICALL Java_org_apache_nifi_processor_JniFlowFile_initialise(JNIEnv *env, jobject obj);
+
+JNIEXPORT jlong JNICALL Java_org_apache_nifi_processor_JniFlowFile_getId(JNIEnv *env, jobject obj);
+JNIEXPORT jlong JNICALL Java_org_apache_nifi_processor_JniFlowFile_getEntryDate(JNIEnv *env, jobject obj);
+JNIEXPORT jlong JNICALL Java_org_apache_nifi_processor_JniFlowFile_getLineageStartDate(JNIEnv *env, jobject obj);
+JNIEXPORT jlong JNICALL Java_org_apache_nifi_processor_JniFlowFile_getLineageStartIndex(JNIEnv *env, jobject obj);
+JNIEXPORT jlong JNICALL Java_org_apache_nifi_processor_JniFlowFile_getLastQueueDatePrim(JNIEnv *env, jobject obj);
+JNIEXPORT jboolean JNICALL Java_org_apache_nifi_processor_JniFlowFile_isPenalized(JNIEnv *env, jobject obj);
+JNIEXPORT jlong JNICALL Java_org_apache_nifi_processor_JniFlowFile_getQueueDateIndex(JNIEnv *env, jobject obj);
+JNIEXPORT jstring JNICALL Java_org_apache_nifi_processor_JniFlowFile_getAttribute(JNIEnv *env, jobject obj, jstring key);
+JNIEXPORT jstring JNICALL Java_org_apache_nifi_processor_JniFlowFile_getUUIDStr(JNIEnv *env, jobject obj);
+JNIEXPORT jlong JNICALL Java_org_apache_nifi_processor_JniFlowFile_getSize(JNIEnv *env, jobject obj);
+JNIEXPORT jobject JNICALL Java_org_apache_nifi_processor_JniFlowFile_getAttributes(JNIEnv *env, jobject obj);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXTENSIONS_JNI_JVM_JNIFLOWFILE_H_ */
diff --git a/extensions/jni/jvm/JniLogger.cpp b/extensions/jni/jvm/JniLogger.cpp
new file mode 100644
index 0000000..9cbc44d
--- /dev/null
+++ b/extensions/jni/jvm/JniLogger.cpp
@@ -0,0 +1,107 @@
+/**
+ *
+ * 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.
+ */
+
+#include "JniLogger.h"
+
+#include <string>
+#include <memory>
+#include <algorithm>
+#include <iterator>
+#include <set>
+#include "core/Property.h"
+#include "io/validation.h"
+#include "utils/StringUtils.h"
+#include "utils/file/FileUtils.h"
+#include "properties/Configure.h"
+#include "JVMLoader.h"
+
+#include "core/Processor.h"
+#include "JniFlowFile.h"
+#include "../JavaException.h"
+#include "core/logging/Logger.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+jboolean Java_org_apache_nifi_processor_JniLogger_isWarnEnabled(JNIEnv *env, jobject obj) {
+  minifi::jni::JniLogger *logger_ref = minifi::jni::JVMLoader::getPtr<minifi::jni::JniLogger>(env, obj);
+  return logger_ref->logger_reference_->should_log(core::logging::LOG_LEVEL::warn);
+}
+jboolean Java_org_apache_nifi_processor_JniLogger_isTraceEnabled(JNIEnv *env, jobject obj) {
+  minifi::jni::JniLogger *logger_ref = minifi::jni::JVMLoader::getPtr<minifi::jni::JniLogger>(env, obj);
+  return logger_ref->logger_reference_->should_log(core::logging::LOG_LEVEL::trace);
+}
+jboolean Java_org_apache_nifi_processor_JniLogger_isInfoEnabled(JNIEnv *env, jobject obj) {
+  minifi::jni::JniLogger *logger_ref = minifi::jni::JVMLoader::getPtr<minifi::jni::JniLogger>(env, obj);
+  return logger_ref->logger_reference_->should_log(core::logging::LOG_LEVEL::info);
+}
+jboolean Java_org_apache_nifi_processor_JniLogger_isErrorEnabled(JNIEnv *env, jobject obj) {
+  minifi::jni::JniLogger *logger_ref = minifi::jni::JVMLoader::getPtr<minifi::jni::JniLogger>(env, obj);
+  return logger_ref->logger_reference_->should_log(core::logging::LOG_LEVEL::err);
+}
+jboolean Java_org_apache_nifi_processor_JniLogger_isDebugEnabled(JNIEnv *env, jobject obj) {
+  minifi::jni::JniLogger *logger_ref = minifi::jni::JVMLoader::getPtr<minifi::jni::JniLogger>(env, obj);
+  return logger_ref->logger_reference_->should_log(core::logging::LOG_LEVEL::debug);
+}
+
+void Java_org_apache_nifi_processor_JniLogger_warn(JNIEnv *env, jobject obj, jstring msg) {
+  minifi::jni::JniLogger *logger_ref = minifi::jni::JVMLoader::getPtr<minifi::jni::JniLogger>(env, obj);
+  if (!logger_ref)
+    return;
+  const char *msgStr = env->GetStringUTFChars(msg, 0);
+  logger_ref->logger_reference_->log_warn(msgStr);
+  env->ReleaseStringUTFChars(msg, msgStr);
+}
+
+void Java_org_apache_nifi_processor_JniLogger_error(JNIEnv *env, jobject obj, jstring msg) {
+  minifi::jni::JniLogger *logger_ref = minifi::jni::JVMLoader::getPtr<minifi::jni::JniLogger>(env, obj);
+  if (!logger_ref)
+    return;
+  const char *msgStr = env->GetStringUTFChars(msg, 0);
+  logger_ref->logger_reference_->log_error(msgStr);
+  env->ReleaseStringUTFChars(msg, msgStr);
+}
+void Java_org_apache_nifi_processor_JniLogger_info(JNIEnv *env, jobject obj, jstring msg) {
+  minifi::jni::JniLogger *logger_ref = minifi::jni::JVMLoader::getPtr<minifi::jni::JniLogger>(env, obj);
+  if (!logger_ref)
+    return;
+  const char *msgStr = env->GetStringUTFChars(msg, 0);
+  logger_ref->logger_reference_->log_info(msgStr);
+  env->ReleaseStringUTFChars(msg, msgStr);
+}
+void Java_org_apache_nifi_processor_JniLogger_debug(JNIEnv *env, jobject obj, jstring msg) {
+  minifi::jni::JniLogger *logger_ref = minifi::jni::JVMLoader::getPtr<minifi::jni::JniLogger>(env, obj);
+  if (!logger_ref)
+    return;
+  const char *msgStr = env->GetStringUTFChars(msg, 0);
+  logger_ref->logger_reference_->log_debug(msgStr);
+  env->ReleaseStringUTFChars(msg, msgStr);
+}
+void Java_org_apache_nifi_processor_JniLogger_trace(JNIEnv *env, jobject obj, jstring msg) {
+  minifi::jni::JniLogger *logger_ref = minifi::jni::JVMLoader::getPtr<minifi::jni::JniLogger>(env, obj);
+  if (!logger_ref)
+    return;
+  const char *msgStr = env->GetStringUTFChars(msg, 0);
+  logger_ref->logger_reference_->log_trace(msgStr);
+  env->ReleaseStringUTFChars(msg, msgStr);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/extensions/jni/jvm/JniLogger.h b/extensions/jni/jvm/JniLogger.h
new file mode 100644
index 0000000..e65689c
--- /dev/null
+++ b/extensions/jni/jvm/JniLogger.h
@@ -0,0 +1,71 @@
+/**
+ *
+ * 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.
+ */
+#ifndef EXTENSIONS_JNILOGGER_H
+#define EXTENSIONS_JNILOGGER_H
+
+#include <string>
+#include <memory>
+#include <vector>
+#include <sstream>
+#include <iterator>
+#include <algorithm>
+#include <jni.h>
+#include "core/logging/LoggerConfiguration.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+
+class JniLogger {
+ public:
+  jclass getClass() {
+    return clazz_;
+  }
+  jclass clazz_;
+  std::shared_ptr<logging::Logger> logger_reference_;
+};
+
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jboolean JNICALL Java_org_apache_nifi_processor_JniLogger_isWarnEnabled(JNIEnv *env, jobject obj);
+JNIEXPORT jboolean JNICALL Java_org_apache_nifi_processor_JniLogger_isTraceEnabled(JNIEnv *env, jobject obj);
+JNIEXPORT jboolean JNICALL Java_org_apache_nifi_processor_JniLogger_isInfoEnabled(JNIEnv *env, jobject obj);
+JNIEXPORT jboolean JNICALL Java_org_apache_nifi_processor_JniLogger_isErrorEnabled(JNIEnv *env, jobject obj);
+JNIEXPORT jboolean JNICALL Java_org_apache_nifi_processor_JniLogger_isDebugEnabled(JNIEnv *env, jobject obj);
+
+JNIEXPORT void JNICALL Java_org_apache_nifi_processor_JniLogger_warn(JNIEnv *env, jobject obj, jstring msg);
+JNIEXPORT void JNICALL Java_org_apache_nifi_processor_JniLogger_error(JNIEnv *env, jobject obj, jstring msg);
+JNIEXPORT void JNICALL Java_org_apache_nifi_processor_JniLogger_info(JNIEnv *env, jobject obj, jstring msg);
+JNIEXPORT void JNICALL Java_org_apache_nifi_processor_JniLogger_debug(JNIEnv *env, jobject obj, jstring msg);
+JNIEXPORT void JNICALL Java_org_apache_nifi_processor_JniLogger_trace(JNIEnv *env, jobject obj, jstring msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXTENSIONS_JNILOGGER_H */
diff --git a/extensions/jni/jvm/JniMethod.h b/extensions/jni/jvm/JniMethod.h
new file mode 100644
index 0000000..c780b34
--- /dev/null
+++ b/extensions/jni/jvm/JniMethod.h
@@ -0,0 +1,129 @@
+/**
+ *
+ * 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.
+ */
+#ifndef EXTENSIONS_JNI_JVM_JNIMETHOD_H_
+#define EXTENSIONS_JNI_JVM_JNIMETHOD_H_
+
+#include <string>
+#include <vector>
+#include <sstream>
+#include <iterator>
+#include <algorithm>
+#include <jni.h>
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+
+/**
+ * Purpose and Justification: Represents a java method signature. Is used
+ * to contain the method signatures in internal objects.
+ */
+class JavaMethodSignature {
+
+ public:
+  JavaMethodSignature(const JavaMethodSignature &other) = delete;
+  JavaMethodSignature(JavaMethodSignature &&other) = default;
+  JavaMethodSignature(const std::string &name, const std::string &params, void *ptr)
+      : name_(name),
+        params_(params),
+        ptr_(ptr) {
+  }
+
+  /**
+   * Returns the method name.
+   * Const cast here to clean up the caller's interface ( and java would force the loss of const )
+   */
+  const char *getName() const {
+    return name_.c_str();
+  }
+
+  /**
+   * Returns the parameters.
+   * Const cast here to clean up the caller's interface ( and java would force the loss of const )
+   */
+  const char *getParameters() const {
+    return params_.c_str();
+  }
+
+  /**
+   * Returns the function pointer. can't be const.
+   */
+  const void *getPointer() const {
+    return ptr_;
+  }
+
+  JavaMethodSignature &operator=(const JavaMethodSignature &other) = delete;
+  JavaMethodSignature &operator=(JavaMethodSignature &&other) = default;
+
+ private:
+
+  std::string name_;
+  std::string params_;
+  void *ptr_;
+};
+
+/**
+ * Class to keep a list of signatures
+ */
+class JavaSignatures {
+ public:
+  JavaSignatures()
+      : method_ptr_(nullptr),
+        size_(0) {
+  }
+  void addSignature(JavaMethodSignature &&signature) {
+    methods_.emplace_back(std::move(signature));
+  }
+
+  bool empty() const {
+    return methods_.empty() && size_ == 0;
+  }
+
+  const JNINativeMethod *getSignatures() const {
+    std::lock_guard<std::mutex> lock(mutex_);
+    if (method_ptr_ == nullptr || size_ != methods_.size()) {
+      method_ptr_ = std::unique_ptr<JNINativeMethod[]>(new JNINativeMethod[methods_.size()]);
+      size_ = methods_.size();
+      for(int i=0; i < methods_.size(); i++) {
+        method_ptr_[i].fnPtr = const_cast<void*>(methods_[i].getPointer());
+        method_ptr_[i].name = const_cast<char*>(methods_[i].getName());
+        method_ptr_[i].signature = const_cast<char*>(methods_[i].getParameters());
+      }
+    }
+    return method_ptr_.get();
+  }
+
+  size_t getSize() const {
+    return size_;
+  }
+ private:
+  mutable std::mutex mutex_;
+  mutable std::unique_ptr<JNINativeMethod[]> method_ptr_;
+  mutable size_t size_;
+  std::vector<JavaMethodSignature> methods_;
+};
+
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
+#endif /* EXTENSIONS_JNI_JVM_JNIMETHOD_H_ */
diff --git a/extensions/jni/jvm/JniProcessContext.cpp b/extensions/jni/jvm/JniProcessContext.cpp
new file mode 100644
index 0000000..bd87661
--- /dev/null
+++ b/extensions/jni/jvm/JniProcessContext.cpp
@@ -0,0 +1,79 @@
+/**
+ *
+ * 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.
+ */
+
+#include "JniProcessContext.h"
+
+#include <string>
+#include <memory>
+#include <algorithm>
+#include <iterator>
+#include <set>
+#include "core/Property.h"
+#include "io/validation.h"
+#include "utils/StringUtils.h"
+#include "utils/file/FileUtils.h"
+#include "properties/Configure.h"
+#include "JVMLoader.h"
+
+jstring Java_org_apache_nifi_processor_JniProcessContext_getPropertyValue(JNIEnv *env, jobject obj, jstring propertyName) {
+  if (obj == nullptr || propertyName == nullptr) {
+    return nullptr;
+  }
+  std::string value;
+  minifi::jni::JniProcessContext *context = minifi::jni::JVMLoader::getPtr<minifi::jni::JniProcessContext>(env, obj);
+
+  if (context == nullptr || context->context_ == nullptr) {
+    return nullptr;
+  }
+  const char *kstr = env->GetStringUTFChars(propertyName, 0);
+  std::string keystr = kstr;
+  if (!context->context_->getProperty(keystr, value)) {
+    if (!context->context_->getDynamicProperty(keystr, value)) {
+      env->ReleaseStringUTFChars(propertyName, kstr);
+      return nullptr;
+    }
+  }
+
+  env->ReleaseStringUTFChars(propertyName, kstr);
+  return env->NewStringUTF(value.c_str());
+}
+
+jobject Java_org_apache_nifi_processor_JniProcessContext_getPropertyNames(JNIEnv *env, jobject obj) {
+  minifi::jni::JniProcessContext *context = minifi::jni::JVMLoader::getPtr<minifi::jni::JniProcessContext>(env, obj);
+  auto cppProcessor = context->processor_;
+  auto keys = cppProcessor->getProperties();
+  jclass arraylist = env->FindClass("java/util/ArrayList");
+  jmethodID init_method = env->GetMethodID(arraylist, "<init>", "(I)V");
+  jmethodID add_method = env->GetMethodID(arraylist, "add", "(Ljava/lang/Object;)Z");
+  jobject result = env->NewObject(arraylist, init_method, keys.size());
+  for (const auto &s : keys) {
+    if (s.second.isTransient()) {
+      jstring element = env->NewStringUTF(s.first.c_str());
+      env->CallBooleanMethod(result, add_method, element);
+      minifi::jni::ThrowIf(env);
+      env->DeleteLocalRef(element);
+    }
+  }
+  return result;
+}
+
+jobject Java_org_apache_nifi_processor_JniProcessContext_getProcessor(JNIEnv *env, jobject obj) {
+  minifi::jni::JniProcessContext *context = minifi::jni::JVMLoader::getPtr<minifi::jni::JniProcessContext>(env, obj);
+  minifi::jni::ThrowIf(env);
+  return context->nifi_processor_;
+}
diff --git a/extensions/jni/jvm/JniProcessContext.h b/extensions/jni/jvm/JniProcessContext.h
new file mode 100644
index 0000000..2086c18
--- /dev/null
+++ b/extensions/jni/jvm/JniProcessContext.h
@@ -0,0 +1,80 @@
+/**
+ *
+ * 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.
+ */
+#ifndef EXTENSIONS_JNIPROCESSCONTEXT_H
+#define EXTENSIONS_JNIPROCESSCONTEXT_H
+
+#include <string>
+#include <vector>
+#include <sstream>
+#include <iterator>
+#include <algorithm>
+#include <jni.h>
+#include "core/Processor.h"
+#include "core/ProcessSession.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+
+struct JniProcessContext {
+  JniProcessContext()
+      : nifi_processor_(nullptr),
+        processor_(nullptr),
+        context_(nullptr) {
+  }
+
+  jclass clazz_;
+  jobject nifi_processor_;
+  std::shared_ptr<core::Processor> processor_;
+  std::shared_ptr<core::ProcessContext> context_;
+};
+
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jstring JNICALL Java_org_apache_nifi_processor_JniProcessContext_getPropertyValue(JNIEnv *env, jobject obj, jstring propertyName);
+
+JNIEXPORT jobject JNICALL Java_org_apache_nifi_processor_JniProcessContext_getPropertyNames(JNIEnv *env, jobject obj);
+
+JNIEXPORT jobject JNICALL Java_org_apache_nifi_processor_JniProcessContext_getProcessor(JNIEnv *env, jobject obj);
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ class JniProcessContext {
+ public:
+ JniProcessContext(const std::shared_ptr<core::ProcessContext> &ctx)
+ : ctx_(ctx) {
+
+ }
+ private:
+ std::shared_ptr<core::ProcessContext> ctx_;
+ };*/
+
+#endif /* EXTENSIONS_JNIPROCESSCONTEXT_H */
diff --git a/extensions/jni/jvm/JniProcessSession.cpp b/extensions/jni/jvm/JniProcessSession.cpp
new file mode 100644
index 0000000..be25dd2
--- /dev/null
+++ b/extensions/jni/jvm/JniProcessSession.cpp
@@ -0,0 +1,478 @@
+/**
+ *
+ * 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.
+ */
+
+#include "JniProcessSession.h"
+
+#include <string>
+#include <memory>
+#include <algorithm>
+#include <iterator>
+#include <set>
+#include "core/Property.h"
+#include "io/validation.h"
+#include "utils/StringUtils.h"
+#include "utils/file/FileUtils.h"
+#include "properties/Configure.h"
+#include "JVMLoader.h"
+#include "JniReferenceObjects.h"
+
+#include "core/Processor.h"
+#include "JniFlowFile.h"
+#include "../JavaException.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+jobject Java_org_apache_nifi_processor_JniProcessSession_create(JNIEnv *env, jobject obj) {
+  if (obj == nullptr) {
+    return nullptr;
+  }
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+
+  auto ff = minifi::jni::JVMLoader::getInstance()->load_class("org/apache/nifi/processor/JniFlowFile", env);
+
+  auto ff_instance = ff.newInstance(env);
+
+  minifi::jni::ThrowIf(env);
+
+  auto flow_file = session->getSession()->create();
+
+  auto flow = std::make_shared<minifi::jni::JniFlowFile>(flow_file, session->getServicer(), ff_instance);
+
+  flow_file->addReference(flow);
+
+  auto rawFlow = session->addFlowFile(flow);
+
+  minifi::jni::JVMLoader::getInstance()->setReference(ff_instance, env, rawFlow);
+
+  return ff_instance;
+
+}
+
+jobject Java_org_apache_nifi_processor_JniProcessSession_readFlowFile(JNIEnv *env, jobject obj, jobject ff) {
+  if (obj == nullptr) {
+    return nullptr;
+  }
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, ff);
+  if (ptr->get()) {
+
+    auto jincls = minifi::jni::JVMLoader::getInstance()->load_class("org/apache/nifi/processor/JniInputStream", env);
+
+    auto jin = jincls.newInstance(env);
+
+    minifi::jni::ThrowIf(env);
+
+    std::unique_ptr<minifi::jni::JniByteInputStream> callback = std::unique_ptr<minifi::jni::JniByteInputStream>(new minifi::jni::JniByteInputStream(4096));
+
+    session->getSession()->read(ptr->get(), callback.get());
+
+    auto jniInpuStream = std::make_shared<minifi::jni::JniInputStream>(std::move(callback), jin, session->getServicer());
+
+    session->addInputStream(jniInpuStream);
+
+    minifi::jni::JVMLoader::getInstance()->setReference(jin, env, jniInpuStream.get());
+
+    return jin;
+  }
+
+  return nullptr;
+
+}
+
+jint Java_org_apache_nifi_processor_JniInputStream_read(JNIEnv *env, jobject obj) {
+  minifi::jni::JniInputStream *jin = minifi::jni::JVMLoader::getPtr<minifi::jni::JniInputStream>(env, obj);
+  if (obj == nullptr) {
+    // this technically can't happen per JNI specs
+    return -1;
+  }
+  char value = 0;
+  if (jin->read(value) > 0)
+    return value;
+  else
+    return -1;
+}
+
+jint Java_org_apache_nifi_processor_JniInputStream_readWithOffset(JNIEnv *env, jobject obj, jbyteArray arr, jint offset, jint length) {
+  minifi::jni::JniInputStream *jin = minifi::jni::JVMLoader::getPtr<minifi::jni::JniInputStream>(env, obj);
+  if (obj == nullptr) {
+    // this technically can't happen per JNI specs
+    return -1;
+  }
+  return jin->read(env, arr, (int) offset, (int) length);
+}
+
+jboolean Java_org_apache_nifi_processor_JniProcessSession_write(JNIEnv *env, jobject obj, jobject ff, jbyteArray byteArray) {
+  if (obj == nullptr) {
+    return false;
+  }
+
+  THROW_IF((ff == nullptr || byteArray == nullptr), env, "No flowfile to write");
+
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, ff);
+
+  if (ptr->get()) {
+    jbyte* buffer = env->GetByteArrayElements(byteArray, 0);
+    jsize length = env->GetArrayLength(byteArray);
+
+    minifi::jni::JniByteOutStream outStream(buffer, (size_t) length);
+    session->getSession()->write(ptr->get(), &outStream);
+
+    env->ReleaseByteArrayElements(byteArray, buffer, 0);
+
+    return true;
+  }
+
+  return false;
+
+}
+
+jobject Java_org_apache_nifi_processor_JniProcessSession_clone(JNIEnv *env, jobject obj, jobject prevff) {
+  if (obj == nullptr) {
+    return nullptr;
+  }
+  THROW_IF_NULL(prevff, env, NO_FF_OBJECT);
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, prevff);
+
+  if (ptr->get()) {
+    auto ff = minifi::jni::JVMLoader::getInstance()->load_class("org/apache/nifi/processor/JniFlowFile", env);
+
+    auto ff_instance = ff.newInstance(env);
+
+    minifi::jni::ThrowIf(env);
+
+    auto flow_file = session->getSession()->clone(ptr->get());
+
+    auto flow = std::make_shared<minifi::jni::JniFlowFile>(flow_file, session->getServicer(), ff_instance);
+
+    flow_file->addReference(flow);
+
+    auto rawFlow = session->addFlowFile(flow);
+
+    minifi::jni::JVMLoader::getInstance()->setReference(ff_instance, env, rawFlow);
+
+    return ff_instance;
+  }
+
+  return nullptr;
+
+}
+
+jobject Java_org_apache_nifi_processor_JniProcessSession_get(JNIEnv *env, jobject obj) {
+  if (obj == nullptr)
+    return nullptr;
+
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+
+  if (session == nullptr)
+    return nullptr;
+
+  auto flow_file = session->getSession()->get();
+
+  auto prevFF = session->getFlowFileReference(flow_file);
+  if (prevFF != nullptr) {
+    return prevFF->getJniReference();
+  }
+
+  // otherwise create one
+  auto ff = minifi::jni::JVMLoader::getInstance()->load_class("org/apache/nifi/processor/JniFlowFile");
+
+  auto ff_instance = ff.newInstance(env);
+
+  if (flow_file == nullptr || ff_instance == nullptr) {
+    // this is an acceptable condition.
+    return nullptr;
+  }
+
+  auto flow = std::make_shared<minifi::jni::JniFlowFile>(flow_file, session->getServicer(), ff_instance);
+
+  flow_file->addReference(flow);
+
+  auto rawFlow = session->addFlowFile(flow);
+
+  minifi::jni::JVMLoader::getInstance()->setReference(ff_instance, env, rawFlow);
+
+  return ff_instance;
+}
+
+jobject Java_org_apache_nifi_processor_JniProcessSession_putAttribute(JNIEnv *env, jobject obj, jobject ff, jstring key, jstring value) {
+  if (obj == nullptr) {
+    // does not mean an error should be thrown, rather we will let
+    // Java do its thing as this is a condition of GC most likely
+    return nullptr;
+  }
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, ff);
+
+  if (ff == nullptr || key == nullptr || value == nullptr) {
+    return nullptr;
+  }
+
+  const char *kstr = env->GetStringUTFChars(key, 0);
+  const char *vstr = env->GetStringUTFChars(value, 0);
+  std::string valuestr = vstr;
+  std::string keystr = kstr;
+
+  ptr->get()->addAttribute(keystr, valuestr);
+  env->ReleaseStringUTFChars(key, kstr);
+  env->ReleaseStringUTFChars(value, vstr);
+  return ff;
+
+}
+
+void Java_org_apache_nifi_processor_JniProcessSession_transfer(JNIEnv *env, jobject obj, jobject ff, jstring relationship) {
+  if (obj == nullptr) {
+    return;
+  }
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, ff);
+  const char *relstr = env->GetStringUTFChars(relationship, 0);
+  std::string relString = relstr;
+  core::Relationship success(relString, "description");
+  session->getSession()->transfer(ptr->get(), success);
+  env->ReleaseStringUTFChars(relationship, relstr);
+}
+
+jstring Java_org_apache_nifi_processor_JniProcessSession_getPropertyValue(JNIEnv *env, jobject obj, jstring propertyName) {
+  std::string value;
+  if (obj == nullptr) {
+    return env->NewStringUTF(value.c_str());
+  }
+  core::ProcessContext *context = minifi::jni::JVMLoader::getPtr<core::ProcessContext>(env, obj);
+  const char *kstr = env->GetStringUTFChars(propertyName, 0);
+  std::string keystr = kstr;
+  if (!context->getProperty(keystr, value)) {
+    context->getDynamicProperty(keystr, value);
+  }
+  env->ReleaseStringUTFChars(propertyName, kstr);
+  return env->NewStringUTF(value.c_str());
+}
+
+jobject Java_org_apache_nifi_processor_JniProcessSession_createWithParent(JNIEnv *env, jobject obj, jobject parent) {
+  if (obj == nullptr) {
+    // does not mean an error should be thrown, rather we will let
+    // Java do its thing as this is a condition of GC most likely
+    return nullptr;
+  }
+  THROW_IF_NULL(parent, env, NO_FF_OBJECT);
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, parent);
+
+  if (ptr->get()) {
+    auto ff = minifi::jni::JVMLoader::getInstance()->load_class("org/apache/nifi/processor/JniFlowFile", env);
+
+    auto ff_instance = ff.newInstance(env);
+
+    minifi::jni::ThrowIf(env);
+
+    auto flow_file = session->getSession()->create(ptr->get());
+
+    auto flow = std::make_shared<minifi::jni::JniFlowFile>(flow_file, session->getServicer(), ff_instance);
+
+    flow_file->addReference(flow);
+
+    auto rawFlow = session->addFlowFile(flow);
+
+    minifi::jni::JVMLoader::getInstance()->setReference(ff_instance, env, rawFlow);
+
+    return ff_instance;
+  }
+
+  return nullptr;
+
+}
+
+void Java_org_apache_nifi_processor_JniProcessSession_commit(JNIEnv *env, jobject obj) {
+  if (obj == nullptr) {
+    // does not mean an error should be thrown, rather we will let
+    // Java do its thing as this is a condition of GC most likely
+    return;
+  }
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+  try {
+    session->getSession()->commit();
+  } catch (const std::exception &e) {
+    std::string error = "error while committing: ";
+    error += e.what();
+    minifi::jni::ThrowJava(env, error.c_str());
+  } catch (...) {
+    minifi::jni::ThrowJava(env, "error while commiting");
+  }
+}
+
+void Java_org_apache_nifi_processor_JniProcessSession_rollback(JNIEnv *env, jobject obj) {
+  if (obj == nullptr) {
+    // does not mean an error should be thrown, rather we will let
+    // Java do its thing as this is a condition of GC most likely
+    return;
+  }
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+  session->getSession()->rollback();
+}
+
+jobject Java_org_apache_nifi_processor_JniProcessSessionFactory_createSession(JNIEnv *env, jobject obj) {
+  if (obj == nullptr) {
+    // does not mean an error should be thrown, rather we will let
+    // Java do its thing as this is a condition of GC most likely
+    return nullptr;
+  }
+  minifi::jni::JniSessionFactory *sessionFactory = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSessionFactory>(env, obj);
+  auto session_class = minifi::jni::JVMLoader::getInstance()->load_class("org/apache/nifi/processor/JniProcessSession", env);
+
+  auto session_instance = session_class.newInstance(env);
+
+  minifi::jni::ThrowIf(env);
+
+  // create a session
+  auto procSession = sessionFactory->getFactory()->createSession();
+
+  std::shared_ptr<minifi::jni::JniSession> session = std::make_shared<minifi::jni::JniSession>(procSession, session_instance, sessionFactory->getServicer());
+
+  // add a reference so the minifi C++ session factory knows to remove these eventually.
+  procSession->addReference(session);
+
+  auto rawSession = sessionFactory->addSession(session);
+
+  // set the reference in session_instance using the raw pointer.
+  minifi::jni::JVMLoader::getInstance()->setReference(session_instance, env, rawSession);
+
+  // catalog the session
+
+  return session_instance;
+}
+
+void Java_org_apache_nifi_processor_JniProcessSession_remove(JNIEnv *env, jobject obj, jobject ff) {
+  if (obj == nullptr) {
+    // does not mean an error should be thrown, rather we will let
+    // Java do its thing as this is a condition of GC most likely
+    return;
+  }
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, ff);
+
+  if (ptr->get()) {
+    session->getSession()->remove(ptr->get());
+  }
+
+}
+
+jobject Java_org_apache_nifi_processor_JniProcessSession_penalize(JNIEnv *env, jobject obj, jobject ff) {
+  if (obj == nullptr) {
+    // does not mean an error should be thrown, rather we will let
+    // Java do its thing as this is a condition of GC most likely
+    return ff;
+  }
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, ff);
+
+  if (ptr->get()) {
+    session->getSession()->penalize(ptr->get());
+  }
+  return ff;
+}
+
+jobject Java_org_apache_nifi_processor_JniProcessSession_removeAttribute(JNIEnv *env, jobject obj, jobject ff, jstring attr) {
+  if (obj == nullptr) {
+    // does not mean an error should be thrown, rather we will let
+    // Java do its thing as this is a condition of GC most likely
+    return ff;
+  }
+  THROW_IF_NULL(ff, env, NO_FF_OBJECT);
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, ff);
+
+  if (ptr->get()) {
+    const char *attrStr = env->GetStringUTFChars(attr, 0);
+    std::string attribute = attrStr;
+    ptr->get()->removeAttribute(attribute);
+    env->ReleaseStringUTFChars(attr, attrStr);
+  }
+  return ff;
+}
+
+jobject Java_org_apache_nifi_processor_JniProcessSession_clonePortion(JNIEnv *env, jobject obj, jobject prevff, jlong offset, jlong size) {
+  if (obj == nullptr) {
+    // does not mean an error should be thrown, rather we will let
+    // Java do its thing as this is a condition of GC most likely
+    return prevff;
+  }
+  THROW_IF_NULL(prevff, env, NO_FF_OBJECT);
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, prevff);
+
+  if (ptr->get()) {
+    auto ff = minifi::jni::JVMLoader::getInstance()->load_class("org/apache/nifi/processor/JniFlowFile", env);
+
+    auto ff_instance = ff.newInstance(env);
+
+    minifi::jni::ThrowIf(env);
+
+    auto flow_file = session->getSession()->clone(ptr->get(), offset, size);
+
+    auto flow = std::make_shared<minifi::jni::JniFlowFile>(flow_file, session->getServicer(), ff_instance);
+
+    flow_file->addReference(flow);
+
+    auto rawFlow = session->addFlowFile(flow);
+
+    minifi::jni::JVMLoader::getInstance()->setReference(ff_instance, env, rawFlow);
+
+    return ff_instance;
+  }
+
+  return nullptr;
+
+}
+
+jboolean Java_org_apache_nifi_processor_JniProcessSession_append(JNIEnv *env, jobject obj, jobject ff, jbyteArray byteArray) {
+  if (obj == nullptr) {
+    // does not mean an error should be thrown, rather we will let
+    // Java do its thing as this is a condition of GC most likely
+    return false;
+  }
+  THROW_IF((ff == nullptr || byteArray == nullptr), env, NO_FF_OBJECT);
+  minifi::jni::JniSession *session = minifi::jni::JVMLoader::getPtr<minifi::jni::JniSession>(env, obj);
+  minifi::jni::JniFlowFile *ptr = minifi::jni::JVMLoader::getInstance()->getReference<minifi::jni::JniFlowFile>(env, ff);
+
+  if (ptr->get()) {
+    jbyte* buffer = env->GetByteArrayElements(byteArray, 0);
+    jsize length = env->GetArrayLength(byteArray);
+
+    minifi::jni::JniByteOutStream outStream(buffer, (size_t) length);
+    session->getSession()->append(ptr->get(), &outStream);
+
+    env->ReleaseByteArrayElements(byteArray, buffer, 0);
+
+    return true;
+  }
+
+  return false;
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/extensions/jni/jvm/JniProcessSession.h b/extensions/jni/jvm/JniProcessSession.h
new file mode 100644
index 0000000..ddec7ea
--- /dev/null
+++ b/extensions/jni/jvm/JniProcessSession.h
@@ -0,0 +1,90 @@
+/**
+ *
+ * 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.
+ */
+#ifndef EXTENSIONS_JNIPROCESSSESSION_H
+#define EXTENSIONS_JNIPROCESSSESSION_H
+
+#include <string>
+#include <memory>
+#include <vector>
+#include <sstream>
+#include <iterator>
+#include <algorithm>
+#include <jni.h>
+#include "io/BaseStream.h"
+#include "FlowFileRecord.h"
+#include "core/ProcessSession.h"
+#include "core/ProcessSessionFactory.h"
+#include "core/WeakReference.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jobject JNICALL Java_org_apache_nifi_processor_JniProcessSession_readFlowFile(JNIEnv *env, jobject obj, jobject ff);
+
+JNIEXPORT jobject JNICALL Java_org_apache_nifi_processor_JniProcessSession_get(JNIEnv *env, jobject obj);
+
+JNIEXPORT jobject JNICALL Java_org_apache_nifi_processor_JniProcessSession_create(JNIEnv *env, jobject obj);
+
+JNIEXPORT jobject JNICALL Java_org_apache_nifi_processor_JniProcessSession_clone(JNIEnv *env, jobject obj, jobject ff);
+
+JNIEXPORT jboolean JNICALL Java_org_apache_nifi_processor_JniProcessSession_write(JNIEnv *env, jobject obj, jobject ff, jbyteArray byteArray);
+
+JNIEXPORT void JNICALL Java_org_apache_nifi_processor_JniProcessSession_transfer(JNIEnv *env, jobject obj, jobject ff, jstring relationship);
+
+JNIEXPORT jobject JNICALL Java_org_apache_nifi_processor_JniProcessSession_putAttribute(JNIEnv *env, jobject obj, jobject ff, jstring key, jstring value);
+
+JNIEXPORT jobject JNICALL Java_org_apache_nifi_processor_JniProcessSession_createWithParent(JNIEnv *env, jobject obj, jobject parent);
+
+JNIEXPORT void JNICALL Java_org_apache_nifi_processor_JniProcessSession_commit(JNIEnv *env, jobject obj);
+
+JNIEXPORT void JNICALL Java_org_apache_nifi_processor_JniProcessSession_rollback(JNIEnv *env, jobject obj);
+
+JNIEXPORT jobject JNICALL Java_org_apache_nifi_processor_JniProcessSessionFactory_createSession(JNIEnv *env, jobject obj);
+
+JNIEXPORT void JNICALL Java_org_apache_nifi_processor_JniProcessSession_remove(JNIEnv *env, jobject obj, jobject ff);
+
+JNIEXPORT jobject JNICALL Java_org_apache_nifi_processor_JniProcessSession_penalize(JNIEnv *env, jobject obj, jobject ff);
+
+JNIEXPORT jobject JNICALL Java_org_apache_nifi_processor_JniProcessSession_removeAttribute(JNIEnv *env, jobject obj, jobject ff, jstring attr);
+
+JNIEXPORT jobject JNICALL Java_org_apache_nifi_processor_JniProcessSession_clonePortion(JNIEnv *env, jobject obj, jobject ff, jlong offset, jlong size);
+
+JNIEXPORT jboolean JNICALL Java_org_apache_nifi_processor_JniProcessSession_append(JNIEnv *env, jobject obj, jobject ff, jbyteArray byteArray);
+
+JNIEXPORT jint JNICALL Java_org_apache_nifi_processor_JniInputStream_read(JNIEnv *env, jobject obj);
+
+JNIEXPORT jint JNICALL Java_org_apache_nifi_processor_JniInputStream_readWithOffset(JNIEnv *env, jobject obj, jbyteArray, jint offset, jint length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXTENSIONS_JNIPROCESSSESSION_H */
diff --git a/extensions/expression-language/noop/expression/Expression.h b/extensions/jni/jvm/JniReferenceObjects.cpp
similarity index 67%
copy from extensions/expression-language/noop/expression/Expression.h
copy to extensions/jni/jvm/JniReferenceObjects.cpp
index 5cbebfe..d9f8686 100644
--- a/extensions/expression-language/noop/expression/Expression.h
+++ b/extensions/jni/jvm/JniReferenceObjects.cpp
@@ -1,4 +1,5 @@
 /**
+ *
  * 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.
@@ -15,35 +16,25 @@
  * limitations under the License.
  */
 
-#ifndef NIFI_MINIFI_CPP_EXPRESSION_H
-#define NIFI_MINIFI_CPP_EXPRESSION_H
-
-#include <core/FlowFile.h>
-#include <Value.h>
+#include "JniReferenceObjects.h"
 
 namespace org {
 namespace apache {
 namespace nifi {
 namespace minifi {
-namespace expression {
-
-typedef struct {
-  std::weak_ptr<core::FlowFile> flow_file;
-} Parameters;
-
-/**
- * A minimal definition of an Expression with a NoOp implementation.
- */
-class Expression {
- public:
+namespace jni {
 
-  explicit Expression(std::string, std::function<std::string(const Parameters &)>);
-};
+void JniFlowFile::remove() {
+  std::lock_guard<std::mutex> guard(session_mutex_);
+  if (ff_object) {
+    servicer_->attach()->DeleteGlobalRef(ff_object);
+  }
+  removed = true;
+}
 
-} /* namespace expression */
+} /* namespace jni */
 } /* namespace minifi */
 } /* namespace nifi */
 } /* namespace apache */
 } /* namespace org */
 
-#endif //NIFI_MINIFI_CPP_EXPRESSION_H
diff --git a/extensions/jni/jvm/JniReferenceObjects.h b/extensions/jni/jvm/JniReferenceObjects.h
new file mode 100644
index 0000000..c5355fb
--- /dev/null
+++ b/extensions/jni/jvm/JniReferenceObjects.h
@@ -0,0 +1,374 @@
+/**
+ *
+ * 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.
+ */
+
+#ifndef EXTENSIONS_JNI_JVM_REFERNCEOBJECTS_H_
+#define EXTENSIONS_JNI_JVM_REFERNCEOBJECTS_H_
+
+#include <string>
+#include <vector>
+#include <sstream>
+#include <iterator>
+#include <algorithm>
+#include <jni.h>
+#include "JavaServicer.h"
+#include "core/Processor.h"
+#include "core/ProcessSession.h"
+#include "core/WeakReference.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+
+/**
+ * Represents a flow file. Exists to provide the ability to remove
+ * global references amongst the
+ */
+class JniFlowFile : public core::WeakReference {
+ public:
+  JniFlowFile(std::shared_ptr<core::FlowFile> ref, const std::shared_ptr<JavaServicer> &servicer, jobject ff)
+      : removed(false),
+        ff_object(ff),
+        ref_(ref),
+        servicer_(servicer) {
+
+  }
+
+  virtual ~JniFlowFile() {
+
+  }
+
+  virtual void remove() override;
+
+  std::shared_ptr<core::FlowFile> get() const {
+    return ref_;
+  }
+
+  jobject getJniReference() {
+    return ff_object;
+  }
+
+  bool operator==(const JniFlowFile &other) const {
+    // compare the pointers
+    return ref_ == other.ref_;
+  }
+
+  bool empty() const {
+    return removed;
+  }
+
+ protected:
+
+  bool removed;
+
+  jobject ff_object;
+
+  std::mutex session_mutex_;
+
+  std::shared_ptr<core::FlowFile> ref_;
+
+  std::shared_ptr<JavaServicer> servicer_;
+
+};
+
+/**
+ * Quick check to determine if a FF is empty.
+ */
+struct check_empty_ff : public std::unary_function<std::shared_ptr<JniFlowFile>, bool> {
+  bool operator()(std::shared_ptr<JniFlowFile> session) const {
+    return session->empty();
+  }
+};
+
+class JniByteOutStream : public minifi::OutputStreamCallback {
+ public:
+  JniByteOutStream(jbyte *bytes, size_t length)
+      : bytes_(bytes),
+        length_(length) {
+
+  }
+
+  virtual ~JniByteOutStream() {
+
+  }
+  virtual int64_t process(std::shared_ptr<minifi::io::BaseStream> stream) {
+    return stream->write((uint8_t*) bytes_, length_);
+  }
+ private:
+  jbyte *bytes_;
+  size_t length_;
+};
+
+/**
+ * Jni byte input stream
+ */
+class JniByteInputStream : public minifi::InputStreamCallback {
+ public:
+  JniByteInputStream(uint64_t size)
+      : read_size_(0),
+        stream_(nullptr) {
+    buffer_size_ = size;
+    buffer_ = new uint8_t[buffer_size_];
+  }
+  ~JniByteInputStream() {
+    if (buffer_)
+      delete[] buffer_;
+  }
+  int64_t process(std::shared_ptr<minifi::io::BaseStream> stream) {
+    stream_ = stream;
+    return 0;
+  }
+
+  int64_t read(JNIEnv *env, jbyteArray arr, int offset, int size) {
+    if (stream_ == nullptr) {
+      return -1;
+    }
+
+    // seek to offset
+    int remaining = size;
+    int writtenOffset = 0;
+    int read = 0;
+    do {
+
+      int actual = (int) stream_->read(buffer_, remaining <= buffer_size_ ? remaining : buffer_size_);
+      if (actual <= 0) {
+        if (read == 0) {
+          stream_ = nullptr;
+          return -1;
+        }
+        break;
+      }
+
+      read += actual;
+      env->SetByteArrayRegion(arr, offset + writtenOffset, actual, (jbyte*) buffer_);
+      writtenOffset += (int) actual;
+
+      remaining -= actual;
+
+    } while (remaining > 0);
+
+    return read;
+  }
+
+  int64_t read(char &arr) {
+    return stream_->read(arr);
+  }
+
+  std::shared_ptr<minifi::io::BaseStream> stream_;
+  uint8_t *buffer_;
+  uint64_t buffer_size_;
+  uint64_t read_size_;
+};
+
+class JniInputStream : public core::WeakReference {
+ public:
+  JniInputStream(std::unique_ptr<JniByteInputStream> jbi, jobject in_instance, const std::shared_ptr<JavaServicer> &servicer)
+      : removed_(false),
+        jbi_(std::move(jbi)),
+        in_instance_(in_instance),
+        servicer_(servicer) {
+
+  }
+
+  virtual void remove() override {
+    std::lock_guard<std::mutex> guard(mutex_);
+    if (!removed_) {
+      servicer_->attach()->DeleteGlobalRef(in_instance_);
+      removed_ = true;
+      jbi_ = nullptr;
+    }
+
+  }
+
+  int64_t read(char &arr) {
+    if (!removed_) {
+      return jbi_->read(arr);
+    }
+    return -1;
+  }
+
+  int64_t read(JNIEnv *env, jbyteArray arr, int offset, int size) {
+    if (!removed_) {
+      return jbi_->read(env, arr, offset, size);
+    }
+    return -1;
+  }
+
+ private:
+  std::mutex mutex_;
+  bool removed_;
+  jobject in_instance_;
+  std::unique_ptr<JniByteInputStream> jbi_;
+  std::shared_ptr<JavaServicer> servicer_;
+};
+
+class JniSession : public core::WeakReference {
+ public:
+  JniSession(const std::shared_ptr<core::ProcessSession> &session, jobject session_instance, const std::shared_ptr<JavaServicer> &servicer)
+      : removed_(false),
+        session_(session),
+        servicer_(servicer),
+        session_instance_(session_instance) {
+  }
+
+  virtual void remove() override {
+    std::lock_guard<std::mutex> guard(session_mutex_);
+    if (!removed_) {
+      for (auto ff : global_ff_objects_) {
+        ff->remove();
+      }
+
+      for (auto in : input_streams_) {
+        in->remove();
+      }
+      global_ff_objects_.clear();
+      servicer_->attach()->DeleteGlobalRef(session_instance_);
+      removed_ = true;
+    }
+
+  }
+
+  std::shared_ptr<core::ProcessSession> &getSession() {
+    return session_;
+  }
+
+  JniFlowFile *getFlowFileReference(const std::shared_ptr<core::FlowFile> &ff) {
+    for (auto &jni_ff : global_ff_objects_) {
+      if (jni_ff->get().get() == ff.get()) {
+        return jni_ff.get();
+      }
+    }
+    return nullptr;
+  }
+
+  JniFlowFile * addFlowFile(std::shared_ptr<JniFlowFile> ff) {
+    std::lock_guard<std::mutex> guard(session_mutex_);
+    global_ff_objects_.push_back(ff);
+    return ff.get();
+  }
+
+  void addInputStream(std::shared_ptr<JniInputStream> in) {
+    std::lock_guard<std::mutex> guard(session_mutex_);
+    input_streams_.push_back(in);
+  }
+
+  std::shared_ptr<JavaServicer> getServicer() const {
+    return servicer_;
+  }
+
+  bool prune() {
+    global_ff_objects_.erase(std::remove_if(global_ff_objects_.begin(), global_ff_objects_.end(), check_empty_ff()), global_ff_objects_.end());
+    if (global_ff_objects_.empty()) {
+      remove();
+    }
+    return global_ff_objects_.empty();
+  }
+  bool empty() const {
+    std::lock_guard<std::mutex> guard(session_mutex_);
+    for (auto ff : global_ff_objects_) {
+      if (!ff->empty())
+        return false;
+    }
+    return true;
+  }
+
+ protected:
+  bool removed_;
+  mutable std::mutex session_mutex_;
+  jobject session_instance_;
+  std::shared_ptr<core::ProcessSession> session_;
+  std::shared_ptr<JavaServicer> servicer_;
+  std::vector<std::shared_ptr<JniInputStream>> input_streams_;
+  // we own
+  std::vector<std::shared_ptr<JniFlowFile>> global_ff_objects_;
+};
+
+struct check_empty : public std::unary_function<std::shared_ptr<JniSession>, bool> {
+  bool operator()(std::shared_ptr<JniSession> session) const {
+    return session->prune();
+  }
+};
+
+class JniSessionFactory : public core::WeakReference {
+ public:
+
+  JniSessionFactory(const std::shared_ptr<core::ProcessSessionFactory> &factory, const std::shared_ptr<JavaServicer> &servicer, jobject java_object)
+      : servicer_(servicer),
+        java_object_(java_object),
+        factory_(factory) {
+  }
+
+  virtual void remove() override {
+    std::lock_guard<std::mutex> guard(session_mutex_);
+    // remove all of the sessions
+    // this should spark their destructor
+    for (auto session : sessions_) {
+      session->remove();
+    }
+    sessions_.clear();
+
+    if (java_object_) {
+      // detach the global reference and let Java take care of this object.
+      servicer_->attach()->DeleteGlobalRef(java_object_);
+    }
+  }
+
+  jobject getJavaReference() const {
+    return java_object_;
+  }
+
+  std::shared_ptr<JavaServicer> getServicer() const {
+    return servicer_;
+  }
+
+  std::shared_ptr<core::ProcessSessionFactory> getFactory() const {
+    return factory_;
+  }
+
+  /**
+   */
+  JniSession *addSession(std::shared_ptr<JniSession> session) {
+    std::lock_guard<std::mutex> guard(session_mutex_);
+
+    sessions_.erase(std::remove_if(sessions_.begin(), sessions_.end(), check_empty()), sessions_.end());
+
+    sessions_.push_back(session);
+
+    return session.get();
+  }
+
+ protected:
+  std::shared_ptr<JavaServicer> servicer_;
+  std::mutex session_mutex_;
+  // we do not own this shared ptr
+  std::shared_ptr<core::ProcessSessionFactory> factory_;
+  // we own the sessions
+  std::vector<std::shared_ptr<JniSession>> sessions_;
+  // we own the java object
+  jobject java_object_;
+
+};
+
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
+#endif /* EXTENSIONS_JNI_JVM_REFERNCEOBJECTS_H_ */
diff --git a/extensions/jni/jvm/NarClassLoader.h b/extensions/jni/jvm/NarClassLoader.h
new file mode 100644
index 0000000..bd79ddf
--- /dev/null
+++ b/extensions/jni/jvm/NarClassLoader.h
@@ -0,0 +1,443 @@
+/**
+ *
+ * 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.
+ */
+#ifndef EXTENSIONS_NARCLASSLOADER_H
+#define EXTENSIONS_NARCLASSLOADER_H
+
+#include "JavaServicer.h"
+#include "JniBundle.h"
+#include "../JavaException.h"
+#include <string>
+#include <vector>
+#include <sstream>
+#include <iterator>
+#include <algorithm>
+#include <jni.h>
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace jni {
+
+class NarClassLoader {
+
+ public:
+
+  NarClassLoader(std::shared_ptr<minifi::jni::JavaServicer> servicer, JavaClass &clazz, const std::string &dir_name, const std::string &scratch_nar_dir, const std::string &docs_dir)
+      : java_servicer_(servicer) {
+    class_ref_ = clazz;
+    auto env = java_servicer_->attach();
+    class_loader_ = class_ref_.newInstance(env);
+    jmethodID mthd = env->GetMethodID(class_ref_.getReference(), "initializeNarDirectory", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V");
+    if (mthd == nullptr) {
+      throw std::runtime_error("Could not find method to construct JniClassLoader");
+    } else {
+      auto dirNameStr = env->NewStringUTF(dir_name.c_str());
+      auto narWriteBaseStr = env->NewStringUTF(scratch_nar_dir.c_str());
+      auto docsDirStr = env->NewStringUTF(docs_dir.c_str());
+      env->CallVoidMethod(class_loader_, mthd, dirNameStr, narWriteBaseStr, docsDirStr, servicer->getClassLoader());
+      ThrowIf(env);
+    }
+
+    // we should now have
+
+    getBundles();
+  }
+
+  ~NarClassLoader() {
+    java_servicer_->attach()->DeleteGlobalRef(class_loader_);
+  }
+
+  jclass getClass(const std::string &requested_name) {
+    auto env = java_servicer_->attach();
+    jmethodID mthd = env->GetMethodID(class_ref_.getReference(), "getClass", "(Ljava/lang/String;)Ljava/lang/Class;");
+    if (mthd == nullptr) {
+      ThrowIf(env);
+    }
+
+    auto clazz_name = env->NewStringUTF(requested_name.c_str());
+
+    auto job = env->CallObjectMethod(class_loader_, mthd, clazz_name);
+
+    ThrowIf(env);
+
+    return (jclass) job;
+  }
+
+  std::pair<std::string, std::string> getAnnotation(const std::string &requested_name, const std::string &method_name) {
+    auto env = java_servicer_->attach();
+    std::string methodName, signature;
+    {
+      jmethodID mthd = env->GetMethodID(class_ref_.getReference(), "getMethod", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+      if (mthd == nullptr) {
+        ThrowIf(env);
+      }
+
+      auto clazz_name = env->NewStringUTF(requested_name.c_str());
+      auto annotation_name = env->NewStringUTF(method_name.c_str());
+
+      jstring obj = (jstring) env->CallObjectMethod(class_loader_, mthd, clazz_name, annotation_name);
+
+      ThrowIf(env);
+
+      if (obj) {
+        const char *str = env->GetStringUTFChars(obj, 0);
+
+        methodName = str;
+        env->ReleaseStringUTFChars(obj, str);
+      }
+    }
+    {
+
+      jmethodID mthd = env->GetMethodID(class_ref_.getReference(), "getSignature", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+      if (mthd == nullptr) {
+        ThrowIf(env);
+      }
+
+      auto clazz_name = env->NewStringUTF(requested_name.c_str());
+      auto annotation_name = env->NewStringUTF(method_name.c_str());
+
+      jstring obj = (jstring) env->CallObjectMethod(class_loader_, mthd, clazz_name, annotation_name);
+
+      ThrowIf(env);
+
+      if (obj) {
+        const char *str = env->GetStringUTFChars(obj, 0);
+        signature = str;
+        env->ReleaseStringUTFChars(obj, str);
+      }
+    }
+
+    return std::make_pair(methodName, signature);
+  }
+  /**
+   * Call empty constructor
+   */
+  JNIEXPORT
+  jobject newInstance(const std::string &requested_name) {
+    auto env = java_servicer_->attach();
+
+    jmethodID mthd = env->GetMethodID(class_ref_.getReference(), "createObject", "(Ljava/lang/String;)Ljava/lang/Object;");
+    if (mthd == nullptr) {
+      ThrowIf(env);
+    }
+
+    auto clazz_name = env->NewStringUTF(requested_name.c_str());
+
+    jobject obj = env->NewGlobalRef(env->CallObjectMethod(class_loader_, mthd, clazz_name));
+
+    ThrowIf(env);
+
+    return obj;
+  }
+
+ private:
+
+  /**
+   * Call empty constructor
+   */
+  jobject getBundles() {
+    auto env = java_servicer_->attach();
+
+    jmethodID mthd = env->GetMethodID(class_ref_.getReference(), "getBundles", "()Ljava/util/List;");
+    if (mthd == nullptr) {
+      ThrowIf(env);
+    }
+
+    jobject obj = env->CallObjectMethod(class_loader_, mthd);
+
+    ThrowIf(env);
+
+    auto list_class = env->FindClass("java/util/ArrayList");
+
+    ThrowIf(env);
+
+    size_t size = getSize(list_class, env, obj);
+
+    for (int i = 0; i < size; i++) {
+
+      JniBundle bundle = getBundle(list_class, env, obj, i);
+      for (const auto &cd : bundle.getDescriptions()) {
+
+        auto lastOfIdx = cd.class_name_.find_last_of(".");
+        if (lastOfIdx != std::string::npos) {
+          lastOfIdx++;  // if a value is found, increment to move beyond the .
+          int nameLength = cd.class_name_.length() - lastOfIdx;
+          const auto processorName = cd.class_name_.substr(lastOfIdx, nameLength);
+          if (core::ClassLoader::getDefaultClassLoader().getGroupForClass(processorName).empty()) {
+            minifi::ExternalBuildDescription::addExternalComponent(bundle.getDetails(), cd);
+          }
+        }
+      }
+    }
+
+    return obj;
+  }
+
+  size_t getSize(jclass list_class, JNIEnv *env, jobject list) {
+    jmethodID mthd = env->GetMethodID(list_class, "size", "()I");
+    if (mthd == nullptr) {
+      ThrowIf(env);
+    }
+    jint obj = (jint) env->CallIntMethod(list, mthd);
+    minifi::jni::ThrowIf(env);
+    return obj;
+  }
+
+  JniBundle getBundle(jclass list_class, JNIEnv *env, jobject list, int index) {
+    jmethodID mthd = env->GetMethodID(list_class, "get", "(I)Ljava/lang/Object;");
+    if (mthd == nullptr) {
+      ThrowIf(env);
+    }
+    auto bundle = env->CallObjectMethod(list, mthd, index);
+    ThrowIf(env);
+    if (bundle != nullptr) {
+      auto jni_bundle_clazz = getClass("org.apache.nifi.processor.JniBundle");
+      struct BundleDetails details = getCoordinateDetails(env, jni_bundle_clazz, bundle);
+      std::vector<ClassDescription> descriptions = getDescriptions(env, jni_bundle_clazz, bundle);
+
+      JniBundle newBundle(details);
+      for (const auto &cd : descriptions) {
+        newBundle.addDescription(cd);
+      }
+
+      return newBundle;
+    }
+    // assuming we have the bundle, we need to get the coordinate.
+
+    return JniBundle();
+
+  }
+
+  std::vector<ClassDescription> getDescriptions(JNIEnv *env, jclass jni_bundle_clazz, jobject bundle) {
+
+    std::vector<ClassDescription> descriptions;
+    auto jni_component_clazz = getClass("org.apache.nifi.processor.JniComponent");
+
+    auto list_class = env->FindClass("java/util/ArrayList");
+
+    if (nullptr == jni_component_clazz || nullptr == jni_bundle_clazz) {
+      return descriptions;
+    }
+    auto components = getComponents(jni_bundle_clazz, env, bundle);
+
+    size_t size = getSize(list_class, env, components);
+
+    for (int i = 0; i < size; i++) {
+      descriptions.push_back(getClassDescription(list_class, env, jni_component_clazz, components, i));
+    }
+
+    return descriptions;
+  }
+
+  ClassDescription getClassDescription(jclass list_class, JNIEnv *env, jclass jni_component_clazz, jobject list, int index) {
+    jmethodID mthd = env->GetMethodID(list_class, "get", "(I)Ljava/lang/Object;");
+    auto property_descriptor_clazz = getClass("org.apache.nifi.components.PropertyDescriptor");
+    if (mthd == nullptr) {
+      ThrowIf(env);
+    }
+    auto component = env->CallObjectMethod(list, mthd, index);
+    minifi::jni::ThrowIf(env);
+    if (component != nullptr) {
+
+      auto type = getStringMethod("getType", jni_component_clazz, env, component);
+      auto isControllerService = getBoolmethod("isControllerService", jni_component_clazz, env, component);
+      ClassDescription description(type);
+      {
+        jmethodID getDescriptorMethod = env->GetMethodID(jni_component_clazz, "getDescriptors", "()Ljava/util/List;");
+
+        jobject descriptors = env->CallObjectMethod(component, getDescriptorMethod);
+
+        ThrowIf(env);
+
+        if (descriptors) {
+          size_t size = getSize(list_class, env, descriptors);
+
+          // iterate through each property descriptor
+          for (int i = 0; i < size; i++) {
+            auto propertyDescriptorObj = env->CallObjectMethod(descriptors, mthd, i);
+            minifi::jni::ThrowIf(env);
+            if (propertyDescriptorObj != nullptr) {
+              auto propName = getStringMethod("getName", property_descriptor_clazz, env, propertyDescriptorObj);
+              auto propDesc = getStringMethod("getDescription", property_descriptor_clazz, env, propertyDescriptorObj);
+              auto defaultValue = getStringMethod("getDefaultValue", property_descriptor_clazz, env, propertyDescriptorObj);
+
+              auto builder = core::PropertyBuilder::createProperty(propName)->withDescription(propDesc);
+              if (!defaultValue.empty()) {
+                builder->withDefaultValue(defaultValue);
+              }
+
+              builder = builder->isRequired(getBoolmethod("isRequired", property_descriptor_clazz, env, propertyDescriptorObj));
+              core::Property prop(builder->build());
+              description.class_properties_.insert(std::make_pair(prop.getName(), prop));
+
+            }
+          }
+        }
+      }
+      description.is_controller_service_ = isControllerService;
+      jmethodID getRelationshipsMethod = env->GetMethodID(jni_component_clazz, "getRelationships", "()Ljava/util/List;");
+      ThrowIf(env);
+      jobject relationships = env->CallObjectMethod(component, getRelationshipsMethod);
+      ThrowIf(env);
+      if (relationships) {
+        size_t size = getSize(list_class, env, relationships);
+
+        // iterate through each property descriptor
+        for (int i = 0; i < size; i++) {
+          auto propertyDescriptorObj = env->CallObjectMethod(relationships, mthd, i);
+          minifi::jni::ThrowIf(env);
+          if (propertyDescriptorObj != nullptr) {
+            auto relName = getStringMethod("getName", property_descriptor_clazz, env, propertyDescriptorObj);
+            auto relDesc = getStringMethod("getDescription", property_descriptor_clazz, env, propertyDescriptorObj);
+
+            core::Relationship relationship(relName, relDesc);
+
+            description.class_relationships_.push_back(relationship);
+
+          }
+        }
+      }
+
+      auto classDescription = getStringMethod("getDescription", jni_component_clazz, env, component);
+      description.dynamic_relationships_ = getBoolmethod("getDynamicRelationshipsSupported", jni_component_clazz, env, component);
+      description.dynamic_properties_ = getBoolmethod("getDynamicPropertiesSupported", jni_component_clazz, env, component);
+
+      AgentDocs::putDescription(type, classDescription);
+
+      return description;
+
+    }
+    // assuming we have the bundle, we need to get the coordinate.
+
+    return ClassDescription("unknown");
+
+  }
+
+  struct BundleDetails getCoordinateDetails(JNIEnv *env, jclass jni_bundle_clazz, jobject bundle) {
+
+    auto bundle_details = getClass("org.apache.nifi.bundle.BundleDetails");
+    auto bundle_coordinate = getClass("org.apache.nifi.bundle.BundleCoordinate");
+    struct BundleDetails details;
+
+    if (nullptr == bundle_details || nullptr == bundle_coordinate || nullptr == jni_bundle_clazz) {
+      return details;
+    }
+
+    auto jdetails = getDetails(jni_bundle_clazz, env, bundle);
+
+    if (nullptr != jdetails) {
+
+      auto jcoordinate = getCoordinate(bundle_details, env, jdetails);
+      if (nullptr != jcoordinate) {
+        details.artifact = getArtifact(bundle_coordinate, env, jcoordinate);
+        details.group = getGroup(bundle_coordinate, env, jcoordinate);
+        details.version = getVersion(bundle_coordinate, env, jcoordinate);
+      }
+
+    }
+
+    return details;
+  }
+
+  bool getBoolmethod(const std::string &methodName, jclass bundle_coordinate, JNIEnv *env, jobject coord) {
+    jmethodID getIdMethod = env->GetMethodID(bundle_coordinate, methodName.c_str(), "()Z");
+
+    if (getIdMethod == nullptr) {
+      ThrowIf(env);
+    }
+    auto res = (jboolean) env->CallBooleanMethod(coord, getIdMethod);
+    ThrowIf(env);
+    return res;
+  }
+
+  std::string getStringMethod(const std::string &methodName, jclass bundle_coordinate, JNIEnv *env, jobject coord) {
+    jmethodID getIdMethod = env->GetMethodID(bundle_coordinate, methodName.c_str(), "()Ljava/lang/String;");
+
+    if (getIdMethod == nullptr) {
+      ThrowIf(env);
+    }
+    auto id = (jstring) env->CallObjectMethod(coord, getIdMethod);
+    ThrowIf(env);
+    if (id == nullptr)
+      return "";
+    auto id_chars = env->GetStringUTFChars(id, 0);
+
+    std::string artifact = id_chars;
+    env->ReleaseStringUTFChars(id, id_chars);
+    return artifact;
+  }
+
+  std::string getArtifact(jclass bundle_coordinate, JNIEnv *env, jobject coord) {
+    return getStringMethod("getId", bundle_coordinate, env, coord);
+  }
+
+  std::string getGroup(jclass bundle_coordinate, JNIEnv *env, jobject coord) {
+    return getStringMethod("getGroup", bundle_coordinate, env, coord);
+  }
+
+  std::string getVersion(jclass bundle_coordinate, JNIEnv *env, jobject coord) {
+    return getStringMethod("getVersion", bundle_coordinate, env, coord);
+  }
+
+  jobject getCoordinate(jclass bundle_details, JNIEnv *env, jobject jdetail) {
+    jmethodID getCoordinateMethod = env->GetMethodID(bundle_details, "getCoordinate", "()Lorg/apache/nifi/bundle/BundleCoordinate;");
+    if (getCoordinateMethod == nullptr) {
+      ThrowIf(env);
+    }
+    auto coordinate = env->CallObjectMethod(jdetail, getCoordinateMethod);
+    ThrowIf(env);
+    return coordinate;
+
+  }
+
+  jobject getDetails(jclass bundle_details, JNIEnv *env, jobject bundle) {
+    jmethodID getDetailsMethod = env->GetMethodID(bundle_details, "getDetails", "()Lorg/apache/nifi/bundle/BundleDetails;");
+    if (getDetailsMethod == nullptr) {
+      ThrowIf(env);
+    }
+    auto details = env->CallObjectMethod(bundle, getDetailsMethod);
+    ThrowIf(env);
+    return details;
+
+  }
+
+  jobject getComponents(jclass bundle_details, JNIEnv *env, jobject bundle) {
+    jmethodID getDetailsMethod = env->GetMethodID(bundle_details, "getComponents", "()Ljava/util/List;");
+    if (getDetailsMethod == nullptr) {
+      ThrowIf(env);
+    }
+    auto details = env->CallObjectMethod(bundle, getDetailsMethod);
+    ThrowIf(env);
+    return details;
+
+  }
+
+  std::shared_ptr<logging::Logger> logger_;
+  std::shared_ptr<minifi::jni::JavaServicer> java_servicer_;
+  JavaClass class_ref_;
+  jobject class_loader_;
+};
+
+} /* namespace jni */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
+#endif /* EXTENSIONS_NARCLASSLOADER_H */
diff --git a/extensions/jni/nifi-framework-jni/pom.xml b/extensions/jni/nifi-framework-jni/pom.xml
new file mode 100644
index 0000000..fd7655f
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/pom.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.nifi</groupId>
+  <version>1.10.0-SNAPSHOT</version>
+  <artifactId>nifi-framework-jni</artifactId>
+  <packaging>jar</packaging>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.nifi</groupId>
+      <artifactId>nifi-framework-core-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.nifi</groupId>
+      <artifactId>nifi-properties-loader</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.nifi</groupId>
+      <artifactId>nifi-api</artifactId>
+      <version>${project.version}</version>
+
+    </dependency>
+    <dependency>
+      <groupId>org.apache.nifi</groupId>
+      <artifactId>nifi-framework-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.nifi</groupId>
+      <artifactId>nifi-nar-utils</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.nifi</groupId>
+      <artifactId>nifi-expression-language</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.nifi</groupId>
+      <artifactId>nifi-utils</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.nifi</groupId>
+      <artifactId>nifi-schema-utils</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.nifi</groupId>
+      <artifactId>nifi-repository-models</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.nifi</groupId>
+      <artifactId>nifi-properties</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.nifi</groupId>
+      <artifactId>nifi-site-to-site</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.nifi</groupId>
+      <artifactId>nifi-logging-utils</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>jcl-over-slf4j</artifactId>
+      <version>${org.slf4j.version}</version>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+  <properties>
+    <org.slf4j.version>1.7.25</org.slf4j.version>
+    <maven.compiler.source>1.8</maven.compiler.source>
+    <maven.compiler.target>1.8</maven.compiler.target>
+  </properties>
+</project>
diff --git a/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/nar/JniUnpacker.java b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/nar/JniUnpacker.java
new file mode 100644
index 0000000..6512f84
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/nar/JniUnpacker.java
@@ -0,0 +1,267 @@
+/*
+ * 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.nar;
+
+import org.apache.nifi.bundle.BundleCoordinate;
+import org.apache.nifi.util.FileUtils;
+import org.apache.nifi.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+/**
+ *  Should change NarPacker so that we can depend on it a little more for our purposes.
+ */
+public class JniUnpacker {
+
+    private static final Logger logger = LoggerFactory.getLogger(JniUnpacker.class);
+    private static String HASH_FILENAME = "nar-md5sum";
+
+    private static final Map<String,File> narMaps = new HashMap<>();
+    private static final Map<String,String> narDependencies = new HashMap<>();
+
+    private static final FileFilter NAR_FILTER = new FileFilter() {
+        @Override
+        public boolean accept(File pathname) {
+            final String nameToTest = pathname.getName().toLowerCase();
+            return nameToTest.endsWith(".nar") && pathname.isFile();
+        }
+    };
+
+
+    public static Optional<String> getDependency(final String narId){
+        return Optional.of( narDependencies.get(narId) );
+    }
+
+    public static Optional<File> getDirectory(final String narId){
+        return Optional.of( narMaps.get(narId) );
+    }
+
+    public static void unpackNars(final File narFilesDir, final File unpackDirectory, List<File> paths ) throws IOException {
+        final List<Path> narLibraryDirs = new ArrayList<>();
+        final Map<File, BundleCoordinate> unpackedNars = new HashMap<>();
+
+        final Path narFilesPath = narFilesDir.toPath();
+        narLibraryDirs.add(narFilesPath);
+
+        try {
+            File unpackedFramework = null;
+            final Set<File> unpackedExtensions = new HashSet<>();
+            final List<File> narFiles = new ArrayList<>();
+
+            // make sure the nar directories are there and accessible
+            FileUtils.ensureDirectoryExistAndCanReadAndWrite(unpackDirectory);
+
+            for (Path narLibraryDir : narLibraryDirs) {
+
+                File narDir = narLibraryDir.toFile();
+
+                // Test if the source NARs can be read
+                FileUtils.ensureDirectoryExistAndCanRead(narDir);
+
+                File[] dirFiles = narDir.listFiles(NAR_FILTER);
+                if (dirFiles != null) {
+                    List<File> fileList = Arrays.asList(dirFiles);
+                    narFiles.addAll(fileList);
+                }
+            }
+
+            if (!narFiles.isEmpty()) {
+                final long startTime = System.nanoTime();
+                logger.info("Expanding " + narFiles.size() + " NAR files with all processors...");
+                for (File narFile : narFiles) {
+                    logger.debug("Expanding NAR file: " + narFile.getAbsolutePath() + " to " + unpackDirectory);
+
+                    // get the manifest for this nar
+                    try (final JarFile nar = new JarFile(narFile)) {
+                        logger.debug("Expanding NAR file: " + nar.getName());
+                        final Manifest manifest = nar.getManifest();
+
+                        // lookup the nar id
+                        final Attributes attributes = manifest.getMainAttributes();
+                        final String groupId = attributes.getValue(NarManifestEntry.NAR_GROUP.getManifestName());
+                        final String narId = attributes.getValue(NarManifestEntry.NAR_ID.getManifestName());
+                        final String version = attributes.getValue(NarManifestEntry.NAR_VERSION.getManifestName());
+                        final String dependencyId = attributes.getValue(NarManifestEntry.NAR_DEPENDENCY_ID.getManifestName());
+                        logger.debug("Expanding NAR file: " + nar.getName() + " has dependency " + dependencyId);
+                        narMaps.put(narId,narFilesDir);
+                        narDependencies.put(narId,dependencyId);
+                        // determine if this is the framework
+                        if (NarClassLoaders.FRAMEWORK_NAR_ID.equals(narId)) {
+                            if (unpackedFramework != null) {
+                                throw new IllegalStateException("Multiple framework NARs discovered. Only one framework is permitted.");
+                            }
+
+                            // unpack the framework nar
+                            unpackedFramework = unpackNar(narFile,paths, unpackDirectory);
+                        } else {
+                            final File unpackedExtension = unpackNar(narFile,paths, unpackDirectory);
+
+                            // record the current bundle
+                            unpackedNars.put(unpackedExtension, new BundleCoordinate(groupId, narId, version));
+
+                            // unpack the extension nar
+                            unpackedExtensions.add(unpackedExtension);
+                        }
+                    }
+                }
+
+                final long duration = System.nanoTime() - startTime;
+                logger.info("NAR loading process took " + duration + " nanoseconds "
+                        + "(" + (int) TimeUnit.SECONDS.convert(duration, TimeUnit.NANOSECONDS) + " seconds).");
+            }
+
+        } catch (IOException e) {
+            logger.warn("Unable to load NAR library bundles due to " + e + " Will proceed without loading any further Nar bundles");
+        }
+    }
+
+    /**
+     * Unpacks the specified nar into the specified base working directory.
+     *
+     * @param nar the nar to unpack
+     * @param baseWorkingDirectory the directory to unpack to
+     * @return the directory to the unpacked NAR
+     * @throws IOException if unable to explode nar
+     */
+    private static File unpackNar(final File nar, List<File> paths,  final File baseWorkingDirectory) throws IOException {
+        final File narWorkingDirectory = new File(baseWorkingDirectory, nar.getName() + "-unpacked");
+
+        // if the working directory doesn't exist, unpack the nar
+        if (!narWorkingDirectory.exists()) {
+            narWorkingDirectory.mkdirs();
+            paths.add(narWorkingDirectory);
+            unpack(nar, narWorkingDirectory, calculateMd5sum(nar));
+        } else {
+            // the working directory does exist. Run MD5 sum against the nar
+            // file and check if the nar has changed since it was deployed.
+            final byte[] narMd5 = calculateMd5sum(nar);
+            final File workingHashFile = new File(narWorkingDirectory, HASH_FILENAME);
+            if (!workingHashFile.exists()) {
+                FileUtils.deleteFile(narWorkingDirectory, true);
+                unpack(nar, narWorkingDirectory, narMd5);
+            } else {
+                final byte[] hashFileContents = Files.readAllBytes(workingHashFile.toPath());
+                if (!Arrays.equals(hashFileContents, narMd5)) {
+                    FileUtils.deleteFile(narWorkingDirectory, true);
+                    unpack(nar, narWorkingDirectory, narMd5);
+                }
+            }
+        }
+
+        return narWorkingDirectory;
+    }
+
+    /**
+     * Unpacks the NAR to the specified directory. Creates a checksum file that
+     * used to determine if future expansion is necessary.
+     *
+     * @param workingDirectory the root directory to which the NAR should be unpacked.
+     * @throws IOException if the NAR could not be unpacked.
+     */
+    private static void unpack(final File nar, final File workingDirectory, final byte[] hash) throws IOException {
+
+        try (JarFile jarFile = new JarFile(nar)) {
+            Enumeration<JarEntry> jarEntries = jarFile.entries();
+            while (jarEntries.hasMoreElements()) {
+                JarEntry jarEntry = jarEntries.nextElement();
+                String name = jarEntry.getName();
+
+                if(name.contains("META-INF/bundled-dependencies")){
+                    name = name.replace("META-INF/bundled-dependencies", "NAR-INF/bundled-dependencies");
+                }
+                File f = new File(workingDirectory, name);
+                logger.info("Creating {}",f.getAbsolutePath());
+                if (jarEntry.isDirectory()) {
+                    f.mkdirs();
+                    FileUtils.ensureDirectoryExistAndCanReadAndWrite(f);
+                } else {
+                    makeFile(jarFile.getInputStream(jarEntry), f);
+                }
+            }
+        }
+
+        final File hashFile = new File(workingDirectory, HASH_FILENAME);
+        try (final FileOutputStream fos = new FileOutputStream(hashFile)) {
+            fos.write(hash);
+        }
+    }
+
+
+    /**
+     * Creates the specified file, whose contents will come from the
+     * <tt>InputStream</tt>.
+     *
+     * @param inputStream
+     *            the contents of the file to create.
+     * @param file
+     *            the file to create.
+     * @throws IOException
+     *             if the file could not be created.
+     */
+    private static void makeFile(final InputStream inputStream, final File file) throws IOException {
+        try (final InputStream in = inputStream;
+             final FileOutputStream fos = new FileOutputStream(file)) {
+            byte[] bytes = new byte[65536];
+            int numRead;
+            while ((numRead = in.read(bytes)) != -1) {
+                fos.write(bytes, 0, numRead);
+            }
+        }
+    }
+
+    /**
+     * Calculates an md5 sum of the specified file.
+     *
+     * @param file
+     *            to calculate the md5sum of
+     * @return the md5sum bytes
+     * @throws IOException
+     *             if cannot read file
+     */
+    private static byte[] calculateMd5sum(final File file) throws IOException {
+        try (final FileInputStream inputStream = new FileInputStream(file)) {
+            final MessageDigest md5 = MessageDigest.getInstance("md5");
+
+            final byte[] buffer = new byte[1024];
+            int read = inputStream.read(buffer);
+
+            while (read > -1) {
+                md5.update(buffer, 0, read);
+                read = inputStream.read(buffer);
+            }
+
+            return md5.digest();
+        } catch (NoSuchAlgorithmException nsae) {
+            throw new IllegalArgumentException(nsae);
+        }
+    }
+
+    private JniUnpacker() {
+    }
+}
diff --git a/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniBundle.java b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniBundle.java
new file mode 100644
index 0000000..2d636a6
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniBundle.java
@@ -0,0 +1,29 @@
+package org.apache.nifi.processor;
+
+import org.apache.nifi.bundle.BundleDetails;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Simply defines a bundle reference within JNI.
+ */
+public class JniBundle {
+    private BundleDetails details;
+    private List<JniComponent> components;
+
+
+
+    public JniBundle(BundleDetails details, List<JniComponent> components){
+        this.details = details;
+        this.components = components;
+    }
+
+    public BundleDetails getDetails(){
+        return details;
+    }
+
+    public List<JniComponent> getComponents(){
+        return Collections.unmodifiableList(components);
+    }
+}
diff --git a/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniClassLoader.java b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniClassLoader.java
new file mode 100644
index 0000000..4b61a26
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniClassLoader.java
@@ -0,0 +1,498 @@
+package org.apache.nifi.processor;
+
+import org.apache.nifi.annotation.behavior.DynamicProperty;
+import org.apache.nifi.annotation.behavior.DynamicRelationship;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.bundle.Bundle;
+import org.apache.nifi.bundle.BundleDetails;
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.init.ConfigurableComponentInitializer;
+import org.apache.nifi.init.ConfigurableComponentInitializerFactory;
+import org.apache.nifi.nar.*;
+import org.apache.nifi.reporting.InitializationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+
+/**
+ * Basic class loader that references the dependencies within NARs
+ */
+public class JniClassLoader  {
+
+    private static final Logger logger = LoggerFactory
+            .getLogger(JniClassLoader.class);
+
+
+    /**
+     * Pointer to native object.
+     */
+    private long nativePtr;
+
+
+    /**
+     * Parent class loader reference ( will ultimately be a reference to the JNI class loader).
+     */
+    private ClassLoader parent= null;
+
+    private static ConcurrentHashMap<String,Class<?>> classes = new ConcurrentHashMap<>();
+
+    private ConcurrentHashMap<Map.Entry<String,String>,Method> onScheduledMethod = new ConcurrentHashMap<>();
+
+    private ConcurrentHashMap<String,JniComponent> componentMap = new ConcurrentHashMap<>();
+
+    private List<JniBundle> bundles = new ArrayList<>();
+
+    private ConcurrentHashMap<String, BundleDetails> bundleMap = new ConcurrentHashMap<>();
+    private ConcurrentHashMap<String, File> fileMap = new ConcurrentHashMap<>();
+    private ConcurrentHashMap<String, NarClassLoader> loaderMap = new ConcurrentHashMap<>();
+
+    public JniClassLoader(){
+    }
+
+
+    static class Dependency
+    {
+        String artifactName;
+        AtomicBoolean visited;
+        List<Dependency> subDependencies;
+
+        Dependency(final String name )
+        {
+            artifactName = name;
+            visited = new AtomicBoolean(false);
+            subDependencies =new ArrayList<>();
+
+        }
+        public void addDependency(Dependency neighbourDependency)
+        {
+            this.subDependencies.add(neighbourDependency);
+        }
+        public List<Dependency> getSubDependencies() {
+            return subDependencies;
+        }
+        public String toString()
+        {
+            return artifactName;
+        }
+
+        @Override
+        public int hashCode(){
+            return artifactName.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj){
+            return artifactName.equals(obj);
+        }
+    }
+
+    public  void sortDependencies(final Stack<Dependency> dependencyStack, Dependency dependency)
+    {
+        if (dependency != null) {
+
+            dependency.getSubDependencies().stream().filter(x -> !x.visited.get()).forEach(
+                    x -> {
+                        sortDependencies(dependencyStack, x);
+                        x.visited.set(true);
+                    }
+            );
+        }
+        dependencyStack.push(dependency);
+    }
+
+    /**
+     * Initializes the nar directory. This is the mainstay of the JNI class loader's initialization.
+     * @param narDirectory directory in which we place our nars
+     * @param narWriteBase the directory to which we will write or explode our NARs
+     * @param docsDir nar document directory
+     * @param parent parent class loader, if there is one
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    public synchronized void initializeNarDirectory(final String narDirectory,final String narWriteBase,final String docsDir, ClassLoader parent) throws IOException, ClassNotFoundException{
+        // unpack the nar
+        if (this.parent != null)
+            throw new IllegalArgumentException("Already initialized");
+
+        this.parent = parent;
+        List<File> paths = new ArrayList<>();
+        File narDeploy = new File(narWriteBase);
+
+        JniUnpacker.unpackNars(new File(narDirectory),narDeploy,paths);
+
+        List<File> directories = Arrays.asList(narDeploy.listFiles(File::isDirectory));
+
+        for(File narPath : directories) {
+            final BundleDetails details = NarBundleUtil.fromNarDirectory(narPath);
+            bundleMap.put(details.getCoordinate().getId(), details );
+            fileMap.put(details.getCoordinate().getId(), narPath);
+        }
+
+
+
+        // these two allow us to see load those which do not have dependencies followed by those that do
+        // if a dependency exists within this set then we can chain those class loaders as needed.
+        final Map<String, Dependency> dependencyMap = new HashMap<>();
+        final List<String> nonDeps = new ArrayList<>();
+        bundleMap.entrySet().stream().filter((e) ->{
+            return e.getValue().getDependencyCoordinate() == null;
+        }).collect(Collectors.toList()).forEach( entry ->{
+            File path = fileMap.get(entry.getKey());
+            if (path != null) {
+                try {
+                    nonDeps.add(entry.getKey());
+                    final NarClassLoader loader = new NarClassLoader(path, parent);
+
+                    loaderMap.put(entry.getKey(),loader);
+
+                    Dependency dep = new Dependency(entry.getKey());
+                    dependencyMap.put(entry.getKey(), dep);
+
+                   discoverAndLoad(new Bundle(entry.getValue(), loader));
+
+                } catch (ClassNotFoundException e) {
+                    logger.error("Could not create NarClassLoader",e);
+                } catch (IOException e) {
+                    logger.error("Could not create NarClassLoader",e);
+                }
+
+                Dependency dep = new Dependency(entry.getKey());
+                dependencyMap.put(entry.getKey(), dep);
+            }
+        });
+
+
+        bundleMap.entrySet().stream().filter((e) ->{
+            return e.getValue().getDependencyCoordinate() != null;
+        }).collect(Collectors.toList()).forEach(entry ->{
+            File path = fileMap.get(entry.getKey());
+            if (path != null) {
+
+                Dependency dep = dependencyMap.get(entry.getValue().getCoordinate().getId());
+                if (dep == null) {
+                    dep = new Dependency(entry.getValue().getCoordinate().getId());
+                    dependencyMap.put(entry.getValue().getCoordinate().getId(), dep);
+                }
+                Dependency subDep = dependencyMap.get(entry.getValue().getDependencyCoordinate().getId());
+                if (subDep == null) {
+                    subDep = new Dependency(entry.getValue().getDependencyCoordinate().getId());
+                    dependencyMap.put(entry.getValue().getDependencyCoordinate().getId(), subDep);
+                }
+                dep.addDependency(subDep);
+            }
+
+        });
+
+        bundleMap.entrySet().stream().filter((e) ->{
+            return e.getValue().getDependencyCoordinate() != null;
+        }).collect(Collectors.toList()).forEach(entry ->{
+
+                try {
+
+                    Dependency dep = dependencyMap.get(entry.getKey());
+
+                    Stack<Dependency> stackedDependencies = new Stack<>();
+
+                    sortDependencies(stackedDependencies,dep);
+
+                    NarClassLoader loader = null;
+                    NarClassLoader depLoader = null;
+                    // test the dependencies to ensure we've loaded them
+                    // and performed a topological sort.
+                    for(Dependency sortedDependency : stackedDependencies) {
+                        depLoader = loaderMap.get(sortedDependency);
+                        if (depLoader == null) {
+                        File path = fileMap.get(sortedDependency.artifactName);
+                        BundleDetails deets = bundleMap.get(sortedDependency.artifactName);
+                        if (deets.getDependencyCoordinate() == null){
+                            loader = null;
+                        }
+                        else
+                        loader = loaderMap.get(deets.getDependencyCoordinate().getId());
+                            depLoader = new NarClassLoader(path, loader == null ? parent : loader);
+                            loaderMap.put(sortedDependency.artifactName, depLoader);
+                        }
+                    }
+
+                    NarClassLoader thisLoaader = loaderMap.get(entry.getKey());
+                    discoverAndLoad(new Bundle(entry.getValue(), thisLoaader));
+
+                } catch (ClassNotFoundException e) {
+                    logger.error("Could not create NarClassLoader",e);
+                } catch (Throwable e) {
+                    logger.error("Could not create NarClassLoader",e);
+                }
+
+        });
+    }
+
+    protected void discoverAndLoad(Bundle bundle){
+        List<JniComponent> components = discoverExtensions(bundle);
+
+        componentMap.putAll(
+                components.stream().collect(Collectors.toMap(JniComponent::getType, jniComponent -> jniComponent)));
+
+
+        bundles.add(new JniBundle(bundle.getBundleDetails(), components));
+
+    }
+
+    public Class getClass(final String className) throws ClassNotFoundException {
+        Class clazz = classes.get(className);
+        if (clazz == null){
+            clazz = parent.loadClass(className);
+        }
+        return clazz;
+    }
+
+    public List<JniBundle> getBundles(){
+        return Collections.unmodifiableList(bundles);
+    }
+
+    /**
+     * Loads all FlowFileProcessor, FlowFileComparator, ReportingTask class types that can be found on the bootstrap classloader and by creating classloaders for all NARs found within the classpath.
+     * @param bundle the bundles to scan through in search of extensions
+     */
+    public static List<JniComponent> discoverExtensions(final Bundle bundle) {
+        // get the current context class loader
+        ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader();
+
+        List<JniComponent> components = new ArrayList<>();
+
+            // Must set the context class loader to the nar classloader itself
+            // so that static initialization techniques that depend on the context class loader will work properly
+            final ClassLoader ncl = bundle.getClassLoader();
+            Thread.currentThread().setContextClassLoader(ncl);
+            components.addAll( loadProcessors(bundle) );
+
+
+        // restore the current context class loader if appropriate
+        if (currentContextClassLoader != null) {
+            Thread.currentThread().setContextClassLoader(currentContextClassLoader);
+        }
+        return components;
+    }
+
+    /**
+     * Loads extensions from the specified bundle.
+     *
+     * @param bundle from which to load extensions
+     */
+    @SuppressWarnings("unchecked")
+    private static List<JniComponent> loadProcessors(final Bundle bundle) {
+        List<JniComponent> components = new ArrayList<>();
+        ServiceLoader<?> serviceLoader = ServiceLoader.load(Processor.class, bundle.getClassLoader());
+        Iterator<?> sli = serviceLoader.iterator();
+            while(sli.hasNext()){
+                try {
+                Object o = sli.next();
+                // create a cache of temp ConfigurableComponent instances, the initialize here has to happen before the checks below
+                if (o instanceof ConfigurableComponent) {
+
+                        final ConfigurableComponent configurableComponent = (ConfigurableComponent) o;
+                        initializeTempComponent(configurableComponent);
+                        if (configurableComponent instanceof Processor ) {
+                            final Processor processor = Processor.class.cast(configurableComponent);
+                            if (processor != null) {
+                                List<PropertyDescriptor> descriptors = processor.getPropertyDescriptors();
+                                final String description = getDescription(processor.getClass());
+                                classes.put(processor.getClass().getCanonicalName(),processor.getClass());
+                                final DynamicProperty dynProperty = getDynamicPropertyAnnotation(processor.getClass());
+                                final DynamicRelationship dynRelationShip = getDynamicRelationshipAnnotation(processor.getClass());
+                                JniComponent.JniComponentBuilder builder = JniComponent.JniComponentBuilder.create(processor.getClass().getCanonicalName()).addProperties(descriptors).addDescription(description).addRelationships(processor.getRelationships());
+                                if (dynProperty != null) {
+                                    builder.setDynamicProperties();
+                                }
+                                if (dynRelationShip != null) {
+                                    builder.setDynamicRelationships();
+                                }
+                                components.add(builder.build());
+                            }
+                        }
+                }
+                }catch(Throwable e){
+                    logger.info("Ignoring ",e);
+                }
+
+
+            }
+
+        serviceLoader = ServiceLoader.load(ControllerService.class, bundle.getClassLoader());
+        sli = serviceLoader.iterator();
+        while(sli.hasNext()){
+            try {
+                Object o = sli.next();
+                // create a cache of temp ConfigurableComponent instances, the initialize here has to happen before the checks below
+                if (o instanceof ConfigurableComponent) {
+
+                    final ConfigurableComponent configurableComponent = (ConfigurableComponent) o;
+                    initializeTempComponent(configurableComponent);
+                    if (configurableComponent instanceof ControllerService) {
+                        final ControllerService cs = ControllerService.class.cast(configurableComponent);
+                        if (cs != null) {
+                            List<PropertyDescriptor> descriptors = cs.getPropertyDescriptors();
+                            final String description = getDescription(cs.getClass());
+                            final DynamicProperty dynProperty = getDynamicPropertyAnnotation(cs.getClass());
+                            final DynamicRelationship dynRelationShip = getDynamicRelationshipAnnotation(cs.getClass());
+                            classes.put(cs.getClass().getCanonicalName(),cs.getClass());
+                            JniComponent.JniComponentBuilder builder = JniComponent.JniComponentBuilder.create(cs.getClass().getCanonicalName()).addProperties(descriptors).addDescription(description).setIsControllerService();
+                            if (dynProperty != null) {
+                                builder.setDynamicProperties();
+                            }
+                            if (dynRelationShip != null) {
+                                builder.setDynamicRelationships();
+                            }
+                            components.add(builder.build());
+                        }
+
+                    }
+
+                }
+            }catch(Throwable e){
+                logger.info("Ignoring ",e);
+            }
+
+
+        }
+            return components;
+    }
+    /**
+     * Gets the description from the specified class.
+     */
+    private static String getDescription(final Class<?> cls) {
+        final CapabilityDescription capabilityDesc = cls.getAnnotation(CapabilityDescription.class);
+        return capabilityDesc == null ? "" : capabilityDesc.value();
+    }
+
+    private static DynamicProperty getDynamicPropertyAnnotation(final Class<?> cls) {
+        final DynamicProperty dynProperty = cls.getAnnotation(DynamicProperty.class);
+        return dynProperty;
+    }
+
+    private static DynamicRelationship getDynamicRelationshipAnnotation(final Class<?> cls) {
+        final DynamicRelationship dynamicRelationship = cls.getAnnotation(DynamicRelationship.class);
+        return dynamicRelationship;
+    }
+
+    private static void initializeTempComponent(final ConfigurableComponent configurableComponent) {
+        ExtensionManager manager = new StandardExtensionDiscoveringManager();
+        ConfigurableComponentInitializer initializer = null;
+        try {
+            initializer = ConfigurableComponentInitializerFactory.createComponentInitializer(manager,configurableComponent.getClass());
+            initializer.initialize(configurableComponent);
+        } catch (final InitializationException e) {
+            logger.warn(String.format("Unable to initialize component %s due to %s", configurableComponent.getClass().getName(), e.getMessage()));
+        }
+    }
+
+    public static List<Method> getAnnotatedMethods(final Class<?> type, final Class<? extends Annotation> annotation) {
+        final List<Method> methods = new ArrayList<Method>();
+        Class<?> klass = type;
+        while (klass != Object.class) {
+            for (final Method method : klass.getDeclaredMethods()) {
+                if (method.isAnnotationPresent(annotation)) {
+                    methods.add(method);
+                }
+            }
+            if (methods.isEmpty())
+                klass = klass.getSuperclass();
+            else
+                break;
+        }
+        return methods;
+    }
+
+
+    public String getMethod(final String className, final String annotation){
+        Method mthd = onScheduledMethod.get(new AbstractMap.SimpleImmutableEntry<>(className,annotation));
+
+        if (mthd == null)
+        {
+            return null;
+        }
+
+        return mthd.getName();
+    }
+
+    public String getSignature(final String className, final String annotation){
+        Method mthd = onScheduledMethod.get(new AbstractMap.SimpleImmutableEntry<>(className,annotation));
+        if (mthd == null)
+        {
+            return null;
+        }
+        String ret = "", argTypes="";
+        if (mthd.getReturnType().equals(Void.TYPE)){
+            ret = "V";
+        }
+        else{
+            ret = classToType(mthd.getReturnType());
+        }
+        argTypes = "(";
+        for(Class<?> type : mthd.getParameterTypes()){
+            argTypes += classToType(type);
+        }
+
+        argTypes += ")";
+
+        return argTypes + ret;
+    }
+
+    private static String classToType(Class<?> type){
+        if (type.equals(Integer.TYPE)){
+            return "I";
+        } else if (type.equals(Boolean.TYPE)) {
+            return "Z";
+        }
+        else if (type.equals(Byte.TYPE)) {
+            return "B";
+        }
+        else if (type.equals(Character.TYPE)) {
+            return "C";
+        }
+        else if (type.equals(Short.TYPE)) {
+            return "S";
+        }
+        else if (type.equals(Long.TYPE)) {
+            return "J";
+        }
+        else if (type.equals(Boolean.TYPE)) {
+            return "Z";
+        }
+        else{
+            return "L" + type.getCanonicalName().replace(".","/") + ";";
+        }
+
+
+    }
+
+    public Object createObject(final String className) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
+        synchronized (this) {
+            Class clazz = classes.get(className);
+            if (clazz == null) {
+                clazz = parent.loadClass(className);
+            }
+
+            if (clazz == null) {
+                logger.warn("Could not find {}", className);
+                return null;
+            } else {
+                List<Method> methods = getAnnotatedMethods(clazz, OnScheduled.class);
+                methods.stream().forEach(mthd -> onScheduledMethod.put(new AbstractMap.SimpleImmutableEntry<>(className, "OnScheduled"), mthd));
+            }
+
+            return clazz.newInstance();
+        }
+    }
+
+}
diff --git a/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniComponent.java b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniComponent.java
new file mode 100644
index 0000000..5613b98
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniComponent.java
@@ -0,0 +1,118 @@
+package org.apache.nifi.processor;
+
+import org.apache.nifi.components.PropertyDescriptor;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * JNIComponent is a compnent object used for building the manifest within MiNiFi C++
+ */
+public class JniComponent {
+    private String type;
+    private String description;
+    private List<PropertyDescriptor> descriptorsList;
+    private Set<Relationship> relationships;
+    private boolean dynamicProperties;
+    private boolean dynamicRelationships;
+    private boolean isControllerService;
+
+    private JniComponent(final String type){
+        this.type = type;
+        this.descriptorsList = new ArrayList<>();
+        this.relationships = new HashSet<>();
+        this.dynamicProperties = false;
+        this.dynamicRelationships = false;
+        isControllerService = false;
+    }
+
+
+    public String getType(){
+        return type;
+    }
+
+    public String getDescription(){
+        return description;
+    }
+
+    public boolean isControllerService() { return isControllerService; }
+
+    public List<PropertyDescriptor> getDescriptors(){
+        return Collections.unmodifiableList(descriptorsList);
+    }
+
+
+    /**
+     * Return a list of relationshipos. internally we capture this as a set, but converting this
+     * to a list enables us to access this easier in JNI.
+     * @return
+     */
+    public List<Relationship> getRelationships(){
+        return Collections.unmodifiableList(relationships.stream().collect(Collectors.toList()));
+    }
+
+    public boolean getDynamicRelationshipsSupported(){
+        return dynamicRelationships;
+    }
+
+    public boolean getDynamicPropertiesSupported(){
+        return dynamicProperties;
+    }
+
+    public static class JniComponentBuilder{
+
+        public static JniComponentBuilder create(final String type){
+            return new JniComponentBuilder(type);
+        }
+
+
+        public JniComponentBuilder addProperty(final PropertyDescriptor property){
+            component.descriptorsList.add(property);
+            return this;
+        }
+
+        public JniComponentBuilder addProperties(final List<PropertyDescriptor> descriptorsList){
+            component.descriptorsList.addAll(descriptorsList);
+            return this;
+        }
+
+        public JniComponentBuilder addRelationships(final Set<Relationship> relationships){
+            component.relationships.addAll(relationships);
+            return this;
+        }
+
+        public JniComponentBuilder setDynamicProperties(){
+            component.dynamicProperties = true;
+            return this;
+        }
+
+        public JniComponentBuilder setDynamicRelationships(){
+            component.dynamicRelationships = true;
+            return this;
+        }
+
+        public JniComponentBuilder setIsControllerService(){
+            component.isControllerService = true;
+            return this;
+        }
+
+        public JniComponent build() {
+            return component;
+        }
+
+
+        public JniComponentBuilder addDescription(String description) {
+            component.description = description;
+            return this;
+        }
+
+       private JniComponentBuilder(final String type){
+        component = new JniComponent(type);
+        }
+
+        private JniComponent component;
+
+    }
+
+
+}
diff --git a/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniComponentLogger.java b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniComponentLogger.java
new file mode 100644
index 0000000..afcf24d
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniComponentLogger.java
@@ -0,0 +1,358 @@
+package org.apache.nifi.processor;
+
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.logging.LogLevel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.helpers.MessageFormatter;
+
+/**
+ * JniComponent Logger enables us to access the log instance within MiNiFi C++
+ */
+public class JniComponentLogger implements ComponentLog {
+
+    private static final Logger logger = LoggerFactory
+            .getLogger(JniComponentLogger.class);
+
+    private JniLogger jniLogger = null;
+
+    public JniComponentLogger(JniLogger logger){
+        jniLogger = logger;
+        if (null != jniLogger){
+            jniLogger.info("Starting NiFi component logger..");
+        }
+    }
+
+    @Override
+    public void warn(String msg, Throwable t) {
+        if (jniLogger != null){
+            jniLogger.warn(MessageFormatter.format(msg,t).getMessage());
+        }
+        else {
+            logger.warn(msg, t);
+        }
+    }
+
+    @Override
+    public void warn(String msg, Object[] os) {
+        if (jniLogger != null){
+            jniLogger.warn(MessageFormatter.arrayFormat(msg,os).getMessage());
+        }
+        else {
+            logger.warn(msg, os);
+        }
+    }
+
+    @Override
+    public void warn(String msg, Object[] os, Throwable t) {
+        if (jniLogger != null){
+            jniLogger.warn(MessageFormatter.arrayFormat(msg,os,t).getMessage());
+        }
+        else {
+            logger.warn(msg, os);
+            logger.warn("", t);
+        }
+    }
+
+    @Override
+    public void warn(String msg) {
+        if (jniLogger != null){
+            jniLogger.warn(msg);
+        }
+        else {
+            logger.warn(msg);
+        }
+    }
+
+    @Override
+    public void trace(String msg, Throwable t) {
+        if (jniLogger != null){
+            jniLogger.trace(MessageFormatter.format(msg,t).getMessage());
+        }
+        else {
+            logger.trace(msg, t);
+        }
+    }
+
+    @Override
+    public void trace(String msg, Object[] os) {
+        if (jniLogger != null){
+            jniLogger.trace(MessageFormatter.arrayFormat(msg,os).getMessage());
+        }
+        else {
+            logger.trace(msg, os);
+        }
+    }
+
+    @Override
+    public void trace(String msg) {
+        if (jniLogger != null){
+            jniLogger.trace(msg);
+        }
+        else {
+            logger.trace(msg);
+        }
+    }
+
+    @Override
+    public void trace(String msg, Object[] os, Throwable t) {
+        if (jniLogger != null){
+            jniLogger.trace(MessageFormatter.arrayFormat(msg,os,t).getMessage());
+        }
+        else {
+            logger.trace(msg, os);
+            logger.trace("", t);
+        }
+    }
+
+    @Override
+    public boolean isWarnEnabled() {
+        if (jniLogger != null)
+            return jniLogger.isWarnEnabled();
+        return logger.isWarnEnabled();
+    }
+
+    @Override
+    public boolean isTraceEnabled() {
+        if (jniLogger != null)
+            return jniLogger.isTraceEnabled();
+        return logger.isTraceEnabled();
+    }
+
+    @Override
+    public boolean isInfoEnabled() {
+        if (jniLogger != null)
+            return jniLogger.isInfoEnabled();
+        return logger.isInfoEnabled();
+    }
+
+    @Override
+    public boolean isErrorEnabled() {
+        if (jniLogger != null)
+            return jniLogger.isErrorEnabled();
+        return logger.isErrorEnabled();
+    }
+
+    @Override
+    public boolean isDebugEnabled() {
+        if (jniLogger != null)
+            return jniLogger.isDebugEnabled();
+        return logger.isDebugEnabled();
+    }
+
+    @Override
+    public void info(String msg, Throwable t) {
+        if (jniLogger != null){
+            jniLogger.info(MessageFormatter.format(msg,t).getMessage());
+        }
+        else {
+            logger.info(msg, t);
+        }
+    }
+
+    @Override
+    public void info(String msg, Object[] os) {
+        if (jniLogger != null){
+            jniLogger.info(MessageFormatter.arrayFormat(msg,os).getMessage());
+        }
+        else {
+            logger.info(msg, os);
+        }
+    }
+
+    @Override
+    public void info(String msg) {
+        if (jniLogger != null){
+            jniLogger.info(msg);
+        }
+        else {
+            logger.info(msg);
+        }
+    }
+
+    @Override
+    public void info(String msg, Object[] os, Throwable t) {
+        if (jniLogger != null){
+            jniLogger.info(MessageFormatter.format(msg,os,t).getMessage());
+        }
+        else {
+            logger.info(msg, os);
+        }
+    }
+
+    @Override
+    public String getName() {
+        return logger.getName();
+    }
+
+    @Override
+    public void error(String msg, Throwable t) {
+        if (jniLogger != null){
+            jniLogger.info(MessageFormatter.format(msg,t).getMessage());
+        }
+        else {
+            logger.error(msg, t);
+        }
+    }
+
+    @Override
+    public void error(String msg, Object[] os) {
+        if (jniLogger != null){
+            jniLogger.error(MessageFormatter.arrayFormat(msg,os).getMessage());
+        }
+        else {
+            logger.error(msg, os);
+        }
+    }
+
+    @Override
+    public void error(String msg) {
+        if (jniLogger != null){
+            jniLogger.error(msg);
+        }
+        else {
+            logger.error(msg);
+        }
+    }
+
+    @Override
+    public void error(String msg, Object[] os, Throwable t) {
+        if (jniLogger != null){
+            jniLogger.error(MessageFormatter.arrayFormat(msg,os).getMessage());
+        }
+        else {
+            logger.error(msg, os);
+            logger.error("",t.getMessage());
+        }
+    }
+
+    @Override
+    public void debug(String msg, Throwable t) {
+        if (jniLogger != null){
+            jniLogger.debug(MessageFormatter.format(msg,t).getMessage());
+
+        }
+        else {
+            logger.debug(msg, t);
+        }
+    }
+
+    @Override
+    public void debug(String msg, Object[] os) {
+        if (jniLogger != null){
+            jniLogger.debug(MessageFormatter.arrayFormat(msg,os).getMessage());
+        }
+        else {
+            logger.debug(msg, os);
+        }
+    }
+
+    @Override
+    public void debug(String msg, Object[] os, Throwable t) {
+        if (jniLogger != null){
+            jniLogger.debug(MessageFormatter.arrayFormat(msg,os,t).getMessage());
+        }
+        else {
+            logger.debug(msg, os);
+            logger.debug("",t.getMessage());
+        }
+    }
+
+    @Override
+    public void debug(String msg) {
+        if (jniLogger != null){
+            jniLogger.debug(msg);
+        }
+        else {
+            logger.debug(msg);
+        }
+    }
+
+    @Override
+    public void log(LogLevel level, String msg, Throwable t) {
+        switch (level) {
+            case DEBUG:
+                debug(msg, t);
+                break;
+            case ERROR:
+            case FATAL:
+                error(msg, t);
+                break;
+            case INFO:
+                info(msg, t);
+                break;
+            case TRACE:
+                trace(msg, t);
+                break;
+            case WARN:
+                warn(msg, t);
+                break;
+        }
+    }
+
+    @Override
+    public void log(LogLevel level, String msg, Object[] os) {
+        switch (level) {
+            case DEBUG:
+                debug(msg, os);
+                break;
+            case ERROR:
+            case FATAL:
+                error(msg, os);
+                break;
+            case INFO:
+                info(msg, os);
+                break;
+            case TRACE:
+                trace(msg, os);
+                break;
+            case WARN:
+                warn(msg, os);
+                break;
+        }
+    }
+
+    @Override
+    public void log(LogLevel level, String msg) {
+        switch (level) {
+            case DEBUG:
+                debug(msg);
+                break;
+            case ERROR:
+            case FATAL:
+                error(msg);
+                break;
+            case INFO:
+                info(msg);
+                break;
+            case TRACE:
+                trace(msg);
+                break;
+            case WARN:
+                warn(msg);
+                break;
+        }
+    }
+
+    @Override
+    public void log(LogLevel level, String msg, Object[] os, Throwable t) {
+        switch (level) {
+            case DEBUG:
+                debug(msg, os, t);
+                break;
+            case ERROR:
+            case FATAL:
+                error(msg, os, t);
+                break;
+            case INFO:
+                info(msg, os, t);
+                break;
+            case TRACE:
+                trace(msg, os, t);
+                break;
+            case WARN:
+                warn(msg, os, t);
+                break;
+        }
+    }
+}
diff --git a/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniFlowFile.java b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniFlowFile.java
new file mode 100644
index 0000000..9a9743f
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniFlowFile.java
@@ -0,0 +1,59 @@
+package org.apache.nifi.processor;
+
+import org.apache.nifi.flowfile.FlowFile;
+
+import java.util.Map;
+
+public class JniFlowFile implements FlowFile {
+
+    private long nativePtr;
+
+    public JniFlowFile(){
+    }
+
+    @Override
+    public native long getId();
+
+    @Override
+    public native long getEntryDate();
+
+    @Override
+    public native long getLineageStartDate();
+
+    @Override
+    public native long getLineageStartIndex();
+
+    @Override
+    public Long getLastQueueDate(){
+        return getLastQueueDatePrim();
+    }
+
+    private native long getLastQueueDatePrim();
+
+    @Override
+    public native long getQueueDateIndex();
+
+    @Override
+    public native boolean isPenalized();
+
+    @Override
+    public native String getAttribute(String key);
+
+    @Override
+    public native long getSize();
+
+    @Override
+    public native Map<String, String> getAttributes();
+
+    @Override
+    public int compareTo(FlowFile o) {
+        return 0;
+    }
+
+    public native String getUUIDStr();
+
+    @Override
+    public String toString(){
+        return getUUIDStr();
+    }
+}
diff --git a/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniInitializationContext.java b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniInitializationContext.java
new file mode 100644
index 0000000..365a92d
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniInitializationContext.java
@@ -0,0 +1,61 @@
+package org.apache.nifi.processor;
+
+import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.controller.NodeTypeProvider;
+import org.apache.nifi.logging.ComponentLog;
+
+import java.io.File;
+
+public class JniInitializationContext implements ProcessorInitializationContext {
+
+
+    private long nativePtr;
+
+    JniLogger logger = null;
+
+
+    @Override
+    public String getIdentifier() {
+        return null;
+    }
+
+
+    /**
+     * Native method to set the logger instance.
+     * @param logger logger instance
+     */
+    public void setLogger(final JniLogger logger){
+        this.logger = logger;
+    }
+
+
+    @Override
+    public ComponentLog getLogger() {
+        return new JniComponentLogger(logger);
+    }
+
+    @Override
+    public ControllerServiceLookup getControllerServiceLookup() {
+        return null;
+    }
+
+    @Override
+    public NodeTypeProvider getNodeTypeProvider() {
+        return null;
+    }
+
+    @Override
+    public String getKerberosServicePrincipal() {
+        return null;
+    }
+
+    @Override
+    public File getKerberosServiceKeytab() {
+        return null;
+    }
+
+    @Override
+    public File getKerberosConfigurationFile() {
+        return null;
+    }
+}
diff --git a/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniInputStream.java b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniInputStream.java
new file mode 100644
index 0000000..959e7bd
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniInputStream.java
@@ -0,0 +1,19 @@
+package org.apache.nifi.processor;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class JniInputStream extends InputStream {
+
+    private long nativePtr;
+
+    @Override
+    public native int read() throws IOException;
+/*
+    @Override
+    public int read(byte[] copyTo, int offset, int length) throws IOException{
+        return readWithOffset(copyTo,offset,length);
+    }*/
+
+    public native int readWithOffset(byte[] copyTo, int offset, int length) throws IOException;
+}
diff --git a/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniLogger.java b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniLogger.java
new file mode 100644
index 0000000..3fb23b6
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniLogger.java
@@ -0,0 +1,21 @@
+package org.apache.nifi.processor;
+
+public class JniLogger {
+
+    private long nativePtr;
+
+
+    public native boolean isWarnEnabled();
+    public native boolean isTraceEnabled();
+    public native boolean isInfoEnabled();
+    public native boolean isErrorEnabled();
+    public native boolean isDebugEnabled();
+
+
+    public native void warn(String msg);
+    public native void error(String msg);
+    public native void info(String msg);
+    public native void debug(String msg);
+    public native void trace(String msg);
+
+}
diff --git a/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProcessContext.java b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProcessContext.java
new file mode 100644
index 0000000..c0fbef9
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProcessContext.java
@@ -0,0 +1,169 @@
+package org.apache.nifi.processor;
+
+import org.apache.nifi.attribute.expression.language.StandardPropertyValue;
+import org.apache.nifi.components.AbstractConfigurableComponent;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.components.state.StateManager;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.controller.ControllerServiceLookup;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class JniProcessContext implements ProcessContext, ControllerServiceLookup {
+
+    private long nativePtr;
+
+    @Override
+    public ControllerService getControllerService(String serviceIdentifier) {
+        return null;
+    }
+
+    @Override
+    public boolean isControllerServiceEnabled(String serviceIdentifier) {
+        return false;
+    }
+
+    @Override
+    public boolean isControllerServiceEnabling(String serviceIdentifier) {
+        return false;
+    }
+
+    @Override
+    public boolean isControllerServiceEnabled(ControllerService service) {
+        return false;
+    }
+
+    @Override
+    public Set<String> getControllerServiceIdentifiers(Class<? extends ControllerService> serviceType) throws IllegalArgumentException {
+        return null;
+    }
+
+    @Override
+    public String getControllerServiceName(String serviceIdentifier) {
+        return null;
+    }
+
+    @Override
+    public PropertyValue getProperty(String propertyName) {
+        String value = getPropertyValue(propertyName);
+        System.out.println("for " + propertyName + " got " + value);
+        return new StandardPropertyValue(value,this);
+    }
+
+
+
+    @Override
+    public PropertyValue newPropertyValue(String rawValue) {
+        return new StandardPropertyValue(rawValue,this);
+    }
+
+    public native String getPropertyValue(final String propertyName);
+
+    @Override
+    public void yield() {
+
+    }
+
+    @Override
+    public int getMaxConcurrentTasks() {
+        return 0;
+    }
+
+    @Override
+    public String getAnnotationData() {
+        return null;
+    }
+
+    @Override
+    public Map<PropertyDescriptor, String> getProperties() {
+        List<String> propertyNames = getPropertyNames();
+        Processor processor = getProcessor();
+        if (processor instanceof AbstractConfigurableComponent) {
+            AbstractConfigurableComponent process = AbstractConfigurableComponent.class.cast(getProcessor());
+            if (process != null) {
+                return propertyNames.stream().collect(Collectors.toMap(process::getPropertyDescriptor, this::getPropertyValue));
+            }
+        }
+
+        return null;
+    }
+
+    private native List<String> getPropertyNames();
+
+    private native Processor getProcessor();
+
+    @Override
+    public String encrypt(String unencrypted) {
+        return null;
+    }
+
+    @Override
+    public String decrypt(String encrypted) {
+        return null;
+    }
+
+    @Override
+    public ControllerServiceLookup getControllerServiceLookup() {
+        return null;
+    }
+
+    @Override
+    public Set<Relationship> getAvailableRelationships() {
+        return null;
+    }
+
+    @Override
+    public boolean hasIncomingConnection() {
+        return false;
+    }
+
+    @Override
+    public boolean hasNonLoopConnection() {
+        return false;
+    }
+
+    @Override
+    public boolean hasConnection(Relationship relationship) {
+        return false;
+    }
+
+    @Override
+    public boolean isExpressionLanguagePresent(PropertyDescriptor property) {
+        return false;
+    }
+
+    @Override
+    public StateManager getStateManager() {
+        return null;
+    }
+
+    @Override
+    public String getName() {
+        return null;
+    }
+
+    @Override
+    public PropertyValue getProperty(PropertyDescriptor descriptor) {
+        String value = getPropertyValue(descriptor.getName());
+        if (value == null || "null".equals(value))
+            value = descriptor.getDefaultValue();
+        return new StandardPropertyValue(value,this);
+    }
+
+    @Override
+    public Map<String, String> getAllProperties() {
+        Map<PropertyDescriptor, String> map = getProperties();
+        Map<String,String> newProps = new HashMap<>();
+        map.forEach((x,y) ->
+        {
+           newProps.put(x.getName(),y);
+        });
+        return newProps;
+
+    }
+}
diff --git a/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProcessSession.java b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProcessSession.java
new file mode 100644
index 0000000..f1a1385
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProcessSession.java
@@ -0,0 +1,323 @@
+package org.apache.nifi.processor;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.NotImplementedException;
+import org.apache.nifi.controller.queue.QueueSize;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.exception.FlowFileAccessException;
+import org.apache.nifi.processor.io.InputStreamCallback;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.apache.nifi.processor.io.StreamCallback;
+import org.apache.nifi.provenance.ProvenanceReporter;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.regex.Pattern;
+import java.util.stream.IntStream;
+
+public class JniProcessSession implements ProcessSession {
+
+
+    private long nativePtr;
+
+    @Override
+    public native void commit();
+    @Override
+    public native void rollback();
+
+    @Override
+    public void rollback(boolean penalize){
+        rollback();
+    }
+
+
+
+    @Override
+    public void migrate(ProcessSession newOwner, Collection<FlowFile> flowFiles) {
+
+    }
+
+    @Override
+    public void adjustCounter(String name, long delta, boolean immediate) {
+
+    }
+
+    @Override
+    public native FlowFile get();
+
+    @Override
+    public List<FlowFile> get(int maxResults) {
+        List<FlowFile> flowfiles = new ArrayList<>();
+        FlowFile ff = null;
+        int i=0;
+        do{
+            ff = get();
+            if (ff == null)
+                break;
+            flowfiles.add(ff);
+            i++;
+        }while(i < maxResults);
+        return flowfiles;
+    }
+
+    @Override
+    public List<FlowFile> get(FlowFileFilter filter) {
+        return null;
+    }
+
+    @Override
+    public QueueSize getQueueSize() {
+        return null;
+    }
+
+    @Override
+    public native FlowFile create();
+
+    @Override
+    public FlowFile create(FlowFile parent){
+        return createWithParent(parent);
+    }
+
+    private native FlowFile createWithParent(FlowFile parent);
+
+    @Override
+    public FlowFile create(Collection<FlowFile> parents) {
+        return null;
+    }
+
+    @Override
+    public native FlowFile clone(FlowFile example);
+
+    @Override
+    public FlowFile clone(FlowFile parent, long offset, long size){
+        return clonePortion(parent,offset,size);
+    }
+
+    private native FlowFile clonePortion(FlowFile parent,  long offset, long size);
+
+    @Override
+    public native FlowFile penalize(FlowFile flowFile);
+
+    @Override
+    public native FlowFile putAttribute(FlowFile flowFile, String key, String value);
+
+    @Override
+    public FlowFile putAllAttributes(FlowFile flowFile, Map<String, String> attributes) {
+        for(Map.Entry<String,String> entry : attributes.entrySet()){
+            putAttribute(flowFile,entry.getKey(),entry.getValue());
+        }
+        return flowFile;
+    }
+
+    @Override
+    public native FlowFile removeAttribute(FlowFile flowFile, String key);
+
+    @Override
+    public FlowFile removeAllAttributes(FlowFile flowFile, Set<String> keys){
+        for(String attr : keys){
+            removeAttribute(flowFile,attr);
+        }
+        return flowFile;
+    }
+
+    @Override
+    public FlowFile removeAllAttributes(FlowFile flowFile, Pattern keyPattern){
+        if (flowFile != null){
+            Map<String,String> attributes = flowFile.getAttributes();
+            Set<String> keys = new HashSet<>();
+            for(Map.Entry<String,String> attr : attributes.entrySet()){
+                if (keyPattern.matcher(attr.getKey()).matches()){
+                    keys.add(attr.getKey());
+                }
+            }
+            return removeAllAttributes(flowFile,keys);
+        }
+        return null;
+    }
+
+    @Override
+    public void transfer(FlowFile flowFile, Relationship relationship){
+        transfer(flowFile,relationship.getName());
+    }
+
+    protected native void transfer(FlowFile flowFile, String relationship);
+
+    @Override
+    public void transfer(FlowFile flowFile){
+        transfer(flowFile,"success");
+    }
+
+    @Override
+    public void transfer(Collection<FlowFile> flowFiles) {
+        for(FlowFile ff : flowFiles){
+            transfer(ff);
+        }
+    }
+
+    @Override
+    public void transfer(Collection<FlowFile> flowFiles, Relationship relationship) {
+        for(FlowFile flowFile : flowFiles){
+            transfer(flowFile,relationship.getName());
+        }
+    }
+
+    @Override
+    public native void remove(FlowFile flowFile);
+
+    @Override
+    public void remove(Collection<FlowFile> flowFiles) {
+        for(FlowFile ff: flowFiles){
+            remove(ff);
+        }
+    }
+
+    @Override
+    public void read(FlowFile source, InputStreamCallback reader) throws FlowFileAccessException {
+        try {
+            final JniInputStream input = readFlowFile(source);
+            if (input != null)
+                reader.process(input);
+        } catch (IOException e) {
+            throw new FlowFileAccessException("Could not read from native source", e);
+        }
+    }
+
+    @Override
+    public InputStream read(FlowFile flowFile) {
+        return readFlowFile(flowFile);
+    }
+
+    @Override
+    public void read(FlowFile source, boolean allowSessionStreamManagement, InputStreamCallback reader) throws FlowFileAccessException {
+        throw new NotImplementedException("Currently not implemented");
+    }
+
+    @Override
+    public FlowFile merge(Collection<FlowFile> sources, FlowFile destination) {
+        throw new NotImplementedException("Currently not implemented");
+    }
+
+    @Override
+    public FlowFile merge(Collection<FlowFile> sources, FlowFile destination, byte[] header, byte[] footer, byte[] demarcator) {
+        throw new NotImplementedException("Currently not implemented");
+    }
+
+    @Override
+    public FlowFile write(FlowFile source, OutputStreamCallback writer) throws FlowFileAccessException {
+        // must write data.
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try {
+            writer.process(bos);
+        }catch(IOException os){
+            throw new FlowFileAccessException("IOException while processing ff data");
+        }
+        write(source, bos.toByteArray());
+
+        return source;
+    }
+
+    protected native JniInputStream readFlowFile(FlowFile source);
+
+    protected native boolean write(FlowFile source, byte [] array);
+
+    protected native boolean append(FlowFile source , byte [] array);
+
+    @Override
+    public OutputStream write(final FlowFile source) {
+        return new OutputStream() {
+            ByteArrayOutputStream bin = new ByteArrayOutputStream();
+            @Override
+            public void write(int b) throws IOException {
+                synchronized (bin) {
+                    bin.write(b);
+                    // better suited to writing pages of memory
+                    if (bin.size() > 4096) {
+                        flushByterArray();
+                    }
+                }
+            }
+
+            @Override
+            public void flush() throws IOException {
+                synchronized (bin) {
+                    // flush as an append.
+                    flushByterArray();
+                }
+            }
+
+            private void flushByterArray(){
+                append(source,bin.toByteArray());
+                bin = new ByteArrayOutputStream();
+            }
+        };
+    }
+
+    @Override
+    public FlowFile write(FlowFile source, StreamCallback writer) throws FlowFileAccessException {
+        // must write data.
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try {
+            writer.process(read(source),bos);
+        }catch(IOException os){
+            throw new FlowFileAccessException("IOException while processing ff data");
+        }
+        write(source, bos.toByteArray());
+
+        return source;
+    }
+
+    @Override
+    public FlowFile append(FlowFile source, OutputStreamCallback writer) throws FlowFileAccessException {
+        // must write data.
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        try {
+            writer.process(bos);
+            append(source,bos.toByteArray());
+        }catch(IOException os){
+            throw new FlowFileAccessException("IOException while processing ff data");
+        }
+        write(source, bos.toByteArray());
+
+        return source;
+    }
+
+    @Override
+    public FlowFile importFrom(Path source, boolean keepSourceFile, FlowFile destination){
+        try {
+            IOUtils.copy(Files.newInputStream(source),write(destination));
+            if (!keepSourceFile){
+                Files.delete(source);
+            }
+        } catch (IOException e) {
+            return null;
+        }
+        return destination;
+    }
+
+    @Override
+    public FlowFile importFrom(InputStream source, FlowFile destination){
+        try {
+            IOUtils.copy(source,write(destination));
+        } catch (IOException e) {
+            return null;
+        }
+        return destination;
+    }
+
+    @Override
+    public void exportTo(FlowFile flowFile, Path destination, boolean append) {
+
+    }
+
+    @Override
+    public void exportTo(FlowFile flowFile, OutputStream destination) {
+
+    }
+
+    @Override
+    public ProvenanceReporter getProvenanceReporter() {
+        return new JniProvenanceReporter();
+    }
+}
diff --git a/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProcessSessionFactory.java b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProcessSessionFactory.java
new file mode 100644
index 0000000..9ea8960
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProcessSessionFactory.java
@@ -0,0 +1,9 @@
+package org.apache.nifi.processor;
+
+public class JniProcessSessionFactory implements ProcessSessionFactory {
+
+    private long nativePtr;
+
+    @Override
+    public native ProcessSession createSession();
+}
diff --git a/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProperty.java b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProperty.java
new file mode 100644
index 0000000..da11df5
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProperty.java
@@ -0,0 +1,9 @@
+package org.apache.nifi.processor;
+
+public class JniProperty {
+
+
+    public JniProperty(){
+
+    }
+}
diff --git a/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProvenanceReporter.java b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProvenanceReporter.java
new file mode 100644
index 0000000..3bc5990
--- /dev/null
+++ b/extensions/jni/nifi-framework-jni/src/main/java/org/apache/nifi/processor/JniProvenanceReporter.java
@@ -0,0 +1,208 @@
+package org.apache.nifi.processor;
+
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.provenance.ProvenanceReporter;
+
+import java.util.Collection;
+
+public class JniProvenanceReporter implements ProvenanceReporter {
+    @Override
+    public void receive(FlowFile flowFile, String transitUri) {
+
+    }
+
+    @Override
+    public void receive(FlowFile flowFile, String transitUri, String sourceSystemFlowFileIdentifier) {
+
+    }
+
+    @Override
+    public void receive(FlowFile flowFile, String transitUri, long transmissionMillis) {
+
+    }
+
+    @Override
+    public void receive(FlowFile flowFile, String transitUri, String details, long transmissionMillis) {
+
+    }
+
+    @Override
+    public void receive(FlowFile flowFile, String transitUri, String sourceSystemFlowFileIdentifier, String details, long transmissionMillis) {
+
+    }
+
+    @Override
+    public void fetch(FlowFile flowFile, String transitUri) {
+
+    }
+
+    @Override
+    public void fetch(FlowFile flowFile, String transitUri, long transmissionMillis) {
+
+    }
+
+    @Override
+    public void fetch(FlowFile flowFile, String transitUri, String details, long transmissionMillis) {
+
+    }
+
+    @Override
+    public void send(FlowFile flowFile, String transitUri) {
+
+    }
+
+    @Override
+    public void send(FlowFile flowFile, String transitUri, String details) {
+
+    }
+
+    @Override
+    public void send(FlowFile flowFile, String transitUri, long transmissionMillis) {
+
+    }
+
+    @Override
+    public void send(FlowFile flowFile, String transitUri, String details, long transmissionMillis) {
+
+    }
+
+    @Override
+    public void send(FlowFile flowFile, String transitUri, boolean force) {
+
+    }
+
+    @Override
+    public void send(FlowFile flowFile, String transitUri, String details, boolean force) {
+
+    }
+
+    @Override
+    public void send(FlowFile flowFile, String transitUri, long transmissionMillis, boolean force) {
+
+    }
+
+    @Override
+    public void send(FlowFile flowFile, String transitUri, String details, long transmissionMillis, boolean force) {
+
+    }
+
+    @Override
+    public void invokeRemoteProcess(FlowFile flowFile, String transitUri) {
+
+    }
+
+    @Override
+    public void invokeRemoteProcess(FlowFile flowFile, String transitUri, String details) {
+
+    }
+
+    @Override
+    public void associate(FlowFile flowFile, String alternateIdentifierNamespace, String alternateIdentifier) {
+
+    }
+
+    @Override
+    public void fork(FlowFile parent, Collection<FlowFile> children) {
+
+    }
+
+    @Override
+    public void fork(FlowFile parent, Collection<FlowFile> children, String details) {
+
+    }
+
+    @Override
+    public void fork(FlowFile parent, Collection<FlowFile> children, long forkDuration) {
+
+    }
+
+    @Override
+    public void fork(FlowFile parent, Collection<FlowFile> children, String details, long forkDuration) {
+
+    }
+
+    @Override
+    public void join(Collection<FlowFile> parents, FlowFile child) {
+
+    }
+
+    @Override
+    public void join(Collection<FlowFile> parents, FlowFile child, String details) {
+
+    }
+
+    @Override
+    public void join(Collection<FlowFile> parents, FlowFile child, long joinDuration) {
+
+    }
+
+    @Override
+    public void join(Collection<FlowFile> parents, FlowFile child, String details, long joinDuration) {
+
+    }
+
+    @Override
+    public void clone(FlowFile parent, FlowFile child) {
+
+    }
+
+    @Override
+    public void modifyContent(FlowFile flowFile) {
+
+    }
+
+    @Override
+    public void modifyContent(FlowFile flowFile, String details) {
+
+    }
+
+    @Override
+    public void modifyContent(FlowFile flowFile, long processingMillis) {
+
+    }
+
+    @Override
+    public void modifyContent(FlowFile flowFile, String details, long processingMillis) {
+
+    }
+
+    @Override
+    public void modifyAttributes(FlowFile flowFile) {
+
+    }
+
+    @Override
+    public void modifyAttributes(FlowFile flowFile, String details) {
+
+    }
+
... 5424 lines suppressed ...


[nifi-minifi-cpp] 01/02: MINIFICPP-738 - EL should be able to access global properties

Posted by al...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

aldrin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git

commit f01953b8cd8814fa5b4faa0a7cb588f0545bb231
Author: Arpad Boda <ab...@hortonworks.com>
AuthorDate: Mon Feb 18 23:19:16 2019 +0100

    MINIFICPP-738 - EL should be able to access global properties
---
 extensions/expression-language/Expression.cpp           |  8 ++++++--
 extensions/expression-language/ProcessContextExpr.cpp   | 10 ++++++++--
 .../expression-language/impl/expression/Expression.h    |  9 +++++++--
 extensions/rocksdb-repos/DatabaseContentRepository.cpp  |  1 +
 libminifi/include/core/ContentRepository.h              |  5 +++++
 libminifi/src/core/repository/FileSystemRepository.cpp  |  1 +
 .../src/core/repository/VolatileContentRepository.cpp   |  1 +
 libminifi/test/TestBase.h                               |  6 ++++--
 .../ExpressionLanguageTests.cpp                         | 17 ++++++++++++++++-
 9 files changed, 49 insertions(+), 9 deletions(-)

diff --git a/extensions/expression-language/Expression.cpp b/extensions/expression-language/Expression.cpp
index b031b04..07fd509 100644
--- a/extensions/expression-language/Expression.cpp
+++ b/extensions/expression-language/Expression.cpp
@@ -69,9 +69,13 @@ Expression make_dynamic_attr(const std::string &attribute_id) {
     const auto cur_flow_file = params.flow_file.lock();
     if (cur_flow_file && cur_flow_file->getAttribute(attribute_id, result)) {
       return Value(result);
-    } else {
-      return Value();
+    } else if (attribute_id.rfind("nifi.", 0) == 0) {
+      const auto config = params.configuration.lock();
+      if (config && config->get(attribute_id, result)) {
+        return Value(result);
+      }
     }
+    return Value();
   });
 }
 
diff --git a/extensions/expression-language/ProcessContextExpr.cpp b/extensions/expression-language/ProcessContextExpr.cpp
index 5cceb77..71c0b36 100644
--- a/extensions/expression-language/ProcessContextExpr.cpp
+++ b/extensions/expression-language/ProcessContextExpr.cpp
@@ -34,7 +34,10 @@ bool ProcessContext::getProperty(const Property &property, std::string &value, c
     expressions_.emplace(name, expression::compile(expression_str));
   }
 
-  value = expressions_[name]( { flow_file }).asString();
+  minifi::expression::Parameters p(flow_file);
+  p.configuration = content_repo_->getConfig();
+
+  value = expressions_[name]( p ).asString();
   return true;
 }
 
@@ -50,7 +53,10 @@ bool ProcessContext::getDynamicProperty(const Property &property, std::string &v
     dynamic_property_expressions_.emplace(name, expression::compile(expression_str));
   }
 
-  value = dynamic_property_expressions_[name]( { flow_file }).asString();
+  minifi::expression::Parameters p(flow_file);
+  p.configuration = content_repo_->getConfig();
+
+  value = dynamic_property_expressions_[name]( p ).asString();
   return true;
 }
 
diff --git a/extensions/expression-language/impl/expression/Expression.h b/extensions/expression-language/impl/expression/Expression.h
index 15f7558..826f588 100644
--- a/extensions/expression-language/impl/expression/Expression.h
+++ b/extensions/expression-language/impl/expression/Expression.h
@@ -45,9 +45,14 @@ namespace nifi {
 namespace minifi {
 namespace expression {
 
-typedef struct {
+struct Parameters {
   std::weak_ptr<core::FlowFile> flow_file;
-} Parameters;
+  std::weak_ptr<minifi::Configure> configuration;
+  Parameters(std::shared_ptr<core::FlowFile> ff = nullptr) {
+    flow_file = ff;
+  }
+
+};
 
 class Expression;
 
diff --git a/extensions/rocksdb-repos/DatabaseContentRepository.cpp b/extensions/rocksdb-repos/DatabaseContentRepository.cpp
index 5842bf2..29864f4 100644
--- a/extensions/rocksdb-repos/DatabaseContentRepository.cpp
+++ b/extensions/rocksdb-repos/DatabaseContentRepository.cpp
@@ -30,6 +30,7 @@ namespace core {
 namespace repository {
 
 bool DatabaseContentRepository::initialize(const std::shared_ptr<minifi::Configure> &configuration) {
+  configuration_ = configuration;
   std::string value;
   if (configuration->get(Configure::nifi_dbcontent_repository_directory_default, value)) {
     directory_ = value;
diff --git a/libminifi/include/core/ContentRepository.h b/libminifi/include/core/ContentRepository.h
index 51d2765..50c6de1 100644
--- a/libminifi/include/core/ContentRepository.h
+++ b/libminifi/include/core/ContentRepository.h
@@ -108,6 +108,10 @@ class ContentRepository : public StreamManager<minifi::ResourceClaim> {
     }
   }
 
+  virtual std::weak_ptr<Configure> getConfig(){
+    return configuration_;
+  }
+
  protected:
 
   std::string directory_;
@@ -116,6 +120,7 @@ class ContentRepository : public StreamManager<minifi::ResourceClaim> {
 
   std::map<std::string, uint32_t> count_map_;
 
+  std::weak_ptr<Configure> configuration_;
 };
 
 } /* namespace core */
diff --git a/libminifi/src/core/repository/FileSystemRepository.cpp b/libminifi/src/core/repository/FileSystemRepository.cpp
index c0f1694..0a114d3 100644
--- a/libminifi/src/core/repository/FileSystemRepository.cpp
+++ b/libminifi/src/core/repository/FileSystemRepository.cpp
@@ -30,6 +30,7 @@ namespace core {
 namespace repository {
 
 bool FileSystemRepository::initialize(const std::shared_ptr<minifi::Configure> &configuration) {
+  configuration_ = configuration;
   std::string value;
   if (configuration->get(Configure::nifi_dbcontent_repository_directory_default, value)) {
     directory_ = value;
diff --git a/libminifi/src/core/repository/VolatileContentRepository.cpp b/libminifi/src/core/repository/VolatileContentRepository.cpp
index 674566b..1b27157 100644
--- a/libminifi/src/core/repository/VolatileContentRepository.cpp
+++ b/libminifi/src/core/repository/VolatileContentRepository.cpp
@@ -35,6 +35,7 @@ namespace repository {
 const char *VolatileContentRepository::minimal_locking = "minimal.locking";
 
 bool VolatileContentRepository::initialize(const std::shared_ptr<Configure> &configure) {
+  configuration_ = configure;
   VolatileRepository::initialize(configure);
   resource_claim_comparator_ = [](std::shared_ptr<minifi::ResourceClaim> lhsPtr, std::shared_ptr<minifi::ResourceClaim> rhsPtr) {
     if (lhsPtr == nullptr || rhsPtr == nullptr) {
diff --git a/libminifi/test/TestBase.h b/libminifi/test/TestBase.h
index c12edeb..c8104d5 100644
--- a/libminifi/test/TestBase.h
+++ b/libminifi/test/TestBase.h
@@ -249,8 +249,10 @@ class TestController {
     flow_version_ = std::make_shared<minifi::state::response::FlowVersion>("test", "test", "test");
   }
 
-  std::shared_ptr<TestPlan> createPlan() {
-    std::shared_ptr<minifi::Configure> configuration = std::make_shared<minifi::Configure>();
+  std::shared_ptr<TestPlan> createPlan(std::shared_ptr<minifi::Configure> configuration = nullptr) {
+    if(configuration == nullptr) {
+      configuration = std::make_shared<minifi::Configure>();
+    }
     std::shared_ptr<core::ContentRepository> content_repo = std::make_shared<core::repository::VolatileContentRepository>();
 
     content_repo->initialize(configuration);
diff --git a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
index 3ee6ec3..c96cd12 100644
--- a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
+++ b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
@@ -25,6 +25,7 @@
 #include <ExtractText.h>
 #include <GetFile.h>
 #include <PutFile.h>
+#include <UpdateAttribute.h>
 #include <LogAttribute.h>
 
 namespace expression = org::apache::nifi::minifi::expression;
@@ -176,8 +177,13 @@ TEST_CASE("GetFile PutFile dynamic attribute", "[expressionLanguageTestGetFilePu
   LogTestController::getInstance().setTrace<processors::GetFile>();
   LogTestController::getInstance().setTrace<processors::PutFile>();
   LogTestController::getInstance().setTrace<processors::LogAttribute>();
+  LogTestController::getInstance().setTrace<processors::UpdateAttribute>();
 
-  auto plan = testController.createPlan();
+  auto conf = std::make_shared<minifi::Configure>();
+
+  conf->set("nifi.my.own.property", "custom_value");
+
+  auto plan = testController.createPlan(conf);
   auto repo = std::make_shared<TestRepository>();
 
   std::string in_dir("/tmp/gt.XXXXXX");
@@ -203,6 +209,12 @@ TEST_CASE("GetFile PutFile dynamic attribute", "[expressionLanguageTestGetFilePu
       get_file,
       processors::GetFile::KeepSourceFile.getName(),
       "false");
+  auto update = plan->addProcessor(
+      "UpdateAttribute",
+      "UpdateAttribute",
+      core::Relationship("success", "description"),
+      true);
+  update->setDynamicProperty("prop_attr", "${'nifi.my.own.property'}_added");
   plan->addProcessor(
       "LogAttribute",
       "LogAttribute",
@@ -246,6 +258,7 @@ TEST_CASE("GetFile PutFile dynamic attribute", "[expressionLanguageTestGetFilePu
   }
 
   plan->runNextProcessor();  // GetFile
+  plan->runNextProcessor();  // Update
   plan->runNextProcessor();  // Log
   plan->runNextProcessor();  // ExtractText
   plan->runNextProcessor();  // Log
@@ -258,6 +271,8 @@ TEST_CASE("GetFile PutFile dynamic attribute", "[expressionLanguageTestGetFilePu
     output_str << out_file_stream.rdbuf();
     REQUIRE("extracted_attr" == output_str.str());
   }
+
+  REQUIRE(LogTestController::getInstance().contains("key:prop_attr value:custom_value_added"));
 }
 
 TEST_CASE("Substring 2 arg", "[expressionLanguageSubstring2]") {  // NOLINT