2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019 AT&T Intellectual Property. 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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.policy.pap.main.rest.depundep;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.List;
30 import java.util.regex.Pattern;
31 import java.util.stream.Collectors;
32 import org.apache.commons.lang3.tuple.Pair;
33 import org.onap.policy.models.base.PfModelException;
34 import org.onap.policy.models.pdp.concepts.PdpGroup;
35 import org.onap.policy.models.pdp.concepts.PdpGroupFilter;
36 import org.onap.policy.models.pdp.concepts.PdpStateChange;
37 import org.onap.policy.models.pdp.concepts.PdpUpdate;
38 import org.onap.policy.models.pdp.enums.PdpState;
39 import org.onap.policy.models.provider.PolicyModelsProvider;
40 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
41 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyFilter;
42 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyFilter.ToscaPolicyFilterBuilder;
43 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier;
44 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifierOptVersion;
45 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType;
46 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
47 import org.onap.policy.pap.main.notification.PolicyPdpNotificationData;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
52 * Data used during a single REST call when updating PDP policies.
54 public class SessionData {
55 private static final Logger logger = LoggerFactory.getLogger(SessionData.class);
58 * If a version string matches this, then it is just a prefix (i.e., major or
61 private static final Pattern VERSION_PREFIX_PAT = Pattern.compile("[^.]+(?:[.][^.]+)?");
66 private final PolicyModelsProvider dao;
69 * Maps a group name to its group data. This accumulates the set of groups to be
70 * created and updated when the REST call completes.
72 private final Map<String, GroupData> groupCache = new HashMap<>();
75 * Maps a policy type to the list of matching groups. Every group appearing within
76 * this map has a corresponding entry in {@link #groupCache}.
78 private final Map<ToscaPolicyTypeIdentifier, List<GroupData>> type2groups = new HashMap<>();
81 * Maps a PDP name to its most recently generated update and state-change requests.
83 private final Map<String, Pair<PdpUpdate, PdpStateChange>> pdpRequests = new HashMap<>();
86 * Maps a policy's identifier to the policy.
88 private final Map<ToscaPolicyIdentifierOptVersion, ToscaPolicy> policyCache = new HashMap<>();
91 * Maps a policy type's identifier to the policy.
93 private final Map<ToscaPolicyTypeIdentifier, ToscaPolicyType> typeCache = new HashMap<>();
96 * Policies to be deployed. This is just used to build up the data, which is then
97 * passed to the notifier once the update is "committed".
99 private final Map<ToscaPolicyIdentifier, PolicyPdpNotificationData> deploy = new HashMap<>();
102 * Policies to be undeployed. This is just used to build up the data, which is then
103 * passed to the notifier once the update is "committed".
105 private final Map<ToscaPolicyIdentifier, PolicyPdpNotificationData> undeploy = new HashMap<>();
109 * Constructs the object.
111 * @param dao DAO provider
113 public SessionData(PolicyModelsProvider dao) {
118 * Gets the policy type, referenced by an identifier. Loads it from the cache, if
119 * possible. Otherwise, gets it from the DB.
121 * @param desiredType policy type identifier
122 * @return the specified policy type
123 * @throws PfModelException if an error occurred
125 public ToscaPolicyType getPolicyType(ToscaPolicyTypeIdentifier desiredType) throws PfModelException {
127 ToscaPolicyType type = typeCache.get(desiredType);
130 List<ToscaPolicyType> lst = dao.getPolicyTypeList(desiredType.getName(), desiredType.getVersion());
136 typeCache.put(desiredType, type);
143 * Gets the policy, referenced by an identifier. Loads it from the cache, if possible.
144 * Otherwise, gets it from the DB.
146 * @param desiredPolicy policy identifier
147 * @return the specified policy
148 * @throws PfModelException if an error occurred
150 public ToscaPolicy getPolicy(ToscaPolicyIdentifierOptVersion desiredPolicy) throws PfModelException {
152 ToscaPolicy policy = policyCache.get(desiredPolicy);
153 if (policy == null) {
154 ToscaPolicyFilterBuilder filterBuilder = ToscaPolicyFilter.builder().name(desiredPolicy.getName());
155 setPolicyFilterVersion(filterBuilder, desiredPolicy.getVersion());
157 List<ToscaPolicy> lst = dao.getFilteredPolicyList(filterBuilder.build());
163 policyCache.put(desiredPolicy, policy);
166 // desired version may have only been a prefix - cache with full identifier, too
167 policyCache.putIfAbsent(new ToscaPolicyIdentifierOptVersion(policy.getIdentifier()), policy);
173 * Sets the "version" in a policy filter.
175 * @param filterBuilder filter builder whose version should be set
176 * @param desiredVersion desired version
178 private void setPolicyFilterVersion(ToscaPolicyFilterBuilder filterBuilder, String desiredVersion) {
180 if (desiredVersion == null) {
181 // no version specified - get the latest
182 filterBuilder.version(ToscaPolicyFilter.LATEST_VERSION);
184 } else if (isVersionPrefix(desiredVersion)) {
185 // version prefix provided - match the prefix and then pick the latest
186 filterBuilder.versionPrefix(desiredVersion + ".").version(ToscaPolicyFilter.LATEST_VERSION);
189 // must be an exact match
190 filterBuilder.version(desiredVersion);
195 * Determines if a version contains only a prefix.
197 * @param version version to inspect
198 * @return {@code true} if the version contains only a prefix, {@code false} if it is
201 public static boolean isVersionPrefix(String version) {
202 return VERSION_PREFIX_PAT.matcher(version).matches();
206 * Adds an update and state-change to the sets, replacing any previous entries for the
209 * @param update the update to be added
210 * @param change the state-change to be added
212 public void addRequests(PdpUpdate update, PdpStateChange change) {
213 if (!update.getName().equals(change.getName())) {
214 throw new IllegalArgumentException("PDP name mismatch " + update.getName() + ", " + change.getName());
217 logger.info("add update and state-change {} {} {} policies={}", update.getName(), update.getPdpGroup(),
218 update.getPdpSubgroup(), update.getPolicies().size());
219 pdpRequests.put(update.getName(), Pair.of(update, change));
223 * Adds an update to the set of updates, replacing any previous entry for the given
226 * @param update the update to be added
228 public void addUpdate(PdpUpdate update) {
229 logger.info("add update {} {} {} policies={}", update.getName(), update.getPdpGroup(), update.getPdpSubgroup(),
230 update.getPolicies().size());
231 pdpRequests.compute(update.getName(), (name, data) -> Pair.of(update, (data == null ? null : data.getRight())));
235 * Adds a state-change to the set of state-change requests, replacing any previous
236 * entry for the given PDP.
238 * @param change the state-change to be added
240 public void addStateChange(PdpStateChange change) {
241 logger.info("add state-change {}", change.getName());
242 pdpRequests.compute(change.getName(), (name, data) -> Pair.of((data == null ? null : data.getLeft()), change));
246 * Determines if any changes were made due to the REST call.
248 * @return {@code true} if nothing was changed, {@code false} if something was changed
250 public boolean isUnchanged() {
251 return groupCache.values().stream().allMatch(GroupData::isUnchanged);
255 * Gets the accumulated PDP requests.
257 * @return the PDP requests
259 public Collection<Pair<PdpUpdate, PdpStateChange>> getPdpRequests() {
260 return pdpRequests.values();
264 * Gets the accumulated PDP update requests.
266 * @return the PDP requests
268 public List<PdpUpdate> getPdpUpdates() {
269 return pdpRequests.values().stream().filter(req -> req.getLeft() != null).map(Pair::getLeft)
270 .collect(Collectors.toList());
274 * Gets the accumulated PDP state-change requests.
276 * @return the PDP requests
278 public List<PdpStateChange> getPdpStateChanges() {
279 return pdpRequests.values().stream().filter(req -> req.getRight() != null).map(Pair::getRight)
280 .collect(Collectors.toList());
286 * @param newGroup the new group
288 public void create(PdpGroup newGroup) {
289 String name = newGroup.getName();
291 if (groupCache.put(name, new GroupData(newGroup, true)) != null) {
292 throw new IllegalStateException("group already cached: " + name);
295 logger.info("create cached group {}", newGroup.getName());
301 * @param newGroup the updated group
303 public void update(PdpGroup newGroup) {
304 String name = newGroup.getName();
305 GroupData data = groupCache.get(name);
307 throw new IllegalStateException("group not cached: " + name);
310 logger.info("update cached group {}", newGroup.getName());
311 data.update(newGroup);
315 * Gets the group by the given name.
317 * @param name name of the group to get
318 * @return the group, or {@code null} if it does not exist
319 * @throws PfModelException if an error occurred
321 public PdpGroup getGroup(String name) throws PfModelException {
323 GroupData data = groupCache.get(name);
325 List<PdpGroup> lst = dao.getPdpGroups(name);
327 logger.info("unknown group {}", name);
331 logger.info("cache group {}", name);
332 data = new GroupData(lst.get(0));
333 groupCache.put(name, data);
336 logger.info("use cached group {}", name);
339 return data.getGroup();
343 * Gets the active groups supporting the given policy.
345 * @param type desired policy type
346 * @return the active groups supporting the given policy
347 * @throws PfModelException if an error occurred
349 public List<PdpGroup> getActivePdpGroupsByPolicyType(ToscaPolicyTypeIdentifier type) throws PfModelException {
350 List<GroupData> data = type2groups.get(type);
352 PdpGroupFilter filter = PdpGroupFilter.builder().policyTypeList(Collections.singletonList(type))
353 .groupState(PdpState.ACTIVE).build();
355 List<PdpGroup> groups = dao.getFilteredPdpGroups(filter);
357 data = groups.stream().map(this::addGroup).collect(Collectors.toList());
358 type2groups.put(type, data);
361 return data.stream().map(GroupData::getGroup).collect(Collectors.toList());
365 * Adds a group to the group cache, if it isn't already in the cache.
367 * @param group the group to be added
368 * @return the cache entry
370 private GroupData addGroup(PdpGroup group) {
371 GroupData data = groupCache.get(group.getName());
373 logger.info("cache group {}", group.getName());
374 data = new GroupData(group);
375 groupCache.put(group.getName(), data);
378 logger.info("use cached group {}", group.getName());
385 * Update the DB with the changes.
387 * @throws PfModelException if an error occurred
389 public void updateDb() throws PfModelException {
391 List<GroupData> created = groupCache.values().stream().filter(GroupData::isNew).collect(Collectors.toList());
392 if (!created.isEmpty()) {
393 if (logger.isInfoEnabled()) {
394 created.forEach(group -> logger.info("creating DB group {}", group.getGroup().getName()));
396 dao.createPdpGroups(created.stream().map(GroupData::getGroup).collect(Collectors.toList()));
399 // update existing groups
400 List<GroupData> updated =
401 groupCache.values().stream().filter(GroupData::isUpdated).collect(Collectors.toList());
402 if (!updated.isEmpty()) {
403 if (logger.isInfoEnabled()) {
404 updated.forEach(group -> logger.info("updating DB group {}", group.getGroup().getName()));
406 dao.updatePdpGroups(updated.stream().map(GroupData::getGroup).collect(Collectors.toList()));
411 * Deletes a group from the DB, immediately (i.e., without caching the request to be
414 * @param group the group to be deleted
415 * @throws PfModelException if an error occurred
417 public void deleteGroupFromDb(PdpGroup group) throws PfModelException {
418 logger.info("deleting DB group {}", group.getName());
419 dao.deletePdpGroup(group.getName());
423 * Adds policy deployment data.
425 * @param policyId ID of the policy being deployed
426 * @param pdps PDPs to which the policy is being deployed
427 * @throws PfModelException if an error occurred
429 protected void trackDeploy(ToscaPolicyIdentifier policyId, Collection<String> pdps) throws PfModelException {
430 trackDeploy(policyId, new HashSet<>(pdps));
434 * Adds policy deployment data.
436 * @param policyId ID of the policy being deployed
437 * @param pdps PDPs to which the policy is being deployed
438 * @throws PfModelException if an error occurred
440 protected void trackDeploy(ToscaPolicyIdentifier policyId, Set<String> pdps) throws PfModelException {
441 addData(policyId, pdps, deploy, undeploy);
445 * Adds policy undeployment data.
447 * @param policyId ID of the policy being undeployed
448 * @param pdps PDPs to which the policy is being undeployed
449 * @throws PfModelException if an error occurred
451 protected void trackUndeploy(ToscaPolicyIdentifier policyId, Collection<String> pdps) throws PfModelException {
452 trackUndeploy(policyId, new HashSet<>(pdps));
456 * Adds policy undeployment data.
458 * @param policyId ID of the policy being undeployed
459 * @param pdps PDPs to which the policy is being undeployed
460 * @throws PfModelException if an error occurred
462 protected void trackUndeploy(ToscaPolicyIdentifier policyId, Set<String> pdps) throws PfModelException {
463 addData(policyId, pdps, undeploy, deploy);
467 * Adds policy deployment/undeployment data.
469 * @param policyId ID of the policy being deployed/undeployed
470 * @param pdps PDPs to which the policy is being deployed/undeployed
471 * @param addMap map to which it should be added
472 * @param removeMap map from which it should be removed
473 * @throws PfModelException if an error occurred
475 private void addData(ToscaPolicyIdentifier policyId, Set<String> pdps,
476 Map<ToscaPolicyIdentifier, PolicyPdpNotificationData> addMap,
477 Map<ToscaPolicyIdentifier, PolicyPdpNotificationData> removeMap) throws PfModelException {
479 PolicyPdpNotificationData removeData = removeMap.get(policyId);
480 if (removeData != null) {
481 removeData.removeAll(pdps);
484 ToscaPolicyIdentifierOptVersion optid = new ToscaPolicyIdentifierOptVersion(policyId);
485 ToscaPolicyTypeIdentifier policyType = getPolicy(optid).getTypeIdentifier();
487 addMap.computeIfAbsent(policyId, key -> new PolicyPdpNotificationData(policyId, policyType)).addAll(pdps);
491 * Gets the policies to be deployed.
493 * @return the policies to be deployed
495 public Collection<PolicyPdpNotificationData> getDeployData() {
496 return deploy.values();
500 * Gets the policies to be undeployed.
502 * @return the policies to be undeployed
504 public Collection<PolicyPdpNotificationData> getUndeployData() {
505 return undeploy.values();