Application Context
Statically using Context (Map based) in Kotlin/Java
Overview
Application context util is a simple kotlin based library that provides for setting up and using simple key/value context in our application. Sure key/value pairs can be managed by a simple Map but then passing that map all around the code methods is very cumbersome.
What this library aims is to enable to create and access context anywhere in code statically without needing to pass around in methods meanwhile providing thread/coroutine safety as well.
Central interface of this library is ApplicationContext
This allows to store values against named Keys which also store value type information
Usage
Maven dependency
<dependency>
<groupId>io.github.funofprograming</groupId>
<artifactId>application-context-util</artifactId>
<version>LATEST</version>
</dependency>
Versions
Check for latest versions here: Releases
Code
Globally available context in all threads/coroutine
import io.github.funofprograming.context.impl.*
val contextName: String = "TestGlobalApplicationContext"
val valueSetInThread1 = "Value T1"
val validKey: Key<String> = Key.of("ValidKey", String::class.java)
val def1 = launch {
val globalContext = getGlobalContext(contextName)
globalContext?.add(validKey, valueSetInThread1)
}
delay(1000)
val def2 = async {
val globalContext = getGlobalContext(contextName)
return@async globalContext?.fetch(validKey)
}
println(valueSetInThread1 == def2.await())
import io.github.funofprograming.context.impl.*;
import java.util.concurrent.*;
public class TestGlobalApplicationContext{
public static void main(String[] args) {
String contextName = "TestGlobalApplicationContext";
Key<String> validKey = Key.Companion.of("ValidKey", String.class);
ThreadPoolExecutor executorService = new ThreadPoolExecutor(1, 1, 1L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
String valueSetInThread1 = "Value T1";
CompletableFuture.runAsync(()->{
ApplicationContext globalContext = ApplicationContextHoldersKt.getGlobalContext(contextName);
globalContext.add(validKey, valueSetInThread1);
});
Thread.sleep(1000);
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{
ApplicationContext globalContext = ApplicationContextHoldersKt.getGlobalContext(contextName);
return globalContext.fetch(validKey);
});
Thread.sleep(1000);
System.out.println(valueSetInThread1.equals(future2.get()));
}
}
Above snippet prints:
true
ThreadLocal available context in current thread/coroutine
import io.github.funofprograming.context.impl.*
val contextName: String = "TestThreadLocalApplicationContext"
val valueSetInThread1 = "Value T1"
val validKey: Key<String> = Key.of("ValidKey", String::class.java)
val cs1 = initCoroutineScopeForApplicationContext() //notice the initialization of coroutine scope in case we plan to use a thread local context inside a coroutine
val def1 = cs1.async {
val localContext = getThreadLocalContext(contextName)
localContext?.add(validKey, valueSetInThread1)
return@async getThreadLocalContext(contextName)?.fetch(validKey)
}
delay(1000)
val cs2 = initCoroutineScopeForApplicationContext() //notice the initialization of coroutine scope in case we plan to use a thread local context inside a coroutine
val def2 = cs2.async {
val localContext = getThreadLocalContext(contextName)
return@async localContext?.fetch(invalidKey)
}
println(def2.await() == null)
println(valueSetInThread1 == def1.await())
import io.github.funofprograming.context.impl.*;
import java.util.concurrent.*;
public class TestThreadLocalApplicationContext{
public static void main(String[] args) {
String contextName = "TestThreadLocalApplicationContext";
Key<String> validKey = Key.Companion.of("ValidKey", String.class);
ThreadPoolExecutor executorService = new ThreadPoolExecutor(1, 1, 1L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
String valueSetInThread1 = "Value T1";
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(()->{
ApplicationContext localContext = ApplicationContextHoldersKt.getThreadLocalContext(contextName);
localContext.add(validKey, valueSetInThread1);
return ApplicationContextHoldersKt.getThreadLocalContext(contextName).fetch(validKey);
}, executorService);
Thread.sleep(1000);
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{
ApplicationContext localContext = ApplicationContextHoldersKt.getThreadLocalContext(contextName);
return localContext.fetch(validKey);
}, executorService);
System.out.println(future2.get() == null);
System.out.println(valueSetInThread1.equals(future1.get()));
}
}
Above snippet prints:
true
true
Also, the context supports restricting keys of context to some predetermined set as follows:
import io.github.funofprograming.context.impl.*
val contextName: String = "TestApplicationContext"
val validKey: Key<String> = Key.of("ValidKey", String::class.java)
val invalidKey: Key<String> = Key.of("InvalidKey", String::class.java)
val permittedKeys: Set<Key<*>> = setOf(validKey)
val globalContext = getGlobalContext(contextName, permittedKeys)
globalContext?.add(validKey, valueSetInThread1)
globalContext?.fetch(invalidKey) //throws InvalidKeyException here since invalidKey is not part of permittedKeys
val localContext = getThreadLocalContext(contextName, permittedKeys)
localContext?.add(validKey, valueSetInThread1)
localContext?.fetch(invalidKey) //throws InvalidKeyException here since invalidKey is not part of permittedKeys
import io.github.funofprograming.context.impl.*;
import java.util.concurrent.*;
public class TestApplicationContext{
public static void main(String[] args) {
String contextName = "TestApplicationContext";
Key<String> validKey = Key.Companion.of("ValidKey", String.class);
String valueSetInThread1 = "Value T1";
Key<String> invalidKey = Key.Companion.of("InvalidKey", String.class);
Set<Key<?>> permittedKeys = new HashSet<>(Arrays.asList(validKey));
ApplicationContext globalContext = ApplicationContextHoldersKt.getGlobalContext(contextName, permittedKeys);
globalContext.add(validKey, valueSetInThread1);
globalContext.fetch(invalidKey); //throws InvalidKeyException here since invalidKey is not part of permittedKeys
ApplicationContext localContext = ApplicationContextHoldersKt.getThreadLocalContext(contextName, permittedKeys);
localContext.add(validKey, valueSetInThread1);
localContext.fetch(invalidKey); //throws InvalidKeyException here since invalidKey is not part of permittedKeys
}
}
Compatibility matrix
Application Context Util | Java | Kotlin-Std | Kotlin-Coroutines |
---|---|---|---|
1.0.0 | 21 | 2.1.10 | 1.10.1 |
License
Application context util is is released under version 2.0 of the Apache License.