1 /*******************************************************************************
\r
2 * ============LICENSE_START====================================================
\r
4 * * ===========================================================================
\r
5 * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
\r
6 * * Copyright © 2017 Amdocs
\r
7 * * ===========================================================================
\r
8 * * Licensed under the Apache License, Version 2.0 (the "License");
\r
9 * * you may not use this file except in compliance with the License.
\r
10 * * You may obtain a copy of the License at
\r
12 * * http://www.apache.org/licenses/LICENSE-2.0
\r
14 * * Unless required by applicable law or agreed to in writing, software
\r
15 * * distributed under the License is distributed on an "AS IS" BASIS,
\r
16 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
17 * * See the License for the specific language governing permissions and
\r
18 * * limitations under the License.
\r
19 * * ============LICENSE_END====================================================
\r
21 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
23 ******************************************************************************/
\r
24 package com.att.cadi.locator;
\r
26 import com.att.cadi.Access;
\r
27 import com.att.cadi.Access.Level;
\r
28 import com.att.cadi.Locator;
\r
29 import com.att.cadi.LocatorException;
\r
30 import com.att.cadi.routing.GreatCircle;
\r
31 import com.att.inno.env.util.Split;
\r
34 * This Locator is to handle Hot Peer load protection, when the Servers are
\r
36 * 2) Well known client URL
\r
38 * The intention is to change traffic over to the Hot Peer, if a server goes down, and reinstate
\r
39 * when it is back up.
\r
41 * Example of this kind of Service is a MS Certificate Server
\r
47 public abstract class HotPeerLocator<CLIENT> implements Locator<CLIENT> {
\r
48 private final String[] urlstrs;
\r
49 private final CLIENT[] clients;
\r
50 private final long[] failures;
\r
51 private final double[] distances;
\r
52 private int preferred;
\r
53 private long invalidateTime;
\r
54 private Thread refreshThread;
\r
55 protected Access access;
\r
58 * Construct: Expect one or more Strings in the form:
\r
59 * 192.555.112.223:39/38.88087/-77.30122
\r
60 * separated by commas
\r
64 * @param invalidateTime
\r
65 * @param localLatitude
\r
66 * @param localLongitude
\r
67 * @throws LocatorException
\r
69 @SuppressWarnings("unchecked")
\r
70 protected HotPeerLocator(Access access, final String urlstr, final long invalidateTime, final String localLatitude, final String localLongitude) throws LocatorException {
\r
71 this.access = access;
\r
72 urlstrs = Split.split(',', urlstr);
\r
73 clients = (CLIENT[])new Object[urlstrs.length];
\r
74 failures = new long[urlstrs.length];
\r
75 distances= new double[urlstrs.length];
\r
76 this.invalidateTime = invalidateTime;
\r
78 double distance = Double.MAX_VALUE;
\r
79 for(int i=0;i<urlstrs.length;++i) {
\r
80 String[] info = Split.split('/', urlstrs[i]);
\r
82 throw new LocatorException("Configuration needs LAT and LONG, i.e. ip:port/lat/long");
\r
85 clients[i] = _newClient(urlstrs[i]);
\r
87 } catch(LocatorException le) {
\r
88 failures[i] = System.currentTimeMillis()+invalidateTime;
\r
91 double d = GreatCircle.calc(info[1],info[2],localLatitude,localLongitude);
\r
94 // find preferred server
\r
101 access.printf(Level.INIT,"Preferred Client is %s",urlstrs[preferred]);
\r
102 for(int i=0;i<urlstrs.length;++i) {
\r
104 access.printf(Level.INIT,"Alternate Client is %s",urlstrs[i]);
\r
109 protected abstract CLIENT _newClient(String hostInfo) throws LocatorException;
\r
111 * If client can reconnect, then return. Otherwise, destroy and return null;
\r
114 * @throws LocatorException
\r
116 protected abstract CLIENT _invalidate(CLIENT client);
\r
118 protected abstract void _destroy(CLIENT client);
\r
121 public Item best() throws LocatorException {
\r
122 if(failures[preferred]==0L) {
\r
123 return new HPItem(preferred);
\r
125 long now = System.currentTimeMillis();
\r
126 double d = Double.MAX_VALUE;
\r
128 boolean tickle = false;
\r
129 // try for best existing client
\r
130 for(int i=0;i<urlstrs.length;++i) {
\r
131 if(failures[i]<now && distances[i]<d) {
\r
132 if(clients[i]!=null) {
\r
136 tickle = true; // There's some failed clients which can be restored
\r
140 if(best<0 && tickle) {
\r
144 for(int i=0;i<urlstrs.length;++i) {
\r
145 if(failures[i]==0L && distances[i]<d) {
\r
146 if(clients[i]!=null) {
\r
156 * If a valid client is available, but there are some that can refresh, return the client immediately
\r
157 * but start a Thread to do the background Client setup.
\r
160 synchronized(clients) {
\r
161 if(refreshThread==null) {
\r
162 refreshThread = new Thread(new Runnable(){
\r
164 public void run() {
\r
166 refreshThread = null;
\r
169 refreshThread.setDaemon(true);
\r
170 refreshThread.start();
\r
176 throw new LocatorException("No Clients available");
\r
180 return new HPItem(best);
\r
186 public CLIENT get(Item item) throws LocatorException {
\r
187 HPItem hpi = (HPItem)item;
\r
188 CLIENT c = clients[hpi.idx];
\r
190 if(failures[hpi.idx]>System.currentTimeMillis()) {
\r
191 throw new LocatorException("Client requested is invalid");
\r
193 synchronized(clients) {
\r
194 c = _newClient(urlstrs[hpi.idx]);
\r
195 failures[hpi.idx]=0L;
\r
198 } else if(failures[hpi.idx]>0){
\r
199 throw new LocatorException("Client requested is invalid");
\r
204 public String info(Item item) {
\r
205 HPItem hpi = (HPItem)item;
\r
206 if(hpi!=null && hpi.idx<urlstrs.length) {
\r
207 return urlstrs[hpi.idx];
\r
209 return "Invalid Item";
\r
214 public boolean hasItems() {
\r
215 for(int i=0;i<clients.length;++i) {
\r
216 if(clients[i]!=null && failures[i]==0L) {
\r
224 public synchronized void invalidate(Item item) throws LocatorException {
\r
225 HPItem hpi = (HPItem)item;
\r
226 failures[hpi.idx] = System.currentTimeMillis() + invalidateTime;
\r
227 CLIENT c = clients[hpi.idx];
\r
228 clients[hpi.idx] = _invalidate(c);
\r
232 public Item first() throws LocatorException {
\r
233 return new HPItem(0);
\r
237 public Item next(Item item) throws LocatorException {
\r
238 HPItem hpi = (HPItem)item;
\r
239 if(++hpi.idx>=clients.length) {
\r
246 public boolean refresh() {
\r
247 boolean force = !hasItems(); // If no Items at all, reset
\r
249 long now = System.currentTimeMillis();
\r
250 for(int i=0;i<clients.length;++i) {
\r
251 if(failures[i]>0L && (failures[i]<now || force)) { // retry
\r
253 synchronized(clients) {
\r
254 if(clients[i]==null) {
\r
255 clients[i]=_newClient(urlstrs[i]);
\r
259 } catch (LocatorException e) {
\r
260 failures[i]=now+invalidateTime;
\r
269 public void destroy() {
\r
270 for(int i=0;i<clients.length;++i) {
\r
271 if(clients[i]!=null) {
\r
272 _destroy(clients[i]);
\r
278 private static class HPItem implements Item {
\r
281 public HPItem(int i) {
\r
288 * Convenience Functions
\r
290 public CLIENT bestClient() throws LocatorException {
\r
291 return get(best());
\r
294 public boolean invalidate(CLIENT client) throws LocatorException {
\r
295 for(int i=0;i<clients.length;++i) {
\r
296 if(clients[i]==client) { // yes, "==" is appropriate here.. Comparing Java Object Reference
\r
297 invalidate(new HPItem(i));
\r