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