Scheduler changes is a combination of 2 commits.

update API changes for scheduler jobs

added scheduler job update API

added pagination for job history
This commit is contained in:
Pramod Nuthakki
2013-07-18 14:44:09 +05:30
parent 12abc17a88
commit fffa2fa952
22 changed files with 325 additions and 98 deletions
@@ -0,0 +1 @@
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('jobs', 'UPDATE_SCHEDULER', 'SCHEDULER', 'UPDATE', 0);
@@ -512,7 +512,7 @@ public class CommandWrapper {
public boolean isAccountingRuleResource() {
return this.entityName.equalsIgnoreCase("ACCOUNTINGRULE");
}
public boolean isClientUnassignStaff() {
return this.actionName.equalsIgnoreCase("UNASSIGNSTAFF") && this.entityName.equalsIgnoreCase("CLIENT");
}
@@ -532,8 +532,12 @@ public class CommandWrapper {
public boolean isClientAssignStaff() {
return this.actionName.equalsIgnoreCase("ASSIGNSTAFF") && this.entityName.equalsIgnoreCase("CLIENT");
}
public boolean isClientClose() {
return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("CLIENT");
}
public boolean isSchedulerResource() {
return this.entityName.equalsIgnoreCase("SCHEDULER");
}
}
@@ -1116,4 +1116,13 @@ public class CommandWrapperBuilder {
this.href = "/clients/" + clientId + "?command=assignStaff";
return this;
}
public CommandWrapperBuilder updateJobDetail(final Long jobId) {
this.actionName = "UPDATE";
this.entityName = "SCHEDULER";
this.entityId = jobId;
this.href = "/updateJobDetail/" + jobId+"/updateJobDetail";
return this;
}
}
@@ -491,6 +491,10 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
if (wrapper.isCreate()) {
handler = this.applicationContext.getBean("createHolidayCommandHandler", NewCommandSourceHandler.class);
}
} else if(wrapper.isSchedulerResource()){
if (wrapper.isUpdate()) {
handler = this.applicationContext.getBean("updateJobDetailCommandhandler", NewCommandSourceHandler.class);
}
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
@@ -16,6 +16,7 @@ import net.fortuna.ical4j.model.ValidationException;
import net.fortuna.ical4j.model.property.RRule;
import org.apache.commons.lang.StringUtils;
import org.quartz.CronExpression;
import org.springframework.util.ObjectUtils;
import com.google.gson.JsonArray;
@@ -684,4 +685,18 @@ public class DataValidatorBuilder {
}
return this;
}
public DataValidatorBuilder validateCronExpression() {
if (value != null && !CronExpression.isValidExpression(this.value.toString().trim())) {
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".invalid");
final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(
" value is not a valid cron expression");
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, this.value);
dataValidationErrors.add(error);
}
return this;
}
}
@@ -64,16 +64,6 @@ public class CronMethodParser {
Map<String, Object> attributes = metadata.getAnnotationAttributes(annotationClass.getName());
JobName attributeValue = (JobName) attributes.get(attributeName);
String className = metadata.getDeclaringClassName();
/**
* TODO: Vishwas, have commented out this piece of code,
* check its need with Pramod
**/
/**
* if (attributeValue == null ||
* attributeValue.trim().length() < 1) { attributeValue
* = className.substring(className.lastIndexOf(".") +
* 1); }
**/
String[] mapVal = { className, metadata.getMethodName() };
targetMethosMap.put(attributeValue.toString(), mapVal);
}
@@ -6,6 +6,8 @@ import java.util.Set;
public class SchedulerJobApiConstants {
public static final String JOB_RESOURCE_NAME = "schedulerjob";
// response parameters
public static final String jobIdentifierParamName = "jobId";
public static final String displayNameParamName = "displayName";
@@ -23,6 +25,8 @@ public class SchedulerJobApiConstants {
public static final String jobRunErrorMessageParamName = "jobRunErrorMessage";
public static final String triggerTypeParamName = "triggerType";
public static final String jobRunErrorLogParamName = "jobRunErrorLog";
public static final String cronExpressionParamName = "cronExpression";
public static final Set<String> JOB_DETAIL_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(jobIdentifierParamName,
displayNameParamName, nextRunTimeParamName, initializingErrorParamName, jobActiveStatusParamName,currentlyRunningParamName, lastRunHistoryObjParamName));
@@ -30,5 +34,7 @@ public class SchedulerJobApiConstants {
public static final Set<String> JOB_HISTORY_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(versionParamName,
jobRunStartTimeParamName, jobRunEndTimeParamName, statusParamName, jobRunErrorMessageParamName, triggerTypeParamName,
jobRunErrorLogParamName));
public static final Set<String> JOB_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(displayNameParamName,jobActiveStatusParamName,cronExpressionParamName));
}
@@ -5,15 +5,21 @@ import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
import org.mifosplatform.infrastructure.core.service.Page;
@@ -21,6 +27,7 @@ import org.mifosplatform.infrastructure.jobs.data.JobDetailData;
import org.mifosplatform.infrastructure.jobs.data.JobDetailHistoryData;
import org.mifosplatform.infrastructure.jobs.service.JobRegisterService;
import org.mifosplatform.infrastructure.jobs.service.SchedulerJobRunnerReadService;
import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -33,17 +40,20 @@ public class SchedulerJobApiResource {
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final ToApiJsonSerializer<JobDetailData> toApiJsonSerializer;
private final ToApiJsonSerializer<JobDetailHistoryData> jobHistoryToApiJsonSerializer;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public SchedulerJobApiResource(final SchedulerJobRunnerReadService schedulerJobRunnerReadService,
final JobRegisterService jobRegisterService, final ToApiJsonSerializer<JobDetailData> toApiJsonSerializer,
final ApiRequestParameterHelper apiRequestParameterHelper,
final ToApiJsonSerializer<JobDetailHistoryData> jobHistoryToApiJsonSerializer) {
final ToApiJsonSerializer<JobDetailHistoryData> jobHistoryToApiJsonSerializer,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
this.schedulerJobRunnerReadService = schedulerJobRunnerReadService;
this.jobRegisterService = jobRegisterService;
this.toApiJsonSerializer = toApiJsonSerializer;
this.jobHistoryToApiJsonSerializer = jobHistoryToApiJsonSerializer;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
}
@GET
@@ -69,8 +79,11 @@ public class SchedulerJobApiResource {
@Path("{jobId}/history")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveHistory(@Context final UriInfo uriInfo, @PathParam("jobId") final Long jobId) {
Page<JobDetailHistoryData> jobhistoryDetailData = this.schedulerJobRunnerReadService.retrieveJobHistory(jobId);
public String retrieveHistory(@Context final UriInfo uriInfo, @PathParam("jobId") final Long jobId,
@QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit,
@QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder) {
final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit, orderBy, sortOrder);
Page<JobDetailHistoryData> jobhistoryDetailData = this.schedulerJobRunnerReadService.retrieveJobHistory(jobId, searchParameters);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.jobHistoryToApiJsonSerializer.serialize(settings, jobhistoryDetailData,
SchedulerJobApiConstants.JOB_HISTORY_RESPONSE_DATA_PARAMETERS);
@@ -86,4 +99,23 @@ public class SchedulerJobApiResource {
response = Response.status(202).build();
return response;
}
@PUT
@Path("{jobId}/updateJobDetail")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String updateJobDetail(@PathParam("jobId") final Long jobId, final String jsonRequestBody) {
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
.updateJobDetail(jobId) //
.withJson(jsonRequestBody) //
.build(); //
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
if (result.getChanges() != null
&& (result.getChanges().containsKey(SchedulerJobApiConstants.jobActiveStatusParamName) || result.getChanges().containsKey(
SchedulerJobApiConstants.cronExpressionParamName))) {
jobRegisterService.rescheduleJob(jobId);
}
return this.toApiJsonSerializer.serialize(result);
}
}
@@ -35,4 +35,4 @@ public class JobDetailData {
this.lastRunHistory = lastRunHistory;
this.currentlyRunning = currentlyRunning;
}
}
}
@@ -0,0 +1,76 @@
package org.mifosplatform.infrastructure.jobs.data;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.jobs.api.SchedulerJobApiConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
@Component
public class JobDetailDataValidator {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public JobDetailDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForUpdate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
boolean atLeastOneParameterPassedForUpdate = false;
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SchedulerJobApiConstants.JOB_UPDATE_REQUEST_DATA_PARAMETERS);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SchedulerJobApiConstants.JOB_RESOURCE_NAME);
if (this.fromApiJsonHelper.parameterExists(SchedulerJobApiConstants.displayNameParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
final String displayName = this.fromApiJsonHelper.extractStringNamed(SchedulerJobApiConstants.displayNameParamName, element);
baseDataValidator.reset().parameter(SchedulerJobApiConstants.displayNameParamName).value(displayName).notBlank();
}
if (this.fromApiJsonHelper.parameterExists(SchedulerJobApiConstants.cronExpressionParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
final String cronExpression = this.fromApiJsonHelper.extractStringNamed(SchedulerJobApiConstants.cronExpressionParamName,
element);
baseDataValidator.reset().parameter(SchedulerJobApiConstants.cronExpressionParamName).value(cronExpression).notBlank()
.validateCronExpression();
}
if (this.fromApiJsonHelper.parameterExists(SchedulerJobApiConstants.jobActiveStatusParamName, element)) {
atLeastOneParameterPassedForUpdate = true;
final String status = this.fromApiJsonHelper.extractStringNamed(SchedulerJobApiConstants.jobActiveStatusParamName, element);
baseDataValidator.reset().parameter(SchedulerJobApiConstants.jobActiveStatusParamName).value(status).notBlank()
.validateForBooleanValue();
}
if (!atLeastOneParameterPassedForUpdate) {
final Object forceError = null;
baseDataValidator.reset().anyOfNotNull(forceError);
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
@@ -6,16 +6,22 @@ public class JobDetailHistoryData {
@SuppressWarnings("unused")
private final Long version;
@SuppressWarnings("unused")
private final Date jobRunStartTime;
@SuppressWarnings("unused")
private final Date jobRunEndTime;
@SuppressWarnings("unused")
private final String status;
@SuppressWarnings("unused")
private final String jobRunErrorMessage;
@SuppressWarnings("unused")
private final String triggerType;
@SuppressWarnings("unused")
private final String jobRunErrorLog;
@@ -1,6 +1,8 @@
package org.mifosplatform.infrastructure.jobs.domain;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -8,6 +10,9 @@ import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.jobs.api.SchedulerJobApiConstants;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@@ -17,12 +22,11 @@ public class ScheduledJobDetail extends AbstractPersistable<Long> {
@Column(name = "name")
private String jobName;
@SuppressWarnings("unused")
@Column(name = "display_name")
private String jobDisplayName;
@Column(name = "cron_expression")
private String croneExpression;
private String cronExpression;
@SuppressWarnings("unused")
@Column(name = "create_time")
@@ -58,33 +62,16 @@ public class ScheduledJobDetail extends AbstractPersistable<Long> {
@Column(name = "currently_running")
private boolean currentlyRunning;
public ScheduledJobDetail() {
protected ScheduledJobDetail() {
}
public ScheduledJobDetail(final String jobName, final String jobDisplayName, final String croneExpression, Date createTime,
final Short taskPriority, final String groupName, final Date previousRunStartTime, final Date nextRunTime, final String jobKey,
final String errorLog, final boolean activeSchedular, final boolean currentlyRunning) {
this.jobName = jobName;
this.jobDisplayName = jobDisplayName;
this.croneExpression = croneExpression;
this.createTime = createTime;
this.taskPriority = taskPriority;
this.groupName = groupName;
this.previousRunStartTime = previousRunStartTime;
this.nextRunTime = nextRunTime;
this.jobKey = jobKey;
this.errorLog = errorLog;
this.activeSchedular = activeSchedular;
this.currentlyRunning = currentlyRunning;
}
public String getJobName() {
return this.jobName;
}
public String getCroneExpression() {
return this.croneExpression;
public String getCronExpression() {
return this.cronExpression;
}
public Short getTaskPriority() {
@@ -103,8 +90,8 @@ public class ScheduledJobDetail extends AbstractPersistable<Long> {
return this.activeSchedular;
}
public void updateCroneExpression(final String croneExpression) {
this.croneExpression = croneExpression;
public void updateCronExpression(final String cronExpression) {
this.cronExpression = cronExpression;
}
public void updatePreviousRunStartTime(final Date previousRunStartTime) {
@@ -131,4 +118,27 @@ public class ScheduledJobDetail extends AbstractPersistable<Long> {
this.currentlyRunning = currentlyRunning;
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(9);
if (command.isChangeInStringParameterNamed(SchedulerJobApiConstants.displayNameParamName, this.jobDisplayName)) {
final String newValue = command.stringValueOfParameterNamed(SchedulerJobApiConstants.displayNameParamName).trim();
actualChanges.put(SchedulerJobApiConstants.displayNameParamName, newValue);
this.jobDisplayName = StringUtils.defaultIfEmpty(newValue, null);
}
if (command.isChangeInStringParameterNamed(SchedulerJobApiConstants.cronExpressionParamName, this.cronExpression)) {
final String newValue = command.stringValueOfParameterNamed(SchedulerJobApiConstants.cronExpressionParamName).trim();
actualChanges.put(SchedulerJobApiConstants.cronExpressionParamName, newValue);
this.cronExpression = StringUtils.defaultIfEmpty(newValue, null);
}
if (command.isChangeInBooleanParameterNamed(SchedulerJobApiConstants.jobActiveStatusParamName, this.activeSchedular)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(SchedulerJobApiConstants.jobActiveStatusParamName);
actualChanges.put(SchedulerJobApiConstants.jobActiveStatusParamName, newValue);
this.activeSchedular = newValue;
}
return actualChanges;
}
}
@@ -0,0 +1,25 @@
package org.mifosplatform.infrastructure.jobs.handler;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.jobs.service.SchedularWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UpdateJobDetailCommandhandler implements NewCommandSourceHandler {
private final SchedularWritePlatformService schedularWritePlatformService;
@Autowired
public UpdateJobDetailCommandhandler(final SchedularWritePlatformService schedularWritePlatformService) {
this.schedularWritePlatformService = schedularWritePlatformService;
}
@Override
public CommandProcessingResult processCommand(JsonCommand command) {
return this.schedularWritePlatformService.updateJobDetail(command.entityId(), command);
}
}
@@ -1,11 +1,9 @@
package org.mifosplatform.infrastructure.jobs.service;
import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobDetail;
public interface JobRegisterService {
public void executeJob(Long jobId);
public void rescheduleJob(ScheduledJobDetail scheduledJobDetails);
public void rescheduleJob(Long jobId);
}
@@ -40,15 +40,15 @@ public class JobRegisterServiceImpl implements JobRegisterService {
private final SchedulerFactoryBean schedulerFactoryBean;
private final SchedularService schedularService;
private final SchedularWritePlatformService schedularWritePlatformService;
private final TenantDetailsService tenantDetailsService;
@Autowired
public JobRegisterServiceImpl(final ApplicationContext applicationContext, final SchedulerFactoryBean schedulerFactoryBean,
final SchedularService schedularService, final TenantDetailsService tenantDetailsService) {
final SchedularWritePlatformService schedularService, final TenantDetailsService tenantDetailsService) {
this.applicationContext = applicationContext;
this.schedularService = schedularService;
this.schedularWritePlatformService = schedularService;
this.schedulerFactoryBean = schedulerFactoryBean;
this.tenantDetailsService = tenantDetailsService;
}
@@ -58,15 +58,10 @@ public class JobRegisterServiceImpl implements JobRegisterService {
List<MifosPlatformTenant> allTenants = this.tenantDetailsService.findAllTenants();
for (MifosPlatformTenant tenant : allTenants) {
ThreadLocalContextUtil.setTenant(tenant);
List<ScheduledJobDetail> scheduledJobDetails = this.schedularService.retrieveAllJobs();
List<ScheduledJobDetail> scheduledJobDetails = this.schedularWritePlatformService.retrieveAllJobs();
for (ScheduledJobDetail jobDetails : scheduledJobDetails) {
if (jobDetails.isActiveSchedular()) {
scheduleJob(jobDetails);
}else{
jobDetails.updateNextRunTime(null);
}
this.schedularService.saveOrUpdate(jobDetails);
scheduleJob(jobDetails);
this.schedularWritePlatformService.saveOrUpdate(jobDetails);
}
}
}
@@ -77,21 +72,11 @@ public class JobRegisterServiceImpl implements JobRegisterService {
jobDataMap.put(SchedularServiceConstants.TRIGGER_TYPE_REFERENCE, SchedularServiceConstants.TRIGGER_TYPE_APPLICATION);
jobDataMap.put(SchedularServiceConstants.TENANT_IDENTIFIER, ThreadLocalContextUtil.getTenant().getTenantIdentifier());
if (schedulerFactoryBean.getScheduler().checkExists(jobKey)) {
Thread newThread = new Thread(new Runnable() {
@Override
public void run() {
final JobKey threadLocalJobKey = jobKey;
final JobDataMap threadLocalJobDataMap = jobDataMap;
try {
schedulerFactoryBean.getScheduler().triggerJob(threadLocalJobKey, threadLocalJobDataMap);
} catch (SchedulerException e) {
logger.error(e.getMessage(), e);
}
}
});
newThread.run();
try {
schedulerFactoryBean.getScheduler().triggerJob(jobKey, jobDataMap);
} catch (SchedulerException e) {
logger.error(e.getMessage(), e);
}
} else {
throw new JobNotFoundException(jobKey.toString());
}
@@ -101,25 +86,30 @@ public class JobRegisterServiceImpl implements JobRegisterService {
}
@Override
public void rescheduleJob(ScheduledJobDetail scheduledJobDetails) {
try {
String jobIdentity = scheduledJobDetails.getJobKey();
JobKey jobKey = constructJobKey(jobIdentity);
schedulerFactoryBean.getScheduler().deleteJob(jobKey);
scheduleJob(scheduledJobDetails);
this.schedularService.saveOrUpdate(scheduledJobDetails);
this.schedularWritePlatformService.saveOrUpdate(scheduledJobDetails);
} catch (Throwable throwable) {
String stackTrace = getStackTraceAsString(throwable);
scheduledJobDetails.updateErrorLog(stackTrace);
this.schedularService.saveOrUpdate(scheduledJobDetails);
this.schedularWritePlatformService.saveOrUpdate(scheduledJobDetails);
}
}
@Override
public void rescheduleJob(Long jobId) {
ScheduledJobDetail scheduledJobDetail = this.schedularWritePlatformService.findByJobId(jobId);
rescheduleJob(scheduledJobDetail);
}
@Override
public void executeJob(Long jobId) {
try {
ScheduledJobDetail scheduledJobDetail = this.schedularService.findByJobId(jobId);
ScheduledJobDetail scheduledJobDetail = this.schedularWritePlatformService.findByJobId(jobId);
if (scheduledJobDetail != null) {
String key = scheduledJobDetail.getJobKey();
JobKey jobKey = constructJobKey(key);
@@ -133,6 +123,10 @@ public class JobRegisterServiceImpl implements JobRegisterService {
}
private void scheduleJob(ScheduledJobDetail scheduledJobDetails) {
if (!scheduledJobDetails.isActiveSchedular()) {
scheduledJobDetails.updateNextRunTime(null);
return;
}
try {
JobDetail jobDetail = createJobDetail(scheduledJobDetails);
Trigger trigger = createTrigger(scheduledJobDetails, jobDetail);
@@ -184,7 +178,7 @@ public class JobRegisterServiceImpl implements JobRegisterService {
TimeZone timeZone = TimeZone.getTimeZone(tenant.getTimezoneId());
cronTriggerFactoryBean.setTimeZone(timeZone);
cronTriggerFactoryBean.setGroup(scheduledJobDetails.getGroupName());
cronTriggerFactoryBean.setCronExpression(scheduledJobDetails.getCroneExpression());
cronTriggerFactoryBean.setCronExpression(scheduledJobDetails.getCronExpression());
cronTriggerFactoryBean.setPriority(scheduledJobDetails.getTaskPriority());
cronTriggerFactoryBean.afterPropertiesSet();
return cronTriggerFactoryBean.getObject();
@@ -213,5 +207,4 @@ public class JobRegisterServiceImpl implements JobRegisterService {
JobKey JobKey = new JobKey(keyParams[0], keyParams[1]);
return JobKey;
}
}
@@ -2,10 +2,12 @@ package org.mifosplatform.infrastructure.jobs.service;
import java.util.List;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobDetail;
import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobRunHistory;
public interface SchedularService {
public interface SchedularWritePlatformService {
public List<ScheduledJobDetail> retrieveAllJobs();
@@ -19,4 +21,6 @@ public interface SchedularService {
public ScheduledJobDetail findByJobId(Long jobId);
CommandProcessingResult updateJobDetail(Long jobId, JsonCommand command);
}
@@ -1,27 +1,36 @@
package org.mifosplatform.infrastructure.jobs.service;
import java.util.List;
import java.util.Map;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.jobs.data.JobDetailDataValidator;
import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobDetail;
import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobDetailRepository;
import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobRunHistory;
import org.mifosplatform.infrastructure.jobs.domain.ScheduledJobRunHistoryRepository;
import org.mifosplatform.infrastructure.jobs.exception.JobNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class SchedularServiceJpaRepositoryImpl implements SchedularService {
public class SchedularWritePlatformServiceJpaRepositoryImpl implements SchedularWritePlatformService {
private final ScheduledJobDetailRepository scheduledJobDetailsRepository;
private final ScheduledJobRunHistoryRepository scheduledJobRunHistoryRepository;
private final JobDetailDataValidator dataValidator;
@Autowired
public SchedularServiceJpaRepositoryImpl(final ScheduledJobDetailRepository scheduledJobDetailsRepository,
final ScheduledJobRunHistoryRepository scheduledJobRunHistoryRepository) {
public SchedularWritePlatformServiceJpaRepositoryImpl(final ScheduledJobDetailRepository scheduledJobDetailsRepository,
final ScheduledJobRunHistoryRepository scheduledJobRunHistoryRepository, final JobDetailDataValidator dataValidator) {
this.scheduledJobDetailsRepository = scheduledJobDetailsRepository;
this.scheduledJobRunHistoryRepository = scheduledJobRunHistoryRepository;
this.dataValidator = dataValidator;
}
@Override
@@ -62,4 +71,22 @@ public class SchedularServiceJpaRepositoryImpl implements SchedularService {
return this.scheduledJobDetailsRepository.findByJobId(jobId);
}
@Transactional
@Override
public CommandProcessingResult updateJobDetail(final Long jobId, final JsonCommand command) {
dataValidator.validateForUpdate(command.json());
final ScheduledJobDetail scheduledJobDetail = findByJobId(jobId);
if (scheduledJobDetail == null) { throw new JobNotFoundException(String.valueOf(jobId)); }
final Map<String, Object> changes = scheduledJobDetail.update(command);
if (!changes.isEmpty()) {
this.scheduledJobDetailsRepository.saveAndFlush(scheduledJobDetail);
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(jobId) //
.with(changes) //
.build();
}
}
@@ -29,12 +29,12 @@ public class SchedulerJobListener implements JobListener {
private final String name = SchedularServiceConstants.DEFAULT_LISTENER_NAME;
private final SchedularService schedularService;
private final SchedularWritePlatformService schedularService;
private final TenantDetailsService tenantDetailsService;
@Autowired
public SchedulerJobListener(final SchedularService schedularService, final TenantDetailsService tenantDetailsService) {
public SchedulerJobListener(final SchedularWritePlatformService schedularService, final TenantDetailsService tenantDetailsService) {
this.schedularService = schedularService;
this.tenantDetailsService = tenantDetailsService;
}
@@ -5,14 +5,14 @@ import java.util.List;
import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.infrastructure.jobs.data.JobDetailData;
import org.mifosplatform.infrastructure.jobs.data.JobDetailHistoryData;
import org.mifosplatform.portfolio.group.service.SearchParameters;
public interface SchedulerJobRunnerReadService {
public List<JobDetailData> findAllJobDeatils();
public List<JobDetailData> findAllJobDeatils();
public JobDetailData retrieveOne(Long jobId);
public Page<JobDetailHistoryData> retrieveJobHistory(Long jobId);
public Page<JobDetailHistoryData> retrieveJobHistory(Long jobId, SearchParameters searchParameters);
}
@@ -11,6 +11,7 @@ import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.infrastructure.jobs.data.JobDetailData;
import org.mifosplatform.infrastructure.jobs.data.JobDetailHistoryData;
import org.mifosplatform.infrastructure.jobs.exception.JobNotFoundException;
import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -50,12 +51,28 @@ public class SchedulerJobRunnerReadServiceImpl implements SchedulerJobRunnerRead
}
@Override
public Page<JobDetailHistoryData> retrieveJobHistory(Long jobId) {
public Page<JobDetailHistoryData> retrieveJobHistory(Long jobId, SearchParameters searchParameters) {
retrieveOne(jobId);
JobHistoryMapper jobHistoryMapper = new JobHistoryMapper();
String sql = jobHistoryMapper.schema() + " where job.id=?";
StringBuilder sqlBuilder = new StringBuilder(jobHistoryMapper.schema());
sqlBuilder.append(" where job.id=?");
if (searchParameters.isOrderByRequested()) {
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
if (searchParameters.isSortOrderProvided()) {
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
}
}
if (searchParameters.isLimited()) {
sqlBuilder.append(" limit ").append(searchParameters.getLimit());
if (searchParameters.isOffset()) {
sqlBuilder.append(" offset ").append(searchParameters.getOffset());
}
}
final String sqlCountRows = "SELECT FOUND_ROWS()";
return paginationHelper.fetchPage(jdbcTemplate, sqlCountRows, sql, new Object[] { jobId }, jobHistoryMapper);
return paginationHelper.fetchPage(jdbcTemplate, sqlCountRows, sqlBuilder.toString(), new Object[] { jobId }, jobHistoryMapper);
}
private static final class JobDetailMapper implements RowMapper<JobDetailData> {
@@ -73,21 +90,24 @@ public class SchedulerJobRunnerReadServiceImpl implements SchedulerJobRunnerRead
public JobDetailData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
Long id = rs.getLong("id");
String displayName = rs.getString("displayName");
Date nextRunTime = rs.getDate("nextRunTime");
Date nextRunTime = rs.getTimestamp("nextRunTime");
String initializingError = rs.getString("initializingError");
boolean active = rs.getBoolean("active");
boolean currentlyRunning = rs.getBoolean("currentlyRunning");
Long version = rs.getLong("version");
Date jobRunStartTime = rs.getDate("lastRunStartTime");
Date jobRunEndTime = rs.getDate("lastRunEndTime");
Date jobRunStartTime = rs.getTimestamp("lastRunStartTime");
Date jobRunEndTime = rs.getTimestamp("lastRunEndTime");
String status = rs.getString("status");
String jobRunErrorMessage = rs.getString("jobRunErrorMessage");
String triggerType = rs.getString("triggerType");
String jobRunErrorLog = rs.getString("jobRunErrorLog");
JobDetailHistoryData lastRunHistory = new JobDetailHistoryData(version, jobRunStartTime, jobRunEndTime, status,
jobRunErrorMessage, triggerType, jobRunErrorLog);
JobDetailHistoryData lastRunHistory = null;
if (version > 0) {
lastRunHistory = new JobDetailHistoryData(version, jobRunStartTime, jobRunEndTime, status, jobRunErrorMessage, triggerType,
jobRunErrorLog);
}
JobDetailData jobDetail = new JobDetailData(id, displayName, nextRunTime, initializingError, active, currentlyRunning,
lastRunHistory);
return jobDetail;
@@ -108,8 +128,8 @@ public class SchedulerJobRunnerReadServiceImpl implements SchedulerJobRunnerRead
@Override
public JobDetailHistoryData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
Long version = rs.getLong("version");
Date jobRunStartTime = rs.getDate("runStartTime");
Date jobRunEndTime = rs.getDate("runEndTime");
Date jobRunStartTime = rs.getTimestamp("runStartTime");
Date jobRunEndTime = rs.getTimestamp("runEndTime");
String status = rs.getString("status");
String jobRunErrorMessage = rs.getString("jobRunErrorMessage");
String triggerType = rs.getString("triggerType");
@@ -61,6 +61,14 @@ public final class SearchParameters {
return new SearchParameters(null, officeId, null, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder);
}
public static SearchParameters forPagination(final Integer offset, final Integer limit, final String orderBy,
final String sortOrder) {
Integer maxLimitAllowed = getCheckedLimit(limit);
return new SearchParameters(null, null, null, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder);
}
private SearchParameters(final String sqlSearch, final Long officeId, final String externalId, final String name,
final String hierarchy, final String firstname, final String lastname, final Integer offset, final Integer limit,
final String orderBy, final String sortOrder) {
@@ -46,7 +46,6 @@
<jpa:repositories base-package="org.mifosplatform.useradministration.domain" />
<jpa:repositories base-package="org.mifosplatform.organisation.*.domain" />
<jpa:repositories base-package="org.mifosplatform.portfolio.*" />
<jpa:repositories base-package="org.mifosplatform.scheduledjobs.domain" />
<import resource="infrastructure.xml" />