Is it possible to activate a session scope and a conversation scope from an existing query scope?

I have an @EJB

injected bean TransactionCompleteJob

. This bean has a @Asynchronous

method on it asyncCompleteTransaction(Integer transactionId)

.

When I try to use other nested beans and entities that have session scope or conversation scope in this method, I get the error:

WELD-001303: No active contexts for scope type javax.enterprise.context.ConversationScoped

So, I Injected weld BoundConversationScope

, BoundSessionScope

and BoundRequestScope

and activated them by creating an empty card for request data and an empty card for session data, as mentioned from the jboss documentation for welding :

The problem is that when I activate the query scope, I get another error message:

WELD-001304: More than one context active for scope type javax.enterprise.context.RequestScoped

I tried not to activate the request scope, but it seems like I am having a resource leak from everything that was in the scope of the actual request, in particular I have a JPA request EntityManager

. In particular, after completing the process, I see another message:

WELD-000019: Error destroying an instance org.hibernate.jpa.internal.EntityManagerImpl@5df070be of Producer Method [EntityManager] with qualifiers [@RequestScopey @Any] declared as [[BackedAnnotatedMethod] @Produces @RequestScoped @RequestScopey public packagename.entitymanager.EntityManagerProducer.createRequestScopedEntityManager()]

How can I start the context of a request context when I have one already active? Or start the session context context and conversation context? The context associated with the existing query context context? Alternatively, are there any better ways to get around this issue?

EDIT:

Is there a way to get RequestScope

from welding so I can deactivate it before starting my own? Or is there a way to run mine TransactionCompleteJob

asynchronously asynchronously without inserting it and calling the method @Asynchronous

?

+3


source to share


1 answer


I had more or less the same problem, but I took a different approach: I had @ConversationScoped

EntityManager

it injected into my repos, but then I needed to batch process where the ConversationContext dialog was not available and an exception was thrown when using my repository. Instead of trying to activate the ConversationContext where it was not intended to be used, I ended up implementing 2 new contexts (+ 1 interceptor):

  • the first one was ThreadContext ( @ThreadScoped

    ), which saved everything in Map

    in ThreadLocal

    (which is good for handling async) + 1 interceptor ( @ThreadContextual

    ) method to be used in my async / batch methods to activate this context for the duration of the call.
  • the second one was a little more complex: it was some kind of dynamic context that delegated the first active context in this order: ThreadContext, (NonTransient) ConversationContext, (NonTransient) ViewContext ( @ViewScoped

    from JSF 2.2), RequestContext. I named this context UnitOfWorkContext with the appropriate annotation @UnitOfWorkScoped

    . I annotated (multiple) beans that were meant to live in this context (for me it was just a method @Produces

    for mine EntityManager

    ).

It would seem difficult to implement all this, but it is not, the code was rather small. I will paste my code in 2-3 days if needed, as I don't have access to it at the moment.

UPDATE: Here is the code for the second context:

The following interface is used in addition to Context.isActive (). Sometimes, even if the context is active it doesn't mean that I want to use it, see below example.

public interface DynamicContextActivation {

    boolean isActive(Context context);
}

      

The following annotation should be placed in your new area

@Retention(RUNTIME)
@Target(ANNOTATION_TYPE)
public @interface DynamicScope {

    class DefaultActivation implements DynamicContextActivation {

        public boolean isActive(Context context) {
            return true;
        }
    }

    Class<? extends Annotation>[] value();

    Class<? extends DynamicContextActivation> activation() default DefaultActivation.class;
}

      

Implementing dynamic context

public class DynamicContext implements AlterableContext {

    private final BeanManager beanManager;
    private final DynamicContextActivation activation;
    private final Class<? extends Annotation> scope;
    private final Class<? extends Annotation>[] scopes;

    public DynamicContext(BeanManager beanManager, DynamicContextActivation activation, Class<? extends Annotation> scope, Class<? extends Annotation>[] scopes) {
        this.beanManager = beanManager;
        this.activation = activation;
        this.scope = scope;
        this.scopes = scopes;
    }

