Sunday, December 3, 2017

How to obtain the contents of a static ThreadLocal by subclassing the Thread class.

To thoroughly unit test a Spring @Async method, it was necessary to check the contents of a static ThreadLocal being used by the method after the method had finished executing.

Because the contents of a static ThreadLocal variable, unlike a normal static variable,  are not shared between thread executions even though the variable has the same object id in all execution contexts, it was not possible to call the @Async method and then just check the static ThreadLocal in the test Thread after the execution of the method had finished.

To access the contents of a static ThreadLocal used by the @Async method, it was necessary to subclass the Thread class and register the subclass with the ThreadFactory being used by the Spring AsyncTaskExecutor.

When the AsyncTaskExecutor needed a Thread to execute the @Async method, it instantiated one of the subclassed Threads and passed it a reference to a Runnable, which was the Runnable of the @Async method.

Eventaully when the AsyncTaskExecutor started the subclassed Thread,  the subclassed Thread first called the @Async method by calling "run" on the Runnable it had been passed during construction and then accessed the static ThreadLocal afterwards. The contents of the ThreadLocal was then passed back to the main Thread through a normal static variable.

To see the full code of what is shown below, please go here.

ThreadFactory threadFactory = new ThreadFactory() {
    @Override    public Thread newThread(Runnable r) {
        return new Thread(r) {
            @Override            public void run() {
                r.run();
                MyLocalContext.passBack = MyLocalContext.threadLocal.get();
            }
        };
    }
};

ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadFactory(threadFactory);

class MyLocalContext {
    public final static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    public static String passBack;
}

Saturday, August 26, 2017

Spring Boot Quiz 1 – 24.3 Application property file

What follows is a small quiz to determine  if one has understood the loading order of the application.yml file as describe in section 24.3 (Application property files) of the spring boot version 1.5.6.RELEASE documentation (shown below).


The object of the quiz is to determine the output of the HelloController, shown below, given the start command and the working directory.


@RestController
public class HelloController {

    @Value("${location}")
    private String location;

    @RequestMapping("/")
    public String index() {
        return String.format("Greetings from: %s", location);
    }
}


The next picture below shows the structure of the getting started spring boot repository (gs-spring-boot) as cloned from github and its location on my file system.

In various directories throughout the project, application.yml files have been created. In each file, one will find the absolute path of the file as an attribute called “location”.  For example, in the first top most application.yml file shown below in the picture, one will find: location:  
Z:\gs-spring-boot\application.yml


The first questions are based on the above file system structure. The gs-springboot.jar is a fat jar.

1.)

When started as follows:
Z:\>java -jar gs-spring-boot\complete\target\gs-springboot.jar


The loaded application.yml is:
Greetings from: Z:\gs-spring-boot\complete\src\main\resources\config\application.yml

Location described by line 3. 

Remember that the files in src\main\resources are copied into the classpath root of the resulting jar produced in the target directory. Locations 1 and 2 failed therefore 3 was used.


2.)

When started as follows:
Z:\gs-spring-boot>java -jar complete\target\gs-springboot.jar


The loaded application.yml is:
Greetings from: Z:\gs-spring-boot\config\application.yml

Location described by line 1. The config subdirectory  of the current directory.


3.)

When started as follows:
Z:\gs-spring-boot\complete>java  -jar target\gs-springboot.jar 


The loaded application.yml is:
Greetings from: Z:\gs-spring-boot\complete\config\application.yml

Location described by line 1. The config subdirectory  of the current directory.


4.)

When started as follows:
Z:\gs-spring-boot\complete\target>java -jar gs-springboot.jar


The loaded application.yml is:
Greetings from: Z:\gs-spring-boot\complete\src\main\resources\config\application.yml

Location described by line 3. The classpath root config subdirectory.




5.)

When started as follows:
Z:\gs-spring-boot>java -jar complete\target\gs-springboot.jar


The loaded application.yml is:
Greetings from: Z:\gs-spring-boot\application.yml

Location described by line 2. 1 failed there the properties file in current directory was loaded.




6.)

When started as follows:
Z:\gs-spring-boot>java -jar complete\target\gs-springboot.jar


The loaded application.yml is (line 2):
Greetings from: Z:\gs-spring-boot\complete\ src\main\resources\application.yml

Location described by line 4. 1, 2 and 3 failed.




7.)

When started as follows:
Z:\gs-spring-boot>java -jar complete\target\gs-springboot.jar  --spring.profiles.active=dev


The loaded application.yml is:
Greetings from: Z:\gs-spring-boot\config\application-dev.yml

Location described by line 1.


8.)

When started as follows:
Z:\gs-spring-boot\complete>java -jar target\gs-springboot.jar 
                                                             --spring.config.location=file:/gs-spring-boot/custom-config/


The loaded application.yml is:
Greetings from: Z:\gs-spring-boot\custom-config\application.yml


9.)

When started as follows:
Z:\gs-spring-boot\complete>java -jar target\gs-springboot.jar 
                                                             --spring.config.location=custom-config/ 
                                                             --spring.profiles.active=prod


The loaded application.yml is:
Greetings from: Z:\gs-spring-boot\complete\custom-config\application-prod.yml


10.)

When started as follows:
Z:\gs-spring-boot\complete>java -jar target\gs-springboot.jar 
                                                              --spring.config.location=custom-config
                                                               --spring.profiles.active=prod


The loaded application.yml is:
Greetings from: Z:\gs-spring-boot\complete\config\application.yml


11.

When started as follows:
java -jar target\gs-springboot.jar --spring.config.name=gs-spring-boot


Exception
Could not resolve placeholder 'location' in value "${location}"


12.

When started as follows:
Z:\gs-spring-boot\complete>java -jar target\gs-springboot.jar 
                                                              --spring.config.name=gs-spring-boot 
                                                              --spring.config.location=custom-config/


The loaded application.yml is:
Greetings from: Z:\gs-spring-boot\complete\custom-config\gs-spring-boot.yml