NONRTRIC PMS added support for custom A1 adapters 03/129203/2
authorPatrikBuhr <patrik.buhr@est.tech>
Wed, 11 May 2022 11:10:40 +0000 (13:10 +0200)
committerPatrikBuhr <patrik.buhr@est.tech>
Thu, 12 May 2022 07:18:45 +0000 (09:18 +0200)
Added support for added external A1-P adapter. This makes it possible to design and include
adapter to APIs for accessing of A1 policies (in a NearRT-RIC) without any changes in this
SW.

Issue-ID: CCSDK-3655
Signed-off-by: PatrikBuhr <patrik.buhr@est.tech>
Change-Id: Idc7bf97963a2455bde983b06a5f861df77d901ae

19 files changed:
a1-policy-management/README.md
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1Client.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1ClientFactory.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/StdA1ClientVersion2.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ApplicationConfigParser.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/RicConfig.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/repository/Ric.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1ClientFactoryTest.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/A1ClientHelper.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/OscA1ClientTest.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/StdA1ClientV2Test.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ApplicationConfigTest.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ApplicationTest.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/tasks/RefreshConfigTaskTest.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/tasks/RicSupervisionTest.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/tasks/RicSynchronizationTaskTest.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/tasks/ServiceSupervisionTest.java
a1-policy-management/src/test/resources/test_application_configuration.json
a1-policy-management/src/test/resources/test_application_configuration_with_dmaap_config.json

index 5c619c2..c25b29d 100644 (file)
@@ -75,7 +75,7 @@ Sample Response Message to DMaaP:
 ## License
 
 ONAP : ccsdk oran
-Copyright (C) 2019-2020 Nordix Foundation. All rights reserved.
+Copyright (C) 2019-2022 Nordix Foundation. 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
index 5e498e4..a691ee1 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.ccsdk.oran.a1policymanagementservice.clients;
 
 import java.util.List;
 
+import org.onap.ccsdk.oran.a1policymanagementservice.configuration.RicConfig;
 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy;
 
 import reactor.core.publisher.Flux;
