jobrunr / jobrunr

An extremely easy way to perform background processing in Java. Backed by persistent storage. Open and free for commercial use.
https://www.jobrunr.io/en/
Other
2.34k stars 226 forks source link

[BUG] Quarkus Extension NPE if property "quarkus.jobrunr.job-scheduler.enabled" was set to false #822

Closed leotu closed 1 year ago

leotu commented 1 year ago

JobRunr Version

6.3.0

JDK Version

jdk-17.0.4.1

Your SQL / NoSQL database

PostgreSQL 15.4

What happened?

When "quarkus. jobrunr.job-scheduler.enabled" set to false value, cause NullPointerException

java.lang.NullPointerException: Cannot invoke "org.jobrunr.scheduling.JobScheduler.scheduleRecurrently(String, org.jobrunr.jobs.JobDetails, org.jobrunr.scheduling.Schedule, java.time.ZoneId)" because "scheduler" is null

JobRunrRecurringJobRecorder.java at line 52

 public void schedule(BeanContainer container, String id, String cron, String interval, String zoneId, String className, String methodName, List<JobParameter> parameterList) {
        JobScheduler scheduler = container.instance(JobScheduler.class);
        String jobId = getId(id);
        String optionalCronExpression = getCronExpression(cron);
        String optionalInterval = getInterval(interval);

        if (StringUtils.isNullOrEmpty(cron) && StringUtils.isNullOrEmpty(optionalInterval))
            throw new IllegalArgumentException("Either cron or interval attribute is required.");
        if (StringUtils.isNotNullOrEmpty(cron) && StringUtils.isNotNullOrEmpty(optionalInterval))
            throw new IllegalArgumentException("Both cron and interval attribute provided. Only one is allowed.");

        if (Recurring.RECURRING_JOB_DISABLED.equals(optionalCronExpression) || Recurring.RECURRING_JOB_DISABLED.equals(optionalInterval)) {
            if (isNullOrEmpty(jobId)) {
                LOGGER.warn("You are trying to disable a recurring job using placeholders but did not define an id.");
            } else {
                scheduler.delete(jobId);
            }
        } else {
            JobDetails jobDetails = new JobDetails(className, null, methodName, parameterList);
            jobDetails.setCacheable(true);
            if (isNotNullOrEmpty(optionalCronExpression)) {
                scheduler.scheduleRecurrently(id, jobDetails, CronExpression.create(optionalCronExpression), getZoneId(zoneId));
            } else {
*** here ==>    scheduler.scheduleRecurrently(id, jobDetails, new Interval(optionalInterval), getZoneId(zoneId)); 
            }
        }
    }

How to reproduce?

@RegisterForReflection
@ApplicationScoped
public class ScheduledService {
...
    @Recurring(id = "findAllCate-Job", interval = "PT15s")
    //@Job(name = "findAllCate Job")
    @Transactional
    public void findAllCateJob() {
        log.info("now: {}", LocalDateTime.now());
        List<PhotoCate> photoCateAll = photoCateResource.findAll();
        log.debug("photoCateAll.size: {}", photoCateAll.size());
    }
...
}
quarkus
  jobrunr:
    job-scheduler:
      enabled: false
    background-job-server:
      enabled: ${quarkus.jobrunr.job-scheduler.enabled}
      worker-count: 10
    dashboard:
      enabled: ${quarkus.jobrunr.job-scheduler.enabled}
      port: 14041

Relevant log output

09:53:13.514 ERROR [Quarkus Main Thread] [io.quarkus.runtime.ApplicationLifecycleManager:197,run()] traceId=, parentId=, spanId= <> Failed to start application (with profile [dev]): java.lang.RuntimeException: Failed to start quarkus
    at io.quarkus.runner.ApplicationImpl.doStart(Unknown Source)
    at io.quarkus.runtime.Application.start(Application.java:101)
    at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:111)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:71)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:44)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:124)
    at io.quarkus.runner.GeneratedMain.main(Unknown Source)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at io.quarkus.runner.bootstrap.StartupActionImpl$1.run(StartupActionImpl.java:104)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.NullPointerException: Cannot invoke "org.jobrunr.scheduling.JobScheduler.scheduleRecurrently(String, org.jobrunr.jobs.JobDetails, org.jobrunr.scheduling.Schedule, java.time.ZoneId)" because "scheduler" is null
    at org.jobrunr.scheduling.JobRunrRecurringJobRecorder.schedule(JobRunrRecurringJobRecorder.java:52)
    at io.quarkus.deployment.steps.JobRunrExtensionProcessor$findRecurringJobAnnotationsAndScheduleThem1351984528.deploy_0(Unknown Source)
    at io.quarkus.deployment.steps.JobRunrExtensionProcessor$findRecurringJobAnnotationsAndScheduleThem1351984528.deploy(Unknown Source)
    ... 13 more
rdehuyss commented 1 year ago

First of all, can you complete your GitHub profile like mentioned in the community guidelines when creating a bug?

And, thanks for reporting this. Just to make sure, what would the expected outcome be in this case? Of course, you cannot schedule any recurring jobs without the JobScheduler. So should it:

rdehuyss commented 1 year ago

I thought the issue was also present in spring boot but that's not the case.

The Quarkus Extension now follows the same principle as with Spring Boot (no exceptions but also no logging).