I am working on a project in which I construct a URL with a valid hostname (but not a blocked hostname) and then execute that URL using RestTemplate
from my main application thread. I also have a single background thread in my application which parses the data from the url and extracts the block list of hostnames from it.
So if any block list of hostnames is present, then I won't make a call to that hostname from the main thread and I will try making a call to another hostname.
By block list, I mean whenever any server is down, its hostname is on the block list.
Below is my background thread code. It will get the data from my service URL and keep on running every 10 minutes once my application has started up. It will then parse the data coming from the URL and store it in a ClientData
class variable -
public class TempScheduler {
// .. scheduledexecutors service code to start the background thread
// call the service and get the data and then parse
// the response.
private void callServiceURL() {
String url = "url";
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
parseResponse(response);
}
// parse the response and store it in a variable
private void parseResponse(String response) {
//...
// get the block list of hostnames
Map<String, List<String>> coloExceptionList = gson.fromJson(response.split("blocklist=")[1], Map.class);
List<String> blockList = new ArrayList<String>();
for(Map.Entry<String, List<String>> entry : coloExceptionList.entrySet()) {
for(String hosts : entry.getValue()) {
blockList.add(hosts);
}
}
// store the block list of hostnames which I am not supposed to make a call
// from my main application
ClientData.replaceBlockedHosts(blockList);
}
}
Below is my ClientData
class. replaceBlockedHosts
method will only be called by a background thread meaning only one writer. But isHostBlocked
will be called by main application threads multiple times to check whether the hostname is blocked or not. And also blockHost
method might be called from catch block
multiple times to add the down host in the blockedHosts
list so I need to make sure all the read threads can see the consistent data and are not making calls to that down host, instead they are making calls to next host in the hostnames linked list.
public class ClientData {
// .. some other variables here which in turn used to decide the list of hostnames
private static final AtomicReference<ConcurrentHashMap<String, String>> blockedHosts =
new AtomicReference<ConcurrentHashMap<String, String>>(new ConcurrentHashMap<String, String>());
public static boolean isHostBlocked(String hostName) {
return blockedHosts.get().containsKey(hostName);
}
public static void blockHost(String hostName) {
blockedHosts.get().put(hostName, hostName);
}
public static void replaceBlockedHosts(List<String> hostNames) {
ConcurrentHashMap<String, String> newBlockedHosts = new ConcurrentHashMap<>();
for (String hostName : hostNames) {
newBlockedHosts.put(hostName, hostName);
}
blockedHosts.set(newBlockedHosts);
}
}
And below is my main application thread code in which I am finding all the hostnames on which I can make a call and then iterate the hostnames
list to make a call.
If that hostname
is null or in the block list category, then I won't make a call to that particular hostname and will try next hostname in the list.
@Override
public DataResponse call() throws Exception {
List<String> hostnames = new LinkedList<String>();
// .. some separate code here to populate the hostnames list
// from ClientData class
for (String hostname : hostnames) {
// If host name is null or host name is in block list category, skip sending request to this host
if (hostname == null || ClientData.isHostBlocked(hostname)) {
continue;
}
try {
String url = generateURL(hostname);
response = restTemplate.getForObject(url, String.class);
break;
} catch (RestClientException ex) {
// does this call will be thread safe for blockHost method in ClientData class?
ClientData.blockHost(hostname);
}
}
}
I don't need to make a call to the hostname whenever it is down from the main thread. And my background thread gets these detail from one of my service, whenever any server is down, it will have the list of hostnames which are block hosts and whenever they are up, that list will get updated.
And also, whenever any RestClientException
is being thrown, I will add that hostname in the blockedHosts
concurrentmap since my background thread is running every 10 minutes so that map won't have this hostname until 10 minutes is done. And whenever this server came back up, my background will update this list automatically.
Should I have fully atomic operations so that all the main threads should see consistent data from isHostBlocked
method?
I'm opting for code review to see whether full atomicity is maintained or not in my above code.