You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by tp...@apache.org on 2021/11/24 21:15:20 UTC

[nifi] branch main updated: NIFI-3328: SendTrapSNMP and ListenTrapSNMP processors added.

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

tpalfy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new bd7536b  NIFI-3328: SendTrapSNMP and ListenTrapSNMP processors added.
bd7536b is described below

commit bd7536b257907d9012084c27dc5cc3595e5dba3d
Author: Lehel Boér <Le...@hotmail.com>
AuthorDate: Thu May 20 17:08:02 2021 +0200

    NIFI-3328: SendTrapSNMP and ListenTrapSNMP processors added.
---
 .../nifi-snmp-bundle/nifi-snmp-processors/pom.xml  |   8 +-
 .../nifi/snmp/configuration/SNMPConfiguration.java | 128 +++++++--
 .../configuration/SNMPConfigurationBuilder.java    | 100 -------
 .../snmp/configuration/V1TrapConfiguration.java    | 117 ++++++++
 .../V2TrapConfiguration.java}                      |  16 +-
 .../ErrorStatus.java}                              |  46 ++--
 .../SNMPResponseStatus.java}                       |  71 ++---
 .../apache/nifi/snmp/dto/SNMPSingleResponse.java   |   2 +-
 .../org/apache/nifi/snmp/dto/SNMPTreeResponse.java |   8 +
 .../java/org/apache/nifi/snmp/dto/UserDetails.java |  58 ++++
 .../apache/nifi/snmp/exception/SNMPException.java  |   4 +
 .../nifi/snmp/factory/AbstractSNMPFactory.java     |  91 -------
 .../nifi/snmp/factory/CompositeSNMPFactory.java    |  70 -----
 .../apache/nifi/snmp/factory/V3SNMPFactory.java    |  76 ------
 .../{SNMPFactory.java => core/SNMPContext.java}    |  26 +-
 .../snmp/factory/core/SNMPFactoryProvider.java     |  48 ++++
 .../SNMPManagerFactory.java}                       |  32 +--
 .../V1V2cSNMPFactory.java}                         |  26 +-
 .../nifi/snmp/factory/core/V3SNMPFactory.java      |  77 ++++++
 .../nifi/snmp/factory/trap/V1TrapPDUFactory.java   |  58 ++++
 .../nifi/snmp/factory/trap/V2TrapPDUFactory.java   |  54 ++++
 .../nifi/snmp/operations/GetSNMPHandler.java       | 150 +++++++++++
 .../snmp/operations/SNMPRequestHandlerFactory.java |  38 ---
 .../nifi/snmp/operations/SNMPResourceHandler.java  |  70 +++++
 .../nifi/snmp/operations/SNMPTrapReceiver.java     |  77 ++++++
 .../snmp/operations/SNMPTrapReceiverHandler.java   | 124 +++++++++
 .../nifi/snmp/operations/SendTrapSNMPHandler.java  |  75 ++++++
 .../nifi/snmp/operations/SetSNMPHandler.java       |  66 +++++
 .../operations/StandardSNMPRequestHandler.java     | 148 -----------
 .../snmp/processors/AbstractSNMPProcessor.java     | 269 +++++--------------
 .../org/apache/nifi/snmp/processors/GetSNMP.java   | 157 +++++++----
 .../nifi/snmp/processors/ListenTrapSNMP.java       | 143 ++++++++++
 .../apache/nifi/snmp/processors/SendTrapSNMP.java  | 170 ++++++++++++
 .../org/apache/nifi/snmp/processors/SetSNMP.java   |  68 +++--
 .../processors/properties/BasicProperties.java     |  77 ++++++
 .../processors/properties/V1TrapProperties.java    |  80 ++++++
 .../processors/properties/V2TrapProperties.java    |  42 +++
 .../properties/V3SecurityProperties.java           | 130 +++++++++
 .../java/org/apache/nifi/snmp/utils/SNMPUtils.java | 116 +++++---
 .../services/org.apache.nifi.processor.Processor   |   2 +
 .../additionalDetails.html                         |  11 +-
 .../additionalDetails.html                         |  54 ++++
 .../additionalDetails.html                         |  45 ++++
 .../snmp/configuration/SNMPConfigurationTest.java  |  75 ++++++
 .../configuration/V1TrapConfigurationTest.java     | 141 ++++++++++
 .../apache/nifi/snmp/dto/SNMPTreeResponseTest.java | 165 ++++++++++++
 .../nifi/snmp/factory/SNMPClientFactoryTest.java   |  68 -----
 .../nifi/snmp/factory/core/SNMPContextTest.java    |  47 ++++
 .../snmp/factory/core/SNMPFactoryProviderTest.java |  37 +++
 .../snmp/factory/core/V1V2cSNMPFactoryTest.java    |  90 +++++++
 .../nifi/snmp/factory/core/V3SNMPFactoryTest.java  | 107 ++++++++
 .../snmp/factory/trap/V1TrapPDUFactoryTest.java    |  59 ++++
 .../snmp/factory/trap/V2TrapPDUFactoryTest.java    |  58 ++++
 .../org/apache/nifi/snmp/helper/SNMPTestUtils.java |  68 -----
 .../nifi/snmp/helper/TrapConfigurationFactory.java |  46 ++++
 .../configurations/SNMPConfigurationFactory.java}  |  16 +-
 .../SNMPV1V2cConfigurationFactory.java             |  57 ++++
 .../configurations/SNMPV3ConfigurationFactory.java |  71 +++++
 .../helper/testrunners/SNMPTestRunnerFactory.java  |  58 ++++
 .../testrunners/SNMPV1TestRunnerFactory.java       |  93 +++++++
 .../testrunners/SNMPV2cTestRunnerFactory.java      |  89 +++++++
 .../testrunners/SNMPV3TestRunnerFactory.java       | 112 ++++++++
 .../nifi/snmp/operations/GetSNMPHandlerTest.java   | 271 +++++++++++++++++++
 .../nifi/snmp/operations/SNMPRequestTest.java      | 296 +++++++++++++++++----
 .../operations/SNMPTrapReceiverHandlerTest.java    | 146 ++++++++++
 .../nifi/snmp/operations/SNMPTrapReceiverTest.java | 153 +++++++++++
 .../nifi/snmp/operations/SNMPV1RequestTest.java    |  86 ------
 .../nifi/snmp/operations/SNMPV2CRequestTest.java   |  86 ------
 .../nifi/snmp/operations/SNMPV3RequestTest.java    | 135 ----------
 .../snmp/operations/SendTrapSNMPHandlerTest.java   | 109 ++++++++
 .../nifi/snmp/operations/SetSNMPHandlerTest.java   | 119 +++++++++
 .../snmp/processors/AbstractSNMPProcessorTest.java | 162 +++++++++++
 .../snmp/processors/GetSNMPIntegrationTest.java    | 154 +++++++++++
 .../apache/nifi/snmp/processors/GetSNMPTest.java   |  86 ++----
 .../snmp/processors/SetSNMPIntegrationTest.java    | 112 ++++++++
 .../apache/nifi/snmp/processors/SetSNMPTest.java   |  88 ------
 .../snmp/processors/TrapSNMPIntegrationTest.java   | 108 ++++++++
 .../org/apache/nifi/snmp/testagents/TestAgent.java |   5 +
 .../src/test/resources/users.json                  |  16 ++
 79 files changed, 5216 insertions(+), 1601 deletions(-)

diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/pom.xml b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/pom.xml
index ab5b06e..88bc157 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/pom.xml
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/pom.xml
@@ -51,6 +51,12 @@ language governing permissions and limitations under the License. -->
             <version>1.16.0-SNAPSHOT</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.12.3</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -61,7 +67,7 @@ language governing permissions and limitations under the License. -->
                 <configuration>
                     <excludes>
                         <!-- test data -->
