Mass whitespace changes (Style Warnings)
[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 }