    public void destroy(Contextual<?> contextual) {
        Context context = getContext();
        if (context instanceof AlterableContext) {
            ((AlterableContext) context).destroy(contextual);
        }
    }

    public <T> T get(Contextual<T> contextual) {
        return getContext().get(contextual);
    }

    public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) {
        return getContext().get(contextual, creationalContext);
    }

    // Find the first active context
    private Context getContext() {
        for (Class<? extends Annotation> scope : this.scopes) {
            try {
                Context context = this.beanManager.getContext(scope);
                if (context.isActive() && this.activation.isActive(context)) {
                    return context;
                }
            } catch (ContextNotActiveException exception) {
                continue;
            }
        }
        return null;
    }

    public Class<? extends Annotation> getScope() {
        return this.scope;
    }

    public boolean isActive() {
        return getContext() != null;
    }
}

      



An extension that automatically registers dynamic context (add it to /META-INF/services/javax.enterprise.inject.spi.Extension

)

public class DynamicContextExtension implements Extension {

    private final Set<Class<? extends Annotation>> scopes = new HashSet<>();

    public void processBean(@Observes ProcessBean<?> bean) {
        Class<? extends Annotation> scope = bean.getBean().getScope();
        if (scope.isAnnotationPresent(DynamicScope.class)) {
            this.scopes.add(scope);
        }
    }

    public void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) {
        for (Class<? extends Annotation> scope : scopes) {
            DynamicScope dynamicScope = scope.getAnnotation(DynamicScope.class);
            try {
                // TODO use a managed DynamicContextActivation instead of instantiating it here
                DynamicContextActivation activation = dynamicScope.activation().newInstance();
                Context context = new DynamicContext(beanManager, activation, scope, dynamicScope.value());
                afterBeanDiscovery.addContext(context);
            } catch (InstantiationException | IllegalAccessException exception) {
                afterBeanDiscovery.addDefinitionError(exception);
            }
        }
    }
}

      

This allows delegates for ThreadScoped, (LongRunning) ConversationScoped, (NonTransient) ViewScoped, RequestScoped:

@Retention(RUNTIME)
@NormalScope(passivating = true) // must be true if any of the delegate context is passivation-capable
@DynamicScope(value = {ThreadScoped.class, ConversationScoped.class, ViewScoped.class, RequestScoped.class}, activation = UnitOfWorkActivation.class)
public @interface UnitOfWorkScoped {

    class UnitOfWorkActivation implements DynamicContextActivation {

        public boolean isActive(Context context) {
            if (context.getScope().equals(ConversationScoped.class)) {
                // I only want long-running conversations here because in JSF there
                // is always a transient conversation per request and it could take
                // precedence over all other scopes that come after it
                return !CDI.current().select(Conversation.class).get().isTransient();
            }
            if (context.getScope().equals(ViewScoped.class)) {
                // Storing things in view scope when the view is transient gives warnings
                return !FacesContext.getCurrentInstance().getViewRoot().isTransient();
            }
            return true;
        }
    }
}

      

An EntityManager

producer that gives @UnitOfWorkScoped

EntityManager

s:

@Stateful // it could work without @Stateful (but Serializable) but I haven't tested enough
@UnitOfWorkScoped
public class EntityManagerProducer {

    @PersistenceContext(type = EXTENDED)
    private EntityManager entityManager;

    @Produces
    @UnitOfWorkScoped
    @TransactionAttribute(NOT_SUPPORTED)
    public EntityManager entityManager() {
        return entityManager;
    }
}

      

There is certainly room for improvement, so feel free to give your feedback.

UPDATE 2: It would be nice to replace DynamicContextActivation with EL expressions

@Retention(RUNTIME)
@NormalScope(passivating = true)
@DynamicScope({
    @Scope(scope = ThreadScoped.class),
    @Scope(scope = ConversationScoped.class, ifExpression = "#{not javax.enterprise.context.conversation.transient}"),
    @Scope(scope = ViewScoped.class, ifExpression = "#{not facesContext.viewRoot.transient}"),
    @Scope(scope = RequestScoped.class)
})
public @interface UnitOfWorkScoped {}

      

+4


source







All Articles