是否可以从现有的请求范围激活会话范围和会话范围?

时间:2015-05-21 09:34:47

标签: java java-ee ejb weld conversation-scope

我有@EJB注入的bean TransactionCompleteJob。这个bean上有一个@Asynchronous方法asyncCompleteTransaction(Integer transactionId)

当我尝试使用其他注入的bean以及在此方法中作为会话作用域或会话作用域的实体时,我最终收到错误:

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

所以我注入焊接BoundConversationScopeBoundSessionScopeBoundRequestScope并激活它们,生成请求数据的空地图,以及会话数据的空地图,如指定by jboss weld documentation

问题在于,当激活请求范围时,我收到另一条错误消息:

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

我已尝试不激活请求范围,但我似乎最终导致实际请求范围内的任何内容的资源泄漏,特别是我有一个请求范围JPA EntityManager。特别是一旦流程结束,我会看到另一条消息:

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()]

如果我已经激活了一个请求范围上下文,怎么能启动它?或者启动会话范围上下文和会话范围上下文,它与现有的请求范围上下文相关联?或者,有没有更好的方法来解决这个问题?

编辑:

有没有办法从焊接中抓住RequestScope所以我可以在开始自己之前停用它?或者一种异步启动TransactionCompleteJob的方法,而不是注入它并调用@Asynchronous方法?

1 个答案:

答案 0 :(得分:4)

我或多或少有同样的问题但采取了不同的方法:我在我的存储库中注入了@ConversationScoped EntityManager但是我需要进行一些批处理,其中没有ConversationContext可用且得到了使用我的存储库时的例外情况。我没有尝试激活不打算使用的ConversationContext,而是结束了实现2个新的上下文(+ 1拦截器):

  • 第一个是ThreadContext(@ThreadScoped),它将Map中的所有内容存储在ThreadLocal中(这对异步处理很有用)+ 1方法拦截器({{1} })用于我的异步/批处理方法,以便在调用时激活此上下文。
  • 第二个有点复杂:它是某种动态上下文,它按顺序委托给第一个活动上下文:ThreadContext,(NonTransient)ConversationContext,(NonTransient)ViewContext(来自JSF 2.2的@ThreadContextual ),RequestContext。我使用相应的@ViewScoped注释调用了此上下文UnitOfWorkContext。我注释了那些需要生活在那个环境中的(少数)bean(对我而言,它只是我@UnitOfWorkScoped的{​​{1}}方法。

实现所有这些似乎很难,但事实并非如此,代码非常小。如果需要,我会在2-3天内粘贴我的代码,因为我暂时无法访问它。

更新:以下是第二个上下文的代码:

以下接口用作Context.isActive()的补充。有时,即使上下文处于活动状态,也不意味着我想使用它,请参阅下面的示例。

@Produces

以下注释应放在新范围

EntityManager

动态上下文的实现

public interface DynamicContextActivation {

    boolean isActive(Context context);
}

自动注册动态上下文的扩展(将其添加到@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; }

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;
    }
}

这个范围代表以ThreadScoped,(LongRunning)ConversationScoped,(NonTransient)ViewScoped,RequestScoped:

/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); } } } } 制作人,提供@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; } } } EntityManager s:

@UnitOfWorkScoped

肯定有改进的余地,所以不要犹豫,提出反馈。

更新2:将DynamicContextActivation替换为EL表达式

会很不错
EntityManager