]
George Gastaldi commented on CDI-666:
-------------------------------------
That looks cool. It could also introduce methods (using lamdas of course) to create and
destroy the context and another one to retrieve the requested object in this scope
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}