2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.policy.pap.main.comm;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.List;
31 import org.onap.policy.models.base.PfModelException;
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.PdpGroupFilter;
35 import org.onap.policy.models.pdp.concepts.PdpMessage;
36 import org.onap.policy.models.pdp.concepts.PdpStateChange;
37 import org.onap.policy.models.pdp.concepts.PdpSubGroup;
38 import org.onap.policy.models.pdp.concepts.PdpUpdate;
39 import org.onap.policy.models.pdp.enums.PdpState;
40 import org.onap.policy.models.provider.PolicyModelsProvider;
41 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyIdentifier;
42 import org.onap.policy.pap.main.PolicyModelsProviderFactoryWrapper;
43 import org.onap.policy.pap.main.comm.msgdata.Request;
44 import org.onap.policy.pap.main.comm.msgdata.RequestListener;
45 import org.onap.policy.pap.main.comm.msgdata.StateChangeReq;
46 import org.onap.policy.pap.main.comm.msgdata.UpdateReq;
47 import org.onap.policy.pap.main.notification.PolicyNotifier;
48 import org.onap.policy.pap.main.parameters.PdpModifyRequestMapParams;
49 import org.onap.policy.pap.main.parameters.RequestParams;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 * Maps a PDP name to requests that modify PDPs.
56 public class PdpModifyRequestMap {
57 private static final Logger logger = LoggerFactory.getLogger(PdpModifyRequestMap.class);
59 private static final String UNEXPECTED_BROADCAST = "unexpected broadcast message: ";
62 * Maps a PDP name to its outstanding requests.
64 private final Map<String, PdpRequests> pdp2requests = new HashMap<>();
67 * PDP modification lock.
69 private final Object modifyLock;
72 * The configuration parameters.
74 private final PdpModifyRequestMapParams params;
77 * Factory for PAP DAO.
79 private final PolicyModelsProviderFactoryWrapper daoFactory;
82 * Used to notify when policy updates completes.
84 private final PolicyNotifier policyNotifier;
87 * Used to undeploy policies from the system, when they cannot be deployed to a PDP.
90 * Note: there's a "catch-22" here. The request map needs an undeployer, but the
91 * undeployer needs the request map. Consequently, the request map is created first,
92 * then the undeployer, and finally, this field is set.
95 private PolicyUndeployer policyUndeployer;
99 * Constructs the object.
101 * @param params configuration parameters
103 * @throws IllegalArgumentException if a required parameter is not set
105 public PdpModifyRequestMap(PdpModifyRequestMapParams params) {
108 this.params = params;
109 this.modifyLock = params.getModifyLock();
110 this.daoFactory = params.getDaoFactory();
111 this.policyNotifier = params.getPolicyNotifier();
115 * Determines if the map contains any requests.
117 * @return {@code true} if the map is empty, {@code false} otherwise
119 public boolean isEmpty() {
120 return pdp2requests.isEmpty();
124 * Stops publishing requests to the given PDP.
126 * @param pdpName PDP name
128 public void stopPublishing(String pdpName) {
129 synchronized (modifyLock) {
130 PdpRequests requests = pdp2requests.remove(pdpName);
131 if (requests != null) {
132 requests.stopPublishing();
138 * Adds a pair of requests to the map.
140 * @param update the UPDATE request or {@code null}
141 * @param stateChange the STATE-CHANGE request or {@code null}
143 public void addRequest(PdpUpdate update, PdpStateChange stateChange) {
144 if (update == null) {
145 addRequest(stateChange);
147 } else if (stateChange == null) {
150 } else if (stateChange.getState() == PdpState.ACTIVE) {
151 // publish update before activating
152 synchronized (modifyLock) {
154 addRequest(stateChange);
158 // deactivate before publishing update
159 synchronized (modifyLock) {
160 addRequest(stateChange);
167 * Adds an UPDATE request to the map.
169 * @param update the UPDATE request or {@code null}
171 public void addRequest(PdpUpdate update) {
172 if (update == null) {
176 if (isBroadcast(update)) {
177 throw new IllegalArgumentException(UNEXPECTED_BROADCAST + update);
181 RequestParams reqparams = new RequestParams()
182 .setMaxRetryCount(params.getParams().getUpdateParameters().getMaxRetryCount())
183 .setTimers(params.getUpdateTimers())
184 .setModifyLock(params.getModifyLock())
185 .setPdpPublisher(params.getPdpPublisher())
186 .setResponseDispatcher(params.getResponseDispatcher());
189 String name = update.getName() + " " + PdpUpdate.class.getSimpleName();
190 UpdateReq request = new UpdateReq(reqparams, name, update);
192 addSingleton(request);
196 * Adds a STATE-CHANGE request to the map.
198 * @param stateChange the STATE-CHANGE request or {@code null}
200 public void addRequest(PdpStateChange stateChange) {
201 if (stateChange == null) {
205 if (isBroadcast(stateChange)) {
206 throw new IllegalArgumentException(UNEXPECTED_BROADCAST + stateChange);
210 RequestParams reqparams = new RequestParams()
211 .setMaxRetryCount(params.getParams().getStateChangeParameters().getMaxRetryCount())
212 .setTimers(params.getStateChangeTimers())
213 .setModifyLock(params.getModifyLock())
214 .setPdpPublisher(params.getPdpPublisher())
215 .setResponseDispatcher(params.getResponseDispatcher());
218 String name = stateChange.getName() + " " + PdpStateChange.class.getSimpleName();
219 StateChangeReq request = new StateChangeReq(reqparams, name, stateChange);
221 addSingleton(request);
225 * Determines if a message is a broadcast message.
227 * @param message the message to examine
228 * @return {@code true} if the message is a broadcast message, {@code false} if
229 * destined for a single PDP
231 private boolean isBroadcast(PdpMessage message) {
232 return (message.getName() == null);
236 * Configures and adds a request to the map.
238 * @param request the request to be added
240 private void addSingleton(Request request) {
242 synchronized (modifyLock) {
243 PdpRequests requests = pdp2requests.computeIfAbsent(request.getMessage().getName(), this::makePdpRequests);
245 request.setListener(new SingletonListener(requests, request));
246 requests.addSingleton(request);
251 * Removes a PDP from all active groups.
253 * @param pdpName name of the PDP to be removed
254 * @return {@code true} if the PDP was removed from a group, {@code false} if it was
255 * not assigned to a group
256 * @throws PfModelException if an error occurred
258 public boolean removeFromGroups(String pdpName) throws PfModelException {
260 try (PolicyModelsProvider dao = daoFactory.create()) {
262 PdpGroupFilter filter = PdpGroupFilter.builder().groupState(PdpState.ACTIVE).build();
263 List<PdpGroup> groups = dao.getFilteredPdpGroups(filter);
264 List<PdpGroup> updates = new ArrayList<>(1);
266 for (PdpGroup group : groups) {
267 if (removeFromGroup(pdpName, group)) {
272 if (updates.isEmpty()) {
276 dao.updatePdpGroups(updates);
283 * Removes a PDP from a group.
285 * @param pdpName name of the PDP to be removed
286 * @param group group from which it should be removed
287 * @return {@code true} if the PDP was removed from the group, {@code false} if it was
288 * not assigned to the group
290 private boolean removeFromGroup(String pdpName, PdpGroup group) {
291 for (PdpSubGroup subgrp : group.getPdpSubgroups()) {
292 if (removeFromSubgroup(pdpName, group, subgrp)) {
301 * Removes a PDP from a subgroup.
303 * @param pdpName name of the PDP to be removed
304 * @param group group from which to attempt to remove the PDP
305 * @param subgrp subgroup from which to attempt to remove the PDP
306 * @return {@code true} if the PDP was removed, {@code false} if the PDP was not in
309 private boolean removeFromSubgroup(String pdpName, PdpGroup group, PdpSubGroup subgrp) {
311 Iterator<Pdp> iter = subgrp.getPdpInstances().iterator();
313 while (iter.hasNext()) {
314 Pdp instance = iter.next();
316 if (pdpName.equals(instance.getInstanceId())) {
317 logger.info("removed {} from group={} subgroup={}", pdpName, group.getName(), subgrp.getPdpType());
319 subgrp.setCurrentInstanceCount(subgrp.getPdpInstances().size());
328 * Creates a new set of requests for a PDP. May be overridden by junit tests.
330 * @param pdpName PDP name
331 * @return a new set of requests
333 protected PdpRequests makePdpRequests(String pdpName) {
334 return new PdpRequests(pdpName, policyNotifier);
338 * Listener for singleton request events.
340 private class SingletonListener implements RequestListener {
341 private final PdpRequests requests;
342 private final Request request;
343 private final String pdpName;
345 public SingletonListener(PdpRequests requests, Request request) {
346 this.requests = requests;
347 this.request = request;
348 this.pdpName = requests.getPdpName();
352 public void failure(String responsePdpName, String reason) {
353 Collection<ToscaPolicyIdentifier> undeployPolicies = requestCompleted(responsePdpName);
354 if (undeployPolicies.isEmpty()) {
355 // nothing to undeploy
360 * Undeploy the extra policies. Note: this will likely cause a new message to
361 * be assigned to the request, thus we must re-start it after making the
364 PdpMessage oldmsg = request.getMessage();
367 logger.warn("undeploy policies from {}:{} that failed to deploy: {}", oldmsg.getPdpGroup(),
368 oldmsg.getPdpSubgroup(), undeployPolicies);
369 policyUndeployer.undeploy(oldmsg.getPdpGroup(), oldmsg.getPdpSubgroup(), undeployPolicies);
370 } catch (PfModelException | RuntimeException e) {
371 logger.error("cannot undeploy policies {}", undeployPolicies, e);
374 if (request.getMessage() == oldmsg) {
375 // message is unchanged - start the next request
376 startNextRequest(request);
378 // message changed - restart the request
379 request.startPublishing();
384 public void success(String responsePdpName) {
385 requestCompleted(responsePdpName);
389 * Handles a request completion, starting the next request, if there is one.
391 * @param responsePdpName name of the PDP provided in the response
392 * @return a list of policies to be undeployed
394 private Collection<ToscaPolicyIdentifier> requestCompleted(String responsePdpName) {
395 if (!pdpName.equals(responsePdpName)) {
396 return Collections.emptyList();
399 if (pdp2requests.get(pdpName) != requests) {
400 logger.info("discard old requests for {}", responsePdpName);
401 requests.stopPublishing();
402 return Collections.emptyList();
405 if (!requests.isFirstInQueue(request)) {
406 logger.error("request is not first in the queue {}", request.getMessage());
407 return Collections.emptyList();
410 Collection<ToscaPolicyIdentifier> undeployPolicies = request.getUndeployPolicies();
411 if (undeployPolicies.isEmpty()) {
412 // nothing to undeploy - just start the next request
413 startNextRequest(request);
416 return undeployPolicies;
420 public void retryCountExhausted() {
425 * Starts the next request associated with a PDP.
427 * @param request the request that just completed
429 private void startNextRequest(Request request) {
430 if (!requests.startNextRequest(request)) {
431 pdp2requests.remove(pdpName, requests);
436 * Removes a PDP from its subgroup.
438 private void removePdp() {
439 requests.stopPublishing();
441 // remove the requests from the map
442 if (!pdp2requests.remove(pdpName, requests)) {
443 // wasn't in the map - the requests must be old
444 logger.warn("discarding old requests for {}", pdpName);
448 logger.warn("removing {}", pdpName);
450 policyNotifier.removePdp(pdpName);
452 // remove the PDP from all groups
454 removeFromGroups(pdpName);
455 } catch (PfModelException e) {
456 logger.info("unable to remove PDP {} from subgroup", pdpName, e);