Skip to content

Commit 7517053

Browse files
committed
quartz: bean method job are not persisted #3074
- Fix saving of jobData - Fix quartz: java.lang.NullPointerException: Cannot invoke "io.jooby.internal.quartz.JobRegistry.getJobMethod()" because "entry" is null fix #3069 - fix #3074
1 parent 74b5cc9 commit 7517053

11 files changed

Lines changed: 155 additions & 194 deletions
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal.quartz;
7+
8+
import org.quartz.DisallowConcurrentExecution;
9+
10+
@DisallowConcurrentExecution
11+
public class DisallowConcurrentJobDelegate extends JobDelegate {}

modules/jooby-quartz/src/main/java/io/jooby/internal/quartz/ExtendedJobExecutionContextImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,9 @@ public void put(Object key, Object value) {
143143
public Object get(Object key) {
144144
return jobExecutionContext.get(key);
145145
}
146+
147+
@Override
148+
public String toString() {
149+
return jobExecutionContext.toString();
150+
}
146151
}

modules/jooby-quartz/src/main/java/io/jooby/internal/quartz/JobDelegate.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@ public void execute(final JobExecutionContext context) throws JobExecutionExcept
3131
context.put("interrupted", interrupted);
3232
JobKey key = detail.getKey();
3333
try {
34-
JobRegistry entry = JobRegistry.get(key);
35-
Method method = entry.getJobMethod();
36-
Registry registry = entry.getRegistry();
34+
var scheduler = context.getScheduler();
35+
var schedulerContext = scheduler.getContext();
36+
var method = (Method) schedulerContext.get(key.toString());
37+
38+
Registry registry = (Registry) context.getScheduler().getContext().get("registry");
3739
// Set registry
3840
context.put("registry", registry);
3941
Object job = newInstance(registry, method.getDeclaringClass());

modules/jooby-quartz/src/main/java/io/jooby/internal/quartz/JobGenerator.java

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import java.lang.reflect.Method;
1111
import java.lang.reflect.Modifier;
12+
import java.util.ArrayList;
1213
import java.util.Arrays;
1314
import java.util.Date;
1415
import java.util.HashMap;
@@ -24,10 +25,12 @@
2425

2526
import org.quartz.CronScheduleBuilder;
2627
import org.quartz.CronTrigger;
28+
import org.quartz.DisallowConcurrentExecution;
2729
import org.quartz.Job;
2830
import org.quartz.JobDetail;
2931
import org.quartz.JobExecutionContext;
3032
import org.quartz.JobKey;
33+
import org.quartz.PersistJobDataAfterExecution;
3134
import org.quartz.ScheduleBuilder;
3235
import org.quartz.SimpleScheduleBuilder;
3336
import org.quartz.SimpleTrigger;
@@ -54,13 +57,31 @@
5457

5558
public class JobGenerator {
5659

57-
private static List<Class> SUPPORTED_ARGS =
60+
private static final List<Class<?>> SUPPORTED_ARGS =
5861
Arrays.asList(
5962
JobExecutionContext.class,
6063
Registry.class,
6164
ExtendedJobExecutionContext.class,
6265
AtomicBoolean.class);
6366

67+
public static List<Method> jobMethod(Class<?> jobClass) {
68+
List<Method> result = new ArrayList<>();
69+
for (Method method : jobClass.getDeclaredMethods()) {
70+
Scheduled scheduled = method.getAnnotation(Scheduled.class);
71+
if (scheduled != null) {
72+
int mods = method.getModifiers();
73+
if (!Modifier.isPublic(mods)) {
74+
throw new IllegalArgumentException("Job method must be public: " + method);
75+
}
76+
if (Modifier.isStatic(mods)) {
77+
throw new IllegalArgumentException("Job method should NOT be public: " + method);
78+
}
79+
result.add(method);
80+
}
81+
}
82+
return result;
83+
}
84+
6485
@SuppressWarnings("unchecked")
6586
public static Map<JobDetail, Trigger> build(final Jooby application, final List<Class<?>> jobs)
6687
throws NoSuchMethodException {
@@ -70,31 +91,20 @@ public static Map<JobDetail, Trigger> build(final Jooby application, final List<
7091
if (Job.class.isAssignableFrom(job)) {
7192
triggers.put(job((Class<? extends Job>) job), trigger(config, (Class<? extends Job>) job));
7293
} else {
73-
Method[] methods = job.getDeclaredMethods();
7494
int size = triggers.size();
75-
for (Method method : methods) {
95+
for (Method method : jobMethod(job)) {
7696
Scheduled scheduled = method.getAnnotation(Scheduled.class);
77-
if (scheduled != null) {
78-
int mods = method.getModifiers();
79-
if (!Modifier.isPublic(mods)) {
80-
throw new IllegalArgumentException("Job method must be public: " + method);
81-
}
82-
if (Modifier.isStatic(mods)) {
83-
throw new IllegalArgumentException("Job method should NOT be public: " + method);
84-
}
85-
List<Class<?>> types =
86-
Stream.of(method.getParameterTypes()).collect(Collectors.toList());
87-
types.removeAll(SUPPORTED_ARGS);
97+
List<Class<?>> types = Stream.of(method.getParameterTypes()).collect(Collectors.toList());
98+
types.removeAll(SUPPORTED_ARGS);
8899

89-
if (types.size() > 0) {
90-
throw new UnsupportedOperationException(
91-
"Argument(s) not supported on job method: "
92-
+ types
93-
+ " supported parameters are: "
94-
+ SUPPORTED_ARGS);
95-
}
96-
triggers.put(provisioningJob(method), newTrigger(config, scheduled, jobKey(method)));
100+
if (!types.isEmpty()) {
101+
throw new UnsupportedOperationException(
102+
"Argument(s) not supported on job method: "
103+
+ types
104+
+ " supported parameters are: "
105+
+ SUPPORTED_ARGS);
97106
}
107+
triggers.put(provisioningJob(method), newTrigger(config, scheduled, jobKey(method)));
98108
}
99109
if (size >= triggers.size()) {
100110
throw new IllegalArgumentException(format("Scheduled is missing on %s", job.getName()));
@@ -109,7 +119,26 @@ private static JobDetail job(final Class<? extends Job> jobType) throws NoSuchMe
109119
}
110120

111121
private static JobDetail provisioningJob(final Method method) {
112-
return jobDetail(new JobMethodDetail(method), jobKey(method), JobDelegate.class);
122+
var detail =
123+
jobDetail(new JobDetailImpl(), jobKey(method), findJobClass(method.getDeclaringClass()));
124+
detail.getJobDataMap().put("jobMethod", method);
125+
return detail;
126+
}
127+
128+
private static Class<? extends Job> findJobClass(Class<?> declaringClass) {
129+
var persistent = declaringClass.getAnnotation(PersistJobDataAfterExecution.class);
130+
var nonConcurrent = declaringClass.getAnnotation(DisallowConcurrentExecution.class);
131+
if (persistent != null && nonConcurrent != null) {
132+
return StatefulJobDelegate.class;
133+
} else {
134+
if (persistent != null) {
135+
return PersistJobDataAfterJobDelegate.class;
136+
}
137+
if (nonConcurrent != null) {
138+
return DisallowConcurrentJobDelegate.class;
139+
}
140+
return JobDelegate.class;
141+
}
113142
}
114143

115144
private static JobDetail jobDetail(

modules/jooby-quartz/src/main/java/io/jooby/internal/quartz/JobMethodBuilder.java

Lines changed: 0 additions & 68 deletions
This file was deleted.

modules/jooby-quartz/src/main/java/io/jooby/internal/quartz/JobMethodDetail.java

Lines changed: 0 additions & 43 deletions
This file was deleted.

modules/jooby-quartz/src/main/java/io/jooby/internal/quartz/JobRegistry.java

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal.quartz;
7+
8+
import org.quartz.PersistJobDataAfterExecution;
9+
10+
@PersistJobDataAfterExecution
11+
public class PersistJobDataAfterJobDelegate extends JobDelegate {}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal.quartz;
7+
8+
import org.quartz.DisallowConcurrentExecution;
9+
import org.quartz.PersistJobDataAfterExecution;
10+
11+
@PersistJobDataAfterExecution
12+
@DisallowConcurrentExecution
13+
public class StatefulJobDelegate extends JobDelegate {}

modules/jooby-quartz/src/main/java/io/jooby/quartz/QuartzApp.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,14 @@ public class QuartzApp extends Jooby {
138138
scheduler.deleteJob(jobKey);
139139
return ctx.send(StatusCode.NO_CONTENT);
140140
});
141+
142+
delete(
143+
"/clear",
144+
ctx -> {
145+
Scheduler scheduler = getScheduler();
146+
scheduler.clear();
147+
return ctx.send(StatusCode.NO_CONTENT);
148+
});
141149
}
142150

143151
private Map<String, Object> job(Context ctx, Scheduler scheduler, JobKey jobKey) {

0 commit comments

Comments
 (0)