【问题标题】:Java EE 7: invoke EJB method+transaction from "unmanaged" contextJava EE 7:从“非托管”上下文调用 EJB 方法+事务
【发布时间】:2014-12-18 14:51:49
【问题描述】:

请考虑以下场景:

1) 一个 Singleton SchedulerService 管理/创建一堆JobQueues

@Startup
@Singleton
public class SchedulerService
{
    @Inject
    private Instance<JobQueue> jobQueueInstance;

    ...

    public JobQueue addQueue(String name)
    {
         JobQueue q = jobQueueInstance.get();
         ....
         return q;
    }
}

2) 可能有几个JobQueues,他们各自管理/启动/跟进他们正在运行/待定的工作:

public class JobQueue implements SchedulerListener
{
    @PersistenceContext(unitName = "...")
    private EntityManager entityManager;

    public void addJob(Job newJob)
    {
       .... entityManager.persist(newJob); ....
       newJob.addSchedulerListener(this);
    }

    ...

    public void deleteJob(Job j)
    {
        ....  entityManager.delete(j); ....
    }

    // part of SchedulerListener, invoked from Job's Thread
    @Override
    public void taskSucceeded(Job job)
    {
        deleteJob(job);
    }

    // part of SchedulerListener, invoked from Job's Thread
    @Override
    public void taskFailed(Job job)
    {
        deleteJob(job);
    }
}

一切运行良好,entityManager 被正确地 @Injected,当从其他托管 bean 调用 addJob()deleteJob() 时,实体被正确地持久化/删除。

现在,对于实际的作业执行,我使用的是不支持 CDI 的 Cron4j。 它启动新线程并在该线程中运行实际作业。 当作业结束时,它会通过taskSucceeded/taskFailed 方法通知我的 JobQueue(监听作业终止事件)。

因为这些taskSucceeded/taskFailed 方法是从作业线程(不是“容器管理”的)调用的,所以我可以理解地得到以下异常:

4:46:27,032 ERROR [cob.scheduler.service.JobQueue] (cron4j::scheduler[DEFAULT]::task[442]) Job xxx failed: javax.persistence.TransactionRequiredException: WFLYJPA0060: Transaction is required to perform this operation (either use a transaction or extended persistence context)
    at org.jboss.as.jpa.container.AbstractEntityManager.transactionIsRequired(AbstractEntityManager.java:869) [wildfly-jpa-9.0.0.Alpha1.jar:9.0.0.Alpha1]
    at org.jboss.as.jpa.container.AbstractEntityManager.merge(AbstractEntityManager.java:567) [wildfly-jpa-9.0.0.Alpha1.jar:9.0.0.Alpha1]
    at cob.scheduler.service.JobQueue.deleteJob(JobQueue.java:287) [classes:]
    at cob.scheduler.service.JobQueue.deleteAndAdvance(JobQueue.java:241) [classes:]
    at cob.scheduler.service.JobQueue.taskSucceeded(JobQueue.java:226) [classes:]
    at it.sauronsoftware.cron4j.Scheduler.notifyTaskSucceeded(Scheduler.java:724) [classes:]
    at it.sauronsoftware.cron4j.TaskExecutor$Runner.run(TaskExecutor.java:500) [classes:]
    at java.lang.Thread.run(Thread.java:744) [rt.jar:1.7.0_45]

我想知道什么是继续的好方法。基本上我需要以某种方式“回到 EE 容器”,即从 EE 领域之外调用容器管理的方法。

我已阅读有关 ManagedExecutorService 的信息,但我不确定这是否适用或如何使用它。

我还尝试@Inject private JobQueue self; 并调用self.deleteJob() 而不仅仅是this.deleteJob(),但这会在部署时生成以下异常:

org.jboss.weld.exceptions.DeploymentException: WELD-001443: Pseudo scoped bean has circular dependencies. Dependency path: 
  - Managed Bean [class cob.scheduler.service.JobQueue] with qualifiers [@Any @Default],
  - [BackedAnnotatedField] @Inject private cob.scheduler.service.JobQueue.self,
  - Managed Bean [class cob.scheduler.service.JobQueue] with qualifiers [@Any @Default]
    at org.jboss.weld.bootstrap.Validator.reallyValidatePseudoScopedBean(Validator.java:904)
    at org.jboss.weld.bootstrap.Validator.validatePseudoScopedInjectionPoint(Validator.java:946)
    at org.jboss.weld.bootstrap.Validator.reallyValidatePseudoScopedBean(Validator.java:913)
    at org.jboss.weld.bootstrap.Validator.validatePseudoScopedBean(Validator.java:890)
    at org.jboss.weld.bootstrap.Validator.validateGeneralBean(Validator.java:148)
    at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:165)
    at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:529)
    at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:68)
    at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:66)
    at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:60)
    at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:53)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262) [rt.jar:1.7.0_45]
    ... 3 more

注意:除了“资源本地”事务管理,我一切正常。我认为将其转换为 JTA 会很容易,但是唉。

任何指针将不胜感激。

【问题讨论】:

    标签: jakarta-ee managed-bean java-ee-7


    【解决方案1】:

    结果证明解决方案并不难。我们每天都在学习。耶!

    1. @将SchedulerService 注入JobQueue。这会产生循环依赖,但很好。
    2. SchedulerService 中,创建一个代理方法deleteJob(JobQueue jq, Job j)。所有这些都是调用jq.deleteJob(j)
    3. 从“非托管”事件回调中,调用代理 schedulerService.deleteJob(this, j) 而不是 this.deleteJob(j)。通过检查 @Injected 代理,Java EE 再次启动,为我们创建事务和所有其他奇妙的魔法。
    4. 利润!

    【讨论】:

    • 您可以避免使用 cron4J 并在您的一个 EJB 中的方法上使用 @Schedule,或者您可以使用编程计时器以编程方式创建计划任务。在任何一种情况下,他们都将使用 CMT,而您不必跳过所有这些障碍。
    • @NBW 谢谢我知道@Schedule 和其他替代方案,但我需要最大的灵活性/可配置性来模拟一些遗留行为。
    猜你喜欢
    • 2017-12-28
    • 1970-01-01
    • 2013-03-24
    • 2013-11-26
    • 1970-01-01
    • 2012-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多