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