1

there's a session scoped bean 'Identity' which I injected in a @Stateless bean which implements Runnable:

@Stateless
@LocalBean
public class Test implements Runnable {
    @Inject
    Identity identity;
    @Inject
    Logger log;

    @Override
    public void run() {
        log.warn("Test: " + this + " " + identity.getAccount().getId());
    }
}

There's also a bean which invokes the above Runnable asynchronously:

@Stateless
@LocalBean
public class BeanContextExecutor implements Executor {
    @Asynchronous
    @Override
    public void execute(Runnable command) {
        command.run();
    }
}

and finally, the invocation looks like this:

@Stateless
public class OtherBean {
    @Inject
    BeanContextExecutor executor;
...
        executor.execute(command);
...
}

When running this I'm getting the following error:

...
Caused by: org.jboss.weld.context.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.SessionScoped
...

Is there any way to propagate the SessionContext to the background thread?

I also tried to submit this Runnable to ManagedExecutorService and even to create a proxy for it with a ContextService and submit a proxy but still getting the same error.

Thanks for any help with this!

Oleg Khaschansky
  • 296
  • 3
  • 10

1 Answers1

0

As a workaround in BeanContextExecutor I used BoundSessionContext to create a dummy session context for a new thread and also had to manually copy the required session bean to make its state available in the background thread:

@Inject
BoundSessionContext boundSessionContext;
// Backed by a ConcurrentHashMap<Runnable, Identity> which stores the state of the session scoped bean before spawning a new thread
@Inject
GlobalExecutionContext globalExecutionContext;
@Inject
Instance<Identity> identityInstance;
@Inject
Cloner cloner;
@Inject
private BeanManager beanManager;

@Asynchronous
@Override
public void execute(Runnable command) {
    HashMap<String, Object> storage = new HashMap<>();
    boundSessionContext.associate(storage);
    boundSessionContext.activate();

    Identity identity = globalExecutionContext.remove(command);
    Bean<Identity> bean = (Bean<Identity>) beanManager.resolve(beanManager.getBeans(Identity.class));
    Identity localIdentity = beanManager.getContext(bean.getScope()).get(bean, beanManager.createCreationalContext(bean));

    cloner.copyPropertiesOfInheritedClass(identity, localIdentity);

    command.run();

    boundSessionContext.invalidate();
    boundSessionContext.deactivate();
    boundSessionContext.dissociate(storage);
}

The example is intended to demonstrate the approach, it's possible to improve it like support passing beans of an arbitrary type. But I don't like this approach at all. There should be a better solution for context propagation problem.

Update: I'd like to keep the caller identity in a background thread even if initial session is expired, it looks like the above solution is suitable for this.

Oleg Khaschansky
  • 296
  • 3
  • 10