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