Make PAP stateless
[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-2021 Nordix Foundation.
7  * Modifications Copyright (C) 2020 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 java.util.stream.Collectors;
27 import javax.ws.rs.core.Response.Status;
28 import org.onap.policy.common.utils.services.Registry;
29 import org.onap.policy.models.base.PfModelException;
30 import org.onap.policy.models.base.PfModelRuntimeException;
31 import org.onap.policy.models.pap.concepts.PolicyNotification;
32 import org.onap.policy.models.pdp.concepts.Pdp;
33 import org.onap.policy.models.pdp.concepts.PdpGroup;
34 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
35 import org.onap.policy.models.pdp.concepts.PdpUpdate;
36 import org.onap.policy.models.provider.PolicyModelsProvider;
37 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
38 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifierOptVersion;
39 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
40 import org.onap.policy.pap.main.PapConstants;
41 import org.onap.policy.pap.main.PolicyModelsProviderFactoryWrapper;
42 import org.onap.policy.pap.main.comm.PdpModifyRequestMap;
43 import org.onap.policy.pap.main.notification.PolicyNotifier;
44
45 /**
46  * Super class of providers that deploy and undeploy PDP groups. The following items must
47  * be in the {@link Registry}:
48  * <ul>
49  * <li>PDP Modification Lock</li>
50  * <li>PDP Modify Request Map</li>
51  * <li>PAP DAO Factory</li>
52  * </ul>
53  */
54 public abstract class ProviderBase {
55     public static final String DB_ERROR_MSG = "DB error";
56
57     /**
58      * Lock used when updating PDPs.
59      */
60     private final Object updateLock;
61
62     /**
63      * Used to send UPDATE and STATE-CHANGE requests to the PDPs.
64      */
65     private final PdpModifyRequestMap requestMap;
66
67     /**
68      * Generates policy notifications based on responses from PDPs.
69      */
70     private final PolicyNotifier notifier;
71
72     /**
73      * Factory for PAP DAO.
74      */
75     private final PolicyModelsProviderFactoryWrapper daoFactory;
76
77
78     /**
79      * Constructs the object.
80      */
81     protected ProviderBase() {
82         this.updateLock = Registry.get(PapConstants.REG_PDP_MODIFY_LOCK, Object.class);
83         this.requestMap = Registry.get(PapConstants.REG_PDP_MODIFY_MAP, PdpModifyRequestMap.class);
84         this.daoFactory = Registry.get(PapConstants.REG_PAP_DAO_FACTORY, PolicyModelsProviderFactoryWrapper.class);
85         this.notifier = Registry.get(PapConstants.REG_POLICY_NOTIFIER, PolicyNotifier.class);
86     }
87
88     /**
89      * Processes a policy request.
90      *
91      * @param request PDP policy request
92      * @param processor function that processes the request
93      * @throws PfModelException if an error occurred
94      */
95     protected <T> void process(T request, BiConsumerWithEx<SessionData, T> processor) throws PfModelException {
96
97         synchronized (updateLock) {
98             SessionData data;
99             PolicyNotification notif = new PolicyNotification();
100
101             try (PolicyModelsProvider dao = daoFactory.create()) {
102
103                 data = new SessionData(dao);
104                 processor.accept(data, request);
105
106                 // make all of the DB updates
107                 data.updateDb(notif);
108
109             } catch (PfModelException | PfModelRuntimeException e) {
110                 throw e;
111
112             } catch (RuntimeException e) {
113                 throw new PfModelException(Status.INTERNAL_SERVER_ERROR, "request failed", e);
114             }
115
116             // publish the requests
117             data.getPdpRequests().forEach(pair -> requestMap.addRequest(pair.getLeft(), pair.getRight()));
118
119             // publish the notifications
120             notifier.publish(notif);
121         }
122     }
123
124     /**
125      * Process a single policy from the request.
126      *
127      * @param data session data
128      * @param desiredPolicy request policy
129      * @throws PfModelException if an error occurred
130      */
131     protected void processPolicy(SessionData data, ToscaConceptIdentifierOptVersion desiredPolicy)
132                     throws PfModelException {
133
134         ToscaPolicy policy = getPolicy(data, desiredPolicy);
135
136         Collection<PdpGroup> groups = getGroups(data, policy.getTypeIdentifier());
137         if (groups.isEmpty()) {
138             throw new PfModelException(Status.BAD_REQUEST, "policy not supported by any PDP group: "
139                             + desiredPolicy.getName() + " " + desiredPolicy.getVersion());
140         }
141
142         Updater updater = makeUpdater(data, policy, desiredPolicy);
143
144         for (PdpGroup group : groups) {
145             upgradeGroup(data, group, updater);
146         }
147     }
148
149     /**
150      * Makes a function to update a subgroup. The function is expected to return
151      * {@code true} if the subgroup was updated, {@code false} if no update was
152      * necessary/appropriate.
153      *
154      * @param data session data
155      * @param policy policy to be added to or removed from each subgroup
156      * @param desiredPolicy request policy
157      * @return a function to update a subgroup
158      */
159     protected abstract Updater makeUpdater(SessionData data, ToscaPolicy policy,
160                     ToscaConceptIdentifierOptVersion desiredPolicy);
161
162     /**
163      * Finds the active PDP group(s) that supports the given policy type.
164      *
165      * @param data session data
166      * @param policyType the policy type of interest
167      * @return the matching PDP group, or {@code null} if no active group supports the
168      *         given PDP types
169      * @throws PfModelException if an error occurred
170      */
171     private Collection<PdpGroup> getGroups(SessionData data, ToscaConceptIdentifier policyType)
172                     throws PfModelException {
173
174         return data.getActivePdpGroupsByPolicyType(policyType);
175     }
176
177     /**
178      * Updates a group, assigning a new version number, if it actually changes.
179      *
180      * @param data session data
181      * @param group the original group, to be updated
182      * @param updater function to update a group
183      * @throws PfModelException if an error occurred
184      */
185     private void upgradeGroup(SessionData data, PdpGroup group, Updater updater) throws PfModelException {
186
187         boolean updated = false;
188
189         for (PdpSubGroup subgroup : group.getPdpSubgroups()) {
190
191             if (!updater.apply(group, subgroup)) {
192                 continue;
193             }
194
195             updated = true;
196
197             makeUpdates(data, group, subgroup);
198         }
199
200
201         if (updated) {
202             // something changed
203             data.update(group);
204         }
205     }
206
207     /**
208      * Makes UPDATE messages for each PDP in a subgroup.
209      *
210      * @param data session data
211      * @param group group containing the subgroup
212      * @param subgroup subgroup whose PDPs should receive messages
213      */
214     protected void makeUpdates(SessionData data, PdpGroup group, PdpSubGroup subgroup) {
215         for (Pdp pdp : subgroup.getPdpInstances()) {
216             data.addUpdate(makeUpdate(data, group, subgroup, pdp));
217         }
218     }
219
220     /**
221      * Makes an UPDATE message for a particular PDP.
222      *
223      * @param data session data
224      * @param group group to which the PDP should belong
225      * @param subgroup subgroup to which the PDP should belong
226      * @param pdp the PDP of interest
227      * @return a new UPDATE message
228      */
229     private PdpUpdate makeUpdate(SessionData data, PdpGroup group, PdpSubGroup subgroup, Pdp pdp) {
230
231         PdpUpdate update = new PdpUpdate();
232
233         update.setName(pdp.getInstanceId());
234         update.setDescription(group.getDescription());
235         update.setPdpGroup(group.getName());
236         update.setPdpSubgroup(subgroup.getPdpType());
237         update.setPolicies(subgroup.getPolicies().stream().map(ToscaConceptIdentifierOptVersion::new)
238                         .map(ident -> getPolicy(data, ident)).collect(Collectors.toList()));
239
240         return update;
241     }
242
243     /**
244      * Gets the specified policy.
245      *
246      * @param data session data
247      * @param ident policy identifier, with an optional version
248      * @return the policy of interest
249      * @throws PfModelRuntimeException if an error occurred or the policy was not found
250      */
251     private ToscaPolicy getPolicy(SessionData data, ToscaConceptIdentifierOptVersion ident) {
252         try {
253             ToscaPolicy policy = data.getPolicy(ident);
254             if (policy == null) {
255                 throw new PfModelRuntimeException(Status.NOT_FOUND,
256                                 "cannot find policy: " + ident.getName() + " " + ident.getVersion());
257             }
258
259             return policy;
260
261         } catch (PfModelException e) {
262             throw new PfModelRuntimeException(e.getErrorResponse().getResponseCode(),
263                             e.getErrorResponse().getErrorMessage(), e);
264         }
265     }
266
267     @FunctionalInterface
268     public static interface BiConsumerWithEx<F, S> {
269         /**
270          * Performs this operation on the given arguments.
271          *
272          * @param firstArg the first input argument
273          * @param secondArg the second input argument
274          * @throws PfModelException if an error occurred
275          */
276         void accept(F firstArg, S secondArg) throws PfModelException;
277     }
278
279     @FunctionalInterface
280     public static interface Updater {
281         boolean apply(PdpGroup group, PdpSubGroup subgroup) throws PfModelException;
282     }
283 }