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