or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-configuration.mdconfiguration.mdindex.mdmethod-locking.mdtask-scheduler.md

task-scheduler.mddocs/

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.