I have to parse a response after making HTTP call on the server. If the response is not successful then try another server otherwise parse the successful response and populate two ConcurrentHashMap
and break out of for loop. Also all the servers will give same exact response.
Below is how my response will look like after executing an URL on any of the server. Each line in the response separated by new line (\n).
11.111.101.203,1,2,9096,4,process_1234
11.111.101.204,1,2,9096,4,process_7110
11.111.101.205,2,2,9096,4,process_7890
11.111.101.206,2,2,9096,4,process_1110
11.124.182.109,1,1,29000,4,client_ty129
12.265.200.100,4,1,29000,4,client_oiu654
13.451.453.1136,3,625,20002,2,tr_old_dev
15.223.114.111,3,608,20002,2,jh7_new_laptop
29.198.223.265,2,441,9094,1,a3_new_proc
11.111.101.203,1,5,33000,2,device_09876asd
11.111.101.204,2,7,33000,2,device_kjuy6543
Below are the types of category we can have in the above response and each line will always be in that same format. If by any chance it is not, then we can throw an exception.
- First: A line which will have "process_"
- Second: A line which will have "client_"
- Third: A line which will have "device_"
- Fourth: A line which does not have all above three.
Now I need to populate two maps basis on above response:
If a line that follows fourth category condition above, I will split the line on "," and get 0 index which is "ip address" and second index which is "appId" and populate it in a "ipAddressToAppidMapping" map. For example in below line:
13.451.453.1136,3,625,20002,2,tr_old_dev
ipAddressToAppidMapping
map will have13.451.453.1136
as key and value as625
and I will do same things for other lines which follows same pattern.If a line that follows first category condition, I will again split the line on "," and and get 0 index which is "ip address" and first index which is "datacenter number" and third index which is "portNumber" and fourth index which is "numberOfPorts". Now basis on "datacenter number", I will locate actual enum for that from Datacenter enum class which will be my
ipAddressToTcpAddress
map key. Now I have to make aList<String>
which will be value for my map given a "numberOfPorts", "portNumber" and "ip address". For example for below line:11.111.101.203,1,2,9096,4,process_1234
My list will look like this after using "numberOfPorts as 4":
[tcp://11.111.101.203:9099, tcp://11.111.101.203:9098, tcp://11.111.101.203:9097]
As you can see it will not contain first port number which is 9096 so it will have 9097,9098,9099 and it will skip first port whatever is there on the line and use remaining "numberOfPorts -1" always.
Below is my code where loadConfig
method will be called by a background thread every 15 minutes to populate these two maps and then from multiple threads, I will access these maps through getters to get data from them so it has to be thread safe and that's why I am using ConcurrentHashMap
.
private static final Splitter SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
private final Map<String, Short> ipAddressToAppidMapping = new ConcurrentHashMap<>();
private final Map<DatacenterEnum, List<String>> ipAddressToTcpAddress = new ConcurrentHashMap<>();
private void loadConfig() {
List<String> servers = getServers();
for (String server : servers) {
try {
String response = HttpClient.getInstance().execute(makeUrl(server));
if (Strings.isNullOrEmpty(response) || response.equalsIgnoreCase("TIMED OUT")
|| response.equalsIgnoreCase("NO DATA FOUND")) {
continue;
}
parseConfig(response);
break;
} catch (Exception ex) {
logger.logError("error= ", ExceptionUtils.getStackTrace(ex), ", server= ", server);
}
}
}
private void parseConfig(final String response) throws IOException {
List<String> lines = IOUtils.readLines(new StringReader(response));
for (String line : lines) {
if (line.trim().length() > 0 && !line.startsWith("#") && !line.contains("process_")
&& !line.contains("client_") && !line.contains("device_")) {
List<String> config = SPLITTER.splitToList(line);
String ipAddress = config.get(0);
Short appId = Short.parseShort(config.get(2));
ipAddressToAppidMapping.put(ipAddress, appId); // populate first map
} else if (line.trim().length() > 0 && !line.startsWith("#") && line.contains("process_")) {
List<String> config = SPLITTER.splitToList(line);
String ipAddress = config.get(0);
int datacenter = Integer.parseInt(config.get(1));
int portNumber = Integer.parseInt(config.get(3));
int numberOfPorts = Integer.parseInt(config.get(4));
DatacenterEnum dc = Utils.isProduction() ? DatacenterEnum.name((byte) datacenter) : DatacenterEnum.DEV;
List<String> address = makeTcpAddress(ipAddress, dc, portNumber, numberOfPorts);
ipAddressToTcpAddress.put(dc, address); // populate second map
}
}
}
private List<String> makeTcpAddress(String key, DatacenterEnum dc, int portNumber, int numberOfPorts) {
List<String> address = ipAddressToTcpAddress.get(dc);
if (CollectionUtils.isEmpty(address)) {
address = new ArrayList<>();
}
for (int num = (portNumber - 1) + numberOfPorts; num >= portNumber + 1; num--) {
address.add("tcp://" + key + ":" + num);
}
return address;
}
If the code is not running in production, then my key is always DatacenterEnum.DEV
for ipAddressToTcpAddress
map otherwise it will be always whatever datacenter is there in the line.. And basis on that I keep appending tcpaddress in a List. Also I have access to guava library in my project so I am using it here to split the line on comma. May be I can use same thing to split the response on a new line instead of using IOUtils.readLines
.
I have my above code working fine. Wanted to see if there is any better way to do this efficiently so opting for code review.