From b5b189d610180fc1dadcf3a9e5340b63100cc565 Mon Sep 17 00:00:00 2001 From: Arnold Galovics Date: Mon, 12 Sep 2022 15:25:57 +0200 Subject: [PATCH] FINERACT-1744: Tiny refactorings as a preparation for idempotency implementation --- .../domain/CommandProcessingResultType.java | 34 ++-------- .../provider/CommandHandlerProvider.java | 49 +++++++------- .../service/CommandProcessingService.java | 2 +- ...CommandSourceWritePlatformServiceImpl.java | 6 +- .../SynchronousCommandProcessingService.java | 65 +++++++++---------- .../hooks/listener/FineractHookListener.java | 4 +- .../SchedulerJobRunnerReadServiceImpl.java | 2 +- ...UpdateLoanDisburseDateCommandHandler.java} | 2 +- 8 files changed, 70 insertions(+), 94 deletions(-) rename fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/{UpdateLoanDisbuseDateCommandHandler.java => UpdateLoanDisburseDateCommandHandler.java} (95%) diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/domain/CommandProcessingResultType.java b/fineract-provider/src/main/java/org/apache/fineract/commands/domain/CommandProcessingResultType.java index 5a1993fde..f25287e7f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/commands/domain/CommandProcessingResultType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/commands/domain/CommandProcessingResultType.java @@ -18,6 +18,11 @@ */ package org.apache.fineract.commands.domain; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor public enum CommandProcessingResultType { INVALID(0, "commandProcessingResultType.invalid"), // @@ -27,33 +32,4 @@ public enum CommandProcessingResultType { private final Integer value; private final String code; - - CommandProcessingResultType(final Integer value, final String code) { - this.value = value; - this.code = code; - } - - public Integer getValue() { - return this.value; - } - - public String getCode() { - return this.code; - } - - public static CommandProcessingResultType fromInt(final Integer typeValue) { - CommandProcessingResultType type = CommandProcessingResultType.INVALID; - switch (typeValue) { - case 1: - type = PROCESSED; - break; - case 2: - type = AWAITING_APPROVAL; - break; - case 3: - type = REJECTED; - break; - } - return type; - } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/provider/CommandHandlerProvider.java b/fineract-provider/src/main/java/org/apache/fineract/commands/provider/CommandHandlerProvider.java index cdefacc81..a4297f9d5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/commands/provider/CommandHandlerProvider.java +++ b/fineract-provider/src/main/java/org/apache/fineract/commands/provider/CommandHandlerProvider.java @@ -28,6 +28,7 @@ import org.apache.fineract.commands.annotation.CommandType; import org.apache.fineract.commands.exception.UnsupportedCommandException; import org.apache.fineract.commands.handler.NewCommandSourceHandler; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @@ -47,10 +48,30 @@ import org.springframework.stereotype.Component; @Component @NoArgsConstructor @Slf4j -public class CommandHandlerProvider implements ApplicationContextAware { +public class CommandHandlerProvider implements ApplicationContextAware, InitializingBean { + private final HashMap registeredHandlers = new HashMap<>(); private ApplicationContext applicationContext; - private HashMap registeredHandlers; + + @Override + public void afterPropertiesSet() throws Exception { + initializeHandlerRegistry(); + } + + private void initializeHandlerRegistry() { + final String[] commandHandlerBeans = applicationContext.getBeanNamesForAnnotation(CommandType.class); + if (ArrayUtils.isNotEmpty(commandHandlerBeans)) { + for (final String commandHandlerName : commandHandlerBeans) { + log.debug("Register command handler '{}' ...", commandHandlerName); + final CommandType commandType = applicationContext.findAnnotationOnBean(commandHandlerName, CommandType.class); + try { + registeredHandlers.put(commandType.entity() + "|" + commandType.action(), commandHandlerName); + } catch (final Throwable th) { + log.error("Unable to register command handler '{}'!", commandHandlerName, th); + } + } + } + } /** * Returns a handler for the given entity and action.
@@ -68,34 +89,14 @@ public class CommandHandlerProvider implements ApplicationContextAware { Preconditions.checkArgument(StringUtils.isNoneEmpty(action), "An action must be given!"); final String key = entity + "|" + action; - if (!this.registeredHandlers.containsKey(key)) { + if (!registeredHandlers.containsKey(key)) { throw new UnsupportedCommandException(key); } - return (NewCommandSourceHandler) this.applicationContext.getBean(this.registeredHandlers.get(key)); - } - - private void initializeHandlerRegistry() { - if (this.registeredHandlers == null) { - this.registeredHandlers = new HashMap<>(); - - final String[] commandHandlerBeans = this.applicationContext.getBeanNamesForAnnotation(CommandType.class); - if (ArrayUtils.isNotEmpty(commandHandlerBeans)) { - for (final String commandHandlerName : commandHandlerBeans) { - log.debug("Register command handler '{}' ...", commandHandlerName); - final CommandType commandType = this.applicationContext.findAnnotationOnBean(commandHandlerName, CommandType.class); - try { - this.registeredHandlers.put(commandType.entity() + "|" + commandType.action(), commandHandlerName); - } catch (final Throwable th) { - log.error("Unable to register command handler '{}'!", commandHandlerName, th); - } - } - } - } + return (NewCommandSourceHandler) applicationContext.getBean(registeredHandlers.get(key)); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; - this.initializeHandlerRegistry(); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandProcessingService.java b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandProcessingService.java index 50c62e436..d566c6222 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandProcessingService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandProcessingService.java @@ -26,7 +26,7 @@ import org.apache.fineract.useradministration.domain.AppUser; public interface CommandProcessingService { - CommandProcessingResult processAndLogCommand(CommandWrapper wrapper, JsonCommand command, boolean isApprovedByChecker); + CommandProcessingResult executeCommand(CommandWrapper wrapper, JsonCommand command, boolean isApprovedByChecker); CommandProcessingResult logCommand(CommandSource commandSourceResult); diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java index ad64791a1..38b7e937e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/commands/service/PortfolioCommandSourceWritePlatformServiceImpl.java @@ -77,7 +77,7 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio final String json = wrapper.getJson(); CommandProcessingResult result = null; - JsonCommand command = null; + JsonCommand command; int numberOfRetries = 0; int maxNumberOfRetries = ThreadLocalContextUtil.getTenant().getConnection().getMaxRetriesOnDeadlock(); int maxIntervalBetweenRetries = ThreadLocalContextUtil.getTenant().getConnection().getMaxIntervalBetweenRetries(); @@ -88,7 +88,7 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio wrapper.getOrganisationCreditBureauId()); while (numberOfRetries <= maxNumberOfRetries) { try { - result = this.processAndLogCommandService.processAndLogCommand(wrapper, command, isApprovedByChecker); + result = this.processAndLogCommandService.executeCommand(wrapper, command, isApprovedByChecker); numberOfRetries = maxNumberOfRetries + 1; } catch (CannotAcquireLockException | ObjectOptimisticLockingFailureException exception) { log.info("The following command {} has been retried {} time(s)", command.json(), numberOfRetries); @@ -140,7 +140,7 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio commandSourceInput.getOrganisationCreditBureauId()); final boolean makerCheckerApproval = true; - return this.processAndLogCommandService.processAndLogCommand(wrapper, command, makerCheckerApproval); + return this.processAndLogCommandService.executeCommand(wrapper, command, makerCheckerApproval); } @Transactional diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/service/SynchronousCommandProcessingService.java b/fineract-provider/src/main/java/org/apache/fineract/commands/service/SynchronousCommandProcessingService.java index 1d6c5fccf..c15376007 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/commands/service/SynchronousCommandProcessingService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/commands/service/SynchronousCommandProcessingService.java @@ -65,10 +65,10 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer @Transactional @Override - public CommandProcessingResult processAndLogCommand(final CommandWrapper wrapper, final JsonCommand command, + public CommandProcessingResult executeCommand(final CommandWrapper wrapper, final JsonCommand command, final boolean isApprovedByChecker) { - final boolean rollbackTransaction = this.configurationDomainService.isMakerCheckerEnabledForTask(wrapper.taskPermissionName()); + final boolean rollbackTransaction = configurationDomainService.isMakerCheckerEnabledForTask(wrapper.taskPermissionName()); final NewCommandSourceHandler handler = findCommandHandler(wrapper); @@ -76,16 +76,15 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer try { result = handler.processCommand(command); } catch (Throwable t) { - // publish error event - publishErrorEvent(wrapper, command, t); + publishHookErrorEvent(wrapper, command, t); throw t; } - final AppUser maker = this.context.authenticatedUser(wrapper); + final AppUser maker = context.authenticatedUser(wrapper); CommandSource commandSourceResult; if (command.commandId() != null) { - commandSourceResult = this.commandSourceRepository.findById(command.commandId()) + commandSourceResult = commandSourceRepository.findById(command.commandId()) .orElseThrow(() -> new CommandNotFoundException(command.commandId())); commandSourceResult.markAsChecked(maker); } else { @@ -98,7 +97,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer String changesOnlyJson; boolean rollBack = (rollbackTransaction || result.isRollbackTransaction()) && !isApprovedByChecker; if (result.hasChanges() && !rollBack) { - changesOnlyJson = this.toApiJsonSerializer.serializeResult(result.getChanges()); + changesOnlyJson = toApiJsonSerializer.serializeResult(result.getChanges()); commandSourceResult.updateJsonTo(changesOnlyJson); } @@ -107,7 +106,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer } if (commandSourceResult.hasJson()) { - this.commandSourceRepository.save(commandSourceResult); + commandSourceRepository.save(commandSourceResult); } if ((rollbackTransaction || result.isRollbackTransaction()) && !isApprovedByChecker) { @@ -125,7 +124,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer } result.setRollbackTransaction(null); - publishEvent(wrapper.entityName(), wrapper.actionName(), command, result); + publishHookEvent(wrapper.entityName(), wrapper.actionName(), command, result); return result; } @@ -135,7 +134,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer public CommandProcessingResult logCommand(CommandSource commandSourceResult) { commandSourceResult.markAsAwaitingApproval(); - commandSourceResult = this.commandSourceRepository.saveAndFlush(commandSourceResult); + commandSourceResult = commandSourceRepository.saveAndFlush(commandSourceResult); return new CommandProcessingResultBuilder().withCommandId(commandSourceResult.getId()) .withEntityId(commandSourceResult.getResourceId()).build(); @@ -146,54 +145,54 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer if (wrapper.isDatatableResource()) { if (wrapper.isCreateDatatable()) { - handler = this.applicationContext.getBean("createDatatableCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("createDatatableCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isDeleteDatatable()) { - handler = this.applicationContext.getBean("deleteDatatableCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("deleteDatatableCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isUpdateDatatable()) { - handler = this.applicationContext.getBean("updateDatatableCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("updateDatatableCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isCreate()) { - handler = this.applicationContext.getBean("createDatatableEntryCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("createDatatableEntryCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isUpdateMultiple()) { - handler = this.applicationContext.getBean("updateOneToManyDatatableEntryCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("updateOneToManyDatatableEntryCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isUpdateOneToOne()) { - handler = this.applicationContext.getBean("updateOneToOneDatatableEntryCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("updateOneToOneDatatableEntryCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isDeleteMultiple()) { - handler = this.applicationContext.getBean("deleteOneToManyDatatableEntryCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("deleteOneToManyDatatableEntryCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isDeleteOneToOne()) { - handler = this.applicationContext.getBean("deleteOneToOneDatatableEntryCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("deleteOneToOneDatatableEntryCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isRegisterDatatable()) { - handler = this.applicationContext.getBean("registerDatatableCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("registerDatatableCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } } else if (wrapper.isNoteResource()) { if (wrapper.isCreate()) { - handler = this.applicationContext.getBean("createNoteCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("createNoteCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isUpdate()) { - handler = this.applicationContext.getBean("updateNoteCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("updateNoteCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isDelete()) { - handler = this.applicationContext.getBean("deleteNoteCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("deleteNoteCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } } else if (wrapper.isSurveyResource()) { if (wrapper.isRegisterSurvey()) { - handler = this.applicationContext.getBean("registerSurveyCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("registerSurveyCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isFullFilSurvey()) { - handler = this.applicationContext.getBean("fullFilSurveyCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("fullFilSurveyCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } } else if (wrapper.isLoanDisburseDetailResource()) { if (wrapper.isUpdateDisbursementDate()) { - handler = this.applicationContext.getBean("updateLoanDisbuseDateCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("updateLoanDisburseDateCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.addAndDeleteDisbursementDetails()) { - handler = this.applicationContext.getBean("addAndDeleteLoanDisburseDetailsCommandHandler", NewCommandSourceHandler.class); + handler = applicationContext.getBean("addAndDeleteLoanDisburseDetailsCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } } else { - handler = this.commandHandlerProvider.getHandler(wrapper.entityName(), wrapper.actionName()); + handler = commandHandlerProvider.getHandler(wrapper.entityName(), wrapper.actionName()); } return handler; @@ -201,12 +200,12 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer @Override public boolean validateCommand(final CommandWrapper commandWrapper, final AppUser user) { - boolean rollbackTransaction = this.configurationDomainService.isMakerCheckerEnabledForTask(commandWrapper.taskPermissionName()); + boolean rollbackTransaction = configurationDomainService.isMakerCheckerEnabledForTask(commandWrapper.taskPermissionName()); user.validateHasPermissionTo(commandWrapper.getTaskPermissionName()); return rollbackTransaction; } - private void publishErrorEvent(CommandWrapper wrapper, JsonCommand command, Throwable t) { + private void publishHookErrorEvent(CommandWrapper wrapper, JsonCommand command, Throwable t) { ErrorInfo ex; if (t instanceof final RuntimeException e) { @@ -215,13 +214,13 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer ex = new ErrorInfo(500, 9999, "{\"Exception\": " + t.toString() + "}"); } - publishEvent(wrapper.entityName(), wrapper.actionName(), command, ex); + publishHookEvent(wrapper.entityName(), wrapper.actionName(), command, ex); } - private void publishEvent(final String entityName, final String actionName, JsonCommand command, final Object result) { + private void publishHookEvent(final String entityName, final String actionName, JsonCommand command, final Object result) { Gson gson = new Gson(); try { - final AppUser appUser = this.context.authenticatedUser(CommandWrapper.wrap(actionName, entityName, null, null)); + final AppUser appUser = context.authenticatedUser(CommandWrapper.wrap(actionName, entityName, null, null)); final HookEventSource hookEventSource = new HookEventSource(entityName, actionName); @@ -260,7 +259,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer reqmap.put("timestamp", Instant.now().toString()); - final String serializedResult = this.toApiResultJsonSerializer.serialize(reqmap); + final String serializedResult = toApiResultJsonSerializer.serialize(reqmap); final HookEvent applicationEvent = new HookEvent(hookEventSource, serializedResult, appUser, ThreadLocalContextUtil.getContext()); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/hooks/listener/FineractHookListener.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/hooks/listener/FineractHookListener.java index 90c307ae1..53e10c4dd 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/hooks/listener/FineractHookListener.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/hooks/listener/FineractHookListener.java @@ -52,11 +52,11 @@ public class FineractHookListener implements HookListener { final String actionName = hookEventSource.getActionName(); final String payload = event.getPayload(); - final List hooks = this.hookReadPlatformService.retrieveHooksByEvent(hookEventSource.getEntityName(), + final List hooks = hookReadPlatformService.retrieveHooksByEvent(hookEventSource.getEntityName(), hookEventSource.getActionName()); for (final Hook hook : hooks) { - final HookProcessor processor = this.hookProcessorProvider.getProcessor(hook); + final HookProcessor processor = hookProcessorProvider.getProcessor(hook); try { processor.process(hook, payload, entityName, actionName, fineractContext); } catch (Throwable e) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/SchedulerJobRunnerReadServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/SchedulerJobRunnerReadServiceImpl.java index b915521a5..10cce9a94 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/SchedulerJobRunnerReadServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/SchedulerJobRunnerReadServiceImpl.java @@ -125,7 +125,7 @@ public class SchedulerJobRunnerReadServiceImpl implements SchedulerJobRunnerRead public boolean isUpdatesAllowed() { final String sql = "select job.display_name from job job where job.currently_running=true and job.updates_allowed=false"; final List names = this.jdbcTemplate.queryForList(sql, String.class); - if (names != null && names.size() > 0) { + if (names.size() > 0) { final String listVals = names.toString(); final String jobNames = listVals.substring(listVals.indexOf("[") + 1, listVals.indexOf("]")); throw new OperationNotAllowedException(jobNames); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/UpdateLoanDisbuseDateCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/UpdateLoanDisburseDateCommandHandler.java similarity index 95% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/UpdateLoanDisbuseDateCommandHandler.java rename to fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/UpdateLoanDisburseDateCommandHandler.java index af5cde066..17fa829dc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/UpdateLoanDisbuseDateCommandHandler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/UpdateLoanDisburseDateCommandHandler.java @@ -28,7 +28,7 @@ import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor -public class UpdateLoanDisbuseDateCommandHandler implements NewCommandSourceHandler { +public class UpdateLoanDisburseDateCommandHandler implements NewCommandSourceHandler { private final LoanWritePlatformService writePlatformService;