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