Merge "Add junit tests for PdpGroupDeleteProvider"
[policy/pap.git] / main / src / main / java / org / onap / policy / pap / main / comm / PdpModifyRequestMap.java
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP PAP
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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=========================================================
19  */
20
21 package org.onap.policy.pap.main.comm;
22
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import org.onap.policy.models.base.PfModelException;
29 import org.onap.policy.models.pdp.concepts.Pdp;
30 import org.onap.policy.models.pdp.concepts.PdpGroup;
31 import org.onap.policy.models.pdp.concepts.PdpGroupFilter;
32 import org.onap.policy.models.pdp.concepts.PdpMessage;
33 import org.onap.policy.models.pdp.concepts.PdpStateChange;
34 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
35 import org.onap.policy.models.pdp.concepts.PdpUpdate;
36 import org.onap.policy.models.pdp.enums.PdpState;
37 import org.onap.policy.models.provider.PolicyModelsProvider;
38 import org.onap.policy.pap.main.PolicyModelsProviderFactoryWrapper;
39 import org.onap.policy.pap.main.comm.msgdata.Request;
40 import org.onap.policy.pap.main.comm.msgdata.RequestListener;
41 import org.onap.policy.pap.main.comm.msgdata.StateChangeReq;
42 import org.onap.policy.pap.main.comm.msgdata.UpdateReq;
43 import org.onap.policy.pap.main.parameters.PdpModifyRequestMapParams;
44 import org.onap.policy.pap.main.parameters.RequestParams;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 /**
49  * Maps a PDP name to requests that modify PDPs.
50  */
51 public class PdpModifyRequestMap {
52     private static final Logger logger = LoggerFactory.getLogger(PdpModifyRequestMap.class);
53
54     private static final String UNEXPECTED_BROADCAST = "unexpected broadcast message: ";
55
56     /**
57      * Maps a PDP name to its outstanding requests.
58      */
59     private final Map<String, PdpRequests> pdp2requests = new HashMap<>();
60
61     /**
62      * PDP modification lock.
63      */
64     private final Object modifyLock;
65
66     /**
67      * The configuration parameters.
68      */
69     private final PdpModifyRequestMapParams params;
70
71     /**
72      * Factory for PAP DAO.
73      */
74     private final PolicyModelsProviderFactoryWrapper daoFactory;
75
76
77     /**
78      * Constructs the object.
79      *
80      * @param params configuration parameters
81      *
82      * @throws IllegalArgumentException if a required parameter is not set
83      */
84     public PdpModifyRequestMap(PdpModifyRequestMapParams params) {
85         params.validate();
86
87         this.params = params;
88         this.modifyLock = params.getModifyLock();
89         this.daoFactory = params.getDaoFactory();
90     }
91
92     /**
93      * Stops publishing requests to the given PDP.
94      *
95      * @param pdpName PDP name
96      */
97     public void stopPublishing(String pdpName) {
98         synchronized (modifyLock) {
99             PdpRequests requests = pdp2requests.remove(pdpName);
100             if (requests != null) {
101                 requests.stopPublishing();
102             }
103         }
104     }
105
106     /**
107      * Adds a pair of requests to the map.
108      *
109      * @param update the UPDATE request or {@code null}
110      * @param stateChange the STATE-CHANGE request or {@code null}
111      */
112     public void addRequest(PdpUpdate update, PdpStateChange stateChange) {
113         if (update == null) {
114             addRequest(stateChange);
115
116         } else {
117             synchronized (modifyLock) {
118                 addRequest(update);
119                 addRequest(stateChange);
120             }
121         }
122     }
123
124     /**
125      * Adds an UPDATE request to the map.
126      *
127      * @param update the UPDATE request or {@code null}
128      */
129     public void addRequest(PdpUpdate update) {
130         if (update == null) {
131             return;
132         }
133
134         if (isBroadcast(update)) {
135             throw new IllegalArgumentException(UNEXPECTED_BROADCAST + update);
136         }
137
138         // @formatter:off
139         RequestParams reqparams = new RequestParams()
140             .setMaxRetryCount(params.getParams().getUpdateParameters().getMaxRetryCount())
141             .setTimers(params.getUpdateTimers())
142             .setModifyLock(params.getModifyLock())
143             .setPublisher(params.getPublisher())
144             .setResponseDispatcher(params.getResponseDispatcher());
145         // @formatter:on
146
147         String name = update.getName() + " " + PdpUpdate.class.getSimpleName();
148         UpdateReq request = new UpdateReq(reqparams, name, update);
149
150         addSingleton(request);
151     }
152
153     /**
154      * Adds a STATE-CHANGE request to the map.
155      *
156      * @param stateChange the STATE-CHANGE request or {@code null}
157      */
158     public void addRequest(PdpStateChange stateChange) {
159         if (stateChange == null) {
160             return;
161         }
162
163         if (isBroadcast(stateChange)) {
164             throw new IllegalArgumentException(UNEXPECTED_BROADCAST + stateChange);
165         }
166
167         // @formatter:off
168         RequestParams reqparams = new RequestParams()
169             .setMaxRetryCount(params.getParams().getStateChangeParameters().getMaxRetryCount())
170             .setTimers(params.getStateChangeTimers())
171             .setModifyLock(params.getModifyLock())
172             .setPublisher(params.getPublisher())
173             .setResponseDispatcher(params.getResponseDispatcher());
174         // @formatter:on
175
176         String name = stateChange.getName() + " " + PdpStateChange.class.getSimpleName();
177         StateChangeReq request = new StateChangeReq(reqparams, name, stateChange);
178
179         addSingleton(request);
180     }
181
182     /**
183      * Determines if a message is a broadcast message.
184      *
185      * @param message the message to examine
186      * @return {@code true} if the message is a broadcast message, {@code false} if
187      *         destined for a single PDP
188      */
189     private boolean isBroadcast(PdpMessage message) {
190         return (message.getName() == null);
191     }
192
193     /**
194      * Configures and adds a request to the map.
195      *
196      * @param request the request to be added
197      */
198     private void addSingleton(Request request) {
199
200         synchronized (modifyLock) {
201             PdpRequests requests = pdp2requests.computeIfAbsent(request.getMessage().getName(), this::makePdpRequests);
202
203             request.setListener(new SingletonListener(requests, request));
204             requests.addSingleton(request);
205         }
206     }
207
208     /**
209      * Starts the next request associated with a PDP.
210      *
211      * @param requests current set of requests
212      * @param request the request that just completed
213      */
214     private void startNextRequest(PdpRequests requests, Request request) {
215         if (!requests.startNextRequest(request)) {
216             pdp2requests.remove(requests.getPdpName(), requests);
217         }
218     }
219
220     /**
221      * Disables a PDP by removing it from its subgroup and then sending it a PASSIVE
222      * request.
223      *
224      * @param requests the requests associated with the PDP to be disabled
225      */
226     private void disablePdp(PdpRequests requests) {
227
228         // remove the requests from the map
229         if (!pdp2requests.remove(requests.getPdpName(), requests)) {
230             // don't have the info we need to disable it
231             logger.warn("no requests with which to disable {}", requests.getPdpName());
232             return;
233         }
234
235         logger.warn("disabling {}", requests.getPdpName());
236
237         requests.stopPublishing();
238
239         // don't do anything if we don't have a group
240         String name = requests.getLastGroupName();
241         if (name == null) {
242             logger.warn("no group with which to disable {}", requests.getPdpName());
243             return;
244         }
245
246         // remove the PDP from the group
247         removeFromGroup(requests.getPdpName(), name);
248
249         // send the state change
250         PdpStateChange change = new PdpStateChange();
251         change.setName(requests.getPdpName());
252         change.setState(PdpState.PASSIVE);
253         addRequest(change);
254     }
255
256     /**
257      * Removes a PDP from its group.
258      *
259      * @param pdpName name of the PDP to be removed
260      * @param groupName name of the group from which it should be removed
261      */
262     private void removeFromGroup(String pdpName, String groupName) {
263
264         try (PolicyModelsProvider dao = daoFactory.create()) {
265
266             PdpGroupFilter filter = PdpGroupFilter.builder().name(groupName).groupState(PdpState.ACTIVE)
267                             .version(PdpGroupFilter.LATEST_VERSION).build();
268
269             List<PdpGroup> groups = dao.getFilteredPdpGroups(filter);
270             if (groups.isEmpty()) {
271                 return;
272             }
273
274             PdpGroup group = groups.get(0);
275
276             for (PdpSubGroup subgrp : group.getPdpSubgroups()) {
277                 if (removeFromSubgroup(pdpName, group, subgrp)) {
278                     dao.updatePdpGroups(Collections.singletonList(group));
279                     return;
280                 }
281             }
282
283         } catch (PfModelException e) {
284             logger.info("unable to remove PDP {} from subgroup", pdpName, e);
285         }
286     }
287
288     /**
289      * Removes a PDP from a subgroup.
290      *
291      * @param pdpName name of the PDP to be removed
292      * @param group group from which to attempt to remove the PDP
293      * @param subgrp subgroup from which to attempt to remove the PDP
294      * @return {@code true} if the PDP was removed, {@code false} if the PDP was not in
295      *         the group
296      * @throws PfModelException if a DB error occurs
297      */
298     private boolean removeFromSubgroup(String pdpName, PdpGroup group, PdpSubGroup subgrp) throws PfModelException {
299
300         Iterator<Pdp> iter = subgrp.getPdpInstances().iterator();
301
302         while (iter.hasNext()) {
303             Pdp instance = iter.next();
304
305             if (pdpName.equals(instance.getInstanceId())) {
306                 logger.info("removed {} from group={} version={} subgroup={}", pdpName, group.getName(),
307                                 group.getVersion(), subgrp.getPdpType());
308                 iter.remove();
309                 subgrp.setCurrentInstanceCount(subgrp.getPdpInstances().size());
310                 return true;
311             }
312         }
313
314         return false;
315     }
316
317     /**
318      * Creates a new set of requests for a PDP. May be overridden by junit tests.
319      *
320      * @param pdpName PDP name
321      * @return a new set of requests
322      */
323     protected PdpRequests makePdpRequests(String pdpName) {
324         return new PdpRequests(pdpName);
325     }
326
327     /**
328      * Listener for singleton request events.
329      */
330     private class SingletonListener implements RequestListener {
331         private final PdpRequests requests;
332         private final Request request;
333
334         public SingletonListener(PdpRequests requests, Request request) {
335             this.requests = requests;
336             this.request = request;
337         }
338
339         @Override
340         public void failure(String pdpName, String reason) {
341             if (requests.getPdpName().equals(pdpName)) {
342                 disablePdp(requests);
343             }
344         }
345
346         @Override
347         public void success(String pdpName) {
348             if (requests.getPdpName().equals(pdpName)) {
349                 if (pdp2requests.get(requests.getPdpName()) == requests) {
350                     startNextRequest(requests, request);
351
352                 } else {
353                     logger.info("discard old requests for {}", pdpName);
354                     requests.stopPublishing();
355                 }
356             }
357         }
358
359         @Override
360         public void retryCountExhausted() {
361             disablePdp(requests);
362         }
363     }
364 }