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