2 * ============LICENSE_START====================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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====================================================
22 package org.onap.aaf.cadi.aaf.v2_0;
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;
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;
45 import locate.v1_0.Endpoint;
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;
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;
62 protected final String aaf_locator_host;
63 protected URI aaf_locator_uri;
64 private long earliest;
65 private final long refreshWait;
68 public AbsAAFLocator(Access access, String name, final long refreshMin) throws LocatorException {
69 RegistrationPropHolder rph;
71 rph = new RegistrationPropHolder(access, 0);
72 } catch (UnknownHostException | CadiException e1) {
73 throw new LocatorException(e1);
77 aaf_locator_host = rph.replacements(getClass().getSimpleName(),"https://"+Config.AAF_LOCATE_URL_TAG,null,null);
78 if(aaf_locator_host.endsWith("/locate")) {
79 aaf_locator_uri = new URI(aaf_locator_host);
81 aaf_locator_uri = new URI(aaf_locator_host+"/locate");
83 access.printf(Level.INFO, "AbsAAFLocator AAF URI is %s",aaf_locator_uri);
84 } catch (URISyntaxException e) {
85 throw new LocatorException(e);
88 name = rph.replacements(getClass().getSimpleName(),name, null,null);
89 access.printf(Level.INFO, "AbsAAFLocator name is %s",aaf_locator_uri);
91 epList = new LinkedList<>();
92 refreshWait = refreshMin;
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.");
100 latitude = Double.parseDouble(lat);
101 longitude = Double.parseDouble(lng);
105 if (name.startsWith("http")) { // simple URL
107 this.version = access.getProperty(Config.AAF_API_VERSION,Config.AAF_DEFAULT_API_VERSION);
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);
116 * This is the way to setup specialized AAFLocators ahead of time.
119 public static void setCreator(LocatorCreator lc) {
123 public static Locator<URI> create(final String name, final String version) throws LocatorException {
124 if(locatorCreator==null) {
125 throw new LocatorException("LocatorCreator is not set");
127 return locatorCreator.create(name, version);
130 public interface LocatorCreator {
131 public AbsAAFLocator<?> create(String key, String version) throws LocatorException;
132 public void setSelf(String hostname, int port);
135 protected static String nameFromLocatorURI(URI locatorURI) {
136 String[] path = Split.split('/', locatorURI.getPath());
137 if (path.length>1 && "locate".equals(path[1])) {
139 } else if(path.length>1) {
142 return locatorURI.toString();
147 * Setting "self" excludes this service from the list. Critical for contacting peers.
149 public void setSelf(final String hostname, final int port) {
155 public static void setCreatorSelf(final String hostname, final int port) {
156 if (locatorCreator!=null) {
157 locatorCreator.setSelf(hostname,port);
161 protected final synchronized void replace(List<EP> list) {
166 * Call _refresh as needed during calls, but actual refresh will not occur if there
167 * are existing entities or if it has been called in the last 10 (settable) seconds.
168 * Timed Refreshes happen by Scheduled Thread
170 private final boolean _refresh() {
172 long now=System.currentTimeMillis();
175 synchronized(epList) {
177 earliest = now + refreshWait; // call only up to 10 seconds.
180 access.log(Level.ERROR, "Must wait at least " + refreshWait/1000 + " seconds for Locator Refresh");
186 private boolean noEntries() {
187 return epList.isEmpty();
191 public URI get(Item item) throws LocatorException {
194 } else if (item instanceof AAFLItem) {
195 return getURI(((AAFLItem)item).uri);
197 throw new LocatorException(item.getClass().getName() + " does not belong to AAFLocator");
202 public boolean hasItems() {
203 boolean isEmpty = epList.isEmpty();
205 for (Iterator<EP> iter = epList.iterator(); iter.hasNext(); ) {
213 if (_refresh()) { // is refreshed... check again
214 isEmpty = epList.isEmpty();
220 public void invalidate(Item item) throws LocatorException {
222 if (item instanceof AAFLItem) {
223 AAFLItem ali =(AAFLItem)item;
225 synchronized(epList) {
229 ali.iter = getIterator(); // for next guy... fresh iterator
231 throw new LocatorException(item.getClass().getName() + " does not belong to AAFLocator");
237 public Item best() throws LocatorException {
239 throw new LocatorException(String.format("No Entries found for '%s/%s:%s'",
240 (aaf_locator_uri==null?aaf_locator_host:aaf_locator_uri.toString()),
244 List<EP> lep = new ArrayList<>();
246 // Note: Deque is sorted on the way by closest distance
247 Iterator<EP> iter = getIterator();
249 while (iter.hasNext()) {
256 if (Math.abs(ep.distance-first.distance)<.1) { // allow for nearby/precision issues.
268 return new AAFLItem(iter,first);
270 int rand = sr.nextInt(); // Sonar chokes without.
271 int i = Math.abs(rand)%lep.size();
275 return new AAFLItem(iter,lep.get(i));
281 private Iterator<EP> getIterator() {
282 Object[] epa = epList.toArray();
285 epa = epList.toArray();
287 return new EPIterator(epa, epList);
290 public class EPIterator implements Iterator<EP> {
291 private final Object[] epa;
292 private final List<EP> epList;
295 public EPIterator(Object[] epa, List<EP> epList) {
297 this.epList = epList;
298 idx = epa.length>0?0:-1;
302 public boolean hasNext() {
307 while (idx<epa.length) {
308 if ((obj=epa[idx])==null || !((EP)obj).valid) {
314 return idx<epa.length;
321 throw new NoSuchElementException();
323 return (EP)epa[idx++];
327 public void remove() {
328 if (idx>=0 && idx<epa.length) {
329 synchronized(epList) {
330 epList.remove(epa[idx]);
337 public Item first() {
338 Iterator<EP> iter = getIterator();
339 EP ep = AAFLItem.next(iter);
343 return new AAFLItem(iter,ep);
347 public Item next(Item prev) throws LocatorException {
349 StringBuilder sb = new StringBuilder("Locator Item passed in next(item) is null.");
351 for (StackTraceElement st : Thread.currentThread().getStackTrace()) {
353 sb.append(st.toString());
355 sb.append("\n\t...");
359 access.log(Level.ERROR, sb);
361 if (prev instanceof AAFLItem) {
362 AAFLItem ali = (AAFLItem)prev;
363 EP ep = AAFLItem.next(ali.iter);
365 return new AAFLItem(ali.iter,ep);
368 throw new LocatorException(prev.getClass().getName() + " does not belong to AAFLocator");
374 protected static class AAFLItem implements Item {
375 private Iterator<EP> iter;
379 public AAFLItem(Iterator<EP> iter, EP ep) {
385 private static EP next(Iterator<EP> iter) {
387 while (iter.hasNext() && (ep==null || !ep.valid)) {
393 public String toString() {
394 return ep==null?"Locator Item Invalid":ep.toString();
398 protected static class EP implements Comparable<EP> {
400 private final double distance;
401 private boolean valid;
403 public EP(final Endpoint ep, double latitude, double longitude) throws URISyntaxException {
404 uri = new URI(ep.getProtocol(),null,ep.getHostname(),ep.getPort(),null,null,null);
405 distance = GreatCircle.calc(latitude, longitude, ep.getLatitude(), ep.getLongitude());
409 public void invalid() {
414 public int compareTo(EP o) {
415 if (distance<o.distance) {
417 } else if (distance>o.distance) {
425 public String toString() {
426 return distance + ": " + uri + (valid?" valid":" invalidate");
431 * @see org.onap.aaf.cadi.Locator#destroy()
434 public void destroy() {
439 public String toString() {
440 return "AAFLocator for " + name + " on " + getURI();
443 public AbsAAFLocator<TRANS> setPathInfo(String pathInfo) {
444 this.pathInfo = pathInfo;
449 public AbsAAFLocator<TRANS> setQuery(String query) {
455 public AbsAAFLocator<TRANS> setFragment(String fragment) {
456 this.fragment = fragment;
461 // Core URI, for reporting purposes
462 protected abstract URI getURI();
464 protected URI getURI(URI rv) throws LocatorException {
467 return new URI(rv.getScheme(),rv.getUserInfo(),rv.getHost(),rv.getPort(),pathInfo,query,fragment);
468 } catch (URISyntaxException e) {
469 throw new LocatorException("Error copying URL", e);
475 protected void clear() {