2014-12-18 2 views
1

Пожалуйста, рассмотрим следующий сценарий:Java EE 7: вызвать метод EJB + транзакции "неуправляемого" контекст

1) Один Singleton SchedulerService, что среди прочего управляет/создает кучу JobQueue s

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

    ... 

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

2) Возможно несколько JobQueue s, каждый из которых управляют/запуск/последующие их работу/незавершенные задания:

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 правильно, и при вызове addJob() и deleteJob() из других управляемых компонентов объекты будут правильно сохранены/удалены.

Теперь для фактического выполнения задания я использую Cron4j, который не является CDI. Он запускает новые потоки и запускает фактическое задание в этой теме. Когда работа заканчивается, она сообщает мне JobQueue (кто слушает события завершения работы) с помощью методов taskSucceeded/taskFailed.

Поскольку эти taskSucceeded/taskFailed методы вызываются из нитей Job (которые не «контейнер удалось»), я по понятным причинам получаю следующее исключение:

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, но, увы.

Любые указатели будут оценены по достоинству.

ответ

1

Оказывается, решение было не так сложно. Каждый день мы учимся. Yey!

  1. @ Внесите SchedulerService в JobQueue. Это создает круговое значение, но хорошо.
  2. В SchedulerService, сделать прокси-метод deleteJob(JobQueue jq, Job j). Все это делает invoke jq.deleteJob(j)
  3. Из «неуправляемых» обратных вызовов событий вызывается прокси schedulerService.deleteJob(this, j) вместо this.deleteJob(j). Перейдя через прокси-сервер @Injected, Java EE снова запускается, создавая транзакции для нас и всех других чудесных магов.
  4. Прибыль!
+1

Вы можете избежать использования cron4J и использовать '@ Schedule' для метода в одном из ваших EJB или вы можете программно создать запланированную задачу с использованием программных таймеров. В любом случае они будут использовать CMT, и вам не придется перепрыгивать через все эти обручи. – NBW

+1

@NBW спасибо, что я знал о «@ Schedule» и других альтернативах, но мне нужна была максимальная гибкость/настраиваемость, чтобы имитировать некоторое наследие. – geert3