-
Feature Request
-
Resolution: Obsolete
-
Major
-
None
-
None
-
None
-
None
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 ):
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; } } }
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:
void addContext(@Observes final AfterBeanDiscovery abd) {
context = abd.contextBuilder().concurrent().scope(TheScoped.class).create();
abd.addContext(context);
}
The CDI project is part ofJakarta and uses GitHub issues as it's issue tracking system.
Therefore, all issues in CDI JIRA project are being bulk-closed as described in this GitHub issue.
If you feel like this particular issue deserves ongoing discussion, investigation or fixes in CDI/CDI TCK, please create a new issue under GitHub repository and include a link to this JIRA.
For specification related question/issues, please use - https://github.com/eclipse-ee4j/cdi/issues
For CDI TCK related questions/issues, please use - https://github.com/eclipse-ee4j/cdi-tck/issues