Sonar fixes related to exceptions
[aaf/authz.git] / cadi / aaf / src / main / java / org / onap / aaf / cadi / aaf / v2_0 / AbsAAFLocator.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 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
22 package org.onap.aaf.cadi.aaf.v2_0;
23
24 import java.net.URI;
25 import java.net.URISyntaxException;
26 import java.security.SecureRandom;
27 import java.util.ArrayList;
28 import java.util.Iterator;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.NoSuchElementException;
32
33 import org.onap.aaf.cadi.Access;
34 import org.onap.aaf.cadi.Access.Level;
35 import org.onap.aaf.cadi.Locator;
36 import org.onap.aaf.cadi.LocatorException;
37 import org.onap.aaf.cadi.config.Config;
38 import org.onap.aaf.cadi.locator.PropertyLocator;
39 import org.onap.aaf.cadi.routing.GreatCircle;
40 import org.onap.aaf.misc.env.Trans;
41 import org.onap.aaf.misc.env.util.Split;
42
43 import locate.v1_0.Endpoint;
44
45 public abstract class AbsAAFLocator<TRANS extends Trans> implements Locator<URI> {
46         protected static final SecureRandom sr = new SecureRandom();
47         private static LocatorCreator locatorCreator;
48         protected final Access access;
49
50         protected final double latitude;
51         protected final double longitude;
52         protected List<EP> epList;
53         protected final String name, version;
54         private String pathInfo = null;
55         private String query = null;
56         private String fragment = null;
57         private boolean additional = false;
58         protected String myhostname;
59         protected int myport;
60         protected final String aaf_locator_host;
61         protected final URI aaf_locator_uri;
62         private long earliest;
63         private final long refreshWait;
64
65
66         public AbsAAFLocator(Access access, String name, final long refreshMin) throws LocatorException {
67                 aaf_locator_host = access.getProperty(Config.AAF_LOCATE_URL, null);
68                 if(aaf_locator_host==null) {
69                         aaf_locator_uri = null;
70                 } else {
71                         try {
72                                 aaf_locator_uri = new URI(aaf_locator_host);
73                         } catch (URISyntaxException e) {
74                                 throw new LocatorException(e);
75                         }
76                 }
77
78                 epList = new LinkedList<>();
79                 refreshWait = refreshMin;
80
81                 this.access = access;
82                 String lat = access.getProperty(Config.CADI_LATITUDE,null);
83                 String lng = access.getProperty(Config.CADI_LONGITUDE,null);
84                 if(lat==null || lng==null) {
85                         throw new LocatorException(Config.CADI_LATITUDE + " and " + Config.CADI_LONGITUDE + " properties are required.");
86                 } else {
87                         latitude = Double.parseDouble(lat);
88                         longitude = Double.parseDouble(lng);
89                 }
90                 if(name.startsWith("http")) { // simple URL
91                         this.name = name;
92                         this.version = Config.AAF_DEFAULT_VERSION;
93                 } else {
94                         String[] split = Split.split(':', name);
95                         this.name = split[0];
96                         this.version = (split.length > 1) ? split[1] : Config.AAF_DEFAULT_VERSION;
97                 }
98                 
99         }
100
101         /**
102          * This is the way to setup specialized AAFLocators ahead of time.
103          * @param preload
104          */
105         public static void setCreator(LocatorCreator lc) {
106                 locatorCreator = lc; 
107         }
108         
109         public static Locator<URI> create(String key) throws LocatorException {
110                 String name = null;
111                 String version = Config.AAF_DEFAULT_VERSION;
112                 String pathInfo = null;
113                 int prev = key.indexOf("/locate");
114                 if(prev>0) {
115                         prev = key.indexOf('/',prev+6);
116                         if(prev>0) {
117                                 int next = key.indexOf('/',++prev);
118                                 if(next>0) {
119                                         name = key.substring(prev, next);
120                                         pathInfo=key.substring(next);
121                                 } else {
122                                         name = key.substring(prev);
123                                 }
124                                 String[] split = Split.split(':', name);
125                                 switch(split.length) {
126                                         case 3:
127                                         case 2:
128                                                 version = split[1];
129                                                 name = split[0];
130                                                 break;
131                                         default:
132                                                 break;
133                                 }
134                         }
135                 }
136
137                 if(key.startsWith("http")) {
138                         if(name!=null) {
139                                 if(locatorCreator != null) {
140                                         AbsAAFLocator<?> aal = locatorCreator.create(name, version);
141                                         if(pathInfo!=null) {
142                                                 aal.setPathInfo(pathInfo);
143                                         }
144                                         return aal;
145                                 }
146                         } else {
147                                 return new PropertyLocator(key);
148                         }
149                 }
150                 return null;
151         }
152         
153         public static Locator<URI> create(final String name, final String version) throws LocatorException {
154                 return locatorCreator.create(name, version);
155         }
156
157         public interface LocatorCreator {
158                 public AbsAAFLocator<?> create(String key, String version) throws LocatorException;
159                 public void setSelf(String hostname, int port);
160         }
161
162         protected static String nameFromLocatorURI(URI locatorURI) {
163                 String[] path = Split.split('/', locatorURI.getPath());
164                 if(path.length>2 && "locate".equals(path[1])) {
165                         return path[2];
166                 } else {
167                         return locatorURI.toString();
168                 }
169         }
170         
171         /**
172          * Setting "self" excludes this service from the list.  Critical for contacting peers. 
173          */
174         public void setSelf(final String hostname, final int port) {
175                 myhostname=hostname;
176                 myport=port;
177         }
178
179
180         public static void setCreatorSelf(final String hostname, final int port) {
181                 if(locatorCreator!=null) {
182                         locatorCreator.setSelf(hostname,port);
183                 }
184         }
185
186         protected final synchronized void replace(List<EP> list) {
187                 epList = list;
188         }
189         
190         /**
191          * Call _refresh as needed during calls, but actual refresh will not occur if there
192          * are existing entities or if it has been called in the last 10 (settable) seconds.  
193          * Timed Refreshes happen by Scheduled Thread
194          */
195         private final boolean _refresh() {
196                 boolean rv = false;
197                 long now=System.currentTimeMillis();
198                 if(noEntries()) {
199                         if(earliest<now) {
200                                 synchronized(epList) {
201                                         rv = refresh();
202                                         earliest = now + refreshWait; // call only up to 10 seconds.
203                                 }
204                         } else {
205                                 access.log(Level.ERROR, "Must wait at least " + refreshWait/1000 + " seconds for Locator Refresh");
206                         }
207                 }
208                 return rv;
209         }
210
211         private boolean noEntries() {
212                 return epList.isEmpty();
213         }
214
215         @Override
216         public URI get(Item item) throws LocatorException {
217                 if(item==null) {
218                         return null;
219                 } else if(item instanceof AAFLItem) {
220                         return getURI(((AAFLItem)item).uri);
221                 } else {
222                         throw new LocatorException(item.getClass().getName() + " does not belong to AAFLocator");
223                 }
224         }
225
226         @Override
227         public boolean hasItems() {
228                 boolean isEmpty = epList.isEmpty();
229                 if(!isEmpty) {
230                         for(Iterator<EP> iter = epList.iterator(); iter.hasNext(); ) {
231                                 EP ep = iter.next();
232                                 if(ep.valid) {
233                                         return true;
234                                 }
235                         }
236                         isEmpty = true;
237                 }
238                 if(_refresh()) { // is refreshed... check again
239                         isEmpty = epList.isEmpty();
240                 }
241                 return !isEmpty;
242         }
243
244         @Override
245         public void invalidate(Item item) throws LocatorException {
246                 if(item!=null) {
247                         if(item instanceof AAFLItem) {
248                                 AAFLItem ali =(AAFLItem)item; 
249                                 EP ep = ali.ep;
250                                 synchronized(epList) {
251                                         epList.remove(ep);
252                                 }
253                                 ep.invalid();
254                                 ali.iter = getIterator(); // for next guy... fresh iterator
255                         } else {
256                                 throw new LocatorException(item.getClass().getName() + " does not belong to AAFLocator");
257                         }
258                 }
259         }
260
261         @Override
262         public Item best() throws LocatorException {
263                 if(!hasItems()) {
264                         throw new LocatorException("No Entries found" + (pathInfo==null?"":(" for " + pathInfo)));
265                 }
266                 List<EP> lep = new ArrayList<>();
267                 EP first = null;
268                 // Note: Deque is sorted on the way by closest distance
269                 Iterator<EP> iter = getIterator();
270                 EP ep;
271                 while(iter.hasNext()) {
272                         ep = iter.next();
273                         if(ep.valid) {
274                                 if(first==null) {
275                                         first = ep;
276                                         lep.add(first);
277                                 } else {
278                                         if(Math.abs(ep.distance-first.distance)<.1) { // allow for nearby/precision issues.
279                                                 lep.add(ep);
280                                         } else {
281                                                 break;
282                                         }
283                                 }
284                         }
285                 }
286                 switch(lep.size()) {
287                         case 0:
288                                 return null;
289                         case 1:
290                                 return new AAFLItem(iter,first);
291                         default:
292                                 int rand = sr.nextInt(); // Sonar chokes without.
293                                 int i = Math.abs(rand)%lep.size();
294                                 if(i<0) {
295                                         return null;
296                                 } else {
297                                         return new AAFLItem(iter,lep.get(i));
298                                 }
299                         
300                 }
301         }
302
303         private Iterator<EP> getIterator() {
304                 Object[] epa = epList.toArray();
305                 if(epa.length==0) {
306                         _refresh();
307                         epa = epList.toArray();
308                 }
309                 return new EPIterator(epa, epList);
310         }
311
312         public class EPIterator implements Iterator<EP> {
313                 private final Object[] epa;
314                 private final List<EP> epList;
315                 private int idx;
316                 
317                 public EPIterator(Object[] epa, List<EP> epList) {
318                         this.epa = epa;
319                         this.epList = epList;
320                         idx = epa.length>0?0:-1;
321                 }
322
323                 @Override
324                 public boolean hasNext() {
325                         if(idx<0) {
326                                 return false;
327                         } else {
328                                 Object obj;
329                                 while(idx<epa.length) {
330                                         if((obj=epa[idx])==null || !((EP)obj).valid) {
331                                                 ++idx;
332                                                 continue;
333                                         }
334                                         break;
335                                 }
336                                 return idx<epa.length;
337                         }
338                 }
339
340                 @Override
341                 public EP next() {
342                         if(!hasNext() ) {
343                                 throw new NoSuchElementException();
344                         }
345                         return (EP)epa[idx++];
346                 }
347
348                 @Override
349                 public void remove() {
350                         if(idx>=0 && idx<epa.length) {
351                                 synchronized(epList) {
352                                         epList.remove(epa[idx]);
353                                 }
354                         }
355                 }
356         }
357         
358         @Override
359         public Item first()  {
360                 Iterator<EP> iter = getIterator();
361                 EP ep = AAFLItem.next(iter);
362                 if(ep==null) {
363                         return null;
364                 }
365                 return new AAFLItem(iter,ep);
366         }
367
368         @Override
369         public Item next(Item prev) throws LocatorException {
370                 if(prev==null) {
371                         StringBuilder sb = new StringBuilder("Locator Item passed in next(item) is null.");
372                         int lines = 0;
373                         for(StackTraceElement st : Thread.currentThread().getStackTrace()) {
374                                 sb.append("\n\t");
375                                 sb.append(st.toString());
376                                 if(++lines > 5) {
377                                         sb.append("\n\t...");
378                                         break;
379                                 }
380                         }
381                         access.log(Level.ERROR, sb);
382                 } else {
383                         if(prev instanceof AAFLItem) {
384                                 AAFLItem ali = (AAFLItem)prev;
385                                 EP ep = AAFLItem.next(ali.iter);
386                                 if(ep!=null) {
387                                         return new AAFLItem(ali.iter,ep);
388                                 }
389                         } else {
390                                 throw new LocatorException(prev.getClass().getName() + " does not belong to AAFLocator");
391                         }
392                 }
393                 return null;
394         }
395
396         protected static class AAFLItem implements Item {
397                         private Iterator<EP> iter;
398                         private URI uri;
399                         private EP ep;
400         
401                         public AAFLItem(Iterator<EP> iter, EP ep) {
402                                 this.iter = iter;
403                                 this.ep = ep;
404                                 uri = ep.uri;
405                         }
406                         
407                         private static EP next(Iterator<EP> iter) {
408                                 EP ep=null;
409                                 while(iter.hasNext() && (ep==null || !ep.valid)) {
410                                         ep = iter.next();
411                                 }
412                                 return ep;
413                         }
414                         
415                         public String toString() {
416                                 return ep==null?"Locator Item Invalid":ep.toString();
417                         }
418                 }
419
420         protected static class EP implements Comparable<EP> {
421                 private URI uri;
422                 private final double distance;
423                 private boolean valid;
424                 
425                 public EP(final Endpoint ep, double latitude, double longitude) throws URISyntaxException {
426                         uri = new URI(ep.getProtocol(),null,ep.getHostname(),ep.getPort(),null,null,null);
427                         distance = GreatCircle.calc(latitude, longitude, ep.getLatitude(), ep.getLongitude());
428                         valid = true;
429                 }
430
431                 public void invalid() {
432                         valid = false;
433                 }
434
435                 @Override
436                 public int compareTo(EP o) {
437                         if(distance<o.distance) {
438                                 return -1;
439                         } else if(distance>o.distance) {
440                                 return 1;
441                         } else {
442                                 return 0;
443                         }
444                 }
445                 
446                 @Override
447                 public String toString() {
448                         return distance + ": " + uri + (valid?" valid":" invalidate");
449                 }
450         }
451         
452         /* (non-Javadoc)
453          * @see org.onap.aaf.cadi.Locator#destroy()
454          */
455         @Override
456         public void destroy() {
457                 // Nothing to do
458         }
459         
460         @Override
461         public String toString() {
462                 return "AAFLocator for " + name + " on " + getURI();
463         }
464
465         public AbsAAFLocator<TRANS> setPathInfo(String pathInfo) {
466                 this.pathInfo = pathInfo;
467                 additional=true;
468                 return this;
469         }
470
471         public AbsAAFLocator<TRANS> setQuery(String query) {
472                 this.query = query;
473                 additional=true;
474                 return this;
475         }
476
477         public AbsAAFLocator<TRANS>  setFragment(String fragment) {
478                 this.fragment = fragment;
479                 additional=true;
480                 return this;
481         }
482
483         // Core URI, for reporting purposes
484         protected abstract URI getURI();
485
486         protected URI getURI(URI rv) throws LocatorException {
487                 if(additional) {
488                         try {
489                                 return new URI(rv.getScheme(),rv.getUserInfo(),rv.getHost(),rv.getPort(),pathInfo,query,fragment);
490                         } catch (URISyntaxException e) {
491                                 throw new LocatorException("Error copying URL", e);
492                         }
493                 }
494                 return rv;
495         }
496
497
498 }