Пожалуйста, рассмотрим следующий сценарий: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, но, увы.
Любые указатели будут оценены по достоинству.
Вы можете избежать использования cron4J и использовать '@ Schedule' для метода в одном из ваших EJB или вы можете программно создать запланированную задачу с использованием программных таймеров. В любом случае они будут использовать CMT, и вам не придется перепрыгивать через все эти обручи. – NBW
@NBW спасибо, что я знал о «@ Schedule» и других альтернативах, но мне нужна была максимальная гибкость/настраиваемость, чтобы имитировать некоторое наследие. – geert3