Reputation: 93
This is my code :
@Scheduled(cron = "30 3 * * * *")
public void myCron() {
//we don't care what we do here
}
I want to know if it is possible to add a tracking id (or other information) on my @Scheduled
automatically.
The id will be different each time @Scheduled
is triggered.
I want to do this to avoid to duplicate code like :
@Scheduled(cron = "10 3 * * * *")
public void myCron() {
MDC.put("myId", UUID.randomUUID().toString());
//we don't care what we do here
}
@Scheduled(cron = "30 3 * * * *")
public void mySecondCron() {
MDC.put("myId", UUID.randomUUID().toString());
//we don't care what we do here
}
I tired to implements SchedulingConfigurer
but SchedulingConfigurer#configureTasks
is too late too add behavior on taks because the task (runnable) is already created
Thanks
Upvotes: 9
Views: 4524
Reputation: 4578
You can also use AOP to intercept code before it enters any method annotated with @Scheduled
and set the MDC. For good measure, it clears the MDC upon exiting the method.
Note the atExecutionTimeInMyNamespace
pointcut, where you can optionally put in your own package namespace to limit to just use of @Scheduled in your own code (i.e. excluding its use in any 3rd party libs, however unlikely that may be).
@Aspect
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ScheduledTaskTracingAspect {
@Pointcut("@annotation(org.springframework.scheduling.annotation.Scheduled)")
public void methodAnnotatedWithScheduled() {}
@Pointcut("execution(* com.mycompany..*(..))")
public void atExecutionTimeInMyNamespace() {}
@Around("methodAnnotatedWithScheduled() && atExecutionTimeInMyNamespace()")
public Object connectionAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
MDC.put("myId", UUID.randomUUID().toString());
try {
return joinPoint.proceed();
}
finally {
// Might as well clear all the MDC, not just the "myId"
MDC.clear();
}
}
}
Upvotes: 1
Reputation: 2253
You can try to implement custom TaskScheduler
and register it in SchedulingConfigurer.configureTasks
. ConcurrentTaskScheduler
can be considered as an example. Unfortunately this class isn't well-designed for inheritance, otherwise decorateTask
method will be protected. So you need to override all methods to add one additional Runnable decorator with your logic. Something like this one :
@Configuration
@EnableScheduling
public class ScheduledConfig implements SchedulingConfigurer {
public static class MyTaskScheduler extends ConcurrentTaskScheduler {
public MyTaskScheduler() {
}
public MyTaskScheduler(ScheduledExecutorService scheduledExecutor) {
super(scheduledExecutor);
}
public MyTaskScheduler(Executor concurrentExecutor, ScheduledExecutorService scheduledExecutor) {
super(concurrentExecutor, scheduledExecutor);
}
// TODO override other methods
@Override
public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
return super.schedule(decorateTask(task), trigger);
}
private Runnable decorateTask(Runnable task) {
// not 100% sure about safety of this cast
return new MyRunnable((ScheduledMethodRunnable) task);
}
private static class MyRunnable implements Runnable {
private final ScheduledMethodRunnable runnable;
private final AtomicLong counter = new AtomicLong();
public MyRunnable(ScheduledMethodRunnable runnable) {
this.runnable = runnable;
}
@Override
public void run() {
System.out.println(runnable.getMethod().toGenericString() + " " + counter.incrementAndGet());
runnable.run();
}
}
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
TaskScheduler taskScheduler = new MyTaskScheduler(Executors.newScheduledThreadPool(10));
taskRegistrar.setTaskScheduler(taskScheduler);
}
@Scheduled(cron = "0/1 * * * * *")
public void test() {
System.out.println("Running task in thread " + Thread.currentThread().getId());
}
}
Upvotes: 6