]
John Ament commented on CDI-666:
--------------------------------
+1 to doing something like this. This is effectively every context implementation
I've seen.
simplify context implementation
-------------------------------
Key: CDI-666
URL:
https://issues.jboss.org/browse/CDI-666
Project: CDI Specification Issues
Issue Type: Feature Request
Reporter: Romain Manni-Bucau
Today implementing a context leads to implementing Context or AlterableContext
interface.
Then for Thread Local related scopes it leads to handle activation, deactivation (thread
local set/remove) and delegation of the context to a sub context (implementing or not
Context interface). Finally it requires a destroyEnd() method destroying all instances.
It also requires the instance tracking and instantiation correctly (with lock or not
depending the usage).
Here is a schematic implementation (easier than previous paragraph to read ;)):
{code}
public class CommandContext implements AlterableContext {
private final ThreadLocal<Delegate> delegate = new ThreadLocal<>();
@Override
public Class<? extends Annotation> getScope() {
return CommandScoped.class;
}
@Override
public <T> T get(final Contextual<T> component, final
CreationalContext<T> creationalContext) {
return delegate.get().get(component, creationalContext);
}
@Override
public <T> T get(final Contextual<T> component) {
return delegate.get().get(component);
}
@Override
public boolean isActive() {
final Delegate instance = delegate.get();
if (instance == null) {
delegate.remove();
return false;
}
return instance.isActive();
}
@Override
public void destroy(final Contextual<?> contextual) {
delegate.get().destroy(contextual);
}
public Delegate newInstance() {
return new Delegate();
}
public void withContext(final Delegate value, final Runnable task) {
delegate.set(value);
try {
task.run();
} finally {
delegate.remove();
}
}
public void destroy(final Delegate delegate) {
new
ArrayList<>(delegate.componentInstanceMap.keySet()).forEach(delegate::destroy);
}
public class Delegate implements AlterableContext {
private final Map<Contextual<?>, BeanInstanceBag<?>>
componentInstanceMap = new HashMap<>();
private Delegate() {
// no-op
}
@Override
public Class<? extends Annotation> getScope() {
return CommandScoped.class;
}
@Override
public <T> T get(final Contextual<T> component, final
CreationalContext<T> creationalContext) {
final BeanInstanceBag value = new
BeanInstanceBag<>(creationalContext);
return (T) ofNullable(componentInstanceMap.putIfAbsent(component,
value)).orElse(value).create(component);
}
@Override
public <T> T get(final Contextual<T> component) {
return (T) ofNullable(componentInstanceMap.get(component)).map(b ->
b.beanInstance).orElse(null);
}
@Override
public void destroy(final Contextual<?> contextual) {
ofNullable(componentInstanceMap.remove(contextual)).filter(b ->
b.beanInstance != null).ifPresent(b -> {
final Contextual c = contextual;
c.destroy(b.beanInstance, b.beanCreationalContext);
b.beanCreationalContext.release();
});
}
@Override
public boolean isActive() {
return true;
}
}
@RequiredArgsConstructor
private static class BeanInstanceBag<T> {
private final CreationalContext<T> beanCreationalContext;
private T beanInstance;
public T create(final Contextual<T> contextual) {
if (beanInstance == null) {
beanInstance = contextual.create(beanCreationalContext);
}
return beanInstance;
}
}
}
{code}
This is a lot for finally just define (what the user wants) when a context is active.
Therefore it would be awesome is the spec would provide a context builder. Raw api
proposal could be:
{code}
void addContext(@Observes final AfterBeanDiscovery abd) {
context = abd.contextBuilder().concurrent().scope(TheScoped.class).create();
abd.addContext(context);
}
{code}