one more case added in DmaapClientUtilTest.java
[dmaap/messagerouter/dmaapclient.git] / src / main / java / com / att / nsa / mr / client / HostSelector.java
1 /*******************************************************************************
2  *  ============LICENSE_START=======================================================
3  *  org.onap.dmaap
4  *  ================================================================================
5  *  Copyright © 2017 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  *        http://www.apache.org/licenses/LICENSE-2.0
11  *  
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  *  ============LICENSE_END=========================================================
18  *
19  *  ECOMP is a trademark and service mark of AT&T Intellectual Property.
20  *  
21  *******************************************************************************/
22 package com.att.nsa.mr.client;
23
24 import java.util.Collection;
25 import java.util.Iterator;
26 import java.util.LinkedList;
27 import java.util.Random;
28 import java.util.Set;
29 import java.util.TreeSet;
30 import java.util.Vector;
31 import java.util.concurrent.DelayQueue;
32 import java.util.concurrent.Delayed;
33 import java.util.concurrent.TimeUnit;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 public class HostSelector
38 {
39   private final TreeSet<String> fBaseHosts;
40   private final DelayQueue<BlacklistEntry> fBlacklist;
41   private String fIdealHost;
42   private String fCurrentHost;
43   private static final Logger log = LoggerFactory.getLogger(HostSelector.class);
44
45   public HostSelector(String hostPart)
46   {
47     this(makeSet(hostPart), null);
48   }
49
50   public HostSelector(Collection<String> baseHosts)
51   {
52     this(baseHosts, null);
53   }
54
55   public HostSelector(Collection<String> baseHosts, String signature)
56   {
57     if (baseHosts.isEmpty())
58     {
59       throw new IllegalArgumentException("At least one host must be provided.");
60     }
61
62     this.fBaseHosts = new TreeSet(baseHosts);
63     this.fBlacklist = new DelayQueue();
64     this.fIdealHost = null;
65
66     if (signature == null) {
67       return;
68     }
69     int index = 0 ;
70     int value = signature.hashCode();
71     if(value!=0) {
72     index = Math.abs(value) % baseHosts.size();
73     }
74     Iterator it = this.fBaseHosts.iterator();
75     while (index-- > 0)
76     {
77       it.next();
78     }
79     this.fIdealHost = ((String)it.next());
80   }
81
82   public String selectBaseHost()
83   {
84     if (this.fCurrentHost == null)
85     {
86       makeSelection();
87     }
88     return this.fCurrentHost;
89   }
90
91   public void reportReachabilityProblem(long blacklistUnit, TimeUnit blacklistTimeUnit)
92   {
93     if (this.fCurrentHost == null)
94     {
95       log.warn("Reporting reachability problem, but no host is currently selected.");
96     }
97
98     if (blacklistUnit > 0L)
99     {
100       for (BlacklistEntry be : this.fBlacklist)
101       {
102         if (be.getHost().equals(this.fCurrentHost))
103         {
104           be.expireNow();
105         }
106       }
107
108       LinkedList devNull = new LinkedList();
109       this.fBlacklist.drainTo(devNull);
110
111       if (this.fCurrentHost != null)
112       {
113         this.fBlacklist.add(new BlacklistEntry(this.fCurrentHost, TimeUnit.MILLISECONDS.convert(blacklistUnit, blacklistTimeUnit)));
114       }
115     }
116     this.fCurrentHost = null;
117   }
118
119   private String makeSelection()
120   {
121     TreeSet workingSet = new TreeSet(this.fBaseHosts);
122
123     LinkedList devNull = new LinkedList();
124     this.fBlacklist.drainTo(devNull);
125     for (BlacklistEntry be : this.fBlacklist)
126     {
127       workingSet.remove(be.getHost());
128     }
129
130     if (workingSet.isEmpty())
131     {
132       log.warn("All hosts were blacklisted; reverting to full set of hosts.");
133       workingSet.addAll(this.fBaseHosts);
134       this.fCurrentHost = null;
135     }
136
137     String selection = null;
138     if ((this.fCurrentHost != null) && (workingSet.contains(this.fCurrentHost)))
139     {
140       selection = this.fCurrentHost;
141     }
142     else if ((this.fIdealHost != null) && (workingSet.contains(this.fIdealHost)))
143     {
144       selection = this.fIdealHost;
145     }
146     else
147     {
148       int index = 0;
149       int value = new Random().nextInt();
150       Vector v = new Vector(workingSet);
151       if(value!=0) {
152       index = Math.abs(value) % workingSet.size();
153       }
154       selection = (String)v.elementAt(index);
155     }
156
157     this.fCurrentHost = selection;
158     return this.fCurrentHost;
159   }
160
161   private static Set<String> makeSet(String s)
162   {
163     TreeSet set = new TreeSet();
164     set.add(s);
165     return set; }
166
167   private static class BlacklistEntry implements Delayed {
168     private final String fHost;
169     private long fExpireAtMs;
170
171     public BlacklistEntry(String host, long delayMs) {
172       this.fHost = host;
173       this.fExpireAtMs = (System.currentTimeMillis() + delayMs);
174     }
175
176     public void expireNow()
177     {
178       this.fExpireAtMs = 0L;
179     }
180
181     public String getHost()
182     {
183       return this.fHost;
184     }
185
186     public int compareTo(Delayed o)
187     {
188       Long thisDelay = Long.valueOf(getDelay(TimeUnit.MILLISECONDS));
189       return thisDelay.compareTo(Long.valueOf(o.getDelay(TimeUnit.MILLISECONDS)));
190     }
191
192     public long getDelay(TimeUnit unit)
193     {
194       long remainingMs = this.fExpireAtMs - System.currentTimeMillis();
195       return unit.convert(remainingMs, TimeUnit.MILLISECONDS);
196     }
197   }
198 }