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