1 /*******************************************************************************
\r
2 * ============LICENSE_START====================================================
\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
11 * * http://www.apache.org/licenses/LICENSE-2.0
\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
20 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
22 ******************************************************************************/
\r
23 package com.att.cadi.locator;
\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
33 * This Locator is to handle Hot Peer load protection, when the Servers are
\r
35 * 2) Well known client URL
\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
40 * Example of this kind of Service is a MS Certificate Server
\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
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
63 * @param invalidateTime
\r
64 * @param localLatitude
\r
65 * @param localLongitude
\r
66 * @throws LocatorException
\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
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
81 throw new LocatorException("Configuration needs LAT and LONG, i.e. ip:port/lat/long");
\r
84 clients[i] = _newClient(urlstrs[i]);
\r
86 } catch(LocatorException le) {
\r
87 failures[i] = System.currentTimeMillis()+invalidateTime;
\r
90 double d = GreatCircle.calc(info[1],info[2],localLatitude,localLongitude);
\r
93 // find preferred server
\r
100 access.printf(Level.INIT,"Preferred Client is %s",urlstrs[preferred]);
\r
101 for(int i=0;i<urlstrs.length;++i) {
\r
103 access.printf(Level.INIT,"Alternate Client is %s",urlstrs[i]);
\r
108 protected abstract CLIENT _newClient(String hostInfo) throws LocatorException;
\r
110 * If client can reconnect, then return. Otherwise, destroy and return null;
\r
113 * @throws LocatorException
\r
115 protected abstract CLIENT _invalidate(CLIENT client);
\r
117 protected abstract void _destroy(CLIENT client);
\r
120 public Item best() throws LocatorException {
\r
121 if(failures[preferred]==0L) {
\r
122 return new HPItem(preferred);
\r
124 long now = System.currentTimeMillis();
\r
125 double d = Double.MAX_VALUE;
\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
135 tickle = true; // There's some failed clients which can be restored
\r
139 if(best<0 && tickle) {
\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
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
159 synchronized(clients) {
\r
160 if(refreshThread==null) {
\r
161 refreshThread = new Thread(new Runnable(){
\r
163 public void run() {
\r
165 refreshThread = null;
\r
168 refreshThread.setDaemon(true);
\r
169 refreshThread.start();
\r
175 throw new LocatorException("No Clients available");
\r
179 return new HPItem(best);
\r
185 public CLIENT get(Item item) throws LocatorException {
\r
186 HPItem hpi = (HPItem)item;
\r
187 CLIENT c = clients[hpi.idx];
\r
189 if(failures[hpi.idx]>System.currentTimeMillis()) {
\r
190 throw new LocatorException("Client requested is invalid");
\r
192 synchronized(clients) {
\r
193 c = _newClient(urlstrs[hpi.idx]);
\r
194 failures[hpi.idx]=0L;
\r
197 } else if(failures[hpi.idx]>0){
\r
198 throw new LocatorException("Client requested is invalid");
\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
208 return "Invalid Item";
\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
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
231 public Item first() throws LocatorException {
\r
232 return new HPItem(0);
\r
236 public Item next(Item item) throws LocatorException {
\r
237 HPItem hpi = (HPItem)item;
\r
238 if(++hpi.idx>=clients.length) {
\r
245 public boolean refresh() {
\r
246 boolean force = !hasItems(); // If no Items at all, reset
\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
252 synchronized(clients) {
\r
253 if(clients[i]==null) {
\r
254 clients[i]=_newClient(urlstrs[i]);
\r
258 } catch (LocatorException e) {
\r
259 failures[i]=now+invalidateTime;
\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
277 private static class HPItem implements Item {
\r
280 public HPItem(int i) {
\r
287 * Convenience Functions
\r
289 public CLIENT bestClient() throws LocatorException {
\r
290 return get(best());
\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