SLAs for async methods
[policy/pap.git] / main / src / main / java / org / onap / policy / pap / main / rest / ProviderBase.java
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP PAP
4  * ================================================================================
5  * Copyright (C) 2019, 2021 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2020-2022 Nordix Foundation.
7  * Modifications Copyright (C) 2020,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
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
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.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.policy.pap.main.rest;
24
25 import java.util.Collection;
26 import javax.ws.rs.core.Response.Status;
27 import org.onap.policy.common.utils.services.Registry;
28 import org.onap.policy.models.base.PfModelException;
29 import org.onap.policy.models.base.PfModelRuntimeException;
30 import org.onap.policy.models.pap.concepts.PolicyNotification;
31 import org.onap.policy.models.pdp.concepts.Pdp;
32 import org.onap.policy.models.pdp.concepts.PdpGroup;
33 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
34 import org.onap.policy.models.pdp.concepts.PdpUpdate;
35 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
36 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifierOptVersion;
37 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
38 import org.onap.policy.pap.main.PapConstants;
39 import org.onap.policy.pap.main.comm.PdpModifyRequestMap;
40 import org.onap.policy.pap.main.notification.PolicyNotifier;
41 import org.onap.policy.pap.main.service.PdpGroupService;
42 import org.onap.policy.pap.main.service.PolicyAuditService;
43 import org.onap.policy.pap.main.service.PolicyStatusService;
44 import org.onap.policy.pap.main.service.ToscaServiceTemplateService;
45 import org.springframework.beans.factory.annotation.Autowired;
46 import org.springframework.boot.context.event.ApplicationReadyEvent;
47 import org.springframework.context.event.EventListener;
48
49 /**
50  * Super class of providers that deploy and undeploy PDP groups. The following items must
51  * be in the {@link Registry}:
52  * <ul>
53  * <li>PDP Modification Lock</li>
54  * <li>PDP Modify Request Map</li>
55  * <li>PAP DAO Factory</li>
56  * </ul>
57  */
58 public abstract class ProviderBase {
59     public static final String DEFAULT_USER = "PAP";
60
61     /**
62      * Lock used when updating PDPs.
63      */
64     private Object updateLock;
65
66     /**
67      * Used to send UPDATE and STATE-CHANGE requests to the PDPs.
68      */
69     private PdpModifyRequestMap requestMap;
70
71     /**
72      * Generates policy notifications based on responses from PDPs.
73      */
74     private PolicyNotifier notifier;
75
76     private ToscaServiceTemplateService toscaService;
77
78     private PdpGroupService pdpGroupService;
79
80     private PolicyStatusService policyStatusService;
81
82     private PolicyAuditService policyAuditService;
83
84     /**
85      * The setter method for injecting into Spring context.
86      *
87      * @param toscaService the toscaService to set
88      */
89     @Autowired
90     public final void setToscaService(ToscaServiceTemplateService toscaService) {
91         this.toscaService = toscaService;
92     }
93
94     /**
95      * The setter method for injecting into Spring context.
96      *
97      * @param pdpGroupService the pdpGroupService to set
98      */
99     @Autowired
100     public final void setPdpGroupService(PdpGroupService pdpGroupService) {
101         this.pdpGroupService = pdpGroupService;
102     }
103
104     /**
105      * The setter method for injecting into Spring context.
106      *
107      * @param policyStatusService the policyStatusService to set
108      */
109     @Autowired
110     public final void setPolicyStatusService(PolicyStatusService policyStatusService) {
111         this.policyStatusService = policyStatusService;
112     }
113
114     /**
115      * The setter method for injecting into Spring context.
116      *
117      * @param policyAuditService the policyAuditService to set
118      */
119     @Autowired
120     public final void setPolicyAuditService(PolicyAuditService policyAuditService) {
121         this.policyAuditService = policyAuditService;
122     }
123
124     /**
125      * The setter method for injecting into Spring context.
126      *
127      * @param policyNotifier the policyNotifier to set
128      */
129     @Autowired
130     public final void setPolicyNotifier(PolicyNotifier policyNotifier) {
131         this.notifier = policyNotifier;
132     }
133
134     /**
135      * Initializes the parameters..
136      */
137     @EventListener(ApplicationReadyEvent.class)
138     public void initialize() {
139         this.updateLock = Registry.get(PapConstants.REG_PDP_MODIFY_LOCK, Object.class);
140         this.requestMap = Registry.get(PapConstants.REG_PDP_MODIFY_MAP, PdpModifyRequestMap.class);
141     }
142
143     /**
144      * Processes a policy request.
145      *
146      * @param user user triggering request
147      * @param request PDP policy request
148      * @param processor function that processes the request
149      * @throws PfModelException if an error occurred
150      */
151     protected <T> void process(String user, T request, BiConsumerWithEx<SessionData, T> processor)
152             throws PfModelException {
153
154         synchronized (updateLock) {
155             SessionData data;
156             var notif = new PolicyNotification();
157
158             try {
159
160                 data = new SessionData(user, toscaService, pdpGroupService, policyStatusService, policyAuditService);
161                 processor.accept(data, request);
162
163                 // make all of the DB updates
164                 data.updateDb(notif);
165
166             } catch (PfModelRuntimeException e) {
167                 throw e;
168
169             } catch (RuntimeException e) {
170                 throw new PfModelException(Status.INTERNAL_SERVER_ERROR, "request failed", e);
171             }
172
173             // publish the requests
174             data.getPdpRequests().forEach(pair -> requestMap.addRequest(pair.getLeft(), pair.getRight()));
175
176             // publish the notifications
177             notifier.publish(notif);
178         }
179     }
180
181     /**
182      * Processes a policy request.
183      *
184      * @param request PDP policy request
185      * @param processor function that processes the request
186      * @throws PfModelException if an error occurred
187      */
188     protected <T> void process(T request, BiConsumerWithEx<SessionData, T> processor) throws PfModelException {
189         this.process(DEFAULT_USER, request, processor);
190     }
191
192     /**
193      * Process a single policy from the request.
194      *
195      * @param data session data
196      * @param desiredPolicy request policy
197      * @throws PfModelException if an error occurred
198      */
199     protected void processPolicy(SessionData data, ToscaConceptIdentifierOptVersion desiredPolicy)
200             throws PfModelException {
201
202         ToscaPolicy policy = getPolicy(data, desiredPolicy);
203
204         Collection<PdpGroup> groups = getGroups(data, policy.getTypeIdentifier());
205         if (groups.isEmpty()) {
206             throw new PfModelException(Status.BAD_REQUEST, "policy not supported by any PDP group: "
207                     + desiredPolicy.getName() + " " + desiredPolicy.getVersion());
208         }
209
210         var updater = makeUpdater(data, policy, desiredPolicy);
211
212         for (PdpGroup group : groups) {
213             upgradeGroup(data, group, updater);
214         }
215     }
216
217     /**
218      * Makes a function to update a subgroup. The function is expected to return
219      * {@code true} if the subgroup was updated, {@code false} if no update was
220      * necessary/appropriate.
221      *
222      * @param data session data
223      * @param policy policy to be added to or removed from each subgroup
224      * @param desiredPolicy request policy
225      * @return a function to update a subgroup
226      */
227     protected abstract Updater makeUpdater(SessionData data, ToscaPolicy policy,
228             ToscaConceptIdentifierOptVersion desiredPolicy);
229
230     /**
231      * Finds the active PDP group(s) that supports the given policy type.
232      *
233      * @param data session data
234      * @param policyType the policy type of interest
235      * @return the matching PDP group, or {@code null} if no active group supports the
236      *         given PDP types
237      */
238     private Collection<PdpGroup> getGroups(SessionData data, ToscaConceptIdentifier policyType) {
239         return data.getActivePdpGroupsByPolicyType(policyType);
240     }
241
242     /**
243      * Updates a group, assigning a new version number, if it actually changes.
244      *
245      * @param data session data
246      * @param group the original group, to be updated
247      * @param updater function to update a group
248      * @throws PfModelException if an error occurred
249      */
250     private void upgradeGroup(SessionData data, PdpGroup group, Updater updater) throws PfModelException {
251
252         var updated = false;
253
254         for (PdpSubGroup subgroup : group.getPdpSubgroups()) {
255
256             if (!updater.apply(group, subgroup)) {
257                 continue;
258             }
259
260             updated = true;
261
262             makeUpdates(data, group, subgroup);
263         }
264
265         if (updated) {
266             // something changed
267             data.update(group);
268         }
269     }
270
271     /**
272      * Makes UPDATE messages for each PDP in a subgroup.
273      *
274      * @param data session data
275      * @param group group containing the subgroup
276      * @param subgroup subgroup whose PDPs should receive messages
277      */
278     protected void makeUpdates(SessionData data, PdpGroup group, PdpSubGroup subgroup) {
279         for (Pdp pdp : subgroup.getPdpInstances()) {
280             data.addUpdate(makeUpdate(data, group, subgroup, pdp));
281         }
282     }
283
284     /**
285      * Makes an UPDATE message for a particular PDP.
286      *
287      * @param data session data
288      * @param group group to which the PDP should belong
289      * @param subgroup subgroup to which the PDP should belong
290      * @param pdp the PDP of interest
291      * @return a new UPDATE message
292      */
293     private PdpUpdate makeUpdate(SessionData data, PdpGroup group, PdpSubGroup subgroup, Pdp pdp) {
294
295         var update = new PdpUpdate();
296
297         update.setSource(PapConstants.PAP_NAME);
298         update.setName(pdp.getInstanceId());
299         update.setDescription(group.getDescription());
300         update.setPdpGroup(group.getName());
301         update.setPdpSubgroup(subgroup.getPdpType());
302         update.setPoliciesToBeDeployed(data.getPoliciesToBeDeployed());
303         update.setPoliciesToBeUndeployed(data.getPoliciesToBeUndeployed());
304
305         return update;
306     }
307
308     /**
309      * Gets the specified policy.
310      *
311      * @param data session data
312      * @param ident policy identifier, with an optional version
313      * @return the policy of interest
314      * @throws PfModelRuntimeException if an error occurred or the policy was not found
315      */
316     private ToscaPolicy getPolicy(SessionData data, ToscaConceptIdentifierOptVersion ident) {
317         try {
318             ToscaPolicy policy = data.getPolicy(ident);
319             if (policy == null) {
320                 throw new PfModelRuntimeException(Status.NOT_FOUND,
321                         "cannot find policy: " + ident.getName() + " " + ident.getVersion());
322             }
323
324             return policy;
325
326         } catch (PfModelException e) {
327             throw new PfModelRuntimeException(e.getErrorResponse().getResponseCode(),
328                     e.getErrorResponse().getErrorMessage(), e);
329         }
330     }
331
332     @FunctionalInterface
333     public interface BiConsumerWithEx<F, S> {
334         /**
335          * Performs this operation on the given arguments.
336          *
337          * @param firstArg the first input argument
338          * @param secondArg the second input argument
339          * @throws PfModelException if an error occurred
340          */
341         void accept(F firstArg, S secondArg) throws PfModelException;
342     }
343
344     @FunctionalInterface
345     public interface Updater {
346         boolean apply(PdpGroup group, PdpSubGroup subgroup) throws PfModelException;
347     }
348 }