diff --git a/mifosng-provider/dependencies.gradle b/mifosng-provider/dependencies.gradle index 092847df2..19bf90bdd 100644 --- a/mifosng-provider/dependencies.gradle +++ b/mifosng-provider/dependencies.gradle @@ -81,7 +81,9 @@ dependencies { // Although Mifos (at the time of writing) doesn't have any compile time dep. on this, // it's useful to have this for the Spring Boot TestRestTemplate http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-rest-templates-test-utility - [group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.3.5'] + [group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.3.5'], + // Once we've switched to Java 8 this dep can be removed. + [group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.0'] ) testCompile 'junit:junit:4.11', 'junit:junit-dep:4.11', diff --git a/mifosng-provider/dev-dependencies.gradle b/mifosng-provider/dev-dependencies.gradle index 573f262d3..aa3aa9a7e 100644 --- a/mifosng-provider/dev-dependencies.gradle +++ b/mifosng-provider/dev-dependencies.gradle @@ -81,7 +81,9 @@ dependencies { // Although Mifos (at the time of writing) doesn't have any compile time dep. on this, // it's useful to have this for the Spring Boot TestRestTemplate http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-rest-templates-test-utility - [group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.3.5'] + [group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.3.5'], + // Once we've switched to Java 8 this dep can be removed. + [group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.0'] ) testCompile 'junit:junit:4.11', 'junit:junit-dep:4.11', diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/annotation/CommandType.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/annotation/CommandType.java new file mode 100644 index 000000000..23764bc97 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/annotation/CommandType.java @@ -0,0 +1,33 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.commands.annotation; + +import java.lang.annotation.*; + +/** + * Specifies the command type for the annotated class.
+ *
+ * The entity name (e.g. CLIENT, SAVINGSACCOUNT, LOANPRODUCT) and the action (e.g. CREATE, DELETE) must be given. + * + * @author Markus Geiss + * @version 1.0 + * @since 15.06 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +public @interface CommandType { + + /** + * Returns the name of the entity for this {@link CommandType}. + */ + String entity(); + + /** + * Return the name of the action for this {@link CommandType}. + */ + String action(); +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/provider/CommandHandlerProvider.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/provider/CommandHandlerProvider.java new file mode 100644 index 000000000..ce6375248 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/provider/CommandHandlerProvider.java @@ -0,0 +1,72 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.commands.provider; + +import com.google.common.base.Preconditions; +import org.apache.commons.lang3.StringUtils; +import org.mifosplatform.commands.annotation.CommandType; +import org.mifosplatform.commands.exception.UnsupportedCommandException; +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import java.util.HashMap; + +/** + * {@link CommandHandlerProvider} provides {@link NewCommandSourceHandler}s for a given entity and action.
+ *
+ * A {@link NewCommandSourceHandler} can be registered and the annotation {@link CommandType} is used to determine + * the entity and the action the handler is capable to process. + * + * @author Markus Geiss + * @version 1.0 + * @since 15.06 + * @see NewCommandSourceHandler + * @see CommandType + */ +@Component +@Scope("singleton") +public class CommandHandlerProvider { + + private final HashMap registeredHandlers = new HashMap<>(); + + CommandHandlerProvider() { + super(); + } + + /** + * Registers a {@link NewCommandSourceHandler} using the annotation {@link CommandType} to register the handler.
+ *
+ * @param handler the {@link NewCommandSourceHandler} to be added, must be given + */ + public void registerHandler(@Nonnull final C handler) { + Preconditions.checkArgument(handler != null, "A handler must be given!"); + + final CommandType commandType = handler.getClass().getAnnotation(CommandType.class); + this.registeredHandlers.put(commandType.entity() + "|" + commandType.action(), handler); + } + + /** + * Returns a handler gor the given entity and action.
+ *
+ * Throws an {@link UnsupportedCommandException} if no handler + * for the given entity, action combination can be found. + * @param entity the entity to lookup the handler, must be given. + * @param action the action to lookup the handler, must be given. + */ + @Nonnull + public NewCommandSourceHandler getHandler (@Nonnull final String entity, @Nonnull final String action) { + Preconditions.checkArgument(StringUtils.isNoneEmpty(entity), "An entity must be given!"); + Preconditions.checkArgument(StringUtils.isNoneEmpty(action), "An action must be given!"); + + final String key = entity + "|" + action; + if (!this.registeredHandlers.containsKey(key)) { + throw new UnsupportedCommandException(key); + } + return this.registeredHandlers.get(entity + "|" + action); + } +} 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 9bb20affc..6072e0bd4 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -14,6 +14,7 @@ import org.mifosplatform.commands.domain.CommandWrapper; import org.mifosplatform.commands.exception.RollbackTransactionAsCommandIsNotApprovedByCheckerException; import org.mifosplatform.commands.exception.UnsupportedCommandException; import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.commands.provider.CommandHandlerProvider; import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; @@ -38,12 +39,14 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer private final ToApiJsonSerializer toApiResultJsonSerializer; private CommandSourceRepository commandSourceRepository; private final ConfigurationDomainService configurationDomainService; + private final CommandHandlerProvider commandHandlerProvider; @Autowired public SynchronousCommandProcessingService(final PlatformSecurityContext context, final ApplicationContext applicationContext, final ToApiJsonSerializer> toApiJsonSerializer, final ToApiJsonSerializer toApiResultJsonSerializer, - final CommandSourceRepository commandSourceRepository, final ConfigurationDomainService configurationDomainService) { + final CommandSourceRepository commandSourceRepository, final ConfigurationDomainService configurationDomainService, + final CommandHandlerProvider commandHandlerProvider) { this.context = context; this.context = context; this.applicationContext = applicationContext; @@ -52,6 +55,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer this.commandSourceRepository = commandSourceRepository; this.commandSourceRepository = commandSourceRepository; this.configurationDomainService = configurationDomainService; + this.commandHandlerProvider = commandHandlerProvider; } @Transactional @@ -62,6 +66,10 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer final boolean rollbackTransaction = this.configurationDomainService.isMakerCheckerEnabledForTask(wrapper.taskPermissionName()); final NewCommandSourceHandler handler = findCommandHandler(wrapper); + + // TODO remove line 64 and uncomment following statement once all handlers are refactored + // final NewCommandSourceHandler handler = this.commandHandlerProvider.getHandler(wrapper.entityName(), wrapper.actionName()); + final CommandProcessingResult result = handler.processCommand(command); final AppUser maker = this.context.authenticatedUser(wrapper); @@ -124,6 +132,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer .withEntityId(commandSourceResult.getResourceId()).build(); } + // TODO remove this method once all handlers are refactored + @Deprecated private NewCommandSourceHandler findCommandHandler(final CommandWrapper wrapper) { NewCommandSourceHandler handler = null; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/CreateClientCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/CreateClientCommandHandler.java index 921cb3180..17a864746 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/CreateClientCommandHandler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/CreateClientCommandHandler.java @@ -5,7 +5,9 @@ */ package org.mifosplatform.portfolio.client.handler; +import org.mifosplatform.commands.annotation.CommandType; import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.commands.provider.CommandHandlerProvider; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.portfolio.client.service.ClientWritePlatformService; @@ -14,13 +16,16 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service +@CommandType(entity = "CLIENT", action = "CREATE") public class CreateClientCommandHandler implements NewCommandSourceHandler { private final ClientWritePlatformService clientWritePlatformService; @Autowired - public CreateClientCommandHandler(final ClientWritePlatformService clientWritePlatformService) { + public CreateClientCommandHandler(final ClientWritePlatformService clientWritePlatformService, + final CommandHandlerProvider commandHandlerProvider) { this.clientWritePlatformService = clientWritePlatformService; + commandHandlerProvider.registerHandler(this); } @Transactional diff --git a/mifosng-provider/src/test/java/org/mifosplatform/commands/provider/CommandHandlerProviderTest.java b/mifosng-provider/src/test/java/org/mifosplatform/commands/provider/CommandHandlerProviderTest.java new file mode 100644 index 000000000..132dbb8b1 --- /dev/null +++ b/mifosng-provider/src/test/java/org/mifosplatform/commands/provider/CommandHandlerProviderTest.java @@ -0,0 +1,56 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.commands.provider; + +import org.junit.Assert; +import org.junit.Test; +import org.mifosplatform.commands.exception.UnsupportedCommandException; +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +public class CommandHandlerProviderTest { + + public CommandHandlerProviderTest() { + super(); + } + + @Test + public void shouldRegisterHandler() { + final CommandHandlerProvider provider = new CommandHandlerProvider(); + try { + new ValidCommandHandler(provider); + } catch (NullPointerException pex) { + Assert.fail(); + } + + try { + final Long testCommandId = 815L; + + final NewCommandSourceHandler registeredHandler = provider.getHandler("HUMAN", "UPDATE"); + + final CommandProcessingResult result = + registeredHandler.processCommand( + JsonCommand.fromExistingCommand(testCommandId, null, null, null, null, null, null, null, null)); + Assert.assertEquals(testCommandId, result.commandId()); + } catch (UnsupportedCommandException ucex) { + Assert.fail(); + } + } + + @Test(expected = NullPointerException.class) + public void shouldNotRegisterHandlerAndThrowNullPointerException() { + final CommandHandlerProvider provider = new CommandHandlerProvider(); + new InvalidCommandHandler(provider); + } + + @Test(expected = UnsupportedCommandException.class) + public void shouldThrowUnsupportedCommandException() { + final CommandHandlerProvider provider = new CommandHandlerProvider(); + provider.getHandler("WHATEVER", "DOSOMETHING"); + + } +} diff --git a/mifosng-provider/src/test/java/org/mifosplatform/commands/provider/InvalidCommandHandler.java b/mifosng-provider/src/test/java/org/mifosplatform/commands/provider/InvalidCommandHandler.java new file mode 100644 index 000000000..3ff9e6819 --- /dev/null +++ b/mifosng-provider/src/test/java/org/mifosplatform/commands/provider/InvalidCommandHandler.java @@ -0,0 +1,23 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.commands.provider; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +public class InvalidCommandHandler implements NewCommandSourceHandler { + + public InvalidCommandHandler(final CommandHandlerProvider commandHandlerProvider) { + super(); + commandHandlerProvider.registerHandler(this); + } + + @Override + public CommandProcessingResult processCommand(JsonCommand command) { + return CommandProcessingResult.commandOnlyResult(command.commandId()); + } +} diff --git a/mifosng-provider/src/test/java/org/mifosplatform/commands/provider/ValidCommandHandler.java b/mifosng-provider/src/test/java/org/mifosplatform/commands/provider/ValidCommandHandler.java new file mode 100644 index 000000000..b5c3ef828 --- /dev/null +++ b/mifosng-provider/src/test/java/org/mifosplatform/commands/provider/ValidCommandHandler.java @@ -0,0 +1,25 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.commands.provider; + +import org.mifosplatform.commands.annotation.CommandType; +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +@CommandType(entity = "HUMAN", action = "UPDATE") +public class ValidCommandHandler implements NewCommandSourceHandler { + + public ValidCommandHandler(final CommandHandlerProvider commandHandlerProvider) { + super(); + commandHandlerProvider.registerHandler(this); + } + + @Override + public CommandProcessingResult processCommand(JsonCommand command) { + return CommandProcessingResult.commandOnlyResult(command.commandId()); + } +}