6c178d3694ad023aa14a5e05e7e171317736f99b
[ccsdk/oran.git] /
1 /*-
2  * ========================LICENSE_START=================================
3  * ONAP : ccsdk oran
4  * ======================================================================
5  * Copyright (C) 2019-2020 Nordix Foundation. All rights reserved.
6  * ======================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ========================LICENSE_END===================================
19  */
20
21 package org.onap.ccsdk.oran.a1policymanagementservice.configuration;
22
23 import com.google.common.io.CharStreams;
24 import com.google.gson.JsonArray;
25 import com.google.gson.JsonElement;
26 import com.google.gson.JsonObject;
27
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.nio.charset.StandardCharsets;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Map.Entry;
40 import java.util.Set;
41
42 import lombok.Builder;
43 import lombok.Getter;
44
45 import org.json.JSONObject;
46 import org.onap.ccsdk.oran.a1policymanagementservice.exceptions.ServiceException;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49 import org.springframework.http.HttpStatus;
50
51 /**
52  * Parser for the Json representing of the component configuration.
53  */
54 public class ApplicationConfigParser {
55     private static final Logger logger = LoggerFactory.getLogger(ApplicationConfigParser.class);
56
57     private static final String CONFIG = "config";
58     private static final String CONTROLLER = "controller";
59     private final ApplicationConfig applicationConfig;
60
61     public ApplicationConfigParser(ApplicationConfig applicationConfig) {
62         this.applicationConfig = applicationConfig;
63     }
64
65     @Builder
66     @Getter
67     public static class ConfigParserResult {
68         private List<RicConfig> ricConfigs;
69
70         @Builder.Default
71         private Map<String, ControllerConfig> controllerConfigs = new HashMap<>();
72
73         @Builder.Default
74         private String dmaapConsumerTopicUrl = "";
75
76         @Builder.Default
77         private String dmaapProducerTopicUrl = "";
78
79     }
80
81     public ConfigParserResult parse(JsonObject root) throws ServiceException {
82
83         validateJsonObjectAgainstSchema(root);
84
85         String dmaapProducerTopicUrl = "";
86         String dmaapConsumerTopicUrl = "";
87
88         JsonObject pmsConfigJson = root.getAsJsonObject(CONFIG);
89
90         if (pmsConfigJson == null) {
91             throw new ServiceException("Missing root configuration \"" + CONFIG + "\" in JSON: " + root);
92         }
93
94         JsonObject json = pmsConfigJson.getAsJsonObject("streams_publishes");
95         if (json != null) {
96             dmaapProducerTopicUrl = parseDmaapConfig(json);
97         }
98
99         json = pmsConfigJson.getAsJsonObject("streams_subscribes");
100         if (json != null) {
101             dmaapConsumerTopicUrl = parseDmaapConfig(json);
102         }
103
104         List<RicConfig> ricConfigs = parseRics(pmsConfigJson);
105         Map<String, ControllerConfig> controllerConfigs = parseControllerConfigs(pmsConfigJson);
106         checkConfigurationConsistency(ricConfigs, controllerConfigs);
107
108         return ConfigParserResult.builder() //
109                 .dmaapConsumerTopicUrl(dmaapConsumerTopicUrl) //
110                 .dmaapProducerTopicUrl(dmaapProducerTopicUrl) //
111                 .ricConfigs(ricConfigs) //
112                 .controllerConfigs(controllerConfigs) //
113                 .build();
114     }
115
116     private void validateJsonObjectAgainstSchema(Object object) throws ServiceException {
117         if (applicationConfig.getConfigurationFileSchemaPath() == null
118                 || applicationConfig.getConfigurationFileSchemaPath().isEmpty()) {
119             return;
120         }
121
122         try {
123             String schemaAsString = readSchemaFile();
124
125             JSONObject schemaJSON = new JSONObject(schemaAsString);
126             var schema = org.everit.json.schema.loader.SchemaLoader.load(schemaJSON);
127
128             String objectAsString = object.toString();
129             JSONObject json = new JSONObject(objectAsString);
130             schema.validate(json);
131         } catch (Exception e) {
132             throw new ServiceException("Json schema validation failure: " + e.toString());
133         }
134     }
135
136     private String readSchemaFile() throws IOException, ServiceException {
137         String filePath = applicationConfig.getConfigurationFileSchemaPath();
138         InputStream in = getClass().getResourceAsStream(filePath);
139         logger.debug("Reading application schema file from: {} with: {}", filePath, in);
140         if (in == null) {
141             throw new ServiceException("Could not read application configuration schema file: " + filePath,
142                     HttpStatus.INTERNAL_SERVER_ERROR);
143         }
144         return CharStreams.toString(new InputStreamReader(in, StandardCharsets.UTF_8));
145     }
146
147     private void checkConfigurationConsistency(List<RicConfig> ricConfigs,
148             Map<String, ControllerConfig> controllerConfigs) throws ServiceException {
149         Set<String> ricUrls = new HashSet<>();
150         Set<String> ricNames = new HashSet<>();
151         for (RicConfig ric : ricConfigs) {
152             if (!ricUrls.add(ric.getBaseUrl())) {
153                 throw new ServiceException("Configuration error, more than one RIC URL: " + ric.getBaseUrl());
154             }
155             if (!ricNames.add(ric.getRicId())) {
156                 throw new ServiceException("Configuration error, more than one RIC with name: " + ric.getRicId());
157             }
158             if (!ric.getControllerName().isEmpty() && controllerConfigs.get(ric.getControllerName()) == null) {
159                 throw new ServiceException(
160                         "Configuration error, controller configuration not found: " + ric.getControllerName());
161             }
162         }
163     }
164
165     private List<RicConfig> parseRics(JsonObject config) throws ServiceException {
166         List<RicConfig> result = new ArrayList<>();
167         for (JsonElement ricElem : getAsJsonArray(config, "ric")) {
168             JsonObject ricJsonObj = ricElem.getAsJsonObject();
169             RicConfig ricConfig = RicConfig.builder() //
170                     .ricId(get(ricJsonObj, "name", "id", "ricId").getAsString()) //
171                     .baseUrl(get(ricJsonObj, "baseUrl").getAsString()) //
172                     .managedElementIds(parseManagedElementIds(get(ricJsonObj, "managedElementIds").getAsJsonArray())) //
173                     .controllerName(getString(ricJsonObj, CONTROLLER, ""))
174                     .customAdapterClass(getString(ricJsonObj, "customAdapterClass", "")) //
175                     .build();
176             if (!ricConfig.getBaseUrl().isEmpty()) {
177                 result.add(ricConfig);
178             } else {
179                 logger.error("RIC configuration error {}, baseUrl is empty", ricConfig.getRicId());
180             }
181         }
182         return result;
183     }
184
185     String getString(JsonObject obj, String name, String defaultValue) {
186         JsonElement elem = obj.get(name);
187         if (elem != null) {
188             return elem.getAsString();
189         }
190         return defaultValue;
191     }
192
193     Map<String, ControllerConfig> parseControllerConfigs(JsonObject config) throws ServiceException {
194         if (config.get(CONTROLLER) == null) {
195             return new HashMap<>();
196         }
197         Map<String, ControllerConfig> result = new HashMap<>();
198         for (JsonElement element : getAsJsonArray(config, CONTROLLER)) {
199             JsonObject controllerAsJson = element.getAsJsonObject();
200             ControllerConfig controllerConfig = ControllerConfig.builder() //
201                     .name(get(controllerAsJson, "name").getAsString()) //
202                     .baseUrl(get(controllerAsJson, "baseUrl").getAsString()) //
203                     .password(get(controllerAsJson, "password").getAsString()) //
204                     .userName(get(controllerAsJson, "userName").getAsString()) // )
205                     .build();
206
207             if (result.put(controllerConfig.getName(), controllerConfig) != null) {
208                 throw new ServiceException(
209                         "Configuration error, more than one controller with name: " + controllerConfig.getName());
210             }
211         }
212         return result;
213     }
214
215     private List<String> parseManagedElementIds(JsonArray asJsonObject) {
216         Iterator<JsonElement> iterator = asJsonObject.iterator();
217         List<String> managedElementIds = new ArrayList<>();
218         while (iterator.hasNext()) {
219             managedElementIds.add(iterator.next().getAsString());
220
221         }
222         return managedElementIds;
223     }
224
225     private static JsonElement get(JsonObject obj, String... alternativeMemberNames) throws ServiceException {
226         for (String memberName : alternativeMemberNames) {
227             JsonElement elem = obj.get(memberName);
228             if (elem != null) {
229                 return elem;
230             }
231         }
232         throw new ServiceException("Could not find member: " + Arrays.toString(alternativeMemberNames) + " in: " + obj);
233     }
234
235     private JsonArray getAsJsonArray(JsonObject obj, String memberName) throws ServiceException {
236         return get(obj, memberName).getAsJsonArray();
237     }
238
239     private String parseDmaapConfig(JsonObject streamCfg) throws ServiceException {
240         Set<Entry<String, JsonElement>> streamConfigEntries = streamCfg.entrySet();
241         if (streamConfigEntries.size() != 1) {
242             throw new ServiceException(
243                     "Invalid configuration. Number of streams must be one, config: " + streamConfigEntries);
244         }
245         JsonObject streamConfigEntry = streamConfigEntries.iterator().next().getValue().getAsJsonObject();
246         JsonObject dmaapInfo = get(streamConfigEntry, "dmaap_info").getAsJsonObject();
247         return getAsString(dmaapInfo, "topic_url");
248     }
249
250     private static String getAsString(JsonObject obj, String memberName) throws ServiceException {
251         return get(obj, memberName).getAsString();
252     }
253 }