2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019-2022 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2019-2021, 2023 Nordix Foundation.
7 * Modifications Copyright (C) 2022 Bell Canada. All rights reserved.
8 * ================================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
21 * SPDX-License-Identifier: Apache-2.0
22 * ============LICENSE_END=========================================================
25 package org.onap.policy.api.main.startstop;
27 import com.google.common.collect.Sets;
28 import jakarta.annotation.PostConstruct;
29 import java.util.ArrayList;
30 import java.util.LinkedHashMap;
31 import java.util.LinkedList;
32 import java.util.List;
34 import java.util.Objects;
35 import lombok.RequiredArgsConstructor;
36 import org.onap.policy.api.main.config.PolicyPreloadConfig;
37 import org.onap.policy.api.main.exception.PolicyApiException;
38 import org.onap.policy.api.main.service.ToscaServiceTemplateService;
39 import org.onap.policy.common.utils.coder.CoderException;
40 import org.onap.policy.common.utils.coder.StandardYamlCoder;
41 import org.onap.policy.common.utils.resources.ResourceUtils;
42 import org.onap.policy.models.base.PfModelException;
43 import org.onap.policy.models.base.PfModelRuntimeException;
44 import org.onap.policy.models.tosca.authorative.concepts.ToscaEntity;
45 import org.onap.policy.models.tosca.authorative.concepts.ToscaEntityFilter;
46 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType;
47 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
48 import org.onap.policy.models.tosca.authorative.concepts.ToscaTopologyTemplate;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
52 import org.springframework.stereotype.Component;
55 * This class creates initial policy types in the database.
57 * @author Chenfei Gao (cgao@research.att.com)
60 @ConditionalOnProperty(value = "database.initialize", havingValue = "true", matchIfMissing = true)
61 @RequiredArgsConstructor
62 public class ApiDatabaseInitializer {
64 private static final Logger LOGGER = LoggerFactory.getLogger(ApiDatabaseInitializer.class);
65 private static final StandardYamlCoder coder = new StandardYamlCoder();
67 private final ToscaServiceTemplateService toscaServiceTemplateService;
68 private final PolicyPreloadConfig policyPreloadConfig;
71 public void loadData() throws PolicyApiException {
72 initializeApiDatabase(policyPreloadConfig.getPolicyTypes(), policyPreloadConfig.getPolicies());
76 * Initializes database by preloading policy types and policies.
78 * @param policyTypes List of policy types to preload.
79 * @param policies List of policies to preload.
80 * @throws PolicyApiException in case of errors.
82 public void initializeApiDatabase(final List<String> policyTypes, final List<String> policies)
83 throws PolicyApiException {
85 if (alreadyExists()) {
86 LOGGER.warn("DB already contains policy data - skipping preload");
90 var serviceTemplate = new ToscaServiceTemplate();
91 serviceTemplate.setDataTypes(new LinkedHashMap<>());
92 serviceTemplate.setPolicyTypes(new LinkedHashMap<>());
93 serviceTemplate.setToscaDefinitionsVersion("tosca_simple_yaml_1_1_0");
95 ToscaServiceTemplate createdPolicyTypes =
96 preloadServiceTemplate(serviceTemplate, policyTypes, toscaServiceTemplateService::createPolicyType);
97 preloadServiceTemplate(createdPolicyTypes, policies, toscaServiceTemplateService::createPolicies);
98 } catch (final PolicyApiException | PfModelException | CoderException exp) {
99 throw new PolicyApiException(exp);
103 private boolean alreadyExists() throws PfModelException {
105 ToscaServiceTemplate serviceTemplate = toscaServiceTemplateService
106 .getFilteredPolicyTypes(ToscaEntityFilter.<ToscaPolicyType>builder().build());
107 if (!serviceTemplate.getPolicyTypes().isEmpty()) {
110 } catch (PfModelRuntimeException e) {
111 LOGGER.trace("DB does not yet contain policy types", e);
116 private ToscaServiceTemplate preloadServiceTemplate(ToscaServiceTemplate serviceTemplate, List<String> entities,
117 FunctionWithEx<ToscaServiceTemplate, ToscaServiceTemplate> getter)
118 throws PolicyApiException, CoderException, PfModelException {
120 var multiVersionTemplates = new ArrayList<ToscaServiceTemplate>();
122 for (String entity : entities) {
123 ToscaServiceTemplate singleEntity = deserializeServiceTemplate(entity);
125 if (isMultiVersion(serviceTemplate.getPolicyTypes(), singleEntity.getPolicyTypes())) {
126 // if this entity introduces a new policy version of an existing policy type,
127 // process it on its own as continuing here will override the existing policy type
128 // in a different version
130 multiVersionTemplates.add(singleEntity);
131 LOGGER.warn("Detected multi-versioned type: {}", entity);
135 // Consolidate data types and policy types
136 if (singleEntity.getDataTypes() != null) {
137 serviceTemplate.getDataTypes().putAll(singleEntity.getDataTypes());
139 if (singleEntity.getPolicyTypes() != null) {
140 serviceTemplate.getPolicyTypes().putAll(singleEntity.getPolicyTypes());
143 // Consolidate policies
144 var topologyTemplate = singleEntity.getToscaTopologyTemplate();
145 if (topologyTemplate != null && topologyTemplate.getPolicies() != null) {
146 serviceTemplate.setToscaTopologyTemplate(new ToscaTopologyTemplate());
147 serviceTemplate.getToscaTopologyTemplate().setPolicies(new LinkedList<>());
148 serviceTemplate.getToscaTopologyTemplate().getPolicies()
149 .addAll(singleEntity.getToscaTopologyTemplate().getPolicies());
152 // Preload the specified entities
153 ToscaServiceTemplate createdServiceTemplate = getter.apply(serviceTemplate);
154 LOGGER.debug("Created initial tosca service template in DB - {}", createdServiceTemplate);
156 multiVersionTemplates
157 .forEach(mvServiceTemplate -> {
159 LOGGER.info("Multi-versioned Service Template {}", mvServiceTemplate.getPolicyTypes().keySet());
160 getter.apply(mvServiceTemplate);
161 } catch (PfModelException e) {
162 LOGGER.warn("ToscaServiceTemple cannot be preloaded: {}", mvServiceTemplate, e);
165 return createdServiceTemplate;
168 private ToscaServiceTemplate deserializeServiceTemplate(String entity) throws PolicyApiException, CoderException {
169 var entityAsStringYaml = ResourceUtils.getResourceAsString(entity);
170 if (entityAsStringYaml == null) {
171 throw new PolicyApiException("Preloaded entity cannot be found " + entity);
174 ToscaServiceTemplate singleEntity = coder.decode(entityAsStringYaml, ToscaServiceTemplate.class);
175 if (singleEntity == null) {
176 throw new PolicyApiException("Error deserializing entity from file: " + entity);
181 // This method is templated, so it can be used with other derivations of ToscaEntity in the future,
182 // if multi-version are desired.
184 protected <T extends ToscaEntity> boolean isMultiVersion(Map<String, T> aggEntity,
185 Map<String, T> singleEntity) {
186 if (aggEntity == null || singleEntity == null) {
190 // There is a multi-version entity if both key sets have the same
191 // entity name but different version.
194 Sets.intersection(aggEntity.keySet(), singleEntity.keySet())
196 .anyMatch(e -> !Objects.equals(aggEntity.get(e).getVersion(), singleEntity.get(e).getVersion()));
200 protected interface FunctionWithEx<T, R> {
201 R apply(T value) throws PfModelException;