fd8e99dc871be4c7d90a56b9f638265cd2caa27b
[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                         return new HPItem(best);
178                 }
179         }
180
181
182         @Override
183         public CLIENT get(Item item) throws LocatorException {
184                 HPItem hpi = (HPItem)item;
185                 CLIENT c = clients[hpi.idx];
186                 if(c==null) {
187                         if(failures[hpi.idx]>System.currentTimeMillis()) {
188                                 throw new LocatorException("Client requested is invalid");
189                         } else {
190                                 synchronized(clients) {
191                                         c = _newClient(urlstrs[hpi.idx]);
192                                         failures[hpi.idx]=0L;
193                                 }
194                         }
195                 } else if(failures[hpi.idx]>0){
196                         throw new LocatorException("Client requested is invalid");
197                 }
198                 return c;
199         }
200
201         public String info(Item item) {
202                 HPItem hpi = (HPItem)item;
203                 if(hpi!=null && hpi.idx<urlstrs.length) {
204                         return urlstrs[hpi.idx];
205                 } else {
206                         return "Invalid Item";
207                 }
208         }
209
210         @Override
211         public boolean hasItems() {
212                 for(int i=0;i<clients.length;++i) {
213                         if(clients[i]!=null && failures[i]==0L) {
214                                 return true;
215                         }
216                 }
217                 return false;
218         }
219
220         @Override
221         public synchronized void invalidate(Item item) throws LocatorException {
222                 HPItem hpi = (HPItem)item;
223                 failures[hpi.idx] = System.currentTimeMillis() + invalidateTime;
224                 CLIENT c = clients[hpi.idx];
225                 clients[hpi.idx] = _invalidate(c);
226         }
227
228         @Override
229         public Item first() throws LocatorException {
230                 return new HPItem(0);
231         }
232
233         @Override
234         public Item next(Item item) throws LocatorException {
235                 HPItem hpi = (HPItem)item;
236                 if(++hpi.idx>=clients.length) {
237                         return null;
238                 }
239                 return hpi;
240         }
241
242         @Override
243         public boolean refresh() {
244                 boolean force = !hasItems(); // If no Items at all, reset
245                 boolean rv = true;
246                 long now = System.currentTimeMillis();
247                 for(int i=0;i<clients.length;++i) {
248                         if(failures[i]>0L && (failures[i]<now || force)) { // retry
249                                 try {
250                                         synchronized(clients) {
251                                                 if(clients[i]==null) {
252                                                         clients[i]=_newClient(urlstrs[i]);
253                                                 }
254                                                 failures[i]=0L;
255                                         }
256                                 } catch (LocatorException e) {
257                                         failures[i]=now+invalidateTime;
258                                         rv = false;
259                                 }
260                         }
261                 }
262                 return rv;
263         }
264
265         @Override
266         public void destroy() {
267                 for(int i=0;i<clients.length;++i) {
268                         if(clients[i]!=null) {
269                                 _destroy(clients[i]);
270                                 clients[i] = null;
271                         }
272                 }
273         }
274
275         private static class HPItem implements Item {
276                 private int idx;
277
278                 public HPItem(int i) {
279                         idx = i;
280                 }
281         }
282
283
284         /*
285          * Convenience Functions
286          */
287         public CLIENT bestClient() throws LocatorException {
288                 return get(best());
289         }
290
291         public boolean invalidate(CLIENT client) throws LocatorException {
292                 for(int i=0;i<clients.length;++i) {
293                         if(clients[i]==client) { // yes, "==" is appropriate here.. Comparing Java Object Reference
294                                 invalidate(new HPItem(i));
295                                 return true;
296                         }
297                 }
298                 return false;
299         }
300
301 }