Add components for PDP communication
[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.HashMap;
25 import java.util.Map;
26 import org.onap.policy.models.pdp.concepts.PdpStateChange;
27 import org.onap.policy.models.pdp.concepts.PdpUpdate;
28 import org.onap.policy.models.tosca.simple.concepts.ToscaPolicy;
29 import org.onap.policy.pap.main.comm.msgdata.StateChangeData;
30 import org.onap.policy.pap.main.comm.msgdata.UpdateData;
31 import org.onap.policy.pap.main.parameters.PdpModifyRequestMapParams;
32
33 /**
34  * Maps a PDP name to requests that modify PDPs.
35  */
36 public class PdpModifyRequestMap {
37
38     /**
39      * Maps a PDP name to its request data. An entry is removed once all of the requests
40      * within the data have been completed.
41      */
42     private final Map<String, ModifyReqData> name2data = new HashMap<>();
43
44     /**
45      * PDP modification lock.
46      */
47     private final Object modifyLock;
48
49     /**
50      * The configuration parameters.
51      */
52     private final PdpModifyRequestMapParams params;
53
54     /**
55      * Constructs the data.
56      *
57      * @param params configuration parameters
58      *
59      * @throws IllegalArgumentException if a required parameter is not set
60      */
61     public PdpModifyRequestMap(PdpModifyRequestMapParams params) {
62         params.validate();
63
64         this.params = params;
65         this.modifyLock = params.getModifyLock();
66     }
67
68     /**
69      * Adds an UPDATE request to the map.
70      *
71      * @param update the UPDATE request or {@code null}
72      */
73     public void addRequest(PdpUpdate update) {
74         addRequest(update, null);
75     }
76
77     /**
78      * Adds STATE-CHANGE request to the map.
79      *
80      * @param stateChange the STATE-CHANGE request or {@code null}
81      */
82     public void addRequest(PdpStateChange stateChange) {
83         addRequest(null, stateChange);
84     }
85
86     /**
87      * Adds a pair of requests to the map.
88      *
89      * @param update the UPDATE request or {@code null}
90      * @param stateChange the STATE-CHANGE request or {@code null}
91      */
92     public void addRequest(PdpUpdate update, PdpStateChange stateChange) {
93         if (update == null && stateChange == null) {
94             return;
95         }
96
97         synchronized (modifyLock) {
98             String pdpName = getPdpName(update, stateChange);
99
100             ModifyReqData data = name2data.get(pdpName);
101             if (data != null) {
102                 // update the existing request
103                 data.add(update);
104                 data.add(stateChange);
105
106             } else {
107                 data = makeRequestData(update, stateChange);
108                 name2data.put(pdpName, data);
109                 data.startPublishing();
110             }
111         }
112     }
113
114     /**
115      * Gets the PDP name from two requests.
116      *
117      * @param update the update request, or {@code null}
118      * @param stateChange the state-change request, or {@code null}
119      * @return the PDP name, or {@code null} if both requests are {@code null}
120      */
121     private static String getPdpName(PdpUpdate update, PdpStateChange stateChange) {
122         String pdpName;
123
124         if (update != null) {
125             if ((pdpName = update.getName()) == null) {
126                 throw new IllegalArgumentException("missing name in " + update);
127             }
128
129             if (stateChange != null && !pdpName.equals(stateChange.getName())) {
130                 throw new IllegalArgumentException(
131                                 "name " + stateChange.getName() + " does not match " + pdpName + " " + stateChange);
132             }
133
134         } else {
135             if ((pdpName = stateChange.getName()) == null) {
136                 throw new IllegalArgumentException("missing name in " + stateChange);
137             }
138         }
139
140         return pdpName;
141     }
142
143     /**
144      * Determines if two requests are the "same", which is does not necessarily mean
145      * "equals".
146      *
147      * @param first first request to check
148      * @param second second request to check
149      * @return {@code true} if the requests are the "same", {@code false} otherwise
150      */
151     protected static boolean isSame(PdpUpdate first, PdpUpdate second) {
152         if (first.getPolicies().size() != second.getPolicies().size()) {
153             return false;
154         }
155
156         if (!first.getPdpGroup().equals(second.getPdpGroup())) {
157             return false;
158         }
159
160         if (!first.getPdpSubgroup().equals(second.getPdpSubgroup())) {
161             return false;
162         }
163
164         // see if the other has any policies that this does not have
165         ArrayList<ToscaPolicy> lst = new ArrayList<>(second.getPolicies());
166         lst.removeAll(first.getPolicies());
167
168         return lst.isEmpty();
169     }
170
171     /**
172      * Determines if two requests are the "same", which is does not necessarily mean
173      * "equals".
174      *
175      * @param first first request to check
176      * @param second second request to check
177      * @return {@code true} if this update subsumes the other, {@code false} otherwise
178      */
179     protected static boolean isSame(PdpStateChange first, PdpStateChange second) {
180         return (first.getState() == second.getState());
181     }
182
183     /**
184      * Request data, which contains an UPDATE or a STATE-CHANGE request, or both. The
185      * UPDATE is always published before the STATE-CHANGE. In addition, both requests may
186      * be changed at any point, possibly triggering a restart of the publishing.
187      */
188     public class ModifyReqData extends RequestData {
189
190         /**
191          * The UPDATE message to be published, or {@code null}.
192          */
193         private PdpUpdate update;
194
195         /**
196          * The STATE-CHANGE message to be published, or {@code null}.
197          */
198         private PdpStateChange stateChange;
199
200
201         /**
202          * Constructs the object.
203          *
204          * @param newUpdate the UPDATE message to be sent, or {@code null}
205          * @param newState the STATE-CHANGE message to be sent, or {@code null}
206          */
207         public ModifyReqData(PdpUpdate newUpdate, PdpStateChange newState) {
208             super(params);
209
210             if (newUpdate != null) {
211                 this.stateChange = newState;
212                 setName(newUpdate.getName());
213                 update = newUpdate;
214                 configure(new ModUpdateData(newUpdate));
215
216             } else {
217                 this.update = null;
218                 setName(newState.getName());
219                 stateChange = newState;
220                 configure(new ModStateChangeData(newState));
221             }
222         }
223
224         /**
225          * Determines if this request is still in the map.
226          */
227         @Override
228         protected boolean isActive() {
229             return (name2data.get(getName()) == this);
230         }
231
232         /**
233          * Removes this request from the map.
234          */
235         @Override
236         protected void allCompleted() {
237             name2data.remove(getName(), this);
238         }
239
240         /**
241          * Adds an UPDATE to the request data, replacing any existing UPDATE, if
242          * appropriate. If the UPDATE is replaced, then publishing is restarted.
243          *
244          * @param newRequest the new UPDATE request
245          */
246         private void add(PdpUpdate newRequest) {
247             if (newRequest == null) {
248                 return;
249             }
250
251             synchronized (modifyLock) {
252                 if (update != null && isSame(update, newRequest)) {
253                     // already have this update - discard it
254                     return;
255                 }
256
257                 // must restart from scratch
258                 stopPublishing();
259
260                 update = newRequest;
261                 configure(new ModUpdateData(newRequest));
262
263                 startPublishing();
264             }
265         }
266
267         /**
268          * Adds a STATE-CHANGE to the request data, replacing any existing UPDATE, if
269          * appropriate. If the STATE-CHANGE is replaced, and we're currently publishing
270          * the STATE-CHANGE, then publishing is restarted.
271          *
272          * @param newRequest the new STATE-CHANGE request
273          */
274         private void add(PdpStateChange newRequest) {
275             if (newRequest == null) {
276                 return;
277             }
278
279             synchronized (modifyLock) {
280                 if (stateChange != null && isSame(stateChange, newRequest)) {
281                     // already have this update - discard it
282                     return;
283                 }
284
285                 if (getWrapper() instanceof StateChangeData) {
286                     // we were publishing STATE-CHANGE, thus must restart it
287                     stopPublishing();
288
289                     stateChange = newRequest;
290                     configure(new ModStateChangeData(newRequest));
291
292                     startPublishing();
293
294                 } else {
295                     // haven't started publishing STATE-CHANGE yet, just replace it
296                     stateChange = newRequest;
297                 }
298             }
299         }
300
301         /**
302          * Indicates that the retry count was exhausted.
303          */
304         protected void retryCountExhausted() {
305             // remove this request data from the PDP request map
306             allCompleted();
307
308             // TODO what to do?
309         }
310
311         /**
312          * Indicates that a response did not match the data.
313          *
314          * @param reason the reason for the mismatch
315          */
316         protected void mismatch(String reason) {
317             // remove this request data from the PDP request map
318             allCompleted();
319
320             // TODO what to do?
321         }
322
323         /**
324          * Wraps an UPDATE.
325          */
326         private class ModUpdateData extends UpdateData {
327
328             public ModUpdateData(PdpUpdate message) {
329                 super(message, params);
330             }
331
332             @Override
333             public void mismatch(String reason) {
334                 ModifyReqData.this.mismatch(reason);
335             }
336
337             @Override
338             public void completed() {
339                 if (stateChange == null) {
340                     // no STATE-CHANGE request - we're done
341                     allCompleted();
342
343                 } else {
344                     // now process the STATE-CHANGE request
345                     configure(new ModStateChangeData(stateChange));
346                     startPublishing();
347                 }
348             }
349         }
350
351         /**
352          * Wraps a STATE-CHANGE.
353          */
354         private class ModStateChangeData extends StateChangeData {
355
356             public ModStateChangeData(PdpStateChange message) {
357                 super(message, params);
358             }
359
360             @Override
361             public void mismatch(String reason) {
362                 ModifyReqData.this.mismatch(reason);
363             }
364
365             @Override
366             public void completed() {
367                 allCompleted();
368             }
369         }
370     }
371
372     // these may be overridden by junit tests
373
374     protected ModifyReqData makeRequestData(PdpUpdate update, PdpStateChange stateChange) {
375         return new ModifyReqData(update, stateChange);
376     }
377 }