0

Below factory class is used to get the drivers for execution

public class DriverFactory {
    //holds the device config
    public static Map<String, String> devConfig = new ConcurrentHashMap<>();

    //other lines of code follow
}

This config is loaded in a junit class from an external data source as below:

@RunWith(ConcurrentTestRunner.class)
public class MyTestRunner{
    static final int THREAD_COUNT = 1;

    @ThreadCount(THREAD_COUNT) @Override @Test
    public void run(){
        // Devices class returns config for the device
        DriverFactory.devConfig = Devices.getConfig()
        //other lines of code to perform execution
    }
}

If device config is required in other class during execution, it is accessed like below:

public class MobileActions{
    public void swipe(){
        String pf = DriverFactory.devConfig.get("Platform");
        //other lines of code
    }
}

This approach of (having devConfig as static) works fine there is one thread. Now, to support parallel execution across device, if thread count is changed to 2, devConfig will always have the value set by 2nd thread.

In order to avoid this problem, if devConfig is made an non-static, we have to inject this variable in all other classes for e.g., in the above MobileActions class. Is there a way that this variable can remain static but still work during multithreaded execution(each thread should deal with it's own copy of devConfig). We also tried making this variable as ThreadLocal<>, but that didn't help either.

Any help is much appreciated! Thanks

2
  • which library did you import for @ThreadCount? and would you mind upload Devices.getConfig() snippet? Commented Aug 30, 2018 at 1:03
  • I'm using vmlens concurrent library: mvnrepository.com/artifact/com.vmlens/concurrent-junit Commented Aug 30, 2018 at 4:26

3 Answers 3

1

Make devConfig in DriverFactory private. Provide getters and setters to it. If you need it to be specific to thread, make it threadlocal.

public class DriverFactory {

    // holds the device config
    private static final ThreadLocal<Map<String, String>> devConfig = ThreadLocal
        .withInitial(ConcurrentHashMap::new);

    public static String getDevConfig(String key) {
        return this.devConfig.get().get(key);
    }

    public static void setDevConfig(Map<String, String> config) {
        this.devConfig.get().putAll(config);
   }
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks Chandra for reply. I had tried ThreadLocal approach, but problem with it is value will be accessible only in method of class(run() method in MyTestRunner) where value is set. If i try to access value in other method of the same class or different class, it will return null. For example: Inside run() method: DriverFactory.setDevConfig(Devices.getConfig()) DriverFactory.getDevConfig("Platform") returns ->Android, However if I put same line in MobileActions class it returns null
You will need to debug it. Try adding debug statements in getDevConfig and setDevConfig methods. Make sure to print the current thread using Thread.currentThread().getName()
I had tried this thing(Thread.currentThread().getId()) my idea was to get the current Thread Id and then put the device config object along with the ThreadId as the Key in some global Map. This way i can fetch the config object associated with ThreadId during entire execution. But the problem is subsequent classes(like MobileActions) where config needs to be fetched, doesn't know which thread is calling it's method. I got the stacktrace but that didn't help.If I can find some way to identify the calling Thread Id then probably this can be solved
0

You can wrap your devConfig into ConcurrentHashMap:

public class DriverFactory {
    //holds the device config
    private static final String CONFIG_KEY = "config_key";
    private static final ConcurrentHashMap<String, Map<String, String>> devConfig = new ConcurrentHashMap<>();

    public static Map<String, String> setDevConfig(Map<String, String> devConfig) {
        return DriverFactory.devConfig.putIfAbsent(CONFIG_KEY, devConfig);
    }

    public static Map<String, String> getDevConfig() {
        return DriverFactory.devConfig.get(CONFIG_KEY);
    }

    //other lines of code follow
}

@RunWith(ConcurrentTestRunner.class)
public class MyTestRunner{
    static final int THREAD_COUNT = 1;

    @ThreadCount(THREAD_COUNT) @Override @Test
    public void run(){
        // Devices class returns config for the device
        DriverFactory.setDevConfig(Devices.getConfig())
        //other lines of code to perform execution
    }
}

public class MobileActions{
    public void swipe(){
        String pf = DriverFactory.getDevConfig().get("Platform");
        //other lines of code
    }
}

But if you want to execute Devices.getConfig() once you should try java.util.concurrent.ConcurrentHashMap#computeIfAbsent or custom locking. Something like:

    public static Map<String, String> getDevConfig() {
        return DriverFactory.devConfig
                .computeIfAbsent(CONFIG_KEY, s -> Devices.getConfig());
    }

Comments

0

Finally found a way using this link:

https://dmitrykrivenko.blogspot.com/2016/07/threadlocal-and-inheritablethreadlocal.html

Used InheritableThreadLocal instead of ThreadLocal. In this way, I'm able to access the data in child threads as well now.

public class DriverFactory {

private static final ThreadLocal<Map<String, String>> driverData = new InheritableThreadLocal<>();


public static String getDriverData(String key){
    return driverData.get().get(key);
}   

public static void setDriverData(Map<String, String> driver){
    driverData.set(driver);
}

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.