Mass whitespace changes (Style Warnings)
[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 }