Skip to content

Commit 662458a

Browse files
committed
Scheduler - a more defensive design of SchedulerContext synthetic bean
- SchedulerContext#getScheduledMethods() should return immutable metadata; it's an internal bean but can still be injected and modified
1 parent 11810db commit 662458a

File tree

10 files changed

+99
-24
lines changed

10 files changed

+99
-24
lines changed

extensions/quartz/runtime/src/main/java/io/quarkus/quartz/runtime/QuartzSchedulerImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
import io.quarkus.scheduler.common.runtime.AbstractJobDefinition;
7575
import io.quarkus.scheduler.common.runtime.DefaultInvoker;
7676
import io.quarkus.scheduler.common.runtime.ScheduledInvoker;
77-
import io.quarkus.scheduler.common.runtime.ScheduledMethodMetadata;
77+
import io.quarkus.scheduler.common.runtime.ScheduledMethod;
7878
import io.quarkus.scheduler.common.runtime.SchedulerContext;
7979
import io.quarkus.scheduler.common.runtime.SyntheticScheduled;
8080
import io.quarkus.scheduler.common.runtime.util.SchedulerUtils;
@@ -183,7 +183,7 @@ public QuartzSchedulerImpl(SchedulerContext context, QuartzSupport quartzSupport
183183
if (transaction != null) {
184184
transaction.begin();
185185
}
186-
for (ScheduledMethodMetadata method : context.getScheduledMethods()) {
186+
for (ScheduledMethod method : context.getScheduledMethods()) {
187187
int nameSequence = 0;
188188

189189
for (Scheduled scheduled : method.getSchedules()) {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package io.quarkus.scheduler.common.runtime;
2+
3+
import java.util.List;
4+
import java.util.Objects;
5+
6+
import io.quarkus.scheduler.Scheduled;
7+
8+
public final class ImmutableScheduledMethod implements ScheduledMethod {
9+
10+
private final String invokerClassName;
11+
private final String declaringClassName;
12+
private final String methodName;
13+
private final List<Scheduled> schedules;
14+
15+
public ImmutableScheduledMethod(String invokerClassName, String declaringClassName, String methodName,
16+
List<Scheduled> schedules) {
17+
this.invokerClassName = Objects.requireNonNull(invokerClassName);
18+
this.declaringClassName = Objects.requireNonNull(declaringClassName);
19+
this.methodName = Objects.requireNonNull(methodName);
20+
this.schedules = List.copyOf(schedules);
21+
}
22+
23+
public String getInvokerClassName() {
24+
return invokerClassName;
25+
}
26+
27+
public String getDeclaringClassName() {
28+
return declaringClassName;
29+
}
30+
31+
public String getMethodName() {
32+
return methodName;
33+
}
34+
35+
public List<Scheduled> getSchedules() {
36+
return schedules;
37+
}
38+
39+
}
Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
import io.quarkus.scheduler.Scheduled;
66

7-
public class ScheduledMethodMetadata {
7+
// This class is mutable so that it can be serialized in a recorder method
8+
public class MutableScheduledMethod implements ScheduledMethod {
89

910
private String invokerClassName;
1011
private String declaringClassName;
@@ -19,10 +20,6 @@ public void setInvokerClassName(String invokerClassName) {
1920
this.invokerClassName = invokerClassName;
2021
}
2122

22-
public String getMethodDescription() {
23-
return declaringClassName + "#" + methodName;
24-
}
25-
2623
public String getDeclaringClassName() {
2724
return declaringClassName;
2825
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.quarkus.scheduler.common.runtime;
2+
3+
import java.util.List;
4+
5+
import io.quarkus.scheduler.Scheduled;
6+
7+
/**
8+
* Scheduled method metadata.
9+
*
10+
*/
11+
public interface ScheduledMethod {
12+
13+
String getInvokerClassName();
14+
15+
String getDeclaringClassName();
16+
17+
String getMethodName();
18+
19+
List<Scheduled> getSchedules();
20+
21+
default String getMethodDescription() {
22+
return getDeclaringClassName() + "#" + getMethodName();
23+
}
24+
25+
}

extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/SchedulerContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public interface SchedulerContext {
99

1010
CronType getCronType();
1111

12-
List<ScheduledMethodMetadata> getScheduledMethods();
12+
List<ScheduledMethod> getScheduledMethods();
1313

1414
@SuppressWarnings("unchecked")
1515
default ScheduledInvoker createInvoker(String invokerClassName) {

extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
import io.quarkus.scheduler.ScheduledExecution;
8484
import io.quarkus.scheduler.Scheduler;
8585
import io.quarkus.scheduler.common.runtime.DefaultInvoker;
86-
import io.quarkus.scheduler.common.runtime.ScheduledMethodMetadata;
86+
import io.quarkus.scheduler.common.runtime.MutableScheduledMethod;
8787
import io.quarkus.scheduler.common.runtime.SchedulerContext;
8888
import io.quarkus.scheduler.common.runtime.util.SchedulerUtils;
8989
import io.quarkus.scheduler.runtime.SchedulerConfig;
@@ -279,7 +279,7 @@ public FeatureBuildItem build(SchedulerConfig config, BuildProducer<SyntheticBea
279279
BuildProducer<GeneratedClassBuildItem> generatedClasses, BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
280280
AnnotationProxyBuildItem annotationProxy) {
281281

282-
List<ScheduledMethodMetadata> scheduledMetadata = new ArrayList<>();
282+
List<MutableScheduledMethod> scheduledMetadata = new ArrayList<>();
283283
ClassOutput classOutput = new GeneratedClassGizmoAdaptor(generatedClasses, new Function<String, String>() {
284284
@Override
285285
public String apply(String name) {
@@ -296,9 +296,9 @@ public String apply(String name) {
296296
});
297297

298298
for (ScheduledBusinessMethodItem scheduledMethod : scheduledMethods) {
299-
ScheduledMethodMetadata metadata = new ScheduledMethodMetadata();
299+
MutableScheduledMethod metadata = new MutableScheduledMethod();
300300
String invokerClass = generateInvoker(scheduledMethod, classOutput);
301-
reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, invokerClass));
301+
reflectiveClass.produce(ReflectiveClassBuildItem.builder(invokerClass).constructors().methods().fields().build());
302302
metadata.setInvokerClassName(invokerClass);
303303
List<Scheduled> schedules = new ArrayList<>();
304304
for (AnnotationInstance scheduled : scheduledMethod.getSchedules()) {
Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
package io.quarkus.scheduler.runtime;
22

3+
import java.util.ArrayList;
34
import java.util.List;
45
import java.util.function.Supplier;
56

67
import com.cronutils.model.CronType;
78

89
import io.quarkus.runtime.annotations.Recorder;
9-
import io.quarkus.scheduler.common.runtime.ScheduledMethodMetadata;
10+
import io.quarkus.scheduler.common.runtime.ImmutableScheduledMethod;
11+
import io.quarkus.scheduler.common.runtime.MutableScheduledMethod;
12+
import io.quarkus.scheduler.common.runtime.ScheduledMethod;
1013
import io.quarkus.scheduler.common.runtime.SchedulerContext;
1114

1215
@Recorder
1316
public class SchedulerRecorder {
1417

1518
public Supplier<Object> createContext(SchedulerConfig config,
16-
List<ScheduledMethodMetadata> scheduledMethods) {
19+
List<MutableScheduledMethod> scheduledMethods) {
20+
// Defensive design - make an immutable copy of the scheduled method metadata
21+
List<ScheduledMethod> metadata = immutableCopy(scheduledMethods);
1722
return new Supplier<Object>() {
1823
@Override
1924
public Object get() {
@@ -25,11 +30,20 @@ public CronType getCronType() {
2530
}
2631

2732
@Override
28-
public List<ScheduledMethodMetadata> getScheduledMethods() {
29-
return scheduledMethods;
33+
public List<ScheduledMethod> getScheduledMethods() {
34+
return metadata;
3035
}
3136
};
3237
}
3338
};
3439
}
40+
41+
private List<ScheduledMethod> immutableCopy(List<MutableScheduledMethod> scheduledMethods) {
42+
List<ScheduledMethod> metadata = new ArrayList<>(scheduledMethods.size());
43+
for (ScheduledMethod scheduledMethod : scheduledMethods) {
44+
metadata.add(new ImmutableScheduledMethod(scheduledMethod.getInvokerClassName(),
45+
scheduledMethod.getDeclaringClassName(), scheduledMethod.getMethodName(), scheduledMethod.getSchedules()));
46+
}
47+
return List.copyOf(metadata);
48+
}
3549
}

extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SimpleScheduler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
import io.quarkus.scheduler.common.runtime.AbstractJobDefinition;
5151
import io.quarkus.scheduler.common.runtime.DefaultInvoker;
5252
import io.quarkus.scheduler.common.runtime.ScheduledInvoker;
53-
import io.quarkus.scheduler.common.runtime.ScheduledMethodMetadata;
53+
import io.quarkus.scheduler.common.runtime.ScheduledMethod;
5454
import io.quarkus.scheduler.common.runtime.SchedulerContext;
5555
import io.quarkus.scheduler.common.runtime.SkipConcurrentExecutionInvoker;
5656
import io.quarkus.scheduler.common.runtime.SkipPredicateInvoker;
@@ -126,7 +126,7 @@ public void run() {
126126
}
127127

128128
// Create triggers and invokers for @Scheduled methods
129-
for (ScheduledMethodMetadata method : context.getScheduledMethods()) {
129+
for (ScheduledMethod method : context.getScheduledMethods()) {
130130
int nameSequence = 0;
131131
for (Scheduled scheduled : method.getSchedules()) {
132132
nameSequence++;

extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/devconsole/SchedulerDevConsoleRecorder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import io.quarkus.scheduler.Scheduler;
1515
import io.quarkus.scheduler.Trigger;
1616
import io.quarkus.scheduler.common.runtime.ScheduledInvoker;
17-
import io.quarkus.scheduler.common.runtime.ScheduledMethodMetadata;
17+
import io.quarkus.scheduler.common.runtime.ScheduledMethod;
1818
import io.quarkus.scheduler.common.runtime.SchedulerContext;
1919
import io.quarkus.scheduler.common.runtime.util.SchedulerUtils;
2020
import io.quarkus.vertx.core.runtime.context.VertxContextSafetyToggle;
@@ -80,7 +80,7 @@ protected void handlePost(RoutingContext ctx, MultiMap form) throws Exception {
8080
} else {
8181
String name = form.get("name");
8282
SchedulerContext context = Arc.container().instance(SchedulerContext.class).get();
83-
for (ScheduledMethodMetadata metadata : context.getScheduledMethods()) {
83+
for (ScheduledMethod metadata : context.getScheduledMethods()) {
8484
if (metadata.getMethodDescription().equals(name)) {
8585
Vertx vertx = Arc.container().instance(Vertx.class).get();
8686
Context vdc = VertxContext.getOrCreateDuplicatedContext(vertx);

extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/devui/SchedulerJsonRPCService.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import io.quarkus.scheduler.Scheduler;
1616
import io.quarkus.scheduler.Trigger;
1717
import io.quarkus.scheduler.common.runtime.ScheduledInvoker;
18-
import io.quarkus.scheduler.common.runtime.ScheduledMethodMetadata;
18+
import io.quarkus.scheduler.common.runtime.ScheduledMethod;
1919
import io.quarkus.scheduler.common.runtime.SchedulerContext;
2020
import io.quarkus.scheduler.common.runtime.util.SchedulerUtils;
2121
import io.quarkus.vertx.core.runtime.context.VertxContextSafetyToggle;
@@ -47,7 +47,7 @@ public class SchedulerJsonRPCService {
4747
public Multi<JsonObject> streamRunningStatus() {
4848
SchedulerContext sc = context.get();
4949
Set<String> identities = new HashSet<>();
50-
for (ScheduledMethodMetadata metadata : sc.getScheduledMethods()) {
50+
for (ScheduledMethod metadata : sc.getScheduledMethods()) {
5151
for (Scheduled scheduled : metadata.getSchedules()) {
5252
if (!scheduled.identity().isBlank()) {
5353
identities.add(scheduled.identity());
@@ -69,7 +69,7 @@ public Multi<JsonObject> streamRunningStatus() {
6969
public JsonArray getScheduledMethods() {
7070
JsonArray methodsJson = new JsonArray();
7171
SchedulerContext c = context.get();
72-
for (ScheduledMethodMetadata metadata : c.getScheduledMethods()) {
72+
for (ScheduledMethod metadata : c.getScheduledMethods()) {
7373
JsonObject methodJson = new JsonObject();
7474
methodJson.put("declaringClassName", metadata.getDeclaringClassName());
7575
methodJson.put("methodName", metadata.getMethodName());
@@ -150,7 +150,7 @@ public JsonObject resumeJob(String identity) {
150150

151151
public JsonObject executeJob(String methodDescription) {
152152
SchedulerContext c = context.get();
153-
for (ScheduledMethodMetadata metadata : c.getScheduledMethods()) {
153+
for (ScheduledMethod metadata : c.getScheduledMethods()) {
154154
if (metadata.getMethodDescription().equals(methodDescription)) {
155155
Context vdc = VertxContext.getOrCreateDuplicatedContext(vertx.get());
156156
VertxContextSafetyToggle.setContextSafe(vdc, true);

0 commit comments

Comments
 (0)