Install tools/libs from doc hub image
[aaf/authz.git] / cadi / client / src / main / java / org / onap / aaf / cadi / locator / HotPeerLocator.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.locator;
23
24 import org.onap.aaf.cadi.Access;
25 import org.onap.aaf.cadi.Locator;
26 import org.onap.aaf.cadi.LocatorException;
27 import org.onap.aaf.cadi.Access.Level;
28 import org.onap.aaf.cadi.routing.GreatCircle;
29 import org.onap.aaf.misc.env.util.Split;
30
31 /**
32  * This Locator is to handle Hot Peer load protection, when the Servers are 
33  *      1) Static
34  *      2) Well known client URL
35  * 
36  * The intention is to change traffic over to the Hot Peer, if a server goes down, and reinstate
37  * when it is back up.
38  * 
39  * Example of this kind of Service is a MS Certificate Server
40  * 
41  * @author Jonathan
42  *
43  * @param <CLIENT>
44  */
45 public abstract class HotPeerLocator<CLIENT> implements Locator<CLIENT> {
46                 private final String[] urlstrs;
47                 private final CLIENT[] clients;
48                 private final long[] failures;
49                 private final double[] distances;
50                 private int preferred;
51                 private long invalidateTime;
52                 private Thread refreshThread;
53                 protected Access access;
54
55                 /**
56                  * Construct:  Expect one or more Strings in the form:
57                  *              192.555.112.223:39/38.88087/-77.30122
58                  *    separated by commas
59                  * 
60                  * @param trans
61                  * @param urlstr
62                  * @param invalidateTime
63                  * @param localLatitude
64                  * @param localLongitude
65                  * @throws LocatorException
66                  */
67                 @SuppressWarnings("unchecked")
68                 protected HotPeerLocator(Access access, final String urlstr, final long invalidateTime, final String localLatitude, final String localLongitude) throws LocatorException {
69                         this.access = access;
70                         urlstrs = Split.split(',', urlstr);
71                         clients = (CLIENT[])new Object[urlstrs.length];
72                         failures = new long[urlstrs.length];
73                         distances= new double[urlstrs.length];
74                         this.invalidateTime = invalidateTime;
75                         
76                         double distance = Double.MAX_VALUE;
77                         for(int i=0;i<urlstrs.length;++i) {
78                                 String[] info = Split.split('/', urlstrs[i]);
79                                 if(info.length<3) {
80                                         throw new LocatorException("Configuration needs LAT and LONG, i.e. ip:port/lat/long");
81                                 }
82                                 try {
83                                         clients[i] = _newClient(urlstrs[i]);
84                                         failures[i] = 0L;
85                                 } catch(LocatorException le) {
86                                         failures[i] = System.currentTimeMillis()+invalidateTime;
87                                 }
88                                 
89                                 double d = GreatCircle.calc(info[1],info[2],localLatitude,localLongitude);
90                                 distances[i]=d;
91                                 
92                                 // find preferred server
93                                 if(d<distance) {
94                                         preferred = i;
95                                         distance=d;
96                                 }
97                         }
98                         
99                         access.printf(Level.INIT,"Preferred Client is %s",urlstrs[preferred]);
100                         for(int i=0;i<urlstrs.length;++i) {
101                                 if(i!=preferred) {
102                                         access.printf(Level.INIT,"Alternate Client is %s",urlstrs[i]);
103                                 }
104                         }
105                 }
106                 
107                 protected abstract CLIENT _newClient(String hostInfo) throws LocatorException;
108                 /**
109                  * If client can reconnect, then return.  Otherwise, destroy and return null;
110                  * @param client
111                  * @return
112                  * @throws LocatorException
113                  */
114                 protected abstract CLIENT _invalidate(CLIENT client);
115                 
116                 protected abstract void _destroy(CLIENT client);
117                 
118                 @Override
119                 public Item best() throws LocatorException {
120                         if(failures[preferred]==0L) {
121                                 return new HPItem(preferred);
122                         } else {
123                                 long now = System.currentTimeMillis();
124                                 double d = Double.MAX_VALUE;
125                                 int best = -1;
126                                 boolean tickle = false;
127                                 // try for best existing client
128                                 for(int i=0;i<urlstrs.length;++i) {
129                                         if(failures[i]<now && distances[i]<d) {
130                                                 if(clients[i]!=null) {
131                                                         best = i;
132                                                         break;
133                                                 } else {
134                                                         tickle = true; // There's some failed clients which can be restored
135                                                 }
136                                         }
137                                 }
138                                 if(best<0 && tickle) {
139                                         tickle=false;
140                                         if(refresh()) {
141                                                 // try again
142                                                 for(int i=0;i<urlstrs.length;++i) {
143                                                         if(failures[i]==0L && distances[i]<d) {
144                                                                 if(clients[i]!=null) {
145                                                                         best = i;
146                                                                         break;
147                                                                 }
148                                                         }
149                                                 }
150                                         }
151                                 }
152                         
153                                 /*
154                                  * If a valid client is available, but there are some that can refresh, return the client immediately
155                                  * but start a Thread to do the background Client setup.
156                                  */
157                                 if(tickle) {
158                                         synchronized(clients) {
159                                                 if(refreshThread==null) {
160                                                         refreshThread = new Thread(new Runnable(){
161                                                                 @Override
162                                                                 public void run() {
163                                                                         refresh();
164                                                                         refreshThread = null;
165                                                                 }
166                                                         });
167                                                         refreshThread.setDaemon(true);
168                                                         refreshThread.start();
169                                                 }
170                                         }
171                                 }
172                                 
173                                 if(best<0) {
174                                         throw new LocatorException("No Clients available");
175                                 }
176
177                                 
178                                 return new HPItem(best);
179                         }
180                 }
181                 
182
183                 @Override
184                 public CLIENT get(Item item) throws LocatorException {
185                         HPItem hpi = (HPItem)item;
186                         CLIENT c = clients[hpi.idx];
187                         if(c==null) {
188                                 if(failures[hpi.idx]>System.currentTimeMillis()) {
189                                         throw new LocatorException("Client requested is invalid");      
190                                 } else {
191                                         synchronized(clients) {
192                                                 c = _newClient(urlstrs[hpi.idx]);
193                                                 failures[hpi.idx]=0L;
194                                         }
195                                 }
196                         } else if(failures[hpi.idx]>0){
197                                 throw new LocatorException("Client requested is invalid");
198                         }
199                         return c;
200                 }
201                 
202                 public String info(Item item) {
203                         HPItem hpi = (HPItem)item;
204                         if(hpi!=null && hpi.idx<urlstrs.length) {
205                                 return urlstrs[hpi.idx];
206                         } else {
207                                 return "Invalid Item";
208                         }
209                 }
210
211                 @Override
212                 public boolean hasItems() {
213                         for(int i=0;i<clients.length;++i) {
214                                 if(clients[i]!=null && failures[i]==0L) {
215                                         return true;
216                                 }
217                         }
218                         return false;
219                 }
220                 
221                 @Override
222                 public synchronized void invalidate(Item item) throws LocatorException {
223                         HPItem hpi = (HPItem)item;
224                         failures[hpi.idx] = System.currentTimeMillis() + invalidateTime;
225                         CLIENT c = clients[hpi.idx];
226                         clients[hpi.idx] = _invalidate(c);
227                 }
228                 
229                 @Override
230                 public Item first() throws LocatorException {
231                         return new HPItem(0);
232                 }
233                 
234                 @Override
235                 public Item next(Item item) throws LocatorException {
236                         HPItem hpi = (HPItem)item;
237                         if(++hpi.idx>=clients.length) {
238                                 return null;
239                         }
240                         return hpi;
241                 }
242                 
243                 @Override
244                 public boolean refresh() {
245                         boolean force = !hasItems(); // If no Items at all, reset
246                         boolean rv = true;
247                         long now = System.currentTimeMillis();
248                         for(int i=0;i<clients.length;++i) {
249                                 if(failures[i]>0L && (failures[i]<now || force)) { // retry
250                                         try {
251                                                 synchronized(clients) {
252                                                         if(clients[i]==null) {
253                                                                 clients[i]=_newClient(urlstrs[i]);
254                                                         }
255                                                         failures[i]=0L;
256                                                 }
257                                         } catch (LocatorException e) {
258                                                 failures[i]=now+invalidateTime;
259                                                 rv = false;
260                                         }
261                                 }
262                         }
263                         return rv;
264                 }
265                 
266                 @Override
267                 public void destroy() {
268                         for(int i=0;i<clients.length;++i) {
269                                 if(clients[i]!=null) {
270                                         _destroy(clients[i]);
271                                         clients[i] = null;
272                                 }
273                         }
274                 }
275
276                 private static class HPItem implements Item {
277                         private int idx;
278
279                         public HPItem(int i) {
280                                 idx = i;
281                         }
282                 }
283                 
284
285                 /*
286                  * Convenience Functions
287                  */
288                 public CLIENT bestClient() throws LocatorException {
289                         return get(best());
290                 }
291
292                 public boolean invalidate(CLIENT client) throws LocatorException {
293                         for(int i=0;i<clients.length;++i) {
294                                 if(clients[i]==client) { // yes, "==" is appropriate here.. Comparing Java Object Reference
295                                         invalidate(new HPItem(i));
296                                         return true;
297                                 }
298                         }
299                         return false;
300                 }
301
302         }