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