I'm using Spring 3.2.13.RELEASE and I have a method (on a spring-managed bean) annotated with @Scheduled that runs but is not invoked via a proxy and therefore it fails when I try to access the transaction:
@Service
@Transactional
public class CoreEmailServiceImpl extends BaseEmailServiceImpl implements CoreEmailService {
@Override
@Transactional
@Scheduled(cron = "0 0 5 * * *")
public void sendDailySummaryEmail() {
//.. stuff
TransactionSynchronizationManager.registerSynchronization(...); // fails
}
}
stack trace:
[#|2016-02-03T05:00:00.386-0700|SEVERE|glassfish3.1.2|com.a.b.common.CoreEmailServiceImpl|_ThreadID=22570;_ThreadName=Thread-2;|Error sending email.
java.lang.IllegalStateException: Transaction synchronization is not active
at org.springframework.transaction.support.TransactionSynchronizationManager.registerSynchronization(TransactionSynchronizationManager.java:291)
at com.a.b.common.BaseEmailServiceImpl.sendEmail(BaseEmailServiceImpl.java:144)
at com.a.b.common.BaseEmailServiceImpl.sendEmail(BaseEmailServiceImpl.java:95)
at com.a.b.common.CoreEmailServiceImpl.sendDailySummaryEmail(CoreEmailServiceImpl.java:95)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
But when I invoke the method via a SpringMVC endpoint, it works correctly.
@Controller
@RequestMapping("/admin")
public class AdminController extends AbstractController {
@RequestMapping(value = "/job/daily-summary", method = RequestMethod.POST, produces = "application/json")
@Autowired
private AdminService adminService;
@ResponseBody
public ResponseBean runDailySummaryEmailJob() {
adminService.runDailySummaryEmailJob();
return null;
}
}
@Service
@Transactional(readOnly = true)
public class AdminServiceImpl extends DefaultServiceImpl implements AdminService {
@Autowired
private CoreEmailService coreEmailService;
@Override
public void runDailySummaryEmailJob() {
coreEmailService.sendDailySummaryEmail();
}
}
In a completely unrelated part of my project, I have another scheduled method on spring-managed bean that IS invoked via a proxy. The following lines from the two stack traces really outline the inconsistency I'm seeing between my two scheduled methods which both reside on spring managed beans:
com.a.b.common.CoreEmailServiceImpl.sendDailySummaryEmail(CoreEmailServiceImpl.java:93)
com.sun.proxy.$Proxy679.emailMonthlyAccountStatement(Unknown Source)
edit: I've updated spring to 3.2.16 with no luck.
edit: I've tried throwing the @scheduled annotation on the interface, i.e. CoreEmailService#sendDailySummaryEmail and it doesn't get triggered at ALL.
edit: Spring's ScheduledAnnotationBeanPostProcessor
checks if a scheduled method belongs to a proxy via AopUtils.isJdkDynamicProxy(bean)
and I've confirmed it returns true for all of my other scheduled methods except for this one in particular. Now trying to find out why...
edit: During initialization, I can confirm that the DefaultAopProxyFactory
does in fact pick up the CoreEmailServiceImpl
and create a dynamic proxy for it. Still unsure why the ScheduledAnnotationBeanPostProcessor
doesn't detect this proxy.