com.turbospaces.quartz.AbstractJob Maven / Gradle / Ivy
package com.turbospaces.quartz;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.apache.commons.lang3.time.StopWatch;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;
import org.slf4j.MDC;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.GenericApplicationContext;
import com.netflix.archaius.api.Property;
import com.turbospaces.cfg.ApplicationProperties;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public abstract class AbstractJob implements Job, ApplicationRunner, ApplicationContextAware {
private final ApplicationProperties props;
protected final MeterRegistry meterRegistry;
protected String schedulerName;
protected String schedulerInstanceId;
protected String fireInstanceId;
protected String threadName;
protected JobDataMap jobDataMap;
protected List tags = new ArrayList<>();
protected GenericApplicationContext applicationContext;
protected Class extends Job> jobClass;
protected StopWatch stopWatch;
protected AbstractJob(ApplicationProperties props, MeterRegistry meterRegistry) {
this.props = Objects.requireNonNull(props);
this.meterRegistry = Objects.requireNonNull(meterRegistry);
}
@Override
public void run(ApplicationArguments args) throws Exception {
if (this instanceof ScheduledJob) {
Supplier enabled = ((ScheduledJob) this).isEnabled();
//
// ~ in 'DEV' mode obviously
//
if (props.isDevMode()) {
//
// ~ locally we have to have way not to validate
//
if (props.QUARTZ_ENFORCE_DISABLED_JOBS_ENABLED.get()) {
if (enabled.get()) {
if (enabled instanceof Property>) {
Property prop = (Property) enabled;
String msg = String.format("job: %s is activated by default by prop: %s", getClass().getName(), prop.getKey());
throw new BeanInitializationException(msg);
}
String msg = String.format("job: %s is activated by default", getClass().getName());
throw new BeanInitializationException(msg);
}
}
}
((ScheduledJob) this).schedule();
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = (GenericApplicationContext) applicationContext;
}
@Override
public void execute(JobExecutionContext ctx) throws JobExecutionException {
this.stopWatch = StopWatch.createStarted();
try {
this.jobDataMap = ctx.getMergedJobDataMap();
this.jobClass = ctx.getJobDetail().getJobClass();
this.threadName = Thread.currentThread().getName();
this.schedulerName = ctx.getScheduler().getSchedulerName();
this.schedulerInstanceId = ctx.getScheduler().getSchedulerInstanceId();
this.fireInstanceId = ctx.getFireInstanceId();
} catch (SchedulerException err) {
throw new JobExecutionException(err);
}
boolean toApply = applicationContext.isRunning();
if (toApply) {
if (this instanceof ScheduledJob) {
//
// ~ only locally
//
if (props.isDevMode()) {
if (Objects.nonNull(ctx.getTrigger())) {
Date nextFire = ctx.getTrigger().getNextFireTime(); // ~ scheduled permanently
if (Objects.nonNull(nextFire)) {
Supplier enabled = ((ScheduledJob) this).isEnabled();
toApply = enabled.get();
}
}
} else {
Supplier enabled = ((ScheduledJob) this).isEnabled();
toApply = enabled.get();
}
}
if (toApply) {
try {
doBeforeExecution(ctx);
doExecute(ctx);
} catch (Throwable err) {
doOnFailure(ctx, err);
} finally {
stopWatch.stop();
Timer timer = meterRegistry.timer("job", tags);
log.debug("job=({}) completed in {}", getClass().getSimpleName(), stopWatch);
timer.record(stopWatch.getTime(), TimeUnit.MILLISECONDS);
MDC.clear();
Thread.currentThread().setName(threadName);
tags.clear();
try {
doAfterExecution(ctx);
} catch (Throwable err) {
log.error(err.getMessage(), err);
}
}
}
}
}
protected void doBeforeExecution(JobExecutionContext ctx) throws Throwable {
log.trace("before job execution ctx: {}", ctx);
}
protected void doAfterExecution(JobExecutionContext ctx) throws Throwable {
log.trace("after job execution ctx: {}", ctx);
}
protected void doOnFailure(JobExecutionContext ctx, Throwable err) throws JobExecutionException {
log.error("job finished exceptionally, dirty flag cleared now ...", err);
throw new JobExecutionException(err);
}
protected abstract void doExecute(JobExecutionContext ctx) throws Throwable;
}