Quartz

This add-on allows you to manage Quartz jobs via user interface:

  • Create new Quartz jobs from the existing implementation of org.quartz.Job interface.

  • Pause and resume jobs execution.

  • Trigger immediate execution of inactive jobs.

  • Edit job triggers and parameters.

  • Delete jobs.

Installation

For automatic installation through Jmix Marketplace, follow instructions in the Add-ons section.

For manual installation, add the following dependencies to your build.gradle:

implementation 'io.jmix.quartz:jmix-quartz-starter'
implementation 'io.jmix.quartz:jmix-quartz-flowui-starter'

If URL of your main database is set as variable replacing the whole URL (e.g. main.datasource.url = ${DB_URL}) you may need to explicitly set the proper driver delegate class via spring.quartz.properties.org.quartz.jobStore.driverDelegateClass application property.

Value depends on your database:

  • PostgreSQL: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate

  • Oracle: org.quartz.impl.jdbcjobstore.oracle.OracleDelegate

  • MS SQL: org.quartz.impl.jdbcjobstore.MSSQLDelegate

  • Other database: skip this step - default value will be used

If variable within main.datasource.url property doesn’t include database prefix (e.g. main.datasource.url = jdbc:postgresql://${DB_HOST}/${DB_NAME}) or there are no variables at all - driver delegate class will be resolved automatically and this step can be skipped.

Usage

To create and schedule a job for execution, do the following:

  1. Create a class implementing the org.quartz.Job interface. It’s execute() method will be invoked by the scheduler. For example:

    package quartz.ex1.app;
    
    import org.quartz.Job;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class SampleJob implements Job {
    
        private static final Logger log = LoggerFactory.getLogger(SampleJob.class);
    
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            log.info("Sample job is executed");
        }
    }

    In the job class, you can inject Spring beans with @Autowired.

  2. Run the application, open Quartz → Quartz jobs view and click Create.

    • Enter an arbitrary unique name in the Name field and select your job class in the Class field.

      The Group field is optional but can be used to group jobs in UI.

    • Create at least one trigger for the job on the Triggers tab. You can select either Cron expression or Simple schedule type. In the former case, enter a Cron expression like 0/5 * * ? * * * (each 5 sec). In the latter case, enter a repeat interval in milliseconds.

    After you save the trigger and the job, it is immediately scheduled for execution according to the trigger settings.

    To edit the job settings, first pause it by selecting and clicking Pause. After saving changes, you can resume the scheduling by clicking Resume.

    You can also execute any registered job immediately by clicking Execute now, even if the job has no triggers. This is useful for testing.

Authentication in Jobs

The code executed by the scheduler is not authenticated, that is not associated with any user.

If you invoke operations that require authentication, for example working with data through DataManager, use SystemAuthenticator or @Authenticated annotation:

public class SampleAuthenticatedJob implements Job {

    private static final Logger log = LoggerFactory.getLogger(SampleAuthenticatedJob.class);

    @Autowired
    private DataManager dataManager;

    @Authenticated
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        int usersCount = dataManager.load(User.class).all().list().size();
        log.info("There are {} registered users", usersCount);
    }
}
You can also use UnconstrainedDataManager to work with data in unauthenticated contexts.

Job Parameters

The Job editor view allows you to set parameters for the job instance on the Job data parameters tab. You can use the parameters in the job class as follows:

public class SampleParameterizedJob implements Job {

    private static final Logger log = LoggerFactory.getLogger(SampleParameterizedJob.class);

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        String paramStr = jobDataMap.entrySet().stream()
                .map(e -> e.getKey() + " : " + e.getValue())
                .collect(Collectors.joining(", ", "[", "]"));
        log.info("Sample job is executed with parameters: " + paramStr);
    }
}

Tracking Job Execution

If you want to track execution of jobs, create a bean implementing the JobListener interface or extending the JobListenerSupport as follows:

package quartz.ex1.app;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.listeners.JobListenerSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
public class JobExecutionListener extends JobListenerSupport {

    private static final Logger log = LoggerFactory.getLogger(JobExecutionListener.class);

    @Autowired
    private Scheduler scheduler;

    @Override
    public String getName() {
        return "SampleJobExecutionListener";
    }

    @PostConstruct
    private void registerListener() { (1)
        try {
            scheduler.getListenerManager().addJobListener(this);
        } catch (SchedulerException e) {
            log.error("Cannot register job listener", e);
        }
    }

    @Override
    public void jobWasExecuted(JobExecutionContext context,
                               JobExecutionException jobException) { (2)
        log.info("jobWasExecuted: name={}, context={}",
                context.getJobDetail().getKey().getName(), context);
    }
}
1 Invoked by Spring after the bean is instantiated.
2 Invoked by Quartz scheduler after a job is executed.

In the jobWasExecuted() method, you can store information about the executed job in the log or in the database.