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