From 12bfb7b86a783f63b8072ac67462202d59173940 Mon Sep 17 00:00:00 2001
From: "waqas.ikram" <waqas.ikram@est.tech>
Date: Wed, 8 Apr 2020 15:48:21 +0100
Subject: Refactor SOL003 Adapter to organize its modules

based on functions

Change-Id: I10b2376a552272ac3b405b2dae718adcb7e1e489
Issue-ID: SO-2771
Signed-off-by: waqas.ikram <waqas.ikram@est.tech>
---
 .../etsi-sol003-package-management-adapter/pom.xml | 109 ++++
 .../ConversionServiceConfiguration.java            |  55 ++
 .../vnfmadapter/PackageManagementConstants.java    |  39 ++
 .../sol003/AbstractPkgNotificationConverter.java   |  56 ++
 .../sol003/PkgChangeNotificationConverter.java     |  79 +++
 .../sol003/PkgOnboardingNotificationConverter.java |  69 +++
 .../etsicatalog/sol003/VnfPkgInfoConverter.java    | 198 ++++++
 .../PkgmSubscriptionRequestConverter.java          | 198 ++++++
 .../AbstractServiceProviderConfiguration.java      |  49 ++
 ...tsiCatalogPackageManagementServiceProvider.java |  72 +++
 .../etsicatalog/EtsiCatalogServiceProvider.java    |  32 +
 .../EtsiCatalogServiceProviderConfiguration.java   | 187 ++++++
 .../EtsiCatalogServiceProviderImpl.java            | 281 +++++++++
 .../EtsiCatalogSubscriptionServiceProvider.java    |  55 ++
 .../etsicatalog/EtsiCatalogUrlProvider.java        | 113 ++++
 .../vnfm/VnfmHttpServiceProviderConfiguration.java |  49 ++
 .../vnfm/VnfmServiceProviderConfiguration.java     | 154 +++++
 .../AbstractNotificationServiceProvider.java       |  59 ++
 .../BasicAuthNotificationServiceProvider.java      |  68 +++
 .../NotificationManager.java                       | 137 +++++
 .../NotificationServiceProvider.java               |  51 ++
 .../NotificationServiceProviderFactory.java        |  67 ++
 .../OAuthNotificationServiceProvider.java          | 103 ++++
 .../subscriptionmanagement/OAuthTokenResponse.java |  56 ++
 .../SubscriptionManager.java                       | 209 +++++++
 .../TlsNotificationServiceProvider.java            |  57 ++
 .../cache/AbstractCacheServiceProvider.java        |  47 ++
 .../cache/CacheManagerConfiguration.java           |  50 ++
 .../cache/CacheNotFoundException.java              |  37 ++
 .../PackageManagementCacheServiceProvider.java     |  74 +++
 .../PackageManagementCacheServiceProviderImpl.java | 119 ++++
 .../EtsiSubscriptionNotificationController.java    | 131 ++++
 .../rest/Sol003PackageManagementController.java    | 176 ++++++
 ...003PackageManagementSubscriptionController.java | 172 ++++++
 .../AuthenticationTypeNotSupportedException.java   |  44 ++
 .../rest/exceptions/ConversionFailedException.java |  44 ++
 .../EtsiCatalogManagerBadRequestException.java     |  43 ++
 .../EtsiCatalogManagerRequestFailureException.java |  43 ++
 ...tionNotificationControllerExceptionHandler.java | 112 ++++
 .../exceptions/InternalServerErrorException.java   |  43 ++
 .../NotificationTypeNotSupportedException.java     |  44 ++
 ...ackageManagementControllerExceptionHandler.java |  94 +++
 .../exceptions/SubscriptionNotFoundException.java  |  43 ++
 .../rest/exceptions/VnfPkgBadRequestException.java |  43 ++
 .../rest/exceptions/VnfPkgConflictException.java   |  43 ++
 .../rest/exceptions/VnfPkgNotFoundException.java   |  43 ++
 ...EtsiSubscriptionNotificationControllerTest.java | 602 ++++++++++++++++++
 .../Sol003PackageManagementControllerTest.java     | 677 +++++++++++++++++++++
 ...ackageManagementSubscriptionControllerTest.java | 403 ++++++++++++
 .../adapters/vnfmadapter/rest/TestApplication.java |  38 ++
 .../src/test/resources/application.yaml            |  61 ++
 .../resources/requests/SubscriptionRequest.json    |  45 ++
 52 files changed, 5873 insertions(+)
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/pom.xml
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/ConversionServiceConfiguration.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/PackageManagementConstants.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/AbstractPkgNotificationConverter.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/PkgChangeNotificationConverter.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/PkgOnboardingNotificationConverter.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/VnfPkgInfoConverter.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/sol003/etsicatalog/PkgmSubscriptionRequestConverter.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/AbstractServiceProviderConfiguration.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogPackageManagementServiceProvider.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogServiceProvider.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogServiceProviderConfiguration.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogServiceProviderImpl.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogSubscriptionServiceProvider.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogUrlProvider.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmHttpServiceProviderConfiguration.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/AbstractNotificationServiceProvider.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/BasicAuthNotificationServiceProvider.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/NotificationManager.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/NotificationServiceProvider.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/NotificationServiceProviderFactory.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/OAuthNotificationServiceProvider.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/OAuthTokenResponse.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/SubscriptionManager.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/TlsNotificationServiceProvider.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/AbstractCacheServiceProvider.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/CacheManagerConfiguration.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/CacheNotFoundException.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/PackageManagementCacheServiceProvider.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/PackageManagementCacheServiceProviderImpl.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/EtsiSubscriptionNotificationController.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementController.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementSubscriptionController.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/AuthenticationTypeNotSupportedException.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/ConversionFailedException.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/EtsiCatalogManagerBadRequestException.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/EtsiCatalogManagerRequestFailureException.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/EtsiSubscriptionNotificationControllerExceptionHandler.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/InternalServerErrorException.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/NotificationTypeNotSupportedException.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/Sol003PackageManagementControllerExceptionHandler.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/SubscriptionNotFoundException.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfPkgBadRequestException.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfPkgConflictException.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfPkgNotFoundException.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/EtsiSubscriptionNotificationControllerTest.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementControllerTest.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementSubscriptionControllerTest.java
 create mode 100755 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/TestApplication.java
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/resources/application.yaml
 create mode 100644 adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/resources/requests/SubscriptionRequest.json

(limited to 'adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter')

diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/pom.xml b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/pom.xml
new file mode 100644
index 0000000000..f24ad64db4
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/pom.xml
@@ -0,0 +1,109 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.onap.so.adapters</groupId>
+    <artifactId>etsi-sol003-package-management</artifactId>
+    <version>1.6.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>etsi-sol003-package-management-adapter</artifactId>
+  <name>ETSI SOL003 VNF Package Management Adapter</name>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <systemPropertyVariables>
+            <so.log.level>DEBUG</so.log.level>
+          </systemPropertyVariables>
+          <rerunFailingTestsCount>2</rerunFailingTestsCount>
+          <parallel>suites</parallel>
+          <useUnlimitedThreads>false</useUnlimitedThreads>
+          <threadCount>1</threadCount>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+      <exclusions>
+        <exclusion>
+          <groupId>com.fasterxml.jackson.core</groupId>
+          <artifactId>jackson-databind</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-security</artifactId>
+      <exclusions>
+        <exclusion>
+          <groupId>org.springframework.boot</groupId>
+          <artifactId>spring-boot-starter-tomcat</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-actuator</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.onap.so</groupId>
+      <artifactId>common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.onap.so.adapters</groupId>
+      <artifactId>etsi-sol003-adapter-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.onap.so.adapters</groupId>
+      <artifactId>etsi-sol003-package-management-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.onap.so.adapters</groupId>
+      <artifactId>etsi-sol003-package-management-ext-clients</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.jersey.core</groupId>
+      <artifactId>jersey-client</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.jersey.core</groupId>
+      <artifactId>jersey-common</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.jersey.inject</groupId>
+      <artifactId>jersey-hk2</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.jersey.media</groupId>
+      <artifactId>jersey-media-json-jackson</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.yaml</groupId>
+      <artifactId>snakeyaml</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/ConversionServiceConfiguration.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/ConversionServiceConfiguration.java
new file mode 100644
index 0000000000..fa9bc3e9c9
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/ConversionServiceConfiguration.java
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter;
+
+import org.onap.so.adapters.vnfmadapter.common.VnfmAdapterUrlProvider;
+import org.onap.so.adapters.vnfmadapter.converters.etsicatalog.sol003.PkgChangeNotificationConverter;
+import org.onap.so.adapters.vnfmadapter.converters.etsicatalog.sol003.PkgOnboardingNotificationConverter;
+import org.onap.so.adapters.vnfmadapter.converters.etsicatalog.sol003.VnfPkgInfoConverter;
+import org.onap.so.adapters.vnfmadapter.converters.sol003.etsicatalog.PkgmSubscriptionRequestConverter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.core.convert.support.DefaultConversionService;
+
+/**
+ * @author Waqas Ikram (waqas.ikram@est.tech)
+ *
+ */
+public class ConversionServiceConfiguration {
+
+    private final VnfmAdapterUrlProvider vnfmAdapterUrlProvider;
+
+    @Autowired
+    public ConversionServiceConfiguration(final VnfmAdapterUrlProvider vnfmAdapterUrlProvider) {
+        this.vnfmAdapterUrlProvider = vnfmAdapterUrlProvider;
+    }
+
+    @Bean
+    public ConversionService conversionService() {
+        final DefaultConversionService service = new DefaultConversionService();
+        service.addConverter(new VnfPkgInfoConverter(vnfmAdapterUrlProvider));
+        service.addConverter(new PkgmSubscriptionRequestConverter());
+        service.addConverter(new PkgChangeNotificationConverter());
+        service.addConverter(new PkgOnboardingNotificationConverter());
+        return service;
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/PackageManagementConstants.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/PackageManagementConstants.java
new file mode 100644
index 0000000000..cf3c6c5ac6
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/PackageManagementConstants.java
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter;
+
+/**
+ * ETSI SOL003 VNF Package Management Adapter constants
+ *
+ * @author Ronan Kenny (ronan.kenny@est.tech)
+ * @author Gareth Roper (gareth.roper@est.tech)
+ */
+public class PackageManagementConstants {
+
+    public static final String APPLICATION_ZIP = "application/zip";
+
+    /**
+     * Name of the subscription cache
+     */
+    public static final String PACKAGE_MANAGEMENT_SUBSCRIPTION_CACHE = "PackageManagementSubscriptionCache";
+
+    private PackageManagementConstants() {}
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/AbstractPkgNotificationConverter.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/AbstractPkgNotificationConverter.java
new file mode 100644
index 0000000000..221ec8d3a0
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/AbstractPkgNotificationConverter.java
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.converters.etsicatalog.sol003;
+
+import org.onap.so.adapters.vnfmadapter.etsicatalog.notification.model.PkgmLinks;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.packagemanagement.notification.model.URIisprovidedbytheclientwhencreatingthesubscriptionVnfPackageOnboardingNotificationLinks;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.packagemanagement.notification.model.URIisprovidedbytheclientwhencreatingthesubscriptionVnfPackageOnboardingNotificationLinksVnfPackage;
+
+/**
+ * A base class that can be extended by classes for converting Etsi Catalog Manager Pkg Notification classes. Provides
+ * common methods that will be useful to those classes.
+ *
+ * @author andrew.a.lamb@est.tech
+ */
+abstract public class AbstractPkgNotificationConverter {
+
+    protected URIisprovidedbytheclientwhencreatingthesubscriptionVnfPackageOnboardingNotificationLinks convert(
+            final PkgmLinks pkgmLinks) {
+        final URIisprovidedbytheclientwhencreatingthesubscriptionVnfPackageOnboardingNotificationLinksVnfPackage linksVnfPackage =
+                new URIisprovidedbytheclientwhencreatingthesubscriptionVnfPackageOnboardingNotificationLinksVnfPackage();
+        if (pkgmLinks.getVnfPackage() != null) {
+            linksVnfPackage.setHref(pkgmLinks.getVnfPackage().getHref());
+        }
+
+        final URIisprovidedbytheclientwhencreatingthesubscriptionVnfPackageOnboardingNotificationLinksVnfPackage linksSubscription =
+                new URIisprovidedbytheclientwhencreatingthesubscriptionVnfPackageOnboardingNotificationLinksVnfPackage();
+        if (pkgmLinks.getSubscription() != null) {
+            linksSubscription.setHref(pkgmLinks.getSubscription().getHref());
+        }
+
+        final URIisprovidedbytheclientwhencreatingthesubscriptionVnfPackageOnboardingNotificationLinks links =
+                new URIisprovidedbytheclientwhencreatingthesubscriptionVnfPackageOnboardingNotificationLinks();
+        links.setVnfPackage(linksVnfPackage);
+        links.setSubscription(linksSubscription);
+        return links;
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/PkgChangeNotificationConverter.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/PkgChangeNotificationConverter.java
new file mode 100644
index 0000000000..04b304e93b
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/PkgChangeNotificationConverter.java
@@ -0,0 +1,79 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.converters.etsicatalog.sol003;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import org.onap.so.adapters.vnfmadapter.etsicatalog.notification.model.PkgChangeNotification;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.packagemanagement.notification.model.VnfPackageChangeNotification;
+import org.slf4j.Logger;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.stereotype.Service;
+
+/**
+ * Converter to convert from an Etsi Catalog Manager {@link PkgChangeNotification} Object to its equivalent SOL003
+ * {@link VnfPackageChangeNotification} Object
+ *
+ * @author andrew.a.lamb@est.tech
+ */
+@Service
+public class PkgChangeNotificationConverter extends AbstractPkgNotificationConverter
+        implements Converter<PkgChangeNotification, VnfPackageChangeNotification> {
+    private static final Logger logger = getLogger(PkgChangeNotificationConverter.class);
+
+    /**
+     * Convert a {@link PkgChangeNotification} Object to an {@link VnfPackageChangeNotification} Object
+     * 
+     * @param pkgChangeNotification The PkgChangeNotification Object to Convert
+     * @return The Converted VnfPackageChangeNotification Object
+     */
+    @Override
+    public VnfPackageChangeNotification convert(final PkgChangeNotification pkgChangeNotification) {
+        logger.info("Converting PkgChangeNotification\n{}", pkgChangeNotification.toString());
+        final VnfPackageChangeNotification vnfPackageChangeNotification = new VnfPackageChangeNotification();
+        vnfPackageChangeNotification.setId(pkgChangeNotification.getId());
+
+        if (pkgChangeNotification.getNotificationType() != null) {
+            vnfPackageChangeNotification.setNotificationType(VnfPackageChangeNotification.NotificationTypeEnum
+                    .fromValue(pkgChangeNotification.getNotificationType().getValue()));
+        }
+
+        vnfPackageChangeNotification.setSubscriptionId(pkgChangeNotification.getSubscriptionId());
+        vnfPackageChangeNotification.setTimeStamp(pkgChangeNotification.getTimeStamp());
+        vnfPackageChangeNotification.setVnfPkgId(pkgChangeNotification.getVnfPkgId());
+
+        vnfPackageChangeNotification.setVnfdId(pkgChangeNotification.getVnfdId());
+
+        if (pkgChangeNotification.getChangeType() != null) {
+            vnfPackageChangeNotification.setChangeType(VnfPackageChangeNotification.ChangeTypeEnum
+                    .fromValue(pkgChangeNotification.getChangeType().getValue()));
+        }
+
+        if (pkgChangeNotification.getOperationalState() != null) {
+            vnfPackageChangeNotification.setOperationalState(VnfPackageChangeNotification.OperationalStateEnum
+                    .fromValue(pkgChangeNotification.getOperationalState().getValue()));
+        }
+
+        vnfPackageChangeNotification.setLinks(convert((pkgChangeNotification.getLinks())));
+
+        return vnfPackageChangeNotification;
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/PkgOnboardingNotificationConverter.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/PkgOnboardingNotificationConverter.java
new file mode 100644
index 0000000000..9828972fd0
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/PkgOnboardingNotificationConverter.java
@@ -0,0 +1,69 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.converters.etsicatalog.sol003;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import org.onap.so.adapters.vnfmadapter.etsicatalog.notification.model.PkgOnboardingNotification;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.packagemanagement.notification.model.VnfPackageOnboardingNotification;
+import org.slf4j.Logger;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.stereotype.Service;
+
+/**
+ * Converter to convert from an Etsi Catalog Manager {@link PkgOnboardingNotification} Object to its equivalent SOL003
+ * {@link VnfPackageOnboardingNotification} Object
+ *
+ * @author andrew.a.lamb@est.tech
+ */
+@Service
+public class PkgOnboardingNotificationConverter extends AbstractPkgNotificationConverter
+        implements Converter<PkgOnboardingNotification, VnfPackageOnboardingNotification> {
+    private static final Logger logger = getLogger(PkgOnboardingNotificationConverter.class);
+
+    /**
+     * Convert a {@link PkgOnboardingNotification} Object to an {@link VnfPackageOnboardingNotification} Object
+     * 
+     * @param pkgOnboardingNotification The PkgOnboardingNotification Object to Convert
+     * @return The Converted VnfPackageOnboardingNotification Object
+     */
+    @Override
+    public VnfPackageOnboardingNotification convert(final PkgOnboardingNotification pkgOnboardingNotification) {
+        logger.info("Converting PkgChangeNotification\n{}", pkgOnboardingNotification.toString());
+        final VnfPackageOnboardingNotification vnfPackageOnboardingNotification =
+                new VnfPackageOnboardingNotification();
+        vnfPackageOnboardingNotification.setId(pkgOnboardingNotification.getId());
+
+        if (pkgOnboardingNotification.getNotificationType() != null) {
+            vnfPackageOnboardingNotification.setNotificationType(VnfPackageOnboardingNotification.NotificationTypeEnum
+                    .fromValue(pkgOnboardingNotification.getNotificationType().getValue()));
+        }
+
+        vnfPackageOnboardingNotification.setSubscriptionId(pkgOnboardingNotification.getSubscriptionId());
+        vnfPackageOnboardingNotification.setTimeStamp(pkgOnboardingNotification.getTimeStamp());
+        vnfPackageOnboardingNotification.setVnfPkgId(pkgOnboardingNotification.getVnfPkgId());
+        vnfPackageOnboardingNotification.setVnfdId(pkgOnboardingNotification.getVnfdId());
+
+        vnfPackageOnboardingNotification.setLinks(convert((pkgOnboardingNotification.getLinks())));
+
+        return vnfPackageOnboardingNotification;
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/VnfPkgInfoConverter.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/VnfPkgInfoConverter.java
new file mode 100644
index 0000000000..dc25a6fbea
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/etsicatalog/sol003/VnfPkgInfoConverter.java
@@ -0,0 +1,198 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.converters.etsicatalog.sol003;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.onap.so.adapters.vnfmadapter.common.VnfmAdapterUrlProvider;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.Checksum;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.VNFPKGMLinkSerializer;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.VnfPackageArtifactInfo;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.VnfPackageSoftwareImageInfo;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.VnfPkgInfo;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.InlineResponse2001;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.VnfPackagesAdditionalArtifacts;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.VnfPackagesChecksum;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.VnfPackagesLinks;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.VnfPackagesLinksSelf;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.VnfPackagesSoftwareImages;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.stereotype.Service;
+
+/**
+ * Converter to convert from an Etsi Catalog Manager {@link VnfPkgInfo} Object to its equivalent SOL003 Object
+ * {@link InlineResponse2001} Object
+ *
+ * @author andrew.a.lamb@est.tech
+ */
+@Service
+public class VnfPkgInfoConverter implements Converter<VnfPkgInfo, InlineResponse2001> {
+    private static final Logger logger = LoggerFactory.getLogger(VnfPkgInfoConverter.class);
+    private final VnfmAdapterUrlProvider vnfmAdapterUrlProvider;
+
+    public VnfPkgInfoConverter(final VnfmAdapterUrlProvider vnfmAdapterUrlProvider) {
+        this.vnfmAdapterUrlProvider = vnfmAdapterUrlProvider;
+    }
+
+    /**
+     * Convert a {@link VnfPkgInfo} Object to an {@link InlineResponse2001} Object
+     * 
+     * @param vnfPkgInfo The VnfPkgInfo Object to Convert
+     * @return The Converted InlineResponse2001 Object
+     */
+    @Override
+    public InlineResponse2001 convert(final VnfPkgInfo vnfPkgInfo) {
+        if (vnfPkgInfo == null) {
+            logger.error("No VnfPkgInfo Object Provided for Conversion. (Null object received, returning Null)");
+            return null;
+        }
+        final InlineResponse2001 response = new InlineResponse2001();
+        response.setId(vnfPkgInfo.getId());
+        response.setVnfdId(vnfPkgInfo.getVnfdId());
+        response.setVnfProvider(vnfPkgInfo.getVnfProvider());
+        response.setVnfProductName(vnfPkgInfo.getVnfProductName());
+        response.setVnfSoftwareVersion(vnfPkgInfo.getVnfSoftwareVersion());
+        response.setVnfdVersion(vnfPkgInfo.getVnfdVersion());
+        response.setChecksum(convertChecksumToVnfPackagesChecksum(vnfPkgInfo.getChecksum()));
+        response.setSoftwareImages(
+                convertVnfPackageSoftwareImageInfoListToVnfPackagesSoftwareImagesList(vnfPkgInfo.getSoftwareImages()));
+        response.setAdditionalArtifacts(convertVnfPackageArtifactInfoListToVnfPackagesAdditionalArtifactsList(
+                vnfPkgInfo.getAdditionalArtifacts()));
+
+        if (vnfPkgInfo.getOnboardingState() != null) {
+            response.setOnboardingState(
+                    InlineResponse2001.OnboardingStateEnum.fromValue(vnfPkgInfo.getOnboardingState().getValue()));
+        }
+
+        if (vnfPkgInfo.getOperationalState() != null) {
+            response.setOperationalState(
+                    InlineResponse2001.OperationalStateEnum.fromValue(vnfPkgInfo.getOperationalState().getValue()));
+        }
+
+        response.setUserDefinedData((vnfPkgInfo.getUserDefinedData()));
+
+        if (vnfPkgInfo.getLinks() != null) {
+            response.setLinks(getVnfPackagesLinks(vnfPkgInfo.getLinks(), vnfPkgInfo.getId()));
+        }
+
+        return response;
+    }
+
+    private VnfPackagesChecksum convertChecksumToVnfPackagesChecksum(final Checksum checksum) {
+        final VnfPackagesChecksum vnfPackagesChecksum = new VnfPackagesChecksum();
+        if (checksum != null) {
+            vnfPackagesChecksum.setAlgorithm(checksum.getAlgorithm());
+            vnfPackagesChecksum.setHash(checksum.getHash());
+        }
+        return vnfPackagesChecksum;
+    }
+
+    private List<VnfPackagesSoftwareImages> convertVnfPackageSoftwareImageInfoListToVnfPackagesSoftwareImagesList(
+            final List<VnfPackageSoftwareImageInfo> vnfPackageSoftwareImageInfoList) {
+        final List<VnfPackagesSoftwareImages> vnfPackagesSoftwareImages = new ArrayList<>();
+        if (vnfPackageSoftwareImageInfoList != null) {
+            for (final VnfPackageSoftwareImageInfo vnfPackageSoftwareImageInfo : vnfPackageSoftwareImageInfoList) {
+                final VnfPackagesSoftwareImages softwareImage =
+                        convertVnfPackageSoftwareImageInfoToVnfPackagesSoftwareImages(vnfPackageSoftwareImageInfo);
+                vnfPackagesSoftwareImages.add(softwareImage);
+            }
+        }
+        return vnfPackagesSoftwareImages;
+    }
+
+    private VnfPackagesSoftwareImages convertVnfPackageSoftwareImageInfoToVnfPackagesSoftwareImages(
+            final VnfPackageSoftwareImageInfo vnfPackageSoftwareImageInfo) {
+        final VnfPackagesSoftwareImages vnfPackagesSoftwareImages = new VnfPackagesSoftwareImages();
+        vnfPackagesSoftwareImages.setId(vnfPackageSoftwareImageInfo.getId());
+        vnfPackagesSoftwareImages.setName(vnfPackageSoftwareImageInfo.getName());
+        vnfPackagesSoftwareImages.setProvider(vnfPackageSoftwareImageInfo.getProvider());
+        vnfPackagesSoftwareImages.setVersion(vnfPackageSoftwareImageInfo.getVersion());
+        vnfPackagesSoftwareImages
+                .setChecksum(convertChecksumToVnfPackagesChecksum(vnfPackageSoftwareImageInfo.getChecksum()));
+        if (vnfPackageSoftwareImageInfo.getContainerFormat() != null) {
+            vnfPackagesSoftwareImages.setContainerFormat(VnfPackagesSoftwareImages.ContainerFormatEnum
+                    .fromValue(vnfPackageSoftwareImageInfo.getContainerFormat().getValue()));
+        }
+
+        if (vnfPackageSoftwareImageInfo.getDiskFormat() != null) {
+            vnfPackagesSoftwareImages.setDiskFormat(VnfPackagesSoftwareImages.DiskFormatEnum
+                    .fromValue(vnfPackageSoftwareImageInfo.getDiskFormat().getValue()));
+        }
+
+        vnfPackagesSoftwareImages.setCreatedAt(vnfPackageSoftwareImageInfo.getCreatedAt());
+        vnfPackagesSoftwareImages.setMinDisk(vnfPackageSoftwareImageInfo.getMinDisk());
+        vnfPackagesSoftwareImages.setMinRam(vnfPackageSoftwareImageInfo.getMinRam());
+        vnfPackagesSoftwareImages.setSize(vnfPackageSoftwareImageInfo.getSize());
+        vnfPackagesSoftwareImages.setUserMetadata(vnfPackageSoftwareImageInfo.getUserMetadata());
+        vnfPackagesSoftwareImages.setImagePath(vnfPackageSoftwareImageInfo.getImagePath());
+        return vnfPackagesSoftwareImages;
+    }
+
+    private List<VnfPackagesAdditionalArtifacts> convertVnfPackageArtifactInfoListToVnfPackagesAdditionalArtifactsList(
+            final List<VnfPackageArtifactInfo> vnfPackageArtifactInfoList) {
+        if (vnfPackageArtifactInfoList != null) {
+            final List<VnfPackagesAdditionalArtifacts> additionalArtifacts = new ArrayList<>();
+            for (final VnfPackageArtifactInfo artifactInfo : vnfPackageArtifactInfoList) {
+                final VnfPackagesAdditionalArtifacts artifact =
+                        convertVnfPackageArtifactInfoToVnfPackagesAdditionalArtifacts(artifactInfo);
+                additionalArtifacts.add(artifact);
+            }
+            return additionalArtifacts;
+        }
+        return null;
+    }
+
+    private VnfPackagesAdditionalArtifacts convertVnfPackageArtifactInfoToVnfPackagesAdditionalArtifacts(
+            final VnfPackageArtifactInfo vnfPackageArtifactInfo) {
+        final VnfPackagesAdditionalArtifacts vnfPackagesAdditionalArtifacts = new VnfPackagesAdditionalArtifacts();
+        vnfPackagesAdditionalArtifacts.setArtifactPath(vnfPackageArtifactInfo.getArtifactPath());
+        vnfPackagesAdditionalArtifacts
+                .setChecksum(convertChecksumToVnfPackagesChecksum(vnfPackageArtifactInfo.getChecksum()));
+        vnfPackagesAdditionalArtifacts.setMetadata(vnfPackageArtifactInfo.getMetadata());
+        return vnfPackagesAdditionalArtifacts;
+    }
+
+    private VnfPackagesLinks getVnfPackagesLinks(final VNFPKGMLinkSerializer links, final String vnfPkgId) {
+        final VnfPackagesLinks vnfPackagesLinks = new VnfPackagesLinks();
+
+        if (links.getSelf() != null) {
+            vnfPackagesLinks.setSelf(getVnfPackagesLinksSelf(vnfmAdapterUrlProvider.getVnfPackageUrl(vnfPkgId)));
+        }
+
+        if (links.getVnfd() != null) {
+            vnfPackagesLinks.setVnfd(getVnfPackagesLinksSelf(vnfmAdapterUrlProvider.getVnfPackageVnfdUrl(vnfPkgId)));
+        }
+
+        if (links.getPackageContent() != null) {
+            vnfPackagesLinks.setPackageContent(
+                    getVnfPackagesLinksSelf(vnfmAdapterUrlProvider.getVnfPackageContentUrl(vnfPkgId)));
+        }
+
+        return vnfPackagesLinks;
+    }
+
+    private VnfPackagesLinksSelf getVnfPackagesLinksSelf(final String href) {
+        return new VnfPackagesLinksSelf().href(href);
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/sol003/etsicatalog/PkgmSubscriptionRequestConverter.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/sol003/etsicatalog/PkgmSubscriptionRequestConverter.java
new file mode 100644
index 0000000000..7defa4b278
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/converters/sol003/etsicatalog/PkgmSubscriptionRequestConverter.java
@@ -0,0 +1,198 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.converters.sol003.etsicatalog;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.Version;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.VnfProducts;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.VnfProductsProviders;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.PkgmSubscriptionRequest;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsFilter1;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsFilter1.NotificationTypesEnum;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsFilter1.OperationalStateEnum;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsFilter1.UsageStateEnum;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsFilterVersions;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsFilterVnfProducts;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsFilterVnfProductsFromProviders;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.stereotype.Service;
+
+/**
+ * Converter to convert from an Etsi Catalog Manager {@link PkgmSubscriptionRequest} Object to its equivalent ETSI
+ * Catalog Manager Object
+ *
+ * @author Ronan Kenny (ronan.kenny@est.tech)
+ * @author Gareth Roper (gareth.roper@est.tech)
+ */
+@Service
+public class PkgmSubscriptionRequestConverter implements
+        Converter<PkgmSubscriptionRequest, org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscriptionRequest> {
+
+    @Override
+    public org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscriptionRequest convert(
+            final PkgmSubscriptionRequest pkgmSubscriptionRequest) {
+        final org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscriptionRequest etsiCatalogManagerSubscriptionRequest =
+                new org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscriptionRequest();
+
+        etsiCatalogManagerSubscriptionRequest
+                .setFilter(getPkgmNotificationsFilter(pkgmSubscriptionRequest.getFilter()));
+
+        return etsiCatalogManagerSubscriptionRequest;
+    }
+
+
+    private org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmNotificationsFilter getPkgmNotificationsFilter(
+            final SubscriptionsFilter1 sol003SubscriptionsFilter) {
+        final org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmNotificationsFilter etsiCatalogManagerFilters =
+                new org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmNotificationsFilter();
+
+        if (sol003SubscriptionsFilter.getNotificationTypes() != null) {
+            etsiCatalogManagerFilters.setNotificationTypes(
+                    getPkgmNotificationsFilterNotificationTypes(sol003SubscriptionsFilter.getNotificationTypes()));
+        }
+
+        etsiCatalogManagerFilters.setVnfProductsFromProviders(
+                getVnfProductsProviders(sol003SubscriptionsFilter.getVnfProductsFromProviders()));
+
+        etsiCatalogManagerFilters.setVnfdId(getVnfdIds(sol003SubscriptionsFilter.getVnfdId()));
+
+        etsiCatalogManagerFilters.setVnfPkgId(getVnfPkgIds(sol003SubscriptionsFilter.getVnfPkgId()));
+
+        etsiCatalogManagerFilters
+                .setOperationalState(getOperationalState(sol003SubscriptionsFilter.getOperationalState()));
+
+        etsiCatalogManagerFilters.setUsageState(getUsageState(sol003SubscriptionsFilter.getUsageState()));
+
+        return etsiCatalogManagerFilters;
+    }
+
+
+    private List<org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmNotificationsFilter.UsageStateEnum> getUsageState(
+            final List<UsageStateEnum> usageStates) {
+        if (usageStates != null) {
+            final List<org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmNotificationsFilter.UsageStateEnum> etsiCatalogUsageStates =
+                    new ArrayList<>();
+            usageStates.stream().forEach(state -> {
+                etsiCatalogUsageStates.add(
+                        org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmNotificationsFilter.UsageStateEnum
+                                .fromValue(state.getValue()));
+            });
+            return etsiCatalogUsageStates;
+        }
+        return Collections.emptyList();
+    }
+
+
+    private List<org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmNotificationsFilter.OperationalStateEnum> getOperationalState(
+            final List<OperationalStateEnum> operationalStates) {
+        if (operationalStates != null) {
+            final List<org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmNotificationsFilter.OperationalStateEnum> etsiCatalogOperationalStates =
+                    new ArrayList<>();
+            operationalStates.forEach(state -> {
+                etsiCatalogOperationalStates.add(
+                        org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmNotificationsFilter.OperationalStateEnum
+                                .fromValue(state.getValue()));
+            });
+
+            return etsiCatalogOperationalStates;
+        }
+        return Collections.emptyList();
+    }
+
+    private List<String> getVnfPkgIds(final List<String> vnfPkgId) {
+        if (vnfPkgId != null) {
+            final List<String> etsiCatalogManagerVnfPkgId = new ArrayList<>();
+            vnfPkgId.forEach(type -> {
+                etsiCatalogManagerVnfPkgId.add(type);
+            });
+            return etsiCatalogManagerVnfPkgId;
+        }
+        return Collections.emptyList();
+    }
+
+    private List<String> getVnfdIds(final List<String> vnfdId) {
+        if (vnfdId != null) {
+            final List<String> etsiCatalogManagerVnfdId = new ArrayList<>();
+            vnfdId.forEach(type -> {
+                etsiCatalogManagerVnfdId.add(type);
+            });
+            return etsiCatalogManagerVnfdId;
+        }
+        return Collections.emptyList();
+    }
+
+    private List<VnfProductsProviders> getVnfProductsProviders(
+            final List<SubscriptionsFilterVnfProductsFromProviders> filterProductsFromProvider) {
+
+        if (filterProductsFromProvider != null && !filterProductsFromProvider.isEmpty()) {
+            final List<VnfProductsProviders> etsiCatalogManagerVnfProductsProviders = new ArrayList<>();
+            filterProductsFromProvider.forEach(vnfProduct -> {
+                etsiCatalogManagerVnfProductsProviders
+                        .add(new VnfProductsProviders().vnfProvider(vnfProduct.getVnfProvider())
+                                .vnfProducts(getVnfProducts(vnfProduct.getVnfProducts())));
+            });
+            return etsiCatalogManagerVnfProductsProviders;
+        }
+        return Collections.emptyList();
+    }
+
+    private List<VnfProducts> getVnfProducts(final List<SubscriptionsFilterVnfProducts> sol003VnfProducts) {
+        if (sol003VnfProducts != null) {
+            final List<VnfProducts> etsiCatalogManagerVnfProductsList = new ArrayList<>();
+            sol003VnfProducts.forEach(vnfProduct -> {
+                etsiCatalogManagerVnfProductsList.add(new VnfProducts().vnfProductName(vnfProduct.getVnfProductName())
+                        .versions(getVersion(vnfProduct.getVersions())));
+            });
+            return etsiCatalogManagerVnfProductsList;
+        }
+        return Collections.emptyList();
+    }
+
+    private List<Version> getVersion(final List<SubscriptionsFilterVersions> sol003FilterVersions) {
+        if (sol003FilterVersions != null && !sol003FilterVersions.isEmpty()) {
+            final List<Version> etsiCatalogVersionList = new ArrayList<>();
+            sol003FilterVersions.forEach(vnfFilterVersion -> {
+                etsiCatalogVersionList.add(new Version().vnfSoftwareVersion(vnfFilterVersion.getVnfSoftwareVersion())
+                        .vnfdVersions(vnfFilterVersion.getVnfdVersions()));
+            });
+            return etsiCatalogVersionList;
+        }
+        return Collections.emptyList();
+    }
+
+    private List<org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmNotificationsFilter.NotificationTypesEnum> getPkgmNotificationsFilterNotificationTypes(
+            final List<NotificationTypesEnum> notificationTypes) {
+
+        if (notificationTypes != null && !notificationTypes.isEmpty()) {
+            final List<org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmNotificationsFilter.NotificationTypesEnum> etsiCatalogNotificationTypes =
+                    new ArrayList<>();
+            notificationTypes.forEach(type -> etsiCatalogNotificationTypes.add(
+                    org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmNotificationsFilter.NotificationTypesEnum
+                            .fromValue(type.getValue())));
+            return etsiCatalogNotificationTypes;
+        }
+        return Collections.emptyList();
+    }
+
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/AbstractServiceProviderConfiguration.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/AbstractServiceProviderConfiguration.java
new file mode 100644
index 0000000000..7b6faf2876
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/AbstractServiceProviderConfiguration.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients;
+
+import java.util.Iterator;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.JSON;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.GsonHttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.client.RestTemplate;
+import com.google.gson.Gson;
+
+/**
+ * A base class that can be extended by classes for configuring HttpRestServiceProvider classes. Provides common methods
+ * that will be useful to some such classes.
+ *
+ * @author gareth.roper@est.tech
+ */
+public abstract class AbstractServiceProviderConfiguration {
+
+    public void setGsonMessageConverter(final RestTemplate restTemplate) {
+        final Iterator<HttpMessageConverter<?>> iterator = restTemplate.getMessageConverters().iterator();
+        while (iterator.hasNext()) {
+            if (iterator.next() instanceof MappingJackson2HttpMessageConverter) {
+                iterator.remove();
+            }
+        }
+        final Gson gson = new JSON().getGson();
+        restTemplate.getMessageConverters().add(new GsonHttpMessageConverter(gson));
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogPackageManagementServiceProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogPackageManagementServiceProvider.java
new file mode 100644
index 0000000000..9e12b536c9
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogPackageManagementServiceProvider.java
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.extclients.etsicatalog;
+
+import java.util.Optional;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.InlineResponse2001;
+
+/**
+ * @author Waqas Ikram (waqas.ikram@est.tech)
+ *
+ */
+public interface EtsiCatalogPackageManagementServiceProvider {
+
+    /**
+     * GET Package Content, from VNF Package.
+     * 
+     * @param vnfPkgId The ID of the VNF Package from which the "package_content" will be retrieved.
+     * @return The Package Content of a VNF Package ("vnfPkgId").
+     */
+    Optional<byte[]> getVnfPackageContent(final String vnfPkgId);
+
+    /**
+     * GET VNF packages information from ETSI Catalog. Will return zero or more VNF package representations.
+     *
+     * @return An Array of all VNF packages retrieved from the ETSI Catalog.
+     */
+    Optional<InlineResponse2001[]> getVnfPackages();
+
+    /**
+     * GET specific VNF package information from ETSI Catalog.
+     *
+     * @param vnfPkgId The ID of the VNF Package that you want to query.
+     * @return The VNF package retrieved from the ETSI Catalog
+     */
+    Optional<InlineResponse2001> getVnfPackage(final String vnfPkgId);
+
+    /**
+     * GET specific VNF package VNFD from ETSI Catalog.
+     *
+     * @param vnfPkgId The ID of the VNF Package that you want to query.
+     * @return The VNF package retrieved from the ETSI Catalog
+     */
+    Optional<byte[]> getVnfPackageVnfd(final String vnfPkgId);
+
+    /**
+     * GET Package Artifact, from VNF Package.
+     *
+     * @param vnfPkgId The ID of the VNF Package from which the artifact will be retrieved.
+     * @param artifactPath Sequence of one or more path segments representing the path of the artifact within the VNF
+     *        Package, e.g., foo/bar/run.sh
+     * @return The Package Artifact of a VNF Package ("vnfPkgId", "artifactPath").
+     */
+    Optional<byte[]> getVnfPackageArtifact(final String vnfPkgId, final String artifactPath);
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogServiceProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogServiceProvider.java
new file mode 100644
index 0000000000..61db82f38c
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogServiceProvider.java
@@ -0,0 +1,32 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.etsicatalog;
+
+/**
+ * Provides methods for invoking REST calls to the ETSI Catalog Manager.
+ * 
+ * @author gareth.roper@est.tech
+ */
+public interface EtsiCatalogServiceProvider
+        extends EtsiCatalogSubscriptionServiceProvider, EtsiCatalogPackageManagementServiceProvider {
+
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogServiceProviderConfiguration.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogServiceProviderConfiguration.java
new file mode 100644
index 0000000000..fc44a6ec6f
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogServiceProviderConfiguration.java
@@ -0,0 +1,187 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.etsicatalog;
+
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.util.Iterator;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.onap.logging.filter.spring.SpringClientPayloadFilter;
+import org.onap.so.adapters.vnfmadapter.extclients.AbstractServiceProviderConfiguration;
+import org.onap.so.configuration.rest.BasicHttpHeadersProvider;
+import org.onap.so.configuration.rest.HttpClientConnectionConfiguration;
+import org.onap.so.logging.jaxrs.filter.SOSpringClientFilter;
+import org.onap.so.rest.service.HttpRestServiceProvider;
+import org.onap.so.rest.service.HttpRestServiceProviderImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.Resource;
+import org.springframework.http.client.BufferingClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.GsonHttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.client.RestTemplate;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+/**
+ * Configures the HttpRestServiceProvider to make REST calls to the ETSI Catalog Manager
+ * 
+ * @author gareth.roper@est.tech
+ */
+
+@Configuration
+public class EtsiCatalogServiceProviderConfiguration extends AbstractServiceProviderConfiguration {
+
+    public static final String ETSI_CATALOG_REST_TEMPLATE_BEAN = "etsiCatalogRestTemplate";
+
+    public static final String ETSI_CATALOG_SERVICE_PROVIDER_BEAN = "etsiCatalogServiceProvider";
+
+    private final static Logger LOGGER = LoggerFactory.getLogger(EtsiCatalogServiceProviderConfiguration.class);
+
+    private final HttpClientConnectionConfiguration clientConnectionConfiguration;
+
+    @Value("${etsi-catalog-manager.http.client.ssl.trust-store:#{null}}")
+    private Resource trustStore;
+    @Value("${etsi-catalog-manager.http.client.ssl.trust-store-password:#{null}}")
+    private String trustStorePassword;
+
+    @Autowired
+    public EtsiCatalogServiceProviderConfiguration(
+            final HttpClientConnectionConfiguration clientConnectionConfiguration) {
+        this.clientConnectionConfiguration = clientConnectionConfiguration;
+    }
+
+    @Bean
+    @Qualifier(ETSI_CATALOG_REST_TEMPLATE_BEAN)
+    public RestTemplate etsiCatalogRestTemplate() {
+        final RestTemplate restTemplate = new RestTemplate();
+        restTemplate.getInterceptors().add(new SOSpringClientFilter());
+        restTemplate.getInterceptors().add((new SpringClientPayloadFilter()));
+        return restTemplate;
+    }
+
+    @Bean
+    @Qualifier(ETSI_CATALOG_SERVICE_PROVIDER_BEAN)
+    public HttpRestServiceProvider etsiCatalogHttpRestServiceProvider(
+            @Qualifier(ETSI_CATALOG_REST_TEMPLATE_BEAN) final RestTemplate restTemplate) {
+        setGsonMessageConverter(restTemplate);
+
+        final HttpClientBuilder httpClientBuilder = getHttpClientBuilder();
+        if (trustStore != null) {
+            try {
+                LOGGER.debug("Setting up HttpComponentsClientHttpRequestFactory with SSL Context");
+                LOGGER.debug("Setting client trust-store: {}", trustStore.getURL());
+                LOGGER.debug("Creating SSLConnectionSocketFactory with AllowAllHostsVerifier ... ");
+                final SSLContext sslContext = new SSLContextBuilder()
+                        .loadTrustMaterial(trustStore.getURL(), trustStorePassword.toCharArray()).build();
+                final SSLConnectionSocketFactory sslConnectionSocketFactory =
+                        new SSLConnectionSocketFactory(sslContext, AllowAllHostsVerifier.INSTANCE);
+                httpClientBuilder.setSSLSocketFactory(sslConnectionSocketFactory);
+                final Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
+                        .<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.INSTANCE)
+                        .register("https", sslConnectionSocketFactory).build();
+
+                httpClientBuilder.setConnectionManager(getConnectionManager(socketFactoryRegistry));
+            } catch (final KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
+                    | IOException exception) {
+                LOGGER.error("Error reading truststore, TLS connection will fail.", exception);
+            }
+
+        } else {
+            LOGGER.debug("Setting connection manager without SSL ConnectionSocketFactory ...");
+            httpClientBuilder.setConnectionManager(getConnectionManager());
+        }
+
+        final HttpComponentsClientHttpRequestFactory factory =
+                new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build());
+        restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(factory));
+
+        return new HttpRestServiceProviderImpl(restTemplate, new BasicHttpHeadersProvider().getHttpHeaders());
+    }
+
+    private PoolingHttpClientConnectionManager getConnectionManager(
+            final Registry<ConnectionSocketFactory> socketFactoryRegistry) {
+        return new PoolingHttpClientConnectionManager(socketFactoryRegistry, null, null, null,
+                clientConnectionConfiguration.getTimeToLiveInMins(), TimeUnit.MINUTES);
+    }
+
+    private PoolingHttpClientConnectionManager getConnectionManager() {
+        return new PoolingHttpClientConnectionManager(clientConnectionConfiguration.getTimeToLiveInMins(),
+                TimeUnit.MINUTES);
+    }
+
+    private HttpClientBuilder getHttpClientBuilder() {
+        return HttpClientBuilder.create().setMaxConnPerRoute(clientConnectionConfiguration.getMaxConnectionsPerRoute())
+                .setMaxConnTotal(clientConnectionConfiguration.getMaxConnections())
+                .setDefaultRequestConfig(getRequestConfig());
+    }
+
+    private RequestConfig getRequestConfig() {
+        return RequestConfig.custom().setSocketTimeout(clientConnectionConfiguration.getSocketTimeOutInMiliSeconds())
+                .setConnectTimeout(clientConnectionConfiguration.getConnectionTimeOutInMilliSeconds()).build();
+    }
+
+    private static final class AllowAllHostsVerifier implements HostnameVerifier {
+
+        private static final AllowAllHostsVerifier INSTANCE = new AllowAllHostsVerifier();
+
+        @Override
+        public boolean verify(final String hostname, final SSLSession session) {
+            LOGGER.debug("Skipping hostname verification ...");
+            return true;
+        }
+
+    }
+
+    public void setGsonMessageConverter(final RestTemplate restTemplate) {
+        final Iterator<HttpMessageConverter<?>> iterator = restTemplate.getMessageConverters().iterator();
+        while (iterator.hasNext()) {
+            if (iterator.next() instanceof MappingJackson2HttpMessageConverter) {
+                iterator.remove();
+            }
+        }
+        final Gson gson = new GsonBuilder().create();
+        restTemplate.getMessageConverters().add(new GsonHttpMessageConverter(gson));
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogServiceProviderImpl.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogServiceProviderImpl.java
new file mode 100644
index 0000000000..6651bc136e
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogServiceProviderImpl.java
@@ -0,0 +1,281 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.etsicatalog;
+
+import static org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.EtsiCatalogServiceProviderConfiguration.ETSI_CATALOG_SERVICE_PROVIDER_BEAN;
+import java.util.Optional;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.NsdmSubscription;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscription;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.VnfPkgInfo;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.InlineResponse2001;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.EtsiCatalogManagerBadRequestException;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.EtsiCatalogManagerRequestFailureException;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.SubscriptionNotFoundException;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.VnfPkgBadRequestException;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.VnfPkgConflictException;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.VnfPkgNotFoundException;
+import org.onap.so.rest.exceptions.HttpResouceNotFoundException;
+import org.onap.so.rest.exceptions.InvalidRestRequestException;
+import org.onap.so.rest.exceptions.RestProcessingException;
+import org.onap.so.rest.service.HttpRestServiceProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+
+/**
+ * Provides the implementations of the REST Requests to the ETSI Catalog Manager.
+ * 
+ * @author gareth.roper@est.tech
+ */
+@Service
+public class EtsiCatalogServiceProviderImpl implements EtsiCatalogServiceProvider {
+    private static final Logger logger = LoggerFactory.getLogger(EtsiCatalogServiceProviderImpl.class);
+
+    private final HttpRestServiceProvider httpServiceProvider;
+    private final EtsiCatalogUrlProvider etsiCatalogUrlProvider;
+    private final ConversionService conversionService;
+
+    @Autowired
+    public EtsiCatalogServiceProviderImpl(final EtsiCatalogUrlProvider etsiCatalogUrlProvider,
+            @Qualifier(ETSI_CATALOG_SERVICE_PROVIDER_BEAN) final HttpRestServiceProvider httpServiceProvider,
+            final ConversionService conversionService) {
+        this.etsiCatalogUrlProvider = etsiCatalogUrlProvider;
+        this.httpServiceProvider = httpServiceProvider;
+        this.conversionService = conversionService;
+    }
+
+    @Override
+    public Optional<byte[]> getVnfPackageContent(final String vnfPkgId)
+            throws EtsiCatalogManagerRequestFailureException {
+        final String vnfRequestUrl = etsiCatalogUrlProvider.getVnfPackageContentUrl(vnfPkgId);
+        final String vnfRequestName = "getVnfPackageContent";
+        return requestVnfElement(vnfPkgId, vnfRequestUrl, vnfRequestName);
+    }
+
+    @Override
+    public Optional<byte[]> getVnfPackageArtifact(final String vnfPkgId, final String artifactPath) {
+        try {
+            final ResponseEntity<byte[]> response = httpServiceProvider.getHttpResponse(
+                    etsiCatalogUrlProvider.getVnfPackageArtifactUrl(vnfPkgId, artifactPath), byte[].class);
+            logger.info("getVnfPackageArtifact Request to ETSI Catalog Manager Status Code: {}",
+                    response.getStatusCodeValue());
+            if (response.getStatusCode().is2xxSuccessful()) {
+                return Optional.ofNullable(response.getBody());
+            }
+        } catch (final HttpResouceNotFoundException httpResouceNotFoundException) {
+            logger.error("Caught HttpResouceNotFoundException", httpResouceNotFoundException);
+            throw new VnfPkgNotFoundException("No Vnf Package Artifact found with vnfPkgId: \"" + vnfPkgId
+                    + "\" and artifactPath: \"" + artifactPath + "\".");
+        } catch (final RestProcessingException restProcessingException) {
+            logger.error("Caught RestProcessingException with Status Code: {}", restProcessingException.getStatusCode(),
+                    restProcessingException);
+            if (restProcessingException.getStatusCode() == HttpStatus.CONFLICT.value()) {
+                throw new VnfPkgConflictException("A conflict occurred with the state of the resource,\n"
+                        + "due to the attribute: onboardingState not being set to ONBOARDED.");
+            }
+        }
+        throw new EtsiCatalogManagerRequestFailureException("Internal Server Error Occurred.");
+    }
+
+    @Override
+    public Optional<InlineResponse2001[]> getVnfPackages() {
+        try {
+            final ResponseEntity<VnfPkgInfo[]> response =
+                    httpServiceProvider.getHttpResponse(etsiCatalogUrlProvider.getVnfPackagesUrl(), VnfPkgInfo[].class);
+            logger.info("getVnfPackages Request to ETSI Catalog Manager Status Code: {}",
+                    response.getStatusCodeValue());
+            if (response.getStatusCode().is2xxSuccessful()) {
+                if (response.hasBody()) {
+                    final VnfPkgInfo[] vnfPackages = response.getBody();
+                    assert (vnfPackages != null);
+                    final InlineResponse2001[] responses = new InlineResponse2001[vnfPackages.length];
+                    for (int index = 0; index < vnfPackages.length; index++) {
+                        if (conversionService.canConvert(vnfPackages[index].getClass(), InlineResponse2001.class)) {
+                            final InlineResponse2001 inlineResponse2001 =
+                                    conversionService.convert(vnfPackages[index], InlineResponse2001.class);
+                            if (inlineResponse2001 != null) {
+                                responses[index] = inlineResponse2001;
+                            }
+                        } else {
+                            logger.error("Unable to find Converter for response class: {}",
+                                    vnfPackages[index].getClass());
+                        }
+                    }
+                    return Optional.of(responses);
+                }
+                logger.error("Received response without body ...");
+            }
+            logger.error("Unexpected status code received {}", response.getStatusCode());
+            return Optional.empty();
+        } catch (final InvalidRestRequestException invalidRestRequestException) {
+            logger.error("Caught InvalidRestRequestException", invalidRestRequestException);
+            throw new VnfPkgBadRequestException("Error: Bad Request Received");
+        } catch (final HttpResouceNotFoundException httpResouceNotFoundException) {
+            logger.error("Caught HttpResouceNotFoundException", httpResouceNotFoundException);
+            throw new VnfPkgNotFoundException("No Vnf Packages found");
+        } catch (final RestProcessingException restProcessingException) {
+            logger.error("Caught RestProcessingException with Status Code: {}", restProcessingException.getStatusCode(),
+                    restProcessingException);
+            throw new EtsiCatalogManagerRequestFailureException("Internal Server Error Occurred.");
+        }
+    }
+
+    @Override
+    public Optional<InlineResponse2001> getVnfPackage(final String vnfPkgId) {
+        try {
+            final ResponseEntity<VnfPkgInfo> response = httpServiceProvider
+                    .getHttpResponse(etsiCatalogUrlProvider.getVnfPackageUrl(vnfPkgId), VnfPkgInfo.class);
+            logger.info("getVnfPackage Request for vnfPkgId {} to ETSI Catalog Manager Status Code: {}", vnfPkgId,
+                    response.getStatusCodeValue());
+            if (response.getStatusCode().is2xxSuccessful()) {
+                if (response.hasBody()) {
+                    final VnfPkgInfo vnfPkgInfo = response.getBody();
+                    if (conversionService.canConvert(vnfPkgInfo.getClass(), InlineResponse2001.class)) {
+                        return Optional.ofNullable(conversionService.convert(vnfPkgInfo, InlineResponse2001.class));
+                    }
+                    logger.error("Unable to find Converter for response class: {}", vnfPkgInfo.getClass());
+                }
+                logger.error("Received response without body ....");
+            }
+            return Optional.empty();
+        } catch (final InvalidRestRequestException invalidRestRequestException) {
+            logger.error("Caught InvalidRestRequestException", invalidRestRequestException);
+            throw new VnfPkgBadRequestException("Error: Bad Request Received");
+        } catch (final HttpResouceNotFoundException httpResouceNotFoundException) {
+            logger.error("Caught HttpResouceNotFoundException", httpResouceNotFoundException);
+            throw new VnfPkgNotFoundException("No Vnf Package found with vnfPkgId: " + vnfPkgId);
+        } catch (final RestProcessingException restProcessingException) {
+            logger.error("Caught RestProcessingException with Status Code: {}", restProcessingException.getStatusCode(),
+                    restProcessingException);
+            throw new EtsiCatalogManagerRequestFailureException("Internal Server Error Occurred.");
+        }
+    }
+
+    @Override
+    public Optional<byte[]> getVnfPackageVnfd(final String vnfPkgId) {
+        final String vnfRequestUrl = etsiCatalogUrlProvider.getVnfPackageVnfdUrl(vnfPkgId);
+        final String vnfRequestName = "getVnfPackageVnfd";
+        return requestVnfElement(vnfPkgId, vnfRequestUrl, vnfRequestName);
+    }
+
+    @Override
+    public Optional<PkgmSubscription> postSubscription(
+            final org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscriptionRequest etsiCatalogManagerSubscriptionRequest) {
+        try {
+            final ResponseEntity<PkgmSubscription> responseEntity =
+                    httpServiceProvider.postHttpRequest(etsiCatalogManagerSubscriptionRequest,
+                            etsiCatalogUrlProvider.getSubscriptionUrl(), PkgmSubscription.class);
+            if (responseEntity.getStatusCode().is2xxSuccessful()) {
+                if (responseEntity.hasBody()) {
+                    return Optional.of(responseEntity.getBody());
+                }
+                logger.error("Received response without body on postSubscription");
+            }
+            logger.error("Unexpected Status Code Received on postSubscription: {}", responseEntity.getStatusCode());
+            return Optional.empty();
+        } catch (final InvalidRestRequestException invalidRestRequestException) {
+            logger.error("Caught InvalidRestRequestException", invalidRestRequestException);
+            throw new EtsiCatalogManagerBadRequestException(
+                    "Bad Request Received on postSubscription call to ETSI Catalog Manager.");
+        } catch (final RestProcessingException restProcessingException) {
+            logger.error("Caught RestProcessingException with Status Code: {}", restProcessingException.getStatusCode(),
+                    restProcessingException);
+            throw new EtsiCatalogManagerRequestFailureException(
+                    "Internal Server Error Occurred. On postSubscription with StatusCode: "
+                            + restProcessingException.getStatusCode());
+        }
+    }
+
+    @Override
+    public boolean deleteSubscription(final String subscriptionId) {
+        try {
+            final ResponseEntity<Void> responseEntity = httpServiceProvider
+                    .deleteHttpRequest(etsiCatalogUrlProvider.getSubscriptionUrl() + "/" + subscriptionId, Void.class);
+
+            if (responseEntity.getStatusCode().is2xxSuccessful()) {
+                logger.info("Subscription with ID: {} has been successfully deleted from the ETSI Catalog Manager",
+                        subscriptionId);
+                return true;
+            }
+            logger.error("Unexpected Status Code Received on deleteSubscription: {}", responseEntity.getStatusCode());
+            return false;
+        } catch (final HttpResouceNotFoundException resouceNotFoundException) {
+            final String message = "Unable to find subscription in ETSI Catalog Manager using id: " + subscriptionId;
+            logger.error(message);
+            throw new SubscriptionNotFoundException(message);
+        } catch (final InvalidRestRequestException invalidRestRequestException) {
+            logger.error("Caught InvalidRestRequestException on deleteSubscription call to ETSI Catalog Manager.",
+                    invalidRestRequestException);
+            throw new EtsiCatalogManagerBadRequestException(
+                    "Bad Request Received on deleteSubscription call to ETSI Catalog Manager.");
+        }
+    }
+
+    @Override
+    public Optional<NsdmSubscription> getSubscription(final String subscriptionId) {
+        try {
+            final ResponseEntity<NsdmSubscription> responseEntity = httpServiceProvider.getHttpResponse(
+                    etsiCatalogUrlProvider.getSubscriptionUrl() + "/" + subscriptionId, NsdmSubscription.class);
+
+            if (responseEntity.getStatusCode().is2xxSuccessful()) {
+                logger.debug("Found subscription with ID: {} in ETSI Catalog Manager", subscriptionId);
+                return Optional.ofNullable(responseEntity.getBody());
+            }
+            logger.error("Unexpected Status Code Received on getting subscription from ETSI Catalog Manager: {}",
+                    responseEntity.getStatusCode());
+        } catch (final HttpResouceNotFoundException resouceNotFoundException) {
+            logger.error("Unable to find subscription in ETSI Catalog Manager using id: {}", subscriptionId);
+            return Optional.empty();
+        } catch (final RestProcessingException | InvalidRestRequestException exception) {
+            logger.error("Unable to query ETSI Catalog Manager for subscription using id: {}", subscriptionId,
+                    exception);
+        }
+        throw new EtsiCatalogManagerRequestFailureException("Internal Server Error Occurred.");
+    }
+
+    private Optional<byte[]> requestVnfElement(final String vnfPkgId, final String vnfRequestUrl,
+            final String vnfRequestName) {
+        try {
+            final ResponseEntity<byte[]> response = httpServiceProvider.getHttpResponse(vnfRequestUrl, byte[].class);
+            logger.info("{} Request to ETSI Catalog Manager Status Code: {}", vnfRequestName,
+                    response.getStatusCodeValue());
+            if (response.getStatusCode() == HttpStatus.OK) {
+                return Optional.ofNullable(response.getBody());
+            }
+        } catch (final HttpResouceNotFoundException httpResouceNotFoundException) {
+            logger.error("Caught HttpResouceNotFoundException", httpResouceNotFoundException);
+            throw new VnfPkgNotFoundException("No Vnf Package found with vnfPkgId: " + vnfPkgId);
+        } catch (final RestProcessingException restProcessingException) {
+            logger.error("Caught RestProcessingException with Status Code: {}", restProcessingException.getStatusCode(),
+                    restProcessingException);
+            if (restProcessingException.getStatusCode() == HttpStatus.CONFLICT.value()) {
+                throw new VnfPkgConflictException("A conflict occurred with the state of the resource,\n"
+                        + "due to the attribute: onboardingState not being set to ONBOARDED.");
+            }
+        }
+        throw new EtsiCatalogManagerRequestFailureException("Internal Server Error Occurred.");
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogSubscriptionServiceProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogSubscriptionServiceProvider.java
new file mode 100644
index 0000000000..ed04ad7746
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogSubscriptionServiceProvider.java
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.extclients.etsicatalog;
+
+import java.util.Optional;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.NsdmSubscription;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscription;
+
+/**
+ * @author Waqas Ikram (waqas.ikram@est.tech)
+ *
+ */
+public interface EtsiCatalogSubscriptionServiceProvider {
+
+    /**
+     * POST the SubscriptionRequest Object.
+     *
+     * @return The ETSI Catalog Manager's PkgmSubscription object.
+     */
+    Optional<PkgmSubscription> postSubscription(
+            final org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscriptionRequest etsiCatalogManagerSubscriptionRequest);
+
+    /**
+     * Get the Subscription from ETSI Catalog.
+     * 
+     * @param subscriptionId Subscription ID
+     * @return The Subscription {@link NsdmSubscription} from ETSI Catalog
+     */
+    Optional<NsdmSubscription> getSubscription(final String subscriptionId);
+
+    /**
+     * DELETE the SubscriptionRequest Object.
+     *
+     * @return A Boolean representing if the delete was successful or not.
+     */
+    boolean deleteSubscription(final String subscriptionId);
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogUrlProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogUrlProvider.java
new file mode 100644
index 0000000000..3b4c4c3066
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/etsicatalog/EtsiCatalogUrlProvider.java
@@ -0,0 +1,113 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.etsicatalog;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+/**
+ * Provides the URLs for the REST Requests to the ETSI Catalog Manager.
+ * 
+ * @author gareth.roper@est.tech
+ */
+@Service
+public class EtsiCatalogUrlProvider {
+
+    private static final Logger logger = getLogger(EtsiCatalogUrlProvider.class);
+
+    @Value("${etsi-catalog-manager.vnfpkgm.endpoint}")
+    private String etsiCatalogManagerEndpoint;
+
+    public EtsiCatalogUrlProvider() {}
+
+    /**
+     * Get the URL for retrieving the Package Content from the ETSI Catalog.".
+     *
+     * @param vnfPkgId The ID of the VNF Package
+     * @return the URL for the GET operation
+     */
+    public String getVnfPackageContentUrl(final String vnfPkgId) {
+        final String url = etsiCatalogManagerEndpoint + "/vnf_packages/" + vnfPkgId + "/package_content";
+        logger.info("getEtsiCatalogVnfPackageContentUrl: {}", url);
+        return url;
+    }
+
+    /**
+     * Get the URL for retrieving VNF packages information from ETSI Catalog.".
+     *
+     * @return the URL for the GET operation
+     */
+    public String getVnfPackagesUrl() {
+        final String url = etsiCatalogManagerEndpoint + "/vnf_packages";
+        logger.info("getEtsiCatalogVnfPackagesEndpoint: {}", url);
+        return url;
+    }
+
+    /**
+     * Get the URL for retrieving specific VNF package information from the ETSI Catalog.".
+     *
+     * @param vnfPkgId The ID of the VNF Package
+     * @return the URL for the GET operation
+     */
+    public String getVnfPackageUrl(final String vnfPkgId) {
+        final String url = etsiCatalogManagerEndpoint + "/vnf_packages/" + vnfPkgId;
+        logger.info("getEtsiCatalogVnfPackageEndpoint: {}", url);
+        return url;
+    }
+
+    /**
+     * Get the URL for retrieving VNF Package Artifacts
+     *
+     * @param vnfPkgId The ID of the VNF Package
+     * @param artifactPath The path to the Artifact
+     * @return the URL for the GET operation
+     */
+    public String getVnfPackageArtifactUrl(final String vnfPkgId, final String artifactPath) {
+        final String url = etsiCatalogManagerEndpoint + "/vnf_packages/" + vnfPkgId + "/artifacts/" + artifactPath;
+        logger.info("getVnfPackageArtifactUrl: {}", url);
+        return url;
+    }
+
+    /**
+     * Get the URL for retrieving VNF packages vnfd from ETSI Catalog.
+     *
+     * @param vnfPkgId The ID of the VNF Package
+     * @return the URL for the GET operation
+     */
+    public String getVnfPackageVnfdUrl(final String vnfPkgId) {
+        final String url = etsiCatalogManagerEndpoint + "/vnf_packages/" + vnfPkgId + "/vnfd";
+        logger.info("getEtsiCatalogVnfPackageVnfd: {}", url);
+        return url;
+    }
+
+    /**
+     * Get the URL for posting/retrieving a Subscription
+     *
+     * @return the URL for the operation
+     */
+    public String getSubscriptionUrl() {
+        final String url = etsiCatalogManagerEndpoint + "/subscriptions";
+        logger.info("getSubscriptionNotificationUrl: {}", url);
+        return url;
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmHttpServiceProviderConfiguration.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmHttpServiceProviderConfiguration.java
new file mode 100644
index 0000000000..9ed17e4379
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmHttpServiceProviderConfiguration.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.extclients.vnfm;
+
+import static org.onap.so.client.RestTemplateConfig.CONFIGURABLE_REST_TEMPLATE;
+import org.onap.so.adapters.vnfmadapter.extclients.AbstractServiceProviderConfiguration;
+import org.onap.so.configuration.rest.BasicHttpHeadersProvider;
+import org.onap.so.rest.service.HttpRestServiceProvider;
+import org.onap.so.rest.service.HttpRestServiceProviderImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author Waqas Ikram (waqas.ikram@est.tech)
+ *
+ */
+@Configuration
+public class VnfmHttpServiceProviderConfiguration extends AbstractServiceProviderConfiguration {
+    public static final String VNFM_ADAPTER_HTTP_SERVICE_PROVIDER_BEAN = "vnfmAdapterHttpServiceProvider";
+
+    @Bean
+    @Qualifier(VNFM_ADAPTER_HTTP_SERVICE_PROVIDER_BEAN)
+    public HttpRestServiceProvider vnfmAdapterHttpRestServiceProvider(
+            @Autowired @Qualifier(CONFIGURABLE_REST_TEMPLATE) RestTemplate restTemplate) {
+        setGsonMessageConverter(restTemplate);
+        return new HttpRestServiceProviderImpl(restTemplate, new BasicHttpHeadersProvider().getHttpHeaders());
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java
new file mode 100644
index 0000000000..eaaa8d8544
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java
@@ -0,0 +1,154 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.extclients.vnfm;
+
+import static org.onap.so.client.RestTemplateConfig.CONFIGURABLE_REST_TEMPLATE;
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.net.ssl.SSLContext;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.client.HttpClient;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.onap.aai.domain.yang.EsrSystemInfo;
+import org.onap.aai.domain.yang.EsrVnfm;
+import org.onap.so.adapters.vnfmadapter.extclients.AbstractServiceProviderConfiguration;
+import org.onap.so.configuration.rest.BasicHttpHeadersProvider;
+import org.onap.so.rest.service.HttpRestServiceProvider;
+import org.onap.so.rest.service.HttpRestServiceProviderImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.Resource;
+import org.springframework.http.client.BufferingClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.security.oauth2.client.OAuth2RestTemplate;
+import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Configures the HttpRestServiceProvider for REST call to a VNFM.
+ */
+@Configuration
+public class VnfmServiceProviderConfiguration extends AbstractServiceProviderConfiguration {
+
+    private static final Logger logger = LoggerFactory.getLogger(VnfmServiceProviderConfiguration.class);
+    private Map<String, HttpRestServiceProvider> mapOfVnfmIdToHttpRestServiceProvider = new ConcurrentHashMap<>();
+
+    @Value("${http.client.ssl.trust-store:#{null}}")
+    private Resource trustStore;
+    @Value("${http.client.ssl.trust-store-password:#{null}}")
+    private String trustStorePassword;
+
+    @Value("${server.ssl.key-store:#{null}}")
+    private Resource keyStoreResource;
+    @Value("${server.ssl.key--store-password:#{null}}")
+    private String keyStorePassword;
+
+    /**
+     * This property is only intended to be temporary until the AAI schema is updated to support setting the endpoint
+     */
+    @Value("${vnfmadapter.temp.vnfm.oauth.endpoint:#{null}}")
+    private String oauthEndpoint;
+
+    @Qualifier(CONFIGURABLE_REST_TEMPLATE)
+    @Autowired()
+    private RestTemplate defaultRestTemplate;
+
+    public HttpRestServiceProvider getHttpRestServiceProvider(final EsrVnfm vnfm) {
+        if (!mapOfVnfmIdToHttpRestServiceProvider.containsKey(vnfm.getVnfmId())) {
+            mapOfVnfmIdToHttpRestServiceProvider.put(vnfm.getVnfmId(), createHttpRestServiceProvider(vnfm));
+        }
+        return mapOfVnfmIdToHttpRestServiceProvider.get(vnfm.getVnfmId());
+    }
+
+    private HttpRestServiceProvider createHttpRestServiceProvider(final EsrVnfm vnfm) {
+        final RestTemplate restTemplate = createRestTemplate(vnfm);
+        setGsonMessageConverter(restTemplate);
+        if (trustStore != null) {
+            setTrustStore(restTemplate);
+        }
+        return new HttpRestServiceProviderImpl(restTemplate, new BasicHttpHeadersProvider().getHttpHeaders());
+    }
+
+    private RestTemplate createRestTemplate(final EsrVnfm vnfm) {
+        if (vnfm != null) {
+            for (final EsrSystemInfo esrSystemInfo : vnfm.getEsrSystemInfoList().getEsrSystemInfo()) {
+                if (!StringUtils.isEmpty(esrSystemInfo.getUserName())
+                        && !StringUtils.isEmpty(esrSystemInfo.getPassword())) {
+                    return createOAuth2RestTemplate(esrSystemInfo);
+                }
+            }
+        }
+        return defaultRestTemplate;
+    }
+
+    private OAuth2RestTemplate createOAuth2RestTemplate(final EsrSystemInfo esrSystemInfo) {
+        logger.debug("Getting OAuth2RestTemplate ...");
+        final ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
+        resourceDetails.setId(UUID.randomUUID().toString());
+        resourceDetails.setClientId(esrSystemInfo.getUserName());
+        resourceDetails.setClientSecret(esrSystemInfo.getPassword());
+        resourceDetails.setAccessTokenUri(
+                oauthEndpoint == null ? esrSystemInfo.getServiceUrl().replace("vnflcm/v1", "oauth/token")
+                        : oauthEndpoint);
+        resourceDetails.setGrantType("client_credentials");
+        return new OAuth2RestTemplate(resourceDetails);
+    }
+
+    private void setTrustStore(final RestTemplate restTemplate) {
+        SSLContext sslContext;
+        try {
+            if (keyStoreResource != null) {
+                KeyStore keystore = KeyStore.getInstance("pkcs12");
+                keystore.load(keyStoreResource.getInputStream(), keyStorePassword.toCharArray());
+                sslContext =
+                        new SSLContextBuilder().loadTrustMaterial(trustStore.getURL(), trustStorePassword.toCharArray())
+                                .loadKeyMaterial(keystore, keyStorePassword.toCharArray()).build();
+            } else {
+                sslContext = new SSLContextBuilder()
+                        .loadTrustMaterial(trustStore.getURL(), trustStorePassword.toCharArray()).build();
+            }
+            logger.info("Setting truststore: {}", trustStore.getURL());
+            final SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
+            final HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
+            final HttpComponentsClientHttpRequestFactory factory =
+                    new HttpComponentsClientHttpRequestFactory(httpClient);
+            restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(factory));
+        } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
+                | IOException | UnrecoverableKeyException exception) {
+            logger.error("Error reading truststore, TLS connection to VNFM will fail.", exception);
+        }
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/AbstractNotificationServiceProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/AbstractNotificationServiceProvider.java
new file mode 100644
index 0000000000..d6b7ae7201
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/AbstractNotificationServiceProvider.java
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement;
+
+import static org.onap.so.adapters.vnfmadapter.extclients.vnfm.VnfmHttpServiceProviderConfiguration.VNFM_ADAPTER_HTTP_SERVICE_PROVIDER_BEAN;
+import java.nio.charset.StandardCharsets;
+import org.apache.commons.codec.binary.Base64;
+import org.onap.so.configuration.rest.BasicHttpHeadersProvider;
+import org.onap.so.rest.service.HttpRestServiceProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+
+/**
+ * A base class that can be extended by classes for providing notification services. Provides common methods that will
+ * be useful to those classes.
+ *
+ * @author Waqas Ikram (waqas.ikram@est.tech)
+ * @author Andrew Lamb (andrew.a.lamb@est.tech)
+ */
+public abstract class AbstractNotificationServiceProvider {
+
+    @Autowired
+    @Qualifier(VNFM_ADAPTER_HTTP_SERVICE_PROVIDER_BEAN)
+    private HttpRestServiceProvider httpRestServiceProvider;
+
+    protected HttpRestServiceProvider getHttpRestServiceProvider() {
+        return httpRestServiceProvider;
+    }
+
+    protected BasicHttpHeadersProvider getBasicHttpHeadersProviderWithBasicAuth(final String username,
+            final String password) {
+        final byte[] encodedAuth = getBasicAuth(username, password);
+        final String authHeader = "Basic " + new String(encodedAuth);
+        return new BasicHttpHeadersProvider(authHeader);
+    }
+
+    protected byte[] getBasicAuth(final String username, final String password) {
+        final String auth = username + ":" + password;
+        return Base64.encodeBase64(auth.getBytes(StandardCharsets.ISO_8859_1));
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/BasicAuthNotificationServiceProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/BasicAuthNotificationServiceProvider.java
new file mode 100644
index 0000000000..4bed300a08
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/BasicAuthNotificationServiceProvider.java
@@ -0,0 +1,68 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthentication;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthentication.AuthTypeEnum;
+import org.onap.so.configuration.rest.HttpHeadersProvider;
+import org.onap.so.rest.service.HttpRestServiceProvider;
+import org.slf4j.Logger;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+
+/**
+ * Implementation of a NotificationServiceProvider which supports Basic Authentication
+ *
+ * @author Waqas Ikram (waqas.ikram@est.tech)
+ * @author Andrew Lamb (andrew.a.lamb@est.tech)
+ */
+@Service
+public class BasicAuthNotificationServiceProvider extends AbstractNotificationServiceProvider
+        implements NotificationServiceProvider {
+
+    private static final Logger logger = getLogger(BasicAuthNotificationServiceProvider.class);
+
+    @Override
+    public boolean send(final Object notification, final SubscriptionsAuthentication subscriptionsAuthentication,
+            final String callbackUri) {
+        logger.info("Sending notification to uri: {}", callbackUri);
+        final HttpHeadersProvider httpHeadersProvider =
+                getBasicHttpHeadersProviderWithBasicAuth(subscriptionsAuthentication.getParamsBasic().getUserName(),
+                        subscriptionsAuthentication.getParamsBasic().getPassword());
+        final HttpRestServiceProvider httpRestServiceProvider = getHttpRestServiceProvider();
+
+        final ResponseEntity<Void> responseEntity = httpRestServiceProvider.postHttpRequest(notification, callbackUri,
+                httpHeadersProvider.getHttpHeaders(), Void.class);
+        if (responseEntity.getStatusCode().is2xxSuccessful()) {
+            logger.info("Notification sent successfully.");
+            return true;
+        }
+
+        logger.error("Failed to send notification.");
+        return false;
+    }
+
+    @Override
+    public AuthTypeEnum getAuthType() {
+        return AuthTypeEnum.BASIC;
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/NotificationManager.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/NotificationManager.java
new file mode 100644
index 0000000000..ad85abe053
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/NotificationManager.java
@@ -0,0 +1,137 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import java.util.List;
+import java.util.Optional;
+import org.onap.so.adapters.vnfmadapter.etsicatalog.notification.model.PkgChangeNotification;
+import org.onap.so.adapters.vnfmadapter.etsicatalog.notification.model.PkgOnboardingNotification;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.PkgmSubscriptionRequest;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthentication;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthentication.AuthTypeEnum;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.packagemanagement.notification.model.VnfPackageChangeNotification;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.packagemanagement.notification.model.VnfPackageOnboardingNotification;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.AuthenticationTypeNotSupportedException;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.ConversionFailedException;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.NotificationTypeNotSupportedException;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.SubscriptionNotFoundException;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.stereotype.Service;
+
+/**
+ * Manages package management subscription notifications to the VNFMs
+ *
+ * @author Andrew Lamb (andrew.a.lamb@est.tech)
+ *
+ */
+@Service
+public class NotificationManager {
+
+    private static final Logger logger = getLogger(NotificationManager.class);
+    private final ConversionService conversionService;
+    private final SubscriptionManager subscriptionManager;
+    private final NotificationServiceProviderFactory notificationServiceProviderFactory;
+
+    @Autowired
+    public NotificationManager(final SubscriptionManager subscriptionManager, final ConversionService conversionService,
+            final NotificationServiceProviderFactory notificationServiceProviderFactory) {
+        this.subscriptionManager = subscriptionManager;
+        this.conversionService = conversionService;
+        this.notificationServiceProviderFactory = notificationServiceProviderFactory;
+    }
+
+    /**
+     * Process a subscription notification. Checks for a subscription request stored in the adapter and if there is, it
+     * sends the notification to the subscribed vnfm.
+     * 
+     * @param notification the notification to send to the vnfm
+     * @param subscriptionId the id of the subscription request
+     * @return true if the notification is successfully sent
+     */
+    public boolean processSubscriptionNotification(final Object notification, final String subscriptionId) {
+        final Optional<PkgmSubscriptionRequest> optionalSubscription =
+                subscriptionManager.getSubscriptionRequest(subscriptionId);
+        if (optionalSubscription.isPresent()) {
+            final PkgmSubscriptionRequest subscriptionRequest = optionalSubscription.get();
+            return notifyVnfm(subscriptionRequest, notification);
+        }
+        final String errorMessage = "No subscription found with subscriptionId " + subscriptionId
+                + ". Unable to forward notification to subscriber.";
+        logger.error(errorMessage);
+        throw new SubscriptionNotFoundException(errorMessage);
+    }
+
+    private boolean notifyVnfm(final PkgmSubscriptionRequest subscriptionRequest, final Object notification) {
+        if (!(notification instanceof PkgOnboardingNotification) && !(notification instanceof PkgChangeNotification)) {
+            final String errorMessage =
+                    "An error occurred.  Notification type not supported for: " + notification.getClass();
+            logger.error(errorMessage);
+            throw new NotificationTypeNotSupportedException(errorMessage);
+        }
+
+        final SubscriptionsAuthentication subscriptionsAuthentication = subscriptionRequest.getAuthentication();
+        final AuthTypeEnum authType = getAuthType(subscriptionsAuthentication.getAuthType());
+        final NotificationServiceProvider sender = notificationServiceProviderFactory.getNotificationSender(authType);
+
+        final Object vnfmNotificationObject = convertEtsiCatalogNotification(notification);
+
+        if (sender.send(vnfmNotificationObject, subscriptionsAuthentication, subscriptionRequest.getCallbackUri())) {
+            logger.info("Notification delivered successfully {}", notification);
+            return true;
+        }
+
+        logger.error("Failed to deliver notification.");
+        return false;
+    }
+
+    private SubscriptionsAuthentication.AuthTypeEnum getAuthType(final List<AuthTypeEnum> authTypes) {
+        if (authTypes.contains(SubscriptionsAuthentication.AuthTypeEnum.TLS_CERT)) {
+            return SubscriptionsAuthentication.AuthTypeEnum.TLS_CERT;
+        }
+        if (authTypes.contains(SubscriptionsAuthentication.AuthTypeEnum.OAUTH2_CLIENT_CREDENTIALS)) {
+            return SubscriptionsAuthentication.AuthTypeEnum.OAUTH2_CLIENT_CREDENTIALS;
+        }
+        if (authTypes.contains(SubscriptionsAuthentication.AuthTypeEnum.BASIC)) {
+            return SubscriptionsAuthentication.AuthTypeEnum.BASIC;
+        }
+        final String errorMessage =
+                "An error occurred. No supported authentication type provided in subscription request.";
+        logger.error(errorMessage);
+        throw new AuthenticationTypeNotSupportedException(errorMessage);
+    }
+
+    private Object convertEtsiCatalogNotification(final Object etsiCatalogNotification) {
+        logger.info("Converting notification:\n {}", etsiCatalogNotification);
+        if (conversionService.canConvert(etsiCatalogNotification.getClass(), VnfPackageOnboardingNotification.class)) {
+            return conversionService.convert(etsiCatalogNotification, VnfPackageOnboardingNotification.class);
+        } else if (conversionService.canConvert(etsiCatalogNotification.getClass(),
+                VnfPackageChangeNotification.class)) {
+            return conversionService.convert(etsiCatalogNotification, VnfPackageChangeNotification.class);
+        }
+        final String errorMessage = "An error occurred. Unable to convert provided notification object.";
+        logger.error(errorMessage + "\n" + etsiCatalogNotification);
+        throw new ConversionFailedException(errorMessage);
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/NotificationServiceProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/NotificationServiceProvider.java
new file mode 100644
index 0000000000..87a293456f
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/NotificationServiceProvider.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement;
+
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthentication;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthentication.AuthTypeEnum;
+
+/**
+ * Interface which lays out requirements for a Notification Service Provider
+ *
+ * @author Waqas Ikram (waqas.ikram@est.tech)
+ * @author Andrew Lamb (andrew.a.lamb@est.tech)
+ */
+public interface NotificationServiceProvider {
+
+    /**
+     * Method to send a notification to a uri, given the subscription authentication
+     * 
+     * @param notification The notification to send
+     * @param subscriptionsAuthentication Object containing the authentication details
+     * @param callbackUri The uri to send the notification to
+     * @return true if notification is delivered successfully, otherwise false
+     */
+    boolean send(final Object notification, final SubscriptionsAuthentication subscriptionsAuthentication,
+            final String callbackUri);
+
+    /**
+     * Method to get the supported authorization type of the service provider
+     * 
+     * @return the supported AuthTypeEnum
+     */
+    AuthTypeEnum getAuthType();
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/NotificationServiceProviderFactory.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/NotificationServiceProviderFactory.java
new file mode 100644
index 0000000000..256e6f4da5
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/NotificationServiceProviderFactory.java
@@ -0,0 +1,67 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthentication.AuthTypeEnum;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.AuthenticationTypeNotSupportedException;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * Factory to provide a notification services
+ *
+ * @author Waqas Ikram (waqas.ikram@est.tech)
+ * @author Andrew Lamb (andrew.a.lamb@est.tech)
+ */
+@Component
+public class NotificationServiceProviderFactory {
+
+    private static final Logger logger = getLogger(NotificationServiceProviderFactory.class);
+    private static final Map<AuthTypeEnum, NotificationServiceProvider> CACHE = new HashMap<>();
+
+    @Autowired
+    public NotificationServiceProviderFactory(final List<NotificationServiceProvider> services) {
+        for (final NotificationServiceProvider notificationServiceProvider : services) {
+            logger.debug("Adding {} of type {} to cache", notificationServiceProvider.getClass().getCanonicalName(),
+                    notificationServiceProvider.getAuthType());
+            CACHE.put(notificationServiceProvider.getAuthType(), notificationServiceProvider);
+        }
+    }
+
+    /**
+     * Get a notification service for a given authorization type
+     * 
+     * @param type the type of authentication required
+     * @return the notification service
+     */
+    public NotificationServiceProvider getNotificationSender(final AuthTypeEnum type) {
+        final NotificationServiceProvider service = CACHE.get(type);
+        if (service == null) {
+            throw new AuthenticationTypeNotSupportedException("Unknown type: " + type);
+        }
+        return service;
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/OAuthNotificationServiceProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/OAuthNotificationServiceProvider.java
new file mode 100644
index 0000000000..bbb41e8515
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/OAuthNotificationServiceProvider.java
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthentication;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthentication.AuthTypeEnum;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.InternalServerErrorException;
+import org.onap.so.configuration.rest.BasicHttpHeadersProvider;
+import org.onap.so.configuration.rest.HttpHeadersProvider;
+import org.onap.so.rest.service.HttpRestServiceProvider;
+import org.slf4j.Logger;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author Waqas Ikram (waqas.ikram@est.tech)
+ * @author Andrew Lamb (andrew.a.lamb@est.tech)
+ *
+ */
+@Service
+public class OAuthNotificationServiceProvider extends AbstractNotificationServiceProvider
+        implements NotificationServiceProvider {
+
+    private static final Logger logger = getLogger(OAuthNotificationServiceProvider.class);
+
+    @Override
+    public boolean send(final Object notification, final SubscriptionsAuthentication subscriptionsAuthentication,
+            final String callbackUri) {
+        logger.info("Sending notification to uri: {}", callbackUri);
+        final String token = getAccessToken(subscriptionsAuthentication);
+
+        if (token == null) {
+            logger.error("Failed to get access token");
+            return false;
+        }
+
+        final HttpHeadersProvider httpHeadersProvider = getHttpHeadersProvider(token);
+        final HttpRestServiceProvider httpRestServiceProvider = getHttpRestServiceProvider();
+        final ResponseEntity<Void> responseEntity = httpRestServiceProvider.postHttpRequest(notification, callbackUri,
+                httpHeadersProvider.getHttpHeaders(), Void.class);
+        if (responseEntity.getStatusCode().is2xxSuccessful()) {
+            logger.info("Notification sent successfully.");
+            return true;
+        }
+
+        logger.error("Failed to send notification.");
+        return false;
+    }
+
+    @Override
+    public AuthTypeEnum getAuthType() {
+        return AuthTypeEnum.OAUTH2_CLIENT_CREDENTIALS;
+    }
+
+    private BasicHttpHeadersProvider getHttpHeadersProvider(final String token) {
+        final String authHeader = "Bearer " + token;
+        return new BasicHttpHeadersProvider(authHeader);
+    }
+
+    private String getAccessToken(final SubscriptionsAuthentication subscriptionsAuthentication) {
+        logger.info("Requesting Access Token.");
+
+        final String tokenEndpoint = subscriptionsAuthentication.getParamsOauth2ClientCredentials().getTokenEndpoint();
+
+        final HttpHeadersProvider httpHeadersProvider = getBasicHttpHeadersProviderWithBasicAuth(
+                subscriptionsAuthentication.getParamsOauth2ClientCredentials().getClientId(),
+                subscriptionsAuthentication.getParamsOauth2ClientCredentials().getClientPassword());
+
+        final HttpRestServiceProvider httpRestServiceProvider = getHttpRestServiceProvider();
+        final ResponseEntity<OAuthTokenResponse> responseEntity = httpRestServiceProvider.postHttpRequest(null,
+                tokenEndpoint, httpHeadersProvider.getHttpHeaders(), OAuthTokenResponse.class);
+        if (responseEntity.getStatusCode().is2xxSuccessful()) {
+            if (responseEntity.getBody() != null) {
+                logger.info("Returning Access Token.");
+                return responseEntity.getBody().getAccessToken();
+            }
+        }
+
+        final String errorMessage = "An error occurred.  Unable to retrieve OAuth Token from VNFM for notification.";
+        logger.error(errorMessage);
+        throw new InternalServerErrorException(errorMessage);
+    }
+
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/OAuthTokenResponse.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/OAuthTokenResponse.java
new file mode 100644
index 0000000000..146641cc7c
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/OAuthTokenResponse.java
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement;
+
+import java.io.Serializable;
+import javax.xml.bind.annotation.XmlElement;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * @author Waqas Ikram (waqas.ikram@est.tech)
+ * @author Andrew Lamb (andrew.a.lamb@est.tech)
+ */
+public class OAuthTokenResponse implements Serializable {
+
+    private static final long serialVersionUID = -6455742984985959926L;
+
+    @XmlElement(name = "access_token")
+    @SerializedName("access_token")
+    private String accessToken;
+
+    /**
+     * Get the Accees Token
+     *
+     * @return the Access Token
+     */
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    /**
+     * Set the Access Token
+     *
+     * @param accessToken
+     */
+    public void setAccessToken(final String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/SubscriptionManager.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/SubscriptionManager.java
new file mode 100644
index 0000000000..e45a175a2d
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/SubscriptionManager.java
@@ -0,0 +1,209 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement;
+
+import static org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.SubscriptionAuthentication.AuthTypeEnum.BASIC;
+import static org.slf4j.LoggerFactory.getLogger;
+import java.net.URI;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.onap.so.adapters.vnfmadapter.common.VnfmAdapterUrlProvider;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.EtsiCatalogServiceProvider;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.BasicAuth;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.NsdmSubscription;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscription;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.InlineResponse201;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.PkgmSubscriptionRequest;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsLinks;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.VnfPackagesLinksSelf;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement.cache.PackageManagementCacheServiceProvider;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.ConversionFailedException;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.InternalServerErrorException;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.SubscriptionNotFoundException;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.stereotype.Service;
+
+/**
+ * Manages package management subscriptions from the VNFMs
+ *
+ * @author Ronan Kenny (ronan.kenny@est.tech)
+ * @author Gareth Roper (gareth.roper@est.tech)
+ */
+@Service
+public class SubscriptionManager {
+
+    private static final Logger logger = getLogger(SubscriptionManager.class);
+    private final PackageManagementCacheServiceProvider packageManagementCacheServiceProvider;
+    private final ConversionService conversionService;
+    private final EtsiCatalogServiceProvider etsiCatalogServiceProvider;
+    private final VnfmAdapterUrlProvider vnfmAdapterUrlProvider;
+
+    @Autowired
+    public SubscriptionManager(final PackageManagementCacheServiceProvider packageManagementCacheServiceProvider,
+            final ConversionService conversionService, final EtsiCatalogServiceProvider etsiCatalogServiceProvider,
+            final VnfmAdapterUrlProvider vnfmAdapterUrlProvider) {
+        this.packageManagementCacheServiceProvider = packageManagementCacheServiceProvider;
+        this.conversionService = conversionService;
+        this.etsiCatalogServiceProvider = etsiCatalogServiceProvider;
+        this.vnfmAdapterUrlProvider = vnfmAdapterUrlProvider;
+    }
+
+    public Optional<InlineResponse201> createSubscription(final PkgmSubscriptionRequest pkgmSubscriptionRequest)
+            throws GeneralSecurityException {
+
+        final org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscriptionRequest etsiCatalogManagerSubscriptionRequest =
+                buildEtsiCatalogManagerPkgmSubscriptionRequest(pkgmSubscriptionRequest);
+
+        final Optional<PkgmSubscription> optionalEtsiCatalogManagerSubscription =
+                etsiCatalogServiceProvider.postSubscription(etsiCatalogManagerSubscriptionRequest);
+
+        if (optionalEtsiCatalogManagerSubscription.isPresent()) {
+            final PkgmSubscription etsiCatalogManagerSubscription = optionalEtsiCatalogManagerSubscription.get();
+            logger.debug("postPkgmSubscriptionRequest Response SubscriptionId: {}",
+                    Objects.requireNonNull(etsiCatalogManagerSubscription.getId()));
+            final String subscriptionId = etsiCatalogManagerSubscription.getId();
+
+            packageManagementCacheServiceProvider.addSubscription(subscriptionId, pkgmSubscriptionRequest);
+
+            final InlineResponse201 response = new InlineResponse201();
+            response.setId(subscriptionId);
+            response.setFilter(pkgmSubscriptionRequest.getFilter());
+            response.setCallbackUri(vnfmAdapterUrlProvider.getSubscriptionUriString(subscriptionId));
+            response.setLinks(new SubscriptionsLinks()
+                    .self(new VnfPackagesLinksSelf().href(getSubscriptionUri(subscriptionId).toString())));
+
+            return Optional.of(response);
+        }
+        throw new InternalServerErrorException(
+                "Received empty response from POST to ETSI Catalog Manager Subscription Endpoint.");
+    }
+
+    public Optional<String> getSubscriptionId(final PkgmSubscriptionRequest pkgmSubscriptionRequest) {
+        return packageManagementCacheServiceProvider.getSubscriptionId(pkgmSubscriptionRequest);
+    }
+
+    public Optional<InlineResponse201> getSubscription(final String subscriptionId) {
+
+        logger.debug("Checking if subscrition with id: {} exists in ETSI Catalog Manager", subscriptionId);
+        final Optional<NsdmSubscription> etsiCatalogSubscriptionOption =
+                etsiCatalogServiceProvider.getSubscription(subscriptionId);
+
+        if (!etsiCatalogSubscriptionOption.isPresent()) {
+            logger.debug("Unable to find subscription in ETSI Catalog Manager using id: {}", subscriptionId);
+            if (packageManagementCacheServiceProvider.getSubscription(subscriptionId).isPresent()) {
+                logger.debug("will remove subcription with id: {} from local cache", subscriptionId);
+                packageManagementCacheServiceProvider.deleteSubscription(subscriptionId);
+            }
+        }
+
+        final Optional<PkgmSubscriptionRequest> optional =
+                packageManagementCacheServiceProvider.getSubscription(subscriptionId);
+        if (optional.isPresent()) {
+            final PkgmSubscriptionRequest subscription = optional.get();
+            return Optional.of(getInlineResponse2002(subscriptionId, subscription));
+        }
+        return Optional.empty();
+    }
+
+    public List<InlineResponse201> getSubscriptions() {
+        final Map<String, PkgmSubscriptionRequest> subscriptions =
+                packageManagementCacheServiceProvider.getSubscriptions();
+        final List<InlineResponse201> response = new ArrayList<>();
+        subscriptions.forEach((key, value) -> {
+            final Optional<InlineResponse201> optional = getSubscription(key);
+            if (optional.isPresent()) {
+                response.add(optional.get());
+            }
+        });
+        return response;
+    }
+
+    public boolean deleteSubscription(final String subscriptionId) {
+        if (packageManagementCacheServiceProvider.getSubscription(subscriptionId).isPresent()) {
+            try {
+                if (etsiCatalogServiceProvider.deleteSubscription(subscriptionId)) {
+                    return packageManagementCacheServiceProvider.deleteSubscription(subscriptionId);
+                }
+            } catch (final SubscriptionNotFoundException subscriptionNotFoundException) {
+                logger.error(
+                        "Unable to find subscription in ETSI Catalog Manager using id: {} will delete it from local cache",
+                        subscriptionId);
+                return packageManagementCacheServiceProvider.deleteSubscription(subscriptionId);
+            }
+        }
+        return false;
+    }
+
+    public URI getSubscriptionUri(final String subscriptionId) {
+        return vnfmAdapterUrlProvider.getSubscriptionUri(subscriptionId);
+    }
+
+    public Optional<PkgmSubscriptionRequest> getSubscriptionRequest(final String subscriptionId) {
+        return packageManagementCacheServiceProvider.getSubscription(subscriptionId);
+    }
+
+    private InlineResponse201 getInlineResponse2002(final String id, final PkgmSubscriptionRequest subscription) {
+        return new InlineResponse201().id(id).filter(subscription.getFilter())
+                .callbackUri(subscription.getCallbackUri());
+    }
+
+    private org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscriptionRequest buildEtsiCatalogManagerPkgmSubscriptionRequest(
+            final PkgmSubscriptionRequest pkgmSubscriptionRequest) throws GeneralSecurityException {
+
+        final org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscriptionRequest etsiCatalogManagerSubscriptionRequest;
+        try {
+            etsiCatalogManagerSubscriptionRequest = conversionService.convert(pkgmSubscriptionRequest,
+                    org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscriptionRequest.class);
+        } catch (final org.springframework.core.convert.ConversionException conversionException) {
+            logger.error(conversionException.getMessage());
+            throw new ConversionFailedException(
+                    "Could not convert Sol003 PkgmSubscriptionRequest to ETSI-Catalog Manager PkgmSubscriptionRequest");
+        } catch (final Exception exception) {
+            logger.error(exception.getMessage());
+            throw new InternalServerErrorException(
+                    "Could not convert Sol003 PkgmSubscriptionRequest to ETSI-Catalog Manager PkgmSubscriptionRequest");
+        }
+
+        if (etsiCatalogManagerSubscriptionRequest != null) {
+            etsiCatalogManagerSubscriptionRequest
+                    .setCallbackUri(vnfmAdapterUrlProvider.getEtsiSubscriptionNotificationBaseUrl());
+
+            final ImmutablePair<String, String> immutablePair = vnfmAdapterUrlProvider.getDecryptAuth();
+            if (!immutablePair.equals(ImmutablePair.nullPair())) {
+                etsiCatalogManagerSubscriptionRequest.setAuthentication(
+                        new org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.SubscriptionAuthentication()
+                                .addAuthTypeItem(BASIC).paramsBasic(new BasicAuth().userName(immutablePair.getLeft())
+                                        .password(immutablePair.getRight())));
+            }
+            return etsiCatalogManagerSubscriptionRequest;
+        }
+        throw new ConversionFailedException(
+                "Failed to convert Sol003 PkgmSubscriptionRequest to ETSI-Catalog Manager PkgmSubscriptionRequest");
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/TlsNotificationServiceProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/TlsNotificationServiceProvider.java
new file mode 100644
index 0000000000..5e922293d5
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/TlsNotificationServiceProvider.java
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * 
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthentication;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthentication.AuthTypeEnum;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.AuthenticationTypeNotSupportedException;
+import org.slf4j.Logger;
+import org.springframework.stereotype.Service;
+
+/*
+ * @author Waqas Ikram (waqas.ikram@est.tech)
+ * 
+ * @author Andrew Lamb (andrew.a.lamb@est.tech)
+ */
+@Service
+public class TlsNotificationServiceProvider extends AbstractNotificationServiceProvider
+        implements NotificationServiceProvider {
+
+    private static final Logger logger = getLogger(TlsNotificationServiceProvider.class);
+
+    @Override
+    public boolean send(final Object notification, final SubscriptionsAuthentication subscriptionsAuthentication,
+            final String callbackUri) {
+        final String errorMessage = "An error occurred.  Authentication type "
+                + subscriptionsAuthentication.getAuthType().toString() + " not currently supported.";
+        logger.error(errorMessage);
+        throw new AuthenticationTypeNotSupportedException(errorMessage);
+    }
+
+
+    @Override
+    public AuthTypeEnum getAuthType() {
+        return AuthTypeEnum.TLS_CERT;
+    }
+
+
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/AbstractCacheServiceProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/AbstractCacheServiceProvider.java
new file mode 100644
index 0000000000..e1e9b2307e
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/AbstractCacheServiceProvider.java
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement.cache;
+
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+
+/**
+ * @author Ronan Kenny (ronan.kenny@est.tech)
+ * @author Gareth Roper (gareth.roper@est.tech)
+ */
+public abstract class AbstractCacheServiceProvider {
+
+    private final CacheManager cacheManager;
+    private final String cacheName;
+
+    public AbstractCacheServiceProvider(final String cacheName, final CacheManager cacheManager) {
+        this.cacheName = cacheName;
+        this.cacheManager = cacheManager;
+    }
+
+    public Cache getCache() {
+        final Cache cache = cacheManager.getCache(cacheName);
+        if (cache == null) {
+            throw new CacheNotFoundException("Unable to find " + cacheName + " cache");
+        }
+        return cache;
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/CacheManagerConfiguration.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/CacheManagerConfiguration.java
new file mode 100644
index 0000000000..a2e1d76ad6
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/CacheManagerConfiguration.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement.cache;
+
+import java.util.Arrays;
+import org.onap.so.adapters.vnfmadapter.PackageManagementConstants;
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.concurrent.ConcurrentMapCache;
+import org.springframework.cache.support.SimpleCacheManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Ronan Kenny (ronan.kenny@est.tech)
+ * @author Gareth Roper (gareth.roper@est.tech)
+ */
+@Configuration
+public class CacheManagerConfiguration {
+
+    @Bean
+    public CacheManager cacheManager() {
+        final SimpleCacheManager manager = new SimpleCacheManager();
+        manager.setCaches(Arrays.asList(getCache(PackageManagementConstants.PACKAGE_MANAGEMENT_SUBSCRIPTION_CACHE)));
+
+        return manager;
+    }
+
+    private Cache getCache(final String name) {
+        return new ConcurrentMapCache(name);
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/CacheNotFoundException.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/CacheNotFoundException.java
new file mode 100644
index 0000000000..edd5982ab1
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/CacheNotFoundException.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement.cache;
+
+/**
+ * Exception for failure to find the cache.
+ *
+ * @author Ronan Kenny (ronan.kenny@est.tech)
+ * @author Gareth Roper (gareth.roper@est.tech)
+ *
+ */
+public class CacheNotFoundException extends RuntimeException {
+
+    private static final long serialVersionUID = -372361485260755367L;
+
+    public CacheNotFoundException(final String message) {
+        super(message);
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/PackageManagementCacheServiceProvider.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/PackageManagementCacheServiceProvider.java
new file mode 100644
index 0000000000..713d14b077
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/PackageManagementCacheServiceProvider.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement.cache;
+
+import java.util.Map;
+import java.util.Optional;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.PkgmSubscriptionRequest;
+
+/**
+ * Interface which provides methods for communicating with the cache
+ *
+ * @author Ronan Kenny (ronan.kenny@est.tech)
+ * @author Gareth Roper (gareth.roper@est.tech)
+ *
+ */
+public interface PackageManagementCacheServiceProvider {
+
+    /**
+     * Checks cache if subscription request Id is already present. If not, it adds the subscription to the cache.
+     * 
+     * @param subscriptionId
+     * @param pkgmSubscriptionRequest
+     */
+    void addSubscription(final String subscriptionId, final PkgmSubscriptionRequest pkgmSubscriptionRequest);
+
+    /**
+     * Gets individual subscription from cache
+     * 
+     * @param subscriptionId
+     * @return <AbstractMap.SimpleImmutableEntry<String, PkgmSubscriptionRequest>>
+     */
+    Optional<PkgmSubscriptionRequest> getSubscription(final String subscriptionId);
+
+    /**
+     * Gets Map of subscriptions from cache
+     * 
+     * @return Map<String, PkgmSubscriptionRequest>>
+     */
+    Map<String, PkgmSubscriptionRequest> getSubscriptions();
+
+    /**
+     * Delete subscription from cache
+     * 
+     * @param subscriptionId
+     * @return Boolean
+     */
+    boolean deleteSubscription(final String subscriptionId);
+
+    /**
+     * Checks if subscription exists in cache and return its subscriptionId
+     * 
+     * @param pkgmSubscriptionRequest
+     * @return Subscription Id
+     */
+    Optional<String> getSubscriptionId(final PkgmSubscriptionRequest pkgmSubscriptionRequest);
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/PackageManagementCacheServiceProviderImpl.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/PackageManagementCacheServiceProviderImpl.java
new file mode 100644
index 0000000000..82438228ec
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/packagemanagement/subscriptionmanagement/cache/PackageManagementCacheServiceProviderImpl.java
@@ -0,0 +1,119 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement.cache;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import org.onap.so.adapters.vnfmadapter.PackageManagementConstants;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.PkgmSubscriptionRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+import org.springframework.stereotype.Service;
+
+/**
+ * Implementation which provides methods for communicating with the cache
+ *
+ * @author Ronan Kenny (ronan.kenny@est.tech)
+ * @author Gareth Roper (gareth.roper@est.tech)
+ */
+@Service
+public class PackageManagementCacheServiceProviderImpl extends AbstractCacheServiceProvider
+        implements PackageManagementCacheServiceProvider {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(PackageManagementCacheServiceProviderImpl.class);
+
+    @Autowired
+    public PackageManagementCacheServiceProviderImpl(final CacheManager cacheManager) {
+        super(PackageManagementConstants.PACKAGE_MANAGEMENT_SUBSCRIPTION_CACHE, cacheManager);
+    }
+
+    @Override
+    public void addSubscription(final String subscriptionId, final PkgmSubscriptionRequest pkgmSubscriptionRequest) {
+        LOGGER.debug("Adding {} to cache with subscription id: {}", pkgmSubscriptionRequest, subscriptionId);
+        getCache().put(subscriptionId, pkgmSubscriptionRequest);
+    }
+
+    @Override
+    public Optional<PkgmSubscriptionRequest> getSubscription(final String subscriptionId) {
+        LOGGER.debug("Getting subscription from cache using Id: {}", subscriptionId);
+        final Cache cache = getCache();
+        final PkgmSubscriptionRequest cacheValue = cache.get(subscriptionId, PkgmSubscriptionRequest.class);
+        if (cacheValue != null) {
+            return Optional.of(cacheValue);
+        }
+        LOGGER.error("Unable to find Subscription in cache using Id: {}", subscriptionId);
+        return Optional.empty();
+    }
+
+    @Override
+    public Map<String, PkgmSubscriptionRequest> getSubscriptions() {
+        LOGGER.info("Getting all subscriptions from cache");
+        final Cache cache = getCache();
+
+        final Object nativeCache = cache.getNativeCache();
+        if (nativeCache instanceof ConcurrentHashMap) {
+            @SuppressWarnings("unchecked")
+            final ConcurrentHashMap<Object, Object> concurrentHashMap = (ConcurrentHashMap<Object, Object>) nativeCache;
+            final Map<String, PkgmSubscriptionRequest> result = new HashMap<>();
+            concurrentHashMap.keySet().forEach(key -> {
+                final Optional<PkgmSubscriptionRequest> optional = getSubscription(key.toString());
+                optional.ifPresent(pkgmSubscriptionRequest -> result.put(key.toString(), pkgmSubscriptionRequest));
+            });
+            return result;
+        }
+        LOGGER.error("Unable to find Subscriptions in cache");
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public boolean deleteSubscription(final String subscriptionId) {
+        final Cache cache = getCache();
+        final Optional<PkgmSubscriptionRequest> optional = getSubscription(subscriptionId);
+        if (optional.isPresent()) {
+            cache.evict(subscriptionId);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public Optional<String> getSubscriptionId(final PkgmSubscriptionRequest pkgmSubscriptionRequest) {
+        final Cache cache = getCache();
+        final Object nativeCache = cache.getNativeCache();
+        if (nativeCache instanceof ConcurrentHashMap) {
+            @SuppressWarnings("unchecked")
+            final ConcurrentHashMap<Object, Object> concurrentHashMap = (ConcurrentHashMap<Object, Object>) nativeCache;
+            final Optional<Entry<Object, Object>> optional = concurrentHashMap.entrySet().stream()
+                    .filter(entry -> entry.getValue().equals(pkgmSubscriptionRequest)).findAny();
+            if (optional.isPresent()) {
+                return Optional.of(optional.get().getKey().toString());
+            }
+        }
+        return Optional.empty();
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/EtsiSubscriptionNotificationController.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/EtsiSubscriptionNotificationController.java
new file mode 100644
index 0000000000..fe9b085095
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/EtsiSubscriptionNotificationController.java
@@ -0,0 +1,131 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import static org.onap.so.adapters.vnfmadapter.common.CommonConstants.ETSI_SUBSCRIPTION_NOTIFICATION_CONTROLLER_BASE_URL;
+import static org.slf4j.LoggerFactory.getLogger;
+import java.util.AbstractMap;
+import java.util.Map.Entry;
+import javax.ws.rs.core.MediaType;
+import org.onap.so.adapters.vnfmadapter.etsicatalog.notification.model.PkgChangeNotification;
+import org.onap.so.adapters.vnfmadapter.etsicatalog.notification.model.PkgOnboardingNotification;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement.NotificationManager;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.InternalServerErrorException;
+import org.onap.so.adapters.vnfmadapter.rest.exceptions.NotificationTypeNotSupportedException;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+/**
+ * This controller handles the ETSI Subscription Notification Endpoints.
+ *
+ * @author Ronan Kenny (ronan.kenny@est.tech)
+ * @author Gareth Roper (gareth.roper@est.tech)
+ * @author Andrew Lamb (andrew.a.lamb@est.tech)
+ */
+@Controller
+@RequestMapping(value = ETSI_SUBSCRIPTION_NOTIFICATION_CONTROLLER_BASE_URL)
+public class EtsiSubscriptionNotificationController {
+
+    private static final Logger logger = getLogger(EtsiSubscriptionNotificationController.class);
+    private final NotificationManager notificationManager;
+    private final Gson gson;
+
+    @Autowired
+    public EtsiSubscriptionNotificationController(final NotificationManager notificationManager) {
+        this.notificationManager = notificationManager;
+        this.gson = new GsonBuilder().create();
+    }
+
+    @GetMapping(value = "/notification")
+    public ResponseEntity<Void> testSubscriptionNotificationEndPoint() {
+        logger.debug("Testing Notification Endpoint");
+        return ResponseEntity.noContent().build();
+    }
+
+    /**
+     * POST notification on to subscriber.
+     * 
+     * @param notification The notification to send.
+     * @return Response Code: 204 No Content if Successful, ProblemDetails Object if not.
+     */
+    @PostMapping(value = "/notification", consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON)
+    public ResponseEntity<?> postSubscriptionNotification(@RequestBody final String notification) {
+        logger.info("Posting subscription notification \n{}", notification);
+
+        final Entry<String, Object> notificationObject = getNotificationObject(notification);
+        if (notificationManager.processSubscriptionNotification(notificationObject.getValue(),
+                notificationObject.getKey())) {
+            logger.info("Notification Delivered Successfully");
+            return ResponseEntity.noContent().build();
+        }
+        final String errorMessage = "An error occurred.  Sending of notification to VNFM failed.";
+        logger.error(errorMessage);
+        throw new InternalServerErrorException(errorMessage);
+    }
+
+    private Entry<String, Object> getNotificationObject(final String notification) {
+        final String notificationType = getNotificationType(notification);
+        if (PkgOnboardingNotification.NotificationTypeEnum.VNFPACKAGEONBOARDINGNOTIFICATION.getValue()
+                .equals(notificationType)) {
+            final PkgOnboardingNotification pkgOnboardingNotification =
+                    gson.fromJson(notification, PkgOnboardingNotification.class);
+            logger.info("Onboarding notification received:\n{}", pkgOnboardingNotification);
+            return new AbstractMap.SimpleEntry<>(pkgOnboardingNotification.getSubscriptionId(),
+                    pkgOnboardingNotification);
+        }
+        if (PkgChangeNotification.NotificationTypeEnum.VNFPACKAGECHANGENOTIFICATION.getValue()
+                .equals(notificationType)) {
+            final PkgChangeNotification pkgChangeNotification =
+                    gson.fromJson(notification, PkgChangeNotification.class);
+            logger.info("Change notification received:\n{}", pkgChangeNotification);
+            return new AbstractMap.SimpleEntry<>(pkgChangeNotification.getSubscriptionId(), pkgChangeNotification);
+
+        }
+
+        final String errorMessage = "An error occurred.  Notification type not supported for: " + notificationType;
+        logger.error(errorMessage);
+        throw new NotificationTypeNotSupportedException(errorMessage);
+
+    }
+
+    private String getNotificationType(final String notification) {
+        try {
+            final JsonParser parser = new JsonParser();
+            final JsonObject element = (JsonObject) parser.parse(notification);
+            return element.get("notificationType").getAsString();
+        } catch (final Exception e) {
+            logger.error("An error occurred processing notificiation: {}", e.getMessage());
+        }
+        throw new NotificationTypeNotSupportedException(
+                "Unable to parse notification type in object \n" + notification);
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementController.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementController.java
new file mode 100644
index 0000000000..48d6a33082
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementController.java
@@ -0,0 +1,176 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import static org.onap.so.adapters.vnfmadapter.PackageManagementConstants.APPLICATION_ZIP;
+import static org.onap.so.adapters.vnfmadapter.common.CommonConstants.PACKAGE_MANAGEMENT_BASE_URL;
+import static org.slf4j.LoggerFactory.getLogger;
+import java.util.Optional;
+import javax.ws.rs.core.MediaType;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.EtsiCatalogServiceProvider;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.ProblemDetails;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.InlineResponse2001;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * Controller for handling the VNF Package Management. For further information please read:
+ * https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/003/02.05.01_60/gs_nfv-sol003v020501p.pdf Use the section number
+ * above each endpoint to find the corresponding section in the above document.
+ *
+ * @author gareth.roper@est.tech
+ */
+@Controller
+@RequestMapping(value = PACKAGE_MANAGEMENT_BASE_URL, consumes = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+public class Sol003PackageManagementController {
+
+    private final EtsiCatalogServiceProvider etsiCatalogServiceProvider;
+    private static final String LOG_REQUEST_RECEIVED = "VNF PackageManagement Controller: {} {} {} {}";
+    private static final Logger logger = getLogger(Sol003PackageManagementController.class);
+
+    @Autowired
+    Sol003PackageManagementController(final EtsiCatalogServiceProvider etsiCatalogServiceProvider) {
+        this.etsiCatalogServiceProvider = etsiCatalogServiceProvider;
+    }
+
+    /**
+     * GET VNF packages information. Will return zero or more VNF package representations that match the attribute
+     * filter. These representations will be in a list. Section Number: 10.4.2
+     * 
+     * @return An Array of all VNF packages. Object: InlineResponse2001[] Response Code: 200 OK
+     */
+    @GetMapping(value = "/vnf_packages", produces = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    public ResponseEntity<?> getVnfPackages() {
+        logger.info(LOG_REQUEST_RECEIVED, "getVnfPackages.");
+        final Optional<InlineResponse2001[]> response = etsiCatalogServiceProvider.getVnfPackages();
+        if (response.isPresent()) {
+            logger.info(LOG_REQUEST_RECEIVED, "getVnfPackages Response: ", HttpStatus.OK);
+            return ResponseEntity.ok().body(response.get());
+        }
+        final String errorMessage = "An error occurred, a null response was received by the\n"
+                + " Sol003PackageManagementController from the EtsiCatalogManager using the GET \"vnf_packages\" \n"
+                + "endpoint.";
+        logger.error(errorMessage);
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ProblemDetails().detail(errorMessage));
+    }
+
+    /**
+     * GET VNF package information. Will return a specific VNF package representation that match the attribute filter.
+     * Section Number: 10.4.3
+     *
+     * @param vnfPkgId The ID of the VNF Package that you want to query.
+     * @return A VNF package based on vnfPkgId. Object: VnfPkgInfo Response Code: 200 OK
+     */
+    @GetMapping(value = "/vnf_packages/{vnfPkgId}", produces = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    public ResponseEntity<?> getVnfPackage(@PathVariable("vnfPkgId") final String vnfPkgId) {
+        logger.info(LOG_REQUEST_RECEIVED, "getVnfPackage: ", vnfPkgId);
+        final Optional<InlineResponse2001> response = etsiCatalogServiceProvider.getVnfPackage(vnfPkgId);
+        if (response.isPresent()) {
+            logger.info(LOG_REQUEST_RECEIVED, "getVnfPackage Response: ", HttpStatus.OK);
+            return ResponseEntity.ok().body(response.get());
+        }
+        final String errorMessage = "An error occurred, a null response was received by the\n"
+                + " Sol003PackageManagementController from the EtsiCatalogManager using the GET \"vnf_packages\" by vnfPkgId: \""
+                + vnfPkgId + "\" \n" + "endpoint.";
+        logger.error(errorMessage);
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ProblemDetails().detail(errorMessage));
+    }
+
+    /**
+     * GET VNFD, from VNF package. Will return a copy of the file representing the VNFD or a ZIP file that contains the
+     * file/multiple files representing the VNFD specified. Section Number: 10.4.4
+     *
+     * @param vnfPkgId The ID of the VNF Package that you want to retrieve the VNFD from.
+     * @return The VNFD of a VNF Package as a single file or within a ZIP file. Object: byte[] Response Code: 200 OK
+     */
+    @GetMapping(value = "/vnf_packages/{vnfPkgId}/vnfd",
+            produces = {MediaType.TEXT_PLAIN, APPLICATION_ZIP, MediaType.APPLICATION_JSON})
+    public ResponseEntity<?> getVnfPackageVnfd(@PathVariable("vnfPkgId") final String vnfPkgId) {
+        logger.info(LOG_REQUEST_RECEIVED, "getVnfPackageVnfd Endpoint Invoked with VNF Package ID: ", vnfPkgId);
+        final Optional<byte[]> response = etsiCatalogServiceProvider.getVnfPackageVnfd(vnfPkgId);
+        if (response.isPresent()) {
+            logger.info(LOG_REQUEST_RECEIVED, "getVnfPackageVnfd Response: ", HttpStatus.OK);
+            return new ResponseEntity<>(response.get(), HttpStatus.OK);
+        }
+        final String errorMessage = "An error occurred, a null response was received by the\n"
+                + " Sol003PackageManagementController from the EtsiCatalogManager using the GET \"vnfd\" \n"
+                + "endpoint.";
+
+        logger.error(errorMessage);
+        return new ResponseEntity<>(new ProblemDetails().detail(errorMessage), HttpStatus.INTERNAL_SERVER_ERROR);
+    }
+
+    /**
+     * GET Package Content, from VNF Package. Will return a copy of the VNF package file that you specified. Section
+     * Number: 10.4.5
+     * 
+     * @param vnfPkgId The ID of the VNF Package that you want to retrieve the "package_content" from.
+     * @return The Package Content of a VNF Package. Object: byte[] Response Code: 200 OK
+     */
+    @GetMapping(value = "/vnf_packages/{vnfPkgId}/package_content",
+            produces = {MediaType.APPLICATION_JSON, APPLICATION_ZIP, MediaType.APPLICATION_OCTET_STREAM})
+    public ResponseEntity<?> getVnfPackageContent(@PathVariable("vnfPkgId") final String vnfPkgId) {
+        logger.info(LOG_REQUEST_RECEIVED, "getVnfPackageContent Endpoint Invoked with VNF Package ID: ", vnfPkgId);
+        final Optional<byte[]> response = etsiCatalogServiceProvider.getVnfPackageContent(vnfPkgId);
+        if (response.isPresent()) {
+            logger.info(LOG_REQUEST_RECEIVED, "getVnfPackageContent Response: ", HttpStatus.OK);
+            return ResponseEntity.ok().body(response.get());
+        }
+        final String errorMessage = "An error occurred, a null response was received by the\n"
+                + " Sol003PackageManagementController from the EtsiCatalogManager using the GET \"package_content\" \n"
+                + "endpoint.";
+        logger.error(errorMessage);
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ProblemDetails().detail(errorMessage));
+    }
+
+    /**
+     * GET Artifact, from VNF Package Will return a the content of the artifact that you specified. Section Number:
+     * 10.4.6
+     * 
+     * @param vnfPkgId The ID of the VNF Package that you want to retrieve an artifact from.
+     * @param artifactPath The path of the artifact that you want to retrieve.
+     * @return An Artifact from a VNF Package. Object: byte[] Response Code: 200 OK
+     */
+    @GetMapping(value = "/vnf_packages/{vnfPkgId}/artifacts/{artifactPath}",
+            produces = {MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON})
+    public ResponseEntity<?> getVnfPackageArtifact(@PathVariable("vnfPkgId") final String vnfPkgId,
+            @PathVariable("artifactPath") final String artifactPath) {
+        logger.info(LOG_REQUEST_RECEIVED, "getVnfPackageArtifact: vnfPkgId= ", vnfPkgId, " artifactPath=",
+                artifactPath);
+        final Optional<byte[]> response = etsiCatalogServiceProvider.getVnfPackageArtifact(vnfPkgId, artifactPath);
+        if (response.isPresent()) {
+            logger.info(LOG_REQUEST_RECEIVED, "getVnfPackageArtifact Response: ", HttpStatus.OK);
+            return ResponseEntity.ok().body(response.get());
+        }
+        final String errorMessage = "An error occurred, a null response was received by the\n"
+                + " Sol003PackageManagementController from the EtsiCatalogManager using the\n GET \"vnf_packages\" by vnfPkgId: \""
+                + vnfPkgId + "\" for artifactPath: \"" + artifactPath + "\"\n" + "endpoint.";
+        logger.error(errorMessage);
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ProblemDetails().detail(errorMessage));
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementSubscriptionController.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementSubscriptionController.java
new file mode 100644
index 0000000000..75c72b6df3
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementSubscriptionController.java
@@ -0,0 +1,172 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import static org.onap.so.adapters.vnfmadapter.common.CommonConstants.PACKAGE_MANAGEMENT_BASE_URL;
+import static org.slf4j.LoggerFactory.getLogger;
+import java.net.URI;
+import java.security.GeneralSecurityException;
+import java.util.List;
+import java.util.Optional;
+import javax.ws.rs.core.MediaType;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.ProblemDetails;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.InlineResponse201;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.PkgmSubscriptionRequest;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.subscriptionmanagement.SubscriptionManager;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * Controller for handling the Subscription Management. The client can use this resource to subscribe to notifications
+ * related to the VNF package management, and to query its subscriptions. For further information please read:
+ * https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/003/02.05.01_60/gs_nfv-sol003v020501p.pdf Use the section number
+ * above each endpoint to find the corresponding section in the above document.
+ *
+ * @author Ronan Kenny (ronan.kenny@est.tech)
+ * @author Gareth Roper (gareth.roper@est.tech)
+ */
+@Controller
+@RequestMapping(value = PACKAGE_MANAGEMENT_BASE_URL, produces = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML},
+        consumes = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+public class Sol003PackageManagementSubscriptionController {
+
+    private static final String LOG_REQUEST_RECEIVED = "Subscription Management Controller: {} {}";
+    private static final Logger logger = getLogger(Sol003PackageManagementSubscriptionController.class);
+    private final SubscriptionManager subscriptionManager;
+
+    @Autowired
+    public Sol003PackageManagementSubscriptionController(final SubscriptionManager subscriptionManager) {
+        this.subscriptionManager = subscriptionManager;
+    }
+
+    /**
+     * POST Subscribe request. Will send request and respond with the subscription that you subscribed to, if
+     * successful. Section Number: 10.4.7
+     * 
+     * @param pkgmSubscriptionRequest This includes the details of the subscription to be created.
+     * @return The subscription requested, if successful. Object: InlineRespone2002 Response Code: 201 Created Response
+     *         Code: 303 Duplicate Subscription
+     * @throws GeneralSecurityException
+     */
+    @PostMapping(value = "/subscriptions")
+    public ResponseEntity<?> postSubscriptionRequest(@RequestBody final PkgmSubscriptionRequest pkgmSubscriptionRequest)
+            throws GeneralSecurityException {
+        logger.info(LOG_REQUEST_RECEIVED, " postSubscriptionRequest Endpoint Called", pkgmSubscriptionRequest);
+
+        // Check if subscription exists already.
+        final Optional<String> exists = subscriptionManager.getSubscriptionId(pkgmSubscriptionRequest);
+
+        if (exists.isPresent()) {
+            final URI subscriptionUri = subscriptionManager.getSubscriptionUri(exists.get());
+            final HttpHeaders headers = createLocationHeader(subscriptionUri);
+            logger.info("PkgmSubscriptionRequest already exists with uri {} ", subscriptionUri);
+            return new ResponseEntity<>(headers, HttpStatus.SEE_OTHER);
+        }
+
+        logger.debug("No duplicate Subscription exists, continuing with POST.");
+        final Optional<InlineResponse201> optionalInlineResponse =
+                subscriptionManager.createSubscription(pkgmSubscriptionRequest);
+
+        if (optionalInlineResponse.isPresent()) {
+            InlineResponse201 inlineResponse = optionalInlineResponse.get();
+            final URI subscriptionUri = subscriptionManager.getSubscriptionUri(inlineResponse.getId());
+            final HttpHeaders headers = createLocationHeader(subscriptionUri);
+            logger.debug("Sending response with uri {} ", subscriptionUri);
+            return new ResponseEntity<>(inlineResponse, headers, HttpStatus.CREATED);
+        }
+        final String errorMessage = "A null response was received during the postSubscriptionRequest call.";
+        logger.error(errorMessage);
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ProblemDetails().detail(errorMessage));
+    }
+
+    /**
+     * GET all subscriptions. Will return a list of all subscriptions currently active. Section Number: 10.4.7
+     * 
+     * @return All of the current active subscriptions. Object: List<InlineResponse2002> Response Code: 200 OK
+     */
+    @GetMapping(value = "/subscriptions")
+    public ResponseEntity<List<InlineResponse201>> getSubscriptions() {
+        logger.info(LOG_REQUEST_RECEIVED, " getSubscriptions.");
+        List<InlineResponse201> subscriptionsList = subscriptionManager.getSubscriptions();
+        return new ResponseEntity<>(subscriptionsList, HttpStatus.OK);
+    }
+
+    /**
+     * GET a specific subscription, by subscriptionId. Section Number: 10.4.8
+     * 
+     * @param subscriptionId The ID of the subscription that you wish to retrieve.
+     * @return A subscription based on subscriptionId. Object: InlineResponse2002 Response Code: 200 OK
+     */
+    @GetMapping(value = "/subscriptions/{subscriptionId}")
+    public ResponseEntity<?> getSubscription(@PathVariable("subscriptionId") final String subscriptionId) {
+        logger.info(LOG_REQUEST_RECEIVED, " Getting Subscription: ", subscriptionId);
+        final Optional<InlineResponse201> optional = subscriptionManager.getSubscription(subscriptionId);
+        if (optional.isPresent()) {
+            logger.debug("Return subscription with id {} and body {}", subscriptionId, optional);
+            return new ResponseEntity<>(optional.get(), HttpStatus.OK);
+        }
+        final String errorMessage =
+                "The requested subscription: " + subscriptionId + " was not found on call getSubscription";
+        logger.error(errorMessage);
+        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ProblemDetails().detail(errorMessage));
+    }
+
+    /**
+     * DELETE a specific subscription, by subscriptionId. Section Number: 10.4.8.3.5
+     *
+     * @param subscriptionId The ID of the subscription that you wish to delete.
+     * @return Empty response if successful. Object: Void Response Code: 204 No Content
+     */
+    @DeleteMapping(value = "/subscriptions/{subscriptionId}")
+    public ResponseEntity<?> deleteSubscription(@PathVariable("subscriptionId") final String subscriptionId) {
+        if (subscriptionManager.deleteSubscription(subscriptionId)) {
+            logger.debug("Successfully deleted subscription with id {}", subscriptionId);
+            return ResponseEntity.noContent().build();
+        }
+        final String errorMessage =
+                "The requested subscription: " + subscriptionId + " was not found on call deleteSubscription";
+        logger.error(errorMessage);
+        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ProblemDetails().detail(errorMessage));
+    }
+
+    /**
+     * Method to set the Location in the header with the URI parameter
+     * 
+     * @param subscriptionUri
+     * @return header with callbackUri in Location
+     */
+    private HttpHeaders createLocationHeader(final URI subscriptionUri) {
+        final HttpHeaders headers = new HttpHeaders();
+        headers.setLocation(subscriptionUri);
+        return headers;
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/AuthenticationTypeNotSupportedException.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/AuthenticationTypeNotSupportedException.java
new file mode 100644
index 0000000000..303420f1e8
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/AuthenticationTypeNotSupportedException.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception for an unsupported authentication type
+ * 
+ * @author Andrew Lamb (andrew.a.lamb@est.tech)
+ */
+@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
+public class AuthenticationTypeNotSupportedException extends RuntimeException {
+
+    private static final long serialVersionUID = 2939423208362066902L;
+
+    public AuthenticationTypeNotSupportedException(final String message) {
+        super(message);
+    }
+
+    @Override
+    public synchronized Throwable fillInStackTrace() {
+        return this;
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/ConversionFailedException.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/ConversionFailedException.java
new file mode 100644
index 0000000000..e19a53d1dc
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/ConversionFailedException.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception for Conversion Failures
+ * 
+ * @author Gareth Roper (gareth.roper@est.tech)
+ * @author Andrew Lamb (andrew.a.lamb@est.tech)
+ */
+@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
+public class ConversionFailedException extends RuntimeException {
+
+    private static final long serialVersionUID = 45898561453196895L;
+
+    public ConversionFailedException(final String message) {
+        super(message);
+    }
+
+    @Override
+    public synchronized Throwable fillInStackTrace() {
+        return this;
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/EtsiCatalogManagerBadRequestException.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/EtsiCatalogManagerBadRequestException.java
new file mode 100644
index 0000000000..dbd098f7cd
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/EtsiCatalogManagerBadRequestException.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception for an ETSI Catalog Manager Bad Request Exception.
+ * 
+ * @author Gareth Roper (gareth.roper@est.tech
+ */
+@ResponseStatus(code = HttpStatus.BAD_REQUEST)
+public class EtsiCatalogManagerBadRequestException extends RuntimeException {
+
+    private static final long serialVersionUID = 6571317418914258768L;
+
+    public EtsiCatalogManagerBadRequestException(final String message) {
+        super(message);
+    }
+
+    @Override
+    public synchronized Throwable fillInStackTrace() {
+        return this;
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/EtsiCatalogManagerRequestFailureException.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/EtsiCatalogManagerRequestFailureException.java
new file mode 100644
index 0000000000..dbdc354f4e
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/EtsiCatalogManagerRequestFailureException.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception for an ETSI Catalog Manager Request Failure
+ * 
+ * @author gareth.roper@est.tech
+ */
+@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
+public class EtsiCatalogManagerRequestFailureException extends RuntimeException {
+
+    private static final long serialVersionUID = 66862444537194516L;
+
+    public EtsiCatalogManagerRequestFailureException(final String message) {
+        super(message);
+    }
+
+    @Override
+    public synchronized Throwable fillInStackTrace() {
+        return this;
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/EtsiSubscriptionNotificationControllerExceptionHandler.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/EtsiSubscriptionNotificationControllerExceptionHandler.java
new file mode 100644
index 0000000000..e8e1ce35b9
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/EtsiSubscriptionNotificationControllerExceptionHandler.java
@@ -0,0 +1,112 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.ProblemDetails;
+import org.onap.so.adapters.vnfmadapter.rest.EtsiSubscriptionNotificationController;
+import org.onap.so.rest.exceptions.HttpResouceNotFoundException;
+import org.onap.so.rest.exceptions.InvalidRestRequestException;
+import org.onap.so.rest.exceptions.RestProcessingException;
+import org.slf4j.Logger;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+
+/**
+ * Exception Handler for the Etsi Subscription Notification Controller {@link EtsiSubscriptionNotificationController
+ * EtsiSubscriptionNotificationController}
+ * 
+ * @author Andrew Lamb (andrew.a.lamb@est.tech)
+ */
+@ControllerAdvice(assignableTypes = EtsiSubscriptionNotificationController.class)
+public class EtsiSubscriptionNotificationControllerExceptionHandler {
+
+    private static final Logger logger = getLogger(EtsiSubscriptionNotificationControllerExceptionHandler.class);
+
+    @ExceptionHandler(InvalidRestRequestException.class)
+    public ResponseEntity<ProblemDetails> handleInvalidRestRequestException(
+            final InvalidRestRequestException invalidRestRequestException) {
+        final String errorMessage = "An error occurred.  Sending of notification to VNFM failed with response: "
+                + HttpStatus.BAD_REQUEST + ".\n" + invalidRestRequestException.getMessage();
+        logger.error(errorMessage);
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ProblemDetails().detail(errorMessage));
+    }
+
+    @ExceptionHandler(HttpResouceNotFoundException.class)
+    public ResponseEntity<ProblemDetails> handleHttpResourceNotFoundException(
+            final HttpResouceNotFoundException httpResourceNotFoundException) {
+        final String errorMessage = "An error occurred.  Sending of notification to VNFM failed with response: "
+                + HttpStatus.NOT_FOUND + ".\n" + httpResourceNotFoundException.getMessage();
+        logger.error(errorMessage);
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ProblemDetails().detail(errorMessage));
+    }
+
+    @ExceptionHandler(RestProcessingException.class)
+    public ResponseEntity<ProblemDetails> handleRestProcessingException(
+            final RestProcessingException restProcessingException) {
+        final String errorMessage = "An error occurred.  Sending of notification to VNFM failed with response: "
+                + restProcessingException.getStatusCode() + ".\n" + restProcessingException.getMessage();
+        logger.error(errorMessage);
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ProblemDetails().detail(errorMessage));
+    }
+
+    @ExceptionHandler(InternalServerErrorException.class)
+    public ResponseEntity<ProblemDetails> handleInternalServerErrorException(
+            final InternalServerErrorException internalServerErrorException) {
+        logger.error(internalServerErrorException.getMessage());
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
+                .body(new ProblemDetails().detail(internalServerErrorException.getMessage()));
+    }
+
+    @ExceptionHandler(AuthenticationTypeNotSupportedException.class)
+    public ResponseEntity<ProblemDetails> handleAuthenticationTypeNotSupportedException(
+            final AuthenticationTypeNotSupportedException authenticationTypeNotSupportedException) {
+        logger.error(authenticationTypeNotSupportedException.getMessage());
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
+                .body(new ProblemDetails().detail(authenticationTypeNotSupportedException.getMessage()));
+    }
+
+    @ExceptionHandler(ConversionFailedException.class)
+    public ResponseEntity<ProblemDetails> handleConversionFailedException(
+            final ConversionFailedException conversionFailedException) {
+        logger.error(conversionFailedException.getMessage());
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
+                .body(new ProblemDetails().detail(conversionFailedException.getMessage()));
+    }
+
+    @ExceptionHandler(NotificationTypeNotSupportedException.class)
+    public ResponseEntity<ProblemDetails> handleNotificationTypeNotSupportedException(
+            final NotificationTypeNotSupportedException notificationTypeNotSupportedException) {
+        logger.error(notificationTypeNotSupportedException.getMessage());
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
+                .body(new ProblemDetails().detail(notificationTypeNotSupportedException.getMessage()));
+    }
+
+    @ExceptionHandler(SubscriptionNotFoundException.class)
+    public ResponseEntity<ProblemDetails> handleSubscriptionNotFoundException(
+            final SubscriptionNotFoundException subscriptionNotFoundException) {
+        logger.error(subscriptionNotFoundException.getMessage());
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
+                .body(new ProblemDetails().detail(subscriptionNotFoundException.getMessage()));
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/InternalServerErrorException.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/InternalServerErrorException.java
new file mode 100644
index 0000000000..9b547d3c5a
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/InternalServerErrorException.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception for an ETSI Catalog Manager Request Failure
+ * 
+ * @author gareth.roper@est.tech
+ */
+@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
+public class InternalServerErrorException extends RuntimeException {
+
+    private static final long serialVersionUID = 66864561537194516L;
+
+    public InternalServerErrorException(final String message) {
+        super(message);
+    }
+
+    @Override
+    public synchronized Throwable fillInStackTrace() {
+        return this;
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/NotificationTypeNotSupportedException.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/NotificationTypeNotSupportedException.java
new file mode 100644
index 0000000000..dcc98864aa
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/NotificationTypeNotSupportedException.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception for an unsupported notification type
+ * 
+ * @author Andrew Lamb (andrew.a.lamb@est.tech)
+ */
+@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
+public class NotificationTypeNotSupportedException extends RuntimeException {
+
+    private static final long serialVersionUID = 2939423208362066902L;
+
+    public NotificationTypeNotSupportedException(final String message) {
+        super(message);
+    }
+
+    @Override
+    public synchronized Throwable fillInStackTrace() {
+        return this;
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/Sol003PackageManagementControllerExceptionHandler.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/Sol003PackageManagementControllerExceptionHandler.java
new file mode 100644
index 0000000000..8091f35da6
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/Sol003PackageManagementControllerExceptionHandler.java
@@ -0,0 +1,94 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.ProblemDetails;
+import org.onap.so.adapters.vnfmadapter.rest.Sol003PackageManagementController;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+
+/**
+ * Exception Handler for the Package Management Controller {@link Sol003PackageManagementController Sol003Controller}
+ * 
+ * @author gareth.roper@est.tech
+ */
+@ControllerAdvice(assignableTypes = Sol003PackageManagementController.class)
+
+public class Sol003PackageManagementControllerExceptionHandler {
+
+    @ExceptionHandler(EtsiCatalogManagerRequestFailureException.class)
+    public ResponseEntity<ProblemDetails> handleEtsiCatalogManagerRequestFailureException(
+            final EtsiCatalogManagerRequestFailureException etsiCatalogManagerRequestFailureException) {
+        final ProblemDetails problemDetails = new ProblemDetails();
+        problemDetails.setDetail(etsiCatalogManagerRequestFailureException.getMessage());
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(problemDetails);
+    }
+
+    @ExceptionHandler(EtsiCatalogManagerBadRequestException.class)
+    public ResponseEntity<ProblemDetails> handleEtsiCatalogManagerBadRequestFailureException(
+            final EtsiCatalogManagerBadRequestException etsiCatalogManagerBadRequestException) {
+        final ProblemDetails problemDetails = new ProblemDetails();
+        problemDetails.setDetail(etsiCatalogManagerBadRequestException.getMessage());
+        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(problemDetails);
+    }
+
+    @ExceptionHandler(SubscriptionNotFoundException.class)
+    public ResponseEntity<ProblemDetails> handleSubscriptionNotFoundException(
+            final SubscriptionNotFoundException subscriptionNotFoundException) {
+        final ProblemDetails problemDetails = new ProblemDetails();
+        problemDetails.setDetail(subscriptionNotFoundException.getMessage());
+        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(problemDetails);
+    }
+
+    @ExceptionHandler(ConversionFailedException.class)
+    public ResponseEntity<ProblemDetails> handleConversionFailedException(
+            final ConversionFailedException conversionFailedException) {
+        final ProblemDetails problemDetails = new ProblemDetails();
+        problemDetails.setDetail(conversionFailedException.getMessage());
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(problemDetails);
+    }
+
+    @ExceptionHandler(VnfPkgConflictException.class)
+    public ResponseEntity<ProblemDetails> handleVnfPkgConflictException(
+            final VnfPkgConflictException vnfPkgConflictException) {
+        final ProblemDetails problemDetails = new ProblemDetails();
+        problemDetails.setDetail(vnfPkgConflictException.getMessage());
+        return ResponseEntity.status(HttpStatus.CONFLICT).body(problemDetails);
+    }
+
+    @ExceptionHandler(VnfPkgNotFoundException.class)
+    public ResponseEntity<ProblemDetails> handleVnfPkgNotFoundException(
+            final VnfPkgNotFoundException vnfPkgNotFoundException) {
+        final ProblemDetails problemDetails = new ProblemDetails();
+        problemDetails.setDetail(vnfPkgNotFoundException.getMessage());
+        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(problemDetails);
+    }
+
+    @ExceptionHandler(VnfPkgBadRequestException.class)
+    public ResponseEntity<ProblemDetails> handleVnfPkgBadRequestException(
+            final VnfPkgBadRequestException vnfPkgBadRequestException) {
+        final ProblemDetails problemDetails = new ProblemDetails();
+        problemDetails.setDetail(vnfPkgBadRequestException.getMessage());
+        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(problemDetails);
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/SubscriptionNotFoundException.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/SubscriptionNotFoundException.java
new file mode 100644
index 0000000000..58c2ef050d
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/SubscriptionNotFoundException.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception for an ETSI Catalog Manager Request Failure
+ * 
+ * @author gareth.roper@est.tech
+ */
+@ResponseStatus(code = HttpStatus.NOT_FOUND)
+public class SubscriptionNotFoundException extends RuntimeException {
+
+    private static final long serialVersionUID = 85268561453194516L;
+
+    public SubscriptionNotFoundException(final String message) {
+        super(message);
+    }
+
+    @Override
+    public synchronized Throwable fillInStackTrace() {
+        return this;
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfPkgBadRequestException.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfPkgBadRequestException.java
new file mode 100644
index 0000000000..211131c2a4
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfPkgBadRequestException.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception for a Vnfpkg Bad Request failure, due to state of resource.
+ * 
+ * @author andrew.a.lamb@est.tech
+ */
+@ResponseStatus(code = HttpStatus.BAD_REQUEST)
+public class VnfPkgBadRequestException extends RuntimeException {
+
+    private static final long serialVersionUID = 3301317418914258411L;
+
+    public VnfPkgBadRequestException(final String message) {
+        super(message);
+    }
+
+    @Override
+    public synchronized Throwable fillInStackTrace() {
+        return this;
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfPkgConflictException.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfPkgConflictException.java
new file mode 100644
index 0000000000..f9aa2a0e21
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfPkgConflictException.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception for a VnfPkg Conflict failures, due to state of resource.
+ * 
+ * @author gareth.roper@est.tech
+ */
+@ResponseStatus(code = HttpStatus.CONFLICT)
+public class VnfPkgConflictException extends RuntimeException {
+
+    private static final long serialVersionUID = 26862444537198441L;
+
+    public VnfPkgConflictException(final String message) {
+        super(message);
+    }
+
+    @Override
+    public synchronized Throwable fillInStackTrace() {
+        return this;
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfPkgNotFoundException.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfPkgNotFoundException.java
new file mode 100644
index 0000000000..c15e7052ab
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/exceptions/VnfPkgNotFoundException.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.vnfmadapter.rest.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+/**
+ * Exception for VnfPkg Not Found Failures
+ * 
+ * @author gareth.roper@est.tech
+ */
+@ResponseStatus(code = HttpStatus.NOT_FOUND)
+public class VnfPkgNotFoundException extends RuntimeException {
+
+    private static final long serialVersionUID = 26862444537198441L;
+
+    public VnfPkgNotFoundException(final String message) {
+        super(message);
+    }
+
+    @Override
+    public synchronized Throwable fillInStackTrace() {
+        return this;
+    }
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/EtsiSubscriptionNotificationControllerTest.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/EtsiSubscriptionNotificationControllerTest.java
new file mode 100644
index 0000000000..1d3cf73729
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/EtsiSubscriptionNotificationControllerTest.java
@@ -0,0 +1,602 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.onap.so.adapters.vnfmadapter.common.CommonConstants.ETSI_SUBSCRIPTION_NOTIFICATION_CONTROLLER_BASE_URL;
+import static org.onap.so.client.RestTemplateConfig.CONFIGURABLE_REST_TEMPLATE;
+import static org.springframework.test.annotation.DirtiesContext.ClassMode.BEFORE_CLASS;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.jsonPath;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.onap.so.adapters.vnfmadapter.PackageManagementConstants;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.ProblemDetails;
+import org.onap.so.adapters.vnfmadapter.etsicatalog.notification.model.NOTIFICATIONLINKSERIALIZER;
+import org.onap.so.adapters.vnfmadapter.etsicatalog.notification.model.PkgChangeNotification;
+import org.onap.so.adapters.vnfmadapter.etsicatalog.notification.model.PkgOnboardingNotification;
+import org.onap.so.adapters.vnfmadapter.etsicatalog.notification.model.PkgmLinks;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.PkgmSubscriptionRequest;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthentication;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthenticationParamsBasic;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthenticationParamsOauth2ClientCredentials;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.packagemanagement.notification.model.VnfPackageChangeNotification;
+import org.onap.so.adapters.vnfmadapter.extclients.vnfm.packagemanagement.notification.model.VnfPackageOnboardingNotification;
+import org.onap.so.configuration.rest.BasicHttpHeadersProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.web.client.RestTemplate;
+import org.threeten.bp.LocalDateTime;
+import org.threeten.bp.OffsetDateTime;
+import org.threeten.bp.ZoneOffset;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+/**
+ * @author Andrew Lamb (andrew.a.lamb@est.tech)
+ *
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = TestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+@DirtiesContext(classMode = BEFORE_CLASS)
+public class EtsiSubscriptionNotificationControllerTest {
+
+    @LocalServerPort
+    private int port;
+
+    private static final URI CALLBACK_URI = URI.create("http://test_callback_uri/notification");
+    private static final String TOKEN_ENDPOINT = "http://test_token_endpoint_uri/";
+    private static final String TOKEN = "dXNlcm5hbWU6cGFzc3dvcmQ=......";
+    private static final String JSON_TOKEN = "{\"access_token\":\"" + TOKEN + "\"}";
+    private static final String LOCALHOST_URL = "http://localhost:";
+    private static final String NOTIFICATION_BASE_URL =
+            ETSI_SUBSCRIPTION_NOTIFICATION_CONTROLLER_BASE_URL + "/notification";
+    private static final String USERNAME = "username";
+    private static final String PASSWORD = "password";
+    private static final String EXPECTED_BASIC_AUTHORIZATION = "Basic dXNlcm5hbWU6cGFzc3dvcmQ=";
+    private static final String EXPECTED_OAUTH_AUTHORIZATION = "Bearer " + TOKEN;
+    private static final String NOTIFICATION_ID = "NOTIFICATION_ID";
+    private static final String SUBSCRIPTION_ID = "SUBSCRIPTION_ID";
+    private static final OffsetDateTime TIMESTAMP =
+            OffsetDateTime.of(LocalDateTime.of(2020, 1, 1, 1, 1, 1, 1), ZoneOffset.ofHours(1));
+    private static final String VNFPKG_ID = UUID.randomUUID().toString();
+    private static final String VNFD_ID = UUID.randomUUID().toString();
+
+    private BasicHttpHeadersProvider basicHttpHeadersProvider;
+    private final Gson gson = new GsonBuilder().create();;
+
+    @Autowired
+    @Qualifier(CONFIGURABLE_REST_TEMPLATE)
+    private RestTemplate restTemplate;
+    private MockRestServiceServer mockRestServiceServer;
+
+    @Autowired
+    private TestRestTemplate testRestTemplate;
+
+    @Autowired
+    private CacheManager cacheServiceProvider;
+    private Cache cache;
+
+    @Before
+    public void setUp() {
+        mockRestServiceServer = MockRestServiceServer.bindTo(restTemplate).build();
+        basicHttpHeadersProvider = new BasicHttpHeadersProvider();
+        cache = cacheServiceProvider.getCache(PackageManagementConstants.PACKAGE_MANAGEMENT_SUBSCRIPTION_CACHE);
+        cache.clear();
+    }
+
+    @After
+    public void tearDown() {
+        mockRestServiceServer.reset();
+        cache.clear();
+    }
+
+    @Test
+    public void testSubscriptionNotificationEndPoint_ReturnsNoContent() {
+        final ResponseEntity<?> response = sendHttpGet(NOTIFICATION_BASE_URL);
+        assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
+    }
+
+    @Test
+    public void testOnboardingNotificationSentOnToVnfmCallbackUri_SubscriptionRequestInCache_Success() {
+        final PkgmSubscriptionRequest subscriptionRequest =
+                buildPkgmSubscriptionRequest(SubscriptionsAuthentication.AuthTypeEnum.BASIC);
+        cache.put(SUBSCRIPTION_ID, subscriptionRequest);
+        final PkgOnboardingNotification notification = buildPkgOnboardingNotification();
+        final String notificationString = gson.toJson(notification);
+
+        mockRestServiceServer.expect(requestTo(CALLBACK_URI)).andExpect(method(HttpMethod.POST))
+                .andExpect(jsonPath("$.id").value(NOTIFICATION_ID))
+                .andExpect(jsonPath("$.notificationType")
+                        .value(VnfPackageOnboardingNotification.NotificationTypeEnum.VNFPACKAGEONBOARDINGNOTIFICATION
+                                .toString()))
+                .andExpect(jsonPath("$.subscriptionId").value(SUBSCRIPTION_ID))
+                .andExpect(jsonPath("$.timeStamp").value(TIMESTAMP.toString()))
+                .andExpect(jsonPath("$.vnfPkgId").value(VNFPKG_ID.toString()))
+                .andExpect(jsonPath("$.vnfdId").value(VNFD_ID.toString()))
+                .andExpect(jsonPath("$._links").value(buildPkgmLinks()))
+                .andExpect(header("Authorization", EXPECTED_BASIC_AUTHORIZATION)).andRespond(withSuccess());
+
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
+    }
+
+    @Test
+    public void testOnboardingNotificationNotSentOnToVnfmCallbackUri_SubscriptionRequestNotInCache_Fail() {
+        final PkgOnboardingNotification notification = buildPkgOnboardingNotification();
+        final String notificationString = gson.toJson(notification);
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+        assertTrue(response.getBody() instanceof ProblemDetails);
+
+        final ProblemDetails problemDetails = (ProblemDetails) response.getBody();
+        final String errorMessage = "No subscription found with subscriptionId " + SUBSCRIPTION_ID
+                + ". Unable to forward notification to subscriber.";
+
+        assertEquals(errorMessage, problemDetails.getDetail());
+    }
+
+    @Test
+    public void testOnboardingNotificationSentOnToVnfmCallbackUri_BadRequestResponseFromCallbackUri_Fail() {
+        final PkgmSubscriptionRequest subscriptionRequest =
+                buildPkgmSubscriptionRequest(SubscriptionsAuthentication.AuthTypeEnum.BASIC);
+        cache.put(SUBSCRIPTION_ID, subscriptionRequest);
+        final PkgOnboardingNotification notification = buildPkgOnboardingNotification();
+        final String notificationString = gson.toJson(notification);
+
+        mockRestServiceServer.expect(requestTo(CALLBACK_URI)).andExpect(method(HttpMethod.POST))
+                .andRespond(withStatus(HttpStatus.BAD_REQUEST));
+
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+        assertTrue(response.getBody() instanceof ProblemDetails);
+
+        final ProblemDetails problemDetails = (ProblemDetails) response.getBody();
+        final String errorMessage = "An error occurred.  Sending of notification to VNFM failed with response: "
+                + HttpStatus.BAD_REQUEST + ".\n" + "No result found for given url: " + CALLBACK_URI;
+
+        assertEquals(errorMessage, problemDetails.getDetail());
+    }
+
+    @Test
+    public void testOnboardingNotificationSentOnToVnfmCallbackUri_301MovedPermanentlyResponseFromCallbackUri_Fail() {
+        final PkgmSubscriptionRequest subscriptionRequest =
+                buildPkgmSubscriptionRequest(SubscriptionsAuthentication.AuthTypeEnum.BASIC);
+        cache.put(SUBSCRIPTION_ID, subscriptionRequest);
+        final PkgOnboardingNotification notification = buildPkgOnboardingNotification();
+        final String notificationString = gson.toJson(notification);
+
+        mockRestServiceServer.expect(requestTo(CALLBACK_URI)).andExpect(method(HttpMethod.POST))
+                .andRespond(withStatus(HttpStatus.MOVED_PERMANENTLY));
+
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+        assertTrue(response.getBody() instanceof ProblemDetails);
+
+        final ProblemDetails problemDetails = (ProblemDetails) response.getBody();
+        final String errorMessage = "An error occurred.  Sending of notification to VNFM failed.";
+
+        assertEquals(errorMessage, problemDetails.getDetail());
+    }
+
+    @Test
+    public void testOnboardingNotificationSentOnToVnfmCallbackUri_NotFoundResponseFromCallbackUri_Fail() {
+        final PkgmSubscriptionRequest subscriptionRequest =
+                buildPkgmSubscriptionRequest(SubscriptionsAuthentication.AuthTypeEnum.BASIC);
+        cache.put(SUBSCRIPTION_ID, subscriptionRequest);
+        final PkgOnboardingNotification notification = buildPkgOnboardingNotification();
+        final String notificationString = gson.toJson(notification);
+
+        mockRestServiceServer.expect(requestTo(CALLBACK_URI)).andExpect(method(HttpMethod.POST))
+                .andRespond(withStatus(HttpStatus.NOT_FOUND));
+
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+        assertTrue(response.getBody() instanceof ProblemDetails);
+
+        final ProblemDetails problemDetails = (ProblemDetails) response.getBody();
+        final String errorMessage = "An error occurred.  Sending of notification to VNFM failed with response: "
+                + HttpStatus.NOT_FOUND + ".\n" + "No result found for given url: " + CALLBACK_URI;
+
+        assertEquals(errorMessage, problemDetails.getDetail());
+    }
+
+    @Test
+    public void testOnboardingNotificationSentOnToVnfmCallbackUri_InternalServerErrorResponseFromCallbackUri_Fail() {
+        final PkgmSubscriptionRequest subscriptionRequest =
+                buildPkgmSubscriptionRequest(SubscriptionsAuthentication.AuthTypeEnum.BASIC);
+        cache.put(SUBSCRIPTION_ID, subscriptionRequest);
+        final PkgOnboardingNotification notification = buildPkgOnboardingNotification();
+        final String notificationString = gson.toJson(notification);
+
+        mockRestServiceServer.expect(requestTo(CALLBACK_URI)).andExpect(method(HttpMethod.POST))
+                .andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
+
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+        assertTrue(response.getBody() instanceof ProblemDetails);
+
+        final ProblemDetails problemDetails = (ProblemDetails) response.getBody();
+        final String errorMessage = "An error occurred.  Sending of notification to VNFM failed with response: "
+                + HttpStatus.INTERNAL_SERVER_ERROR.value() + ".\n" + "Unable to invoke HTTP POST using URL: "
+                + CALLBACK_URI;
+
+        assertEquals(errorMessage, problemDetails.getDetail());
+    }
+
+    @Test
+    public void testChangeNotificationSentOnToVnfmCallbackUri_SubscriptionRequestInCache_Success() {
+        final PkgmSubscriptionRequest subscriptionRequest =
+                buildPkgmSubscriptionRequest(SubscriptionsAuthentication.AuthTypeEnum.BASIC);
+        cache.put(SUBSCRIPTION_ID, subscriptionRequest);
+        final PkgChangeNotification notification = buildPkgChangeNotification();
+        final String notificationString = gson.toJson(notification);
+
+        mockRestServiceServer.expect(requestTo(CALLBACK_URI)).andExpect(method(HttpMethod.POST))
+                .andExpect(jsonPath("$.id").value(NOTIFICATION_ID))
+                .andExpect(jsonPath("$.notificationType").value(
+                        VnfPackageChangeNotification.NotificationTypeEnum.VNFPACKAGECHANGENOTIFICATION.getValue()))
+                .andExpect(jsonPath("$.subscriptionId").value(SUBSCRIPTION_ID))
+                .andExpect(jsonPath("$.timeStamp").value(TIMESTAMP.toString()))
+                .andExpect(jsonPath("$.vnfPkgId").value(VNFPKG_ID.toString()))
+                .andExpect(jsonPath("$.vnfdId").value(VNFD_ID.toString()))
+                .andExpect(
+                        jsonPath("$.changeType").value(PkgChangeNotification.ChangeTypeEnum.OP_STATE_CHANGE.toString()))
+                .andExpect(jsonPath("$.operationalState")
+                        .value(PkgChangeNotification.OperationalStateEnum.ENABLED.toString()))
+                .andExpect(jsonPath("$._links").value(buildPkgmLinks()))
+                .andExpect(header("Authorization", EXPECTED_BASIC_AUTHORIZATION)).andRespond(withSuccess());
+
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
+    }
+
+    @Test
+    public void testChangeNotificationNotSentOnToVnfmCallbackUri_SubscriptionRequestNotInCache_Fail() {
+        final PkgChangeNotification notification = buildPkgChangeNotification();
+        final String notificationString = gson.toJson(notification);
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+        assertTrue(response.getBody() instanceof ProblemDetails);
+
+        final ProblemDetails problemDetails = (ProblemDetails) response.getBody();
+        final String errorMessage = "No subscription found with subscriptionId " + SUBSCRIPTION_ID
+                + ". Unable to forward notification to subscriber.";
+
+        assertEquals(errorMessage, problemDetails.getDetail());
+    }
+
+    @Test
+    public void testChangeNotificationSentOnToVnfmCallbackUri_BadRequestResponseFromCallbackUri_Fail() {
+        final PkgmSubscriptionRequest subscriptionRequest =
+                buildPkgmSubscriptionRequest(SubscriptionsAuthentication.AuthTypeEnum.BASIC);
+        cache.put(SUBSCRIPTION_ID, subscriptionRequest);
+        final PkgChangeNotification notification = buildPkgChangeNotification();
+        final String notificationString = gson.toJson(notification);
+
+        mockRestServiceServer.expect(requestTo(CALLBACK_URI)).andExpect(method(HttpMethod.POST))
+                .andRespond(withStatus(HttpStatus.BAD_REQUEST));
+
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+        assertTrue(response.getBody() instanceof ProblemDetails);
+
+        final ProblemDetails problemDetails = (ProblemDetails) response.getBody();
+        final String errorMessage = "An error occurred.  Sending of notification to VNFM failed with response: "
+                + HttpStatus.BAD_REQUEST + ".\n" + "No result found for given url: " + CALLBACK_URI;
+
+        assertEquals(errorMessage, problemDetails.getDetail());
+    }
+
+    @Test
+    public void testChangeNotificationSentOnToVnfmCallbackUri_NotFoundResponseFromCallbackUri_Fail() {
+        final PkgmSubscriptionRequest subscriptionRequest =
+                buildPkgmSubscriptionRequest(SubscriptionsAuthentication.AuthTypeEnum.BASIC);
+        cache.put(SUBSCRIPTION_ID, subscriptionRequest);
+        final PkgChangeNotification notification = buildPkgChangeNotification();
+        final String notificationString = gson.toJson(notification);
+
+        mockRestServiceServer.expect(requestTo(CALLBACK_URI)).andExpect(method(HttpMethod.POST))
+                .andRespond(withStatus(HttpStatus.NOT_FOUND));
+
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+        assertTrue(response.getBody() instanceof ProblemDetails);
+
+        final ProblemDetails problemDetails = (ProblemDetails) response.getBody();
+        final String errorMessage = "An error occurred.  Sending of notification to VNFM failed with response: "
+                + HttpStatus.NOT_FOUND + ".\n" + "No result found for given url: " + CALLBACK_URI;
+
+        assertEquals(errorMessage, problemDetails.getDetail());
+    }
+
+    @Test
+    public void testChangeNotificationSentOnToVnfmCallbackUri_InternalServerErrorResponseFromCallbackUri_Fail() {
+        final PkgmSubscriptionRequest subscriptionRequest =
+                buildPkgmSubscriptionRequest(SubscriptionsAuthentication.AuthTypeEnum.BASIC);
+        cache.put(SUBSCRIPTION_ID, subscriptionRequest);
+        final PkgChangeNotification notification = buildPkgChangeNotification();
+        final String notificationString = gson.toJson(notification);
+
+        mockRestServiceServer.expect(requestTo(CALLBACK_URI)).andExpect(method(HttpMethod.POST))
+                .andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
+
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+        assertTrue(response.getBody() instanceof ProblemDetails);
+
+        final ProblemDetails problemDetails = (ProblemDetails) response.getBody();
+        final String errorMessage = "An error occurred.  Sending of notification to VNFM failed with response: "
+                + HttpStatus.INTERNAL_SERVER_ERROR.value() + ".\n" + "Unable to invoke HTTP POST using URL: "
+                + CALLBACK_URI;
+
+        assertEquals(errorMessage, problemDetails.getDetail());
+    }
+
+    @Test
+    public void testNotificationSentOnToVnfm_BasicAuthUserPasswordAuthorized_Success() {
+        final PkgmSubscriptionRequest subscriptionRequest =
+                buildPkgmSubscriptionRequest(SubscriptionsAuthentication.AuthTypeEnum.BASIC);
+        cache.put(SUBSCRIPTION_ID, subscriptionRequest);
+        final PkgOnboardingNotification notification = buildPkgOnboardingNotification();
+        final String notificationString = gson.toJson(notification);
+
+        mockRestServiceServer.expect(requestTo(CALLBACK_URI)).andExpect(method(HttpMethod.POST))
+                .andExpect(jsonPath("$.id").value(NOTIFICATION_ID))
+                .andExpect(jsonPath("$.notificationType")
+                        .value(VnfPackageOnboardingNotification.NotificationTypeEnum.VNFPACKAGEONBOARDINGNOTIFICATION
+                                .toString()))
+                .andExpect(jsonPath("$.subscriptionId").value(SUBSCRIPTION_ID))
+                .andExpect(jsonPath("$.timeStamp").value(TIMESTAMP.toString()))
+                .andExpect(jsonPath("$.vnfPkgId").value(VNFPKG_ID.toString()))
+                .andExpect(jsonPath("$.vnfdId").value(VNFD_ID.toString()))
+                .andExpect(jsonPath("$._links").value(buildPkgmLinks()))
+                .andExpect(header("Authorization", EXPECTED_BASIC_AUTHORIZATION)).andRespond(withSuccess());
+
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
+    }
+
+    @Test
+    public void testNotificationSentOnToVnfm_BasicAuthUserPasswordNotAuthorized_Fail() {
+        final PkgmSubscriptionRequest subscriptionRequest =
+                buildPkgmSubscriptionRequest(SubscriptionsAuthentication.AuthTypeEnum.BASIC);
+        cache.put(SUBSCRIPTION_ID, subscriptionRequest);
+        final PkgChangeNotification notification = buildPkgChangeNotification();
+        final String notificationString = gson.toJson(notification);
+
+        mockRestServiceServer.expect(requestTo(CALLBACK_URI)).andExpect(method(HttpMethod.POST))
+                .andExpect(header("Authorization", EXPECTED_BASIC_AUTHORIZATION))
+                .andRespond(withStatus(HttpStatus.UNAUTHORIZED));
+
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+        assertTrue(response.getBody() instanceof ProblemDetails);
+
+        final ProblemDetails problemDetails = (ProblemDetails) response.getBody();
+        final String errorMessage = "An error occurred.  Sending of notification to VNFM failed with response: "
+                + HttpStatus.UNAUTHORIZED.value() + ".\n" + "Unable to invoke HTTP POST using URL: " + CALLBACK_URI;
+
+        assertEquals(errorMessage, problemDetails.getDetail());
+    }
+
+    @Test
+    public void testNotificationSentOnToVnfm_OAuthAuthorized_Success() {
+        final PkgmSubscriptionRequest subscriptionRequest =
+                buildPkgmSubscriptionRequest(SubscriptionsAuthentication.AuthTypeEnum.OAUTH2_CLIENT_CREDENTIALS);
+        cache.put(SUBSCRIPTION_ID, subscriptionRequest);
+        final PkgChangeNotification notification = buildPkgChangeNotification();
+        final String notificationString = gson.toJson(notification);
+
+        mockRestServiceServer.expect(requestTo(TOKEN_ENDPOINT)).andExpect(method(HttpMethod.POST))
+                .andExpect(header("Authorization", EXPECTED_BASIC_AUTHORIZATION))
+                .andRespond(withSuccess(JSON_TOKEN, MediaType.APPLICATION_JSON));
+
+        mockRestServiceServer.expect(requestTo(CALLBACK_URI)).andExpect(method(HttpMethod.POST))
+                .andExpect(header("Authorization", EXPECTED_OAUTH_AUTHORIZATION))
+                .andExpect(jsonPath("$.id").value(NOTIFICATION_ID))
+                .andExpect(jsonPath("$.notificationType").value(
+                        VnfPackageChangeNotification.NotificationTypeEnum.VNFPACKAGECHANGENOTIFICATION.toString()))
+                .andExpect(jsonPath("$.subscriptionId").value(SUBSCRIPTION_ID))
+                .andExpect(jsonPath("$.timeStamp").value(TIMESTAMP.toString()))
+                .andExpect(jsonPath("$.vnfPkgId").value(VNFPKG_ID.toString()))
+                .andExpect(jsonPath("$.vnfdId").value(VNFD_ID.toString()))
+                .andExpect(
+                        jsonPath("$.changeType").value(PkgChangeNotification.ChangeTypeEnum.OP_STATE_CHANGE.toString()))
+                .andExpect(jsonPath("$.operationalState")
+                        .value(PkgChangeNotification.OperationalStateEnum.ENABLED.toString()))
+                .andExpect(jsonPath("$._links").value(buildPkgmLinks())).andRespond(withSuccess());
+
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
+    }
+
+    @Test
+    public void testNotificationSentOnToVnfm_OAuthTokenNotReceived_Fail() {
+        final PkgmSubscriptionRequest subscriptionRequest =
+                buildPkgmSubscriptionRequest(SubscriptionsAuthentication.AuthTypeEnum.OAUTH2_CLIENT_CREDENTIALS);
+        cache.put(SUBSCRIPTION_ID, subscriptionRequest);
+        final PkgChangeNotification notification = buildPkgChangeNotification();
+        final String notificationString = gson.toJson(notification);
+
+        mockRestServiceServer.expect(requestTo(TOKEN_ENDPOINT)).andExpect(method(HttpMethod.POST))
+                .andExpect(header("Authorization", EXPECTED_BASIC_AUTHORIZATION)).andRespond(withSuccess());
+
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+        assertTrue(response.getBody() instanceof ProblemDetails);
+
+        final ProblemDetails problemDetails = (ProblemDetails) response.getBody();
+        final String errorMessage = "An error occurred.  Unable to retrieve OAuth Token from VNFM for notification.";
+
+        assertEquals(errorMessage, problemDetails.getDetail());
+    }
+
+    @Test
+    public void testNotificationSentOnToVnfm_TLSCertNotYetSupported_Fail() {
+        final PkgmSubscriptionRequest subscriptionRequest =
+                buildPkgmSubscriptionRequest(SubscriptionsAuthentication.AuthTypeEnum.TLS_CERT);
+        cache.put(SUBSCRIPTION_ID, subscriptionRequest);
+        final PkgChangeNotification notification = buildPkgChangeNotification();
+        final String notificationString = gson.toJson(notification);
+
+        final ResponseEntity<?> response = sendHttpPost(notificationString);
+
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+        assertTrue(response.getBody() instanceof ProblemDetails);
+
+        final ProblemDetails problemDetails = (ProblemDetails) response.getBody();
+        final String errorMessage = "An error occurred.  Authentication type "
+                + subscriptionRequest.getAuthentication().getAuthType().toString() + " not currently supported.";
+
+        assertEquals(errorMessage, problemDetails.getDetail());
+    }
+
+    private PkgOnboardingNotification buildPkgOnboardingNotification() {
+        final PkgOnboardingNotification notification = new PkgOnboardingNotification();
+        notification.setId(NOTIFICATION_ID);
+        notification
+                .setNotificationType(PkgOnboardingNotification.NotificationTypeEnum.VNFPACKAGEONBOARDINGNOTIFICATION);
+        notification.setSubscriptionId(SUBSCRIPTION_ID);
+        notification.setTimeStamp(TIMESTAMP);
+        notification.setVnfPkgId(VNFPKG_ID);
+        notification.setVnfdId(VNFD_ID);
+        notification.setLinks(buildPkgmLinks());
+        return notification;
+    }
+
+    private PkgChangeNotification buildPkgChangeNotification() {
+        final PkgChangeNotification notification = new PkgChangeNotification();
+        notification.setId(NOTIFICATION_ID);
+        notification.setNotificationType(PkgChangeNotification.NotificationTypeEnum.VNFPACKAGECHANGENOTIFICATION);
+        notification.setSubscriptionId(SUBSCRIPTION_ID);
+        notification.setTimeStamp(TIMESTAMP);
+        notification.setVnfPkgId(VNFPKG_ID);
+        notification.setVnfdId(VNFD_ID);
+        notification.setChangeType(PkgChangeNotification.ChangeTypeEnum.OP_STATE_CHANGE);
+        notification.setOperationalState(PkgChangeNotification.OperationalStateEnum.ENABLED);
+        notification.setLinks(buildPkgmLinks());
+        return notification;
+    }
+
+    private PkgmLinks buildPkgmLinks() {
+        final PkgmLinks pkgmLinks = new PkgmLinks();
+
+        final NOTIFICATIONLINKSERIALIZER subscriptionLinkSerializer = new NOTIFICATIONLINKSERIALIZER();
+        subscriptionLinkSerializer.setHref("subscription_href");
+        pkgmLinks.setSubscription(subscriptionLinkSerializer);
+
+        final NOTIFICATIONLINKSERIALIZER vnfPackageLinkSerializer = new NOTIFICATIONLINKSERIALIZER();
+        vnfPackageLinkSerializer.setHref("vnf_package_href");
+        pkgmLinks.setVnfPackage(vnfPackageLinkSerializer);
+
+        return pkgmLinks;
+    }
+
+    private PkgmSubscriptionRequest buildPkgmSubscriptionRequest(
+            final SubscriptionsAuthentication.AuthTypeEnum authTypeEnum) {
+        final PkgmSubscriptionRequest subscriptionRequest = new PkgmSubscriptionRequest();
+        subscriptionRequest.setCallbackUri(CALLBACK_URI.toString());
+        subscriptionRequest.setAuthentication(buildSubscriptionsAuthentication(authTypeEnum));
+        return subscriptionRequest;
+    }
+
+    // TODO update for auth types other than basicAuth
+    private SubscriptionsAuthentication buildSubscriptionsAuthentication(
+            final SubscriptionsAuthentication.AuthTypeEnum authTypeEnum) {
+        final SubscriptionsAuthentication subscriptionsAuthentication = new SubscriptionsAuthentication();
+        final List<SubscriptionsAuthentication.AuthTypeEnum> authTypes = new ArrayList<>();
+        authTypes.add(authTypeEnum);
+        subscriptionsAuthentication.setAuthType(authTypes);
+        if (authTypeEnum == SubscriptionsAuthentication.AuthTypeEnum.TLS_CERT) {
+            // TODO: remove basic params and code for TLS
+            final SubscriptionsAuthenticationParamsBasic basicParams =
+                    new SubscriptionsAuthenticationParamsBasic().userName(USERNAME).password(PASSWORD);
+            subscriptionsAuthentication.setParamsBasic(basicParams);
+        } else if (authTypeEnum == SubscriptionsAuthentication.AuthTypeEnum.OAUTH2_CLIENT_CREDENTIALS) {
+            final SubscriptionsAuthenticationParamsOauth2ClientCredentials oathParams =
+                    new SubscriptionsAuthenticationParamsOauth2ClientCredentials().clientId(USERNAME)
+                            .clientPassword(PASSWORD).tokenEndpoint(TOKEN_ENDPOINT);
+            subscriptionsAuthentication.setParamsOauth2ClientCredentials(oathParams);
+        } else {
+            final SubscriptionsAuthenticationParamsBasic basicParams =
+                    new SubscriptionsAuthenticationParamsBasic().userName(USERNAME).password(PASSWORD);
+            subscriptionsAuthentication.setParamsBasic(basicParams);
+        }
+
+        return subscriptionsAuthentication;
+    }
+
+    private <T> ResponseEntity<ProblemDetails> sendHttpPost(final T notification) {
+        final String testURL = LOCALHOST_URL + port + NOTIFICATION_BASE_URL;
+        final HttpEntity<?> request = new HttpEntity<>(notification, basicHttpHeadersProvider.getHttpHeaders());
+        return testRestTemplate.withBasicAuth("test", "test").exchange(testURL, HttpMethod.POST, request,
+                ProblemDetails.class);
+    }
+
+    private ResponseEntity<Void> sendHttpGet(final String url) {
+        final String testURL = LOCALHOST_URL + port + url;
+        final HttpEntity<?> request = new HttpEntity<>(basicHttpHeadersProvider.getHttpHeaders());
+        return testRestTemplate.withBasicAuth("test", "test").exchange(testURL, HttpMethod.GET, request, Void.class);
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementControllerTest.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementControllerTest.java
new file mode 100644
index 0000000000..9ea0c1d880
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementControllerTest.java
@@ -0,0 +1,677 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.onap.so.adapters.vnfmadapter.common.CommonConstants.PACKAGE_MANAGEMENT_BASE_URL;
+import static org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.EtsiCatalogServiceProviderConfiguration.ETSI_CATALOG_REST_TEMPLATE_BEAN;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.Checksum;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.ProblemDetails;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.UriLink;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.VNFPKGMLinkSerializer;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.VnfPackageArtifactInfo;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.VnfPackageSoftwareImageInfo;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.VnfPkgInfo;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.InlineResponse2001;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.VnfPackagesLinks;
+import org.onap.so.configuration.rest.BasicHttpHeadersProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.web.client.RestTemplate;
+import com.google.gson.Gson;
+
+/**
+ * @author gareth.roper@est.tech
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = TestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+public class Sol003PackageManagementControllerTest {
+
+    @LocalServerPort
+    private int port;
+
+    @Autowired
+    @Qualifier(ETSI_CATALOG_REST_TEMPLATE_BEAN)
+    private RestTemplate restTemplate;
+
+    @Autowired
+    private TestRestTemplate testRestTemplate;
+
+    private static final String VNF_PACKAGE_ID = "myVnfPackageId";
+    private static final String ARTIFACT_PATH = "myArtifactPath";
+    private static final String MSB_BASE_URL = "http://msb-iag.onap:80/api/vnfpkgm/v1/vnf_packages";
+    private static final String VNFPKGM_BASE_URL = PACKAGE_MANAGEMENT_BASE_URL + "/vnf_packages";
+    private static final String localhostUrl = "http://localhost:";
+    private static final String GET_VNF_PACKAGES_URL = "";
+    private static final String GET_VNF_PACKAGE_BY_ID_URL = "/" + VNF_PACKAGE_ID;
+    private static final String VNFD_ID = "vnfdId";
+    private static final String VNF_PROVIDER = "vnfProvider";
+    private static final String VNF_PRODUCT_NAME = "vnfProductName";
+    private static final String VNF_SOFTWARE_VERSION = "vnfSoftwareVersion";
+    private static final String VNFD_VERSION = "vnfdVersion";
+    private static final String ALGORITHM = "algorithm";
+    private static final String HASH = "hash";
+    private static final String EXPECTED_BASE_URL =
+            "https://so-vnfm-adapter.onap:30406/so/vnfm-adapter/v1/vnfpkgm/v1/vnf_packages/";
+    private static final String EXPECTED_SELF_HREF = EXPECTED_BASE_URL + VNF_PACKAGE_ID;
+    private static final String EXPECTED_VNFD_HREF = EXPECTED_BASE_URL + VNF_PACKAGE_ID + "/vnfd";
+    private static final String EXPECTED_PACKAGE_CONTENT_HREF = EXPECTED_BASE_URL + VNF_PACKAGE_ID + "/package_content";
+
+    private MockRestServiceServer mockRestServiceServer;
+    private BasicHttpHeadersProvider basicHttpHeadersProvider;
+    private final Gson gson = new Gson();
+
+    public Sol003PackageManagementControllerTest() {}
+
+    @Before
+    public void setUp() {
+        final MockRestServiceServer.MockRestServiceServerBuilder builder = MockRestServiceServer.bindTo(restTemplate);
+        builder.ignoreExpectOrder(true);
+        mockRestServiceServer = builder.build();
+        basicHttpHeadersProvider = new BasicHttpHeadersProvider();
+    }
+
+    @After
+    public void after() {
+        mockRestServiceServer.reset();
+    }
+
+    @Test
+    public void testGetPackageContent_ValidArray_Success() {
+        final byte[] responseArray = buildByteArrayWithRandomData(10);
+
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/package_content"))
+                .andExpect(method(HttpMethod.GET))
+                .andRespond(withSuccess(responseArray, MediaType.APPLICATION_OCTET_STREAM));
+
+        final String testURL = "http://localhost:" + port + PACKAGE_MANAGEMENT_BASE_URL + "/vnf_packages/"
+                + VNF_PACKAGE_ID + "/package_content";
+        final HttpEntity<?> request = new HttpEntity<>(basicHttpHeadersProvider.getHttpHeaders());
+        final ResponseEntity<byte[]> responseEntity =
+                testRestTemplate.withBasicAuth("test", "test").exchange(testURL, HttpMethod.GET, request, byte[].class);
+
+        assertEquals(byte[].class, responseEntity.getBody().getClass());
+        assertArrayEquals(responseEntity.getBody(), responseArray);
+        assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageContent_Conflict_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/package_content"))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.CONFLICT));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(VNF_PACKAGE_ID + "/package_content");
+
+        assertTrue(responseEntity.getBody() instanceof ProblemDetails);
+        assertEquals(HttpStatus.CONFLICT, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageContent_NotFound_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/package_content"))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.NOT_FOUND));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(VNF_PACKAGE_ID + "/package_content");
+
+        assertTrue(responseEntity.getBody() instanceof ProblemDetails);
+        assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageContent_UnauthorizedClient_Fail() {
+        final String testURL = "http://localhost:" + port + PACKAGE_MANAGEMENT_BASE_URL + "/vnf_packages/"
+                + VNF_PACKAGE_ID + "/package_content";
+
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/package_content"))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.UNAUTHORIZED));
+
+        final HttpEntity<?> request = new HttpEntity<>(basicHttpHeadersProvider.getHttpHeaders());
+
+
+        final ResponseEntity<ProblemDetails> responseEntity =
+                testRestTemplate.exchange(testURL, HttpMethod.GET, request, ProblemDetails.class);
+
+        assertTrue(responseEntity.getBody() instanceof ProblemDetails);
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageContent_InternalServerError_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/package_content"))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(VNF_PACKAGE_ID + "/package_content");
+
+        assertTrue(responseEntity.getBody() instanceof ProblemDetails);
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageContent_BadRequest_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/package_content"))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.BAD_REQUEST));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(VNF_PACKAGE_ID + "/package_content");
+
+        assertTrue(responseEntity.getBody() instanceof ProblemDetails);
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageContent_UnauthorizedServer_InternalError_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/package_content"))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.UNAUTHORIZED));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(VNF_PACKAGE_ID + "/package_content");
+
+        assertTrue(responseEntity.getBody() instanceof ProblemDetails);
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testGetPackageContent_SuccessResponseFromServerWithNullPackage_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/package_content"))
+                .andExpect(method(HttpMethod.GET)).andRespond(withSuccess());
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(VNF_PACKAGE_ID + "/package_content");
+
+        assertEquals(ProblemDetails.class, responseEntity.getBody().getClass());
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testGetPackageArtifact_ValidArray_Success() {
+        final byte[] responseArray = buildByteArrayWithRandomData(10);
+
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH))
+                .andExpect(method(HttpMethod.GET))
+                .andRespond(withSuccess(responseArray, MediaType.APPLICATION_OCTET_STREAM));
+
+        final String testURL = "http://localhost:" + port + PACKAGE_MANAGEMENT_BASE_URL + "/vnf_packages/"
+                + VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH;
+        final HttpEntity<?> request = new HttpEntity<>(basicHttpHeadersProvider.getHttpHeaders());
+        final ResponseEntity<byte[]> responseEntity =
+                testRestTemplate.withBasicAuth("test", "test").exchange(testURL, HttpMethod.GET, request, byte[].class);
+
+        assertEquals(byte[].class, responseEntity.getBody().getClass());
+        assertArrayEquals(responseEntity.getBody(), responseArray);
+        assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageArtifact_Conflict_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.CONFLICT));
+
+        final ResponseEntity<ProblemDetails> responseEntity =
+                sendHttpRequest(VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH);
+
+        assertNotNull(responseEntity.getBody());
+        assertEquals(HttpStatus.CONFLICT, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageArtifact_NotFound_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.NOT_FOUND));
+
+        final ResponseEntity<ProblemDetails> responseEntity =
+                sendHttpRequest(VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH);
+
+        assertNotNull(responseEntity.getBody());
+        assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageArtifact_UnauthorizedClient_Fail() {
+        final String testURL = "http://localhost:" + port + PACKAGE_MANAGEMENT_BASE_URL + "/vnf_packages/"
+                + VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH;
+
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.UNAUTHORIZED));
+
+        final HttpEntity<?> request = new HttpEntity<>(basicHttpHeadersProvider.getHttpHeaders());
+        final ResponseEntity<ProblemDetails> responseEntity =
+                testRestTemplate.exchange(testURL, HttpMethod.GET, request, ProblemDetails.class);
+
+        assertNotNull(responseEntity.getBody());
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageArtifact_InternalServerError_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
+
+        final ResponseEntity<ProblemDetails> responseEntity =
+                sendHttpRequest(VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH);
+
+        assertNotNull(responseEntity.getBody());
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageArtifact_BadRequest_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.BAD_REQUEST));
+
+        final ResponseEntity<ProblemDetails> responseEntity =
+                sendHttpRequest(VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH);
+
+        assertNotNull(responseEntity.getBody());
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageArtifact_UnauthorizedServer_InternalError_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.UNAUTHORIZED));
+
+        final ResponseEntity<ProblemDetails> responseEntity =
+                sendHttpRequest(VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH);
+
+        assertNotNull(responseEntity.getBody());
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testGetPackageArtifact_SuccessResponseFromServerWithNullPackage_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH))
+                .andExpect(method(HttpMethod.GET)).andRespond(withSuccess());
+
+        final ResponseEntity<ProblemDetails> responseEntity =
+                sendHttpRequest(VNF_PACKAGE_ID + "/artifacts/" + ARTIFACT_PATH);
+
+        assertNotNull(responseEntity.getBody());
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testVnfPackagesReceivedAsInlineResponse2001ListIfGetVnfPackagesSuccessful() {
+        final VnfPkgInfo[] responses = createVnfPkgArray();
+
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL)).andExpect(method(HttpMethod.GET))
+                .andRespond(withSuccess(gson.toJson(responses), MediaType.APPLICATION_JSON));
+
+        final String testURL = localhostUrl + port + VNFPKGM_BASE_URL;
+        final HttpEntity<?> request = new HttpEntity<>(basicHttpHeadersProvider.getHttpHeaders());
+
+        final ResponseEntity<InlineResponse2001[]> responseEntity = testRestTemplate.withBasicAuth("test", "test")
+                .exchange(testURL, HttpMethod.GET, request, InlineResponse2001[].class);
+
+        assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
+        assertNotNull(responseEntity.getBody());
+        final InlineResponse2001[] inlineResponse2001array = responseEntity.getBody();
+        final InlineResponse2001 inlineResponse2001 = inlineResponse2001array[0];
+        assertEquals(VNF_PACKAGE_ID, inlineResponse2001.getId());
+        assertEquals(VNFD_ID, inlineResponse2001.getVnfdId());
+        assertEquals(VNFD_ID, inlineResponse2001.getSoftwareImages().get(0).getId());
+        assertEquals(VNF_PRODUCT_NAME, inlineResponse2001.getSoftwareImages().get(0).getName());
+        assertEquals(ALGORITHM, inlineResponse2001.getChecksum().getAlgorithm());
+        assertEquals(HASH, inlineResponse2001.getChecksum().getHash());
+        assertEquals(ARTIFACT_PATH, inlineResponse2001.getAdditionalArtifacts().get(0).getArtifactPath());
+        assertEquals(ALGORITHM, inlineResponse2001.getAdditionalArtifacts().get(0).getChecksum().getAlgorithm());
+        assertEquals(HASH, inlineResponse2001.getAdditionalArtifacts().get(0).getChecksum().getHash());
+        final VnfPackagesLinks links = inlineResponse2001.getLinks();
+        assertNotNull(links);
+        assertEquals(EXPECTED_SELF_HREF, links.getSelf().getHref());
+        assertEquals(EXPECTED_VNFD_HREF, links.getVnfd().getHref());
+        assertEquals(EXPECTED_PACKAGE_CONTENT_HREF, links.getPackageContent().getHref());
+    }
+
+    @Test
+    public void test400BadRequestInfoReceivedAsProblemDetailsIfGetVnfPackagesIs400BadRequest() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL)).andExpect(method(HttpMethod.GET))
+                .andRespond(withStatus(HttpStatus.BAD_REQUEST));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(GET_VNF_PACKAGES_URL);
+        assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode());
+
+        assertNotNull(responseEntity.getBody());
+        final ProblemDetails problemDetails = responseEntity.getBody();
+        assertEquals("Error: Bad Request Received", problemDetails.getDetail());
+    }
+
+    @Test
+    public void test404NotFoundInfoReceivedAsProblemDetailsIfGetVnfPackagesIs404NotFound() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL)).andExpect(method(HttpMethod.GET))
+                .andRespond(withStatus(HttpStatus.NOT_FOUND));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(GET_VNF_PACKAGES_URL);
+        assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode());
+
+        assertNotNull(responseEntity.getBody());
+        final ProblemDetails problemDetails = responseEntity.getBody();
+        assertEquals("No Vnf Packages found", problemDetails.getDetail());
+    }
+
+    @Test
+    public void test500InternalServerErrorProblemDetailsReceivedIfGetVnfPackagesReturns500InternalServerError() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL)).andExpect(method(HttpMethod.GET))
+                .andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(GET_VNF_PACKAGES_URL);
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+
+        assertNotNull(responseEntity.getBody());
+        final ProblemDetails problemDetails = responseEntity.getBody();
+        assertEquals("Internal Server Error Occurred.", problemDetails.getDetail());
+    }
+
+    @Test
+    public void test500InternalServerErrorProblemDetailsReceivedIfGetVnfPackagesReturnsANullPackage() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL)).andExpect(method(HttpMethod.GET))
+                .andRespond(withSuccess());
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(GET_VNF_PACKAGES_URL);
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+
+        assertNotNull(responseEntity.getBody());
+        final ProblemDetails problemDetails = responseEntity.getBody();
+        assertEquals("An error occurred, a null response was received by the\n"
+                + " Sol003PackageManagementController from the EtsiCatalogManager using the GET \"vnf_packages\" \n"
+                + "endpoint.", problemDetails.getDetail());
+    }
+
+    @Test
+    public void testVnfPackageReceivedAsInlineResponse2001IfGetVnfPackageByIdSuccessful() {
+        final VnfPkgInfo response = createVnfPkgInfo(VNF_PACKAGE_ID);
+
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID)).andExpect(method(HttpMethod.GET))
+                .andRespond(withSuccess(gson.toJson(response), MediaType.APPLICATION_JSON));
+
+        final String testURL = localhostUrl + port + VNFPKGM_BASE_URL + "/" + VNF_PACKAGE_ID;
+        final HttpEntity<?> request = new HttpEntity<>(basicHttpHeadersProvider.getHttpHeaders());
+        final ResponseEntity<InlineResponse2001> responseEntity = testRestTemplate.withBasicAuth("test", "test")
+                .exchange(testURL, HttpMethod.GET, request, InlineResponse2001.class);
+
+        assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
+        assertNotNull(responseEntity.getBody());
+        final InlineResponse2001 inlineResponse2001 = responseEntity.getBody();
+        assertEquals(VNF_PACKAGE_ID, inlineResponse2001.getId());
+        assertEquals(VNFD_ID, inlineResponse2001.getVnfdId());
+        assertEquals(VNFD_ID, inlineResponse2001.getSoftwareImages().get(0).getId());
+        assertEquals(VNF_PRODUCT_NAME, inlineResponse2001.getSoftwareImages().get(0).getName());
+        assertEquals(ALGORITHM, inlineResponse2001.getChecksum().getAlgorithm());
+        assertEquals(HASH, inlineResponse2001.getChecksum().getHash());
+        assertEquals(ARTIFACT_PATH, inlineResponse2001.getAdditionalArtifacts().get(0).getArtifactPath());
+        assertEquals(ALGORITHM, inlineResponse2001.getAdditionalArtifacts().get(0).getChecksum().getAlgorithm());
+        assertEquals(HASH, inlineResponse2001.getAdditionalArtifacts().get(0).getChecksum().getHash());
+        final VnfPackagesLinks links = inlineResponse2001.getLinks();
+        assertNotNull(links);
+        assertEquals(EXPECTED_SELF_HREF, links.getSelf().getHref());
+        assertEquals(EXPECTED_VNFD_HREF, links.getVnfd().getHref());
+        assertEquals(EXPECTED_PACKAGE_CONTENT_HREF, links.getPackageContent().getHref());
+
+    }
+
+    @Test
+    public void test400BadRequestInfoReceivedAsProblemDetailsIfGetVnfPackageByIdIs400BadRequest() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID)).andExpect(method(HttpMethod.GET))
+                .andRespond(withStatus(HttpStatus.BAD_REQUEST));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(GET_VNF_PACKAGE_BY_ID_URL);
+
+        assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode());
+        assertNotNull(responseEntity.getBody());
+        final ProblemDetails problemDetails = responseEntity.getBody();
+        assertEquals("Error: Bad Request Received", problemDetails.getDetail());
+    }
+
+    @Test
+    public void test404NotFoundInfoReceivedAsProblemDetailsIfGetVnfPackageByIdIs404NotFound() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID)).andExpect(method(HttpMethod.GET))
+                .andRespond(withStatus(HttpStatus.NOT_FOUND));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(GET_VNF_PACKAGE_BY_ID_URL);
+
+        assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode());
+        assertNotNull(responseEntity.getBody());
+        final ProblemDetails problemDetails = responseEntity.getBody();
+        assertEquals("No Vnf Package found with vnfPkgId: " + VNF_PACKAGE_ID, problemDetails.getDetail());
+    }
+
+    @Test
+    public void test500InternalServerErrorProblemDetailsReceivedIfGetVnfPackageByIdReturns500InternalServerError() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID)).andExpect(method(HttpMethod.GET))
+                .andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(GET_VNF_PACKAGE_BY_ID_URL);
+
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+        assertNotNull(responseEntity.getBody());
+        final ProblemDetails problemDetails = responseEntity.getBody();
+        assertEquals("Internal Server Error Occurred.", problemDetails.getDetail());
+    }
+
+    @Test
+    public void test500InternalServerErrorProblemDetailsReceivedIfGetVnfPackageByIdReturnsANullPackage() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID)).andExpect(method(HttpMethod.GET))
+                .andRespond(withSuccess());
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(GET_VNF_PACKAGE_BY_ID_URL);
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+
+        assertNotNull(responseEntity.getBody());
+        final ProblemDetails problemDetails = responseEntity.getBody();
+        assertEquals("An error occurred, a null response was received by the\n"
+                + " Sol003PackageManagementController from the EtsiCatalogManager using the GET \"vnf_packages\" by vnfPkgId: \""
+                + VNF_PACKAGE_ID + "\" \n" + "endpoint.", problemDetails.getDetail());
+    }
+
+    // The below test method is here to improve code coverage and provide a foundation for writing
+    // future tests
+    @Test
+    public void testGetPackageVnfd_ValidArray_Success() {
+        final byte[] responseArray = buildByteArrayWithRandomData(10);
+
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/vnfd"))
+                .andExpect(method(HttpMethod.GET))
+                .andRespond(withSuccess(responseArray, MediaType.APPLICATION_OCTET_STREAM));
+
+        final String testURL =
+                "http://localhost:" + port + PACKAGE_MANAGEMENT_BASE_URL + "/vnf_packages/" + VNF_PACKAGE_ID + "/vnfd";
+        final HttpEntity<?> request = new HttpEntity<>(basicHttpHeadersProvider.getHttpHeaders());
+        final ResponseEntity<byte[]> responseEntity =
+                testRestTemplate.withBasicAuth("test", "test").exchange(testURL, HttpMethod.GET, request, byte[].class);
+
+        assertEquals(byte[].class, responseEntity.getBody().getClass());
+        assertArrayEquals(responseEntity.getBody(), responseArray);
+        assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageVnfd_Conflict_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/vnfd"))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.CONFLICT));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(VNF_PACKAGE_ID + "/vnfd");
+
+        assertTrue(responseEntity.getBody() instanceof ProblemDetails);
+        assertEquals(HttpStatus.CONFLICT, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageVnfd_NotFound_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/vnfd"))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.NOT_FOUND));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(VNF_PACKAGE_ID + "/vnfd");
+
+        assertTrue(responseEntity.getBody() instanceof ProblemDetails);
+        assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageVnfd_UnauthorizedClient_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/vnfd"))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.UNAUTHORIZED));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(VNF_PACKAGE_ID + "/vnfd");
+
+        assertTrue(responseEntity.getBody() instanceof ProblemDetails);
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageVnfd_InternalServerError_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/vnfd"))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(VNF_PACKAGE_ID + "/vnfd");
+
+        assertTrue(responseEntity.getBody() instanceof ProblemDetails);
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageVnfd_BadRequest_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/vnfd"))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.BAD_REQUEST));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(VNF_PACKAGE_ID + "/vnfd");
+
+        assertTrue(responseEntity.getBody() instanceof ProblemDetails);
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testOnGetPackageVnfd_UnauthorizedServer_InternalError_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/vnfd"))
+                .andExpect(method(HttpMethod.GET)).andRespond(withStatus(HttpStatus.UNAUTHORIZED));
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(VNF_PACKAGE_ID + "/vnfd");
+
+        assertTrue(responseEntity.getBody() instanceof ProblemDetails);
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    @Test
+    public void testGetPackageVnfd_SuccessResponseFromServerWithNullPackage_Fail() {
+        mockRestServiceServer.expect(requestTo(MSB_BASE_URL + "/" + VNF_PACKAGE_ID + "/vnfd"))
+                .andExpect(method(HttpMethod.GET)).andRespond(withSuccess());
+
+        final ResponseEntity<ProblemDetails> responseEntity = sendHttpRequest(VNF_PACKAGE_ID + "/vnfd");
+
+        assertEquals(ProblemDetails.class, responseEntity.getBody().getClass());
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
+    }
+
+    // Simply returns a byte array filled with random data, for use in the tests.
+    private byte[] buildByteArrayWithRandomData(final int sizeInKb) {
+        final Random rnd = new Random();
+        final byte[] b = new byte[sizeInKb * 1024]; // converting kb to byte
+        rnd.nextBytes(b);
+        return b;
+    }
+
+    private ResponseEntity<ProblemDetails> sendHttpRequest(final String url) {
+        final String testURL = localhostUrl + port + VNFPKGM_BASE_URL + "/" + url;
+        final HttpEntity<?> request = new HttpEntity<>(basicHttpHeadersProvider.getHttpHeaders());
+        return testRestTemplate.withBasicAuth("test", "test").exchange(testURL, HttpMethod.GET, request,
+                ProblemDetails.class);
+    }
+
+    private VnfPkgInfo[] createVnfPkgArray() {
+        final VnfPkgInfo[] vnfPkgInfoArray = new VnfPkgInfo[1];
+        final VnfPkgInfo vnfPkgInfo = createVnfPkgInfo(VNF_PACKAGE_ID);
+        vnfPkgInfoArray[0] = vnfPkgInfo;
+        return vnfPkgInfoArray;
+    }
+
+    private VnfPkgInfo createVnfPkgInfo(final String vnfPackageId) {
+        final VnfPkgInfo vnfPkgInfo = new VnfPkgInfo();
+        vnfPkgInfo.setId(vnfPackageId);
+        vnfPkgInfo.setVnfdId(VNFD_ID);
+        vnfPkgInfo.setVnfProvider(VNF_PROVIDER);
+        vnfPkgInfo.setVnfProductName(VNF_PRODUCT_NAME);
+        vnfPkgInfo.setVnfSoftwareVersion(VNF_SOFTWARE_VERSION);
+        vnfPkgInfo.setVnfdVersion(VNFD_VERSION);
+        vnfPkgInfo.setChecksum(createVnfPkgChecksum());
+        vnfPkgInfo.setSoftwareImages(createSoftwareImages());
+        vnfPkgInfo.setAdditionalArtifacts(createAdditionalArtifacts());
+        vnfPkgInfo.setLinks(createVNFPKGMLinkSerializerLinks());
+        return vnfPkgInfo;
+    }
+
+    private Checksum createVnfPkgChecksum() {
+        final Checksum checksum = new Checksum();
+        checksum.setAlgorithm(ALGORITHM);
+        checksum.setHash(HASH);
+        return checksum;
+    }
+
+    private List<VnfPackageSoftwareImageInfo> createSoftwareImages() {
+        final List<VnfPackageSoftwareImageInfo> softwareImages = new ArrayList<>();
+        final VnfPackageSoftwareImageInfo vnfPackageSoftwareImageInfo = new VnfPackageSoftwareImageInfo();
+        vnfPackageSoftwareImageInfo.setId(VNFD_ID);
+        vnfPackageSoftwareImageInfo.setName(VNF_PRODUCT_NAME);
+        vnfPackageSoftwareImageInfo.setProvider("");
+        vnfPackageSoftwareImageInfo.setVersion("");
+        vnfPackageSoftwareImageInfo.setChecksum(createVnfPkgChecksum());
+        vnfPackageSoftwareImageInfo
+                .setContainerFormat(VnfPackageSoftwareImageInfo.ContainerFormatEnum.fromValue("AKI"));
+        softwareImages.add(vnfPackageSoftwareImageInfo);
+        return softwareImages;
+    }
+
+    private List<VnfPackageArtifactInfo> createAdditionalArtifacts() {
+        final List<VnfPackageArtifactInfo> vnfPackageArtifactInfos = new ArrayList<>();
+        final VnfPackageArtifactInfo vnfPackageArtifactInfo =
+                new VnfPackageArtifactInfo().artifactPath(ARTIFACT_PATH).checksum(createVnfPkgChecksum());
+        vnfPackageArtifactInfos.add(vnfPackageArtifactInfo);
+        return vnfPackageArtifactInfos;
+    }
+
+    private VNFPKGMLinkSerializer createVNFPKGMLinkSerializerLinks() {
+        final String baseUrl = "http://msb-iag:443/api/vnfpkgm/v1/vnf_packages";
+        return new VNFPKGMLinkSerializer().self(new UriLink().href(baseUrl + "/myVnfPackageId"))
+                .vnfd(new UriLink().href(baseUrl + "/myVnfPackageId/vnfd"))
+                .packageContent(new UriLink().href(baseUrl + "/myVnfPackageId/package_content"));
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementSubscriptionControllerTest.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementSubscriptionControllerTest.java
new file mode 100644
index 0000000000..b8eb73b2c8
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/Sol003PackageManagementSubscriptionControllerTest.java
@@ -0,0 +1,403 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.onap.so.adapters.vnfmadapter.common.CommonConstants.ETSI_SUBSCRIPTION_NOTIFICATION_BASE_URL;
+import static org.onap.so.adapters.vnfmadapter.common.CommonConstants.PACKAGE_MANAGEMENT_BASE_URL;
+import static org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.EtsiCatalogServiceProviderConfiguration.ETSI_CATALOG_REST_TEMPLATE_BEAN;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.onap.so.adapters.vnfmadapter.PackageManagementConstants;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.BasicAuth;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.LinkSelf;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.NsdmSubscription;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmNotificationsFilter;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscription;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.ProblemDetails;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.SubscriptionAuthentication;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.Version;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.VnfProducts;
+import org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.VnfProductsProviders;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.InlineResponse2002;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.InlineResponse201;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.PkgmSubscriptionRequest;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsAuthentication;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsFilter1;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsFilterVnfProductsFromProviders;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.SubscriptionsLinks;
+import org.onap.so.adapters.vnfmadapter.packagemanagement.model.VnfPackagesLinksSelf;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.test.web.client.match.MockRestRequestMatchers;
+import org.springframework.web.client.RestTemplate;
+import com.google.gson.Gson;
+
+/**
+ * @author Ronan Kenny (ronan.kenny@est.tech)
+ * @author Gareth Roper (gareth.roper@est.tech)
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = TestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ActiveProfiles("test")
+public class Sol003PackageManagementSubscriptionControllerTest {
+
+    private final Gson gson = new Gson();
+    private final URI msbEndpoint = URI.create("http://msb-iag.onap:80/api/vnfpkgm/v1/subscriptions");
+    private static final String _NOTIFICATION_CALLBACK_URI =
+            "https://so-vnfm-adapter.onap:30406" + ETSI_SUBSCRIPTION_NOTIFICATION_BASE_URL;
+    private static final String LOCALHOST_URL = "http://localhost:";
+
+    @Autowired
+    @Qualifier(ETSI_CATALOG_REST_TEMPLATE_BEAN)
+    private RestTemplate restTemplate;
+    private MockRestServiceServer mockRestServiceServer;
+    @Autowired
+    private CacheManager cacheServiceProvider;
+    @Autowired
+    private Sol003PackageManagementSubscriptionController sol003PackageManagementSubscriptionController;
+
+    @Autowired
+    private TestRestTemplate testRestTemplate;
+
+    @LocalServerPort
+    private int port;
+
+    private static final String ID = UUID.randomUUID().toString();
+
+    @Before
+    public void setUp() {
+        mockRestServiceServer = MockRestServiceServer.bindTo(restTemplate).build();
+        final Cache cache =
+                cacheServiceProvider.getCache(PackageManagementConstants.PACKAGE_MANAGEMENT_SUBSCRIPTION_CACHE);
+        cache.clear();
+    }
+
+    @After
+    public void after() {
+        mockRestServiceServer.reset();
+    }
+
+    @Test
+    public void testSuccessPostSubscription() throws GeneralSecurityException, URISyntaxException {
+        final PkgmSubscriptionRequest pkgmSubscriptionRequest = postSubscriptionForTest();
+        final ResponseEntity<InlineResponse201> response =
+                (ResponseEntity<InlineResponse201>) sol003PackageManagementSubscriptionController
+                        .postSubscriptionRequest(pkgmSubscriptionRequest);
+
+        final HttpHeaders headers = buildHttpHeaders(Objects.requireNonNull(response.getBody()).getCallbackUri());
+
+        final SubscriptionsLinks subscriptionsLinks = new SubscriptionsLinks();
+        final VnfPackagesLinksSelf vnfPackagesLinksSelf = new VnfPackagesLinksSelf();
+        vnfPackagesLinksSelf.setHref("https://so-vnfm-adapter.onap:30406" + PACKAGE_MANAGEMENT_BASE_URL
+                + "/subscriptions/" + response.getBody().getId());
+        subscriptionsLinks.setSelf(vnfPackagesLinksSelf);
+
+        assertEquals(pkgmSubscriptionRequest.getFilter(), response.getBody().getFilter());
+        assertEquals(subscriptionsLinks, response.getBody().getLinks());
+        assertEquals(response.getBody().getFilter(), pkgmSubscriptionRequest.getFilter());
+        assert (response.getHeaders().equals(headers));
+        assertThat(response.getStatusCode(), is(HttpStatus.CREATED));
+        assertNotNull(response.getBody().getCallbackUri());
+    }
+
+    @Test
+    public void testFailPostSubscriptionAlreadyExists() throws GeneralSecurityException {
+        final PkgmSubscriptionRequest pkgmSubscriptionRequest = postSubscriptionForTest();
+
+        final ResponseEntity<InlineResponse2002> response =
+                (ResponseEntity<InlineResponse2002>) sol003PackageManagementSubscriptionController
+                        .postSubscriptionRequest(pkgmSubscriptionRequest);
+
+        // Create duplicate entry
+        final PkgmSubscriptionRequest pkgmSubscriptionRequest2 = buildPkgmSubscriptionRequest();
+
+        final ResponseEntity<InlineResponse2002> response2002 =
+                (ResponseEntity<InlineResponse2002>) sol003PackageManagementSubscriptionController
+                        .postSubscriptionRequest(pkgmSubscriptionRequest2);
+
+        assertEquals(HttpStatus.SEE_OTHER, response2002.getStatusCode());
+    }
+
+    @Test
+    public void testSuccessGetSubscriptionWithSubscriptionId() throws GeneralSecurityException, URISyntaxException {
+
+        final PkgmSubscriptionRequest pkgmSubscriptionRequest = postSubscriptionForTest();
+
+        mockRestServiceServer.expect(requestTo(msbEndpoint + "/" + ID)).andExpect(method(HttpMethod.GET))
+                .andRespond(withSuccess(gson.toJson(new NsdmSubscription().id(ID)), MediaType.APPLICATION_JSON));
+
+        final ResponseEntity<InlineResponse201> response =
+                (ResponseEntity<InlineResponse201>) sol003PackageManagementSubscriptionController
+                        .postSubscriptionRequest(pkgmSubscriptionRequest);
+        final String subscriptionId = response.getBody().getId();
+
+
+
+        final ResponseEntity<InlineResponse201> responseEntity =
+                (ResponseEntity<InlineResponse201>) sol003PackageManagementSubscriptionController
+                        .getSubscription(subscriptionId);
+
+        final HttpHeaders headers = buildHttpHeaders(response.getBody().getCallbackUri());
+
+        assertEquals(response.getBody().getFilter(), pkgmSubscriptionRequest.getFilter());
+        assertEquals(response.getHeaders(), headers);
+        assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
+        assertEquals(pkgmSubscriptionRequest.getFilter(), response.getBody().getFilter());
+        // Ensure CallBackUri is set to new URI
+        assertNotEquals(pkgmSubscriptionRequest.getCallbackUri(), response.getBody().getCallbackUri());
+    }
+
+    @Test
+    public void testFailGetSubscriptionWithInvalidSubscriptionId() {
+        final String invalidId = "invalidSubscriptionId";
+        mockRestServiceServer.expect(requestTo(msbEndpoint + "/" + invalidId)).andExpect(method(HttpMethod.GET))
+                .andRespond(withStatus(HttpStatus.NOT_FOUND));
+        final ResponseEntity<?> response = sol003PackageManagementSubscriptionController.getSubscription(invalidId);
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        assertTrue(response.getBody() instanceof ProblemDetails);
+    }
+
+    @Test
+    public void testSuccessGetSubscriptions() throws GeneralSecurityException {
+        final PkgmSubscription pkgmSubscription = buildPkgmSubscription();
+        final PkgmSubscriptionRequest pkgmSubscriptionRequest = buildPkgmSubscriptionRequest();
+
+        mockRestServiceServer.expect(requestTo(msbEndpoint)).andExpect(method(HttpMethod.POST))
+                .andRespond(withSuccess(gson.toJson(pkgmSubscription), MediaType.APPLICATION_JSON));
+        mockRestServiceServer.expect(requestTo(msbEndpoint + "/" + ID)).andExpect(method(HttpMethod.GET))
+                .andRespond(withSuccess(gson.toJson(new NsdmSubscription().id(ID)), MediaType.APPLICATION_JSON));
+
+        sol003PackageManagementSubscriptionController.postSubscriptionRequest(pkgmSubscriptionRequest);
+        final ResponseEntity<List<InlineResponse201>> response =
+                sol003PackageManagementSubscriptionController.getSubscriptions();
+
+        final List<InlineResponse201> subscriptionsList = response.getBody();
+
+        assertEquals(Objects.requireNonNull(response.getBody()).get(0).getFilter(),
+                pkgmSubscriptionRequest.getFilter());
+        assertNotNull(subscriptionsList != null);
+        assertNotEquals('0', subscriptionsList.size());
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+    }
+
+    @Test
+    public void testSuccessDeleteSubscriptionWithSubscriptionId() throws GeneralSecurityException {
+        final PkgmSubscriptionRequest pkgmSubscriptionRequest = buildPkgmSubscriptionRequest();
+        final PkgmSubscription pkgmSubscription = buildPkgmSubscription();
+        final String subscriptionId = pkgmSubscription.getId();
+
+        mockRestServiceServer.expect(requestTo(msbEndpoint)).andExpect(method(HttpMethod.POST))
+                .andRespond(withSuccess(gson.toJson(pkgmSubscription), MediaType.APPLICATION_JSON));
+
+        mockRestServiceServer.expect(requestTo(msbEndpoint + "/" + subscriptionId)).andExpect(method(HttpMethod.DELETE))
+                .andRespond(withStatus(HttpStatus.NO_CONTENT));
+        mockRestServiceServer.expect(requestTo(msbEndpoint + "/" + subscriptionId)).andExpect(method(HttpMethod.GET))
+                .andRespond(withSuccess(gson.toJson(new NsdmSubscription().id(subscriptionId)),
+                        MediaType.APPLICATION_JSON));
+
+        final ResponseEntity<InlineResponse2002> responsePost =
+                (ResponseEntity<InlineResponse2002>) sol003PackageManagementSubscriptionController
+                        .postSubscriptionRequest(pkgmSubscriptionRequest);
+
+        final ResponseEntity responseDelete =
+                sol003PackageManagementSubscriptionController.deleteSubscription(subscriptionId);
+
+        // Attempt to retrieve the subscription after delete
+        final ResponseEntity<InlineResponse2002> responseGetSubscription =
+                (ResponseEntity<InlineResponse2002>) sol003PackageManagementSubscriptionController
+                        .getSubscription(subscriptionId);
+
+        assertEquals(HttpStatus.NOT_FOUND, responseGetSubscription.getStatusCode());
+        assertEquals(HttpStatus.NO_CONTENT, responseDelete.getStatusCode());
+    }
+
+    @Test
+    public void testDeleteSubscription_SubscripitonNotFoundInEtsiCatalogManager_SubscriptionDeletedFromLocalCache()
+            throws GeneralSecurityException {
+        final PkgmSubscriptionRequest pkgmSubscriptionRequest = buildPkgmSubscriptionRequest();
+        final PkgmSubscription pkgmSubscription = buildPkgmSubscription();
+
+        mockRestServiceServer.expect(requestTo(msbEndpoint)).andExpect(method(HttpMethod.POST))
+                .andRespond(withSuccess(gson.toJson(pkgmSubscription), MediaType.APPLICATION_JSON));
+
+        mockRestServiceServer.expect(requestTo(msbEndpoint + "/" + ID)).andExpect(method(HttpMethod.DELETE))
+                .andRespond(withStatus(HttpStatus.NOT_FOUND));
+
+        final ResponseEntity<InlineResponse2002> responsePost =
+                (ResponseEntity<InlineResponse2002>) sol003PackageManagementSubscriptionController
+                        .postSubscriptionRequest(pkgmSubscriptionRequest);
+
+        final Cache cache =
+                cacheServiceProvider.getCache(PackageManagementConstants.PACKAGE_MANAGEMENT_SUBSCRIPTION_CACHE);
+        assertNotNull(cache.get(ID));
+
+        final ResponseEntity responseDelete = sol003PackageManagementSubscriptionController.deleteSubscription(ID);
+
+        assertEquals(HttpStatus.NO_CONTENT, responseDelete.getStatusCode());
+        assertNull(cache.get(ID));
+
+    }
+
+    @Test
+    public void testFailDeleteSubscriptionWithInvalidSubscriptionId() throws URISyntaxException, InterruptedException {
+        final ResponseEntity<Void> responseDelete = (ResponseEntity<Void>) sol003PackageManagementSubscriptionController
+                .deleteSubscription("invalidSubscriptionId");
+        assertEquals(HttpStatus.NOT_FOUND, responseDelete.getStatusCode());
+    }
+
+
+    @Test
+    public void testSuccessPostSubscriptionWithValidNotificationTypes() throws Exception {
+
+        final String file = getAbsolutePath("src/test/resources/requests/SubscriptionRequest.json");
+        final String json = new String(Files.readAllBytes(Paths.get(file)));
+        final PkgmSubscriptionRequest request = gson.fromJson(json, PkgmSubscriptionRequest.class);
+
+        mockRestServiceServer.expect(requestTo(msbEndpoint)).andExpect(method(HttpMethod.POST))
+                .andExpect(MockRestRequestMatchers.content().json(gson.toJson(getEtsiCatalogPkgmSubscriptionRequest())))
+                .andRespond(withSuccess(gson.toJson(buildPkgmSubscription()), MediaType.APPLICATION_JSON));
+
+
+        final ResponseEntity<InlineResponse201> responseEntity =
+                testRestTemplate.postForEntity(LOCALHOST_URL + port + PACKAGE_MANAGEMENT_BASE_URL + "/subscriptions",
+                        request, InlineResponse201.class);
+
+        assertEquals(HttpStatus.CREATED, responseEntity.getStatusCode());
+        assertTrue(responseEntity.hasBody());
+        final InlineResponse201 actual = responseEntity.getBody();
+        assertEquals(ID, actual.getId());
+
+
+    }
+
+    private org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscriptionRequest getEtsiCatalogPkgmSubscriptionRequest() {
+        return new org.onap.so.adapters.vnfmadapter.extclients.etsicatalog.model.PkgmSubscriptionRequest()
+                .filter(new PkgmNotificationsFilter()
+                        .addNotificationTypesItem(
+                                PkgmNotificationsFilter.NotificationTypesEnum.VNFPACKAGEONBOARDINGNOTIFICATION)
+                        .addVnfdIdItem("VNFDID").addVnfPkgIdItem("VNFPKGID")
+                        .addOperationalStateItem(PkgmNotificationsFilter.OperationalStateEnum.ENABLED)
+                        .addVnfProductsFromProvidersItem(new VnfProductsProviders().vnfProvider("EST")
+                                .addVnfProductsItem(new VnfProducts().vnfProductName("VnfProducts")
+                                        .addVersionsItem(new Version().vnfSoftwareVersion("vnfSoftwareVersion")
+                                                .addVnfdVersionsItem("version1")))))
+                .callbackUri(_NOTIFICATION_CALLBACK_URI).authentication(
+                        new SubscriptionAuthentication().addAuthTypeItem(SubscriptionAuthentication.AuthTypeEnum.BASIC)
+                                .paramsBasic(new BasicAuth().userName("vnfm").password("password1$")));
+    }
+
+    private PkgmSubscriptionRequest buildPkgmSubscriptionRequest() {
+        final PkgmSubscriptionRequest pkgmSubscriptionRequest = new PkgmSubscriptionRequest();
+        final SubscriptionsFilter1 sub = buildSubscriptionsFilter();
+        final SubscriptionsAuthentication auth = new SubscriptionsAuthentication();
+        pkgmSubscriptionRequest.setFilter(sub);
+        pkgmSubscriptionRequest.setCallbackUri(msbEndpoint.toString());
+        pkgmSubscriptionRequest.setAuthentication(auth);
+        return pkgmSubscriptionRequest;
+    }
+
+    private SubscriptionsFilter1 buildSubscriptionsFilter() {
+        final SubscriptionsFilter1 sub = new SubscriptionsFilter1();
+        final List<String> vnfdIdList = new ArrayList<>();
+        final List<String> vnfPkgIdList = new ArrayList<>();
+        final List<SubscriptionsFilter1.NotificationTypesEnum> notificationTypes = new ArrayList<>();
+        final SubscriptionsFilterVnfProductsFromProviders subscriptionsFilterVnfProductsFromProviders =
+                new SubscriptionsFilterVnfProductsFromProviders();
+        final List<SubscriptionsFilterVnfProductsFromProviders> vnfProductsFromProviders = new ArrayList<>();
+
+        vnfProductsFromProviders.add(subscriptionsFilterVnfProductsFromProviders);
+        sub.setVnfdId(vnfdIdList);
+        sub.setNotificationTypes(notificationTypes);
+        sub.setVnfPkgId(vnfPkgIdList);
+        sub.setVnfProductsFromProviders(vnfProductsFromProviders);
+        return sub;
+    }
+
+    private PkgmSubscription buildPkgmSubscription() {
+        final PkgmSubscription pkgmSubscription = new PkgmSubscription();
+        final PkgmNotificationsFilter pkgmNotificationsFilter = new PkgmNotificationsFilter();
+        final LinkSelf linkSelf = new LinkSelf();
+
+        pkgmSubscription.setId(ID);
+        pkgmSubscription.setCallbackUri(msbEndpoint + "/" + pkgmSubscription.getId().toString());
+        pkgmSubscription.setFilter(pkgmNotificationsFilter);
+        pkgmSubscription.setLinks(linkSelf);
+        return pkgmSubscription;
+    }
+
+    private PkgmSubscriptionRequest postSubscriptionForTest() {
+        final PkgmSubscriptionRequest pkgmSubscriptionRequest = buildPkgmSubscriptionRequest();
+        final PkgmSubscription pkgmSubscription = buildPkgmSubscription();
+
+        mockRestServiceServer.expect(requestTo(msbEndpoint)).andExpect(method(HttpMethod.POST))
+                .andRespond(withSuccess(gson.toJson(pkgmSubscription), MediaType.APPLICATION_JSON));
+        return pkgmSubscriptionRequest;
+    }
+
+    private HttpHeaders buildHttpHeaders(final String uri) throws URISyntaxException {
+        final HttpHeaders headers = new HttpHeaders();
+        final URI myUri = new URI(uri);
+        headers.setLocation(myUri);
+        return headers;
+    }
+
+    private String getAbsolutePath(final String path) {
+        final File file = new File(path);
+        return file.getAbsolutePath();
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/TestApplication.java b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/TestApplication.java
new file mode 100755
index 0000000000..152d3136f1
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/TestApplication.java
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.adapters.vnfmadapter.rest;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
+import org.springframework.cache.annotation.EnableCaching;
+
+@EnableCaching
+@SpringBootApplication(scanBasePackages = {"org.onap.so"})
+@EnableAutoConfiguration(exclude = {JacksonAutoConfiguration.class})
+public class TestApplication {
+
+    public static void main(final String[] args) {
+        new SpringApplication(TestApplication.class).run(args);
+    }
+
+}
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/resources/application.yaml b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/resources/application.yaml
new file mode 100644
index 0000000000..cdb6662191
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/resources/application.yaml
@@ -0,0 +1,61 @@
+# Copyright © 2019 Nordix Foundation
+#
+# Licensed 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.
+spring:
+  security:
+    usercredentials:
+      - username: test
+        password: '$2a$12$Zi3AuYcZoZO/gBQyUtST2.F5N6HqcTtaNci2Et.ufsQhski56srIu'
+        role: BPEL-Client
+      - username: vnfm
+        password: '$2a$10$Fh9ffgPw2vnmsghsRD3ZauBL1aKXebigbq3BB1RPWtE62UDILsjke'
+        role: BPEL-Client
+  main:
+    allow-bean-definition-overriding: true
+
+mso:
+  key: 07a7159d3bf51a0e53be7a8f89699be7
+
+aai:
+  auth: 2A11B07DB6214A839394AA1EC5844695F5114FC407FF5422625FB00175A3DCB8A1FF745F22867EFA72D5369D599BBD88DA8BED4233CF5586
+  endpoint: https://aai.onap:8443
+  version: v15
+
+sdc:
+  username: sdcUser
+  password: sdcPassword
+  key: adadadadad
+  endpoint: http://sdc.onap/1234A
+
+
+vnfmadapter:
+  endpoint: https://so-vnfm-adapter.onap:30406
+
+  
+#Actuator
+management:
+  endpoints:
+    web:
+      base-path: /manage
+      exposure:
+        include: "*"
+  metrics:
+    se-global-registry: false
+    export:
+      prometheus:
+        enabled: true # Whether exporting of metrics to Prometheus is enabled.
+        step: 1m # Step size (i.e. reporting frequency) to use.
+
+etsi-catalog-manager:
+   vnfpkgm:
+      endpoint: http://msb-iag.onap:80/api/vnfpkgm/v1
diff --git a/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/resources/requests/SubscriptionRequest.json b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/resources/requests/SubscriptionRequest.json
new file mode 100644
index 0000000000..c38466f8e5
--- /dev/null
+++ b/adapters/etsi-sol003-adapter/etsi-sol003-package-management/etsi-sol003-package-management-adapter/src/test/resources/requests/SubscriptionRequest.json
@@ -0,0 +1,45 @@
+{
+    "filter": 
+    {
+        "notificationTypes": [
+            "VnfPackageOnboardingNotification"
+        ],
+        "vnfProductsFromProviders": [
+            {
+                "vnfProvider": "EST",
+                "vnfProducts": [
+                    {
+                        "vnfProductName": "VnfProducts",
+                        "versions": [
+                            {
+                                "vnfSoftwareVersion": "vnfSoftwareVersion",
+                                "vnfdVersions": [
+                                    "version1"
+                                ]
+                            }
+                        ]
+                    }
+                ]
+            }
+        ],
+        "vnfdId": [
+            "VNFDID"
+        ],
+        "vnfPkgId": [
+            "VNFPKGID"
+        ],
+        "operationalState": ["ENABLED"]
+    },
+    "callbackUri": "http://so-vnfm-adapter:9093/api/vnfpkgm/v1/subscriptions",
+    "authentication": 
+    {
+        "authType": [
+            "BASIC"
+        ],
+        "paramsBasic": 
+        {
+            "userName": "vnfm",
+            "password": "vnfm"
+        }
+    }
+}
-- 
cgit 1.2.3-korg