strolch/agent/src/main/java/li/strolch/job/StrolchJobsHandler.java

284 lines
8.8 KiB
Java

package li.strolch.job;
import static java.lang.String.join;
import static li.strolch.model.StrolchModelConstants.*;
import static li.strolch.runtime.StrolchConstants.TYPE_STROLCH_JOB;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.agent.api.StrolchAgent;
import li.strolch.agent.api.StrolchComponent;
import li.strolch.model.Resource;
import li.strolch.model.parameter.DateParameter;
import li.strolch.model.parameter.IntegerParameter;
import li.strolch.persistence.api.StrolchTransaction;
import li.strolch.privilege.model.Certificate;
import li.strolch.runtime.StrolchConstants;
/**
* The {@link StrolchJobsHandler} registers all {@link StrolchJob StrolchJobs}
*/
public class StrolchJobsHandler extends StrolchComponent {
protected Map<String, StrolchJob> jobs;
public StrolchJobsHandler(ComponentContainer container, String componentName) {
super(container, componentName);
}
@Override
public void start() throws Exception {
reloadJobs(true);
super.start();
}
/**
* Reload the jobs from the {@link Resource Resources} of type {@link StrolchConstants#TYPE_STROLCH_JOB}
*
* @throws Exception
* if something goes wrong while instantiating the jobs
*/
public void reloadJobs() throws Exception {
reloadJobs(false);
}
private void reloadJobs(boolean catchExceptions) throws Exception {
List<StrolchJob> jobs = new ArrayList<>();
if (this.jobs != null) {
this.jobs.values().forEach(value -> {
// cancel any schedule jobs
value.cancel(false);
// copy any already existing programmatically added jobs
if (value.getConfigureMethod() == ConfigureMethod.Programmatic)
jobs.add(value);
});
}
String[] realmNames = getConfiguration().getStringArray("realms", join(",", getContainer().getRealmNames()));
runAsAgent(ctx -> {
for (String realmName : realmNames) {
try (StrolchTransaction tx = openTx(ctx.getCertificate(), realmName, true)) {
tx.streamResources(TYPE_STROLCH_JOB).forEach(jobRes -> {
loadJob(jobs, jobRes, catchExceptions);
});
}
}
});
StrolchAgent agent = getContainer().getAgent();
ReloadJobsJob reloadJobsJob = new ReloadJobsJob(agent, ReloadJobsJob.class.getSimpleName(),
ReloadJobsJob.class.getSimpleName(), JobMode.Manual);
reloadJobsJob.setConfigureMethod(ConfigureMethod.Model);
jobs.add(reloadJobsJob);
this.jobs = new HashMap<>();
jobs.forEach(job -> internalRegister(job).schedule());
}
private void loadJob(List<StrolchJob> jobs, Resource jobRes, boolean catchExceptions) {
try {
String className = jobRes.getParameter(PARAM_CLASS_NAME, true).getValue();
JobMode mode = JobMode.valueOf(jobRes.getParameter(PARAM_MODE, true).getValue());
StrolchJob job;
try {
job = instantiateJob(className, jobRes.getId(), jobRes.getName(), mode);
} catch (ClassNotFoundException e) {
if (!catchExceptions) {
throw new IllegalStateException(
"Failed to load StrolchJob " + jobRes.getId() + " from model as class " + className
+ " does not exist!");
}
logger.error("Failed to load StrolchJob " + jobRes.getId() + " from model as class " + className
+ " does not exist!");
return;
}
job.setConfigureMethod(ConfigureMethod.Model).setMode(mode);
if (mode == JobMode.Manual) {
jobs.add(job);
logger.info("Added job " + job + " from model.");
return;
}
if (jobRes.hasParameter(PARAM_CRON)) {
String cron = jobRes.getParameter(PARAM_CRON, true).getValue();
DateParameter startDateP = jobRes.getParameter(PARAM_START_DATE, true);
job.setCronExpression(cron, startDateP.getValueZdt());
jobs.add(job);
logger.info("Added job " + job + " from model.");
return;
}
if (jobRes.hasParameter(PARAM_INITIAL_DELAY) && jobRes.hasParameter(PARAM_DELAY)) {
IntegerParameter initialDelayP = jobRes.getParameter(PARAM_INITIAL_DELAY, true);
IntegerParameter delayP = jobRes.getParameter(PARAM_DELAY, true);
TimeUnit initialDelayUnit = TimeUnit.valueOf(initialDelayP.getUom());
TimeUnit delayUnit = TimeUnit.valueOf(delayP.getUom());
job.setDelay(initialDelayP.getValue(), initialDelayUnit, delayP.getValue(), delayUnit);
jobs.add(job);
logger.info("Added job " + job + " from model.");
return;
}
logger.error(
"Job " + jobRes.getId() + " is inconsistent, as either cron, or initialDelay/delay is missing!");
} catch (RuntimeException e) {
if (catchExceptions)
logger.error("Failed to load StrolchJob " + jobRes.getId() + " from model", e);
else
throw new IllegalStateException("Failed to load StrolchJob " + jobRes.getId() + " from model", e);
}
}
@SuppressWarnings("unchecked")
private StrolchJob instantiateJob(String className, String id, String name, JobMode mode)
throws ClassNotFoundException {
Class<StrolchJob> clazz = (Class<StrolchJob>) Class.forName(className);
return instantiateJob(clazz, id, name, mode);
}
private StrolchJob instantiateJob(Class<? extends StrolchJob> clazz, String id, String name, JobMode mode) {
StrolchJob strolchJob;
try {
Constructor<? extends StrolchJob> constructor = clazz.getConstructor(StrolchAgent.class, String.class,
String.class, JobMode.class);
strolchJob = constructor.newInstance(getContainer().getAgent(), id, name, mode);
} catch (Exception e) {
throw new IllegalArgumentException("Failed to instantiate job " + clazz.getName(), e);
}
return strolchJob;
}
/**
* Registers the given job as a recurring job, and schedules it for execution
*
* @param strolchJobClass
* the job to instantiate and schedule for execution
*
* @return the instantiated job
*/
public StrolchJob registerAndScheduleJob(Class<? extends StrolchJob> strolchJobClass) {
StrolchJob job = instantiateJob(strolchJobClass, strolchJobClass.getSimpleName(),
strolchJobClass.getSimpleName(), JobMode.Recurring);
job.setConfigureMethod(ConfigureMethod.Programmatic);
return register(job).schedule();
}
/**
* Registers the given job as a manual job, which can be executed later by a job admin
*
* @param strolchJobClass
* the job to register
*
* @return the instantiated job
*/
public StrolchJob register(Class<? extends StrolchJob> strolchJobClass) {
StrolchJob job = instantiateJob(strolchJobClass, strolchJobClass.getSimpleName(),
strolchJobClass.getSimpleName(), JobMode.Manual);
job.setConfigureMethod(ConfigureMethod.Programmatic);
return register(job);
}
/**
* Registers the given job, not changing its current schedule or type
*
* @param job
* the job to register
*
* @return the job
*/
public StrolchJob register(StrolchJob job) {
return internalRegister(job.setConfigureMethod(ConfigureMethod.Programmatic));
}
private StrolchJob internalRegister(StrolchJob job) {
if (this.jobs.containsKey(job.getName())) {
StrolchJob existingJob = this.jobs.get(job.getName());
if (existingJob.getClass().equals(job.getClass()) && existingJob.getConfigureMethod().isModel()
&& job.getConfigureMethod().isProgrammatic()) {
logger.error("Not registering job " + job.getName()
+ " as it is already registered by a model specific job!");
} else {
throw new IllegalArgumentException("Job " + job.getName() + " is already registered!");
}
}
this.jobs.put(job.getName(), job);
return job;
}
/**
* Returns the current list of registered jobs
*
* @param cert
* the certificate to assert privilege
* @param source
* the source of the request
*
* @return a list of registered jobs
*/
public List<StrolchJob> getJobs(Certificate cert, String source) {
getContainer().getPrivilegeHandler()
.validate(cert, source)
.assertHasPrivilege(StrolchJobsHandler.class.getName());
return new ArrayList<>(this.jobs.values());
}
/**
* Returns the job with the given name
*
* @param cert
* the certificate to assert privilege
* @param source
* the source of the request
* @param jobName
* the name of the job to return
*
* @return the job with the requested name
*/
public StrolchJob getJob(Certificate cert, String source, String jobName) {
getContainer().getPrivilegeHandler()
.validate(cert, source)
.assertHasPrivilege(StrolchJobsHandler.class.getName());
StrolchJob strolchJob = this.jobs.get(jobName);
if (strolchJob == null)
throw new IllegalArgumentException("Job " + jobName + " is not registered!");
return strolchJob;
}
@Override
public void stop() throws Exception {
if (this.jobs != null) {
for (StrolchJob job : this.jobs.values()) {
job.cancel(true);
}
}
super.stop();
}
@Override
public void destroy() throws Exception {
if (this.jobs != null)
this.jobs.clear();
super.destroy();
}
}