0
# TaskScheduler Integration
1
2
Classes and utilities for integrating with Spring's TaskScheduler infrastructure, primarily used in the deprecated `PROXY_SCHEDULER` intercept mode.
3
4
## LockableTaskScheduler
5
6
A `TaskScheduler` wrapper that automatically applies locking to all scheduled tasks.
7
8
```java { .api }
9
public class LockableTaskScheduler implements TaskScheduler, DisposableBean {
10
11
public LockableTaskScheduler(TaskScheduler taskScheduler, LockManager lockManager);
12
13
// TaskScheduler methods with Trigger
14
public ScheduledFuture<?> schedule(Runnable task, Trigger trigger);
15
16
// TaskScheduler methods with Date
17
public ScheduledFuture<?> schedule(Runnable task, Date startTime);
18
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period);
19
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
20
21
// TaskScheduler methods with plain timing
22
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period);
23
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay);
24
25
// TaskScheduler methods with Instant (Java 8+ time API)
26
public ScheduledFuture<?> schedule(Runnable task, Instant startTime);
27
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Instant startTime, Duration period);
28
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period);
29
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay);
30
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay);
31
32
// DisposableBean
33
public void destroy() throws Exception;
34
}
35
```
36
37
### Constructor Parameters
38
39
- **taskScheduler**: The underlying `TaskScheduler` to wrap (e.g., `ConcurrentTaskScheduler`)
40
- **lockManager**: The `LockManager` that handles lock acquisition and release
41
42
### Usage Example
43
44
```java
45
@Configuration
46
public class SchedulerConfig {
47
48
@Bean
49
public LockProvider lockProvider() {
50
return new JdbcTemplateLockProvider(dataSource);
51
}
52
53
@Bean
54
public LockManager lockManager(LockProvider lockProvider) {
55
return new DefaultLockManager(lockProvider);
56
}
57
58
@Bean
59
public TaskScheduler taskScheduler(LockManager lockManager) {
60
ConcurrentTaskScheduler scheduler = new ConcurrentTaskScheduler();
61
return new LockableTaskScheduler(scheduler, lockManager);
62
}
63
}
64
```
65
66
### Task Wrapping Behavior
67
68
The `LockableTaskScheduler` automatically wraps all submitted tasks in `LockableRunnable`:
69
70
```java
71
private Runnable wrap(Runnable task) {
72
return new LockableRunnable(task, lockManager);
73
}
74
```
75
76
This ensures that any task scheduled through this scheduler will be protected by distributed locking.
77
78
## Scheduler Proxy Infrastructure
79
80
### SchedulerProxyScheduledLockAdvisor
81
82
AOP advisor that intercepts calls to `TaskScheduler` methods and wraps tasks with locking.
83
84
```java { .api }
85
public class SchedulerProxyScheduledLockAdvisor extends AbstractPointcutAdvisor {
86
87
public SchedulerProxyScheduledLockAdvisor(
88
LockProviderSupplier lockProviderSupplier,
89
ExtendedLockConfigurationExtractor lockConfigurationExtractor
90
);
91
92
public Pointcut getPointcut(); // Matches TaskScheduler methods
93
public Advice getAdvice(); // Returns scheduler interceptor
94
}
95
```
96
97
### Pointcut Definition
98
99
The advisor targets `TaskScheduler` interface methods:
100
101
```java { .api }
102
// Internal pointcut implementation
103
private static class TaskSchedulerPointcut implements Pointcut {
104
public ClassFilter getClassFilter(); // Matches TaskScheduler classes
105
public MethodMatcher getMethodMatcher(); // Matches schedule* methods
106
}
107
```
108
109
Targeted methods:
110
- `schedule`
111
- `scheduleAtFixedRate`
112
- `scheduleWithFixedDelay`
113
114
### Scheduler Interception Logic
115
116
The advisor intercepts scheduler calls and:
117
118
1. **Identifies task type**: Handles `ScheduledMethodRunnable` and `OutcomeTrackingRunnable`
119
2. **Wraps in LockableRunnable**: Creates a `LockableRunnable` wrapper with appropriate lock configuration
120
3. **Supplies lock provider**: Uses the configured `LockProvider` for the task
121
4. **Proceeds with modified arguments**: Replaces the original task with the wrapped version
122
123
```java { .api }
124
// Internal interceptor (simplified)
125
private static class LockingInterceptor implements MethodInterceptor {
126
public Object invoke(MethodInvocation invocation) throws Throwable;
127
}
128
```
129
130
### Supported Task Types
131
132
#### ScheduledMethodRunnable
133
Standard Spring scheduled method wrapper:
134
135
```java
136
// Internal handling
137
private LockableRunnable wrapTask(ScheduledMethodRunnable task) {
138
LockProvider lockProvider = lockProviderSupplier.supply(
139
task.getTarget(), task.getMethod(), new Object[] {}
140
);
141
LockManager lockManager = new DefaultLockManager(lockProvider, lockConfigurationExtractor);
142
return new LockableRunnable(task, lockManager);
143
}
144
```
145
146
#### OutcomeTrackingRunnable
147
Spring's outcome tracking wrapper (requires reflection):
148
149
```java
150
// Internal reflection-based handling for OutcomeTrackingRunnable
151
private Object wrapTask(Object firstArgument) throws NoSuchFieldException, IllegalAccessException {
152
if (firstArgument.getClass().getSimpleName().equals("OutcomeTrackingRunnable")) {
153
Field runnable = firstArgument.getClass().getDeclaredField("runnable");
154
runnable.setAccessible(true);
155
Object wrappedRunnable = runnable.get(firstArgument);
156
if (wrappedRunnable instanceof ScheduledMethodRunnable task) {
157
return wrapTask(task);
158
}
159
}
160
return firstArgument;
161
}
162
```
163
164
## Default TaskScheduler Registration
165
166
When using `PROXY_SCHEDULER` mode, ShedLock automatically registers a default `TaskScheduler` if none exists.
167
168
### RegisterDefaultTaskSchedulerPostProcessor
169
170
```java { .api }
171
public class RegisterDefaultTaskSchedulerPostProcessor
172
implements BeanDefinitionRegistryPostProcessor, Ordered, BeanFactoryAware {
173
174
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
175
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
176
public int getOrder(); // Returns LOWEST_PRECEDENCE
177
public void setBeanFactory(BeanFactory beanFactory) throws BeansException;
178
}
179
```
180
181
### Registration Logic
182
183
1. **Check for existing TaskScheduler**: If any `TaskScheduler` beans exist, do nothing
184
2. **Check for ScheduledExecutorService beans**:
185
- If exactly one exists: Create `ConcurrentTaskScheduler` using it
186
- If zero exist: Create plain `ConcurrentTaskScheduler`
187
- If multiple exist: Create plain `ConcurrentTaskScheduler` and log warning
188
189
### Registered Bean Configuration
190
191
#### With Existing ScheduledExecutorService
192
```java
193
// Equivalent registration when one ScheduledExecutorService exists
194
@Bean(DEFAULT_TASK_SCHEDULER_BEAN_NAME)
195
public TaskScheduler taskScheduler(ScheduledExecutorService scheduledExecutor) {
196
ConcurrentTaskScheduler scheduler = new ConcurrentTaskScheduler();
197
scheduler.setScheduledExecutor(scheduledExecutor);
198
return scheduler;
199
}
200
```
201
202
#### Without ScheduledExecutorService
203
```java
204
// Equivalent registration when no ScheduledExecutorService exists
205
@Bean(DEFAULT_TASK_SCHEDULER_BEAN_NAME)
206
public TaskScheduler taskScheduler() {
207
return new ConcurrentTaskScheduler();
208
}
209
```
210
211
The default bean name is `DEFAULT_TASK_SCHEDULER_BEAN_NAME` from Spring's `ScheduledAnnotationBeanPostProcessor`.
212
213
## Integration Modes Comparison
214
215
### PROXY_METHOD (Recommended)
216
- Intercepts method calls directly
217
- Works with any scheduler configuration
218
- Better performance (no scheduler wrapping)
219
- Cleaner stack traces
220
- Full support for method return values
221
222
### PROXY_SCHEDULER (Deprecated)
223
- Intercepts scheduler calls
224
- Requires compatible task types
225
- Additional reflection overhead
226
- May not work with all Spring versions
227
- Limited return value handling
228
229
### Migration Example
230
231
**Before (PROXY_SCHEDULER):**
232
```java
233
@EnableSchedulerLock(
234
interceptMode = InterceptMode.PROXY_SCHEDULER,
235
defaultLockAtMostFor = "10m"
236
)
237
public class SchedulerConfig {
238
// TaskScheduler beans are automatically wrapped
239
}
240
```
241
242
**After (PROXY_METHOD):**
243
```java
244
@EnableSchedulerLock(
245
interceptMode = InterceptMode.PROXY_METHOD, // or omit (default)
246
defaultLockAtMostFor = "10m"
247
)
248
public class SchedulerConfig {
249
// Methods are intercepted directly
250
}
251
```
252
253
No other changes required - the `@SchedulerLock` annotations work the same way.