From fffa2fa95228f3e502a233a4b8cb0a3c2b9edd79 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Thu, 18 Jul 2013 10:57:09 +0530 Subject: [PATCH] Scheduler changes is a combination of 2 commits. update API changes for scheduler jobs added scheduler job update API added pagination for job history --- .../V84__add_permission_for_scheduler.sql | 1 + .../commands/domain/CommandWrapper.java | 8 +- .../service/CommandWrapperBuilder.java | 9 +++ .../SynchronousCommandProcessingService.java | 4 + .../core/data/DataValidatorBuilder.java | 15 ++++ .../jobs/annotation/CronMethodParser.java | 10 --- .../jobs/api/SchedulerJobApiConstants.java | 6 ++ .../jobs/api/SchedulerJobApiResource.java | 38 +++++++++- .../jobs/data/JobDetailData.java | 2 +- .../jobs/data/JobDetailDataValidator.java | 76 +++++++++++++++++++ .../jobs/data/JobDetailHistoryData.java | 6 ++ .../jobs/domain/ScheduledJobDetail.java | 58 ++++++++------ .../UpdateJobDetailCommandhandler.java | 25 ++++++ .../jobs/service/JobRegisterService.java | 4 +- .../jobs/service/JobRegisterServiceImpl.java | 57 ++++++-------- ...ava => SchedularWritePlatformService.java} | 6 +- ...ritePlatformServiceJpaRepositoryImpl.java} | 33 +++++++- .../jobs/service/SchedulerJobListener.java | 4 +- .../SchedulerJobRunnerReadService.java | 12 +-- .../SchedulerJobRunnerReadServiceImpl.java | 40 +++++++--- .../group/service/SearchParameters.java | 8 ++ .../resources/META-INF/spring/appContext.xml | 1 - 22 files changed, 325 insertions(+), 98 deletions(-) create mode 100755 mifosng-db/migrations/core_db/V84__add_permission_for_scheduler.sql create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/data/JobDetailDataValidator.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/handler/UpdateJobDetailCommandhandler.java rename mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/{SchedularService.java => SchedularWritePlatformService.java} (71%) rename mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/{SchedularServiceJpaRepositoryImpl.java => SchedularWritePlatformServiceJpaRepositoryImpl.java} (58%) diff --git a/mifosng-db/migrations/core_db/V84__add_permission_for_scheduler.sql b/mifosng-db/migrations/core_db/V84__add_permission_for_scheduler.sql new file mode 100755 index 000000000..4e7d90f34 --- /dev/null +++ b/mifosng-db/migrations/core_db/V84__add_permission_for_scheduler.sql @@ -0,0 +1 @@ +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('jobs', 'UPDATE_SCHEDULER', 'SCHEDULER', 'UPDATE', 0); \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java index 17b2ca473..d61586043 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -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"); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index 45db68066..9be684744 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -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; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java index 68c98a282..8815b9d92 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -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()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java index 78bf5a91b..a6f66c1f9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java @@ -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; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/annotation/CronMethodParser.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/annotation/CronMethodParser.java index 2f11804f5..da3219f53 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/annotation/CronMethodParser.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/annotation/CronMethodParser.java @@ -64,16 +64,6 @@ public class CronMethodParser { Map 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); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/api/SchedulerJobApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/api/SchedulerJobApiConstants.java index d626451cf..8cbeae82b 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/api/SchedulerJobApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/api/SchedulerJobApiConstants.java @@ -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 JOB_DETAIL_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(jobIdentifierParamName, displayNameParamName, nextRunTimeParamName, initializingErrorParamName, jobActiveStatusParamName,currentlyRunningParamName, lastRunHistoryObjParamName)); @@ -30,5 +34,7 @@ public class SchedulerJobApiConstants { public static final Set JOB_HISTORY_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(versionParamName, jobRunStartTimeParamName, jobRunEndTimeParamName, statusParamName, jobRunErrorMessageParamName, triggerTypeParamName, jobRunErrorLogParamName)); + + public static final Set JOB_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(displayNameParamName,jobActiveStatusParamName,cronExpressionParamName)); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/api/SchedulerJobApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/api/SchedulerJobApiResource.java index 3ddad867b..0873f00f9 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/api/SchedulerJobApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/api/SchedulerJobApiResource.java @@ -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 toApiJsonSerializer; private final ToApiJsonSerializer jobHistoryToApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; @Autowired public SchedulerJobApiResource(final SchedulerJobRunnerReadService schedulerJobRunnerReadService, final JobRegisterService jobRegisterService, final ToApiJsonSerializer toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper, - final ToApiJsonSerializer jobHistoryToApiJsonSerializer) { + final ToApiJsonSerializer 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 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 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); + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/data/JobDetailData.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/data/JobDetailData.java index 0c0c0df03..66034de97 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/data/JobDetailData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/data/JobDetailData.java @@ -35,4 +35,4 @@ public class JobDetailData { this.lastRunHistory = lastRunHistory; this.currentlyRunning = currentlyRunning; } -} \ No newline at end of file +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/data/JobDetailDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/data/JobDetailDataValidator.java new file mode 100755 index 000000000..b95bc34f1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/data/JobDetailDataValidator.java @@ -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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SchedulerJobApiConstants.JOB_UPDATE_REQUEST_DATA_PARAMETERS); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList(); + + 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 dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/data/JobDetailHistoryData.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/data/JobDetailHistoryData.java index 9aeb69d3e..90a0f8475 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/data/JobDetailHistoryData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/data/JobDetailHistoryData.java @@ -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; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetail.java index cabcf7755..8d3cecec8 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetail.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/domain/ScheduledJobDetail.java @@ -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 { @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 { @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 { 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 { this.currentlyRunning = currentlyRunning; } + public Map update(final JsonCommand command) { + final Map actualChanges = new LinkedHashMap(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; + } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/handler/UpdateJobDetailCommandhandler.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/handler/UpdateJobDetailCommandhandler.java new file mode 100755 index 000000000..18630346a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/handler/UpdateJobDetailCommandhandler.java @@ -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); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterService.java index d11b55307..d89f82d41 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterService.java @@ -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); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java index 1013b2133..6ec7c7eec 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/JobRegisterServiceImpl.java @@ -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 allTenants = this.tenantDetailsService.findAllTenants(); for (MifosPlatformTenant tenant : allTenants) { ThreadLocalContextUtil.setTenant(tenant); - List scheduledJobDetails = this.schedularService.retrieveAllJobs(); + List 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; } - } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularWritePlatformService.java similarity index 71% rename from mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularService.java rename to mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularWritePlatformService.java index 7b578e941..b5c6a680c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularWritePlatformService.java @@ -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 retrieveAllJobs(); @@ -19,4 +21,6 @@ public interface SchedularService { public ScheduledJobDetail findByJobId(Long jobId); + CommandProcessingResult updateJobDetail(Long jobId, JsonCommand command); + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularWritePlatformServiceJpaRepositoryImpl.java similarity index 58% rename from mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularServiceJpaRepositoryImpl.java rename to mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularWritePlatformServiceJpaRepositoryImpl.java index f401b25dc..74a3b96a9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedularWritePlatformServiceJpaRepositoryImpl.java @@ -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 changes = scheduledJobDetail.update(command); + if (!changes.isEmpty()) { + this.scheduledJobDetailsRepository.saveAndFlush(scheduledJobDetail); + } + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(jobId) // + .with(changes) // + .build(); + + } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobListener.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobListener.java index 264fb3d9e..77e53aa07 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobListener.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobListener.java @@ -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; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobRunnerReadService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobRunnerReadService.java index 581734e45..2609f426a 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobRunnerReadService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobRunnerReadService.java @@ -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 findAllJobDeatils(); - + public List findAllJobDeatils(); + public JobDetailData retrieveOne(Long jobId); - - public Page retrieveJobHistory(Long jobId); - + + public Page retrieveJobHistory(Long jobId, SearchParameters searchParameters); + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobRunnerReadServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobRunnerReadServiceImpl.java index 32640b30f..74091975c 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobRunnerReadServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/jobs/service/SchedulerJobRunnerReadServiceImpl.java @@ -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 retrieveJobHistory(Long jobId) { + public Page 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 { @@ -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"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/SearchParameters.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/SearchParameters.java index 98bb243a5..0c023c74b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/SearchParameters.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/SearchParameters.java @@ -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) { diff --git a/mifosng-provider/src/main/resources/META-INF/spring/appContext.xml b/mifosng-provider/src/main/resources/META-INF/spring/appContext.xml index f14d54ba0..e6dde35ea 100644 --- a/mifosng-provider/src/main/resources/META-INF/spring/appContext.xml +++ b/mifosng-provider/src/main/resources/META-INF/spring/appContext.xml @@ -46,7 +46,6 @@ -