Quartz

Данное дополнение позволяет управлять задачами Quartz через пользовательский интерфейс:

  • Создавать новые задачи Quartz из существующей реализации интерфейса org.quartz.Job.

  • Приостанавливать и возобновлять выполнение задач.

  • Запускать немедленное выполнение неактивных задач.

  • Редактировать триггеры и параметры задач.

  • Удалять задачи.

Установка

Для автоматической установки через Jmix Marketplace следуйте инструкциям в разделе Дополнения.

Для ручной установки добавьте следующие зависимости в ваш build.gradle:

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

Если URL вашей основной базы данных задан как переменная, заменяющая весь URL (например, main.datasource.url = ${DB_URL}), вам может потребоваться явно задать соответствующий класс драйвера через свойство приложения spring.quartz.properties.org.quartz.jobStore.driverDelegateClass.

Значение зависит от вашей базы данных:

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

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

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

  • Другая база данных: пропустите этот шаг - будет использовано значение по умолчанию.

Если переменная в свойстве main.datasource.url не включает префикс базы данных (например, main.datasource.url = jdbc:postgresql://${DB_HOST}/${DB_NAME}) или переменных вообще нет - класс драйвера будет разрешен автоматически, и этот шаг можно пропустить.

Использование

Чтобы создать и запланировать задачи для выполнения, сделайте следующее:

  1. Создайте класс, реализующий интерфейс org.quartz.Job. Его метод execute() будет вызываться планировщиком. Например:

    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");
        }
    }

    В классе задачи вы можете инжектировать Spring-бины с помощью @Autowired.

  2. Запустите приложение, откройте экран Quartz → Quartz jobs и нажмите Create.

    • Введите произвольное уникальное имя в поле Name и выберите ваш класс задачи в поле Class.

      Поле Group является необязательным, но может использоваться для группировки задач в пользовательском интерфейсе.

    • Создайте хотя бы один триггер для задачи на вкладке Triggers. Вы можете выбрать тип расписания Cron expression или Simple. В первом случае введите выражение Cron, например 0/5 * * ? * * * (каждые 5 секунд). Во втором случае введите интервал повторения в миллисекундах.

    После сохранения триггера и задачи она немедленно планируется для выполнения в соответствии с настройками триггера.

    Чтобы отредактировать настройки задачи, сначала приостановите ее, выбрав и нажав Pause. После сохранения изменений вы можете возобновить планирование, нажав Resume.

    Вы также можете выполнить любую зарегистрированную задачу немедленно, нажав Execute now, даже если у задачи нет триггеров. Это полезно для тестирования.

Аутентификация в задачах

Код, выполняемый планировщиком, не аутентифицирован, то есть не ассоциирован с каким-либо пользователем.

Если вы вызываете операции, требующие аутентификации, например, работу с данными через DataManager, используйте SystemAuthenticator или аннотацию @Authenticated:

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);
    }
}
Вы также можете использовать UnconstrainedDataManager для работы с данными в неаутентифицированном контексте.

Параметры задачи

Экран Job editor позволяет задать параметры для экземпляра задачи на вкладке Job data parameters. Вы можете использовать параметры в классе задачи следующим образом:

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);
    }
}

Отслеживание выполнения задач

Если вы хотите отслеживать выполнение задач, создайте бин, реализующий интерфейс JobListener или расширяющий JobListenerSupport следующим образом:

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 Вызывается Spring после создания экземпляра бина.
2 Вызывается планировщиком Quartz после выполнения задачи.

В методе jobWasExecuted() вы можете сохранить информацию о выполненной задаче в журнале или в базе данных.