dimanche 19 février 2017

How to update map only once during the first call to Singleton?

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. And all the servers will give same exact response in the same format.

Below is my singleton class which on the first call in the constructor of ProcConfig, calls loadConfig() method to initialize everything and then check whether addressToIdMapping map has one entry in it or not. If it is not there then it throws exception. After that it starts a background thread where every 30 minutes it will call loadConfig() method to update addressToIdMapping and processToTcpMapping map.

public class ProcConfig {
  private static final Splitter SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
  private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
  private final Map<String, Short> addressToIdMapping = new ConcurrentHashMap<>();
  private final Map<DatacenterEnum, List<String>> processToTcpMapping = new ConcurrentHashMap<>();

  private static class Holder {
    private static final ProcConfig INSTANCE = new ProcConfig();
  }

  public static ProcConfig getInstance() {
    return Holder.INSTANCE;
  }

  private ProcConfig() {
    loadConfig();
    checkArgument(!MapUtils.isEmpty(addressToIdMapping), "cannot find id, found '%s'.", addressToIdMapping);
    scheduler.scheduleAtFixedRate(new Runnable() {
      public void run() {
        try {
          loadConfig();
        } catch (Exception ex) {
          // log error
        }
      }
    }, 60, 30, TimeUnit.MINUTES);
  }

  private void loadConfig() {
    // current ipAddress where the program is running
    Optional<String> ipAddress = Utils.getIPAddress();
    List<String> servers = getServers();
    for (String server : servers) {
      try {
        String response = HttpClient.getInstance().execute(makeUrl(server));
        if (Strings.isNullOrEmpty(response) || response.equalsIgnoreCase("KEEP OUT")
            || response.equalsIgnoreCase("NOTHING FOUND")) {
          continue;
        }
        parseConfig(response, ipAddress.get());
        break;
      } catch (Exception ex) {
        // log error
      }
    }
  }

  private void parseConfig(final String response, final String ipAddress) throws IOException {
    List<String> lines = IOUtils.readLines(new StringReader(response));
    for (String line : lines) {
      if (line.contains(ipAddress)) {
        List<String> config = SPLITTER.splitToList(line);
        Short id = Short.parseShort(config.get(2));
        // this map will only have one entry for the ip address where it is running
        addressToIdMapping.put(ipAddress, id);
      } else if (line.contains("process_")) {
        List<String> config = SPLITTER.splitToList(line);
        String procAddr = 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 colo = Utils.isProd() ? DatacenterEnum.name((byte) datacenter) : DatacenterEnum.DEV;
        List<String> address = makeTcpAddress(procAddr, colo, portNumber, numberOfPorts);
        processToTcpMapping.put(colo, address);
      }
    }
  }

  public Optional<Short> getId() {
    Optional<String> ipAddress = Utils.getIPAddress();
    return Optional.fromNullable(addressToIdMapping.get(ipAddress.get()));
  }
}

Now my question is: I want to update my addressToIdMapping map only once during the first call to the singleton so that getId() method always return what was there during the first update in the map. But right now it will return whatever is there in the map after every update of 30 minutes. For example: when the first time this class is called, it will update the map so I want to keep the same value in addressToIdMapping map forever till the program is running. Is this possible to do?

In general addressToIdMapping map will always have only one entry for the IP Address where the code is running in. And I am fine if processToTcpMapping map getting updated every 30 minutes.

Aucun commentaire:

Enregistrer un commentaire