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