@@ -33,6 +34,10 @@ import reactor.core.publisher.Mono;
  */
 public interface A1Client {
 
+    public interface Factory {
+        A1Client create(RicConfig ricConfig, AsyncRestClientFactory restClientFactory);
+    }
+
     public enum A1ProtocolType {
         UNKNOWN, //
         STD_V1_1, // STD A1 version 1.1
@@ -40,7 +45,8 @@ public interface A1Client {
         OSC_V1, // OSC 'A1'
         CCSDK_A1_ADAPTER_STD_V1_1, // CCSDK_A1_ADAPTER with STD A1 version 1.1 southbound
         CCSDK_A1_ADAPTER_STD_V2_0_0, // CCSDK_A1_ADAPTER with STD A1 version 2.0.0 southbound
-        CCSDK_A1_ADAPTER_OSC_V1 // CCSDK_A1_ADAPTER with OSC 'A1' southbound
+        CCSDK_A1_ADAPTER_OSC_V1, // CCSDK_A1_ADAPTER with OSC 'A1' southbound
+        CUSTOM_PROTOCOL // Some other protocol handled by some custom A1 adapter class.
     }
 
     public Mono<A1ProtocolType> getProtocolVersion();
index 1d465c3..04dd08e 100644 (file)
 
 package org.onap.ccsdk.oran.a1policymanagementservice.clients;
 
+import java.lang.reflect.Constructor;
+
 import org.onap.ccsdk.oran.a1policymanagementservice.clients.A1Client.A1ProtocolType;
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig;
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ControllerConfig;
+import org.onap.ccsdk.oran.a1policymanagementservice.configuration.RicConfig;
 import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException;
 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric;
 import org.slf4j.Logger;
@@ -83,6 +86,8 @@ public class A1ClientFactory {
                 || version == A1ProtocolType.CCSDK_A1_ADAPTER_OSC_V1
                 || version == A1ProtocolType.CCSDK_A1_ADAPTER_STD_V2_0_0) {
             return new CcsdkA1AdapterClient(version, ric.getConfig(), getControllerConfig(ric), this.restClientFactory);
+        } else if (version == A1ProtocolType.CUSTOM_PROTOCOL) {
+            return createCustomAdapter(ric);
         } else {
             logger.error("Unhandled protocol: {}", version);
             throw new ServiceException("Unhandled protocol");
@@ -103,6 +108,25 @@ public class A1ClientFactory {
         }
     }
 
+    private A1Client createCustomAdapter(Ric ric) throws ServiceException {
+        try {
+            Class<?> clazz = Class.forName(ric.getConfig().customAdapterClass());
+            if (A1Client.class.isAssignableFrom(clazz)) {
+                Constructor<?> constructor = clazz.getConstructor(RicConfig.class, AsyncRestClientFactory.class);
+                return (A1Client) constructor.newInstance(ric.getConfig(), this.restClientFactory);
+            } else if (A1Client.Factory.class.isAssignableFrom(clazz)) {
+                A1Client.Factory factory = (A1Client.Factory) clazz.getDeclaredConstructor().newInstance();
+                return factory.create(ric.getConfig(), this.restClientFactory);
+            } else {
+                throw new ServiceException("The custom class must either implement A1Client.Factory or A1Client");
+            }
+        } catch (ClassNotFoundException e) {
+            throw new ServiceException("Could not find class: " + ric.getConfig().customAdapterClass(), e);
+        } catch (Exception e) {
+            throw new ServiceException("Cannot create custom adapter: " + ric.getConfig().customAdapterClass(), e);
+        }
+    }
+
     private void assertNoControllerConfig(Ric ric, A1ProtocolType version) throws ServiceException {
         if (!ric.getConfig().controllerName().isEmpty()) {
             ric.setProtocolVersion(A1ProtocolType.UNKNOWN);
index d0f4da2..b91edee 100644 (file)
@@ -41,6 +41,13 @@ import reactor.core.publisher.Mono;
 public class StdA1ClientVersion2 implements A1Client {
     static final int CONCURRENCY_RIC = 1; // How many paralell requests that is sent to one NearRT RIC
 
+    public static class Factory implements A1Client.Factory {
+        @Override
+        public A1Client create(RicConfig ricConfig, AsyncRestClientFactory restClientFactory) {
+            return new StdA1ClientVersion2(ricConfig, restClientFactory);
+        }
+    }
+
     public static class OranV2UriBuilder implements A1UriBuilder {
         private final RicConfig ricConfig;
 
index 726d67f..fc1985a 100644 (file)
@@ -163,13 +163,13 @@ public class ApplicationConfigParser {
     private List<RicConfig> parseRics(JsonObject config) throws ServiceException {
         List<RicConfig> result = new ArrayList<>();
         for (JsonElement ricElem : getAsJsonArray(config, "ric")) {
-            JsonObject ricAsJson = ricElem.getAsJsonObject();
-            JsonElement controllerNameElement = ricAsJson.get(CONTROLLER);
+            JsonObject ricJsonObj = ricElem.getAsJsonObject();
             RicConfig ricConfig = ImmutableRicConfig.builder() //
-                    .ricId(get(ricAsJson, "name", "id", "ricId").getAsString()) //
-                    .baseUrl(get(ricAsJson, "baseUrl").getAsString()) //
-                    .managedElementIds(parseManagedElementIds(get(ricAsJson, "managedElementIds").getAsJsonArray())) //
-                    .controllerName(controllerNameElement != null ? controllerNameElement.getAsString() : "") //
+                    .ricId(get(ricJsonObj, "name", "id", "ricId").getAsString()) //
+                    .baseUrl(get(ricJsonObj, "baseUrl").getAsString()) //
+                    .managedElementIds(parseManagedElementIds(get(ricJsonObj, "managedElementIds").getAsJsonArray())) //
+                    .controllerName(getString(ricJsonObj, CONTROLLER, ""))
+                    .customAdapterClass(getString(ricJsonObj, "customAdapterClass", "")) //
                     .build();
             if (!ricConfig.baseUrl().isEmpty()) {
                 result.add(ricConfig);
@@ -180,6 +180,14 @@ public class ApplicationConfigParser {
         return result;
     }
 
+    String getString(JsonObject obj, String name, String defaultValue) {
+        JsonElement elem = obj.get(name);
+        if (elem != null) {
+            return elem.getAsString();
+        }
+        return defaultValue;
+    }
+
     Map<String, ControllerConfig> parseControllerConfigs(JsonObject config) throws ServiceException {
         if (config.get(CONTROLLER) == null) {
             return new HashMap<>();
index f43737f..012f1f6 100644 (file)
@@ -40,7 +40,7 @@ public class Ric {
     private RicConfig ricConfig;
     private RicState state = RicState.UNAVAILABLE;
     private Map<String, PolicyType> supportedPolicyTypes = new HashMap<>();
-    @Getter
+
     @Setter
     private A1ProtocolType protocolVersion = A1ProtocolType.UNKNOWN;
 
@@ -73,6 +73,14 @@ public class Ric {
         this.state = state;
     }
 
+    public synchronized A1ProtocolType getProtocolVersion() {
+        if (this.ricConfig.customAdapterClass().isEmpty()) {
+            return this.protocolVersion;
+        } else {
+            return A1ProtocolType.CUSTOM_PROTOCOL;
+        }
+    }
+
     /**
      * Gets the nodes managed by this Ric.
      *
index 645f235..499889a 100644 (file)
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.when;
 
 import java.util.Vector;
 
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -39,6 +40,7 @@ import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationCo
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ControllerConfig;
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ImmutableControllerConfig;
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ImmutableRicConfig;
+import org.onap.ccsdk.oran.a1policymanagementservice.configuration.RicConfig;
 import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException;
 import org.onap.ccsdk.oran.a1policymanagementservice.repository.Ric;
 
@@ -68,21 +70,25 @@ class A1ClientFactoryTest {
     private Ric ric;
     private A1ClientFactory factoryUnderTest;
 
-    private static ImmutableRicConfig ricConfig(String controllerName) {
+    private static ImmutableRicConfig ricConfig(String controllerName, String customAdapter) {
         return ImmutableRicConfig.builder() //
                 .ricId(RIC_NAME) //
                 .baseUrl("baseUrl") //
                 .managedElementIds(new Vector<>()) //
                 .controllerName(controllerName) //
+                .customAdapterClass(customAdapter) //
                 .build();
     }
 
+    private static ImmutableRicConfig ricConfig(String controllerName) {
+        return ricConfig(controllerName, "");
+    }
+
     @BeforeEach
     void createFactoryUnderTest() {
         SecurityContext sec = new SecurityContext("");
         factoryUnderTest = spy(new A1ClientFactory(applicationConfigMock, sec));
         this.ric = new Ric(ricConfig(""));
-
     }
 
     @Test
@@ -109,6 +115,38 @@ class A1ClientFactoryTest {
         assertEquals(A1ProtocolType.STD_V1_1, ric.getProtocolVersion(), "Not correct protocol");
     }
 
+    public static class CustomA1AdapterFactory implements A1Client.Factory {
+        @Override
+        public A1Client create(RicConfig ricConfig, AsyncRestClientFactory restClientFactory) {
+            return new StdA1ClientVersion2(ricConfig, restClientFactory);
+        }
+    }
+
+    @Test
+    void testCustomAdapterCreation() {
+
+        Ric ric = new Ric(ricConfig("", CustomA1AdapterFactory.class.getName()));
+        A1Client client = factoryUnderTest.createA1Client(ric).block();
+
+        assertEquals(client.getClass(), StdA1ClientVersion2.class);
+
+        ric = new Ric(ricConfig("", "org.onap.ccsdk.oran.a1policymanagementservice.clients.StdA1ClientVersion2"));
+        client = factoryUnderTest.createA1Client(ric).block();
+
+        assertEquals(client.getClass(), StdA1ClientVersion2.class);
+
+        ric = new Ric(
+                ricConfig("", "org.onap.ccsdk.oran.a1policymanagementservice.clients.StdA1ClientVersion2$Factory"));
+        client = factoryUnderTest.createA1Client(ric).block();
+
+        assertEquals(client.getClass(), StdA1ClientVersion2.class);
+
+        Exception e = Assertions.assertThrows(Exception.class, () -> {
+            factoryUnderTest.createClient(new Ric(ricConfig("", "junk")), A1ProtocolType.CUSTOM_PROTOCOL);
+        });
+        assertEquals("Could not find class: junk", e.getMessage());
+    }
+
     @Test
     void getProtocolVersion_error() throws ServiceException {
         whenGetProtocolVersionThrowException(clientMock1, clientMock2, clientMock3, clientMock4);
index eb00713..3090f64 100644 (file)
@@ -49,6 +49,7 @@ public class A1ClientHelper {
                 .baseUrl(url) //
                 .managedElementIds(new Vector<String>(Arrays.asList("kista_1", "kista_2"))) //
                 .controllerName("") //
+                .customAdapterClass("") //
                 .build();
         return new Ric(cfg);
     }
index 6aacc91..1337963 100644 (file)
@@ -71,6 +71,7 @@ class OscA1ClientTest {
                 .baseUrl("RicBaseUrl") //
                 .managedElementIds(new ArrayList<>()) //
                 .controllerName("") //
+                .customAdapterClass("") //
                 .build();
         asyncRestClientMock = mock(AsyncRestClient.class);
         clientUnderTest = new OscA1Client(ricConfig, asyncRestClientMock);
index de55882..4b6d17b 100644 (file)
@@ -74,6 +74,7 @@ class StdA1ClientV2Test {
                 .baseUrl(RIC_URL) //
                 .managedElementIds(new ArrayList<>()) //
                 .controllerName("") //
+                .customAdapterClass("") //
                 .build();
         asyncRestClientMock = mock(AsyncRestClient.class);
         clientUnderTest = new StdA1ClientVersion2(ricConfig, asyncRestClientMock);
index 7a07542..fd77eb8 100644 (file)
@@ -42,6 +42,7 @@ class ApplicationConfigTest {
             .baseUrl("ric1_url") //
             .managedElementIds(new Vector<>()) //
             .controllerName("") //
+            .customAdapterClass("") //
             .build();
 
     private static final ImmutableRicConfig RIC_CONFIG_2 = ImmutableRicConfig.builder() //
@@ -49,6 +50,7 @@ class ApplicationConfigTest {
             .baseUrl("ric1_url") //
             .managedElementIds(new Vector<>()) //
             .controllerName("") //
+            .customAdapterClass("") //
             .build();
 
     private static final ImmutableRicConfig RIC_CONFIG_3 = ImmutableRicConfig.builder() //
@@ -56,6 +58,7 @@ class ApplicationConfigTest {
             .baseUrl("ric1_url") //
             .managedElementIds(new Vector<>()) //
             .controllerName("") //
+            .customAdapterClass("") //
             .build();
 
     ConfigParserResult configParserResult(RicConfig... rics) {
@@ -103,6 +106,7 @@ class ApplicationConfigTest {
                 .baseUrl("changed_ric1_url") //
                 .managedElementIds(new Vector<>()) //
                 .controllerName("") //
+                .customAdapterClass("") //
                 .build();
 
         update = appConfigUnderTest.setConfiguration(configParserResult(changedRicConfig, RIC_CONFIG_2, RIC_CONFIG_3))
index 569cb57..174add3 100644 (file)
@@ -1046,6 +1046,7 @@ class ApplicationTest {
                 .baseUrl(ricId) //
                 .managedElementIds(mes) //
                 .controllerName("") //
+                .customAdapterClass("") //
                 .build();
     }
 
index f09bd05..fc56530 100644 (file)
@@ -82,6 +82,7 @@ class RefreshConfigTaskTest {
             .baseUrl("http://localhost:8080/") //
             .managedElementIds(new Vector<String>(Arrays.asList("kista_1", "kista_2"))) //
             .controllerName("") //
+            .customAdapterClass("") //
             .build();
 
     private RefreshConfigTask createTestObject(boolean configFileExists) {
index b76131d..320d1fb 100644 (file)
@@ -70,6 +70,7 @@ class RicSupervisionTest {
             .baseUrl("baseUrl1") //
             .managedElementIds(new Vector<String>(Arrays.asList("kista_1", "kista_2"))) //
             .controllerName("controllerName") //
+            .customAdapterClass("") //
             .build());
 
     private static final String POLICY_1_ID = "policyId1";
index a5795ac..e7e0a56 100644 (file)
@@ -110,6 +110,7 @@ class RicSynchronizationTaskTest {
                 .baseUrl("baseUrl1") //
                 .managedElementIds(Collections.emptyList()) //
                 .controllerName("controllerName") //
+                .customAdapterClass("") //
                 .build());
         policy1 = createPolicy("policyId1", false);
         policyTypes = new PolicyTypes(appConfig);
index 294b220..8cdf2cc 100644 (file)
@@ -75,6 +75,7 @@ class ServiceSupervisionTest {
             .baseUrl("baseUrl") //
             .managedElementIds(Collections.emptyList()) //
             .controllerName("") //
+            .customAdapterClass("") //
             .build();
     private Ric ric = new Ric(ricConfig);
     private PolicyType policyType = PolicyType.builder() //
index 959f53f..fef2382 100644 (file)
@@ -1,4 +1,4 @@
-{  
+{
    "config": {
       "description": "Application configuration",
       "ric": [
@@ -13,6 +13,7 @@
          {
             "name": "ric2",
             "baseUrl": "http://localhost:8081/",
+            "customAdapterClass": "org.onap.ccsdk.oran.a1policymanagementservice.clients.StdA1ClientVersion2$Factory",
             "managedElementIds": [
                "kista_3",
                "kista_4"
index b00720c..260dad2 100644 (file)
@@ -1,6 +1,6 @@
 {
    "config": {
-      "//description" : "Test",
+      "description": "Test",
       "controller": [
          {
             "name": "controller1",
@@ -22,6 +22,7 @@
          {
             "name": "ric2",
             "baseUrl": "http://localhost:8085/",
+            "customAdapterClass": "org.onap.ccsdk.oran.a1policymanagementservice.clients.StdA1ClientVersion2$Factory",
             "managedElementIds": [
                "kista_3",
                "kista_4"
@@ -53,4 +54,4 @@
          }
       }
    }
-}
+}
\ No newline at end of file