Question

Below is my factory code which starts the background thread TempScheduler -

public class TempClientFactory {

    public static IClient getInstance() {
    new TempScheduler().startScheduler();
    return ClientHolder.INSTANCE;
    }

    private static class ClientHolder {
        private static final TempClient INSTANCE = new TempClient();
    }
} 

Now customer will call our code using the above factory like this. They will get our Client instance only once and then use that instance going forward to make a call to a read method in my implementation -

IClient client = TempClientFactory.getInstance();
String response = client.read(userId);

And below is my background thread code which will get the data from the URL, parse it and store it in a class variable -

public class TempScheduler {

    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

        public void startScheduler() {
            final ScheduledFuture<?> taskHandle = scheduler.scheduleAtFixedRate(new Runnable() {
                public void run() {
                try {
                    callServers();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                }
            }, 0, 10, TimeUnit.MINUTES);
        }
    }

    // call the servers and get the data and then parse 
    // the response.
    private void callServers() {
        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) {
        //...       
        ConcurrentHashMap<String, Map<Integer, String>> primaryTables = null;

        //...

        // store the data in ClientData class variables which can be
        // used by other threads
        ClientData.setPrimaryMapping(primaryTables);
    }
}

After parsing the data coming from the URL, my above background thread will store the result in my class ClientData in its variables by using its setters. Below is my ClientData class.

public class ClientData {

    private static final AtomicReference<Map<String, Map<Integer, String>>> primaryMapping = new AtomicReference<>();

    public static Map<String, Map<Integer, String>> getPrimaryMapping() {
        return primaryMapping.get();
    }

    public static void setPrimaryMapping(Map<String, Map<Integer, String>> map) {
        primaryMapping.set(map);
    }
}

Problem Statement:-

Now the interesting part comes, As you can see customer will call our code using the above factory and as soon as they will make a call to getInstance() method, my background thread gets started up which will fetch the data from the URL and then parse it and store it in a class variable. But after the getInstance() method is executed they will make a call to read method in my Client code instantly..

And then I am supposed to use the variable which was set by my background thread in my implementation code. My implementation code which has read method uses CallableTaks and future.get so it will automatically comes to call method below. In the below class I am supposed to use the variable value which was set by my background thread. I need to use getPrimaryMapping method to get the value in my below code.

public class ClientTask implements Callable<String> {

    private String userId = null;

    public ClientTask(String userId) {
    this.userId = userId;
    }

    @Override
    public String call() throws Exception {

    //.....

    String hostname = ClientData.getPrimaryMapping("some_string").get(some_number);

    //....

    }   
}

The problem with this approach is, as soon the customer calls our factory for the first time to get the instance, then it will start the background thread which will fetch the data from the URL and parse it and store the data in a class variable but it will call the read method of my implementation class instantly..

Now it might be possible that for the first time, my background thread is still parsing the data but the actual call has come inside the call method and then it will try to get the data from getPrimaryMapping method but it won't have anything, right? Why bcoz it is still parsing the data.. So how do I make sure that for the first time whenever customer is calling our code, once the parsing is done, then only allow to get the variable value in the call method.

And then the second time will be fine.. As the data will be in memory, only the first time is the problem..

Is there any way to do this?

Was it helpful?

Solution

It's not an elegant solution, but a CountDownLatch could do the trick:

public class ClientData {

    private static final AtomicReference<Map<String, Map<Integer, String>>> primaryMapping = new AtomicReference<>();
    private static final CountDownLatch firstSet = new CountDownLatch(1);

    public static Map<String, Map<Integer, String>> getPrimaryMapping() {

        try { firstSet.await(); } catch (Exception ignored) {}
        return primaryMapping.get();
    }

    public static void setPrimaryMapping(Map<String, Map<Integer, String>> map) {

        primaryMapping.set(map);
        firstSet.countDown();
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top