Fixes for sonar critical issues
[policy/engine.git] / ONAP-PDP-REST / src / main / java / org / onap / policy / pdp / rest / PapUrlResolver.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP-PDP-REST
4  * ================================================================================
5  * Copyright (C) 2017 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.pdp.rest;
22
23 import java.net.URI;
24 import java.text.DateFormat;
25 import java.text.ParseException;
26 import java.text.SimpleDateFormat;
27 import java.util.Date;
28 import java.util.NoSuchElementException;
29 import java.util.Objects;
30 import java.util.Properties;
31
32 import org.onap.policy.common.logging.flexlogger.FlexLogger;
33 import org.onap.policy.common.logging.flexlogger.Logger;
34 import org.onap.policy.rest.XACMLRestProperties;
35
36 import com.att.research.xacml.util.XACMLProperties;
37
38 public class PapUrlResolver {
39     private static final Logger LOGGER = FlexLogger.getLogger(PapUrlResolver.class);
40     // how long to keep a pap failed before making it un-failed, in milli-seconds
41     private static final long FAIL_TIMEOUT = 18000000;
42
43     // thread locks
44     public static final Object propertyLock = new Object();
45
46     // keeping this here for backward compatibility
47     public static String extractIdFromUrl(String url) {
48         return extractQuery(url);
49     }
50
51     public static String extractQuery(String url) {
52         try {
53             return URI.create(url).getQuery();
54         } catch (Exception e) {
55             LOGGER.error("Exception occured while extracting query. So, empty string is returned" + e);
56             return "";
57         }
58     }
59
60     public static String modifyUrl(String idUrl, String serverUrl) {
61         URI one = URI.create(idUrl);
62         String host = one.getPath() + one.getQuery();
63         URI two = URI.create(serverUrl);
64         two.resolve(host);
65         return two.toString();
66     }
67
68     // get an instance of a new PapUrlResolver, using XACMLProperties to get the url lists
69     public static PapUrlResolver getInstance() {
70         return new PapUrlResolver(null, null, null, true);
71     }
72
73     // get an instance of a new PapUrlResolver, using the provides strings for the url lists
74     public static PapUrlResolver getInstance(String urlList, String failedList, String succeededList) {
75         return new PapUrlResolver(urlList, failedList, succeededList, false);
76     }
77
78     // keeps track of our current location in the list of urls, allows for iterating
79     private int pointer;
80
81     // should the XACML property lists be updated after anything changes or should we wait for the update
82     // method to be called.
83     private boolean autoUpdateProperties;
84
85     // this list keeps the sorted, priority of PAP URLs
86     private PapUrlNode[] sortedUrlNodes;
87     // this list keeps the original list of nodes so that they can be entered into the property list correctly
88     private PapUrlNode[] originalUrlNodes;
89
90     // private constructor to make an instance of a PapUrlResolver, called by static method getInstance.
91     // If the list property strings are not defined, we get the values from XACMLProperties.
92     // The instance acts as an iterator, with hasNext and next methods, but does not implement Iterable,
93     // because it is used for a difference purpose.
94     private PapUrlResolver(String urlList, String failedList, String succeededList, boolean autoUpdateProperties) {
95         this.autoUpdateProperties = autoUpdateProperties;
96         String papUrlLists = urlList;
97         String papUrlFailedList = failedList;
98         String papUrlSuccessList = succeededList;
99         if (papUrlLists == null) {
100             papUrlLists = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_URLS);
101             if (papUrlLists == null) {
102                 papUrlLists = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_URL);
103             }
104             papUrlFailedList = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_FAILED_URLS);
105             papUrlSuccessList = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_SUCCEEDED_URLS);
106         }
107
108         String[] urls = papUrlLists.split(",");
109         if (urls.length == 0) {
110             // log error
111         }
112         String[] failed = emptyOrSplit(papUrlFailedList, urls.length);
113         String[] succeeded = emptyOrSplit(papUrlSuccessList, urls.length);
114
115         sortedUrlNodes = new PapUrlNode[urls.length];
116         for (int i = 0; i < urls.length; i++) {
117
118             String userId = null;
119             String pass = null;
120             userId = XACMLProperties.getProperty(urls[i] + "." + XACMLRestProperties.PROP_PAP_USERID);
121             pass = XACMLProperties.getProperty(urls[i] + "." + XACMLRestProperties.PROP_PAP_PASS);
122             if (userId == null || pass == null) {
123                 userId = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_USERID);
124                 pass = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_PASS);
125             }
126             if (userId == null || pass == null) {
127                 userId = "";
128                 pass = "";
129             }
130             PapUrlNode newNode = new PapUrlNode(urls[i], userId, pass);
131             newNode.setFailedTime(failed[i]);
132             newNode.setSucceededTime(succeeded[i]);
133             if (sortedUrlNodes[i] == null) {
134                 sortedUrlNodes[i] = newNode;
135             }
136         }
137         originalUrlNodes = sortedUrlNodes.clone();
138         sort(sortedUrlNodes);
139         pointer = 0;
140     }
141
142     // either split a list by commas, or fill an array to the expected length, if the property list is not long enough
143     private String[] emptyOrSplit(String list, int expectedLength) {
144         String[] ret;
145         if (list == null) {
146             ret = new String[expectedLength];
147             for (int i = 0; i < expectedLength; i++) {
148                 ret[i] = "-1";
149             }
150         } else {
151             ret = list.split(",");
152             if (ret.length != expectedLength) {
153                 ret = emptyOrSplit(null, expectedLength);
154             }
155         }
156         return ret;
157     }
158
159     private void sort(PapUrlNode[] array) {
160
161         // O(n^2) double-loop most likely the best in this case, since number of records will be VERY small
162         for (int i = 0; i < array.length; i++) {
163             for (int j = i; j < array.length; j++) {
164                 if (array[j].compareTo(array[i]) < 0) {
165                     PapUrlNode temp = array[i];
166                     array[i] = array[j];
167                     array[j] = temp;
168                 }
169             }
170         }
171     }
172
173     // returns whether this PapUrlResolver object has more PAP urls that can be tried
174     public boolean hasMoreUrls() {
175         return pointer < sortedUrlNodes.length;
176     }
177
178     // sets the current PAP url as being failed
179     // this will set the failed time to now and remove any succeeded time
180     public void failed() {
181         LOGGER.error("PAP Server FAILED: " + sortedUrlNodes[pointer].getUrl());
182
183         sortedUrlNodes[pointer].setFailedTime(new Date());
184         sortedUrlNodes[pointer].setSucceededTime(null);
185         propertiesUpdated();
186     }
187
188     // sets the current PAP url as being working
189     // this will set the succeeded time to now and remove any failed time
190     // Also, this will cause hasMoreUrls to return false, since a working one has been found
191
192     public void succeeded() {
193         registered();
194         pointer = sortedUrlNodes.length;
195     }
196
197     public void registered() {
198         sortedUrlNodes[pointer].setFailedTime(null);
199         sortedUrlNodes[pointer].setSucceededTime(new Date());
200         LOGGER.info("PAP server SUCCEEDED " + sortedUrlNodes[pointer].getUrl());
201         propertiesUpdated();
202     }
203
204     // returns a properties object with the properties that pertain to PAP urls
205     public Properties getProperties() {
206         String failedPropertyString = "";
207         String succeededPropertyString = "";
208         String urlPropertyString = "";
209         for (int i = 0; i < originalUrlNodes.length; i++) {
210             failedPropertyString = failedPropertyString.concat(",").concat(originalUrlNodes[i].getFailedTime());
211             succeededPropertyString = succeededPropertyString.concat(",")
212                     .concat(originalUrlNodes[i].getSucceededTime());
213             urlPropertyString = urlPropertyString.concat(",").concat(originalUrlNodes[i].getUrl());
214         }
215         Properties prop = new Properties();
216         failedPropertyString = failedPropertyString.substring(1);
217         succeededPropertyString = succeededPropertyString.substring(1);
218         urlPropertyString = urlPropertyString.substring(1);
219         prop.setProperty(XACMLRestProperties.PROP_PAP_FAILED_URLS, failedPropertyString);
220         prop.setProperty(XACMLRestProperties.PROP_PAP_URLS, urlPropertyString);
221         prop.setProperty(XACMLRestProperties.PROP_PAP_SUCCEEDED_URLS, succeededPropertyString);
222         return prop;
223     }
224
225     // saves the updates urls to the correct properties
226     private void propertiesUpdated() {
227         if (!autoUpdateProperties) {
228             return;
229         }
230         Properties prop = getProperties();
231
232         LOGGER.debug("Failed PAP Url List: " + prop.getProperty(XACMLRestProperties.PROP_PAP_FAILED_URLS));
233         LOGGER.debug("Succeeded PAP Url List: " + prop.getProperty(XACMLRestProperties.PROP_PAP_SUCCEEDED_URLS));
234         XACMLProperties.setProperty(XACMLRestProperties.PROP_PAP_FAILED_URLS,
235                 prop.getProperty(XACMLRestProperties.PROP_PAP_FAILED_URLS));
236         XACMLProperties.setProperty(XACMLRestProperties.PROP_PAP_SUCCEEDED_URLS,
237                 prop.getProperty(XACMLRestProperties.PROP_PAP_SUCCEEDED_URLS));
238     }
239
240     // iterates to the next available PAP url, according to the priority order
241     public void getNext() {
242         pointer++;
243     }
244
245     // returns the url of the current PAP server that we are iterating over
246     // will append the provided policy id to the url
247     public String getUrl(String query) {
248         if (sortedUrlNodes[pointer] == null) {
249             throw new NoSuchElementException();
250         } else {
251             return sortedUrlNodes[pointer].getUrl().concat("?").concat(query);
252         }
253     }
254
255     // returns the url of the current PAP server that we are iterating over
256     // Just returns the url, with no id appended to it
257     public String getUrl() {
258         if (sortedUrlNodes[pointer] == null) {
259             throw new NoSuchElementException();
260         } else {
261
262             return sortedUrlNodes[pointer].getUrl();
263         }
264     }
265
266     public String getUserId() {
267         if (sortedUrlNodes[pointer] == null) {
268             throw new NoSuchElementException();
269         } else {
270
271             return sortedUrlNodes[pointer].getUserId();
272         }
273     }
274
275     public String getPass() {
276         if (sortedUrlNodes[pointer] == null) {
277             throw new NoSuchElementException();
278         } else {
279
280             return sortedUrlNodes[pointer].getPass();
281         }
282     }
283
284     // This is the class to hold the details of a single PAP URL
285     // including: the url itself, the last time it failed, and the last time it succeeded
286     // It also includes the custom comparer which can compare based on failed and succeeded times, and takes into
287     // account
288     // the timeout on failures.
289     private class PapUrlNode implements Comparable<PapUrlNode> {
290         private String papUrl;
291         private Date failedTime;
292         private Date succeededTime;
293         private String userId;
294         private String pass;
295
296         public PapUrlNode(String url, String userId, String pass) {
297             this.papUrl = url;
298             failedTime = null;
299             this.succeededTime = null;
300             this.userId = userId;
301             this.pass = pass;
302
303         }
304
305         public String getUserId() {
306             return this.userId;
307         }
308
309         public String getPass() {
310             return this.pass;
311         }
312
313         public void setFailedTime(Object time) {
314             Date failedTimeAsDate = setHandler(time);
315             if (failedTimeAsDate == null) {
316                 this.failedTime = null;
317             } else {
318                 long timeDifference = new Date().getTime() - failedTimeAsDate.getTime();
319                 if (timeDifference < FAIL_TIMEOUT) {
320                     this.failedTime = failedTimeAsDate;
321                 } else {
322                     this.failedTime = null;
323                 }
324             }
325         }
326
327         // set the time that this url succeeded at
328         public void setSucceededTime(Object time) {
329             this.succeededTime = setHandler(time);
330         }
331
332         // parses string into a date or a null date, if the url never failed/succeeded (since -1 will be in the
333         // property)
334         private Date setHandler(Object time) {
335             if (time instanceof String) {
336                 if ("-1".equals((String) time)) {
337                     return null;
338                 }
339                 try {
340                     DateFormat df = new SimpleDateFormat();
341                     return df.parse((String) time);
342                 } catch (ParseException e) {
343                     return null;
344                 }
345             }
346             if (time instanceof Date) {
347                 return (Date) time;
348             }
349             return null;
350         }
351
352         public String getFailedTime() {
353             return formatTime(this.failedTime);
354         }
355
356         public String getSucceededTime() {
357             return formatTime(this.succeededTime);
358         }
359
360         // formats a Date into a string or a -1 if there is not date (-1 is used in properties for no date)
361         private String formatTime(Date d) {
362             if (d == null) {
363                 return "-1";
364             }
365             DateFormat df = new SimpleDateFormat();
366             return df.format(d);
367         }
368
369         public String getUrl() {
370             return papUrl;
371         }
372
373         @Override
374         public int compareTo(PapUrlNode other) {
375             if (this.failedTime == null && other.failedTime != null) {
376                 return -1;
377             }
378             if (this.failedTime != null && other.failedTime == null) {
379                 return 1;
380             }
381             if (this.failedTime != null) {
382                 return this.failedTime.compareTo(other.failedTime);
383             }
384             return 0;
385         }
386
387         @Override
388         public boolean equals(Object obj) {
389             if (obj == this) {
390                 return true;
391             }
392             if (!(obj instanceof PapUrlNode)) {
393                 return false;
394             }
395             PapUrlNode papUrlNode = (PapUrlNode) obj;
396             return Objects.equals(papUrlNode.papUrl, papUrl) && Objects.equals(papUrlNode.failedTime, failedTime)
397                     && Objects.equals(papUrlNode.succeededTime, succeededTime)
398                     && Objects.equals(papUrlNode.userId, userId) && Objects.equals(papUrlNode.pass, pass);
399         }
400
401         @Override
402         public int hashCode() {
403             return Objects.hash(papUrl, failedTime, succeededTime, userId, pass);
404         }
405     }
406 }