-                        <exclude>src/test/resources/testdata/*</exclude>
+                        <exclude>src/test/resources/*</exclude>
                     </excludes>
                 </configuration>
             </plugin>
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/configuration/SNMPConfiguration.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/configuration/SNMPConfiguration.java
index aa65ada..36a371b 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/configuration/SNMPConfiguration.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/configuration/SNMPConfiguration.java
@@ -18,10 +18,11 @@ package org.apache.nifi.snmp.configuration;
 
 public class SNMPConfiguration {
 
-    private final String agentHost;
-    private final String agentPort;
+    private final int managerPort;
+    private final String targetHost;
+    private final String targetPort;
     private final int retries;
-    private final int timeout;
+    private final long timeoutInMs;
     private final int version;
     private final String authProtocol;
     private final String authPassphrase;
@@ -31,10 +32,11 @@ public class SNMPConfiguration {
     private final String securityLevel;
     private final String communityString;
 
-    SNMPConfiguration(final String agentHost,
-                      final String agentPort,
+    SNMPConfiguration(final int managerPort,
+                      final String targetHost,
+                      final String targetPort,
                       final int retries,
-                      final int timeout,
+                      final long timeoutInMs,
                       final int version,
                       final String authProtocol,
                       final String authPassphrase,
@@ -43,10 +45,11 @@ public class SNMPConfiguration {
                       final String securityName,
                       final String securityLevel,
                       final String communityString) {
-        this.agentHost = agentHost;
-        this.agentPort = agentPort;
+        this.managerPort = managerPort;
+        this.targetHost = targetHost;
+        this.targetPort = targetPort;
         this.retries = retries;
-        this.timeout = timeout;
+        this.timeoutInMs = timeoutInMs;
         this.version = version;
         this.authProtocol = authProtocol;
         this.authPassphrase = authPassphrase;
@@ -57,20 +60,24 @@ public class SNMPConfiguration {
         this.communityString = communityString;
     }
 
-    public String getAgentHost() {
-        return agentHost;
+    public int getManagerPort() {
+        return managerPort;
     }
 
-    public String getAgentPort() {
-        return agentPort;
+    public String getTargetHost() {
+        return targetHost;
+    }
+
+    public String getTargetPort() {
+        return targetPort;
     }
 
     public int getRetries() {
         return retries;
     }
 
-    public int getTimeout() {
-        return timeout;
+    public long getTimeoutInMs() {
+        return timeoutInMs;
     }
 
     public int getVersion() {
@@ -104,4 +111,95 @@ public class SNMPConfiguration {
     public String getCommunityString() {
         return communityString;
     }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static class Builder {
+
+        private int managerPort;
+        private String targetHost;
+        private String targetPort;
+        private int retries;
+        private long timeoutInMs = 500L;
+        private int version;
+        private String authProtocol;
+        private String authPassphrase;
+        private String privacyProtocol;
+        private String privacyPassphrase;
+        private String securityName;
+        private String securityLevel;
+        private String communityString;
+
+        public Builder setManagerPort(final int managerPort) {
+            this.managerPort = managerPort;
+            return this;
+        }
+
+        public Builder setTargetHost(final String targetHost) {
+            this.targetHost = targetHost;
+            return this;
+        }
+
+        public Builder setTargetPort(final String targetPort) {
+            this.targetPort = targetPort;
+            return this;
+        }
+
+        public Builder setRetries(final int retries) {
+            this.retries = retries;
+            return this;
+        }
+
+        public Builder setTimeoutInMs(final long timeoutInMs) {
+            this.timeoutInMs = timeoutInMs;
+            return this;
+        }
+
+        public Builder setVersion(final int version) {
+            this.version = version;
+            return this;
+        }
+
+        public Builder setAuthProtocol(final String authProtocol) {
+            this.authProtocol = authProtocol;
+            return this;
+        }
+
+        public Builder setAuthPassphrase(final String authPassphrase) {
+            this.authPassphrase = authPassphrase;
+            return this;
+        }
+
+        public Builder setPrivacyProtocol(final String privacyProtocol) {
+            this.privacyProtocol = privacyProtocol;
+            return this;
+        }
+
+        public Builder setPrivacyPassphrase(final String privacyPassphrase) {
+            this.privacyPassphrase = privacyPassphrase;
+            return this;
+        }
+
+        public Builder setSecurityName(final String securityName) {
+            this.securityName = securityName;
+            return this;
+        }
+
+        public Builder setSecurityLevel(final String securityLevel) {
+            this.securityLevel = securityLevel;
+            return this;
+        }
+
+        public Builder setCommunityString(String communityString) {
+            this.communityString = communityString;
+            return this;
+        }
+
+        public SNMPConfiguration build() {
+            return new SNMPConfiguration(managerPort, targetHost, targetPort, retries, timeoutInMs, version, authProtocol,
+                    authPassphrase, privacyProtocol, privacyPassphrase, securityName, securityLevel, communityString);
+        }
+    }
 }
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/configuration/SNMPConfigurationBuilder.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/configuration/SNMPConfigurationBuilder.java
deleted file mode 100644
index dbb7728..0000000
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/configuration/SNMPConfigurationBuilder.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.snmp.configuration;
-
-public class SNMPConfigurationBuilder {
-    private String agentHost;
-    private String agentPort;
-    private int retries;
-    private int timeout;
-    private int version;
-    private String authProtocol;
-    private String authPassphrase;
-    private String privacyProtocol;
-    private String privacyPassphrase;
-    private String securityName;
-    private String securityLevel;
-    private String communityString;
-
-    public SNMPConfigurationBuilder setAgentHost(final String agentHost) {
-        this.agentHost = agentHost;
-        return this;
-    }
-
-    public SNMPConfigurationBuilder setAgentPort(final String agentPort) {
-        this.agentPort = agentPort;
-        return this;
-    }
-
-    public SNMPConfigurationBuilder setRetries(final int retries) {
-        this.retries = retries;
-        return this;
-    }
-
-    public SNMPConfigurationBuilder setTimeout(final int timeout) {
-        this.timeout = timeout;
-        return this;
-    }
-
-    public SNMPConfigurationBuilder setVersion(final int version) {
-        this.version = version;
-        return this;
-    }
-
-    public SNMPConfigurationBuilder setAuthProtocol(final String authProtocol) {
-        this.authProtocol = authProtocol;
-        return this;
-    }
-
-    public SNMPConfigurationBuilder setAuthPassphrase(final String authPassphrase) {
-        this.authPassphrase = authPassphrase;
-        return this;
-    }
-
-    public SNMPConfigurationBuilder setPrivacyProtocol(final String privacyProtocol) {
-        this.privacyProtocol = privacyProtocol;
-        return this;
-    }
-
-    public SNMPConfigurationBuilder setPrivacyPassphrase(final String privacyPassphrase) {
-        this.privacyPassphrase = privacyPassphrase;
-        return this;
-    }
-
-    public SNMPConfigurationBuilder setSecurityName(final String securityName) {
-        this.securityName = securityName;
-        return this;
-    }
-
-    public SNMPConfigurationBuilder setSecurityLevel(final String securityLevel) {
-        this.securityLevel = securityLevel;
-        return this;
-    }
-
-    public SNMPConfigurationBuilder setCommunityString(final String communityString) {
-        this.communityString = communityString;
-        return this;
-    }
-
-    public SNMPConfiguration build() {
-        final boolean isValid = agentHost != null && agentPort != null;
-        if (!isValid) {
-            throw new IllegalStateException("Required properties are not set.");
-        }
-        return new SNMPConfiguration(agentHost, agentPort, retries, timeout, version, authProtocol, authPassphrase, privacyProtocol, privacyPassphrase, securityName, securityLevel, communityString);
-    }
-}
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/configuration/V1TrapConfiguration.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/configuration/V1TrapConfiguration.java
new file mode 100644
index 0000000..896b0d9
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/configuration/V1TrapConfiguration.java
@@ -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.
+ */
+package org.apache.nifi.snmp.configuration;
+
+import org.apache.nifi.util.StringUtils;
+
+public class V1TrapConfiguration {
+
+    private final String enterpriseOid;
+    private final String agentAddress;
+    private final String genericTrapType;
+    private final String specificTrapType;
+
+    private V1TrapConfiguration(final V1TrapConfiguration.Builder builder) {
+        this.enterpriseOid = builder.enterpriseOid;
+        this.agentAddress = builder.agentAddress;
+        this.genericTrapType = builder.genericTrapType;
+        this.specificTrapType = builder.specificTrapType;
+    }
+
+    public String getEnterpriseOid() {
+        return enterpriseOid;
+    }
+
+    public String getAgentAddress() {
+        return agentAddress;
+    }
+
+    public int getGenericTrapType() {
+        return Integer.parseInt(genericTrapType);
+    }
+
+    public Integer getSpecificTrapType() {
+        if (StringUtils.isNotEmpty(specificTrapType)) {
+            return Integer.parseInt(specificTrapType);
+        }
+        return null;
+    }
+
+    public static V1TrapConfiguration.Builder builder() {
+        return new V1TrapConfiguration.Builder();
+    }
+
+    public static final class Builder {
+        String enterpriseOid;
+        String agentAddress;
+        String genericTrapType;
+        String specificTrapType;
+
+        public Builder enterpriseOid(String enterpriseOid) {
+            this.enterpriseOid = enterpriseOid;
+            return this;
+        }
+
+        public Builder agentAddress(String agentAddress) {
+            this.agentAddress = agentAddress;
+            return this;
+        }
+
+        public Builder genericTrapType(String genericTrapType) {
+            this.genericTrapType = genericTrapType;
+            return this;
+        }
+
+        public Builder specificTrapType(String specificTrapType) {
+            this.specificTrapType = specificTrapType;
+            return this;
+        }
+
+        public V1TrapConfiguration build() {
+            if (StringUtils.isEmpty(enterpriseOid)) {
+                throw new IllegalArgumentException("Enterprise OID must be specified.");
+            }
+            if (StringUtils.isEmpty(agentAddress)) {
+                throw new IllegalArgumentException("Agent address must be specified.");
+            }
+
+            final int parsedGenericTrapType;
+            try {
+                parsedGenericTrapType = Integer.parseInt(genericTrapType);
+                if (parsedGenericTrapType < 0 || parsedGenericTrapType > 6) {
+                    throw new IllegalArgumentException("Generic Trap Type must be between 0 and 6.");
+                }
+            } catch (NumberFormatException e) {
+                throw new IllegalArgumentException("Generic Trap Type is not a number.");
+            }
+
+            if (parsedGenericTrapType == 6) {
+                try {
+                    final int parsedSpecificTrapType = Integer.parseInt(specificTrapType);
+                    if (parsedSpecificTrapType < 0) {
+                        throw new IllegalArgumentException("Specific Trap Type must be between 0 and 2147483647.");
+                    }
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("Generic Trap Type is [6 - Enterprise Specific] but Specific Trap Type is not provided or not a number.");
+                }
+            } else if (StringUtils.isNotEmpty(specificTrapType)) {
+                throw new IllegalArgumentException("Invalid argument: Generic Trap Type is not [6 - Enterprise Specific] but Specific Trap Type is provided.");
+            }
+            return new V1TrapConfiguration(this);
+        }
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/exception/CreateSNMPClientException.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/configuration/V2TrapConfiguration.java
similarity index 65%
rename from nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/exception/CreateSNMPClientException.java
rename to nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/configuration/V2TrapConfiguration.java
index f803605..145acbe 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/exception/CreateSNMPClientException.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/configuration/V2TrapConfiguration.java
@@ -14,12 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.nifi.snmp.exception;
+package org.apache.nifi.snmp.configuration;
 
-public class CreateSNMPClientException extends SNMPException {
+public class V2TrapConfiguration {
 
-    public CreateSNMPClientException(final String errorMessage) {
-        super(errorMessage);
+    private final String trapOidValue;
+
+    public V2TrapConfiguration(final String trapOidValue) {
+        if (trapOidValue.isEmpty()) {
+            throw new IllegalArgumentException("Trap OID Value must be specified.");
+        }
+        this.trapOidValue = trapOidValue;
     }
 
+    public String getTrapOidValue() {
+        return trapOidValue;
+    }
 }
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/exception/CloseSNMPClientException.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/ErrorStatus.java
similarity index 79%
rename from nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/exception/CloseSNMPClientException.java
rename to nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/ErrorStatus.java
index 4c70250..2582e13 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/exception/CloseSNMPClientException.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/ErrorStatus.java
@@ -1,25 +1,21 @@
-/*
- * 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.snmp.exception;
-
-public class CloseSNMPClientException extends SNMPException {
-
-    public CloseSNMPClientException(final String errorMessage) {
-        super(errorMessage);
-    }
-
-}
+/*
+ * 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.snmp.dto;
+
+public enum ErrorStatus {
+    FAILURE, SUCCESS
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPRequestHandler.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/SNMPResponseStatus.java
similarity index 61%
rename from nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPRequestHandler.java
rename to nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/SNMPResponseStatus.java
index 97759ed..8080e7c 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPRequestHandler.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/SNMPResponseStatus.java
@@ -1,34 +1,37 @@
-/*
- * 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.snmp.operations;
-
-import org.apache.nifi.flowfile.FlowFile;
-import org.apache.nifi.snmp.dto.SNMPSingleResponse;
-import org.apache.nifi.snmp.dto.SNMPTreeResponse;
-
-import java.io.IOException;
-
-public interface SNMPRequestHandler {
-
-    SNMPSingleResponse get(final String oid) throws IOException;
-
-    SNMPTreeResponse walk(final String oid);
-
-    SNMPSingleResponse set(final FlowFile flowfile) throws IOException;
-
-    void close();
-}
+/*
+ * 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.snmp.dto;
+
+public class SNMPResponseStatus {
+
+    private final String errorMessage;
+    private final ErrorStatus errorStatus;
+
+
+    public SNMPResponseStatus(String errorMessage, ErrorStatus errorStatus) {
+        this.errorMessage = errorMessage;
+        this.errorStatus = errorStatus;
+    }
+
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    public ErrorStatus getErrorStatus() {
+        return errorStatus;
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/SNMPSingleResponse.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/SNMPSingleResponse.java
index dabf41a..03f8306 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/SNMPSingleResponse.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/SNMPSingleResponse.java
@@ -38,7 +38,6 @@ public class SNMPSingleResponse {
         return responsePdu.getErrorStatus() == PDU.noError;
     }
 
-
     public Map<String, String> getAttributes() {
         return SNMPUtils.getPduAttributeMap(responsePdu);
     }
@@ -65,4 +64,5 @@ public class SNMPSingleResponse {
     public boolean isReportPdu() {
         return responsePdu.getType() == PDU.REPORT;
     }
+
 }
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/SNMPTreeResponse.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/SNMPTreeResponse.java
index c250e8d..d58116d 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/SNMPTreeResponse.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/SNMPTreeResponse.java
@@ -26,6 +26,7 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 public class SNMPTreeResponse {
@@ -56,4 +57,11 @@ public class SNMPTreeResponse {
                 .filter(TreeEvent::isError)
                 .forEach(event -> logger.error("Error occured in SNMP walk event: {}", event.getErrorMessage()));
     }
+
+    public boolean isError() {
+        final Optional<TreeEvent> first = events.stream()
+                .filter(TreeEvent::isError)
+                .findFirst();
+        return first.isPresent();
+    }
 }
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/UserDetails.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/UserDetails.java
new file mode 100644
index 0000000..26cc9ff
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/dto/UserDetails.java
@@ -0,0 +1,58 @@
+/*
+ * 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.snmp.dto;
+
+public class UserDetails {
+
+    private String securityName;
+    private String authProtocol;
+    private String authPassphrase;
+    private String privProtocol;
+    private String privPassphrase;
+
+    public UserDetails() {
+    }
+
+    public UserDetails(final String securityName, final String authProtocol, final String authPassphrase,
+                       final String privProtocol, final String privPassphrase) {
+        this.securityName = securityName;
+        this.authProtocol = authProtocol;
+        this.authPassphrase = authPassphrase;
+        this.privProtocol = privProtocol;
+        this.privPassphrase = privPassphrase;
+    }
+
+    public String getSecurityName() {
+        return securityName;
+    }
+
+    public String getAuthProtocol() {
+        return authProtocol;
+    }
+
+    public String getAuthPassphrase() {
+        return authPassphrase;
+    }
+
+    public String getPrivProtocol() {
+        return privProtocol;
+    }
+
+    public String getPrivPassphrase() {
+        return privPassphrase;
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/exception/SNMPException.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/exception/SNMPException.java
index 4e842da..7b51e0a 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/exception/SNMPException.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/exception/SNMPException.java
@@ -22,4 +22,8 @@ public class SNMPException extends RuntimeException {
         super(errorMessage);
     }
 
+    public SNMPException(final Exception exception) {
+        super(exception);
+    }
+
 }
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/AbstractSNMPFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/AbstractSNMPFactory.java
deleted file mode 100644
index baa4588..0000000
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/AbstractSNMPFactory.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.snmp.factory;
-
-import org.apache.nifi.snmp.configuration.SNMPConfiguration;
-import org.apache.nifi.snmp.exception.CreateSNMPClientException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.snmp4j.CommunityTarget;
-import org.snmp4j.Snmp;
-import org.snmp4j.Target;
-import org.snmp4j.UserTarget;
-import org.snmp4j.security.SecurityLevel;
-import org.snmp4j.smi.OctetString;
-import org.snmp4j.smi.UdpAddress;
-import org.snmp4j.transport.DefaultUdpTransportMapping;
-
-import java.io.IOException;
-import java.util.Optional;
-
-public abstract class AbstractSNMPFactory {
-
-    private static final Logger logger = LoggerFactory.getLogger(AbstractSNMPFactory.class);
-
-    protected AbstractSNMPFactory() {
-        // hide implicit constructor
-    }
-
-    protected static Snmp createSnmpClient() {
-        final Snmp snmp;
-        try {
-            snmp = new Snmp(new DefaultUdpTransportMapping());
-            snmp.listen();
-            return snmp;
-        } catch (IOException e) {
-            final String errorMessage = "Creating SNMP client failed.";
-            logger.error(errorMessage, e);
-            throw new CreateSNMPClientException(errorMessage);
-        }
-    }
-
-    protected static Target createUserTarget(final SNMPConfiguration configuration) {
-        final UserTarget userTarget = new UserTarget();
-        setupTargetBasicProperties(userTarget, configuration);
-
-        final int securityLevel = SecurityLevel.valueOf(configuration.getSecurityLevel()).getSnmpValue();
-        userTarget.setSecurityLevel(securityLevel);
-
-        final String securityName = configuration.getSecurityName();
-        Optional.ofNullable(securityName).map(OctetString::new).ifPresent(userTarget::setSecurityName);
-
-        return userTarget;
-    }
-
-    protected static Target createCommunityTarget(final SNMPConfiguration configuration) {
-        final Target communityTarget = new CommunityTarget();
-        setupTargetBasicProperties(communityTarget, configuration);
-        final String community = configuration.getCommunityString();
-
-        Optional.ofNullable(community).map(OctetString::new).ifPresent(communityTarget::setSecurityName);
-
-        return communityTarget;
-    }
-
-    private static void setupTargetBasicProperties(final Target target, final SNMPConfiguration configuration) {
-        final int snmpVersion = configuration.getVersion();
-        final String host = configuration.getAgentHost();
-        final String port = configuration.getAgentPort();
-        final int retries = configuration.getRetries();
-        final int timeout = configuration.getTimeout();
-
-        target.setVersion(snmpVersion);
-        target.setAddress(new UdpAddress(host + "/" + port));
-        target.setRetries(retries);
-        target.setTimeout(timeout);
-    }
-}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/CompositeSNMPFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/CompositeSNMPFactory.java
deleted file mode 100644
index f1234bc..0000000
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/CompositeSNMPFactory.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.snmp.factory;
-
-import org.apache.nifi.snmp.configuration.SNMPConfiguration;
-import org.apache.nifi.snmp.exception.InvalidSnmpVersionException;
-import org.snmp4j.Snmp;
-import org.snmp4j.Target;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-
-public class CompositeSNMPFactory implements SNMPFactory {
-
-    private static final String INVALID_SNMP_VERSION = "SNMP version is not supported.";
-    private static final List<SNMPFactory> FACTORIES;
-
-    static {
-        final List<SNMPFactory> factories = Arrays.asList(new V1SNMPFactory(), new V2cSNMPFactory(), new V3SNMPFactory());
-        FACTORIES = Collections.unmodifiableList(factories);
-    }
-
-    @Override
-    public boolean supports(final int version) {
-        return !getMatchingFactory(version).isPresent();
-    }
-
-    @Override
-    public Snmp createSnmpManagerInstance(final SNMPConfiguration configuration) {
-        final Optional<SNMPFactory> factory = getMatchingFactory(configuration.getVersion());
-        if (!factory.isPresent()) {
-            throw new InvalidSnmpVersionException(INVALID_SNMP_VERSION);
-        }
-        return factory.get().createSnmpManagerInstance(configuration);
-    }
-
-    @Override
-    public Target createTargetInstance(final SNMPConfiguration configuration) {
-        final Optional<SNMPFactory> factory = getMatchingFactory(configuration.getVersion());
-        if (!factory.isPresent()) {
-            throw new InvalidSnmpVersionException(INVALID_SNMP_VERSION);
-        }
-        return factory.get().createTargetInstance(configuration);
-    }
-
-    private Optional<SNMPFactory> getMatchingFactory(final int version) {
-        for (final SNMPFactory factory : FACTORIES) {
-            if (factory.supports(version)) {
-                return Optional.of(factory);
-            }
-        }
-        return Optional.empty();
-    }
-}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/V3SNMPFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/V3SNMPFactory.java
deleted file mode 100644
index 75e34d0..0000000
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/V3SNMPFactory.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.snmp.factory;
-
-import org.apache.nifi.snmp.configuration.SNMPConfiguration;
-import org.apache.nifi.snmp.utils.SNMPUtils;
-import org.snmp4j.Snmp;
-import org.snmp4j.Target;
-import org.snmp4j.mp.MPv3;
-import org.snmp4j.mp.SnmpConstants;
-import org.snmp4j.security.SecurityModels;
-import org.snmp4j.security.SecurityProtocols;
-import org.snmp4j.security.USM;
-import org.snmp4j.security.UsmUser;
-import org.snmp4j.smi.OID;
-import org.snmp4j.smi.OctetString;
-
-import java.util.Optional;
-
-public class V3SNMPFactory extends AbstractSNMPFactory implements SNMPFactory {
-
-    @Override
-    public boolean supports(final int version) {
-        return SnmpConstants.version3 == version;
-    }
-
-    @Override
-    public Snmp createSnmpManagerInstance(final SNMPConfiguration configuration) {
-        final Snmp snmp = createSnmpClient();
-
-        // If there's a USM instance associated with the MPv3 bound to this Snmp instance (like an agent running
-        // on the same host) it is not null.
-        if (snmp.getUSM() == null) {
-            final OctetString localEngineId = new OctetString(MPv3.createLocalEngineID());
-            final USM usm = new USM(SecurityProtocols.getInstance(), localEngineId, 0);
-            SecurityModels.getInstance().addSecurityModel(usm);
-        }
-
-        final String username = configuration.getSecurityName();
-        final OID authProtocol = Optional.ofNullable(configuration.getAuthProtocol())
-                .map(SNMPUtils::getAuth).orElse(null);
-        final OID privacyProtocol = Optional.ofNullable(configuration.getPrivacyProtocol())
-                .map(SNMPUtils::getPriv).orElse(null);
-        final String authPassword = configuration.getAuthPassphrase();
-        final String privacyPassword = configuration.getPrivacyPassphrase();
-        final OctetString authPasswordOctet = authPassword != null ? new OctetString(authPassword) : null;
-        final OctetString privacyPasswordOctet = privacyPassword != null ? new OctetString(privacyPassword) : null;
-
-        // Add user information.
-        snmp.getUSM().addUser(
-                new OctetString(username),
-                new UsmUser(new OctetString(username), authProtocol, authPasswordOctet,
-                        privacyProtocol, privacyPasswordOctet));
-
-        return snmp;
-    }
-
-    @Override
-    public Target createTargetInstance(final SNMPConfiguration configuration) {
-        return createUserTarget(configuration);
-    }
-}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/SNMPFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/SNMPContext.java
similarity index 50%
copy from nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/SNMPFactory.java
copy to nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/SNMPContext.java
index 442c03e..d3dd885 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/SNMPFactory.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/SNMPContext.java
@@ -14,15 +14,35 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.nifi.snmp.factory;
+package org.apache.nifi.snmp.factory.core;
 
 import org.apache.nifi.snmp.configuration.SNMPConfiguration;
+import org.apache.nifi.snmp.operations.SNMPResourceHandler;
 import org.snmp4j.Snmp;
 import org.snmp4j.Target;
+import org.snmp4j.smi.UdpAddress;
 
-public interface SNMPFactory {
+public interface SNMPContext {
 
-    boolean supports(final int version);
+    default SNMPResourceHandler createSNMPResourceHandler(final SNMPConfiguration snmpConfiguration) {
+        return new SNMPResourceHandler(
+                createSnmpManagerInstance(snmpConfiguration),
+                createTargetInstance(snmpConfiguration)
+        );
+    }
+
+    default void setupTargetBasicProperties(final Target target, final SNMPConfiguration configuration) {
+        final int snmpVersion = configuration.getVersion();
+        final String host = configuration.getTargetHost();
+        final String port = configuration.getTargetPort();
+        final int retries = configuration.getRetries();
+        final long timeout = configuration.getTimeoutInMs();
+
+        target.setVersion(snmpVersion);
+        target.setAddress(new UdpAddress(host + "/" + port));
+        target.setRetries(retries);
+        target.setTimeout(timeout);
+    }
 
     Snmp createSnmpManagerInstance(final SNMPConfiguration configuration);
 
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/SNMPFactoryProvider.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/SNMPFactoryProvider.java
new file mode 100644
index 0000000..6cc62aa
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/SNMPFactoryProvider.java
@@ -0,0 +1,48 @@
+/*
+ * 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.snmp.factory.core;
+
+import org.apache.nifi.snmp.exception.InvalidSnmpVersionException;
+import org.snmp4j.mp.SnmpConstants;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class SNMPFactoryProvider {
+
+    private SNMPFactoryProvider() {
+        // not to instantiate
+    }
+
+    private static final String INVALID_SNMP_VERSION = "SNMP version is not supported: %s";
+    private static final Map<Integer, SNMPContext> FACTORIES;
+
+    static {
+        final Map<Integer, SNMPContext> factories = new HashMap<>();
+        factories.put(SnmpConstants.version1, new V1V2cSNMPFactory());
+        factories.put(SnmpConstants.version2c, new V1V2cSNMPFactory());
+        factories.put(SnmpConstants.version3, new V3SNMPFactory());
+        FACTORIES = Collections.unmodifiableMap(factories);
+    }
+
+    public static SNMPContext getFactory(final int version) {
+        return Optional.ofNullable(FACTORIES.get(version))
+                .orElseThrow(() -> new InvalidSnmpVersionException(String.format(INVALID_SNMP_VERSION, version)));
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/V2cSNMPFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/SNMPManagerFactory.java
similarity index 58%
rename from nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/V2cSNMPFactory.java
rename to nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/SNMPManagerFactory.java
index b34609d..17afbb0 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/V2cSNMPFactory.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/SNMPManagerFactory.java
@@ -14,27 +14,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.nifi.snmp.factory;
+package org.apache.nifi.snmp.factory.core;
 
+import org.apache.nifi.processor.exception.ProcessException;
 import org.apache.nifi.snmp.configuration.SNMPConfiguration;
 import org.snmp4j.Snmp;
-import org.snmp4j.Target;
-import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.smi.UdpAddress;
+import org.snmp4j.transport.DefaultUdpTransportMapping;
 
-public class V2cSNMPFactory extends AbstractSNMPFactory implements SNMPFactory {
+import java.io.IOException;
 
-    @Override
-    public boolean supports(final int version) {
-        return SnmpConstants.version2c == version;
-    }
+public class SNMPManagerFactory {
 
-    @Override
-    public Snmp createSnmpManagerInstance(final SNMPConfiguration configuration) {
-        return createSnmpClient();
-    }
+    private static final String LOCALHOST = "127.0.0.1";
 
-    @Override
-    public Target createTargetInstance(final SNMPConfiguration configuration) {
-        return createCommunityTarget(configuration);
+    public Snmp createSnmpManagerInstance(final SNMPConfiguration configuration) {
+        final String managerAddress = LOCALHOST + "/" + configuration.getManagerPort();
+        final Snmp snmpManager;
+        try {
+            snmpManager = new Snmp(new DefaultUdpTransportMapping(new UdpAddress(managerAddress)));
+            snmpManager.listen();
+        } catch (IOException e) {
+            throw new ProcessException(e);
+        }
+        return snmpManager;
     }
 }
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/V1SNMPFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/V1V2cSNMPFactory.java
similarity index 64%
rename from nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/V1SNMPFactory.java
rename to nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/V1V2cSNMPFactory.java
index e089658..636ca4a 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/V1SNMPFactory.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/V1V2cSNMPFactory.java
@@ -14,27 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.nifi.snmp.factory;
+package org.apache.nifi.snmp.factory.core;
 
 import org.apache.nifi.snmp.configuration.SNMPConfiguration;
-import org.snmp4j.Snmp;
+import org.snmp4j.CommunityTarget;
 import org.snmp4j.Target;
-import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.smi.OctetString;
 
-public class V1SNMPFactory extends AbstractSNMPFactory implements SNMPFactory {
+import java.util.Optional;
 
-    @Override
-    public boolean supports(final int version) {
-        return SnmpConstants.version1 == version;
-    }
-
-    @Override
-    public Snmp createSnmpManagerInstance(final SNMPConfiguration configuration) {
-        return createSnmpClient();
-    }
+public class V1V2cSNMPFactory extends SNMPManagerFactory implements SNMPContext {
 
     @Override
     public Target createTargetInstance(final SNMPConfiguration configuration) {
-        return createCommunityTarget(configuration);
+        final Target communityTarget = new CommunityTarget();
+        setupTargetBasicProperties(communityTarget, configuration);
+        final String community = configuration.getCommunityString();
+
+        Optional.ofNullable(community).map(OctetString::new).ifPresent(communityTarget::setSecurityName);
+
+        return communityTarget;
     }
 }
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/V3SNMPFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/V3SNMPFactory.java
new file mode 100644
index 0000000..4bc0897
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/core/V3SNMPFactory.java
@@ -0,0 +1,77 @@
+/*
+ * 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.snmp.factory.core;
+
+import org.apache.nifi.snmp.configuration.SNMPConfiguration;
+import org.apache.nifi.snmp.utils.SNMPUtils;
+import org.snmp4j.Snmp;
+import org.snmp4j.Target;
+import org.snmp4j.UserTarget;
+import org.snmp4j.mp.MPv3;
+import org.snmp4j.security.SecurityLevel;
+import org.snmp4j.security.SecurityModels;
+import org.snmp4j.security.SecurityProtocols;
+import org.snmp4j.security.USM;
+import org.snmp4j.security.UsmUser;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+
+import java.util.Optional;
+
+public class V3SNMPFactory extends SNMPManagerFactory implements SNMPContext {
+
+    @Override
+    public Snmp createSnmpManagerInstance(final SNMPConfiguration configuration) {
+        final Snmp snmpManager = super.createSnmpManagerInstance(configuration);
+
+        // Create USM.
+        final OctetString localEngineId = new OctetString(MPv3.createLocalEngineID());
+        final USM usm = new USM(SecurityProtocols.getInstance(), localEngineId, 0);
+        SecurityModels.getInstance().addSecurityModel(usm);
+
+        Optional.ofNullable(configuration.getSecurityName())
+                .map(OctetString::new)
+                .ifPresent(securityName -> {
+                    OID authProtocol = Optional.ofNullable(configuration.getAuthProtocol())
+                            .map(SNMPUtils::getAuth).orElse(null);
+                    OctetString authPassphrase = Optional.ofNullable(configuration.getAuthPassphrase())
+                            .map(OctetString::new).orElse(null);
+                    OID privacyProtocol = Optional.ofNullable(configuration.getPrivacyProtocol())
+                            .map(SNMPUtils::getPriv).orElse(null);
+                    OctetString privacyPassphrase = Optional.ofNullable(configuration.getPrivacyPassphrase())
+                            .map(OctetString::new).orElse(null);
+                    snmpManager.getUSM().addUser(securityName, new UsmUser(securityName, authProtocol, authPassphrase,
+                            privacyProtocol, privacyPassphrase));
+                });
+
+        return snmpManager;
+    }
+
+    @Override
+    public Target createTargetInstance(final SNMPConfiguration configuration) {
+        final UserTarget userTarget = new UserTarget();
+        setupTargetBasicProperties(userTarget, configuration);
+
+        final int securityLevel = SecurityLevel.valueOf(configuration.getSecurityLevel()).getSnmpValue();
+        userTarget.setSecurityLevel(securityLevel);
+
+        final String securityName = configuration.getSecurityName();
+        Optional.ofNullable(securityName).map(OctetString::new).ifPresent(userTarget::setSecurityName);
+
+        return userTarget;
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/trap/V1TrapPDUFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/trap/V1TrapPDUFactory.java
new file mode 100644
index 0000000..a68f2b1
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/trap/V1TrapPDUFactory.java
@@ -0,0 +1,58 @@
+/*
+ * 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.snmp.factory.trap;
+
+import org.apache.nifi.snmp.configuration.V1TrapConfiguration;
+import org.snmp4j.PDU;
+import org.snmp4j.PDUv1;
+import org.snmp4j.Target;
+import org.snmp4j.smi.IpAddress;
+import org.snmp4j.smi.OID;
+import org.snmp4j.util.DefaultPDUFactory;
+import org.snmp4j.util.PDUFactory;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Optional;
+
+/**
+ * Factory class to create SNMPv1-Trap-PDU for SNMPv1.
+ */
+public class V1TrapPDUFactory {
+
+    private static final PDUFactory v1PduFactory = new DefaultPDUFactory(PDU.V1TRAP);
+
+    final Target target;
+    final Instant startTime;
+
+    public V1TrapPDUFactory(final Target target, final Instant startTime) {
+        this.target = target;
+        this.startTime = startTime;
+    }
+
+    public PDU get(final V1TrapConfiguration v1TrapConfiguration) {
+        final PDUv1 pdu = (PDUv1) v1PduFactory.createPDU(target);
+        Optional.ofNullable(v1TrapConfiguration.getEnterpriseOid()).map(OID::new).ifPresent(pdu::setEnterprise);
+        Optional.ofNullable(v1TrapConfiguration.getAgentAddress()).map(IpAddress::new).ifPresent(pdu::setAgentAddress);
+        pdu.setGenericTrap(v1TrapConfiguration.getGenericTrapType());
+        Optional.ofNullable(v1TrapConfiguration.getSpecificTrapType()).ifPresent(pdu::setSpecificTrap);
+        final long elapsedMillis = Duration.between(startTime, Instant.now()).toMillis();
+        pdu.setTimestamp(elapsedMillis);
+        return pdu;
+    }
+
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/trap/V2TrapPDUFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/trap/V2TrapPDUFactory.java
new file mode 100644
index 0000000..7ff5864
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/trap/V2TrapPDUFactory.java
@@ -0,0 +1,54 @@
+/*
+ * 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.snmp.factory.trap;
+
+import org.apache.nifi.snmp.configuration.V2TrapConfiguration;
+import org.snmp4j.PDU;
+import org.snmp4j.Target;
+import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.smi.OctetString;
+import org.snmp4j.smi.TimeTicks;
+import org.snmp4j.smi.VariableBinding;
+import org.snmp4j.util.DefaultPDUFactory;
+import org.snmp4j.util.PDUFactory;
+
+import java.time.Duration;
+import java.time.Instant;
+
+/**
+ * Factory class to create SNMPv2-Trap-PDU for SNMPv2c and SNMPv3.
+ */
+public class V2TrapPDUFactory {
+    private static final PDUFactory v2PduFactory = new DefaultPDUFactory(PDU.TRAP);
+
+    final Target target;
+    final Instant startTime;
+
+    public V2TrapPDUFactory(final Target target, final Instant startTime) {
+        this.target = target;
+        this.startTime = startTime;
+    }
+
+    public PDU get(final V2TrapConfiguration v2TrapConfiguration) {
+        final PDU pdu = v2PduFactory.createPDU(target);
+        pdu.add(new VariableBinding(SnmpConstants.snmpTrapOID, new OctetString(v2TrapConfiguration.getTrapOidValue())));
+        final long elapsedMillis = Duration.between(startTime, Instant.now()).toMillis();
+        pdu.add(new VariableBinding(SnmpConstants.sysUpTime, new TimeTicks(elapsedMillis)));
+        return pdu;
+    }
+
+}
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/GetSNMPHandler.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/GetSNMPHandler.java
new file mode 100644
index 0000000..3514dd7
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/GetSNMPHandler.java
@@ -0,0 +1,150 @@
+/*
+ * 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.snmp.operations;
+
+import org.apache.nifi.snmp.dto.SNMPSingleResponse;
+import org.apache.nifi.snmp.dto.SNMPTreeResponse;
+import org.apache.nifi.snmp.exception.RequestTimeoutException;
+import org.apache.nifi.snmp.exception.SNMPWalkException;
+import org.apache.nifi.snmp.utils.SNMPUtils;
+import org.snmp4j.PDU;
+import org.snmp4j.Snmp;
+import org.snmp4j.Target;
+import org.snmp4j.event.ResponseEvent;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.VariableBinding;
+import org.snmp4j.util.DefaultPDUFactory;
+import org.snmp4j.util.PDUFactory;
+import org.snmp4j.util.TreeEvent;
+import org.snmp4j.util.TreeUtils;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.apache.nifi.snmp.operations.SNMPResourceHandler.REQUEST_TIMEOUT_EXCEPTION_TEMPLATE;
+
+public class GetSNMPHandler {
+
+    private static final PDUFactory getPduFactory = new DefaultPDUFactory(PDU.GET);
+
+    protected static final String EMPTY_SUBTREE_EXCEPTION_MESSAGE = "Agent is not available, the %s OID not found or user not found. " +
+            "Please, check if (1) the agent is available, (2) the processor's SNMP version matches the agent version, " +
+            "(3) the OID is correct, (4) The user is valid.";
+
+    protected static final String SNMP_ERROR_EXCEPTION_MESSAGE = "Agent is not available, OID not found or user not found. " +
+            "Please, check if (1) the agent is available, (2) the processor's SNMP version matches the agent version, " +
+            "(3) the OID is correct, (4) The user is valid.";
+
+    protected static final String LEAF_ELEMENT_EXCEPTION_MESSAGE = "OID not found or it is a single leaf element. The leaf element " +
+            "associated with this %s OID does not contain child OIDs. Please check if the OID exists in the agent " +
+            "MIB or specify a parent OID with at least one child element";
+
+    private final SNMPResourceHandler snmpResourceHandler;
+    private TreeUtils treeUtils;
+
+    public GetSNMPHandler(final SNMPResourceHandler snmpResourceHandler) {
+        this.snmpResourceHandler = snmpResourceHandler;
+        this.treeUtils = new TreeUtils(snmpResourceHandler.getSnmpManager(), getPduFactory);
+    }
+
+    public SNMPSingleResponse get(final String oid) throws IOException {
+        final Target target = snmpResourceHandler.getTarget();
+        final Snmp snmpManager = snmpResourceHandler.getSnmpManager();
+
+        final PDU pdu = getPduFactory.createPDU(target);
+        pdu.add(new VariableBinding(new OID(oid)));
+
+        final PDU responsePdu = getResponsePdu(target, snmpManager, pdu);
+        return new SNMPSingleResponse(target, responsePdu);
+    }
+
+    public Optional<SNMPSingleResponse> get(final Map<String, String> flowFileAttributes) throws IOException {
+        final Target target = snmpResourceHandler.getTarget();
+        final Snmp snmpManager = snmpResourceHandler.getSnmpManager();
+
+        final PDU pdu = getPduFactory.createPDU(target);
+        VariableBinding[] variableBindings = SNMPUtils.addGetVariables(flowFileAttributes);
+        if (variableBindings.length == 0) {
+            return Optional.empty();
+        }
+        pdu.addAll(variableBindings);
+
+        final PDU responsePdu = getResponsePdu(target, snmpManager, pdu);
+        return Optional.of(new SNMPSingleResponse(target, responsePdu));
+    }
+
+    public SNMPTreeResponse walk(final String oid) {
+        final Target target = snmpResourceHandler.getTarget();
+        final List<TreeEvent> subtree = treeUtils.getSubtree(target, new OID(oid));
+
+        evaluateSubtreeErrors(oid, subtree);
+
+        return new SNMPTreeResponse(target, subtree);
+    }
+
+    public Optional<SNMPTreeResponse> walk(final Map<String, String> flowFileAttributes) {
+        final Target target = snmpResourceHandler.getTarget();
+        final List<TreeEvent> subtree;
+
+        final OID[] oids = SNMPUtils.addWalkVariables(flowFileAttributes);
+        if (oids.length == 0) {
+            return Optional.empty();
+        }
+        subtree = treeUtils.walk(target, oids);
+
+        evaluateSubtreeErrors(Arrays.toString(oids), subtree);
+
+        return Optional.of(new SNMPTreeResponse(target, subtree));
+    }
+
+    private PDU getResponsePdu(Target target, Snmp snmpManager, PDU pdu) throws IOException {
+        final ResponseEvent response = snmpManager.get(pdu, target);
+        final PDU responsePdu = response.getResponse();
+        if (responsePdu == null) {
+            throw new RequestTimeoutException(String.format(REQUEST_TIMEOUT_EXCEPTION_TEMPLATE, "read"));
+        }
+        return responsePdu;
+    }
+
+    private void evaluateSubtreeErrors(String oid, List<TreeEvent> subtree) {
+        if (subtree.isEmpty()) {
+            throw new SNMPWalkException(String.format(EMPTY_SUBTREE_EXCEPTION_MESSAGE, oid));
+        }
+        if (isSnmpError(subtree)) {
+            throw new SNMPWalkException(SNMP_ERROR_EXCEPTION_MESSAGE);
+        }
+        if (isLeafElement(subtree)) {
+            throw new SNMPWalkException(String.format(LEAF_ELEMENT_EXCEPTION_MESSAGE, oid));
+        }
+    }
+
+    private boolean isSnmpError(final List<TreeEvent> subtree) {
+        return subtree.size() == 1 && subtree.get(0).getVariableBindings() == null;
+    }
+
+    private boolean isLeafElement(final List<TreeEvent> subtree) {
+        return subtree.size() == 1 && subtree.get(0).getVariableBindings().length == 0;
+    }
+
+    // Visible for testing
+    void setTreeUtils(final TreeUtils treeUtils) {
+        this.treeUtils = treeUtils;
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPRequestHandlerFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPRequestHandlerFactory.java
deleted file mode 100644
index 6663c4e..0000000
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPRequestHandlerFactory.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.snmp.operations;
-
-import org.apache.nifi.snmp.configuration.SNMPConfiguration;
-import org.apache.nifi.snmp.factory.CompositeSNMPFactory;
-import org.apache.nifi.snmp.factory.SNMPFactory;
-import org.snmp4j.Snmp;
-import org.snmp4j.Target;
-
-public final class SNMPRequestHandlerFactory {
-
-    public static SNMPRequestHandler createStandardRequestHandler(final SNMPConfiguration configuration) {
-        final SNMPFactory snmpFactory = new CompositeSNMPFactory();
-        final Snmp snmpClient = snmpFactory.createSnmpManagerInstance(configuration);
-        final Target target = snmpFactory.createTargetInstance(configuration);
-        return new StandardSNMPRequestHandler(snmpClient, target);
-    }
-
-    private SNMPRequestHandlerFactory() {
-        // This should not be instantiated.
-    }
-
-}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPResourceHandler.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPResourceHandler.java
new file mode 100644
index 0000000..b19ded6
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPResourceHandler.java
@@ -0,0 +1,70 @@
+/*
+ * 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.snmp.operations;
+
+import org.apache.nifi.processor.exception.ProcessException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snmp4j.Snmp;
+import org.snmp4j.Target;
+import org.snmp4j.security.SecurityModels;
+import org.snmp4j.smi.Integer32;
+
+import java.io.IOException;
+
+public class SNMPResourceHandler {
+
+    public static final String INVALID_FLOWFILE_EXCEPTION_MESSAGE = "Could not read the variable bindings from the " +
+            "flowfile. Please, add the OIDs to set in separate properties. E.g. Property name: snmp$1.3.6.1.2.1.1.1.0 " +
+            "Value: Example value. ";
+
+    public static final String REQUEST_TIMEOUT_EXCEPTION_TEMPLATE = "Request timed out. Please check if (1). the " +
+            "agent host and port is correctly set, (2). the agent is running, (3). the agent SNMP version corresponds" +
+            " with the processor's one, (4) the community string is correct and has %1$s access, (5) In case of SNMPv3" +
+            " check if the user credentials are valid and the user in a group with %1$s access.";
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+    private final Snmp snmpManager;
+    private final Target target;
+
+    public SNMPResourceHandler(final Snmp snmpManager, final Target target) {
+        this.snmpManager = snmpManager;
+        this.target = target;
+    }
+
+    public Snmp getSnmpManager() {
+        return snmpManager;
+    }
+
+    public Target getTarget() {
+        return target;
+    }
+
+    public void close() {
+        try {
+            if (snmpManager.getUSM() != null) {
+                snmpManager.getUSM().removeAllUsers();
+                SecurityModels.getInstance().removeSecurityModel(new Integer32(snmpManager.getUSM().getID()));
+            }
+            snmpManager.close();
+        } catch (IOException e) {
+            final String errorMessage = "Could not close SNMP manager.";
+            logger.error(errorMessage, e);
+            throw new ProcessException(errorMessage);
+        }
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPTrapReceiver.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPTrapReceiver.java
new file mode 100644
index 0000000..d2eaf19
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPTrapReceiver.java
@@ -0,0 +1,77 @@
+/*
+ * 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.snmp.operations;
+
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.ProcessSessionFactory;
+import org.apache.nifi.snmp.utils.SNMPUtils;
+import org.snmp4j.CommandResponder;
+import org.snmp4j.CommandResponderEvent;
+import org.snmp4j.PDU;
+import org.snmp4j.PDUv1;
+
+import java.util.Map;
+
+import static org.apache.nifi.snmp.processors.ListenTrapSNMP.REL_FAILURE;
+import static org.apache.nifi.snmp.processors.ListenTrapSNMP.REL_SUCCESS;
+
+public class SNMPTrapReceiver implements CommandResponder {
+
+    private final ProcessSessionFactory processSessionFactory;
+    private final ComponentLog logger;
+
+    public SNMPTrapReceiver(final ProcessSessionFactory processSessionFactory, final ComponentLog logger) {
+        this.processSessionFactory = processSessionFactory;
+        this.logger = logger;
+    }
+
+    @Override
+    public void processPdu(final CommandResponderEvent event) {
+        final PDU pdu = event.getPDU();
+        if (isValidTrapPdu(pdu)) {
+            final ProcessSession processSession = processSessionFactory.createSession();
+            final FlowFile flowFile = createFlowFile(processSession, pdu);
+            processSession.getProvenanceReporter().create(flowFile, event.getPeerAddress() + "/" + pdu.getRequestID());
+            if (pdu.getErrorStatus() == PDU.noError) {
+                processSession.transfer(flowFile, REL_SUCCESS);
+            } else {
+                processSession.transfer(flowFile, REL_FAILURE);
+            }
+            processSession.commitAsync();
+        } else {
+            logger.error("Request timed out or parameters are incorrect.");
+        }
+    }
+
+    private FlowFile createFlowFile(final ProcessSession processSession, final PDU pdu) {
+        FlowFile flowFile = processSession.create();
+        final Map<String, String> attributes;
+        if (pdu instanceof PDUv1) {
+            attributes = SNMPUtils.getV1TrapPduAttributeMap((PDUv1) pdu);
+        } else {
+            attributes = SNMPUtils.getPduAttributeMap(pdu);
+        }
+        flowFile = processSession.putAllAttributes(flowFile, attributes);
+        return flowFile;
+    }
+
+    private boolean isValidTrapPdu(final PDU pdu) {
+        return pdu != null && (pdu.getType() == PDU.V1TRAP || pdu.getType() == PDU.TRAP);
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPTrapReceiverHandler.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPTrapReceiverHandler.java
new file mode 100644
index 0000000..53b1ff2
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SNMPTrapReceiverHandler.java
@@ -0,0 +1,124 @@
+/*
+ * 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.snmp.operations;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.processor.ProcessSessionFactory;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.snmp.configuration.SNMPConfiguration;
+import org.apache.nifi.snmp.dto.UserDetails;
+import org.apache.nifi.snmp.factory.core.SNMPManagerFactory;
+import org.apache.nifi.snmp.utils.SNMPUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snmp4j.Snmp;
+import org.snmp4j.mp.MPv3;
+import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.security.SecurityModels;
+import org.snmp4j.security.SecurityProtocols;
+import org.snmp4j.security.USM;
+import org.snmp4j.security.UsmUser;
+import org.snmp4j.smi.Integer32;
+import org.snmp4j.smi.OctetString;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+import java.util.Scanner;
+
+public class SNMPTrapReceiverHandler {
+
+    private static final Logger logger = LoggerFactory.getLogger(SNMPTrapReceiverHandler.class);
+
+    private final SNMPConfiguration configuration;
+    private final String usmUsersFilePath;
+    private Snmp snmpManager;
+    private boolean isStarted;
+
+    public SNMPTrapReceiverHandler(final SNMPConfiguration configuration, final String usmUsersFilePath) {
+        this.configuration = configuration;
+        this.usmUsersFilePath = usmUsersFilePath;
+        snmpManager = new SNMPManagerFactory().createSnmpManagerInstance(configuration);
+    }
+
+    public void createTrapReceiver(final ProcessSessionFactory processSessionFactory, final ComponentLog logger) {
+        addUsmUsers();
+        SNMPTrapReceiver trapReceiver = new SNMPTrapReceiver(processSessionFactory, logger);
+        snmpManager.addCommandResponder(trapReceiver);
+        isStarted = true;
+    }
+
+    public boolean isStarted() {
+        return isStarted;
+    }
+
+    public void close() {
+        try {
+            if (snmpManager.getUSM() != null) {
+                snmpManager.getUSM().removeAllUsers();
+                SecurityModels.getInstance().removeSecurityModel(new Integer32(snmpManager.getUSM().getID()));
+            }
+            snmpManager.close();
+            isStarted = false;
+        } catch (IOException e) {
+            final String errorMessage = "Could not close SNMP manager.";
+            logger.error(errorMessage, e);
+            throw new ProcessException(errorMessage);
+        }
+    }
+
+    private void addUsmUsers() {
+        if (configuration.getVersion() == SnmpConstants.version3) {
+            USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
+            SecurityModels.getInstance().addSecurityModel(usm);
+
+            try (Scanner scanner = new Scanner(new File(usmUsersFilePath))) {
+                final String content = scanner.useDelimiter("\\Z").next();
+                final ObjectMapper mapper = new ObjectMapper();
+                final List<UserDetails> userDetails = mapper.readValue(content, new TypeReference<List<UserDetails>>() {
+                });
+                userDetails.stream()
+                        .map(this::convertToUsmUser)
+                        .forEach(user -> snmpManager.getUSM().addUser(user));
+
+            } catch (FileNotFoundException e) {
+                throw new ProcessException("USM user file not found, please check the file path and file permissions.", e);
+            } catch (JsonProcessingException e) {
+                throw new ProcessException("Could not parse USM user file, please check the processor details for examples.", e);
+            }
+        }
+    }
+
+    private UsmUser convertToUsmUser(final UserDetails user) {
+        return new UsmUser(
+                new OctetString(user.getSecurityName()),
+                SNMPUtils.getAuth(user.getAuthProtocol()),
+                new OctetString(user.getAuthPassphrase()),
+                SNMPUtils.getPriv(user.getPrivProtocol()),
+                new OctetString(user.getPrivPassphrase())
+        );
+    }
+
+    // Visible for testing.
+    void setSnmpManager(final Snmp snmpManager) {
+        this.snmpManager = snmpManager;
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SendTrapSNMPHandler.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SendTrapSNMPHandler.java
new file mode 100644
index 0000000..f9f608d
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SendTrapSNMPHandler.java
@@ -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.
+ */
+package org.apache.nifi.snmp.operations;
+
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.snmp.configuration.V1TrapConfiguration;
+import org.apache.nifi.snmp.configuration.V2TrapConfiguration;
+import org.apache.nifi.snmp.factory.trap.V1TrapPDUFactory;
+import org.apache.nifi.snmp.factory.trap.V2TrapPDUFactory;
+import org.apache.nifi.snmp.utils.SNMPUtils;
+import org.snmp4j.PDU;
+import org.snmp4j.Snmp;
+import org.snmp4j.Target;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.Map;
+
+public class SendTrapSNMPHandler {
+    private final SNMPResourceHandler snmpResourceHandler;
+    private final ComponentLog logger;
+    private final V1TrapPDUFactory v1TrapPDUFactory;
+    private final V2TrapPDUFactory v2TrapPDUFactory;
+
+    public SendTrapSNMPHandler(final SNMPResourceHandler snmpResourceHandler, final Instant startTime, final ComponentLog logger) {
+        this.snmpResourceHandler = snmpResourceHandler;
+        this.logger = logger;
+        v1TrapPDUFactory = createV1TrapPduFactory(startTime);
+        v2TrapPDUFactory = createV2TrapPduFactory(startTime);
+    }
+
+    public void sendTrap(final Map<String, String> flowFileAttributes, final V1TrapConfiguration trapConfiguration) throws IOException {
+        final PDU pdu = v1TrapPDUFactory.get(trapConfiguration);
+        sendTrap(flowFileAttributes, pdu);
+    }
+
+    public void sendTrap(final Map<String, String> flowFileAttributes, final V2TrapConfiguration trapConfiguration) throws IOException {
+        final PDU pdu = v2TrapPDUFactory.get(trapConfiguration);
+        sendTrap(flowFileAttributes, pdu);
+    }
+
+    private void sendTrap(Map<String, String> flowFileAttributes, PDU pdu) throws IOException {
+        final Target target = snmpResourceHandler.getTarget();
+        final Snmp snmpManager = snmpResourceHandler.getSnmpManager();
+
+        final boolean isAnyVariableAdded = SNMPUtils.addVariables(pdu, flowFileAttributes);
+        if (!isAnyVariableAdded) {
+            logger.debug("No optional SNMP specific variables found in flowfile.");
+        }
+
+        snmpManager.send(pdu, target);
+    }
+
+    V1TrapPDUFactory createV1TrapPduFactory(final Instant startTime) {
+        return new V1TrapPDUFactory(snmpResourceHandler.getTarget(), startTime);
+    }
+
+    V2TrapPDUFactory createV2TrapPduFactory(final Instant startTime) {
+        return new V2TrapPDUFactory(snmpResourceHandler.getTarget(), startTime);
+    }
+}
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SetSNMPHandler.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SetSNMPHandler.java
new file mode 100644
index 0000000..f77c4eb
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/SetSNMPHandler.java
@@ -0,0 +1,66 @@
+/*
+ * 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.snmp.operations;
+
+import org.apache.nifi.snmp.dto.SNMPSingleResponse;
+import org.apache.nifi.snmp.exception.RequestTimeoutException;
+import org.apache.nifi.snmp.utils.SNMPUtils;
+import org.snmp4j.PDU;
+import org.snmp4j.Snmp;
+import org.snmp4j.Target;
+import org.snmp4j.event.ResponseEvent;
+import org.snmp4j.util.DefaultPDUFactory;
+import org.snmp4j.util.PDUFactory;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.apache.nifi.snmp.operations.SNMPResourceHandler.REQUEST_TIMEOUT_EXCEPTION_TEMPLATE;
+
+public class SetSNMPHandler {
+    private static PDUFactory setPduFactory = new DefaultPDUFactory(PDU.SET);
+
+    private final SNMPResourceHandler snmpResourceHandler;
+
+    public SetSNMPHandler(final SNMPResourceHandler snmpResourceHandler) {
+        this.snmpResourceHandler = snmpResourceHandler;
+    }
+
+    public Optional<SNMPSingleResponse> set(final Map<String, String> flowFileAttributes) throws IOException {
+        Target target = snmpResourceHandler.getTarget();
+        Snmp snmpManager = snmpResourceHandler.getSnmpManager();
+
+        final PDU pdu = setPduFactory.createPDU(target);
+        final boolean isAnySnmpVariableInFlowFile = SNMPUtils.addVariables(pdu, flowFileAttributes);
+        if (isAnySnmpVariableInFlowFile) {
+            final ResponseEvent response = snmpManager.set(pdu, target);
+            final PDU responsePdu = response.getResponse();
+            if (responsePdu == null) {
+                throw new RequestTimeoutException(String.format(REQUEST_TIMEOUT_EXCEPTION_TEMPLATE, "write"));
+            }
+            return Optional.of(new SNMPSingleResponse(target, responsePdu));
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    // visible for testing
+    static void setSetPduFactory(final PDUFactory setPduFactory) {
+        SetSNMPHandler.setPduFactory = setPduFactory;
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/StandardSNMPRequestHandler.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/StandardSNMPRequestHandler.java
deleted file mode 100644
index 47ce736..0000000
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/operations/StandardSNMPRequestHandler.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.snmp.operations;
-
-import org.apache.nifi.flowfile.FlowFile;
-import org.apache.nifi.snmp.dto.SNMPSingleResponse;
-import org.apache.nifi.snmp.dto.SNMPTreeResponse;
-import org.apache.nifi.snmp.exception.CloseSNMPClientException;
-import org.apache.nifi.snmp.exception.InvalidFlowFileException;
-import org.apache.nifi.snmp.exception.RequestTimeoutException;
-import org.apache.nifi.snmp.exception.SNMPWalkException;
-import org.apache.nifi.snmp.utils.SNMPUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.snmp4j.PDU;
-import org.snmp4j.Snmp;
-import org.snmp4j.Target;
-import org.snmp4j.event.ResponseEvent;
-import org.snmp4j.security.SecurityModels;
-import org.snmp4j.smi.Integer32;
-import org.snmp4j.smi.OID;
-import org.snmp4j.smi.VariableBinding;
-import org.snmp4j.util.DefaultPDUFactory;
-import org.snmp4j.util.PDUFactory;
-import org.snmp4j.util.TreeEvent;
-import org.snmp4j.util.TreeUtils;
-
-import java.io.IOException;
-import java.util.List;
-
-final class StandardSNMPRequestHandler implements SNMPRequestHandler {
-
-    private static final Logger logger = LoggerFactory.getLogger(StandardSNMPRequestHandler.class);
-    private static final PDUFactory getPduFactory = new DefaultPDUFactory(PDU.GET);
-    private static final PDUFactory setPduFactory = new DefaultPDUFactory(PDU.SET);
-    private final Snmp snmpManager;
-    private final Target target;
-
-    StandardSNMPRequestHandler(final Snmp snmpManager, final Target target) {
-        this.snmpManager = snmpManager;
-        this.target = target;
-    }
-
-    /**
-     * Construct the PDU to perform the SNMP Get request and returns
-     * the result in order to create the flow file.
-     *
-     * @return {@link ResponseEvent}
-     */
-    public SNMPSingleResponse get(final String oid) throws IOException {
-        final PDU pdu = getPduFactory.createPDU(target);
-        pdu.add(new VariableBinding(new OID(oid)));
-        final ResponseEvent response = snmpManager.get(pdu, target);
-        final PDU responsePdu = response.getResponse();
-        if (responsePdu == null) {
-            throw new RequestTimeoutException("Request timed out. Please check if (1). the agent host and port is correctly set, " +
-                    "(2). the agent is running, (3). the agent SNMP version corresponds with the processor's one, (4) the " +
-                    "community string is correct and has read access, (5) In case of SNMPv3 check if the user credentials " +
-                    "are valid and the user in a group with read access.");
-        }
-        return new SNMPSingleResponse(target, responsePdu);
-    }
-
-    /**
-     * Perform a SNMP walk and returns the list of {@link TreeEvent}
-     *
-     * @return the list of {@link TreeEvent}
-     */
-    public SNMPTreeResponse walk(final String oid) {
-        final TreeUtils treeUtils = new TreeUtils(snmpManager, getPduFactory);
-        final List<TreeEvent> subtree = treeUtils.getSubtree(target, new OID(oid));
-        if (subtree.isEmpty()) {
-            throw new SNMPWalkException(String.format("The subtree associated with the specified OID %s is empty.", oid));
-        }
-        if (isSnmpError(subtree)) {
-            throw new SNMPWalkException("Agent is not available, OID not found or user not found. Please, check if (1) the " +
-                    "agent is available, (2) the processor's SNMP version matches the agent version, (3) the OID is " +
-                    "correct, (4) The user is valid.");
-        }
-        if (isLeafElement(subtree)) {
-            throw new SNMPWalkException(String.format("OID not found or it is a single leaf element. The leaf element " +
-                    "associated with this %s OID does not contain child OIDs. Please check if the OID exists in the agent " +
-                    "MIB or specify a parent OID with at least one child element", oid));
-        }
-
-        return new SNMPTreeResponse(target, subtree);
-    }
-
-    private boolean isSnmpError(final List<TreeEvent> subtree) {
-        return subtree.size() == 1 && subtree.get(0).getVariableBindings() == null;
-    }
-
-    private boolean isLeafElement(final List<TreeEvent> subtree) {
-        return subtree.size() == 1 && subtree.get(0).getVariableBindings().length == 0;
-    }
-
-    /**
-     * Executes the SNMP set request and returns the response.
-     *
-     * @param flowFile FlowFile which contains variables for the PDU
-     * @return Response event
-     * @throws IOException IO Exception
-     */
-    public SNMPSingleResponse set(final FlowFile flowFile) throws IOException {
-        final PDU pdu = setPduFactory.createPDU(target);
-        if (SNMPUtils.addVariables(pdu, flowFile.getAttributes())) {
-            final ResponseEvent response = snmpManager.set(pdu, target);
-            final PDU responsePdu = response.getResponse();
-            if (responsePdu == null) {
-                throw new RequestTimeoutException("Request timed out. Please check if (1). the agent host and port is correctly set, " +
-                        "(2). the agent is running, (3). the agent SNMP version corresponds with the processor's one, (4) the " +
-                        "community string is correct and has write access, (5) In case of SNMPv3 check if the user credentials " +
-                        "are valid and the user in a group with write access.");
-            }
-            return new SNMPSingleResponse(target, responsePdu);
-        }
-        throw new InvalidFlowFileException("Could not read the variable bindings from the flowfile. Please, " +
-                "add the OIDs to set in separate properties. E.g. Property name: snmp$1.3.6.1.2.1.1.1.0 Value: Example value. ");
-    }
-
-    public void close() {
-        try {
-            if (snmpManager.getUSM() != null) {
-                snmpManager.getUSM().removeAllUsers();
-                SecurityModels.getInstance().removeSecurityModel(new Integer32(snmpManager.getUSM().getID()));
-            }
-            snmpManager.close();
-        } catch (IOException e) {
-            final String errorMessage = "Could not close SNMP client.";
-            logger.error(errorMessage, e);
-            throw new CloseSNMPClientException(errorMessage);
-        }
-    }
-}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/AbstractSNMPProcessor.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/AbstractSNMPProcessor.java
index 7b9c5d0..43c3a10 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/AbstractSNMPProcessor.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/AbstractSNMPProcessor.java
@@ -19,7 +19,6 @@ package org.apache.nifi.snmp.processors;
 import org.apache.nifi.annotation.behavior.RequiresInstanceClassLoading;
 import org.apache.nifi.annotation.lifecycle.OnScheduled;
 import org.apache.nifi.annotation.lifecycle.OnStopped;
-import org.apache.nifi.components.AllowableValue;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.flowfile.FlowFile;
 import org.apache.nifi.processor.AbstractProcessor;
@@ -27,21 +26,20 @@ import org.apache.nifi.processor.ProcessContext;
 import org.apache.nifi.processor.ProcessSession;
 import org.apache.nifi.processor.Relationship;
 import org.apache.nifi.processor.util.StandardValidators;
-import org.apache.nifi.reporting.InitializationException;
 import org.apache.nifi.snmp.configuration.SNMPConfiguration;
-import org.apache.nifi.snmp.configuration.SNMPConfigurationBuilder;
+import org.apache.nifi.snmp.dto.ErrorStatus;
+import org.apache.nifi.snmp.dto.SNMPResponseStatus;
 import org.apache.nifi.snmp.dto.SNMPSingleResponse;
 import org.apache.nifi.snmp.dto.SNMPValue;
-import org.apache.nifi.snmp.exception.SNMPException;
+import org.apache.nifi.snmp.factory.core.SNMPFactoryProvider;
 import org.apache.nifi.snmp.logging.SLF4JLogFactory;
-import org.apache.nifi.snmp.operations.SNMPRequestHandler;
-import org.apache.nifi.snmp.operations.SNMPRequestHandlerFactory;
+import org.apache.nifi.snmp.operations.SNMPResourceHandler;
+import org.apache.nifi.snmp.processors.properties.BasicProperties;
+import org.apache.nifi.snmp.processors.properties.V3SecurityProperties;
 import org.apache.nifi.snmp.utils.SNMPUtils;
 import org.snmp4j.log.LogFactory;
 import org.snmp4j.mp.SnmpConstants;
 
-import java.util.HashMap;
-import java.util.Map;
 import java.util.Optional;
 
 /**
@@ -55,49 +53,7 @@ abstract class AbstractSNMPProcessor extends AbstractProcessor {
         LogFactory.setLogFactory(new SLF4JLogFactory());
     }
 
-    private static final String SHA_2_ALGORITHM = "Provides authentication based on the HMAC-SHA-2 algorithm.";
     private static final String NO_SUCH_OBJECT = "noSuchObject";
-    // SNMP versions
-    public static final AllowableValue SNMP_V1 = new AllowableValue("SNMPv1", "v1", "SNMP version 1");
-    public static final AllowableValue SNMP_V2C = new AllowableValue("SNMPv2c", "v2c", "SNMP version 2c");
-    public static final AllowableValue SNMP_V3 = new AllowableValue("SNMPv3", "v3", "SNMP version 3 with improved security");
-
-    // SNMPv3 security levels
-    public static final AllowableValue NO_AUTH_NO_PRIV = new AllowableValue("noAuthNoPriv", "noAuthNoPriv",
-            "No authentication or encryption.");
-    public static final AllowableValue AUTH_NO_PRIV = new AllowableValue("authNoPriv", "authNoPriv",
-            "Authentication without encryption.");
-    public static final AllowableValue AUTH_PRIV = new AllowableValue("authPriv", "authPriv",
-            "Authentication and encryption.");
-
-    // SNMPv3 authentication protocols
-    public static final AllowableValue MD5 = new AllowableValue("MD5", "MD5",
-            "Provides authentication based on the HMAC-MD5 algorithm.");
-    public static final AllowableValue SHA = new AllowableValue("SHA", "SHA",
-            "Provides authentication based on the HMAC-SHA algorithm.");
-    public static final AllowableValue HMAC128SHA224 = new AllowableValue("HMAC128SHA224", "SHA224",
-            SHA_2_ALGORITHM);
-    public static final AllowableValue HMAC192SHA256 = new AllowableValue("HMAC192SHA256", "SHA256",
-            SHA_2_ALGORITHM);
-    public static final AllowableValue HMAC256SHA384 = new AllowableValue("HMAC256SHA384", "SHA384",
-            SHA_2_ALGORITHM);
-    public static final AllowableValue HMAC384SHA512 = new AllowableValue("HMAC384SHA512", "SHA512",
-            SHA_2_ALGORITHM);
-
-    // SNMPv3 encryption
-    public static final AllowableValue DES = new AllowableValue("DES", "DES",
-            "Symmetric-key algorithm for the encryption of digital data. DES has been considered insecure" +
-                    "because of the feasilibity of brute-force attacks. We recommend using the AES encryption protocol.");
-    public static final AllowableValue DES3 = new AllowableValue("3DES", "3DES",
-            "Symmetric-key block cipher, which applies the DES cipher algorithm three times to each data block." +
-                    " 3DES has been considered insecure has been deprecated by NIST in 2017. We recommend using the AES encryption protocol.");
-
-    private static final String AES_DESCRIPTION = "AES is a symmetric algorithm which uses the same 128, 192, or 256 bit" +
-            " key for both encryption and decryption (the security of an AES system increases exponentially with key length).";
-
-    public static final AllowableValue AES128 = new AllowableValue("AES128", "AES128", AES_DESCRIPTION);
-    public static final AllowableValue AES192 = new AllowableValue("AES192", "AES192", AES_DESCRIPTION);
-    public static final AllowableValue AES256 = new AllowableValue("AES256", "AES256", AES_DESCRIPTION);
 
     public static final PropertyDescriptor AGENT_HOST = new PropertyDescriptor.Builder()
             .name("snmp-hostname")
@@ -117,136 +73,31 @@ abstract class AbstractSNMPProcessor extends AbstractProcessor {
             .addValidator(StandardValidators.PORT_VALIDATOR)
             .build();
 
-    public static final PropertyDescriptor SNMP_VERSION = new PropertyDescriptor.Builder()
-            .name("snmp-version")
-            .displayName("SNMP Version")
-            .description("Three significant versions of SNMP have been developed and deployed. " +
-                    "SNMPv1 is the original version of the protocol. More recent versions, " +
-                    "SNMPv2c and SNMPv3, feature improvements in performance, flexibility and security.")
-            .required(true)
-            .allowableValues(SNMP_V1, SNMP_V2C, SNMP_V3)
-            .defaultValue(SNMP_V1.getValue())
-            .build();
-
-    public static final PropertyDescriptor SNMP_COMMUNITY = new PropertyDescriptor.Builder()
-            .name("snmp-community")
-            .displayName("SNMP Community")
-            .description("SNMPv1 and SNMPv2 use communities to establish trust between managers and agents." +
-                    " Most agents support three community names, one each for read-only, read-write and trap." +
-                    " These three community strings control different types of activities. The read-only community" +
-                    " applies to get requests. The read-write community string applies to set requests. The trap" +
-                    " community string applies to receipt of traps.")
-            .required(true)
-            .sensitive(true)
-            .defaultValue("public")
-            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
-            .dependsOn(SNMP_VERSION, SNMP_V1, SNMP_V2C)
-            .build();
-
-    public static final PropertyDescriptor SNMP_SECURITY_LEVEL = new PropertyDescriptor.Builder()
-            .name("snmp-security-level")
-            .displayName("SNMP Security Level")
-            .description("SNMP version 3 provides extra security with User Based Security Model (USM). The three levels of security is " +
-                    "1. Communication without authentication and encryption (NoAuthNoPriv). " +
-                    "2. Communication with authentication and without encryption (AuthNoPriv). " +
-                    "3. Communication with authentication and encryption (AuthPriv).")
-            .required(true)
-            .allowableValues(NO_AUTH_NO_PRIV, AUTH_NO_PRIV, AUTH_PRIV)
-            .defaultValue(NO_AUTH_NO_PRIV.getValue())
-            .dependsOn(SNMP_VERSION, SNMP_V3)
-            .build();
-
-    public static final PropertyDescriptor SNMP_SECURITY_NAME = new PropertyDescriptor.Builder()
-            .name("snmp-security-name")
-            .displayName("SNMP Security Name")
-            .description("User name used for SNMP v3 Authentication.")
-            .required(true)
-            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
-            .dependsOn(SNMP_VERSION, SNMP_V3)
-            .build();
-
-    public static final PropertyDescriptor SNMP_AUTH_PROTOCOL = new PropertyDescriptor.Builder()
-            .name("snmp-authentication-protocol")
-            .displayName("SNMP Authentication Protocol")
-            .description("Hash based authentication protocol for secure authentication.")
-            .required(true)
-            .allowableValues(MD5, SHA, HMAC128SHA224, HMAC192SHA256, HMAC256SHA384, HMAC384SHA512)
-            .dependsOn(SNMP_SECURITY_LEVEL, AUTH_NO_PRIV, AUTH_PRIV)
-            .build();
-
-    public static final PropertyDescriptor SNMP_AUTH_PASSWORD = new PropertyDescriptor.Builder()
-            .name("snmp-authentication-passphrase")
-            .displayName("SNMP Authentication Passphrase")
-            .description("Passphrase used for SNMP authentication protocol.")
-            .required(true)
-            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
-            .sensitive(true)
-            .dependsOn(SNMP_SECURITY_LEVEL, AUTH_NO_PRIV, AUTH_PRIV)
-            .build();
-
-    public static final PropertyDescriptor SNMP_PRIVACY_PROTOCOL = new PropertyDescriptor.Builder()
-            .name("snmp-private-protocol")
-            .displayName("SNMP Privacy Protocol")
-            .description("Privacy allows for encryption of SNMP v3 messages to ensure confidentiality of data.")
-            .required(true)
-            .allowableValues(DES, DES3, AES128, AES192, AES256)
-            .dependsOn(SNMP_SECURITY_LEVEL, AUTH_PRIV)
-            .build();
-
-    public static final PropertyDescriptor SNMP_PRIVACY_PASSWORD = new PropertyDescriptor.Builder()
-            .name("snmp-private-protocol-passphrase")
-            .displayName("SNMP Privacy Passphrase")
-            .description("Passphrase used for SNMP privacy protocol.")
-            .required(true)
-            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
-            .sensitive(true)
-            .dependsOn(SNMP_SECURITY_LEVEL, AUTH_PRIV)
-            .build();
-
-    public static final PropertyDescriptor SNMP_RETRIES = new PropertyDescriptor.Builder()
-            .name("snmp-retries")
-            .displayName("Number of Retries")
-            .description("Set the number of retries when requesting the SNMP Agent.")
-            .required(false)
-            .defaultValue("0")
-            .addValidator(StandardValidators.NON_NEGATIVE_INTEGER_VALIDATOR)
-            .build();
-
-    public static final PropertyDescriptor SNMP_TIMEOUT = new PropertyDescriptor.Builder()
-            .name("snmp-timeout")
-            .displayName("Timeout (ms)")
-            .description("Set the timeout (in milliseconds) when requesting the SNMP Agent.")
-            .required(false)
-            .defaultValue("5000")
-            .addValidator(StandardValidators.NON_NEGATIVE_INTEGER_VALIDATOR)
-            .build();
-
-
-    protected volatile SNMPRequestHandler snmpRequestHandler;
+    protected volatile SNMPResourceHandler snmpResourceHandler;
 
     @OnScheduled
-    public void initSnmpManager(final ProcessContext context) throws InitializationException {
-        final int version = SNMPUtils.getVersion(context.getProperty(SNMP_VERSION).getValue());
+    public void initSnmpManager(final ProcessContext context) {
+        final int version = SNMPUtils.getVersion(context.getProperty(BasicProperties.SNMP_VERSION).getValue());
         final SNMPConfiguration configuration;
-        try {
-            configuration = new SNMPConfigurationBuilder()
-                    .setAgentHost(context.getProperty(AGENT_HOST).getValue())
-                    .setAgentPort(context.getProperty(AGENT_PORT).toString())
-                    .setRetries(context.getProperty(SNMP_RETRIES).asInteger())
-                    .setTimeout(context.getProperty(SNMP_TIMEOUT).asInteger())
-                    .setVersion(version)
-                    .setAuthProtocol(context.getProperty(SNMP_AUTH_PROTOCOL).getValue())
-                    .setAuthPassphrase(context.getProperty(SNMP_AUTH_PASSWORD).getValue())
-                    .setPrivacyProtocol(context.getProperty(SNMP_PRIVACY_PROTOCOL).getValue())
-                    .setPrivacyPassphrase(context.getProperty(SNMP_PRIVACY_PASSWORD).getValue())
-                    .setSecurityName(context.getProperty(SNMP_SECURITY_NAME).getValue())
-                    .setSecurityLevel(context.getProperty(SNMP_SECURITY_LEVEL).getValue())
-                    .setCommunityString(context.getProperty(SNMP_COMMUNITY).getValue())
-                    .build();
-        } catch (IllegalStateException e) {
-            throw new InitializationException(e);
-        }
-        snmpRequestHandler = SNMPRequestHandlerFactory.createStandardRequestHandler(configuration);
+        final String targetHost = getTargetHost(context);
+        final String targetPort = getTargetPort(context);
+
+        configuration = SNMPConfiguration.builder()
+                .setTargetHost(targetHost)
+                .setTargetPort(targetPort)
+                .setRetries(context.getProperty(BasicProperties.SNMP_RETRIES).asInteger())
+                .setTimeoutInMs(context.getProperty(BasicProperties.SNMP_TIMEOUT).asInteger())
+                .setVersion(version)
+                .setAuthProtocol(context.getProperty(V3SecurityProperties.SNMP_AUTH_PROTOCOL).getValue())
+                .setAuthPassphrase(context.getProperty(V3SecurityProperties.SNMP_AUTH_PASSWORD).getValue())
+                .setPrivacyProtocol(context.getProperty(V3SecurityProperties.SNMP_PRIVACY_PROTOCOL).getValue())
+                .setPrivacyPassphrase(context.getProperty(V3SecurityProperties.SNMP_PRIVACY_PASSWORD).getValue())
+                .setSecurityName(context.getProperty(V3SecurityProperties.SNMP_SECURITY_NAME).getValue())
+                .setSecurityLevel(context.getProperty(V3SecurityProperties.SNMP_SECURITY_LEVEL).getValue())
+                .setCommunityString(context.getProperty(BasicProperties.SNMP_COMMUNITY).getValue())
+                .build();
+
+        snmpResourceHandler = SNMPFactoryProvider.getFactory(version).createSNMPResourceHandler(configuration);
     }
 
     /**
@@ -254,61 +105,61 @@ abstract class AbstractSNMPProcessor extends AbstractProcessor {
      */
     @OnStopped
     public void close() {
-        if (snmpRequestHandler != null) {
-            snmpRequestHandler.close();
-            snmpRequestHandler = null;
+        if (snmpResourceHandler != null) {
+            snmpResourceHandler.close();
+            snmpResourceHandler = null;
         }
     }
 
-    /**
-     * Method to add attribute in flow file
-     *
-     * @param key            attribute key
-     * @param value          attribute value
-     * @param flowFile       flow file to update
-     * @param processSession session
-     * @return updated flow file
-     */
-    protected FlowFile addAttribute(final String key, final String value, FlowFile flowFile, final ProcessSession processSession) {
-        final Map<String, String> attributes = new HashMap<>();
-        attributes.put(key, value);
-        flowFile = processSession.putAllAttributes(flowFile, attributes);
-        return flowFile;
+    protected void handleResponse(final ProcessContext context, final ProcessSession processSession, final FlowFile flowFile, final SNMPSingleResponse response,
+                                  final Relationship success, final Relationship failure, final String provenanceAddress) {
+        final SNMPResponseStatus snmpResponseStatus = processResponse(response);
+        processSession.putAllAttributes(flowFile, response.getAttributes());
+        if (snmpResponseStatus.getErrorStatus() == ErrorStatus.FAILURE) {
+            getLogger().error("SNMP request failed, response error: " + snmpResponseStatus.getErrorMessage());
+            processSession.getProvenanceReporter().modifyAttributes(flowFile, response.getTargetAddress() + provenanceAddress);
+            processSession.transfer(flowFile, failure);
+            context.yield();
+        } else {
+            processSession.getProvenanceReporter().modifyAttributes(flowFile, response.getTargetAddress() + provenanceAddress);
+            processSession.transfer(flowFile, success);
+        }
     }
 
-    protected void processResponse(final ProcessSession processSession, FlowFile flowFile, final SNMPSingleResponse response,
-                                   final String provenanceAddress, final Relationship success) {
+    protected SNMPResponseStatus processResponse(final SNMPSingleResponse response) {
         if (response.isValid()) {
             if (response.isReportPdu()) {
                 final String oid = response.getVariableBindings().get(0).getOid();
                 final Optional<String> reportPduErrorMessage = SNMPUtils.getErrorMessage(oid);
                 if (!reportPduErrorMessage.isPresent()) {
-                    throw new SNMPException(String.format("SNMP request failed, Report-PDU returned, but no error message found. " +
-                            "Please, check the OID %s in an online OID repository.", oid));
+                    return new SNMPResponseStatus(String.format("Report-PDU returned, but no error message found. " +
+                            "Please, check the OID %s in an online OID repository.", oid), ErrorStatus.FAILURE);
                 }
-                throw new SNMPException("SNMPRequest failed, Report-PDU returned. " + reportPduErrorMessage.get());
+                return new SNMPResponseStatus("Report-PDU returned. " + reportPduErrorMessage.get(), ErrorStatus.FAILURE);
             }
-            checkV2cV3VariableBindings(response);
-            flowFile = processSession.putAllAttributes(flowFile, response.getAttributes());
-            processSession.transfer(flowFile, success);
-            processSession.getProvenanceReporter().receive(flowFile, provenanceAddress);
+            return checkV2cV3VariableBindings(response);
         } else {
-            final String error = response.getErrorStatusText();
-            throw new SNMPException("SNMP request failed, response error: " + error);
+            final String errorMessage = response.getErrorStatusText();
+            return new SNMPResponseStatus(errorMessage, ErrorStatus.FAILURE);
         }
     }
 
-    private void checkV2cV3VariableBindings(SNMPSingleResponse response) {
-        if (response.getVersion() != SnmpConstants.version1) {
+    private SNMPResponseStatus checkV2cV3VariableBindings(SNMPSingleResponse response) {
+        if (response.getVersion() == SnmpConstants.version2c || response.getVersion() == SnmpConstants.version3) {
             final Optional<SNMPValue> firstVariableBinding = response.getVariableBindings().stream().findFirst();
             if (firstVariableBinding.isPresent()) {
                 final String value = firstVariableBinding.get().getVariable();
                 if (NO_SUCH_OBJECT.equals(value)) {
-                    throw new SNMPException("SNMP Request failed, OID not found.");
+                    return new SNMPResponseStatus("OID not found.", ErrorStatus.FAILURE);
                 }
             } else {
-                throw new SNMPException("Empty SNMP response: no variable binding found.");
+                return new SNMPResponseStatus("Empty SNMP response: no variable binding found.", ErrorStatus.FAILURE);
             }
         }
+        return new SNMPResponseStatus("Successful SNMP Response", ErrorStatus.SUCCESS);
     }
+
+    protected abstract String getTargetHost(ProcessContext processContext);
+
+    protected abstract String getTargetPort(ProcessContext processContext);
 }
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/GetSNMP.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/GetSNMP.java
index 58a08bd..2aa29ab 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/GetSNMP.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/GetSNMP.java
@@ -22,6 +22,7 @@ import org.apache.nifi.annotation.behavior.WritesAttribute;
 import org.apache.nifi.annotation.behavior.WritesAttributes;
 import org.apache.nifi.annotation.documentation.CapabilityDescription;
 import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
 import org.apache.nifi.components.AllowableValue;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.flowfile.FlowFile;
@@ -31,8 +32,10 @@ import org.apache.nifi.processor.Relationship;
 import org.apache.nifi.processor.util.StandardValidators;
 import org.apache.nifi.snmp.dto.SNMPSingleResponse;
 import org.apache.nifi.snmp.dto.SNMPTreeResponse;
-import org.apache.nifi.snmp.exception.SNMPException;
 import org.apache.nifi.snmp.exception.SNMPWalkException;
+import org.apache.nifi.snmp.operations.GetSNMPHandler;
+import org.apache.nifi.snmp.processors.properties.BasicProperties;
+import org.apache.nifi.snmp.processors.properties.V3SecurityProperties;
 import org.apache.nifi.snmp.utils.SNMPUtils;
 import org.apache.nifi.snmp.validators.OIDValidator;
 
@@ -41,16 +44,20 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 /**
- * Retrieving data from configured SNMP agent which, upon each invocation of
- * {@link #onTrigger(ProcessContext, ProcessSession)} method, will construct a
- * {@link FlowFile} containing in its properties the information retrieved.
+ * Performs an SNMP Get operation based on processor or incoming FlowFile attributes.
+ * Upon each invocation of {@link #onTrigger(ProcessContext, ProcessSession)}
+ * method, in case of a valid incoming FlowFile, it will inspect the attributes of
+ * the FlowFile and look for attributes with name formatted as "snmp$OID" to set the
+ * attribute value to this OID.
  * The output {@link FlowFile} won't have any content.
  */
 @Tags({"snmp", "get", "oid", "walk"})
-@InputRequirement(Requirement.INPUT_FORBIDDEN)
+@InputRequirement(Requirement.INPUT_ALLOWED)
 @CapabilityDescription("Retrieves information from SNMP Agent with SNMP Get request and outputs a FlowFile with information" +
         " in attributes and without any content")
 @WritesAttributes({
@@ -74,11 +81,12 @@ public class GetSNMP extends AbstractSNMPProcessor {
             "A manager-to-agent request to retrieve the value of multiple variables. Snmp WALK also traverses all subnodes " +
                     "under the specified OID.");
 
-    // OID to request (if walk, it is the root ID of the request).
     public static final PropertyDescriptor OID = new PropertyDescriptor.Builder()
             .name("snmp-oid")
             .displayName("OID")
-            .description("Each OID (object identifier) identifies a variable that can be read or set via SNMP.")
+            .description("Each OID (object identifier) identifies a variable that can be read or set via SNMP." +
+                    " This value is not taken into account for an input flowfile and will be omitted. Can be set to empty" +
+                    "string when the OIDs are provided through flowfile.")
             .required(true)
             .addValidator(new OIDValidator())
             .build();
@@ -95,7 +103,8 @@ public class GetSNMP extends AbstractSNMPProcessor {
     public static final PropertyDescriptor TEXTUAL_OID = new PropertyDescriptor.Builder()
             .name("snmp-textual-oid")
             .displayName("Textual OID")
-            .description("The textual OID to request.")
+            .description("The textual form of the numeric OID to request. This property is user defined, not processed and appended to " +
+                    "the outgoing flowfile.")
             .required(false)
             .addValidator(StandardValidators.NON_BLANK_VALIDATOR)
             .defaultValue(null)
@@ -114,16 +123,16 @@ public class GetSNMP extends AbstractSNMPProcessor {
     protected static final List<PropertyDescriptor> PROPERTY_DESCRIPTORS = Collections.unmodifiableList(Arrays.asList(
             AGENT_HOST,
             AGENT_PORT,
-            SNMP_VERSION,
-            SNMP_COMMUNITY,
-            SNMP_SECURITY_LEVEL,
-            SNMP_SECURITY_NAME,
-            SNMP_AUTH_PROTOCOL,
-            SNMP_AUTH_PASSWORD,
-            SNMP_PRIVACY_PROTOCOL,
-            SNMP_PRIVACY_PASSWORD,
-            SNMP_RETRIES,
-            SNMP_TIMEOUT,
+            BasicProperties.SNMP_VERSION,
+            BasicProperties.SNMP_COMMUNITY,
+            V3SecurityProperties.SNMP_SECURITY_LEVEL,
+            V3SecurityProperties.SNMP_SECURITY_NAME,
+            V3SecurityProperties.SNMP_AUTH_PROTOCOL,
+            V3SecurityProperties.SNMP_AUTH_PASSWORD,
+            V3SecurityProperties.SNMP_PRIVACY_PROTOCOL,
+            V3SecurityProperties.SNMP_PRIVACY_PASSWORD,
+            BasicProperties.SNMP_RETRIES,
+            BasicProperties.SNMP_TIMEOUT,
             OID,
             TEXTUAL_OID,
             SNMP_STRATEGY
@@ -134,54 +143,100 @@ public class GetSNMP extends AbstractSNMPProcessor {
             REL_FAILURE
     )));
 
+    private volatile GetSNMPHandler snmpHandler;
+
+    @OnScheduled
+    public void init(final ProcessContext context) {
+        initSnmpManager(context);
+        snmpHandler = new GetSNMPHandler(snmpResourceHandler);
+    }
+
     @Override
     public void onTrigger(final ProcessContext context, final ProcessSession processSession) {
         final SNMPStrategy snmpStrategy = SNMPStrategy.valueOf(context.getProperty(SNMP_STRATEGY).getValue());
         final String oid = context.getProperty(OID).getValue();
+        final FlowFile flowfile = processSession.get();
 
         if (SNMPStrategy.GET == snmpStrategy) {
-            performSnmpGet(context, processSession, oid);
+            performSnmpGet(context, processSession, oid, flowfile);
         } else if (SNMPStrategy.WALK == snmpStrategy) {
-            performSnmpWalk(context, processSession, oid);
+            performSnmpWalk(context, processSession, oid, flowfile);
         }
     }
 
-    private void performSnmpWalk(final ProcessContext context, final ProcessSession processSession, final String oid) {
+    void performSnmpWalk(final ProcessContext context, final ProcessSession processSession, final String oid,
+                         final FlowFile flowFile) {
         try {
-            final SNMPTreeResponse response = snmpRequestHandler.walk(oid);
-            response.logErrors(getLogger());
-            FlowFile flowFile = createFlowFileWithTreeEventProperties(response, processSession);
-            processSession.getProvenanceReporter().receive(flowFile, response.getTargetAddress() + "/" + oid);
-            processSession.transfer(flowFile, REL_SUCCESS);
+            if (flowFile != null) {
+                performSnmpWalkWithFlowFile(processSession, flowFile);
+            } else {
+                performSnmpWalkWithoutFlowFile(processSession, oid);
+            }
         } catch (SNMPWalkException e) {
             getLogger().error(e.getMessage());
             context.yield();
-            processSession.rollback();
         }
     }
 
-    private void performSnmpGet(final ProcessContext context, final ProcessSession processSession, final String oid) {
-        final SNMPSingleResponse response;
+    private void performSnmpWalkWithFlowFile(ProcessSession processSession, FlowFile flowFile) {
+        final Optional<SNMPTreeResponse> optionalResponse = snmpHandler.walk(flowFile.getAttributes());
+        if (optionalResponse.isPresent()) {
+            final SNMPTreeResponse response = optionalResponse.get();
+            response.logErrors(getLogger());
+            processSession.putAllAttributes(flowFile, response.getAttributes());
+            processSession.getProvenanceReporter().modifyAttributes(flowFile, response.getTargetAddress() + "/walk");
+            processSession.transfer(flowFile, response.isError() ? REL_FAILURE : REL_SUCCESS);
+        } else {
+            getLogger().warn("No SNMP specific attributes found in flowfile.");
+            processSession.getProvenanceReporter().receive(flowFile, "/walk");
+            processSession.transfer(flowFile, REL_FAILURE);
+        }
+    }
+
+    private void performSnmpWalkWithoutFlowFile(ProcessSession processSession, String oid) {
+        final SNMPTreeResponse response = snmpHandler.walk(oid);
+        response.logErrors(getLogger());
+        final FlowFile outgoingFlowFile = processSession.create();
+        processSession.putAllAttributes(outgoingFlowFile, response.getAttributes());
+        processSession.getProvenanceReporter().create(outgoingFlowFile, response.getTargetAddress() + "/walk");
+        processSession.transfer(outgoingFlowFile, REL_SUCCESS);
+    }
+
+    void performSnmpGet(final ProcessContext context, final ProcessSession processSession, final String oid,
+                        final FlowFile flowFile) {
+        final String textualOidKey = SNMPUtils.SNMP_PROP_PREFIX + "textualOid";
+        final Map<String, String> textualOidMap = Collections.singletonMap(textualOidKey, context.getProperty(TEXTUAL_OID).getValue());
         try {
-            response = snmpRequestHandler.get(oid);
-            final FlowFile flowFile = processSession.create();
-            addAttribute(SNMPUtils.SNMP_PROP_PREFIX + "textualOid", context.getProperty(TEXTUAL_OID).getValue(), flowFile, processSession);
-            final String provenanceAddress = response.getTargetAddress() + "/" + oid;
-            processResponse(processSession, flowFile, response, provenanceAddress, REL_SUCCESS);
-        } catch (SNMPException e) {
-            getLogger().error(e.getMessage());
-            context.yield();
-            processSession.rollback();
+            if (flowFile != null) {
+                performSnmpGetWithFlowFile(context, processSession, flowFile, textualOidMap);
+            } else {
+                performSnmpGetWithoutFlowFile(context, processSession, oid, textualOidMap);
+            }
         } catch (IOException e) {
-            getLogger().error("Failed to send request to the agent. Check if the agent supports the used version.");
+            getLogger().error("Failed to send request to the agent. Check if the agent supports the used version.", e);
             context.yield();
-            processSession.rollback();
         }
     }
 
-    @Override
-    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
-        return PROPERTY_DESCRIPTORS;
+    private void performSnmpGetWithoutFlowFile(ProcessContext context, ProcessSession processSession, String oid, Map<String, String> textualOidMap) throws IOException {
+        final SNMPSingleResponse response = snmpHandler.get(oid);
+        final FlowFile outgoingFlowFile = processSession.create();
+        processSession.putAllAttributes(outgoingFlowFile, textualOidMap);
+        processSession.getProvenanceReporter().receive(outgoingFlowFile, response.getTargetAddress() + "/get");
+        handleResponse(context, processSession, outgoingFlowFile, response, REL_SUCCESS, REL_FAILURE, "/get");
+    }
+
+    private void performSnmpGetWithFlowFile(ProcessContext context, ProcessSession processSession, FlowFile flowFile, Map<String, String> textualOidMap) throws IOException {
+        final Optional<SNMPSingleResponse> optionalResponse = snmpHandler.get(flowFile.getAttributes());
+        if (optionalResponse.isPresent()) {
+            final SNMPSingleResponse response = optionalResponse.get();
+            processSession.putAllAttributes(flowFile, textualOidMap);
+            handleResponse(context, processSession, flowFile, response, REL_SUCCESS, REL_FAILURE, "/get");
+        } else {
+            getLogger().warn("No SNMP specific attributes found in flowfile.");
+            processSession.transfer(flowFile, REL_FAILURE);
+            context.yield();
+        }
     }
 
     @Override
@@ -189,10 +244,18 @@ public class GetSNMP extends AbstractSNMPProcessor {
         return RELATIONSHIPS;
     }
 
-    private FlowFile createFlowFileWithTreeEventProperties(final SNMPTreeResponse response, final ProcessSession processSession) {
-        FlowFile flowFile = processSession.create();
-        flowFile = processSession.putAllAttributes(flowFile, response.getAttributes());
-        return flowFile;
+    @Override
+    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return PROPERTY_DESCRIPTORS;
+    }
+
+    protected String getTargetHost(ProcessContext processContext) {
+        return processContext.getProperty(AGENT_HOST).getValue();
+    }
+
+    @Override
+    protected String getTargetPort(ProcessContext processContext) {
+        return processContext.getProperty(AGENT_PORT).getValue();
     }
 
     private enum SNMPStrategy {
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/ListenTrapSNMP.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/ListenTrapSNMP.java
new file mode 100644
index 0000000..b36ff7d
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/ListenTrapSNMP.java
@@ -0,0 +1,143 @@
+/*
+ * 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.snmp.processors;
+
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.RequiresInstanceClassLoading;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.annotation.lifecycle.OnStopped;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractSessionFactoryProcessor;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSessionFactory;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.snmp.configuration.SNMPConfiguration;
+import org.apache.nifi.snmp.operations.SNMPTrapReceiverHandler;
+import org.apache.nifi.snmp.processors.properties.BasicProperties;
+import org.apache.nifi.snmp.processors.properties.V3SecurityProperties;
+import org.apache.nifi.snmp.utils.SNMPUtils;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Receiving data from a configured SNMP agent which, upon each invocation of
+ * {@link #onTrigger(ProcessContext, ProcessSessionFactory)} method, will construct a
+ * {@link FlowFile} containing in its properties the information retrieved.
+ * The output {@link FlowFile} won't have any content.
+ */
+@Tags({"snmp", "listen", "trap"})
+@InputRequirement(InputRequirement.Requirement.INPUT_FORBIDDEN)
+@CapabilityDescription("Receives information from SNMP Agent and outputs a FlowFile with information in attributes and without any content")
+@WritesAttribute(attribute = SNMPUtils.SNMP_PROP_PREFIX + "*", description = "Attributes retrieved from the SNMP response. It may include:"
+        + " snmp$errorIndex, snmp$errorStatus, snmp$errorStatusText, snmp$nonRepeaters, snmp$requestID, snmp$type, snmp$variableBindings")
+@RequiresInstanceClassLoading
+public class ListenTrapSNMP extends AbstractSessionFactoryProcessor {
+
+    public static final PropertyDescriptor SNMP_MANAGER_PORT = new PropertyDescriptor.Builder()
+            .name("snmp-manager-port")
+            .displayName("SNMP Manager Port")
+            .description("The port where the SNMP Manager listens to the incoming traps.")
+            .required(true)
+            .defaultValue("0")
+            .addValidator(StandardValidators.PORT_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor SNMP_USM_USERS_FILE_PATH = new PropertyDescriptor.Builder()
+            .name("snmp-usm-users-file-path")
+            .displayName("SNMP Users File Path")
+            .description("The path of the json file containing the user credentials for SNMPv3. Check Usage for more details.")
+            .required(true)
+            .defaultValue("")
+            .dependsOn(BasicProperties.SNMP_VERSION, BasicProperties.SNMP_V3)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final Relationship REL_SUCCESS = new Relationship.Builder()
+            .name("success")
+            .description("All FlowFiles that are received from the SNMP agent are routed to this relationship")
+            .build();
+
+    public static final Relationship REL_FAILURE = new Relationship.Builder()
+            .name("failure")
+            .description("All FlowFiles that cannot received from the SNMP agent are routed to this relationship")
+            .build();
+
+    protected static final List<PropertyDescriptor> PROPERTY_DESCRIPTORS = Collections.unmodifiableList(Arrays.asList(
+            SNMP_MANAGER_PORT,
+            BasicProperties.SNMP_VERSION,
+            BasicProperties.SNMP_COMMUNITY,
+            V3SecurityProperties.SNMP_SECURITY_LEVEL,
+            SNMP_USM_USERS_FILE_PATH
+    ));
+
+    private static final Set<Relationship> RELATIONSHIPS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+            REL_SUCCESS,
+            REL_FAILURE
+    )));
+
+    private volatile SNMPTrapReceiverHandler snmpTrapReceiverHandler;
+
+    @OnScheduled
+    public void initSnmpManager(ProcessContext context) {
+        final int version = SNMPUtils.getVersion(context.getProperty(BasicProperties.SNMP_VERSION).getValue());
+        final int managerPort = context.getProperty(SNMP_MANAGER_PORT).asInteger();
+        final String usmUsersFilePath = context.getProperty(SNMP_USM_USERS_FILE_PATH).getValue();
+        SNMPConfiguration configuration;
+
+        configuration = SNMPConfiguration.builder()
+                .setManagerPort(managerPort)
+                .setVersion(version)
+                .setSecurityLevel(context.getProperty(V3SecurityProperties.SNMP_SECURITY_LEVEL).getValue())
+                .setCommunityString(context.getProperty(BasicProperties.SNMP_COMMUNITY).getValue())
+                .build();
+
+        snmpTrapReceiverHandler = new SNMPTrapReceiverHandler(configuration, usmUsersFilePath);
+    }
+
+    @Override
+    public void onTrigger(final ProcessContext context, final ProcessSessionFactory processSessionFactory) {
+        if (!snmpTrapReceiverHandler.isStarted()) {
+            snmpTrapReceiverHandler.createTrapReceiver(processSessionFactory, getLogger());
+        }
+    }
+
+    @OnStopped
+    public void close() {
+        if (snmpTrapReceiverHandler != null) {
+            snmpTrapReceiverHandler.close();
+        }
+    }
+
+    @Override
+    public Set<Relationship> getRelationships() {
+        return RELATIONSHIPS;
+    }
+
+    @Override
+    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return PROPERTY_DESCRIPTORS;
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/SendTrapSNMP.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/SendTrapSNMP.java
new file mode 100644
index 0000000..04b06e6
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/SendTrapSNMP.java
@@ -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.
+ */
+package org.apache.nifi.snmp.processors;
+
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.snmp.configuration.V1TrapConfiguration;
+import org.apache.nifi.snmp.configuration.V2TrapConfiguration;
+import org.apache.nifi.snmp.operations.SendTrapSNMPHandler;
+import org.apache.nifi.snmp.processors.properties.BasicProperties;
+import org.apache.nifi.snmp.processors.properties.V1TrapProperties;
+import org.apache.nifi.snmp.processors.properties.V2TrapProperties;
+import org.apache.nifi.snmp.processors.properties.V3SecurityProperties;
+import org.apache.nifi.snmp.utils.SNMPUtils;
+import org.snmp4j.mp.SnmpConstants;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Sends data to an SNMP manager which, upon each invocation of
+ * {@link #onTrigger(ProcessContext, ProcessSession)} method, will construct a
+ * {@link FlowFile} containing in its properties the information retrieved.
+ * The output {@link FlowFile} won't have any content.
+ */
+@Tags({"snmp", "send", "trap"})
+@InputRequirement(Requirement.INPUT_ALLOWED)
+@CapabilityDescription("Sends information to SNMP Manager.")
+public class SendTrapSNMP extends AbstractSNMPProcessor {
+
+    public static final PropertyDescriptor SNMP_MANAGER_HOST = new PropertyDescriptor.Builder()
+            .name("snmp-trap-manager-host")
+            .displayName("SNMP Manager Host")
+            .description("The host where the SNMP Manager sends the trap.")
+            .required(true)
+            .defaultValue("localhost")
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor SNMP_MANAGER_PORT = new PropertyDescriptor.Builder()
+            .name("snmp-trap-manager-port")
+            .displayName("SNMP Manager Port")
+            .description("The port where the SNMP Manager listens to the incoming traps.")
+            .required(true)
+            .defaultValue("0")
+            .addValidator(StandardValidators.PORT_VALIDATOR)
+            .build();
+
+    public static final Relationship REL_SUCCESS = new Relationship.Builder()
+            .name("success")
+            .description("All FlowFiles that have been successfully used to perform SNMP Set are routed to this relationship")
+            .build();
+
+    public static final Relationship REL_FAILURE = new Relationship.Builder()
+            .name("failure")
+            .description("All FlowFiles that cannot received from the SNMP agent are routed to this relationship")
+            .build();
+
+    protected static final List<PropertyDescriptor> PROPERTY_DESCRIPTORS = Collections.unmodifiableList(Arrays.asList(
+            SNMP_MANAGER_HOST,
+            SNMP_MANAGER_PORT,
+            BasicProperties.SNMP_VERSION,
+            BasicProperties.SNMP_COMMUNITY,
+            V3SecurityProperties.SNMP_SECURITY_LEVEL,
+            V3SecurityProperties.SNMP_SECURITY_NAME,
+            V3SecurityProperties.SNMP_AUTH_PROTOCOL,
+            V3SecurityProperties.SNMP_AUTH_PASSWORD,
+            V3SecurityProperties.SNMP_PRIVACY_PROTOCOL,
+            V3SecurityProperties.SNMP_PRIVACY_PASSWORD,
+            BasicProperties.SNMP_RETRIES,
+            BasicProperties.SNMP_TIMEOUT,
+            V1TrapProperties.ENTERPRISE_OID,
+            V1TrapProperties.AGENT_ADDRESS,
+            V1TrapProperties.GENERIC_TRAP_TYPE,
+            V1TrapProperties.SPECIFIC_TRAP_TYPE,
+            V2TrapProperties.TRAP_OID_VALUE
+    ));
+
+    private static final Set<Relationship> RELATIONSHIPS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+            REL_SUCCESS, REL_FAILURE
+    )));
+
+    private volatile SendTrapSNMPHandler snmpHandler;
+
+    @OnScheduled
+    public void init(ProcessContext context) {
+        Instant startTime = Instant.now();
+        initSnmpManager(context);
+        snmpHandler = new SendTrapSNMPHandler(snmpResourceHandler, startTime, getLogger());
+    }
+
+    @Override
+    public void onTrigger(final ProcessContext context, final ProcessSession processSession) {
+        final FlowFile flowFile = processSession.get();
+        if (flowFile != null) {
+            try {
+                final int snmpVersion = SNMPUtils.getVersion(context.getProperty(BasicProperties.SNMP_VERSION).getValue());
+                if (SnmpConstants.version1 == snmpVersion) {
+                    V1TrapConfiguration v1TrapConfiguration = V1TrapConfiguration.builder()
+                            .enterpriseOid(context.getProperty(V1TrapProperties.ENTERPRISE_OID).evaluateAttributeExpressions(flowFile).getValue())
+                            .agentAddress(context.getProperty(V1TrapProperties.AGENT_ADDRESS).evaluateAttributeExpressions(flowFile).getValue())
+                            .genericTrapType(context.getProperty(V1TrapProperties.GENERIC_TRAP_TYPE).evaluateAttributeExpressions(flowFile).getValue())
+                            .specificTrapType(context.getProperty(V1TrapProperties.SPECIFIC_TRAP_TYPE).evaluateAttributeExpressions(flowFile).getValue())
+                            .build();
+                    snmpHandler.sendTrap(flowFile.getAttributes(), v1TrapConfiguration);
+                } else {
+                    V2TrapConfiguration v2TrapConfiguration = new V2TrapConfiguration(
+                            context.getProperty(V2TrapProperties.TRAP_OID_VALUE).evaluateAttributeExpressions(flowFile).getValue()
+                    );
+                    snmpHandler.sendTrap(flowFile.getAttributes(), v2TrapConfiguration);
+                }
+                processSession.transfer(flowFile, REL_SUCCESS);
+            } catch (IOException e) {
+                getLogger().error("Failed to send request to the agent. Check if the agent supports the used version.", e);
+                processSession.transfer(processSession.penalize(flowFile), REL_FAILURE);
+            } catch (IllegalArgumentException e) {
+                getLogger().error("Invalid trap configuration.", e);
+                processSession.transfer(processSession.penalize(flowFile), REL_FAILURE);
+            }
+        }
+    }
+
+    @Override
+    public Set<Relationship> getRelationships() {
+        return RELATIONSHIPS;
+    }
+
+    @Override
+    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return PROPERTY_DESCRIPTORS;
+    }
+
+    @Override
+    protected String getTargetHost(ProcessContext processContext) {
+        return processContext.getProperty(SNMP_MANAGER_HOST).getValue();
+    }
+
+    @Override
+    protected String getTargetPort(ProcessContext processContext) {
+        return processContext.getProperty(SNMP_MANAGER_PORT).getValue();
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/SetSNMP.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/SetSNMP.java
index ab5ca72..74d18a0 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/SetSNMP.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/SetSNMP.java
@@ -22,13 +22,16 @@ import org.apache.nifi.annotation.behavior.WritesAttribute;
 import org.apache.nifi.annotation.behavior.WritesAttributes;
 import org.apache.nifi.annotation.documentation.CapabilityDescription;
 import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.flowfile.FlowFile;
 import org.apache.nifi.processor.ProcessContext;
 import org.apache.nifi.processor.ProcessSession;
 import org.apache.nifi.processor.Relationship;
 import org.apache.nifi.snmp.dto.SNMPSingleResponse;
-import org.apache.nifi.snmp.exception.SNMPException;
+import org.apache.nifi.snmp.operations.SetSNMPHandler;
+import org.apache.nifi.snmp.processors.properties.BasicProperties;
+import org.apache.nifi.snmp.processors.properties.V3SecurityProperties;
 import org.apache.nifi.snmp.utils.SNMPUtils;
 
 import java.io.IOException;
@@ -36,19 +39,21 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 
 /**
- * Performs a SNMP Set operation based on attributes of incoming FlowFile.
+ * Performs an SNMP Set operation based on attributes of incoming FlowFile.
  * Upon each invocation of {@link #onTrigger(ProcessContext, ProcessSession)}
  * method, it will inspect attributes of FlowFile and look for attributes with
  * name formatted as "snmp$OID" to set the attribute value to this OID.
+ * The output {@link FlowFile} won't have any content.
  */
 @Tags({"snmp", "set", "oid"})
 @InputRequirement(Requirement.INPUT_REQUIRED)
 @CapabilityDescription("Based on incoming FlowFile attributes, the processor will execute SNMP Set requests." +
         " When finding attributes with the name snmp$<OID>, the processor will attempt to set the value of" +
-        " the attribute to the corresponding OID given in the attribute name")
+        " the attribute to the corresponding OID given in the attribute name.")
 @WritesAttributes({
         @WritesAttribute(attribute = SNMPUtils.SNMP_PROP_PREFIX + "<OID>", description = "Response variable binding: OID (e.g. 1.3.6.1.4.1.343) and its value."),
         @WritesAttribute(attribute = SNMPUtils.SNMP_PROP_PREFIX + "errorIndex", description = "Denotes the variable binding in which the error occured."),
@@ -74,16 +79,16 @@ public class SetSNMP extends AbstractSNMPProcessor {
     private static final List<PropertyDescriptor> PROPERTY_DESCRIPTORS = Collections.unmodifiableList(Arrays.asList(
             AGENT_HOST,
             AGENT_PORT,
-            SNMP_VERSION,
-            SNMP_COMMUNITY,
-            SNMP_SECURITY_LEVEL,
-            SNMP_SECURITY_NAME,
-            SNMP_AUTH_PROTOCOL,
-            SNMP_AUTH_PASSWORD,
-            SNMP_PRIVACY_PROTOCOL,
-            SNMP_PRIVACY_PASSWORD,
-            SNMP_RETRIES,
-            SNMP_TIMEOUT
+            BasicProperties.SNMP_VERSION,
+            BasicProperties.SNMP_COMMUNITY,
+            V3SecurityProperties.SNMP_SECURITY_LEVEL,
+            V3SecurityProperties.SNMP_SECURITY_NAME,
+            V3SecurityProperties.SNMP_AUTH_PROTOCOL,
+            V3SecurityProperties.SNMP_AUTH_PASSWORD,
+            V3SecurityProperties.SNMP_PRIVACY_PROTOCOL,
+            V3SecurityProperties.SNMP_PRIVACY_PASSWORD,
+            BasicProperties.SNMP_RETRIES,
+            BasicProperties.SNMP_TIMEOUT
     ));
 
     private static final Set<Relationship> RELATIONSHIPS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
@@ -91,19 +96,34 @@ public class SetSNMP extends AbstractSNMPProcessor {
             REL_FAILURE
     )));
 
+    private volatile SetSNMPHandler snmpHandler;
+
+    @OnScheduled
+    public void init(final ProcessContext context) {
+        initSnmpManager(context);
+        snmpHandler = new SetSNMPHandler(snmpResourceHandler);
+    }
+
     @Override
     public void onTrigger(final ProcessContext context, final ProcessSession processSession) {
         final FlowFile flowFile = processSession.get();
         if (flowFile != null) {
             try {
-                final SNMPSingleResponse response = snmpRequestHandler.set(flowFile);
-                processResponse(processSession, flowFile, response, response.getTargetAddress(), REL_SUCCESS);
-            } catch (SNMPException e) {
-                getLogger().error(e.getMessage());
-                processError(context, processSession, flowFile);
+                final Optional<SNMPSingleResponse> optionalResponse = snmpHandler.set(flowFile.getAttributes());
+                if (optionalResponse.isPresent()) {
+                    processSession.remove(flowFile);
+                    final FlowFile outgoingFlowFile = processSession.create();
+                    final SNMPSingleResponse response = optionalResponse.get();
+                    processSession.getProvenanceReporter().receive(outgoingFlowFile, "/set");
+                    handleResponse(context, processSession, outgoingFlowFile, response, REL_SUCCESS, REL_FAILURE, "/set");
+                } else {
+                    getLogger().warn("No SNMP specific attributes found in flowfile.");
+                    processSession.transfer(flowFile, REL_FAILURE);
+                }
             } catch (IOException e) {
                 getLogger().error("Failed to send request to the agent. Check if the agent supports the used version.");
-                processError(context, processSession, flowFile);
+                processSession.transfer(processSession.penalize(flowFile), REL_FAILURE);
+                context.yield();
             }
         }
     }
@@ -118,9 +138,13 @@ public class SetSNMP extends AbstractSNMPProcessor {
         return RELATIONSHIPS;
     }
 
+    @Override
+    protected String getTargetHost(ProcessContext processContext) {
+        return processContext.getProperty(AGENT_HOST).getValue();
+    }
 
-    private void processError(final ProcessContext context, final ProcessSession processSession, final FlowFile flowFile) {
-        processSession.transfer(processSession.penalize(flowFile), REL_FAILURE);
-        context.yield();
+    @Override
+    protected String getTargetPort(ProcessContext processContext) {
+        return processContext.getProperty(AGENT_PORT).getValue();
     }
 }
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/properties/BasicProperties.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/properties/BasicProperties.java
new file mode 100644
index 0000000..ccc6677
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/properties/BasicProperties.java
@@ -0,0 +1,77 @@
+/*
+ * 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.snmp.processors.properties;
+
+import org.apache.nifi.components.AllowableValue;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.processor.util.StandardValidators;
+
+public class BasicProperties {
+
+    private BasicProperties() {
+        // Utility class, not needed to instantiate.
+    }
+
+    public static final AllowableValue SNMP_V1 = new AllowableValue("SNMPv1", "v1", "SNMP version 1");
+    public static final AllowableValue SNMP_V2C = new AllowableValue("SNMPv2c", "v2c", "SNMP version 2c");
+    public static final AllowableValue SNMP_V3 = new AllowableValue("SNMPv3", "v3", "SNMP version 3 with improved security");
+
+
+    public static final PropertyDescriptor SNMP_VERSION = new PropertyDescriptor.Builder()
+            .name("snmp-version")
+            .displayName("SNMP Version")
+            .description("Three significant versions of SNMP have been developed and deployed. " +
+                    "SNMPv1 is the original version of the protocol. More recent versions, " +
+                    "SNMPv2c and SNMPv3, feature improvements in performance, flexibility and security.")
+            .required(true)
+            .allowableValues(SNMP_V1, SNMP_V2C, SNMP_V3)
+            .defaultValue(SNMP_V1.getValue())
+            .build();
+
+    public static final PropertyDescriptor SNMP_COMMUNITY = new PropertyDescriptor.Builder()
+            .name("snmp-community")
+            .displayName("SNMP Community")
+            .description("SNMPv1 and SNMPv2 use communities to establish trust between managers and agents." +
+                    " Most agents support three community names, one each for read-only, read-write and trap." +
+                    " These three community strings control different types of activities. The read-only community" +
+                    " applies to get requests. The read-write community string applies to set requests. The trap" +
+                    " community string applies to receipt of traps.")
+            .required(true)
+            .sensitive(true)
+            .defaultValue("public")
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .dependsOn(SNMP_VERSION, SNMP_V1, SNMP_V2C)
+            .build();
+
+    public static final PropertyDescriptor SNMP_RETRIES = new PropertyDescriptor.Builder()
+            .name("snmp-retries")
+            .displayName("Number of Retries")
+            .description("Set the number of retries when requesting the SNMP Agent.")
+            .required(false)
+            .defaultValue("0")
+            .addValidator(StandardValidators.NON_NEGATIVE_INTEGER_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor SNMP_TIMEOUT = new PropertyDescriptor.Builder()
+            .name("snmp-timeout")
+            .displayName("Timeout (ms)")
+            .description("Set the timeout in ms when requesting the SNMP Agent.")
+            .required(false)
+            .defaultValue("5000")
+            .addValidator(StandardValidators.POSITIVE_INTEGER_VALIDATOR)
+            .build();
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/properties/V1TrapProperties.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/properties/V1TrapProperties.java
new file mode 100644
index 0000000..bcdf122
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/properties/V1TrapProperties.java
@@ -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.
+ */
+package org.apache.nifi.snmp.processors.properties;
+
+import org.apache.nifi.components.AllowableValue;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.expression.ExpressionLanguageScope;
+import org.apache.nifi.processor.util.StandardValidators;
+
+import static org.apache.nifi.snmp.processors.properties.BasicProperties.SNMP_V1;
+import static org.apache.nifi.snmp.processors.properties.BasicProperties.SNMP_VERSION;
+
+
+public class V1TrapProperties {
+
+    private V1TrapProperties() {
+        // Utility class, not needed to instantiate.
+    }
+
+    private static final AllowableValue GENERIC_TRAP_TYPE_ENTERPRISE_SPECIFIC = new AllowableValue("6", "Enterprise Specific",
+            "An enterpriseSpecific trap signifies that a particular enterprise-specific trap has occurred which " +
+                    "can be defined in the Specific Trap Type field.");
+
+    public static final PropertyDescriptor ENTERPRISE_OID = new PropertyDescriptor.Builder()
+            .name("snmp-trap-enterprise-oid")
+            .displayName("Enterprise OID")
+            .description("Enterprise is the vendor identification (OID) for the network management sub-system that generated the trap.")
+            .required(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .dependsOn(SNMP_VERSION, SNMP_V1)
+            .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)
+            .build();
+
+    public static final PropertyDescriptor AGENT_ADDRESS = new PropertyDescriptor.Builder()
+            .name("snmp-trap-agent-address")
+            .displayName("SNMP Trap Agent Address")
+            .description("The address where the SNMP Manager sends the trap.")
+            .required(true)
+            .defaultValue("0")
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .dependsOn(SNMP_VERSION, SNMP_V1)
+            .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)
+            .build();
+
+    public static final PropertyDescriptor GENERIC_TRAP_TYPE = new PropertyDescriptor.Builder()
+            .name("snmp-trap-generic-type")
+            .displayName("Generic Trap Type")
+            .description("Generic trap type is an integer in the range of 0 to 6. See processor usage for details.")
+            .required(false)
+            .addValidator(StandardValidators.createLongValidator(0, 6, true))
+            .dependsOn(SNMP_VERSION, SNMP_V1)
+            .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)
+            .build();
+
+    public static final PropertyDescriptor SPECIFIC_TRAP_TYPE = new PropertyDescriptor.Builder()
+            .name("snmp-trap-specific-type")
+            .displayName("Specific Trap Type")
+            .description("Specific trap type is a number that further specifies the nature of the event that generated " +
+                    "the trap in the case of traps of generic type 6 (enterpriseSpecific). The interpretation of this " +
+                    "code is vendor-specific.")
+            .required(true)
+            .addValidator(StandardValidators.POSITIVE_INTEGER_VALIDATOR)
+            .dependsOn(SNMP_VERSION, SNMP_V1)
+            .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)
+            .build();
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/properties/V2TrapProperties.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/properties/V2TrapProperties.java
new file mode 100644
index 0000000..1866bb5
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/properties/V2TrapProperties.java
@@ -0,0 +1,42 @@
+/*
+ * 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.snmp.processors.properties;
+
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.expression.ExpressionLanguageScope;
+import org.apache.nifi.processor.util.StandardValidators;
+
+import static org.apache.nifi.snmp.processors.properties.BasicProperties.SNMP_V2C;
+import static org.apache.nifi.snmp.processors.properties.BasicProperties.SNMP_V3;
+import static org.apache.nifi.snmp.processors.properties.BasicProperties.SNMP_VERSION;
+
+public class V2TrapProperties {
+
+    private V2TrapProperties() {
+        // Utility class, not needed to instantiate.
+    }
+
+    public static final PropertyDescriptor TRAP_OID_VALUE = new PropertyDescriptor.Builder()
+            .name("snmp-trap-oid-value")
+            .displayName("Trap OID Value")
+            .description("The value of the trap OID.")
+            .required(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .dependsOn(SNMP_VERSION, SNMP_V2C, SNMP_V3)
+            .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)
+            .build();
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/properties/V3SecurityProperties.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/properties/V3SecurityProperties.java
new file mode 100644
index 0000000..07688d7
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/processors/properties/V3SecurityProperties.java
@@ -0,0 +1,130 @@
+/*
+ * 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.snmp.processors.properties;
+
+import org.apache.nifi.components.AllowableValue;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.snmp4j.security.SecurityLevel;
+
+import static org.apache.nifi.snmp.processors.properties.BasicProperties.SNMP_V3;
+import static org.apache.nifi.snmp.processors.properties.BasicProperties.SNMP_VERSION;
+
+public class V3SecurityProperties {
+
+    private V3SecurityProperties() {
+        // Utility class, not needed to instantiate.
+    }
+
+    private static final String SHA_2_ALGORITHM = "Provides authentication based on the HMAC-SHA-2 algorithm.";
+    private static final String AES_DESCRIPTION = "AES is a symmetric algorithm which uses the same 128, 192, or 256 bit" +
+            " key for both encryption and decryption (the security of an AES system increases exponentially with key length).";
+
+    // SNMPv3 security levels
+    public static final AllowableValue NO_AUTH_NO_PRIV = new AllowableValue(SecurityLevel.noAuthNoPriv.name(), SecurityLevel.noAuthNoPriv.name(),
+            "No authentication or encryption.");
+    public static final AllowableValue AUTH_NO_PRIV = new AllowableValue(SecurityLevel.authNoPriv.name(), SecurityLevel.authNoPriv.name(),
+            "Authentication without encryption.");
+    public static final AllowableValue AUTH_PRIV = new AllowableValue(SecurityLevel.authPriv.name(), SecurityLevel.authPriv.name(),
+            "Authentication and encryption.");
+
+    // SNMPv3 authentication protocols
+    public static final AllowableValue MD5 = new AllowableValue("MD5", "MD5",
+            "Provides authentication based on the HMAC-MD5 algorithm.");
+    public static final AllowableValue SHA = new AllowableValue("SHA", "SHA",
+            "Provides authentication based on the HMAC-SHA algorithm.");
+    public static final AllowableValue HMAC128SHA224 = new AllowableValue("HMAC128SHA224", "SHA224",
+            SHA_2_ALGORITHM);
+    public static final AllowableValue HMAC192SHA256 = new AllowableValue("HMAC192SHA256", "SHA256",
+            SHA_2_ALGORITHM);
+    public static final AllowableValue HMAC256SHA384 = new AllowableValue("HMAC256SHA384", "SHA384",
+            SHA_2_ALGORITHM);
+    public static final AllowableValue HMAC384SHA512 = new AllowableValue("HMAC384SHA512", "SHA512",
+            SHA_2_ALGORITHM);
+
+    // SNMPv3 encryption
+    public static final AllowableValue DES = new AllowableValue("DES", "DES",
+            "Symmetric-key algorithm for the encryption of digital data. DES has been considered insecure" +
+                    "because of the feasilibity of brute-force attacks. We recommend using the AES encryption protocol.");
+    public static final AllowableValue DES3 = new AllowableValue("3DES", "3DES",
+            "Symmetric-key block cipher, which applies the DES cipher algorithm three times to each data block." +
+                    " 3DES has been considered insecure has been deprecated by NIST in 2017. We recommend using the AES encryption protocol.");
+
+    public static final AllowableValue AES128 = new AllowableValue("AES128", "AES128", AES_DESCRIPTION);
+    public static final AllowableValue AES192 = new AllowableValue("AES192", "AES192", AES_DESCRIPTION);
+    public static final AllowableValue AES256 = new AllowableValue("AES256", "AES256", AES_DESCRIPTION);
+
+    public static final PropertyDescriptor SNMP_SECURITY_LEVEL = new PropertyDescriptor.Builder()
+            .name("snmp-security-level")
+            .displayName("SNMP Security Level")
+            .description("SNMP version 3 provides extra security with User Based Security Model (USM). The three levels of security is " +
+                    "1. Communication without authentication and encryption (NoAuthNoPriv). " +
+                    "2. Communication with authentication and without encryption (AuthNoPriv). " +
+                    "3. Communication with authentication and encryption (AuthPriv).")
+            .required(true)
+            .allowableValues(NO_AUTH_NO_PRIV, AUTH_NO_PRIV, AUTH_PRIV)
+            .defaultValue(NO_AUTH_NO_PRIV.getValue())
+            .dependsOn(SNMP_VERSION, SNMP_V3)
+            .build();
+
+    public static final PropertyDescriptor SNMP_SECURITY_NAME = new PropertyDescriptor.Builder()
+            .name("snmp-security-name")
+            .displayName("SNMP Security Name")
+            .description("User name used for SNMP v3 Authentication.")
+            .required(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .dependsOn(SNMP_VERSION, SNMP_V3)
+            .build();
+
+    public static final PropertyDescriptor SNMP_AUTH_PROTOCOL = new PropertyDescriptor.Builder()
+            .name("snmp-authentication-protocol")
+            .displayName("SNMP Authentication Protocol")
+            .description("Hash based authentication protocol for secure authentication.")
+            .required(true)
+            .allowableValues(MD5, SHA, HMAC128SHA224, HMAC192SHA256, HMAC256SHA384, HMAC384SHA512)
+            .dependsOn(SNMP_SECURITY_LEVEL, AUTH_NO_PRIV, AUTH_PRIV)
+            .build();
+
+    public static final PropertyDescriptor SNMP_AUTH_PASSWORD = new PropertyDescriptor.Builder()
+            .name("snmp-authentication-passphrase")
+            .displayName("SNMP Authentication Passphrase")
+            .description("Passphrase used for SNMP authentication protocol.")
+            .required(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .sensitive(true)
+            .dependsOn(SNMP_SECURITY_LEVEL, AUTH_NO_PRIV, AUTH_PRIV)
+            .build();
+
+    public static final PropertyDescriptor SNMP_PRIVACY_PROTOCOL = new PropertyDescriptor.Builder()
+            .name("snmp-private-protocol")
+            .displayName("SNMP Privacy Protocol")
+            .description("Privacy allows for encryption of SNMP v3 messages to ensure confidentiality of data.")
+            .required(true)
+            .allowableValues(DES, DES3, AES128, AES192, AES256)
+            .dependsOn(SNMP_SECURITY_LEVEL, AUTH_PRIV)
+            .build();
+
+    public static final PropertyDescriptor SNMP_PRIVACY_PASSWORD = new PropertyDescriptor.Builder()
+            .name("snmp-private-protocol-passphrase")
+            .displayName("SNMP Privacy Passphrase")
+            .description("Passphrase used for SNMP privacy protocol.")
+            .required(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .sensitive(true)
+            .dependsOn(SNMP_SECURITY_LEVEL, AUTH_PRIV)
+            .build();
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/utils/SNMPUtils.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/utils/SNMPUtils.java
index 788b73b..a14e195 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/utils/SNMPUtils.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/utils/SNMPUtils.java
@@ -16,13 +16,13 @@
  */
 package org.apache.nifi.snmp.utils;
 
-import org.apache.nifi.flowfile.FlowFile;
 import org.apache.nifi.snmp.exception.InvalidAuthProtocolException;
 import org.apache.nifi.snmp.exception.InvalidPrivProtocolException;
 import org.apache.nifi.snmp.exception.InvalidSnmpVersionException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.snmp4j.PDU;
+import org.snmp4j.PDUv1;
 import org.snmp4j.mp.SnmpConstants;
 import org.snmp4j.security.AuthHMAC128SHA224;
 import org.snmp4j.security.AuthHMAC192SHA256;
@@ -44,6 +44,7 @@ import org.snmp4j.smi.OctetString;
 import org.snmp4j.smi.Variable;
 import org.snmp4j.smi.VariableBinding;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -67,8 +68,8 @@ public final class SNMPUtils {
 
     private static final Map<String, OID> AUTH_MAP;
     private static final Map<String, OID> PRIV_MAP;
-    private static final Map<String, Integer> VERSION_MAP;
     private static final Map<String, String> REPORT_MAP;
+    private static final Map<String, Integer> VERSION_MAP;
 
     private SNMPUtils() {
         // hide implicit constructor
@@ -132,12 +133,18 @@ public final class SNMPUtils {
         return attributes;
     }
 
-    /**
-     * Method to construct {@link FlowFile} attributes from a vector of {@link VariableBinding}
-     *
-     * @param variableBindings list of {@link VariableBinding}
-     * @return the attributes map
-     */
+    public static Map<String, String> getV1TrapPduAttributeMap(final PDUv1 v1TrapPdu) {
+        final Map<String, String> trapAttributes = getPduAttributeMap(v1TrapPdu);
+
+        trapAttributes.computeIfAbsent(SNMP_PROP_PREFIX + "enterprise", v -> String.valueOf(v1TrapPdu.getEnterprise()));
+        trapAttributes.computeIfAbsent(SNMP_PROP_PREFIX + "agentAddress", v -> String.valueOf(v1TrapPdu.getAgentAddress()));
+        trapAttributes.computeIfAbsent(SNMP_PROP_PREFIX + "genericTrapType", v -> String.valueOf(v1TrapPdu.getGenericTrap()));
+        trapAttributes.computeIfAbsent(SNMP_PROP_PREFIX + "specificTrapType", v -> String.valueOf(v1TrapPdu.getSpecificTrap()));
+        trapAttributes.computeIfAbsent(SNMP_PROP_PREFIX + "timestamp", v -> String.valueOf(v1TrapPdu.getTimestamp()));
+
+        return trapAttributes;
+    }
+
     public static Map<String, String> createWalkOidValuesMap(final List<VariableBinding> variableBindings) {
         final Map<String, String> attributes = new HashMap<>();
         variableBindings.forEach(vb -> addAttributeFromVariable(vb, attributes));
@@ -159,46 +166,72 @@ public final class SNMPUtils {
         throw new InvalidAuthProtocolException("Invalid authentication protocol provided.");
     }
 
-    public static int getVersion(final String snmpVersion) {
-        return Optional.ofNullable(VERSION_MAP.get(snmpVersion))
-                .orElseThrow(() -> new InvalidSnmpVersionException("Invalid SNMP version provided."));
-    }
-
-    /**
-     * Method to construct {@link VariableBinding} based on {@link FlowFile}
-     * attributes in order to update the {@link PDU} that is going to be sent to
-     * the SNMP Agent.
-     *
-     * @param pdu        {@link PDU} to be sent
-     * @param attributes {@link FlowFile} attributes
-     * @return true if at least one {@link VariableBinding} has been created, false otherwise
-     */
     public static boolean addVariables(final PDU pdu, final Map<String, String> attributes) {
         boolean result = false;
-        for (Map.Entry<String, String> attributeEntry : attributes.entrySet()) {
-            if (attributeEntry.getKey().startsWith(SNMPUtils.SNMP_PROP_PREFIX)) {
-                final String[] splits = attributeEntry.getKey().split("\\" + SNMPUtils.SNMP_PROP_DELIMITER);
-                final String snmpPropName = splits[1];
-                final String snmpPropValue = attributeEntry.getValue();
-                if (SNMPUtils.OID_PATTERN.matcher(snmpPropName).matches()) {
-                    final Optional<Variable> var;
-                    if (splits.length == 2) { // no SMI syntax defined
-                        var = Optional.of(new OctetString(snmpPropValue));
-                    } else {
-                        final int smiSyntax = Integer.parseInt(splits[2]);
-                        var = SNMPUtils.stringToVariable(snmpPropValue, smiSyntax);
-                    }
-                    if (var.isPresent()) {
-                        final VariableBinding varBind = new VariableBinding(new OID(snmpPropName), var.get());
-                        pdu.add(varBind);
-                        result = true;
+        try {
+            for (Map.Entry<String, String> attributeEntry : attributes.entrySet()) {
+                if (attributeEntry.getKey().startsWith(SNMPUtils.SNMP_PROP_PREFIX)) {
+                    final String[] splits = attributeEntry.getKey().split("\\" + SNMPUtils.SNMP_PROP_DELIMITER);
+                    final String snmpPropName = splits[1];
+                    final String snmpPropValue = attributeEntry.getValue();
+                    if (SNMPUtils.OID_PATTERN.matcher(snmpPropName).matches()) {
+                        final Optional<Variable> var;
+                        if (splits.length == 2) { // no SMI syntax defined
+                            var = Optional.of(new OctetString(snmpPropValue));
+                        } else {
+                            final int smiSyntax = Integer.parseInt(splits[2]);
+                            var = SNMPUtils.stringToVariable(snmpPropValue, smiSyntax);
+                        }
+                        if (var.isPresent()) {
+                            final VariableBinding varBind = new VariableBinding(new OID(snmpPropName), var.get());
+                            pdu.add(varBind);
+                            result = true;
+                        }
                     }
                 }
             }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return false;
         }
         return result;
     }
 
+    public static VariableBinding[] addGetVariables(final Map<String, String> attributes) {
+        List<VariableBinding> variableBindings = new ArrayList<>();
+        try {
+            for (Map.Entry<String, String> attributeEntry : attributes.entrySet()) {
+                if (attributeEntry.getKey().startsWith(SNMPUtils.SNMP_PROP_PREFIX)) {
+                    final String[] splits = attributeEntry.getKey().split("\\" + SNMPUtils.SNMP_PROP_DELIMITER);
+                    final String snmpPropName = splits[1];
+                    if (SNMPUtils.OID_PATTERN.matcher(snmpPropName).matches()) {
+                        variableBindings.add(new VariableBinding(new OID(snmpPropName)));
+                    }
+                }
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return new VariableBinding[0];
+        }
+        return variableBindings.toArray(new VariableBinding[0]);
+    }
+
+    public static OID[] addWalkVariables(final Map<String, String> attributes) {
+        List<OID> oids = new ArrayList<>();
+        try {
+            for (Map.Entry<String, String> attributeEntry : attributes.entrySet()) {
+                if (attributeEntry.getKey().startsWith(SNMPUtils.SNMP_PROP_PREFIX)) {
+                    final String[] splits = attributeEntry.getKey().split("\\" + SNMPUtils.SNMP_PROP_DELIMITER);
+                    final String snmpPropName = splits[1];
+                    if (SNMPUtils.OID_PATTERN.matcher(snmpPropName).matches()) {
+                        oids.add(new OID(snmpPropName));
+                    }
+                }
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return new OID[0];
+        }
+        return oids.toArray(new OID[0]);
+    }
+
     private static void addAttributeFromVariable(final VariableBinding variableBinding, final Map<String, String> attributes) {
         attributes.put(SNMP_PROP_PREFIX + variableBinding.getOid() + SNMP_PROP_DELIMITER + variableBinding.getVariable().getSyntax(), variableBinding.getVariable().toString());
     }
@@ -231,4 +264,9 @@ public final class SNMPUtils {
         }
         return errorMessage.map(s -> oid + ": " + s);
     }
+
+    public static int getVersion(final String snmpVersion) {
+        return Optional.ofNullable(VERSION_MAP.get(snmpVersion))
+                .orElseThrow(() -> new InvalidSnmpVersionException("Invalid SNMP version provided."));
+    }
 }
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
index c3fcde0..0359271 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
@@ -14,3 +14,5 @@
 # limitations under the License.
 org.apache.nifi.snmp.processors.GetSNMP
 org.apache.nifi.snmp.processors.SetSNMP
+org.apache.nifi.snmp.processors.SendTrapSNMP
+org.apache.nifi.snmp.processors.ListenTrapSNMP
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/docs/org.apache.nifi.snmp.processors.GetSNMP/additionalDetails.html b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/docs/org.apache.nifi.snmp.processors.GetSNMP/additionalDetails.html
index 24f982c..1607987 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/docs/org.apache.nifi.snmp.processors.GetSNMP/additionalDetails.html
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/docs/org.apache.nifi.snmp.processors.GetSNMP/additionalDetails.html
@@ -23,12 +23,19 @@
 <body>
 <h2>Summary</h2>
 <p>
-    This processor polls a SNMP agent to get information for a given OID (Strategy = GET) or for all the sub-tree
-    associated to a given OID
+    This processor polls a SNMP agent to get information for a given OID or OIDs (Strategy = GET) or for all the sub-tree
+    associated to a given OID or OIDs
     (Strategy = WALK). This processors supports SNMPv1, SNMPv2c and SNMPv3. The component is based on <a
         href="http://www.snmp4j.org/">SNMP4J</a>.
 </p>
 <p>
+    The processor can compile the SNMP Get PDU from the attributes of an input flowfile (multiple OIDs can be specified)
+    or from a single OID specified in the processor property. In the former case, the processor will only consider the
+    OIDs specified in the flowfile.
+    The processor is looking for attributes prefixed with <i>snmp$</i>. If such an attribute is found, the attribute name is split using
+    the $ character. The second element must respect the OID format to be considered as a valid OID. The flowfile attribute
+    value can be empty (it will be later filled with the retrieved value and written into the outgoing flowfile).
+
     When the processor is triggered, it sends the SNMP request and gets the information associated to request OID(s).
     Once response is received
     from the SNMP agent, a FlowFile is constructed. The FlowFile content is empty, all the information is written in the
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/docs/org.apache.nifi.snmp.processors.ListenTrapSNMP/additionalDetails.html b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/docs/org.apache.nifi.snmp.processors.ListenTrapSNMP/additionalDetails.html
new file mode 100644
index 0000000..37aaa66
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/docs/org.apache.nifi.snmp.processors.ListenTrapSNMP/additionalDetails.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html lang="en">
+<!--
+  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.
+-->
+<head>
+    <meta charset="utf-8"/>
+    <title>ListenTrapSNMP</title>
+    <link rel="stylesheet" href="../../../../../css/component-usage.css" type="text/css"/>
+</head>
+
+<body>
+<h2>Summary</h2>
+<p>
+    This processor listens to SNMP traps and creates a flowfile from the trap PDU.
+    The versions SNMPv1, SNMPv2c and SNMPv3 are supproted. The component is based on <a href="http://www.snmp4j.org/">SNMP4J</a>.
+</p>
+<p>
+    In case of SNMPv3, users can be specified in a json file. E.g.:
+<pre>
+<code>
+[
+   {
+      "securityName":"user1",
+      "authProtocol":"MD5",
+      "authPassphrase":"abc12345",
+      "privProtocol":"DES",
+      "privPassphrase":"abc12345"
+   },
+   {
+      "securityName":"newUser2",
+      "authProtocol":"MD5",
+      "authPassphrase":"abc12345",
+      "privProtocol":"AES256",
+      "privPassphrase":"abc12345"
+   }
+]
+
+</code>
+</pre>
+</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/docs/org.apache.nifi.snmp.processors.SendTrapSNMP/additionalDetails.html b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/docs/org.apache.nifi.snmp.processors.SendTrapSNMP/additionalDetails.html
new file mode 100644
index 0000000..ef9ed95
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/resources/docs/org.apache.nifi.snmp.processors.SendTrapSNMP/additionalDetails.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+<!--
+  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.
+-->
+<head>
+    <meta charset="utf-8"/>
+    <title>SendTrapSNMP</title>
+    <link rel="stylesheet" href="../../../../../css/component-usage.css" type="text/css"/>
+</head>
+
+<body>
+<h2>Summary</h2>
+<p>
+    This processor generates and transmits SNMP Traps to the specified SNMP manager. Attributes can be given
+    as processor properties, either predefined or dynamically using Expression Language from flowfiles.
+    Flowfile properties with snmp prefix (e.g. snmp$1.2.3.4.5 - OID) value can be used to define additional PDU variables.
+</p>
+<p>
+    The allowable Generic Trap Types are:
+    <ol start="0">
+        <li>Cold Start</li>
+        <li>Warm Start</li>
+        <li>Link Down</li>
+        <li>Link Up</li>
+        <li>Authentication Failure</li>
+        <li>EGP Neighbor Loss</li>
+        <li>Enterprise Specific</li>
+    </ol>
+
+    Specific trap type can set in case of Enterprise Specific generic trap type is chosen.
+</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/configuration/SNMPConfigurationTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/configuration/SNMPConfigurationTest.java
new file mode 100644
index 0000000..e5cd4d3
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/configuration/SNMPConfigurationTest.java
@@ -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.
+ */
+package org.apache.nifi.snmp.configuration;
+
+import org.apache.nifi.remote.io.socket.NetworkUtils;
+import org.junit.Test;
+import org.snmp4j.mp.SnmpConstants;
+
+import static org.apache.nifi.snmp.helper.configurations.SNMPConfigurationFactory.COMMUNITY_STRING;
+import static org.apache.nifi.snmp.helper.configurations.SNMPConfigurationFactory.DEFAULT_HOST;
+import static org.apache.nifi.snmp.helper.configurations.SNMPV3ConfigurationFactory.AUTH_PASSPHRASE;
+import static org.apache.nifi.snmp.helper.configurations.SNMPV3ConfigurationFactory.AUTH_PROTOCOL;
+import static org.apache.nifi.snmp.helper.configurations.SNMPV3ConfigurationFactory.PRIV_PASSPHRASE;
+import static org.apache.nifi.snmp.helper.configurations.SNMPV3ConfigurationFactory.PRIV_PROTOCOL;
+import static org.apache.nifi.snmp.helper.configurations.SNMPV3ConfigurationFactory.SECURITY_LEVEL;
+import static org.apache.nifi.snmp.helper.configurations.SNMPV3ConfigurationFactory.SECURITY_NAME;
+import static org.junit.Assert.assertEquals;
+
+public class SNMPConfigurationTest {
+
+    private static final int MANAGER_PORT = NetworkUtils.availablePort();
+    private static final String TARGET_PORT = "55556";
+    private static final int RETRIES = 3;
+    private static final int VERSION = SnmpConstants.version3;
+    private static final int TIMEOUT_IN_MS = 3000;
+
+    @Test
+    public void testMembersAreSetCorrectly() {
+        final SNMPConfiguration snmpConfiguration = SNMPConfiguration.builder()
+                .setManagerPort(MANAGER_PORT)
+                .setTargetHost(DEFAULT_HOST)
+                .setTargetPort(TARGET_PORT)
+                .setRetries(RETRIES)
+                .setTimeoutInMs(TIMEOUT_IN_MS)
+                .setVersion(VERSION)
+                .setCommunityString(COMMUNITY_STRING)
+                .setSecurityLevel(SECURITY_LEVEL)
+                .setSecurityName(SECURITY_NAME)
+                .setAuthProtocol(AUTH_PROTOCOL)
+                .setAuthPassphrase(AUTH_PASSPHRASE)
+                .setPrivacyProtocol(PRIV_PROTOCOL)
+                .setPrivacyPassphrase(PRIV_PASSPHRASE)
+                .build();
+
+        assertEquals(MANAGER_PORT, snmpConfiguration.getManagerPort());
+        assertEquals(DEFAULT_HOST, snmpConfiguration.getTargetHost());
+        assertEquals(TARGET_PORT, snmpConfiguration.getTargetPort());
+        assertEquals(RETRIES, snmpConfiguration.getRetries());
+        assertEquals(TIMEOUT_IN_MS, snmpConfiguration.getTimeoutInMs());
+        assertEquals(VERSION, snmpConfiguration.getVersion());
+        assertEquals(COMMUNITY_STRING, snmpConfiguration.getCommunityString());
+        assertEquals(SECURITY_LEVEL, snmpConfiguration.getSecurityLevel());
+        assertEquals(SECURITY_NAME, snmpConfiguration.getSecurityName());
+        assertEquals(AUTH_PROTOCOL, snmpConfiguration.getAuthProtocol());
+        assertEquals(AUTH_PASSPHRASE, snmpConfiguration.getAuthPassphrase());
+        assertEquals(PRIV_PROTOCOL, snmpConfiguration.getPrivacyProtocol());
+        assertEquals(PRIV_PASSPHRASE, snmpConfiguration.getPrivacyPassphrase());
+
+    }
+
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/configuration/V1TrapConfigurationTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/configuration/V1TrapConfigurationTest.java
new file mode 100644
index 0000000..1575113
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/configuration/V1TrapConfigurationTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.snmp.configuration;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+public class V1TrapConfigurationTest {
+
+    private static final String AGENT_ADDRESS = "127.0.0.1";
+    private static final String ENTERPRISE_OID = "1.3.6.1.4.1.8072.2.3.0.1";
+    private static final int GENERIC_TRAP_TYPE = 6;
+    private static final Integer SPECIFIC_TRAP_TYPE = 2;
+
+    @Rule
+    public ExpectedException exceptionRule = ExpectedException.none();
+
+    @Test
+    public void testMembersAreSetCorrectly() {
+        final V1TrapConfiguration v1TrapConfiguration = V1TrapConfiguration.builder()
+                .enterpriseOid(ENTERPRISE_OID)
+                .agentAddress(AGENT_ADDRESS)
+                .genericTrapType(String.valueOf(GENERIC_TRAP_TYPE))
+                .specificTrapType(String.valueOf(SPECIFIC_TRAP_TYPE))
+                .build();
+
+        assertEquals(ENTERPRISE_OID, v1TrapConfiguration.getEnterpriseOid());
+        assertEquals(AGENT_ADDRESS, v1TrapConfiguration.getAgentAddress());
+        assertEquals(GENERIC_TRAP_TYPE, v1TrapConfiguration.getGenericTrapType());
+        assertEquals(SPECIFIC_TRAP_TYPE, v1TrapConfiguration.getSpecificTrapType());
+    }
+
+    @Test
+    public void testRequireNonNullEnterpriseOid() {
+        exceptionRule.expect(IllegalArgumentException.class);
+        exceptionRule.expectMessage("Enterprise OID must be specified.");
+        V1TrapConfiguration.builder()
+                .agentAddress(AGENT_ADDRESS)
+                .genericTrapType(String.valueOf(GENERIC_TRAP_TYPE))
+                .specificTrapType(String.valueOf(SPECIFIC_TRAP_TYPE))
+                .build();
+    }
+
+    @Test
+    public void testRequireNonNullAgentAddress() {
+        exceptionRule.expect(IllegalArgumentException.class);
+        exceptionRule.expectMessage("Agent address must be specified.");
+        V1TrapConfiguration.builder()
+                .enterpriseOid(ENTERPRISE_OID)
+                .genericTrapType(String.valueOf(GENERIC_TRAP_TYPE))
+                .specificTrapType(String.valueOf(SPECIFIC_TRAP_TYPE))
+                .build();
+    }
+
+    @Test
+    public void testGenericTypeIsNegative() {
+        final IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> V1TrapConfiguration.builder()
+                .agentAddress(AGENT_ADDRESS)
+                .enterpriseOid(ENTERPRISE_OID)
+                .genericTrapType("-1")
+                .build()
+        );
+        assertEquals("Generic Trap Type must be between 0 and 6.", exception.getMessage());
+    }
+
+    @Test
+    public void testGenericTypeIsGreaterThan6() {
+        final IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> V1TrapConfiguration.builder()
+                .agentAddress(AGENT_ADDRESS)
+                .enterpriseOid(ENTERPRISE_OID)
+                .genericTrapType("7")
+                .build()
+        );
+        assertEquals("Generic Trap Type must be between 0 and 6.", exception.getMessage());
+    }
+
+    @Test
+    public void testGenericTypeIsNotANumber() {
+        final IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> V1TrapConfiguration.builder()
+                .agentAddress(AGENT_ADDRESS)
+                .enterpriseOid(ENTERPRISE_OID)
+                .genericTrapType("invalid")
+                .build()
+        );
+        assertEquals("Generic Trap Type is not a number.", exception.getMessage());
+    }
+
+    @Test
+    public void testSpecificTrapTypeIsNegative() {
+        final IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> V1TrapConfiguration.builder()
+                .agentAddress(AGENT_ADDRESS)
+                .enterpriseOid(ENTERPRISE_OID)
+                .genericTrapType(String.valueOf(GENERIC_TRAP_TYPE))
+                .specificTrapType("-1")
+                .build()
+        );
+        assertEquals("Specific Trap Type must be between 0 and 2147483647.", exception.getMessage());
+    }
+
+    @Test
+    public void testGenericTrapTypeIsEnterpriseSpecificButSpecificTrapTypeIsNotSet() {
+        final IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> V1TrapConfiguration.builder()
+                .agentAddress(AGENT_ADDRESS)
+                .enterpriseOid(ENTERPRISE_OID)
+                .genericTrapType(String.valueOf(GENERIC_TRAP_TYPE))
+                .build()
+        );
+        assertEquals("Generic Trap Type is [6 - Enterprise Specific] but Specific Trap Type is not provided or not a number.", exception.getMessage());
+    }
+
+    @Test
+    public void testGenericTrapTypeIsNotEnterpriseSpecificButSpecificTrapTypeIsSet() {
+        final IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> V1TrapConfiguration.builder()
+                .agentAddress(AGENT_ADDRESS)
+                .enterpriseOid(ENTERPRISE_OID)
+                .genericTrapType("5")
+                .specificTrapType("123")
+                .build()
+        );
+        assertEquals("Invalid argument: Generic Trap Type is not [6 - Enterprise Specific] but Specific Trap Type is provided.", exception.getMessage());
+    }
+
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/dto/SNMPTreeResponseTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/dto/SNMPTreeResponseTest.java
new file mode 100644
index 0000000..1a325bf
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/dto/SNMPTreeResponseTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.snmp.dto;
+
+import org.apache.nifi.util.LogMessage;
+import org.apache.nifi.util.MockComponentLog;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.snmp4j.CommunityTarget;
+import org.snmp4j.Target;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+import org.snmp4j.smi.UdpAddress;
+import org.snmp4j.smi.VariableBinding;
+import org.snmp4j.util.TreeEvent;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SNMPTreeResponseTest {
+
+    private static final String SNMP_SEPARATOR = "$";
+    private static final String SNMP_PREFIX = "snmp" + SNMP_SEPARATOR;
+    private static final String VB_SYNTAX = "4";
+
+    private static final String OID_1 = "1.3.6.1.4.1.32437.1.5.1.4.2.0";
+    private static final String OID_2 = "1.3.6.1.4.1.32437.1.5.1.4.3.0";
+    private static final String OID_3 = "1.3.6.1.4.1.32437.1.5.1.4.4.0";
+    private static final String OID_1_VALUE = "OID_1_VALUE";
+    private static final String OID_2_VALUE = "OID_2_VALUE";
+    private static final String OID_3_VALUE = "OID_3_VALUE";
+
+    private static final String TARGET_ADDRESS = "127.0.0.1/50555";
+
+    private static VariableBinding[] vbs1;
+    private static VariableBinding[] vbs2;
+    private static VariableBinding[] vbs3;
+    private static Map<String, String> vbMap;
+    @Mock
+    private static Target target;
+
+    @BeforeClass
+    public static void setUp() {
+        vbMap = new HashMap<>();
+        vbMap.put(SNMP_PREFIX + OID_1 + SNMP_SEPARATOR + VB_SYNTAX, OID_1_VALUE);
+        vbMap.put(SNMP_PREFIX + OID_2 + SNMP_SEPARATOR + VB_SYNTAX, OID_2_VALUE);
+        vbMap.put(SNMP_PREFIX + OID_3 + SNMP_SEPARATOR + VB_SYNTAX, OID_3_VALUE);
+
+        target = new CommunityTarget();
+        target.setAddress(new UdpAddress(TARGET_ADDRESS));
+
+        vbs1 = new VariableBinding[]{
+                new VariableBinding(new OID(OID_1), new OctetString(OID_1_VALUE)),
+        };
+        vbs2 = new VariableBinding[]{
+                new VariableBinding(new OID(OID_2), new OctetString(OID_2_VALUE)),
+        };
+        vbs3 = new VariableBinding[]{
+                new VariableBinding(new OID(OID_3), new OctetString(OID_3_VALUE))
+        };
+    }
+
+    @Test
+    public void testGetAttributes() {
+        final TreeEvent treeEvent1 = mock(TreeEvent.class);
+        when(treeEvent1.getVariableBindings()).thenReturn(vbs1);
+        final TreeEvent treeEvent2 = mock(TreeEvent.class);
+        when(treeEvent2.getVariableBindings()).thenReturn(vbs2);
+        final TreeEvent treeEvent3 = mock(TreeEvent.class);
+        when(treeEvent3.getVariableBindings()).thenReturn(vbs3);
+
+        final List<TreeEvent> treeEvents = new ArrayList<>();
+        Collections.addAll(treeEvents, treeEvent1, treeEvent2, treeEvent3);
+
+        final SNMPTreeResponse snmpTreeResponse = new SNMPTreeResponse(target, treeEvents);
+        final Map<String, String> attributes = snmpTreeResponse.getAttributes();
+
+        assertEquals(3, attributes.size());
+        assertEquals(vbMap, attributes);
+    }
+
+    @Test
+    public void testGetAttributesFlattensEmptyVariableBindingArrays() {
+        final TreeEvent emptyTreeEvent = mock(TreeEvent.class);
+        when(emptyTreeEvent.getVariableBindings()).thenReturn(new VariableBinding[0]);
+        final TreeEvent normalTreeEvent = mock(TreeEvent.class);
+        when(normalTreeEvent.getVariableBindings()).thenReturn(vbs1);
+
+        final List<TreeEvent> treeEvents = new ArrayList<>();
+        Collections.addAll(treeEvents, emptyTreeEvent, normalTreeEvent);
+
+        final SNMPTreeResponse snmpTreeResponse = new SNMPTreeResponse(target, treeEvents);
+        final Map<String, String> attributes = snmpTreeResponse.getAttributes();
+
+        assertEquals(1, attributes.size());
+        assertEquals(OID_1_VALUE, attributes.get(SNMP_PREFIX + OID_1 + SNMP_SEPARATOR + VB_SYNTAX));
+    }
+
+    @Test
+    public void testGetAttributesFiltersNullVariableBindings() {
+        final TreeEvent nullTreeEvent = mock(TreeEvent.class);
+        when(nullTreeEvent.getVariableBindings()).thenReturn(null);
+        final TreeEvent normalTreeEvent = mock(TreeEvent.class);
+        when(normalTreeEvent.getVariableBindings()).thenReturn(vbs1);
+
+        final List<TreeEvent> treeEvents = new ArrayList<>();
+        Collections.addAll(treeEvents, nullTreeEvent, normalTreeEvent);
+
+        final SNMPTreeResponse snmpTreeResponse = new SNMPTreeResponse(target, treeEvents);
+        final Map<String, String> attributes = snmpTreeResponse.getAttributes();
+
+        assertEquals(1, attributes.size());
+        assertEquals(OID_1_VALUE, attributes.get(SNMP_PREFIX + OID_1 + SNMP_SEPARATOR + VB_SYNTAX));
+    }
+
+    @Test
+    public void testGetTargetAddress() {
+        final TreeEvent treeEvent = mock(TreeEvent.class);
+        final List<TreeEvent> treeEvents = new ArrayList<>();
+        treeEvents.add(treeEvent);
+        final SNMPTreeResponse snmpTreeResponse = new SNMPTreeResponse(target, treeEvents);
+        final String actualTargetAddress = snmpTreeResponse.getTargetAddress();
+
+        assertEquals(TARGET_ADDRESS, actualTargetAddress);
+    }
+
+    @Test
+    public void testLogErrors() {
+        final MockComponentLog logger = new MockComponentLog("id1", new Object());
+
+
+        final List<TreeEvent> treeEvents = new ArrayList<>();
+        final TreeEvent treeEvent = mock(TreeEvent.class);
+        when(treeEvent.isError()).thenReturn(true);
+        when(treeEvent.getErrorMessage()).thenReturn("ERROR MESSAGE");
+        treeEvents.add(treeEvent);
+        final SNMPTreeResponse snmpTreeResponse = new SNMPTreeResponse(target, treeEvents);
+        snmpTreeResponse.logErrors(logger);
+
+        final List<LogMessage> errorMessages = logger.getErrorMessages();
+        System.out.println("asd");
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/SNMPClientFactoryTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/SNMPClientFactoryTest.java
deleted file mode 100644
index 50719fc..0000000
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/SNMPClientFactoryTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.snmp.factory;
-
-import org.apache.nifi.snmp.configuration.SNMPConfiguration;
-import org.apache.nifi.snmp.configuration.SNMPConfigurationBuilder;
-import org.junit.jupiter.api.Test;
-import org.snmp4j.Snmp;
-import org.snmp4j.mp.SnmpConstants;
-import org.snmp4j.security.UsmUser;
-import org.snmp4j.smi.OID;
-import org.snmp4j.smi.OctetString;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-public class SNMPClientFactoryTest {
-
-    private final SNMPConfigurationBuilder configurationBuilder = new SNMPConfigurationBuilder()
-            .setAgentHost("127.0.0.1")
-            .setAgentPort("12345")
-            .setRetries(1)
-            .setTimeout(1000)
-            .setSecurityLevel("noAuthNoPriv")
-            .setSecurityName("userName")
-            .setAuthProtocol("SHA")
-            .setAuthPassphrase("authPassword")
-            .setPrivacyProtocol("DES")
-            .setPrivacyPassphrase("privacyPassword")
-            .setCommunityString("public");
-
-    @Test
-    public void testSnmpV3ClientWithoutCorrespondingAgentDoesNotHaveUSM() {
-        final SNMPConfiguration configuration = configurationBuilder
-                .setVersion(SnmpConstants.version3)
-                .build();
-
-
-        final SNMPFactory snmpFactory = new CompositeSNMPFactory();
-        final Snmp snmpManager = snmpFactory.createSnmpManagerInstance(configuration);
-        final UsmUser user = snmpManager.getUSM().getUserTable().getUser(new OctetString("userName")).getUsmUser();
-
-        final OID usmHMACSHAAuthProtocol = new OID("1.3.6.1.6.3.10.1.1.3");
-        final OID usmDESPrivProtocol = new OID("1.3.6.1.6.3.10.1.2.2");
-
-        assertThat("userName", is(equalTo(user.getSecurityName().toString())));
-        assertThat(usmHMACSHAAuthProtocol, is(equalTo(user.getAuthenticationProtocol())));
-        assertThat("authPassword", is(equalTo(user.getAuthenticationPassphrase().toString())));
-        assertThat(usmDESPrivProtocol, is(equalTo(user.getPrivacyProtocol())));
-        assertThat("privacyPassword", is(equalTo(user.getPrivacyPassphrase().toString())));
-        assertThat(3, is(equalTo(user.getSecurityModel())));
-    }
-}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/core/SNMPContextTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/core/SNMPContextTest.java
new file mode 100644
index 0000000..0d073ac
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/core/SNMPContextTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.snmp.factory.core;
+
+import org.apache.nifi.snmp.configuration.SNMPConfiguration;
+import org.junit.Test;
+import org.snmp4j.Snmp;
+import org.snmp4j.Target;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SNMPContextTest {
+
+    @Test
+    public void testCreateSNMPContext() {
+        final SNMPContext snmpContext = spy(SNMPContext.class);
+        final Snmp mockSnmpManager = mock(Snmp.class);
+        final Target mockTarget = mock(Target.class);
+        final SNMPConfiguration snmpConfiguration = mock(SNMPConfiguration.class);
+
+        when(snmpContext.createSnmpManagerInstance(snmpConfiguration)).thenReturn(mockSnmpManager);
+        when(snmpContext.createTargetInstance(snmpConfiguration)).thenReturn(mockTarget);
+
+        snmpContext.createSNMPResourceHandler(snmpConfiguration);
+
+        verify(snmpContext).createSnmpManagerInstance(snmpConfiguration);
+        verify(snmpContext).createTargetInstance(snmpConfiguration);
+    }
+
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/core/SNMPFactoryProviderTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/core/SNMPFactoryProviderTest.java
new file mode 100644
index 0000000..eaf23ef
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/core/SNMPFactoryProviderTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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.snmp.factory.core;
+
+import org.junit.Test;
+import org.snmp4j.mp.SnmpConstants;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+
+public class SNMPFactoryProviderTest {
+
+    @Test
+    public void testCreateFactoryByVersion() {
+        final SNMPContext snmpV1V2cFactoryFromVersion1 = SNMPFactoryProvider.getFactory(SnmpConstants.version1);
+        final SNMPContext snmpV1V2cFactoryFromVersion2c = SNMPFactoryProvider.getFactory(SnmpConstants.version2c);
+        final SNMPContext snmpV3Factory = SNMPFactoryProvider.getFactory(SnmpConstants.version3);
+        assertThat(snmpV1V2cFactoryFromVersion1, instanceOf(V1V2cSNMPFactory.class));
+        assertThat(snmpV1V2cFactoryFromVersion2c, instanceOf(V1V2cSNMPFactory.class));
+        assertThat(snmpV3Factory, instanceOf(V3SNMPFactory.class));
+    }
+
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/core/V1V2cSNMPFactoryTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/core/V1V2cSNMPFactoryTest.java
new file mode 100644
index 0000000..281bbec
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/core/V1V2cSNMPFactoryTest.java
@@ -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.
+ */
+package org.apache.nifi.snmp.factory.core;
+
+import org.apache.nifi.remote.io.socket.NetworkUtils;
+import org.apache.nifi.snmp.configuration.SNMPConfiguration;
+import org.apache.nifi.util.StringUtils;
+import org.junit.Test;
+import org.snmp4j.CommunityTarget;
+import org.snmp4j.Snmp;
+import org.snmp4j.Target;
+import org.snmp4j.security.SecurityLevel;
+
+import static org.apache.nifi.snmp.helper.configurations.SNMPConfigurationFactory.DEFAULT_HOST;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+public class V1V2cSNMPFactoryTest {
+
+    private static final int RETRIES = 3;
+
+    @Test
+    public void testFactoryCreatesV1V2Configuration() {
+        final V1V2cSNMPFactory snmpFactory = new V1V2cSNMPFactory();
+        final int managerPort = NetworkUtils.availablePort();
+        final String targetPort = String.valueOf(NetworkUtils.availablePort());
+        final SNMPConfiguration snmpConfiguration = getSnmpConfiguration(managerPort, targetPort);
+
+        final Target target = snmpFactory.createTargetInstance(snmpConfiguration);
+
+        assertThat(target, instanceOf(CommunityTarget.class));
+        assertEquals(DEFAULT_HOST + "/" + targetPort, target.getAddress().toString());
+        assertEquals(RETRIES, target.getRetries());
+        assertEquals(1, target.getSecurityLevel());
+        assertEquals(StringUtils.EMPTY, target.getSecurityName().toString());
+    }
+
+    @Test
+    public void testFactoryCreatesSnmpManager() {
+        final V1V2cSNMPFactory snmpFactory = new V1V2cSNMPFactory();
+        final int managerPort = NetworkUtils.availablePort();
+        final String targetPort = String.valueOf(NetworkUtils.availablePort());
+        final SNMPConfiguration snmpConfiguration = getSnmpConfiguration(managerPort, targetPort);
+
+        final Snmp snmpManager = snmpFactory.createSnmpManagerInstance(snmpConfiguration);
+
+        final String address = snmpManager.getMessageDispatcher().getTransportMappings().iterator().next().getListenAddress().toString();
+        assertEquals(DEFAULT_HOST + "/" + managerPort, address);
+    }
+
+    @Test
+    public void testFactoryCreatesResourceHandler() {
+        final V1V2cSNMPFactory snmpFactory = spy(V1V2cSNMPFactory.class);
+        final int managerPort = NetworkUtils.availablePort();
+        final String targetPort = String.valueOf(NetworkUtils.availablePort());
+        final SNMPConfiguration snmpConfiguration = getSnmpConfiguration(managerPort, targetPort);
+
+        snmpFactory.createSNMPResourceHandler(snmpConfiguration);
+
+        verify(snmpFactory).createTargetInstance(snmpConfiguration);
+        verify(snmpFactory).createSnmpManagerInstance(snmpConfiguration);
+    }
+
+    private SNMPConfiguration getSnmpConfiguration(int managerPort, String targetPort) {
+        return new SNMPConfiguration.Builder()
+                .setRetries(RETRIES)
+                .setManagerPort(managerPort)
+                .setTargetHost(DEFAULT_HOST)
+                .setTargetPort(targetPort)
+                .setSecurityLevel(SecurityLevel.noAuthNoPriv.name())
+                .build();
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/core/V3SNMPFactoryTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/core/V3SNMPFactoryTest.java
new file mode 100644
index 0000000..7962389
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/core/V3SNMPFactoryTest.java
@@ -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.
+ */
+package org.apache.nifi.snmp.factory.core;
+
+import org.apache.nifi.remote.io.socket.NetworkUtils;
+import org.apache.nifi.snmp.configuration.SNMPConfiguration;
+import org.junit.Test;
+import org.snmp4j.Snmp;
+import org.snmp4j.Target;
+import org.snmp4j.UserTarget;
+import org.snmp4j.security.SecurityLevel;
+import org.snmp4j.security.SecurityModels;
+import org.snmp4j.security.USM;
+import org.snmp4j.smi.Integer32;
+import org.snmp4j.smi.OctetString;
+
+import static org.apache.nifi.snmp.helper.configurations.SNMPConfigurationFactory.DEFAULT_HOST;
+import static org.apache.nifi.snmp.helper.configurations.SNMPV3ConfigurationFactory.AUTH_PASSPHRASE;
+import static org.apache.nifi.snmp.helper.configurations.SNMPV3ConfigurationFactory.AUTH_PROTOCOL;
+import static org.apache.nifi.snmp.helper.configurations.SNMPV3ConfigurationFactory.PRIV_PASSPHRASE;
+import static org.apache.nifi.snmp.helper.configurations.SNMPV3ConfigurationFactory.PRIV_PROTOCOL;
+import static org.apache.nifi.snmp.helper.configurations.SNMPV3ConfigurationFactory.SECURITY_NAME;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+public class V3SNMPFactoryTest {
+
+    private static final int RETRIES = 3;
+    private static final int EXPECTED_SECURITY_LEVEL = 3;
+
+    @Test
+    public void testFactoryCreatesTarget() {
+        final V3SNMPFactory snmpFactory = new V3SNMPFactory();
+        final int managerPort = NetworkUtils.availablePort();
+        final String targetPort = String.valueOf(NetworkUtils.availablePort());
+        final SNMPConfiguration snmpConfiguration = getSnmpConfiguration(managerPort, targetPort);
+
+        final Target target = snmpFactory.createTargetInstance(snmpConfiguration);
+
+        assertThat(target, instanceOf(UserTarget.class));
+        assertEquals(DEFAULT_HOST + "/" + targetPort, target.getAddress().toString());
+        assertEquals(RETRIES, target.getRetries());
+        assertEquals(EXPECTED_SECURITY_LEVEL, target.getSecurityLevel());
+        assertEquals(SECURITY_NAME, target.getSecurityName().toString());
+    }
+
+    @Test
+    public void testFactoryCreatesSnmpManager() {
+        final V3SNMPFactory snmpFactory = new V3SNMPFactory();
+        final int managerPort = NetworkUtils.availablePort();
+        final String targetPort = String.valueOf(NetworkUtils.availablePort());
+        final SNMPConfiguration snmpConfiguration = getSnmpConfiguration(managerPort, targetPort);
+
+        final Snmp snmpManager = snmpFactory.createSnmpManagerInstance(snmpConfiguration);
+
+        final String address = snmpManager.getMessageDispatcher().getTransportMappings().iterator().next().getListenAddress().toString();
+        USM usm = (USM) SecurityModels.getInstance().getSecurityModel(new Integer32(3));
+        assertEquals(DEFAULT_HOST + "/" + managerPort, address);
+        assertTrue(usm.hasUser(null, new OctetString("SHAAES128")));
+    }
+
+    @Test
+    public void testFactoryCreatesResourceHandler() {
+        final V3SNMPFactory snmpFactory = spy(V3SNMPFactory.class);
+        final int managerPort = NetworkUtils.availablePort();
+        final String targetPort = String.valueOf(NetworkUtils.availablePort());
+        final SNMPConfiguration snmpConfiguration = getSnmpConfiguration(managerPort, targetPort);
+        snmpFactory.createSNMPResourceHandler(snmpConfiguration);
+
+        verify(snmpFactory).createTargetInstance(snmpConfiguration);
+        verify(snmpFactory).createSnmpManagerInstance(snmpConfiguration);
+    }
+
+    private SNMPConfiguration getSnmpConfiguration(int managerPort, String targetPort) {
+        return new SNMPConfiguration.Builder()
+                .setRetries(RETRIES)
+                .setManagerPort(managerPort)
+                .setTargetHost(DEFAULT_HOST)
+                .setTargetPort(targetPort)
+                .setSecurityLevel(SecurityLevel.authPriv.name())
+                .setSecurityName(SECURITY_NAME)
+                .setAuthProtocol(AUTH_PROTOCOL)
+                .setAuthPassphrase(AUTH_PASSPHRASE)
+                .setPrivacyProtocol(PRIV_PROTOCOL)
+                .setPrivacyPassphrase(PRIV_PASSPHRASE)
+                .build();
+    }
+
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/trap/V1TrapPDUFactoryTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/trap/V1TrapPDUFactoryTest.java
new file mode 100644
index 0000000..3cd699d
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/trap/V1TrapPDUFactoryTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.snmp.factory.trap;
+
+import org.apache.nifi.snmp.configuration.V1TrapConfiguration;
+import org.junit.Test;
+import org.snmp4j.PDU;
+import org.snmp4j.PDUv1;
+import org.snmp4j.Target;
+
+import java.time.Instant;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+public class V1TrapPDUFactoryTest {
+
+    private static final String AGENT_ADDRESS = "127.0.0.1";
+    private static final String ENTERPRISE_OID = "1.3.6.1.4.1.8072.2.3.0.1";
+    private static final int GENERIC_TRAP_TYPE = 6;
+    private static final int SPECIFIC_TRAP_TYPE = 2;
+
+    @Test
+    public void testCreateV1TrapPdu() {
+        final Target mockTarget = mock(Target.class);
+        final V1TrapConfiguration v1TrapConfiguration = V1TrapConfiguration.builder()
+                .enterpriseOid(ENTERPRISE_OID)
+                .agentAddress(AGENT_ADDRESS)
+                .genericTrapType(String.valueOf(GENERIC_TRAP_TYPE))
+                .specificTrapType(String.valueOf(SPECIFIC_TRAP_TYPE))
+                .build();
+
+        final V1TrapPDUFactory v1TrapPduFactory = new V1TrapPDUFactory(mockTarget, Instant.now());
+        final PDU pdu = v1TrapPduFactory.get(v1TrapConfiguration);
+
+        assertEquals(PDU.V1TRAP, pdu.getType());
+
+        final PDUv1 pduV1 = (PDUv1) pdu;
+
+        assertEquals(ENTERPRISE_OID, pduV1.getEnterprise().toString());
+        assertEquals(AGENT_ADDRESS, pduV1.getAgentAddress().toString());
+        assertEquals(GENERIC_TRAP_TYPE, pduV1.getGenericTrap());
+        assertEquals(SPECIFIC_TRAP_TYPE, pduV1.getSpecificTrap());
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/trap/V2TrapPDUFactoryTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/trap/V2TrapPDUFactoryTest.java
new file mode 100644
index 0000000..64b81e9
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/factory/trap/V2TrapPDUFactoryTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.snmp.factory.trap;
+
+import org.apache.nifi.snmp.configuration.V2TrapConfiguration;
+import org.junit.Test;
+import org.snmp4j.PDU;
+import org.snmp4j.Target;
+import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.smi.VariableBinding;
+
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Vector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+public class V2TrapPDUFactoryTest {
+
+    private static final String TRAP_OID = "1.3.6.1.4.1.8072.2.3.0.1";
+
+    @Test
+    public void testCreateV2TrapPdu() {
+        final Target mockTarget = mock(Target.class);
+        final V2TrapConfiguration v2TrapConfiguration = new V2TrapConfiguration(TRAP_OID);
+        final V2TrapPDUFactory v2TrapPduFactory = new V2TrapPDUFactory(mockTarget, Instant.now());
+
+        final PDU pdu = v2TrapPduFactory.get(v2TrapConfiguration);
+
+        final Vector<? extends VariableBinding> variableBindings = pdu.getVariableBindings();
+
+        Set<String> expected = new HashSet<>(Arrays.asList(SnmpConstants.snmpTrapOID.toString(), TRAP_OID));
+        Set<String> actual = variableBindings.stream()
+                .flatMap(c -> Stream.of(c.getOid().toString(), c.getVariable().toString()))
+                .collect(Collectors.toSet());
+        assertTrue(actual.containsAll(expected));
+    }
+
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/SNMPTestUtils.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/SNMPTestUtils.java
deleted file mode 100644
index 492690f..0000000
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/SNMPTestUtils.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.snmp.helper;
-
-import org.snmp4j.CommunityTarget;
-import org.snmp4j.Snmp;
-import org.snmp4j.UserTarget;
-import org.snmp4j.security.UsmUser;
-import org.snmp4j.smi.OID;
-import org.snmp4j.smi.OctetString;
-import org.snmp4j.smi.UdpAddress;
-import org.snmp4j.transport.DefaultUdpTransportMapping;
-
-import java.io.IOException;
-
-public class SNMPTestUtils {
-
-    public static Snmp createSnmpClient() throws IOException {
-        final DefaultUdpTransportMapping transportMapping = new DefaultUdpTransportMapping();
-        transportMapping.listen();
-        return new Snmp(transportMapping);
-    }
-
-    public static CommunityTarget createCommTarget(final String community, final String address, final int version) {
-        final CommunityTarget target = new CommunityTarget();
-        target.setVersion(version);
-        target.setCommunity(new OctetString(community));
-        target.setAddress(new UdpAddress(address));
-        target.setRetries(0);
-        target.setTimeout(500);
-        return target;
-    }
-
-    public static UserTarget createUserTarget(final String address, final int securityLevel, final String securityName, final int version) {
-        final UserTarget target = new UserTarget();
-        target.setVersion(version);
-        target.setSecurityLevel(securityLevel);
-        target.setSecurityName(new OctetString(securityName));
-        target.setAddress(new UdpAddress(address));
-        target.setRetries(0);
-        target.setTimeout(500);
-        return target;
-    }
-
-    public static UserTarget prepareUser(final Snmp snmp, final int version, final String address, final int securityLevel, final String securityName,
-                                         final OID auth, final OID priv, final String authPwd, final String privPwd) {
-        snmp.getUSM().removeAllUsers();
-        final OctetString aPwd = authPwd != null ? new OctetString(authPwd) : null;
-        final OctetString pPwd = privPwd != null ? new OctetString(privPwd) : null;
-        snmp.getUSM().addUser(new OctetString(securityName), new UsmUser(new OctetString(securityName), auth, aPwd, priv, pPwd));
-        return createUserTarget(address, securityLevel, securityName, version);
-    }
-
-}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/TrapConfigurationFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/TrapConfigurationFactory.java
new file mode 100644
index 0000000..dff77e8
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/TrapConfigurationFactory.java
@@ -0,0 +1,46 @@
+/*
+ * 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.snmp.helper;
+
+import org.apache.nifi.snmp.configuration.V1TrapConfiguration;
+import org.apache.nifi.snmp.configuration.V2TrapConfiguration;
+import org.snmp4j.PDUv1;
+
+public class TrapConfigurationFactory {
+
+    // v1 specific
+    private static final String ENTERPRISE_OID = "1.3.5.7.11";
+    private static final String AGENT_ADDRESS = "1.2.3.4";
+    private static final String GENERIC_TRAP_TYPE = String.valueOf(PDUv1.ENTERPRISE_SPECIFIC);
+    private static final String SPECIFIC_TRAP_TYPE = "2";
+
+    // v2c/v3 specific
+    private static final String TRAP_OID_VALUE = "testTrapOidValue";
+
+    public static V1TrapConfiguration getV1TrapConfiguration() {
+        return V1TrapConfiguration.builder()
+                .enterpriseOid(ENTERPRISE_OID)
+                .agentAddress(AGENT_ADDRESS)
+                .genericTrapType(GENERIC_TRAP_TYPE)
+                .specificTrapType(SPECIFIC_TRAP_TYPE)
+                .build();
+    }
+
+    public static V2TrapConfiguration getV2TrapConfiguration() {
+        return new V2TrapConfiguration(TRAP_OID_VALUE);
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/SNMPFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/configurations/SNMPConfigurationFactory.java
similarity index 67%
rename from nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/SNMPFactory.java
rename to nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/configurations/SNMPConfigurationFactory.java
index 442c03e..89e8bd4 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/main/java/org/apache/nifi/snmp/factory/SNMPFactory.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/configurations/SNMPConfigurationFactory.java
@@ -14,17 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.nifi.snmp.factory;
+package org.apache.nifi.snmp.helper.configurations;
 
 import org.apache.nifi.snmp.configuration.SNMPConfiguration;
-import org.snmp4j.Snmp;
-import org.snmp4j.Target;
 
-public interface SNMPFactory {
+public interface SNMPConfigurationFactory {
 
-    boolean supports(final int version);
+    String DEFAULT_HOST = "127.0.0.1";
+    String COMMUNITY_STRING = "public";
 
-    Snmp createSnmpManagerInstance(final SNMPConfiguration configuration);
+    SNMPConfiguration createSnmpGetSetConfiguration(int agentPort);
+
+    SNMPConfiguration createSnmpGetSetConfigWithCustomHost(final String host, final int agentPort);
+
+    SNMPConfiguration createSnmpListenTrapConfig(final int managerPort);
 
-    Target createTargetInstance(final SNMPConfiguration configuration);
 }
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/configurations/SNMPV1V2cConfigurationFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/configurations/SNMPV1V2cConfigurationFactory.java
new file mode 100644
index 0000000..72591be
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/configurations/SNMPV1V2cConfigurationFactory.java
@@ -0,0 +1,57 @@
+/*
+ * 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.snmp.helper.configurations;
+
+import org.apache.nifi.snmp.configuration.SNMPConfiguration;
+
+public class SNMPV1V2cConfigurationFactory implements SNMPConfigurationFactory {
+
+    private int snmpVersion;
+
+    public SNMPV1V2cConfigurationFactory(int snmpVersion) {
+        this.snmpVersion = snmpVersion;
+    }
+
+    @Override
+    public SNMPConfiguration createSnmpGetSetConfiguration(final int agentPort) {
+        return SNMPConfiguration.builder()
+                .setTargetHost(DEFAULT_HOST)
+                .setTargetPort(String.valueOf(agentPort))
+                .setCommunityString(COMMUNITY_STRING)
+                .setVersion(snmpVersion)
+                .build();
+    }
+
+    @Override
+    public SNMPConfiguration createSnmpGetSetConfigWithCustomHost(final String host, final int agentPort) {
+        return SNMPConfiguration.builder()
+                .setTargetHost(host)
+                .setTargetPort(String.valueOf(agentPort))
+                .setCommunityString(COMMUNITY_STRING)
+                .setVersion(snmpVersion)
+                .build();
+    }
+
+    @Override
+    public SNMPConfiguration createSnmpListenTrapConfig(final int managerPort) {
+        return SNMPConfiguration.builder()
+                .setManagerPort(managerPort)
+                .setCommunityString(COMMUNITY_STRING)
+                .setVersion(snmpVersion)
+                .build();
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/configurations/SNMPV3ConfigurationFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/configurations/SNMPV3ConfigurationFactory.java
new file mode 100644
index 0000000..7a278f5
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/configurations/SNMPV3ConfigurationFactory.java
@@ -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.
+ */
+package org.apache.nifi.snmp.helper.configurations;
+
+import org.apache.nifi.snmp.configuration.SNMPConfiguration;
+import org.snmp4j.mp.SnmpConstants;
+
+public class SNMPV3ConfigurationFactory implements SNMPConfigurationFactory {
+
+    // V3 security (users are set in test agents)
+    public static final String SECURITY_LEVEL = "authPriv";
+    public static final String SECURITY_NAME = "SHAAES128";
+    public static final String AUTH_PROTOCOL = "SHA";
+    public static final String AUTH_PASSPHRASE = "SHAAES128AuthPassphrase";
+    public static final String PRIV_PROTOCOL = "AES128";
+    public static final String PRIV_PASSPHRASE = "SHAAES128PrivPassphrase";
+
+    @Override
+    public SNMPConfiguration createSnmpGetSetConfiguration(final int agentPort) {
+        return SNMPConfiguration.builder()
+                .setTargetHost(DEFAULT_HOST)
+                .setTargetPort(String.valueOf(agentPort))
+                .setCommunityString(COMMUNITY_STRING)
+                .setVersion(SnmpConstants.version3)
+                .setSecurityLevel(SECURITY_LEVEL)
+                .setSecurityName(SECURITY_NAME)
+                .setAuthProtocol(AUTH_PROTOCOL)
+                .setAuthPassphrase(AUTH_PASSPHRASE)
+                .setPrivacyProtocol(PRIV_PROTOCOL)
+                .setPrivacyPassphrase(PRIV_PASSPHRASE)
+                .build();
+    }
+
+    @Override
+    public SNMPConfiguration createSnmpGetSetConfigWithCustomHost(final String host, final int agentPort) {
+        return SNMPConfiguration.builder()
+                .setTargetHost(host)
+                .setTargetPort(String.valueOf(agentPort))
+                .setCommunityString(COMMUNITY_STRING)
+                .setVersion(SnmpConstants.version3)
+                .setSecurityLevel(SECURITY_LEVEL)
+                .setSecurityName(SECURITY_NAME)
+                .setAuthProtocol(AUTH_PROTOCOL)
+                .setAuthPassphrase(AUTH_PASSPHRASE)
+                .setPrivacyProtocol(PRIV_PROTOCOL)
+                .setPrivacyPassphrase(PRIV_PASSPHRASE)
+                .build();
+    }
+
+    @Override
+    public SNMPConfiguration createSnmpListenTrapConfig(final int managerPort) {
+        return SNMPConfiguration.builder()
+                .setManagerPort(managerPort)
+                .setVersion(SnmpConstants.version3)
+                .build();
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/testrunners/SNMPTestRunnerFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/testrunners/SNMPTestRunnerFactory.java
new file mode 100644
index 0000000..4096f45
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/testrunners/SNMPTestRunnerFactory.java
@@ -0,0 +1,58 @@
+/*
+ * 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.snmp.helper.testrunners;
+
+import org.apache.nifi.snmp.exception.InvalidSnmpVersionException;
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.TestRunner;
+import org.snmp4j.mp.SnmpConstants;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.nifi.snmp.utils.SNMPUtils.SNMP_PROP_PREFIX;
+
+public interface SNMPTestRunnerFactory {
+
+    TestRunner createSnmpGetTestRunner(final int agentPort, final String oid, final String strategy);
+
+    TestRunner createSnmpSetTestRunner(final int agentPort, final String oid, final String oidValue);
+
+    TestRunner createSnmpSendTrapTestRunner(final int managerPort, final String oid, final String oidValue);
+
+    TestRunner createSnmpListenTrapTestRunner(final int managerPort);
+
+    default MockFlowFile getFlowFile(String oid, String oidValue) {
+        final MockFlowFile flowFile = new MockFlowFile(1L);
+        final Map<String, String> attributes = new HashMap<>();
+        attributes.put(SNMP_PROP_PREFIX + oid, oidValue);
+        flowFile.putAttributes(attributes);
+        return flowFile;
+    }
+
+    default String getVersionName(final int version) {
+        if (version == SnmpConstants.version1) {
+            return "SNMPv1";
+        } else if (version == SnmpConstants.version2c) {
+            return "SNMPv2c";
+        } else if (version == SnmpConstants.version3) {
+            return "SNMPv3";
+        } else {
+            throw new InvalidSnmpVersionException("Invalid SNMP version");
+        }
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/testrunners/SNMPV1TestRunnerFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/testrunners/SNMPV1TestRunnerFactory.java
new file mode 100644
index 0000000..4bb17b5
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/testrunners/SNMPV1TestRunnerFactory.java
@@ -0,0 +1,93 @@
+/*
+ * 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.snmp.helper.testrunners;
+
+import org.apache.nifi.snmp.configuration.SNMPConfiguration;
+import org.apache.nifi.snmp.configuration.V1TrapConfiguration;
+import org.apache.nifi.snmp.helper.TrapConfigurationFactory;
+import org.apache.nifi.snmp.helper.configurations.SNMPConfigurationFactory;
+import org.apache.nifi.snmp.helper.configurations.SNMPV1V2cConfigurationFactory;
+import org.apache.nifi.snmp.processors.GetSNMP;
+import org.apache.nifi.snmp.processors.ListenTrapSNMP;
+import org.apache.nifi.snmp.processors.SendTrapSNMP;
+import org.apache.nifi.snmp.processors.SetSNMP;
+import org.apache.nifi.snmp.processors.properties.BasicProperties;
+import org.apache.nifi.snmp.processors.properties.V1TrapProperties;
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.TestRunner;
+import org.apache.nifi.util.TestRunners;
+import org.snmp4j.mp.SnmpConstants;
+
+public class SNMPV1TestRunnerFactory implements SNMPTestRunnerFactory {
+
+
+    private static final SNMPConfigurationFactory snmpV1V2ConfigurationFactory = new SNMPV1V2cConfigurationFactory(SnmpConstants.version1);
+
+    @Override
+    public TestRunner createSnmpGetTestRunner(final int agentPort, final String oid, final String strategy) {
+        final TestRunner runner = TestRunners.newTestRunner(GetSNMP.class);
+        final SNMPConfiguration snmpConfiguration = snmpV1V2ConfigurationFactory.createSnmpGetSetConfiguration(agentPort);
+        runner.setProperty(GetSNMP.AGENT_HOST, snmpConfiguration.getTargetHost());
+        runner.setProperty(GetSNMP.AGENT_PORT, snmpConfiguration.getTargetPort());
+        runner.setProperty(BasicProperties.SNMP_COMMUNITY, snmpConfiguration.getCommunityString());
+        runner.setProperty(BasicProperties.SNMP_VERSION, getVersionName(snmpConfiguration.getVersion()));
+        runner.setProperty(GetSNMP.SNMP_STRATEGY, strategy);
+        runner.setProperty(GetSNMP.OID, oid);
+        return runner;
+    }
+
+    @Override
+    public TestRunner createSnmpSetTestRunner(final int agentPort, final String oid, final String oidValue) {
+        final TestRunner runner = TestRunners.newTestRunner(SetSNMP.class);
+        final SNMPConfiguration snmpConfiguration = snmpV1V2ConfigurationFactory.createSnmpGetSetConfiguration(agentPort);
+        runner.setProperty(SetSNMP.AGENT_HOST, snmpConfiguration.getTargetHost());
+        runner.setProperty(SetSNMP.AGENT_PORT, snmpConfiguration.getTargetPort());
+        runner.setProperty(BasicProperties.SNMP_COMMUNITY, snmpConfiguration.getCommunityString());
+        runner.setProperty(BasicProperties.SNMP_VERSION, getVersionName(snmpConfiguration.getVersion()));
+        final MockFlowFile flowFile = getFlowFile(oid, oidValue);
+        runner.enqueue(flowFile);
+        return runner;
+    }
+
+    @Override
+    public TestRunner createSnmpSendTrapTestRunner(final int managerPort, final String oid, final String oidValue) {
+        final TestRunner runner = TestRunners.newTestRunner(SendTrapSNMP.class);
+        final SNMPConfiguration snmpConfiguration = snmpV1V2ConfigurationFactory.createSnmpGetSetConfiguration(managerPort);
+        final V1TrapConfiguration trapConfiguration = TrapConfigurationFactory.getV1TrapConfiguration();
+        runner.setProperty(SendTrapSNMP.SNMP_MANAGER_HOST, snmpConfiguration.getTargetHost());
+        runner.setProperty(SendTrapSNMP.SNMP_MANAGER_PORT, snmpConfiguration.getTargetPort());
+        runner.setProperty(BasicProperties.SNMP_COMMUNITY, snmpConfiguration.getCommunityString());
+        runner.setProperty(BasicProperties.SNMP_VERSION, getVersionName(snmpConfiguration.getVersion()));
+        runner.setProperty(V1TrapProperties.ENTERPRISE_OID, trapConfiguration.getEnterpriseOid());
+        runner.setProperty(V1TrapProperties.AGENT_ADDRESS, trapConfiguration.getAgentAddress());
+        runner.setProperty(V1TrapProperties.GENERIC_TRAP_TYPE, String.valueOf(trapConfiguration.getGenericTrapType()));
+        runner.setProperty(V1TrapProperties.SPECIFIC_TRAP_TYPE, String.valueOf(trapConfiguration.getSpecificTrapType()));
+        final MockFlowFile flowFile = getFlowFile(oid, oidValue);
+        runner.enqueue(flowFile);
+        return runner;
+    }
+
+    @Override
+    public TestRunner createSnmpListenTrapTestRunner(final int managerPort) {
+        final TestRunner runner = TestRunners.newTestRunner(ListenTrapSNMP.class);
+        final SNMPConfiguration snmpConfiguration = snmpV1V2ConfigurationFactory.createSnmpListenTrapConfig(managerPort);
+        runner.setProperty(ListenTrapSNMP.SNMP_MANAGER_PORT, String.valueOf(snmpConfiguration.getManagerPort()));
+        runner.setProperty(BasicProperties.SNMP_COMMUNITY, snmpConfiguration.getCommunityString());
+        runner.setProperty(BasicProperties.SNMP_VERSION, getVersionName(snmpConfiguration.getVersion()));
+        return runner;
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/testrunners/SNMPV2cTestRunnerFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/testrunners/SNMPV2cTestRunnerFactory.java
new file mode 100644
index 0000000..63fc49e
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/testrunners/SNMPV2cTestRunnerFactory.java
@@ -0,0 +1,89 @@
+/*
+ * 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.snmp.helper.testrunners;
+
+import org.apache.nifi.snmp.configuration.SNMPConfiguration;
+import org.apache.nifi.snmp.configuration.V2TrapConfiguration;
+import org.apache.nifi.snmp.helper.TrapConfigurationFactory;
+import org.apache.nifi.snmp.helper.configurations.SNMPConfigurationFactory;
+import org.apache.nifi.snmp.helper.configurations.SNMPV1V2cConfigurationFactory;
+import org.apache.nifi.snmp.processors.GetSNMP;
+import org.apache.nifi.snmp.processors.ListenTrapSNMP;
+import org.apache.nifi.snmp.processors.SendTrapSNMP;
+import org.apache.nifi.snmp.processors.SetSNMP;
+import org.apache.nifi.snmp.processors.properties.BasicProperties;
+import org.apache.nifi.snmp.processors.properties.V2TrapProperties;
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.TestRunner;
+import org.apache.nifi.util.TestRunners;
+import org.snmp4j.mp.SnmpConstants;
+
+public class SNMPV2cTestRunnerFactory implements SNMPTestRunnerFactory {
+
+    private static final SNMPConfigurationFactory snmpV2cConfigurationFactory = new SNMPV1V2cConfigurationFactory(SnmpConstants.version2c);
+
+    @Override
+    public TestRunner createSnmpGetTestRunner(int agentPort, String oid, String strategy) {
+        final TestRunner runner = TestRunners.newTestRunner(GetSNMP.class);
+        final SNMPConfiguration snmpConfiguration = snmpV2cConfigurationFactory.createSnmpGetSetConfiguration(agentPort);
+        runner.setProperty(GetSNMP.OID, oid);
+        runner.setProperty(GetSNMP.AGENT_HOST, snmpConfiguration.getTargetHost());
+        runner.setProperty(GetSNMP.AGENT_PORT, snmpConfiguration.getTargetPort());
+        runner.setProperty(BasicProperties.SNMP_COMMUNITY, snmpConfiguration.getCommunityString());
+        runner.setProperty(BasicProperties.SNMP_VERSION, getVersionName(snmpConfiguration.getVersion()));
+        runner.setProperty(GetSNMP.SNMP_STRATEGY, strategy);
+        return runner;
+    }
+
+    @Override
+    public TestRunner createSnmpSetTestRunner(int agentPort, String oid, String oidValue) {
+        final TestRunner runner = TestRunners.newTestRunner(SetSNMP.class);
+        final SNMPConfiguration snmpConfiguration = snmpV2cConfigurationFactory.createSnmpGetSetConfiguration(agentPort);
+        runner.setProperty(SetSNMP.AGENT_HOST, snmpConfiguration.getTargetHost());
+        runner.setProperty(SetSNMP.AGENT_PORT, snmpConfiguration.getTargetPort());
+        runner.setProperty(BasicProperties.SNMP_COMMUNITY, snmpConfiguration.getCommunityString());
+        runner.setProperty(BasicProperties.SNMP_VERSION, getVersionName(snmpConfiguration.getVersion()));
+        final MockFlowFile flowFile = getFlowFile(oid, oidValue);
+        runner.enqueue(flowFile);
+        return runner;
+    }
+
+    @Override
+    public TestRunner createSnmpSendTrapTestRunner(int managerPort, final String oid, final String oidValue) {
+        final TestRunner runner = TestRunners.newTestRunner(SendTrapSNMP.class);
+        final SNMPConfiguration snmpConfiguration = snmpV2cConfigurationFactory.createSnmpGetSetConfiguration(managerPort);
+        final V2TrapConfiguration trapConfiguration = TrapConfigurationFactory.getV2TrapConfiguration();
+        runner.setProperty(SendTrapSNMP.SNMP_MANAGER_HOST, snmpConfiguration.getTargetHost());
+        runner.setProperty(SendTrapSNMP.SNMP_MANAGER_PORT, snmpConfiguration.getTargetPort());
+        runner.setProperty(BasicProperties.SNMP_COMMUNITY, snmpConfiguration.getCommunityString());
+        runner.setProperty(BasicProperties.SNMP_VERSION, getVersionName(snmpConfiguration.getVersion()));
+        runner.setProperty(V2TrapProperties.TRAP_OID_VALUE, trapConfiguration.getTrapOidValue());
+        final MockFlowFile flowFile = getFlowFile(oid, oidValue);
+        runner.enqueue(flowFile);
+        return runner;
+    }
+
+    @Override
+    public TestRunner createSnmpListenTrapTestRunner(int managerPort) {
+        final TestRunner runner = TestRunners.newTestRunner(ListenTrapSNMP.class);
+        final SNMPConfiguration snmpConfiguration = snmpV2cConfigurationFactory.createSnmpListenTrapConfig(managerPort);
+        runner.setProperty(ListenTrapSNMP.SNMP_MANAGER_PORT, String.valueOf(snmpConfiguration.getManagerPort()));
+        runner.setProperty(BasicProperties.SNMP_COMMUNITY, snmpConfiguration.getCommunityString());
+        runner.setProperty(BasicProperties.SNMP_VERSION, getVersionName(snmpConfiguration.getVersion()));
+        return runner;
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/testrunners/SNMPV3TestRunnerFactory.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/testrunners/SNMPV3TestRunnerFactory.java
new file mode 100644
index 0000000..998766e
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/helper/testrunners/SNMPV3TestRunnerFactory.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.snmp.helper.testrunners;
+
+import org.apache.nifi.snmp.configuration.SNMPConfiguration;
+import org.apache.nifi.snmp.configuration.V2TrapConfiguration;
+import org.apache.nifi.snmp.helper.TrapConfigurationFactory;
+import org.apache.nifi.snmp.helper.configurations.SNMPConfigurationFactory;
+import org.apache.nifi.snmp.helper.configurations.SNMPV3ConfigurationFactory;
+import org.apache.nifi.snmp.processors.GetSNMP;
+import org.apache.nifi.snmp.processors.ListenTrapSNMP;
+import org.apache.nifi.snmp.processors.SendTrapSNMP;
+import org.apache.nifi.snmp.processors.SetSNMP;
+import org.apache.nifi.snmp.processors.properties.BasicProperties;
+import org.apache.nifi.snmp.processors.properties.V2TrapProperties;
+import org.apache.nifi.snmp.processors.properties.V3SecurityProperties;
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.TestRunner;
+import org.apache.nifi.util.TestRunners;
+
+public class SNMPV3TestRunnerFactory implements SNMPTestRunnerFactory {
+
+    private static final String USM_USERS_FILE_PATH = "src/test/resources/usm.json";
+    private static final SNMPConfigurationFactory snmpV3ConfigurationFactory = new SNMPV3ConfigurationFactory();
+
+    @Override
+    public TestRunner createSnmpGetTestRunner(int agentPort, String oid, String strategy) {
+        final TestRunner runner = TestRunners.newTestRunner(GetSNMP.class);
+        final SNMPConfiguration snmpConfiguration = snmpV3ConfigurationFactory.createSnmpGetSetConfiguration(agentPort);
+        runner.setProperty(GetSNMP.OID, oid);
+        runner.setProperty(GetSNMP.SNMP_STRATEGY, strategy);
+        runner.setProperty(GetSNMP.AGENT_HOST, snmpConfiguration.getTargetHost());
+        runner.setProperty(GetSNMP.AGENT_PORT, snmpConfiguration.getTargetPort());
+        runner.setProperty(BasicProperties.SNMP_COMMUNITY, snmpConfiguration.getCommunityString());
+        runner.setProperty(BasicProperties.SNMP_VERSION, getVersionName(snmpConfiguration.getVersion()));
+        runner.setProperty(V3SecurityProperties.SNMP_SECURITY_LEVEL, snmpConfiguration.getSecurityLevel());
+        runner.setProperty(V3SecurityProperties.SNMP_SECURITY_NAME, snmpConfiguration.getSecurityName());
+        runner.setProperty(V3SecurityProperties.SNMP_AUTH_PROTOCOL, snmpConfiguration.getAuthProtocol());
+        runner.setProperty(V3SecurityProperties.SNMP_AUTH_PASSWORD, snmpConfiguration.getAuthPassphrase());
+        runner.setProperty(V3SecurityProperties.SNMP_PRIVACY_PROTOCOL, snmpConfiguration.getPrivacyProtocol());
+        runner.setProperty(V3SecurityProperties.SNMP_PRIVACY_PASSWORD, snmpConfiguration.getPrivacyPassphrase());
+        return runner;
+    }
+
+    @Override
+    public TestRunner createSnmpSetTestRunner(int agentPort, String oid, String oidValue) {
+        final TestRunner runner = TestRunners.newTestRunner(SetSNMP.class);
+        final SNMPConfiguration snmpConfiguration = snmpV3ConfigurationFactory.createSnmpGetSetConfiguration(agentPort);
+        runner.setProperty(SetSNMP.AGENT_HOST, snmpConfiguration.getTargetHost());
+        runner.setProperty(SetSNMP.AGENT_PORT, snmpConfiguration.getTargetPort());
+        runner.setProperty(BasicProperties.SNMP_COMMUNITY, snmpConfiguration.getCommunityString());
+        runner.setProperty(BasicProperties.SNMP_VERSION, getVersionName(snmpConfiguration.getVersion()));
+        runner.setProperty(V3SecurityProperties.SNMP_SECURITY_LEVEL, snmpConfiguration.getSecurityLevel());
+        runner.setProperty(V3SecurityProperties.SNMP_SECURITY_NAME, snmpConfiguration.getSecurityName());
+        runner.setProperty(V3SecurityProperties.SNMP_AUTH_PROTOCOL, snmpConfiguration.getAuthProtocol());
+        runner.setProperty(V3SecurityProperties.SNMP_AUTH_PASSWORD, snmpConfiguration.getAuthPassphrase());
+        runner.setProperty(V3SecurityProperties.SNMP_PRIVACY_PROTOCOL, snmpConfiguration.getPrivacyProtocol());
+        runner.setProperty(V3SecurityProperties.SNMP_PRIVACY_PASSWORD, snmpConfiguration.getPrivacyPassphrase());
+        final MockFlowFile flowFile = getFlowFile(oid, oidValue);
+        runner.enqueue(flowFile);
+        return runner;
+    }
+
+    @Override
+    public TestRunner createSnmpSendTrapTestRunner(int managerPort, final String oid, final String oidValue) {
+        final TestRunner runner = TestRunners.newTestRunner(SendTrapSNMP.class);
+        final SNMPConfiguration snmpConfiguration = snmpV3ConfigurationFactory.createSnmpGetSetConfiguration(managerPort);
+        final V2TrapConfiguration trapConfiguration = TrapConfigurationFactory.getV2TrapConfiguration();
+        runner.setProperty(SendTrapSNMP.SNMP_MANAGER_HOST, snmpConfiguration.getTargetHost());
+        runner.setProperty(SendTrapSNMP.SNMP_MANAGER_PORT, snmpConfiguration.getTargetPort());
+        runner.setProperty(BasicProperties.SNMP_COMMUNITY, snmpConfiguration.getCommunityString());
+        runner.setProperty(BasicProperties.SNMP_VERSION, getVersionName(snmpConfiguration.getVersion()));
+        runner.setProperty(V2TrapProperties.TRAP_OID_VALUE, trapConfiguration.getTrapOidValue());
+
+        runner.setProperty(V3SecurityProperties.SNMP_SECURITY_LEVEL, snmpConfiguration.getSecurityLevel());
+        runner.setProperty(V3SecurityProperties.SNMP_SECURITY_NAME, snmpConfiguration.getSecurityName());
+        runner.setProperty(V3SecurityProperties.SNMP_AUTH_PROTOCOL, snmpConfiguration.getAuthProtocol());
+        runner.setProperty(V3SecurityProperties.SNMP_AUTH_PASSWORD, snmpConfiguration.getAuthPassphrase());
+        runner.setProperty(V3SecurityProperties.SNMP_PRIVACY_PROTOCOL, snmpConfiguration.getPrivacyProtocol());
+        runner.setProperty(V3SecurityProperties.SNMP_PRIVACY_PASSWORD, snmpConfiguration.getPrivacyPassphrase());
+
+        final MockFlowFile flowFile = getFlowFile(oid, oidValue);
+        runner.enqueue(flowFile);
+
+        return runner;
+    }
+
+    @Override
+    public TestRunner createSnmpListenTrapTestRunner(int managerPort) {
+        final TestRunner runner = TestRunners.newTestRunner(ListenTrapSNMP.class);
+        final SNMPConfiguration snmpConfiguration = snmpV3ConfigurationFactory.createSnmpListenTrapConfig(managerPort);
+        runner.setProperty(ListenTrapSNMP.SNMP_MANAGER_PORT, String.valueOf(snmpConfiguration.getManagerPort()));
+        runner.setProperty(BasicProperties.SNMP_VERSION, getVersionName(snmpConfiguration.getVersion()));
+        runner.setProperty(ListenTrapSNMP.SNMP_USM_USERS_FILE_PATH, USM_USERS_FILE_PATH);
+
+        return runner;
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/GetSNMPHandlerTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/GetSNMPHandlerTest.java
new file mode 100644
index 0000000..8c98ec1
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/GetSNMPHandlerTest.java
@@ -0,0 +1,271 @@
+/*
+ * 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.snmp.operations;
+
+import org.apache.nifi.snmp.dto.SNMPSingleResponse;
+import org.apache.nifi.snmp.dto.SNMPTreeResponse;
+import org.apache.nifi.snmp.exception.RequestTimeoutException;
+import org.apache.nifi.snmp.exception.SNMPWalkException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.snmp4j.PDU;
+import org.snmp4j.Snmp;
+import org.snmp4j.Target;
+import org.snmp4j.event.ResponseEvent;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+import org.snmp4j.smi.VariableBinding;
+import org.snmp4j.util.TreeEvent;
+import org.snmp4j.util.TreeUtils;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.apache.nifi.snmp.operations.GetSNMPHandler.EMPTY_SUBTREE_EXCEPTION_MESSAGE;
+import static org.apache.nifi.snmp.operations.GetSNMPHandler.LEAF_ELEMENT_EXCEPTION_MESSAGE;
+import static org.apache.nifi.snmp.operations.GetSNMPHandler.SNMP_ERROR_EXCEPTION_MESSAGE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class GetSNMPHandlerTest {
+
+    private static final String OID = "1.3.6.1.4.1.343";
+
+    private Target mockTarget;
+    private Snmp mockSnmpManager;
+    private SNMPResourceHandler snmpResourceHandler;
+
+    @Before
+    public void init() {
+        mockTarget = mock(Target.class);
+        mockSnmpManager = mock(Snmp.class);
+        snmpResourceHandler = new SNMPResourceHandler(mockSnmpManager, mockTarget);
+    }
+
+    @Test
+    public void testGetSnmpWithEmptyFlowFile() throws IOException {
+        final ResponseEvent mockResponseEvent = mock(ResponseEvent.class);
+        final PDU mockPdu = mock(PDU.class);
+        when(mockResponseEvent.getResponse()).thenReturn(mockPdu);
+        when(mockSnmpManager.get(any(PDU.class), any(Target.class))).thenReturn(mockResponseEvent);
+
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        getSNMPHandler.get(OID);
+
+        ArgumentCaptor<PDU> captor = ArgumentCaptor.forClass(PDU.class);
+        Mockito.verify(mockSnmpManager).get(captor.capture(), any(Target.class));
+
+        final PDU pdu = captor.getValue();
+        assertEquals(1, pdu.getVariableBindings().size());
+        assertEquals(OID, pdu.getVariableBindings().get(0).getOid().toString());
+    }
+
+    @Test
+    public void testGetSnmpWithInvalidFlowFile() throws IOException {
+        final Map<String, String> invalidFlowFileAttributes = new HashMap<>();
+        invalidFlowFileAttributes.put("invalid", "flowfile attribute");
+
+        final ResponseEvent mockResponseEvent = mock(ResponseEvent.class);
+        final PDU mockPdu = mock(PDU.class);
+        when(mockResponseEvent.getResponse()).thenReturn(mockPdu);
+        when(mockSnmpManager.get(any(PDU.class), any(Target.class))).thenReturn(mockResponseEvent);
+
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        final Optional<SNMPSingleResponse> optionalResponse = getSNMPHandler.get(invalidFlowFileAttributes);
+
+        assertFalse(optionalResponse.isPresent());
+    }
+
+    @Test
+    public void testGetSnmpWithValidFlowFile() throws IOException {
+        final String flowFileOid = "1.3.6.1.2.1.1.1.0";
+        final Map<String, String> flowFileAttributes = new HashMap<>();
+        flowFileAttributes.put("snmp$" + flowFileOid, "OID value");
+
+        final ResponseEvent mockResponseEvent = mock(ResponseEvent.class);
+        final PDU mockPdu = mock(PDU.class);
+        when(mockResponseEvent.getResponse()).thenReturn(mockPdu);
+        when(mockSnmpManager.get(any(PDU.class), any(Target.class))).thenReturn(mockResponseEvent);
+
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        getSNMPHandler.get(flowFileAttributes);
+
+        ArgumentCaptor<PDU> captor = ArgumentCaptor.forClass(PDU.class);
+        Mockito.verify(mockSnmpManager).get(captor.capture(), any(Target.class));
+
+        final PDU pdu = captor.getValue();
+        assertEquals(1, pdu.getVariableBindings().size());
+        assertEquals(flowFileOid, pdu.getVariableBindings().get(0).getOid().toString());
+        assertEquals("Null", pdu.getVariableBindings().get(0).getVariable().toString());
+    }
+
+    @Test
+    public void testGetSnmpWhenTimeout() throws IOException {
+        final ResponseEvent mockResponseEvent = mock(ResponseEvent.class);
+        when(mockResponseEvent.getResponse()).thenReturn(null);
+        when(mockSnmpManager.get(any(PDU.class), any(Target.class))).thenReturn(mockResponseEvent);
+
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+
+        final RequestTimeoutException requestTimeoutException = Assert.assertThrows(
+                RequestTimeoutException.class,
+                () -> getSNMPHandler.get(OID)
+        );
+
+        assertEquals(String.format(SNMPResourceHandler.REQUEST_TIMEOUT_EXCEPTION_TEMPLATE, "read"),
+                requestTimeoutException.getMessage());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testWalkSnmpWithEmptyFlowFile() {
+        final TreeUtils mockTreeUtils = mock(TreeUtils.class);
+        final TreeEvent mockTreeEvent = mock(TreeEvent.class);
+        final List<TreeEvent> mockSubtree = (List<TreeEvent>) mock(List.class);
+
+        final VariableBinding[] variableBindings = new VariableBinding[1];
+        variableBindings[0] = new VariableBinding(new OID(OID), new OctetString("OID value"));
+        when(mockTreeEvent.getVariableBindings()).thenReturn(variableBindings);
+        when(mockSubtree.get(0)).thenReturn(mockTreeEvent);
+        when(mockTreeUtils.getSubtree(mockTarget, new OID(OID))).thenReturn(mockSubtree);
+
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        getSNMPHandler.setTreeUtils(mockTreeUtils);
+        getSNMPHandler.walk(OID);
+
+        ArgumentCaptor<OID> captor = ArgumentCaptor.forClass(OID.class);
+        Mockito.verify(mockTreeUtils).getSubtree(any(Target.class), captor.capture());
+
+        assertEquals(OID, captor.getValue().toString());
+    }
+
+    @Test
+    public void testWalkSnmpWithInvalidFlowFile() {
+        final Map<String, String> invalidFlowFileAttributes = new HashMap<>();
+        invalidFlowFileAttributes.put("invalid", "flowfile attribute");
+
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        final Optional<SNMPTreeResponse> optionalResponse = getSNMPHandler.walk(invalidFlowFileAttributes);
+
+        assertFalse(optionalResponse.isPresent());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testWalkSnmpWithValidFlowFile() {
+        final String flowFileOid = "1.3.6.1.2.1.1.1.0";
+        final Map<String, String> flowFileAttributes = new HashMap<>();
+        flowFileAttributes.put("snmp$" + flowFileOid, "OID value");
+
+        final TreeUtils mockTreeUtils = mock(TreeUtils.class);
+        final TreeEvent mockTreeEvent = mock(TreeEvent.class);
+        final List<TreeEvent> mockSubtree = (List<TreeEvent>) mock(List.class);
+
+        final VariableBinding[] variableBindings = new VariableBinding[1];
+        variableBindings[0] = new VariableBinding(new OID(OID), new OctetString("OID value"));
+        when(mockTreeEvent.getVariableBindings()).thenReturn(variableBindings);
+        when(mockSubtree.get(0)).thenReturn(mockTreeEvent);
+        when(mockSubtree.isEmpty()).thenReturn(false);
+        when(mockTreeUtils.walk(any(Target.class), any(OID[].class))).thenReturn(mockSubtree);
+
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        getSNMPHandler.setTreeUtils(mockTreeUtils);
+        getSNMPHandler.walk(flowFileAttributes);
+
+        ArgumentCaptor<OID[]> captor = ArgumentCaptor.forClass(OID[].class);
+        Mockito.verify(mockTreeUtils).walk(any(Target.class), captor.capture());
+
+        assertEquals(flowFileOid, captor.getValue()[0].toString());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testWalkSnmpWithEmptySubtreeThrowsException() {
+        final TreeUtils mockTreeUtils = mock(TreeUtils.class);
+        final List<TreeEvent> mockSubtree = (List<TreeEvent>) mock(List.class);
+
+        when(mockSubtree.isEmpty()).thenReturn(true);
+        when(mockTreeUtils.getSubtree(any(Target.class), any(org.snmp4j.smi.OID.class))).thenReturn(mockSubtree);
+
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        getSNMPHandler.setTreeUtils(mockTreeUtils);
+
+        final SNMPWalkException snmpWalkException = Assert.assertThrows(
+                SNMPWalkException.class,
+                () -> getSNMPHandler.walk(OID)
+        );
+
+        assertEquals(String.format(EMPTY_SUBTREE_EXCEPTION_MESSAGE, OID), snmpWalkException.getMessage());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testWalkSnmpWithSubtreeErrorThrowsException() {
+        final TreeUtils mockTreeUtils = mock(TreeUtils.class);
+        final TreeEvent mockTreeEvent = mock(TreeEvent.class);
+        final List<TreeEvent> mockSubtree = (List<TreeEvent>) mock(List.class);
+
+        when(mockSubtree.get(0)).thenReturn(mockTreeEvent);
+        when(mockSubtree.isEmpty()).thenReturn(false);
+        when(mockSubtree.size()).thenReturn(1);
+        when(mockTreeUtils.getSubtree(any(Target.class), any(org.snmp4j.smi.OID.class))).thenReturn(mockSubtree);
+
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        getSNMPHandler.setTreeUtils(mockTreeUtils);
+
+        final SNMPWalkException snmpWalkException = Assert.assertThrows(
+                SNMPWalkException.class,
+                () -> getSNMPHandler.walk(OID)
+        );
+
+        assertEquals(SNMP_ERROR_EXCEPTION_MESSAGE, snmpWalkException.getMessage());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testWalkSnmpWithLeafElementSubtreeThrowsException() {
+        final TreeUtils mockTreeUtils = mock(TreeUtils.class);
+        final TreeEvent mockTreeEvent = mock(TreeEvent.class);
+        final List<TreeEvent> mockSubtree = (List<TreeEvent>) mock(List.class);
+
+        final VariableBinding[] variableBindings = new VariableBinding[0];
+        when(mockTreeEvent.getVariableBindings()).thenReturn(variableBindings);
+        when(mockSubtree.get(0)).thenReturn(mockTreeEvent);
+        when(mockSubtree.isEmpty()).thenReturn(false);
+        when(mockSubtree.size()).thenReturn(1);
+        when(mockTreeUtils.getSubtree(any(Target.class), any(org.snmp4j.smi.OID.class))).thenReturn(mockSubtree);
+
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        getSNMPHandler.setTreeUtils(mockTreeUtils);
+
+        final SNMPWalkException snmpWalkException = Assert.assertThrows(
+                SNMPWalkException.class,
+                () -> getSNMPHandler.walk(OID)
+        );
+
+        assertEquals(String.format(LEAF_ELEMENT_EXCEPTION_MESSAGE, OID), snmpWalkException.getMessage());
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPRequestTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPRequestTest.java
index a3ca322..1b1e257 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPRequestTest.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPRequestTest.java
@@ -16,45 +16,67 @@
  */
 package org.apache.nifi.snmp.operations;
 
+import org.apache.nifi.snmp.configuration.SNMPConfiguration;
 import org.apache.nifi.snmp.dto.SNMPSingleResponse;
 import org.apache.nifi.snmp.dto.SNMPTreeResponse;
-import org.apache.nifi.snmp.helper.SNMPTestUtils;
+import org.apache.nifi.snmp.dto.SNMPValue;
+import org.apache.nifi.snmp.exception.RequestTimeoutException;
+import org.apache.nifi.snmp.factory.core.SNMPFactoryProvider;
+import org.apache.nifi.snmp.helper.configurations.SNMPConfigurationFactory;
+import org.apache.nifi.snmp.helper.configurations.SNMPV1V2cConfigurationFactory;
+import org.apache.nifi.snmp.helper.configurations.SNMPV3ConfigurationFactory;
 import org.apache.nifi.snmp.testagents.TestAgent;
-import org.apache.nifi.util.MockFlowFile;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.snmp4j.CommunityTarget;
-import org.snmp4j.Snmp;
+import org.apache.nifi.snmp.testagents.TestSNMPV1Agent;
+import org.apache.nifi.snmp.testagents.TestSNMPV2cAgent;
+import org.apache.nifi.snmp.testagents.TestSNMPV3Agent;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 import org.snmp4j.agent.mo.DefaultMOFactory;
 import org.snmp4j.agent.mo.MOAccessImpl;
+import org.snmp4j.mp.SnmpConstants;
 import org.snmp4j.smi.OID;
 import org.snmp4j.smi.OctetString;
 
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.fail;
 
-public abstract class SNMPRequestTest {
-
-    protected static final String LOCALHOST = "127.0.0.1";
-    protected static final String INVALID_HOST = "127.0.0.2";
-    protected static final String READ_ONLY_OID_1 = "1.3.6.1.4.1.32437.1.5.1.4.2.0";
-    protected static final String READ_ONLY_OID_2 = "1.3.6.1.4.1.32437.1.5.1.4.3.0";
-    protected static final String WRITE_ONLY_OID = "1.3.6.1.4.1.32437.1.5.1.4.4.0";
-    protected static final String READ_ONLY_OID_VALUE_1 = "TestOID1";
-    protected static final String READ_ONLY_OID_VALUE_2 = "TestOID2";
-    protected static final String WRITE_ONLY_OID_VALUE = "writeOnlyOID";
-    protected static final String SNMP_PROP_DELIMITER = "$";
-    protected static final String SNMP_PROP_PREFIX = "snmp" + SNMP_PROP_DELIMITER;
-    protected static final String NOT_WRITABLE = "Not writable";
-    protected static final String NO_ACCESS = "No access";
-    protected static final String SUCCESS = "Success";
-    protected static final String EXPECTED_OID_VALUE = "testValue";
+@RunWith(Parameterized.class)
+public class SNMPRequestTest {
+
+    private static final String LOCALHOST = "127.0.0.1";
+    private static final String INVALID_HOST = "127.0.0.2";
+    private static final String READ_ONLY_OID_1 = "1.3.6.1.4.1.32437.1.5.1.4.2.0";
+    private static final String READ_ONLY_OID_2 = "1.3.6.1.4.1.32437.1.5.1.4.3.0";
+    private static final String WRITE_ONLY_OID = "1.3.6.1.4.1.32437.1.5.1.4.4.0";
+    private static final String WALK_OID = "1.3.6.1.4.1.32437";
+    private static final String INVALID_OID = "1.3.6.1.4.1.32437.0";
+    private static final String READ_ONLY_OID_VALUE_1 = "TestOID1";
+    private static final String READ_ONLY_OID_VALUE_2 = "TestOID2";
+    private static final String WRITE_ONLY_OID_VALUE = "writeOnlyOID";
+    private static final String SNMP_PROP_DELIMITER = "$";
+    private static final String SNMP_PROP_PREFIX = "snmp" + SNMP_PROP_DELIMITER;
+    private static final String NOT_WRITABLE = "Not writable";
+    private static final String NO_ACCESS = "No access";
+    private static final String SUCCESS = "Success";
+    private static final String NO_SUCH_OBJECT = "noSuchObject";
+    private static final String UNABLE_TO_CREATE_OBJECT = "Unable to create object";
+    private static final String TEST_OID_VALUE = "testValue";
+    private static final String NO_SUCH_NAME = "No such name";
     protected static final Map<String, String> WALK_OID_MAP;
 
     static {
@@ -64,53 +86,217 @@ public abstract class SNMPRequestTest {
         WALK_OID_MAP = Collections.unmodifiableMap(oidMap);
     }
 
-    protected final TestAgent agent = getAgentInstance();
+    private static final SNMPConfigurationFactory snmpV1ConfigurationFactory = new SNMPV1V2cConfigurationFactory(SnmpConstants.version1);
+    private static final SNMPConfigurationFactory snmpv2cConfigurationFactory = new SNMPV1V2cConfigurationFactory(SnmpConstants.version2c);
+    private static final SNMPConfigurationFactory snmpv3ConfigurationFactory = new SNMPV3ConfigurationFactory();
+
+    private static final TestAgent v1TestAgent = new TestSNMPV1Agent(LOCALHOST);
+    private static final TestAgent v2cTestAgent = new TestSNMPV2cAgent(LOCALHOST);
+    private static final TestAgent v3TestAgent = new TestSNMPV3Agent(LOCALHOST);
 
-    protected abstract TestAgent getAgentInstance();
+    private SNMPResourceHandler snmpResourceHandler;
 
-    @BeforeEach
+    static {
+        registerManagedObjects(v1TestAgent);
+        registerManagedObjects(v2cTestAgent);
+        registerManagedObjects(v3TestAgent);
+    }
+
+    @Before
     public void initAgent() throws IOException {
         agent.start();
-        agent.registerManagedObjects(
-                DefaultMOFactory.getInstance().createScalar(new OID(READ_ONLY_OID_1), MOAccessImpl.ACCESS_READ_ONLY, new OctetString(READ_ONLY_OID_VALUE_1)),
-                DefaultMOFactory.getInstance().createScalar(new OID(READ_ONLY_OID_2), MOAccessImpl.ACCESS_READ_ONLY, new OctetString(READ_ONLY_OID_VALUE_2)),
-                DefaultMOFactory.getInstance().createScalar(new OID(WRITE_ONLY_OID), MOAccessImpl.ACCESS_WRITE_ONLY, new OctetString(WRITE_ONLY_OID_VALUE))
-        );
     }
 
-    @AfterEach
+    @After
     public void tearDown() {
         agent.stop();
+        agent.unregister();
+        snmpResourceHandler.close();
     }
 
-    protected SNMPTreeResponse getTreeEvents(final int port, final int version) throws IOException {
-        final Snmp snmp = SNMPTestUtils.createSnmpClient();
-        final CommunityTarget target = SNMPTestUtils.createCommTarget("public", LOCALHOST + "/" + port, version);
-        final StandardSNMPRequestHandler standardSnmpRequestHandler = new StandardSNMPRequestHandler(snmp, target);
-        return standardSnmpRequestHandler.walk("1.3.6.1.4.1.32437");
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                {SnmpConstants.version1, snmpV1ConfigurationFactory, v1TestAgent, NO_SUCH_NAME, NO_SUCH_NAME, NO_SUCH_NAME, NO_SUCH_NAME},
+                {SnmpConstants.version2c, snmpv2cConfigurationFactory, v2cTestAgent, NOT_WRITABLE, NO_ACCESS, NO_SUCH_OBJECT, UNABLE_TO_CREATE_OBJECT},
+                {SnmpConstants.version3, snmpv3ConfigurationFactory, v3TestAgent, NOT_WRITABLE, NO_ACCESS, NO_SUCH_OBJECT, UNABLE_TO_CREATE_OBJECT}
+        });
     }
 
-    protected SNMPSingleResponse getResponseEvent(final String address, final int port, final int version, final String oid) throws IOException {
-        final Snmp snmp = SNMPTestUtils.createSnmpClient();
-        final CommunityTarget target = SNMPTestUtils.createCommTarget("public", address + "/" + port, version);
-        final StandardSNMPRequestHandler standardSnmpRequestHandler = new StandardSNMPRequestHandler(snmp, target);
-        return standardSnmpRequestHandler.get(oid);
+    private final int version;
+    private final SNMPConfigurationFactory snmpConfigurationFactory;
+    private final TestAgent agent;
+    private final String cannotSetReadOnlyOidStatusMessage;
+    private final String cannotModifyOidStatusMessage;
+    private final String getInvalidOidStatusMessage;
+    private final String setInvalidOidStatusMessage;
+
+    public SNMPRequestTest(final int version, final SNMPConfigurationFactory snmpConfigurationFactory, final TestAgent agent,
+                           final String cannotSetReadOnlyOidStatusMessage, final String cannotModifyOidStatusMessage,
+                           final String getInvalidOidStatusMessage, final String setInvalidOidStatusMessage) {
+        this.version = version;
+        this.snmpConfigurationFactory = snmpConfigurationFactory;
+        this.agent = agent;
+        this.cannotSetReadOnlyOidStatusMessage = cannotSetReadOnlyOidStatusMessage;
+        this.cannotModifyOidStatusMessage = cannotModifyOidStatusMessage;
+        this.getInvalidOidStatusMessage = getInvalidOidStatusMessage;
+        this.setInvalidOidStatusMessage = setInvalidOidStatusMessage;
     }
 
-    protected SNMPSingleResponse getSetResponse(final int port, final int version, final String oid, final String expectedOid) throws IOException {
-        final Snmp snmp = SNMPTestUtils.createSnmpClient();
-        final CommunityTarget target = SNMPTestUtils.createCommTarget("public", LOCALHOST + "/" + port, version);
-        final StandardSNMPRequestHandler standardSnmpRequestHandler = new StandardSNMPRequestHandler(snmp, target);
+    @Test
+    public void testSuccessfulSnmpGet() throws IOException {
+        final SNMPConfiguration snmpConfiguration = snmpConfigurationFactory.createSnmpGetSetConfiguration(agent.getPort());
+        snmpResourceHandler = SNMPFactoryProvider.getFactory(version).createSNMPResourceHandler(snmpConfiguration);
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        final SNMPSingleResponse response = getSNMPHandler.get(READ_ONLY_OID_1);
+        assertEquals(READ_ONLY_OID_VALUE_1, response.getVariableBindings().get(0).getVariable());
+        assertEquals(SUCCESS, response.getErrorStatusText());
+
+    }
+
+    @Test
+    public void testSuccessfulSnmpGetWithFlowFileInput() throws IOException {
+        final SNMPConfiguration snmpConfiguration = snmpConfigurationFactory.createSnmpGetSetConfiguration(agent.getPort());
+        snmpResourceHandler = SNMPFactoryProvider.getFactory(version).createSNMPResourceHandler(snmpConfiguration);
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        final Optional<SNMPSingleResponse> optionalResponse = getSNMPHandler.get(getFlowFileAttributesForSnmpGet(READ_ONLY_OID_1, READ_ONLY_OID_2));
+        if (optionalResponse.isPresent()) {
+            final SNMPSingleResponse response = optionalResponse.get();
+            Set<String> expectedVariables = new HashSet<>(Arrays.asList(READ_ONLY_OID_VALUE_1, READ_ONLY_OID_VALUE_2));
+            Set<String> actualVariables = response.getVariableBindings().stream().map(SNMPValue::getVariable).collect(Collectors.toSet());
+            assertEquals(expectedVariables, actualVariables);
+            assertEquals(SUCCESS, response.getErrorStatusText());
+        } else {
+            fail("Response is not present.");
+        }
+    }
+
+    @Test
+    public void testSuccessfulSnmpWalk() {
+        final SNMPConfiguration snmpConfiguration = snmpConfigurationFactory.createSnmpGetSetConfiguration(agent.getPort());
+        snmpResourceHandler = SNMPFactoryProvider.getFactory(version).createSNMPResourceHandler(snmpConfiguration);
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        final SNMPTreeResponse response = getSNMPHandler.walk(WALK_OID);
+
+        assertSubTreeContainsOids(response);
+    }
+
+    @Test(expected = RequestTimeoutException.class)
+    public void testSnmpGetTimeoutReturnsNull() throws IOException {
+        final SNMPConfiguration snmpConfiguration = snmpConfigurationFactory.createSnmpGetSetConfigWithCustomHost(INVALID_HOST, agent.getPort());
+        snmpResourceHandler = SNMPFactoryProvider.getFactory(version).createSNMPResourceHandler(snmpConfiguration);
+
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        getSNMPHandler.get(READ_ONLY_OID_1);
+    }
+
+    @Test
+    public void testSnmpGetInvalidOidWithFlowFileInput() throws IOException {
+        final SNMPConfiguration snmpConfiguration = snmpConfigurationFactory.createSnmpGetSetConfiguration(agent.getPort());
+        snmpResourceHandler = SNMPFactoryProvider.getFactory(version).createSNMPResourceHandler(snmpConfiguration);
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        final Optional<SNMPSingleResponse> optionalResponse = getSNMPHandler.get(getFlowFileAttributesForSnmpGet(INVALID_OID, READ_ONLY_OID_2));
+        if (optionalResponse.isPresent()) {
+            final SNMPSingleResponse response = optionalResponse.get();
+            if (version == SnmpConstants.version1) {
+                assertEquals("Null", response.getVariableBindings().get(1).getVariable());
+                assertEquals(READ_ONLY_OID_VALUE_2, response.getVariableBindings().get(0).getVariable());
+                assertEquals(NO_SUCH_NAME, response.getErrorStatusText());
+            } else {
+                assertEquals(NO_SUCH_OBJECT, response.getVariableBindings().get(1).getVariable());
+                assertEquals(READ_ONLY_OID_VALUE_2, response.getVariableBindings().get(0).getVariable());
+                assertEquals(SUCCESS, response.getErrorStatusText());
+            }
+        } else {
+            fail("Response is not present.");
+        }
+    }
 
-        final MockFlowFile flowFile = new MockFlowFile(1L);
+    @Test
+    public void testSuccessfulSnmpSet() throws IOException {
+        final Map<String, String> flowFileAttributes = getFlowFileAttributes(WRITE_ONLY_OID);
+
+        final SNMPConfiguration snmpConfiguration = snmpConfigurationFactory.createSnmpGetSetConfiguration(agent.getPort());
+        snmpResourceHandler = SNMPFactoryProvider.getFactory(version).createSNMPResourceHandler(snmpConfiguration);
+        final SetSNMPHandler setSNMPHandler = new SetSNMPHandler(snmpResourceHandler);
+        final Optional<SNMPSingleResponse> optionalResponse = setSNMPHandler.set(flowFileAttributes);
+        if (optionalResponse.isPresent()) {
+            final SNMPSingleResponse response = optionalResponse.get();
+            assertEquals(TEST_OID_VALUE, response.getVariableBindings().get(0).getVariable());
+            assertEquals(SUCCESS, response.getErrorStatusText());
+        } else {
+            fail("Response is not present.");
+        }
+    }
+
+    @Test
+    public void testCannotSetReadOnlyObject() throws IOException {
+        final Map<String, String> flowFileAttributes = getFlowFileAttributes(READ_ONLY_OID_1);
+
+        final SNMPConfiguration snmpConfiguration = snmpConfigurationFactory.createSnmpGetSetConfiguration(agent.getPort());
+        snmpResourceHandler = SNMPFactoryProvider.getFactory(version).createSNMPResourceHandler(snmpConfiguration);
+        final SetSNMPHandler setSNMPHandler = new SetSNMPHandler(snmpResourceHandler);
+        final Optional<SNMPSingleResponse> optionalResponse = setSNMPHandler.set(flowFileAttributes);
+        if (optionalResponse.isPresent()) {
+            final SNMPSingleResponse response = optionalResponse.get();
+            assertEquals(cannotSetReadOnlyOidStatusMessage, response.getErrorStatusText());
+        } else {
+            fail("Response is not present.");
+        }
+    }
+
+    @Test
+    public void testCannotGetWriteOnlyObject() throws IOException {
+        final SNMPConfiguration snmpConfiguration = snmpConfigurationFactory.createSnmpGetSetConfiguration(agent.getPort());
+        snmpResourceHandler = SNMPFactoryProvider.getFactory(version).createSNMPResourceHandler(snmpConfiguration);
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        final SNMPSingleResponse response = getSNMPHandler.get(WRITE_ONLY_OID);
+
+        assertEquals(cannotModifyOidStatusMessage, response.getErrorStatusText());
+    }
+
+    @Test
+    public void testCannotGetInvalidOid() throws IOException {
+        final SNMPConfiguration snmpConfiguration = snmpConfigurationFactory.createSnmpGetSetConfiguration(agent.getPort());
+        snmpResourceHandler = SNMPFactoryProvider.getFactory(version).createSNMPResourceHandler(snmpConfiguration);
+        final GetSNMPHandler getSNMPHandler = new GetSNMPHandler(snmpResourceHandler);
+        final SNMPSingleResponse response = getSNMPHandler.get(INVALID_OID);
+        if (version == SnmpConstants.version1) {
+            assertEquals(getInvalidOidStatusMessage, response.getErrorStatusText());
+        } else {
+            assertEquals(getInvalidOidStatusMessage, response.getVariableBindings().get(0).getVariable());
+            assertEquals(SUCCESS, response.getErrorStatusText());
+        }
+    }
+
+    @Test
+    public void testCannotSetInvalidOid() throws IOException {
+        final Map<String, String> flowFileAttributes = getFlowFileAttributes(INVALID_OID);
+        final SNMPConfiguration snmpConfiguration = snmpConfigurationFactory.createSnmpGetSetConfiguration(agent.getPort());
+        snmpResourceHandler = SNMPFactoryProvider.getFactory(version).createSNMPResourceHandler(snmpConfiguration);
+        final SetSNMPHandler setSNMPHandler = new SetSNMPHandler(snmpResourceHandler);
+        final Optional<SNMPSingleResponse> optionalResponse = setSNMPHandler.set(flowFileAttributes);
+        if (optionalResponse.isPresent()) {
+            final SNMPSingleResponse response = optionalResponse.get();
+            assertEquals(setInvalidOidStatusMessage, response.getErrorStatusText());
+        } else {
+            fail("Response is not present.");
+        }
+    }
+
+    private Map<String, String> getFlowFileAttributes(String oid) {
         final Map<String, String> attributes = new HashMap<>();
-        attributes.put(SNMP_PROP_PREFIX + oid, expectedOid);
-        flowFile.putAttributes(attributes);
+        attributes.put(SNMP_PROP_PREFIX + oid, TEST_OID_VALUE);
+        return attributes;
+    }
 
-        return standardSnmpRequestHandler.set(flowFile);
+    private Map<String, String> getFlowFileAttributesForSnmpGet(String... oids) {
+        final Map<String, String> attributes = new HashMap<>();
+        Arrays.stream(oids).forEach(oid -> attributes.put(SNMP_PROP_PREFIX + oid, null));
+        return attributes;
     }
 
-    protected void assertSubTreeContainsOids(SNMPTreeResponse response) {
+    private void assertSubTreeContainsOids(SNMPTreeResponse response) {
         final Map<String, String> attributes = response.getAttributes();
         attributes.entrySet().forEach(this::checkEntryContainsSubString);
     }
@@ -124,7 +310,15 @@ public abstract class SNMPRequestTest {
             }
         });
         if (!isMatch.get()) {
-            fail("Expected OID did not found in subtree.");
+            fail("Expected OID is not found in subtree.");
         }
     }
+
+    private static void registerManagedObjects(final TestAgent agent) {
+        agent.registerManagedObjects(
+                DefaultMOFactory.getInstance().createScalar(new OID(READ_ONLY_OID_1), MOAccessImpl.ACCESS_READ_ONLY, new OctetString(READ_ONLY_OID_VALUE_1)),
+                DefaultMOFactory.getInstance().createScalar(new OID(READ_ONLY_OID_2), MOAccessImpl.ACCESS_READ_ONLY, new OctetString(READ_ONLY_OID_VALUE_2)),
+                DefaultMOFactory.getInstance().createScalar(new OID(WRITE_ONLY_OID), MOAccessImpl.ACCESS_WRITE_ONLY, new OctetString(WRITE_ONLY_OID_VALUE))
+        );
+    }
 }
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPTrapReceiverHandlerTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPTrapReceiverHandlerTest.java
new file mode 100644
index 0000000..795c4ea
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPTrapReceiverHandlerTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.snmp.operations;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.nifi.processor.ProcessSessionFactory;
+import org.apache.nifi.remote.io.socket.NetworkUtils;
+import org.apache.nifi.snmp.configuration.SNMPConfiguration;
+import org.apache.nifi.snmp.dto.UserDetails;
+import org.apache.nifi.snmp.utils.SNMPUtils;
+import org.apache.nifi.util.MockComponentLog;
+import org.junit.Test;
+import org.snmp4j.Snmp;
+import org.snmp4j.mp.SnmpConstants;
+import org.snmp4j.security.USM;
+import org.snmp4j.security.UsmUser;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SNMPTrapReceiverHandlerTest {
+
+    public static final String USERS_JSON = "src/test/resources/users.json";
+
+    @Test
+    public void testTrapReceiverCreatesCommandResponder() {
+        final SNMPConfiguration snmpConfiguration = mock(SNMPConfiguration.class);
+        final ProcessSessionFactory mockProcessSessionFactory = mock(ProcessSessionFactory.class);
+        final MockComponentLog mockComponentLog = new MockComponentLog("componentId", new Object());
+        final Snmp mockSnmpManager = mock(Snmp.class);
+        when(snmpConfiguration.getManagerPort()).thenReturn(NetworkUtils.availablePort());
+        when(snmpConfiguration.getVersion()).thenReturn(SnmpConstants.version1);
+
+        final SNMPTrapReceiverHandler trapReceiverHandler = new SNMPTrapReceiverHandler(snmpConfiguration, null);
+        trapReceiverHandler.setSnmpManager(mockSnmpManager);
+        trapReceiverHandler.createTrapReceiver(mockProcessSessionFactory, mockComponentLog);
+
+
+        verify(mockSnmpManager).addCommandResponder(any(SNMPTrapReceiver.class));
+
+        assertTrue(trapReceiverHandler.isStarted());
+    }
+
+    @Test
+    public void testCloseTrapReceiverCleansUpResources() throws IOException {
+        final SNMPConfiguration snmpConfiguration = mock(SNMPConfiguration.class);
+        final ProcessSessionFactory mockProcessSessionFactory = mock(ProcessSessionFactory.class);
+        final MockComponentLog mockComponentLog = new MockComponentLog("componentId", new Object());
+        final USM mockUsm = mock(USM.class);
+        final Snmp mockSnmpManager = mock(Snmp.class);
+
+        when(mockSnmpManager.getUSM()).thenReturn(mockUsm);
+        when(snmpConfiguration.getManagerPort()).thenReturn(NetworkUtils.availablePort());
+        when(snmpConfiguration.getVersion()).thenReturn(SnmpConstants.version1);
+
+        final SNMPTrapReceiverHandler trapReceiverHandler = new SNMPTrapReceiverHandler(snmpConfiguration, null);
+        trapReceiverHandler.setSnmpManager(mockSnmpManager);
+        trapReceiverHandler.createTrapReceiver(mockProcessSessionFactory, mockComponentLog);
+        trapReceiverHandler.close();
+
+        verify(mockUsm).removeAllUsers();
+        verify(mockSnmpManager).close();
+
+        assertFalse(trapReceiverHandler.isStarted());
+    }
+
+    @Test
+    public void testAddUsmUsers() throws JsonProcessingException, FileNotFoundException {
+        final Set<String> expectedUserAttributes = new HashSet<>();
+        try (Scanner scanner = new Scanner(new File(USERS_JSON))) {
+            final String content = scanner.useDelimiter("\\Z").next();
+            final ObjectMapper mapper = new ObjectMapper();
+            final List<UserDetails> userDetails = mapper.readValue(content, new TypeReference<List<UserDetails>>() {
+            });
+            userDetails
+                    .forEach(user -> {
+                        expectedUserAttributes.add(user.getSecurityName());
+                        expectedUserAttributes.add(SNMPUtils.getAuth(user.getAuthProtocol()).toString());
+                        expectedUserAttributes.add(user.getAuthPassphrase());
+                        expectedUserAttributes.add(SNMPUtils.getPriv(user.getPrivProtocol()).toString());
+                        expectedUserAttributes.add(user.getPrivPassphrase());
+                    });
+        }
+        final Set<String> usmAttributes = new HashSet<>();
+        final SNMPConfiguration snmpConfiguration = mock(SNMPConfiguration.class);
+        final ProcessSessionFactory mockProcessSessionFactory = mock(ProcessSessionFactory.class);
+        final MockComponentLog mockComponentLog = new MockComponentLog("componentId", new Object());
+        final Snmp mockSnmpManager = mock(Snmp.class);
+        final USM mockUsm = mock(USM.class);
+
+        when(snmpConfiguration.getManagerPort()).thenReturn(NetworkUtils.availablePort());
+        when(snmpConfiguration.getVersion()).thenReturn(SnmpConstants.version3);
+        doAnswer(invocation -> {
+            UsmUser usmUser = (UsmUser) invocation.getArgument(0);
+            usmAttributes.add(usmUser.getSecurityName().toString());
+            usmAttributes.add(usmUser.getAuthenticationProtocol().toString());
+            usmAttributes.add(usmUser.getAuthenticationPassphrase().toString());
+            usmAttributes.add(usmUser.getPrivacyProtocol().toString());
+            usmAttributes.add(usmUser.getPrivacyPassphrase().toString());
+            return null;
+        }).when(mockUsm).addUser(any(UsmUser.class));
+        when(mockSnmpManager.getUSM()).thenReturn(mockUsm);
+
+        final SNMPTrapReceiverHandler trapReceiverHandler = new SNMPTrapReceiverHandler(snmpConfiguration, USERS_JSON);
+        trapReceiverHandler.setSnmpManager(mockSnmpManager);
+        trapReceiverHandler.createTrapReceiver(mockProcessSessionFactory, mockComponentLog);
+
+
+        verify(mockSnmpManager).addCommandResponder(any(SNMPTrapReceiver.class));
+
+        assertTrue(trapReceiverHandler.isStarted());
+
+        assertEquals(expectedUserAttributes, usmAttributes);
+    }
+
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPTrapReceiverTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPTrapReceiverTest.java
new file mode 100644
index 0000000..6b77b85
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPTrapReceiverTest.java
@@ -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.
+ */
+package org.apache.nifi.snmp.operations;
+
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.ProcessSessionFactory;
+import org.apache.nifi.snmp.processors.ListenTrapSNMP;
+import org.apache.nifi.util.LogMessage;
+import org.apache.nifi.util.MockComponentLog;
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.MockProcessSession;
+import org.apache.nifi.util.SharedSessionState;
+import org.junit.Before;
+import org.junit.Test;
+import org.snmp4j.CommandResponderEvent;
+import org.snmp4j.PDU;
+import org.snmp4j.PDUv1;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.VariableBinding;
+
+import java.util.List;
+import java.util.Vector;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SNMPTrapReceiverTest {
+
+    private static final Object MOCK_COMPONENT_ID = new Object();
+
+    private ProcessSessionFactory mockProcessSessionFactory;
+    private MockProcessSession mockProcessSession;
+    private MockComponentLog mockComponentLog;
+    private PDU mockPdu;
+    private SNMPTrapReceiver snmpTrapReceiver;
+
+
+    @Before
+    public void init() {
+        mockProcessSessionFactory = mock(ProcessSessionFactory.class);
+        mockComponentLog = new MockComponentLog("componentId", MOCK_COMPONENT_ID);
+        mockPdu = mock(PDU.class);
+        snmpTrapReceiver = new SNMPTrapReceiver(mockProcessSessionFactory, mockComponentLog);
+        final ListenTrapSNMP listenTrapSNMP = new ListenTrapSNMP();
+        mockProcessSession = new MockProcessSession(new SharedSessionState(listenTrapSNMP, new AtomicLong(0L)), listenTrapSNMP);
+    }
+
+    @Test
+    public void testReceiveTrapWithNullPduLogsError() {
+        CommandResponderEvent mockEvent = mock(CommandResponderEvent.class);
+
+        snmpTrapReceiver.processPdu(mockEvent);
+
+        final LogMessage logMessage = mockComponentLog.getErrorMessages().get(0);
+
+        assertEquals(String.format("%s Request timed out or parameters are incorrect.", MOCK_COMPONENT_ID), logMessage.getMsg());
+    }
+
+    @Test
+    public void testReceiveTrapWithInvalidPduTypeLogsError() {
+        final CommandResponderEvent mockEvent = mock(CommandResponderEvent.class);
+
+        when(mockPdu.getType()).thenReturn(PDU.REPORT);
+        when(mockEvent.getPDU()).thenReturn(mockPdu);
+        snmpTrapReceiver.processPdu(mockEvent);
+
+        final LogMessage logMessage = mockComponentLog.getErrorMessages().get(0);
+
+        assertEquals(String.format("%s Request timed out or parameters are incorrect.", MOCK_COMPONENT_ID), logMessage.getMsg());
+    }
+
+    @Test
+    public void testTrapReceiverCreatesTrapPduV1FlowFile() {
+        final CommandResponderEvent mockEvent = mock(CommandResponderEvent.class);
+        final PDUv1 mockV1Pdu = mock(PDUv1.class);
+
+        when(mockV1Pdu.getType()).thenReturn(PDU.V1TRAP);
+        when(mockV1Pdu.getEnterprise()).thenReturn(new OID("1.3.6.1.2.1.1.1.0"));
+        when(mockV1Pdu.getSpecificTrap()).thenReturn(4);
+        final Vector<VariableBinding> vbs = new Vector<>();
+        doReturn(vbs).when(mockV1Pdu).getVariableBindings();
+        when(mockEvent.getPDU()).thenReturn(mockV1Pdu);
+        when(mockProcessSessionFactory.createSession()).thenReturn(mockProcessSession);
+
+        snmpTrapReceiver.processPdu(mockEvent);
+
+        final List<MockFlowFile> flowFiles = mockProcessSession.getFlowFilesForRelationship(ListenTrapSNMP.REL_SUCCESS);
+        final FlowFile flowFile = flowFiles.get(0);
+
+        assertEquals("1.3.6.1.2.1.1.1.0", flowFile.getAttribute("snmp$enterprise"));
+        assertEquals(String.valueOf(4), flowFile.getAttribute("snmp$specificTrapType"));
+    }
+
+    @Test
+    public void testTrapReceiverCreatesTrapPduV2FlowFile() {
+        final CommandResponderEvent mockEvent = mock(CommandResponderEvent.class);
+
+        when(mockPdu.getType()).thenReturn(PDU.TRAP);
+        when(mockPdu.getErrorIndex()).thenReturn(123);
+        when(mockPdu.getErrorStatusText()).thenReturn("test error status text");
+        final Vector<VariableBinding> vbs = new Vector<>();
+        doReturn(vbs).when(mockPdu).getVariableBindings();
+        when(mockEvent.getPDU()).thenReturn(mockPdu);
+        when(mockProcessSessionFactory.createSession()).thenReturn(mockProcessSession);
+
+        snmpTrapReceiver.processPdu(mockEvent);
+
+        final List<MockFlowFile> flowFiles = mockProcessSession.getFlowFilesForRelationship(ListenTrapSNMP.REL_SUCCESS);
+        final FlowFile flowFile = flowFiles.get(0);
+
+        assertEquals(String.valueOf(123), flowFile.getAttribute("snmp$errorIndex"));
+        assertEquals("test error status text", flowFile.getAttribute("snmp$errorStatusText"));
+    }
+
+    @Test
+    public void testReceiveTrapWithErrorGetsTransferredToFailure() {
+        final CommandResponderEvent mockEvent = mock(CommandResponderEvent.class);
+
+        when(mockPdu.getType()).thenReturn(PDU.TRAP);
+        when(mockPdu.getErrorStatus()).thenReturn(PDU.badValue);
+        final Vector<VariableBinding> vbs = new Vector<>();
+        doReturn(vbs).when(mockPdu).getVariableBindings();
+        when(mockEvent.getPDU()).thenReturn(mockPdu);
+        when(mockProcessSessionFactory.createSession()).thenReturn(mockProcessSession);
+
+        snmpTrapReceiver.processPdu(mockEvent);
+
+        final List<MockFlowFile> flowFiles = mockProcessSession.getFlowFilesForRelationship(ListenTrapSNMP.REL_FAILURE);
+
+        assertFalse(flowFiles.isEmpty());
+        final FlowFile flowFile = flowFiles.get(0);
+
+        assertEquals(String.valueOf(PDU.badValue), flowFile.getAttribute("snmp$errorStatus"));
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPV1RequestTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPV1RequestTest.java
deleted file mode 100644
index 15be017..0000000
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPV1RequestTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.snmp.operations;
-
-import org.apache.nifi.snmp.dto.SNMPSingleResponse;
-import org.apache.nifi.snmp.dto.SNMPTreeResponse;
-import org.apache.nifi.snmp.exception.RequestTimeoutException;
-import org.apache.nifi.snmp.testagents.TestAgent;
-import org.apache.nifi.snmp.testagents.TestSNMPV1Agent;
-import org.junit.jupiter.api.Test;
-import org.snmp4j.MessageException;
-import org.snmp4j.mp.SnmpConstants;
-
-import java.io.IOException;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-public class SNMPV1RequestTest extends SNMPRequestTest {
-
-    private static final String NO_SUCH_NAME = "No such name";
-
-    @Override
-    protected TestAgent getAgentInstance() {
-        return new TestSNMPV1Agent(LOCALHOST);
-    }
-
-    @Test
-    public void testSuccessfulSnmpGet() throws IOException {
-        final SNMPSingleResponse response = getResponseEvent(LOCALHOST, agent.getPort(), SnmpConstants.version1, READ_ONLY_OID_1);
-        assertEquals(READ_ONLY_OID_VALUE_1, response.getVariableBindings().get(0).getVariable());
-        assertEquals(SUCCESS, response.getErrorStatusText());
-    }
-
-    @Test
-    public void testSuccessfulSnmpWalk() throws IOException {
-        final SNMPTreeResponse response = getTreeEvents(agent.getPort(), SnmpConstants.version1);
-        assertSubTreeContainsOids(response);
-    }
-
-    @Test
-    public void testSnmpGetTimeoutReturnsNull() {
-        assertThrows(RequestTimeoutException.class, () -> getResponseEvent(INVALID_HOST, agent.getPort(), SnmpConstants.version1, READ_ONLY_OID_1));
-    }
-
-    @Test
-    public void testSnmpGetWithInvalidTargetThrowsException() {
-        assertThrows(MessageException.class, () -> getResponseEvent(LOCALHOST, agent.getPort(), -1, READ_ONLY_OID_1));
-    }
-
-    @Test
-    public void testSuccessfulSnmpSet() throws IOException {
-        final SNMPSingleResponse response = getSetResponse(agent.getPort(), SnmpConstants.version1, WRITE_ONLY_OID, EXPECTED_OID_VALUE);
-
-        assertEquals(EXPECTED_OID_VALUE, response.getVariableBindings().get(0).getVariable());
-        assertEquals(SUCCESS, response.getErrorStatusText());
-    }
-
-    @Test
-    public void testCannotSetReadOnlyObject() throws IOException {
-        final SNMPSingleResponse response = getSetResponse(agent.getPort(), SnmpConstants.version1, READ_ONLY_OID_1, EXPECTED_OID_VALUE);
-
-        assertEquals(NO_SUCH_NAME, response.getErrorStatusText());
-    }
-
-    @Test
-    public void testCannotGetWriteOnlyObject() throws IOException {
-        final SNMPSingleResponse response = getResponseEvent(LOCALHOST, agent.getPort(), SnmpConstants.version1, WRITE_ONLY_OID);
-
-        assertEquals(NO_SUCH_NAME, response.getErrorStatusText());
-    }
-}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPV2CRequestTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPV2CRequestTest.java
deleted file mode 100644
index c5eeac1..0000000
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPV2CRequestTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.snmp.operations;
-
-import org.apache.nifi.snmp.dto.SNMPSingleResponse;
-import org.apache.nifi.snmp.dto.SNMPTreeResponse;
-import org.apache.nifi.snmp.exception.RequestTimeoutException;
-import org.apache.nifi.snmp.testagents.TestAgent;
-import org.apache.nifi.snmp.testagents.TestSNMPV2cAgent;
-import org.junit.jupiter.api.Test;
-import org.snmp4j.MessageException;
-import org.snmp4j.mp.SnmpConstants;
-
-import java.io.IOException;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-public class SNMPV2CRequestTest extends SNMPRequestTest {
-
-    @Override
-    protected TestAgent getAgentInstance() {
-        return new TestSNMPV2cAgent(LOCALHOST);
-    }
-
-    @Test
-    public void testSuccessfulSnmpGet() throws IOException {
-        final SNMPSingleResponse response = getResponseEvent(LOCALHOST, agent.getPort(), SnmpConstants.version2c, READ_ONLY_OID_1);
-
-        assertEquals(READ_ONLY_OID_VALUE_1, response.getVariableBindings().get(0).getVariable());
-        assertEquals(SUCCESS, response.getErrorStatusText());
-    }
-
-    @Test
-    public void testSuccessfulSnmpWalk() throws IOException {
-        final SNMPTreeResponse response = getTreeEvents(agent.getPort(), SnmpConstants.version2c);
-
-        assertSubTreeContainsOids(response);
-    }
-
-    @Test
-    public void testSnmpGetTimeoutReturnsNull() {
-        assertThrows(RequestTimeoutException.class, () -> getResponseEvent(INVALID_HOST, agent.getPort(), SnmpConstants.version2c, READ_ONLY_OID_1));
-    }
-
-    @Test
-    public void testSnmpGetWithInvalidTargetThrowsException() {
-        assertThrows(MessageException.class, () -> getResponseEvent(LOCALHOST, agent.getPort(), -1, READ_ONLY_OID_1));
-    }
-
-    @Test
-    public void testSuccessfulSnmpSet() throws IOException {
-        final SNMPSingleResponse response = getSetResponse(agent.getPort(), SnmpConstants.version2c, WRITE_ONLY_OID, EXPECTED_OID_VALUE);
-
-        assertEquals(EXPECTED_OID_VALUE, response.getVariableBindings().get(0).getVariable());
-        assertEquals(SUCCESS, response.getErrorStatusText());
-    }
-
-    @Test
-    public void testCannotSetReadOnlyObject() throws IOException {
-        final SNMPSingleResponse response = getSetResponse(agent.getPort(), SnmpConstants.version2c, READ_ONLY_OID_1, EXPECTED_OID_VALUE);
-
-        assertEquals(NOT_WRITABLE, response.getErrorStatusText());
-    }
-
-    @Test
-    public void testCannotGetWriteOnlyObject() throws IOException {
-        final SNMPSingleResponse response = getResponseEvent(LOCALHOST, agent.getPort(), SnmpConstants.version2c, WRITE_ONLY_OID);
-
-        assertEquals(NO_ACCESS, response.getErrorStatusText());
-    }
-}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPV3RequestTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPV3RequestTest.java
deleted file mode 100644
index 1586862..0000000
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SNMPV3RequestTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.snmp.operations;
-
-import org.apache.nifi.snmp.dto.SNMPSingleResponse;
-import org.apache.nifi.snmp.dto.SNMPTreeResponse;
-import org.apache.nifi.snmp.exception.RequestTimeoutException;
-import org.apache.nifi.snmp.helper.SNMPTestUtils;
-import org.apache.nifi.snmp.testagents.TestAgent;
-import org.apache.nifi.snmp.testagents.TestSNMPV3Agent;
-import org.apache.nifi.util.MockFlowFile;
-import org.junit.jupiter.api.Test;
-import org.snmp4j.MessageException;
-import org.snmp4j.SNMP4JSettings;
-import org.snmp4j.Snmp;
-import org.snmp4j.UserTarget;
-import org.snmp4j.mp.SnmpConstants;
-import org.snmp4j.security.AuthSHA;
-import org.snmp4j.security.SecurityLevel;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-public class SNMPV3RequestTest extends SNMPRequestTest {
-
-    @Override
-    protected TestAgent getAgentInstance() {
-        return new TestSNMPV3Agent(LOCALHOST);
-    }
-
-    @Test
-    public void testSuccessfulSnmpGet() throws IOException {
-        final StandardSNMPRequestHandler standardSnmpRequestHandler = getSnmpV3Getter(LOCALHOST, SnmpConstants.version3,
-                "SHA", "SHAAuthPassword");
-        final SNMPSingleResponse response = standardSnmpRequestHandler.get(READ_ONLY_OID_1);
-        assertEquals(READ_ONLY_OID_VALUE_1, response.getVariableBindings().get(0).getVariable());
-        assertEquals(SUCCESS, response.getErrorStatusText());
-    }
-
-    @Test
-    public void testSuccessfulSnmpWalk() throws IOException {
-        final StandardSNMPRequestHandler standardSnmpRequestHandler = getSnmpV3Getter(LOCALHOST, SnmpConstants.version3,
-                "SHA", "SHAAuthPassword");
-        final SNMPTreeResponse response = standardSnmpRequestHandler.walk("1.3.6.1.4.1.32437");
-
-        assertSubTreeContainsOids(response);
-    }
-
-    @Test
-    public void testSnmpGetTimeoutReturnsNull() throws IOException {
-        final StandardSNMPRequestHandler standardSnmpRequestHandler = getSnmpV3Getter(INVALID_HOST, SnmpConstants.version3,
-                "SHA", "SHAAuthPassword");
-        assertThrows(RequestTimeoutException.class, () -> standardSnmpRequestHandler.get(READ_ONLY_OID_1));
-    }
-
-    @Test
-    public void testSnmpGetWithInvalidTargetThrowsException() throws IOException {
-        final StandardSNMPRequestHandler standardSnmpRequestHandler = getSnmpV3Getter(LOCALHOST, -1, "SHA", "SHAAuthPassword");
-        assertThrows(MessageException.class, () -> standardSnmpRequestHandler.get(READ_ONLY_OID_1));
-    }
-
-    @Test
-    public void testSuccessfulSnmpSet() throws IOException {
-        final StandardSNMPRequestHandler standardSnmpRequestHandler = getSnmpV3Getter(LOCALHOST, SnmpConstants.version3,
-                "SHA", "SHAAuthPassword");
-
-        final MockFlowFile flowFile = new MockFlowFile(1L);
-        final Map<String, String> attributes = new HashMap<>();
-        attributes.put(SNMP_PROP_PREFIX + WRITE_ONLY_OID, EXPECTED_OID_VALUE);
-        flowFile.putAttributes(attributes);
-
-        final SNMPSingleResponse response = standardSnmpRequestHandler.set(flowFile);
-
-        assertEquals(EXPECTED_OID_VALUE, response.getVariableBindings().get(0).getVariable());
-
-    }
-
-    @Test
-    public void testCannotSetReadOnlyObject() throws IOException {
-        final StandardSNMPRequestHandler standardSnmpRequestHandler = getSnmpV3Getter(LOCALHOST, SnmpConstants.version3,
-                "SHA", "SHAAuthPassword");
-
-        final MockFlowFile flowFile = new MockFlowFile(1L);
-        final Map<String, String> attributes = new HashMap<>();
-        attributes.put(SNMP_PROP_PREFIX + READ_ONLY_OID_1, EXPECTED_OID_VALUE);
-        flowFile.putAttributes(attributes);
-
-        final SNMPSingleResponse response = standardSnmpRequestHandler.set(flowFile);
-
-        assertEquals(NOT_WRITABLE, response.getErrorStatusText());
-    }
-
-    @Test
-    public void testCannotGetWriteOnlyObject() throws IOException {
-        final StandardSNMPRequestHandler standardSnmpRequestHandler = getSnmpV3Getter(LOCALHOST, SnmpConstants.version3, "SHA", "SHAAuthPassword");
-
-        final SNMPSingleResponse response = standardSnmpRequestHandler.get(WRITE_ONLY_OID);
-
-        assertEquals(NO_ACCESS, response.getErrorStatusText());
-    }
-
-    @Test
-    public void testUnauthorizedUserSnmpGetReturnsNull() throws IOException {
-        final StandardSNMPRequestHandler standardSnmpRequestHandler = getSnmpV3Getter(LOCALHOST, SnmpConstants.version3,
-                "FakeUserName", "FakeAuthPassword");
-        final SNMPSingleResponse response = standardSnmpRequestHandler.get(READ_ONLY_OID_1);
-        assertEquals("Null", response.getVariableBindings().get(0).getVariable());
-    }
-
-    private StandardSNMPRequestHandler getSnmpV3Getter(final String host, final int version, final String sha, final String shaAuthPassword) throws IOException {
-        SNMP4JSettings.setForwardRuntimeExceptions(true);
-        final Snmp snmp = SNMPTestUtils.createSnmpClient();
-        final UserTarget userTarget = SNMPTestUtils.prepareUser(snmp, version, host + "/" + agent.getPort(), SecurityLevel.AUTH_NOPRIV,
-                sha, AuthSHA.ID, null, shaAuthPassword, null);
-        return new StandardSNMPRequestHandler(snmp, userTarget);
-    }
-}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SendTrapSNMPHandlerTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SendTrapSNMPHandlerTest.java
new file mode 100644
index 0000000..6a20205
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SendTrapSNMPHandlerTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.snmp.operations;
+
+import org.apache.nifi.snmp.configuration.V1TrapConfiguration;
+import org.apache.nifi.snmp.configuration.V2TrapConfiguration;
+import org.apache.nifi.snmp.factory.trap.V1TrapPDUFactory;
+import org.apache.nifi.snmp.factory.trap.V2TrapPDUFactory;
+import org.apache.nifi.util.MockComponentLog;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.snmp4j.PDU;
+import org.snmp4j.Snmp;
+import org.snmp4j.Target;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SendTrapSNMPHandlerTest {
+
+    private Target mockTarget;
+    private Snmp mockSnmpManager;
+    private PDU mockPdu;
+    private MockComponentLog mockComponentLog;
+    private V1TrapConfiguration mockV1TrapConfiguration;
+    private V2TrapConfiguration mockV2TrapConfiguration;
+    private SNMPResourceHandler snmpResourceHandler;
+    private SendTrapSNMPHandler sendTrapSNMPHandler;
+
+    @Before
+    public void init() {
+        mockTarget = mock(Target.class);
+        mockSnmpManager = mock(Snmp.class);
+        mockPdu = mock(PDU.class);
+        mockComponentLog = new MockComponentLog("id", new Object());
+        mockV1TrapConfiguration = mock(V1TrapConfiguration.class);
+        mockV2TrapConfiguration = mock(V2TrapConfiguration.class);
+        V1TrapPDUFactory mockV1TrapPDUFactory = mock(V1TrapPDUFactory.class);
+        when(mockV1TrapPDUFactory.get(mockV1TrapConfiguration)).thenReturn(mockPdu);
+        V2TrapPDUFactory mockV2TrapPDUFactory = mock(V2TrapPDUFactory.class);
+        when(mockV2TrapPDUFactory.get(mockV2TrapConfiguration)).thenReturn(mockPdu);
+
+        snmpResourceHandler = new SNMPResourceHandler(mockSnmpManager, mockTarget);
+
+        sendTrapSNMPHandler = new SendTrapSNMPHandler(snmpResourceHandler, Instant.now(), mockComponentLog) {
+            @Override
+            V1TrapPDUFactory createV1TrapPduFactory(final Instant startTime) {
+                return mockV1TrapPDUFactory;
+            }
+
+            @Override
+            V2TrapPDUFactory createV2TrapPduFactory(final Instant startTime) {
+                return mockV2TrapPDUFactory;
+            }
+        };
+    }
+
+    @After
+    public void tearDown() {
+        snmpResourceHandler.close();
+    }
+
+    @Test
+    public void testSendV1TrapWithValidFlowfile() throws IOException {
+        final String flowFileOid = "1.3.6.1.2.1.1.1.0";
+        sendTrapSNMPHandler.sendTrap(Collections.singletonMap("snmp$" + flowFileOid, "OID value"), mockV1TrapConfiguration);
+
+        verify(mockSnmpManager).send(mockPdu, mockTarget);
+    }
+
+    @Test
+    public void testSendV2TrapWithValidFlowfile() throws IOException {
+        final String flowFileOid = "1.3.6.1.2.1.1.1.0";
+        sendTrapSNMPHandler.sendTrap(Collections.singletonMap("snmp$" + flowFileOid, "OID value"), mockV2TrapConfiguration);
+
+        verify(mockSnmpManager).send(mockPdu, mockTarget);
+    }
+
+    @Test
+    public void testSendV1TrapWithFlowfileWithoutOptionalSnmpAttributes() throws IOException {
+        sendTrapSNMPHandler.sendTrap(Collections.singletonMap("invalid key", "invalid value"), mockV1TrapConfiguration);
+
+        verify(mockSnmpManager).send(mockPdu, mockTarget);
+
+        final String expectedDebugLog = "{} No optional SNMP specific variables found in flowfile.";
+        assertEquals(expectedDebugLog, mockComponentLog.getDebugMessages().get(0).getMsg());
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SetSNMPHandlerTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SetSNMPHandlerTest.java
new file mode 100644
index 0000000..65d9f59
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/operations/SetSNMPHandlerTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.snmp.operations;
+
+import org.apache.nifi.snmp.dto.SNMPSingleResponse;
+import org.apache.nifi.snmp.exception.RequestTimeoutException;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.snmp4j.PDU;
+import org.snmp4j.Snmp;
+import org.snmp4j.Target;
+import org.snmp4j.event.ResponseEvent;
+import org.snmp4j.util.DefaultPDUFactory;
+import org.snmp4j.util.PDUFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.apache.nifi.snmp.operations.SNMPResourceHandler.REQUEST_TIMEOUT_EXCEPTION_TEMPLATE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SetSNMPHandlerTest {
+
+    private static final PDUFactory defaultSetPduFactory = new DefaultPDUFactory(PDU.SET);
+
+    private Target mockTarget;
+    private Snmp mockSnmpManager;
+    private PDU mockPdu;
+    private PDU mockResponsePdu;
+    private ResponseEvent mockResponseEvent;
+
+    private SetSNMPHandler setSNMPHandler;
+
+    @Before
+    public void init() {
+        mockTarget = mock(Target.class);
+        mockSnmpManager = mock(Snmp.class);
+        PDUFactory mockPduFactory = mock(PDUFactory.class);
+        mockPdu = mock(PDU.class);
+        mockResponsePdu = mock(PDU.class);
+        mockResponseEvent = mock(ResponseEvent.class);
+
+        when(mockPduFactory.createPDU(mockTarget)).thenReturn(mockPdu);
+
+        SNMPResourceHandler snmpResourceHandler = new SNMPResourceHandler(mockSnmpManager, mockTarget);
+        setSNMPHandler = new SetSNMPHandler(snmpResourceHandler);
+        SetSNMPHandler.setSetPduFactory(mockPduFactory);
+    }
+
+    @After
+    public void tearDown() {
+        SetSNMPHandler.setSetPduFactory(defaultSetPduFactory);
+    }
+
+    @Test
+    public void testSetSnmpValidResponse() throws IOException {
+        final String flowFileOid = "1.3.6.1.2.1.1.1.0";
+        final Map<String, String> flowFileAttributes = new HashMap<>();
+        flowFileAttributes.put("snmp$" + flowFileOid, "OID value");
+
+        when(mockResponseEvent.getResponse()).thenReturn(mockResponsePdu);
+        when(mockSnmpManager.set(any(PDU.class), any(Target.class))).thenReturn(mockResponseEvent);
+
+        setSNMPHandler.set(flowFileAttributes);
+
+        verify(mockSnmpManager).set(mockPdu, mockTarget);
+    }
+
+    @Test
+    public void testSetSnmpTimeoutThrowsException() throws IOException {
+        final String flowFileOid = "1.3.6.1.2.1.1.1.0";
+        final Map<String, String> flowFileAttributes = new HashMap<>();
+        flowFileAttributes.put("snmp$" + flowFileOid, "OID value");
+
+        when(mockSnmpManager.set(any(PDU.class), any(Target.class))).thenReturn(mockResponseEvent);
+
+        final RequestTimeoutException requestTimeoutException = Assert.assertThrows(
+                RequestTimeoutException.class,
+                () -> setSNMPHandler.set(flowFileAttributes)
+        );
+
+        assertEquals(String.format(REQUEST_TIMEOUT_EXCEPTION_TEMPLATE, "write"), requestTimeoutException.getMessage());
+    }
+
+    @Test
+    public void testSetSnmpWithInvalidPduThrowsException() throws IOException {
+        final Map<String, String> flowFileAttributes = new HashMap<>();
+        flowFileAttributes.put("invalid key", "invalid value");
+
+        when(mockSnmpManager.set(any(PDU.class), any(Target.class))).thenReturn(mockResponseEvent);
+
+        final Optional<SNMPSingleResponse> optionalResponse = setSNMPHandler.set(flowFileAttributes);
+
+        assertFalse(optionalResponse.isPresent());
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/AbstractSNMPProcessorTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/AbstractSNMPProcessorTest.java
new file mode 100644
index 0000000..a9b66a8
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/AbstractSNMPProcessorTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.snmp.processors;
+
+import org.apache.nifi.remote.io.socket.NetworkUtils;
+import org.apache.nifi.snmp.dto.SNMPSingleResponse;
+import org.apache.nifi.snmp.dto.SNMPValue;
+import org.apache.nifi.snmp.helper.testrunners.SNMPV1TestRunnerFactory;
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.MockProcessContext;
+import org.apache.nifi.util.MockProcessSession;
+import org.apache.nifi.util.SharedSessionState;
+import org.apache.nifi.util.TestRunner;
+import org.junit.Before;
+import org.junit.Test;
+import org.snmp4j.mp.SnmpConstants;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class AbstractSNMPProcessorTest {
+
+    private static final String TEST_OID = "1.3.6.1.4.1.32437.1.5.1.4.2.0";
+    private static final String UNSUPPORTED_SECURITY_LEVEL = "1.3.6.1.6.3.15.1.1.1";
+
+    private GetSNMP getSNMP;
+    private MockProcessContext mockProcessContext;
+    private MockProcessSession mockProcessSession;
+    private MockFlowFile mockFlowFile;
+    private SNMPSingleResponse mockResponse;
+    private TestRunner getTestRunner;
+
+    @Before
+    public void init() {
+        getTestRunner = new SNMPV1TestRunnerFactory().createSnmpGetTestRunner(NetworkUtils.availablePort(), TEST_OID, "GET");
+        getSNMP = (GetSNMP) getTestRunner.getProcessor();
+        mockProcessContext = new MockProcessContext(getSNMP);
+        mockProcessSession = new MockProcessSession(new SharedSessionState(getSNMP, new AtomicLong(0L)), getSNMP);
+        mockFlowFile = mockProcessSession.create();
+        mockResponse = mock(SNMPSingleResponse.class);
+    }
+
+    @Test
+    public void testProcessResponseWithInvalidResponseThrowsException() {
+        final String errorStatus = "Test error status text";
+        when(mockResponse.getErrorStatusText()).thenReturn(errorStatus);
+
+
+        getSNMP.handleResponse(mockProcessContext, mockProcessSession, mockFlowFile, mockResponse, GetSNMP.REL_SUCCESS, GetSNMP.REL_FAILURE, "provenanceAddress");
+
+        final String actualLogMessage = getTestRunner.getLogger().getErrorMessages().get(0).getMsg();
+        final String expectedLogMessage = String.format("SNMP request failed, response error: %s", errorStatus);
+        assertTrue(actualLogMessage.contains(expectedLogMessage));
+    }
+
+    @Test
+    public void testProcessResponseWithNoSuchObjectThrowsException() {
+        when(mockResponse.isValid()).thenReturn(true);
+        when(mockResponse.getVersion()).thenReturn(SnmpConstants.version2c);
+
+        List<SNMPValue> vbs = Collections.singletonList(new SNMPValue(TEST_OID, "noSuchObject"));
+        when(mockResponse.getVariableBindings()).thenReturn(vbs);
+
+
+        getSNMP.handleResponse(mockProcessContext, mockProcessSession, mockFlowFile, mockResponse, GetSNMP.REL_SUCCESS, GetSNMP.REL_FAILURE, "provenanceAddress");
+
+
+        final String actualLogMessage = getTestRunner.getLogger().getErrorMessages().get(0).getMsg();
+        final String expectedLogMessage = "SNMP request failed, response error: OID not found.";
+        assertTrue(actualLogMessage.contains(expectedLogMessage));
+    }
+
+    @Test
+    public void testValidProcessResponseWithoutVariableBindingThrowsException() {
+        when(mockResponse.isValid()).thenReturn(true);
+        when(mockResponse.getVersion()).thenReturn(SnmpConstants.version2c);
+
+        when(mockResponse.getVariableBindings()).thenReturn(Collections.emptyList());
+
+        getSNMP.handleResponse(mockProcessContext, mockProcessSession, mockFlowFile, mockResponse, GetSNMP.REL_SUCCESS, GetSNMP.REL_FAILURE, "provenanceAddress");
+
+
+        final String actualLogMessage = getTestRunner.getLogger().getErrorMessages().get(0).getMsg();
+        final String expectedLogMessage = "Empty SNMP response: no variable binding found.";
+        assertTrue(actualLogMessage.contains(expectedLogMessage));
+    }
+
+    @Test
+    public void testValidProcessResponse() {
+        when(mockResponse.isValid()).thenReturn(true);
+        when(mockResponse.getVersion()).thenReturn(SnmpConstants.version2c);
+
+        final List<SNMPValue> vbs = Collections.singletonList(new SNMPValue(TEST_OID, "testOIDValue"));
+        when(mockResponse.getVariableBindings()).thenReturn(vbs);
+
+        final Map<String, String> attributes = Collections.singletonMap(TEST_OID, "testOIDValue");
+        when(mockResponse.getAttributes()).thenReturn(attributes);
+
+        getSNMP.handleResponse(mockProcessContext, mockProcessSession, mockFlowFile, mockResponse, GetSNMP.REL_SUCCESS, GetSNMP.REL_FAILURE, "provenanceAddress");
+        final List<MockFlowFile> flowFilesForRelationship = mockProcessSession.getFlowFilesForRelationship(GetSNMP.REL_SUCCESS);
+
+        assertEquals("testOIDValue", flowFilesForRelationship.get(0).getAttribute(TEST_OID));
+    }
+
+    @Test
+    public void testProcessResponseWithReportPduWithoutErrorMessage() {
+        when(mockResponse.isValid()).thenReturn(true);
+        when(mockResponse.isReportPdu()).thenReturn(true);
+
+        final List<SNMPValue> vbs = Collections.singletonList(new SNMPValue(TEST_OID, "testOIDValue"));
+        when(mockResponse.getVariableBindings()).thenReturn(vbs);
+
+
+        getSNMP.handleResponse(mockProcessContext, mockProcessSession, mockFlowFile, mockResponse, GetSNMP.REL_SUCCESS, GetSNMP.REL_FAILURE, "provenanceAddress");
+
+
+        final String actualLogMessage = getTestRunner.getLogger().getErrorMessages().get(0).getMsg();
+        final String expectedLogMessage = String.format("SNMP request failed, response error: Report-PDU returned, but no error message found. " +
+                "Please, check the OID %s in an online OID repository.", TEST_OID);
+
+        assertTrue(actualLogMessage.contains(expectedLogMessage));
+    }
+
+    @Test
+    public void testProcessResponseWithReportPdu() {
+        when(mockResponse.isValid()).thenReturn(true);
+        when(mockResponse.isReportPdu()).thenReturn(true);
+
+        final List<SNMPValue> vbs = Collections.singletonList(new SNMPValue(UNSUPPORTED_SECURITY_LEVEL, "testOIDValue"));
+        when(mockResponse.getVariableBindings()).thenReturn(vbs);
+
+
+        getSNMP.handleResponse(mockProcessContext, mockProcessSession, mockFlowFile, mockResponse, GetSNMP.REL_SUCCESS, GetSNMP.REL_FAILURE, "provenanceAddress");
+
+
+        final String actualLogMessage = getTestRunner.getLogger().getErrorMessages().get(0).getMsg();
+        final String expectedLogMessage = String.format("SNMP request failed, response error: Report-PDU returned. %s: usmStatsUnsupportedSecLevels", UNSUPPORTED_SECURITY_LEVEL);
+
+        assertTrue(actualLogMessage.contains(expectedLogMessage));
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/GetSNMPIntegrationTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/GetSNMPIntegrationTest.java
new file mode 100644
index 0000000..ce0f7dc
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/GetSNMPIntegrationTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.snmp.processors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.snmp.helper.testrunners.SNMPTestRunnerFactory;
+import org.apache.nifi.snmp.helper.testrunners.SNMPV1TestRunnerFactory;
+import org.apache.nifi.snmp.helper.testrunners.SNMPV2cTestRunnerFactory;
+import org.apache.nifi.snmp.helper.testrunners.SNMPV3TestRunnerFactory;
+import org.apache.nifi.snmp.testagents.TestAgent;
+import org.apache.nifi.snmp.testagents.TestSNMPV1Agent;
+import org.apache.nifi.snmp.testagents.TestSNMPV2cAgent;
+import org.apache.nifi.snmp.testagents.TestSNMPV3Agent;
+import org.apache.nifi.snmp.utils.SNMPUtils;
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.TestRunner;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.snmp4j.agent.mo.DefaultMOFactory;
+import org.snmp4j.agent.mo.MOAccessImpl;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@RunWith(Parameterized.class)
+public class GetSNMPIntegrationTest {
+
+    private static final String LOCALHOST = "127.0.0.1";
+    private static final String READ_ONLY_OID_1 = "1.3.6.1.4.1.32437.1.5.1.4.2.0";
+    private static final String READ_ONLY_OID_2 = "1.3.6.1.4.1.32437.1.5.1.4.3.0";
+    private static final String NOT_FOUND_OID = "1.3.4.1.2.1.343";
+    private static final String WALK_OID = "1.3.6.1.4.1.32437";
+    private static final String READ_ONLY_OID_VALUE_1 = "TestOID1";
+    private static final String READ_ONLY_OID_VALUE_2 = "TestOID2";
+    private static final String GET = "GET";
+    private static final String WALK = "WALK";
+
+    private static final SNMPTestRunnerFactory v1TestRunnerFactory = new SNMPV1TestRunnerFactory();
+    private static final SNMPTestRunnerFactory v2cTestRunnerFactory = new SNMPV2cTestRunnerFactory();
+    private static final SNMPTestRunnerFactory v3TestRunnerFactory = new SNMPV3TestRunnerFactory();
+
+    private static final TestAgent v1TestAgent = new TestSNMPV1Agent(LOCALHOST);
+    private static final TestAgent v2cTestAgent = new TestSNMPV2cAgent(LOCALHOST);
+    private static final TestAgent v3TestAgent = new TestSNMPV3Agent(LOCALHOST);
+
+    static {
+        registerManagedObjects(v1TestAgent);
+        registerManagedObjects(v2cTestAgent);
+        registerManagedObjects(v3TestAgent);
+    }
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                {v1TestAgent, v1TestRunnerFactory},
+                {v2cTestAgent, v2cTestRunnerFactory},
+                {v3TestAgent, v3TestRunnerFactory}
+        });
+    }
+
+    private final TestAgent testAgent;
+    private final SNMPTestRunnerFactory testRunnerFactory;
+
+    public GetSNMPIntegrationTest(final TestAgent testAgent, final SNMPTestRunnerFactory testRunnerFactory) {
+        this.testAgent = testAgent;
+        this.testRunnerFactory = testRunnerFactory;
+    }
+
+    @Before
+    public void setUp() throws IOException {
+        testAgent.start();
+    }
+
+    @After
+    public void tearDown() {
+        testAgent.stop();
+        testAgent.unregister();
+    }
+
+    @Test
+    public void testSnmpGet() {
+
+        final TestRunner runner = testRunnerFactory.createSnmpGetTestRunner(testAgent.getPort(), READ_ONLY_OID_1, GET);
+        runner.run();
+        final MockFlowFile successFF = runner.getFlowFilesForRelationship(GetSNMP.REL_SUCCESS).get(0);
+
+        assertNotNull(successFF);
+        assertEquals(READ_ONLY_OID_VALUE_1, successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + READ_ONLY_OID_1 + SNMPUtils.SNMP_PROP_DELIMITER + "4"));
+    }
+
+    @Test
+    public void testSnmpWalk() {
+        final TestRunner runner = testRunnerFactory.createSnmpGetTestRunner(testAgent.getPort(), WALK_OID, WALK);
+        runner.run();
+        final MockFlowFile successFF = runner.getFlowFilesForRelationship(GetSNMP.REL_SUCCESS).get(0);
+        assertNotNull(successFF);
+
+        assertEquals(READ_ONLY_OID_VALUE_1, successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + READ_ONLY_OID_1 + SNMPUtils.SNMP_PROP_DELIMITER + "4"));
+        assertEquals(READ_ONLY_OID_VALUE_2, successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + READ_ONLY_OID_2 + SNMPUtils.SNMP_PROP_DELIMITER + "4"));
+    }
+
+    @Test
+    public void testSnmpGetWithEmptyResponse() {
+        final MockFlowFile mockFlowFile = new MockFlowFile(0L);
+        mockFlowFile.putAttributes(Collections.singletonMap("snmp$" + NOT_FOUND_OID, StringUtils.EMPTY));
+        final TestRunner runner = testRunnerFactory.createSnmpGetTestRunner(testAgent.getPort(), NOT_FOUND_OID, GET);
+        runner.enqueue(mockFlowFile);
+        runner.run();
+
+        if (testAgent == v1TestAgent) {
+            final MockFlowFile failureFF = runner.getFlowFilesForRelationship(GetSNMP.REL_FAILURE).get(0);
+            assertNotNull(failureFF);
+            assertEquals(StringUtils.EMPTY, failureFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + NOT_FOUND_OID));
+            assertEquals("No such name", failureFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + "errorStatusText"));
+        } else {
+            final MockFlowFile failureFF = runner.getFlowFilesForRelationship(GetSNMP.REL_FAILURE).get(0);
+            assertNotNull(failureFF);
+             assertEquals("noSuchObject", failureFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + NOT_FOUND_OID + SNMPUtils.SNMP_PROP_DELIMITER + "128"));
+            assertEquals("Success", failureFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + "errorStatusText"));
+        }
+
+    }
+
+    private static void registerManagedObjects(final TestAgent agent) {
+        agent.registerManagedObjects(
+                DefaultMOFactory.getInstance().createScalar(new OID(READ_ONLY_OID_1), MOAccessImpl.ACCESS_READ_ONLY, new OctetString(READ_ONLY_OID_VALUE_1)),
+                DefaultMOFactory.getInstance().createScalar(new OID(READ_ONLY_OID_2), MOAccessImpl.ACCESS_READ_ONLY, new OctetString(READ_ONLY_OID_VALUE_2))
+                );
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/GetSNMPTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/GetSNMPTest.java
index ef6332d..59ccb0f 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/GetSNMPTest.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/GetSNMPTest.java
@@ -16,77 +16,47 @@
  */
 package org.apache.nifi.snmp.processors;
 
-import org.apache.nifi.snmp.testagents.TestSNMPV1Agent;
-import org.apache.nifi.snmp.utils.SNMPUtils;
-import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.remote.io.socket.NetworkUtils;
+import org.apache.nifi.snmp.helper.testrunners.SNMPV1TestRunnerFactory;
+import org.apache.nifi.util.MockProcessSession;
+import org.apache.nifi.util.SharedSessionState;
 import org.apache.nifi.util.TestRunner;
-import org.apache.nifi.util.TestRunners;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
-import org.snmp4j.agent.mo.DefaultMOFactory;
-import org.snmp4j.agent.mo.MOAccessImpl;
-import org.snmp4j.smi.OID;
-import org.snmp4j.smi.OctetString;
 
-import java.io.IOException;
+import java.util.concurrent.atomic.AtomicLong;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
 public class GetSNMPTest {
 
-    private static TestSNMPV1Agent snmpV1Agent;
-    private static final OID readOnlyOID1 = new OID("1.3.6.1.4.1.32437.1.5.1.4.2.0");
-    private static final OID readOnlyOID2 = new OID("1.3.6.1.4.1.32437.1.5.1.4.3.0");
-    private static final String OIDValue1 = "TestOID1";
-    private static final String OIDValue2 = "TestOID2";
-    private static final String GET = "GET";
-    private static final String WALK = "WALK";
+    private static final String OID = "1.3.6.1.4.1.32437.1.5.1.4.2.0";
 
-    @BeforeAll
-    public static void setUp() throws IOException {
-        snmpV1Agent = new TestSNMPV1Agent("127.0.0.1");
-        snmpV1Agent.start();
-        snmpV1Agent.registerManagedObjects(
-                DefaultMOFactory.getInstance().createScalar(new OID(readOnlyOID1), MOAccessImpl.ACCESS_READ_ONLY, new OctetString(OIDValue1)),
-                DefaultMOFactory.getInstance().createScalar(new OID(readOnlyOID2), MOAccessImpl.ACCESS_READ_ONLY, new OctetString(OIDValue2))
-        );
-    }
+    @Test
+    public void testOnTriggerWithGetStrategyPerformsSnmpGet() {
+        final TestRunner getSnmpTestRunner = new SNMPV1TestRunnerFactory().createSnmpGetTestRunner(NetworkUtils.availablePort(), OID, "GET");
+        final GetSNMP spyGetSNMP = spy((GetSNMP) getSnmpTestRunner.getProcessor());
+        final MockProcessSession mockProcessSession = new MockProcessSession(new SharedSessionState(spyGetSNMP, new AtomicLong(0L)), spyGetSNMP);
 
-    @AfterAll
-    public static void tearDown() {
-        snmpV1Agent.stop();
-    }
+        doNothing().when(spyGetSNMP).performSnmpGet(any(), any(), any(), any());
 
-    @Test
-    public void testSnmpV1Get() {
-        final TestRunner runner = getTestRunner(readOnlyOID1.toString(), String.valueOf(snmpV1Agent.getPort()), GET);
-        runner.run();
-        final MockFlowFile successFF = runner.getFlowFilesForRelationship(GetSNMP.REL_SUCCESS).get(0);
-        assertNotNull(successFF);
-        assertEquals(OIDValue1, successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + readOnlyOID1.toString() + SNMPUtils.SNMP_PROP_DELIMITER + "4"));
+        spyGetSNMP.onTrigger(getSnmpTestRunner.getProcessContext(), mockProcessSession);
+
+        verify(spyGetSNMP).performSnmpGet(any(), any(), any(), any());
     }
 
     @Test
-    public void testSnmpV1Walk() {
-        final TestRunner runner = getTestRunner("1.3.6.1.4.1.32437", String.valueOf(snmpV1Agent.getPort()), WALK);
-        runner.run();
-        final MockFlowFile successFF = runner.getFlowFilesForRelationship(GetSNMP.REL_SUCCESS).get(0);
-        assertNotNull(successFF);
-        assertEquals(OIDValue1, successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + readOnlyOID1.toString() + SNMPUtils.SNMP_PROP_DELIMITER + "4"));
-        assertEquals(OIDValue2, successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + readOnlyOID2.toString() + SNMPUtils.SNMP_PROP_DELIMITER + "4"));
-    }
+    public void testOnTriggerWithWalkStrategyPerformsSnmpWalk() {
+        final TestRunner getSnmpTestRunner = new SNMPV1TestRunnerFactory().createSnmpGetTestRunner(NetworkUtils.availablePort(), OID, "WALK");
+        final GetSNMP spyGetSNMP = spy((GetSNMP) getSnmpTestRunner.getProcessor());
+        final MockProcessSession mockProcessSession = new MockProcessSession(new SharedSessionState(spyGetSNMP, new AtomicLong(0L)), spyGetSNMP);
+
+        doNothing().when(spyGetSNMP).performSnmpWalk(any(), any(), any(), any());
+
+        spyGetSNMP.onTrigger(getSnmpTestRunner.getProcessContext(), mockProcessSession);
 
-    private TestRunner getTestRunner(final String oid, final String port, final String strategy) {
-        final TestRunner runner = TestRunners.newTestRunner(GetSNMP.class);
-        runner.setProperty(GetSNMP.OID, oid);
-        runner.setProperty(GetSNMP.AGENT_HOST, "127.0.0.1");
-        runner.setProperty(GetSNMP.AGENT_PORT, port);
-        runner.setProperty(GetSNMP.SNMP_COMMUNITY, "public");
-        runner.setProperty(GetSNMP.SNMP_VERSION, "SNMPv1");
-        runner.setProperty(GetSNMP.SNMP_STRATEGY, strategy);
-        runner.setProperty(GetSNMP.SNMP_SECURITY_LEVEL, "noAuthNoPriv");
-        return runner;
+        verify(spyGetSNMP).performSnmpWalk(any(), any(), any(), any());
     }
 }
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/SetSNMPIntegrationTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/SetSNMPIntegrationTest.java
new file mode 100644
index 0000000..d96a4f4
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/SetSNMPIntegrationTest.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.snmp.processors;
+
+import org.apache.nifi.snmp.helper.testrunners.SNMPTestRunnerFactory;
+import org.apache.nifi.snmp.helper.testrunners.SNMPV1TestRunnerFactory;
+import org.apache.nifi.snmp.helper.testrunners.SNMPV2cTestRunnerFactory;
+import org.apache.nifi.snmp.helper.testrunners.SNMPV3TestRunnerFactory;
+import org.apache.nifi.snmp.testagents.TestAgent;
+import org.apache.nifi.snmp.testagents.TestSNMPV1Agent;
+import org.apache.nifi.snmp.testagents.TestSNMPV2cAgent;
+import org.apache.nifi.snmp.testagents.TestSNMPV3Agent;
+import org.apache.nifi.snmp.utils.SNMPUtils;
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.TestRunner;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.snmp4j.agent.mo.DefaultMOFactory;
+import org.snmp4j.agent.mo.MOAccessImpl;
+import org.snmp4j.smi.OID;
+import org.snmp4j.smi.OctetString;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@RunWith(Parameterized.class)
+public class SetSNMPIntegrationTest {
+
+    private static final String LOCALHOST = "127.0.0.1";
+    private static final String TEST_OID = "1.3.6.1.4.1.32437.1.5.1.4.2.0";
+    private static final String TEST_OID_VALUE = "TestOID";
+
+    private static final SNMPTestRunnerFactory v1TestRunnerFactory = new SNMPV1TestRunnerFactory();
+    private static final SNMPTestRunnerFactory v2cTestRunnerFactory = new SNMPV2cTestRunnerFactory();
+    private static final SNMPTestRunnerFactory v3TestRunnerFactory = new SNMPV3TestRunnerFactory();
+
+    private static final TestAgent v1TestAgent = new TestSNMPV1Agent(LOCALHOST);
+    private static final TestAgent v2cTestAgent = new TestSNMPV2cAgent(LOCALHOST);
+    private static final TestAgent v3TestAgent = new TestSNMPV3Agent(LOCALHOST);
+
+    static {
+        registerManagedObjects(v1TestAgent);
+        registerManagedObjects(v2cTestAgent);
+        registerManagedObjects(v3TestAgent);
+    }
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                {v1TestAgent, v1TestRunnerFactory},
+                {v2cTestAgent, v2cTestRunnerFactory},
+                {v3TestAgent, v3TestRunnerFactory}
+        });
+    }
+
+    private final TestAgent testAgent;
+    private final SNMPTestRunnerFactory testRunnerFactory;
+
+    public SetSNMPIntegrationTest(final TestAgent testAgent, final SNMPTestRunnerFactory testRunnerFactory) {
+        this.testAgent = testAgent;
+        this.testRunnerFactory = testRunnerFactory;
+    }
+
+    @Before
+    public void setUp() throws IOException {
+        testAgent.start();
+    }
+
+    @After
+    public void tearDown() {
+        testAgent.stop();
+        testAgent.unregister();
+    }
+
+
+    @Test
+    public void testSnmpSet() {
+        final TestRunner runner = testRunnerFactory.createSnmpSetTestRunner(testAgent.getPort(), TEST_OID, TEST_OID_VALUE);
+        runner.run();
+        final MockFlowFile successFF = runner.getFlowFilesForRelationship(SetSNMP.REL_SUCCESS).get(0);
+
+        assertNotNull(successFF);
+        assertEquals(TEST_OID_VALUE, successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + TEST_OID + SNMPUtils.SNMP_PROP_DELIMITER + "4"));
+    }
+
+    private static void registerManagedObjects(final TestAgent agent) {
+        agent.registerManagedObjects(
+                DefaultMOFactory.getInstance().createScalar(new OID(TEST_OID), MOAccessImpl.ACCESS_READ_WRITE, new OctetString(TEST_OID_VALUE))
+        );
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/SetSNMPTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/SetSNMPTest.java
deleted file mode 100644
index 0e9f967..0000000
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/SetSNMPTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.snmp.processors;
-
-import org.apache.nifi.snmp.testagents.TestSNMPV1Agent;
-import org.apache.nifi.snmp.utils.SNMPUtils;
-import org.apache.nifi.util.MockFlowFile;
-import org.apache.nifi.util.TestRunner;
-import org.apache.nifi.util.TestRunners;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.snmp4j.agent.mo.DefaultMOFactory;
-import org.snmp4j.agent.mo.MOAccessImpl;
-import org.snmp4j.smi.OID;
-import org.snmp4j.smi.OctetString;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-public class SetSNMPTest {
-
-    private static TestSNMPV1Agent snmpV1Agent;
-    private static final OID TEST_OID = new OID("1.3.6.1.4.1.32437.1.5.1.4.2.0");
-    private static final String TEST_OID_VALUE = "TestOID";
-    private static final String LOCALHOST = "127.0.0.1";
-    private static final String VALID_OID_FF_ATTRIBUTE = "snmp$1.3.6.1.4.1.32437.1.5.1.4.2.0$4";
-    private static final String INVALID_OID_FF_ATTRIBUTE = "snmp$1.3.6.1.4.1.32437.1.5.1.4.213.0$4";
-
-    @BeforeAll
-    public static void setUp() throws IOException {
-        snmpV1Agent = new TestSNMPV1Agent("127.0.0.1");
-        snmpV1Agent.start();
-        snmpV1Agent.registerManagedObjects(
-                DefaultMOFactory.getInstance().createScalar(new OID(TEST_OID), MOAccessImpl.ACCESS_READ_WRITE, new OctetString(TEST_OID_VALUE))
-        );
-    }
-
-    @AfterAll
-    public static void tearDown() {
-        snmpV1Agent.stop();
-    }
-
-    @Test
-    public void testSnmpV1Set() {
-        final TestRunner runner = getTestRunner(LOCALHOST, String.valueOf(snmpV1Agent.getPort()), VALID_OID_FF_ATTRIBUTE, true);
-        runner.run();
-        final MockFlowFile successFF = runner.getFlowFilesForRelationship(SetSNMP.REL_SUCCESS).get(0);
-        assertNotNull(successFF);
-        assertEquals(TEST_OID_VALUE, successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + TEST_OID.toString() + SNMPUtils.SNMP_PROP_DELIMITER + "4"));
-    }
-
-    private TestRunner getTestRunner(final String host, final String port, final String oid, final boolean withAttributes) {
-        final SetSNMP processor = new SetSNMP();
-        final TestRunner runner = TestRunners.newTestRunner(processor);
-        final MockFlowFile ff = new MockFlowFile(123);
-        if (withAttributes) {
-            final Map<String, String> attributes = ff.getAttributes();
-            final Map<String, String> newAttributes = new HashMap<>(attributes);
-            newAttributes.put(oid, TEST_OID_VALUE);
-            ff.putAttributes(newAttributes);
-        }
-        runner.enqueue(ff);
-        runner.setProperty(GetSNMP.AGENT_HOST, host);
-        runner.setProperty(GetSNMP.AGENT_PORT, port);
-        runner.setProperty(GetSNMP.SNMP_COMMUNITY, "public");
-        runner.setProperty(GetSNMP.SNMP_VERSION, "SNMPv1");
-        return runner;
-    }
-}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/TrapSNMPIntegrationTest.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/TrapSNMPIntegrationTest.java
new file mode 100644
index 0000000..b5b0f62
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/processors/TrapSNMPIntegrationTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.snmp.processors;
+
+import org.apache.nifi.remote.io.socket.NetworkUtils;
+import org.apache.nifi.snmp.configuration.V1TrapConfiguration;
+import org.apache.nifi.snmp.configuration.V2TrapConfiguration;
+import org.apache.nifi.snmp.helper.TrapConfigurationFactory;
+import org.apache.nifi.snmp.helper.testrunners.SNMPTestRunnerFactory;
+import org.apache.nifi.snmp.helper.testrunners.SNMPV1TestRunnerFactory;
+import org.apache.nifi.snmp.helper.testrunners.SNMPV2cTestRunnerFactory;
+import org.apache.nifi.snmp.utils.SNMPUtils;
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.TestRunner;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.snmp4j.mp.SnmpConstants;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class TrapSNMPIntegrationTest {
+
+    protected static final String SYSTEM_DESCRIPTION_OID = "1.3.6.1.2.1.1.1.0";
+    protected static final String SYSTEM_DESCRIPTION_OID_VALUE = "optionalTrapOidTestValue";
+
+    @Test
+    public void testSendReceiveV1Trap() throws InterruptedException {
+        final int listenPort = NetworkUtils.availablePort();
+
+        final V1TrapConfiguration v1TrapConfiguration = TrapConfigurationFactory.getV1TrapConfiguration();
+        final SNMPTestRunnerFactory v1TestRunnerFactory = new SNMPV1TestRunnerFactory();
+
+        final TestRunner sendTrapTestRunner = v1TestRunnerFactory.createSnmpSendTrapTestRunner(listenPort, SYSTEM_DESCRIPTION_OID, SYSTEM_DESCRIPTION_OID_VALUE);
+        final TestRunner listenTrapTestRunner = v1TestRunnerFactory.createSnmpListenTrapTestRunner(listenPort);
+
+        listenTrapTestRunner.run(1, false);
+        sendTrapTestRunner.run(1);
+
+        Thread.sleep(50);
+
+        final MockFlowFile successFF = listenTrapTestRunner.getFlowFilesForRelationship(GetSNMP.REL_SUCCESS).get(0);
+
+        assertNotNull(successFF);
+        assertEquals("Success", successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + "errorStatusText"));
+
+        assertEquals(v1TrapConfiguration.getEnterpriseOid(), successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + "enterprise"));
+        assertEquals(v1TrapConfiguration.getAgentAddress(), successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + "agentAddress"));
+        assertEquals(String.valueOf(v1TrapConfiguration.getGenericTrapType()), successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + "genericTrapType"));
+        assertEquals(String.valueOf(v1TrapConfiguration.getSpecificTrapType()), successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + "specificTrapType"));
+
+
+        assertEquals(SYSTEM_DESCRIPTION_OID_VALUE, successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + SYSTEM_DESCRIPTION_OID
+                + SNMPUtils.SNMP_PROP_DELIMITER + "4"));
+
+        listenTrapTestRunner.shutdown();
+    }
+
+    @Test
+    public void testSendReceiveV2Trap() throws InterruptedException {
+        final int listenPort = NetworkUtils.availablePort();
+
+        final V2TrapConfiguration v2TrapConfiguration = TrapConfigurationFactory.getV2TrapConfiguration();
+        final SNMPTestRunnerFactory v2cTestRunnerFactory = new SNMPV2cTestRunnerFactory();
+
+        final TestRunner sendTrapTestRunner = v2cTestRunnerFactory.createSnmpSendTrapTestRunner(listenPort, SYSTEM_DESCRIPTION_OID, SYSTEM_DESCRIPTION_OID_VALUE);
+        final TestRunner listenTrapTestRunner = v2cTestRunnerFactory.createSnmpListenTrapTestRunner(listenPort);
+
+        listenTrapTestRunner.run(1, false);
+        sendTrapTestRunner.run(1);
+
+        Thread.sleep(50);
+
+        final MockFlowFile successFF = listenTrapTestRunner.getFlowFilesForRelationship(GetSNMP.REL_SUCCESS).get(0);
+
+        assertNotNull(successFF);
+        assertEquals("Success", successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + "errorStatusText"));
+
+        assertEquals(v2TrapConfiguration.getTrapOidValue(), successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + SnmpConstants.snmpTrapOID
+                + SNMPUtils.SNMP_PROP_DELIMITER + "4"));
+
+        assertEquals(SYSTEM_DESCRIPTION_OID_VALUE, successFF.getAttribute(SNMPUtils.SNMP_PROP_PREFIX + SYSTEM_DESCRIPTION_OID
+                + SNMPUtils.SNMP_PROP_DELIMITER + "4"));
+
+        listenTrapTestRunner.shutdown();
+    }
+
+    @Ignore("The ListenTrapSNMP and SendTrapSNMP processors use the same SecurityProtocols instance" +
+            " and same USM (the USM is stored in a map by version), hence this case shall be manually tested." +
+            " Check assertByVersion() to see what the trap payload must contain.")
+    @Test
+    public void testReceiveV3Trap() {
+    }
+}
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/testagents/TestAgent.java b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/testagents/TestAgent.java
index 06ad0b3..9d05190 100644
--- a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/testagents/TestAgent.java
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/java/org/apache/nifi/snmp/testagents/TestAgent.java
@@ -53,6 +53,11 @@ public abstract class TestAgent extends BaseAgent {
         this.address = String.format("udp:%s/%d", host, port);
     }
 
+    public void unregister() {
+        unregisterSnmpMIBs();
+        snmpMpdMib.unregisterMOs(server, getContext(snmpMpdMib));
+    }
+
     @Override
     protected void initTransportMappings() {
         transportMappings = new TransportMapping[1];
diff --git a/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/resources/users.json b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/resources/users.json
new file mode 100644
index 0000000..b6ce71a
--- /dev/null
+++ b/nifi-nar-bundles/nifi-snmp-bundle/nifi-snmp-processors/src/test/resources/users.json
@@ -0,0 +1,16 @@
+[
+  {
+    "securityName":"user1",
+    "authProtocol":"MD5",
+    "authPassphrase":"abc12345",
+    "privProtocol":"DES",
+    "privPassphrase":"abc12345"
+  },
+  {
+    "securityName":"newUser2",
+    "authProtocol":"MD5",
+    "authPassphrase":"abc12345",
+    "privProtocol":"AES256",
+    "privPassphrase":"abc12345"
+  }
+]
\ No newline at end of file