diff --git a/src/main/java/io/vlingo/xoom/codegen/template/autodispatch/AutoDispatchResourceHandlerDetail.java b/src/main/java/io/vlingo/xoom/codegen/template/autodispatch/AutoDispatchResourceHandlerDetail.java new file mode 100644 index 00000000..ed0875f9 --- /dev/null +++ b/src/main/java/io/vlingo/xoom/codegen/template/autodispatch/AutoDispatchResourceHandlerDetail.java @@ -0,0 +1,31 @@ +// Copyright © 2012-2021 VLINGO LABS. All rights reserved. +// +// 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 https://mozilla.org/MPL/2.0/. + +package io.vlingo.xoom.codegen.template.autodispatch; + +import io.vlingo.xoom.codegen.CodeGenerationContext; +import io.vlingo.xoom.codegen.content.CodeElementFormatter; +import io.vlingo.xoom.codegen.template.storage.Queries; + +import java.util.Optional; + +import static io.vlingo.xoom.codegen.template.TemplateParameter.*; +import static io.vlingo.xoom.codegen.template.TemplateStandard.AUTO_DISPATCH_RESOURCE_HANDLER; + +public class AutoDispatchResourceHandlerDetail { + + public static Optional findQueriesAttributeName(final String qualifiedName, final CodeGenerationContext context) { + final String packageName = CodeElementFormatter.packageOf(qualifiedName); + final String className = CodeElementFormatter.simpleNameOf(qualifiedName); + return context.templateParametersOf(AUTO_DISPATCH_RESOURCE_HANDLER).stream() + .filter(data -> data.parameters().find(REST_RESOURCE_NAME).equals(className)) + .filter(data -> data.parameters().find(PACKAGE_NAME).equals(packageName)) + .map(data -> data.parameters().find(QUERIES).getAttributeName()) + .filter(attributeName -> !attributeName.isEmpty()).findFirst(); + } + +} diff --git a/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/DefaultBootstrapTemplateData.java b/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/DefaultBootstrapTemplateData.java index 97f22da5..8aecb413 100644 --- a/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/DefaultBootstrapTemplateData.java +++ b/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/DefaultBootstrapTemplateData.java @@ -32,7 +32,7 @@ protected void enrichParameters(final CodeGenerationContext context) { STORE_PROVIDER, PROJECTION_DISPATCHER_PROVIDER, REST_RESOURCE, AUTO_DISPATCH_RESOURCE_HANDLER, EXCHANGE_BOOTSTRAP); - parameters().and(REST_RESOURCES, RestResource.from(context.contents())) + parameters().and(REST_RESOURCES, RestResource.from(context)) .and(EXCHANGE_BOOTSTRAP_NAME, EXCHANGE_BOOTSTRAP.resolveClassname()) .and(HAS_EXCHANGE, ContentQuery.exists(EXCHANGE_BOOTSTRAP, context.contents())) .addImports(qualifiedNames).addImports(storageType.resolveTypeRegistryQualifiedNames(useCQRS)); diff --git a/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/RestResource.java b/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/RestResource.java index de037bac..f2a69678 100644 --- a/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/RestResource.java +++ b/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/RestResource.java @@ -7,45 +7,89 @@ package io.vlingo.xoom.codegen.template.bootstrap; +import io.vlingo.xoom.codegen.CodeGenerationContext; import io.vlingo.xoom.codegen.content.CodeElementFormatter; -import io.vlingo.xoom.codegen.content.Content; import io.vlingo.xoom.codegen.content.ContentQuery; +import io.vlingo.xoom.codegen.template.TemplateStandard; +import io.vlingo.xoom.codegen.template.autodispatch.AutoDispatchResourceHandlerDetail; import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.IntStream; +import java.util.stream.Stream; +import static io.vlingo.xoom.codegen.template.TemplateParameter.MODEL; +import static io.vlingo.xoom.codegen.template.TemplateParameter.STORAGE_TYPE; +import static io.vlingo.xoom.codegen.template.TemplateParameters.with; import static io.vlingo.xoom.codegen.template.TemplateStandard.AUTO_DISPATCH_RESOURCE_HANDLER; import static io.vlingo.xoom.codegen.template.TemplateStandard.REST_RESOURCE; +import static io.vlingo.xoom.codegen.template.storage.Model.QUERY; +import static io.vlingo.xoom.codegen.template.storage.StorageType.STATE_STORE; +import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; public class RestResource { private final String className; private final String objectName; + private final String arguments; private final boolean last; - public static List from(final List contents) { - final Set classNames = - ContentQuery.findClassNames(contents, REST_RESOURCE, - AUTO_DISPATCH_RESOURCE_HANDLER); + public static List from(final CodeGenerationContext context) { + final Set qualifiedNames = + ContentQuery.findFullyQualifiedClassNames(context.contents(), + REST_RESOURCE, AUTO_DISPATCH_RESOURCE_HANDLER); - final Iterator iterator = classNames.iterator(); + final Iterator iterator = qualifiedNames.iterator(); - return IntStream.range(0, classNames.size()).mapToObj(index -> - new RestResource(iterator.next(), index, - classNames.size())).collect(toList()); + return IntStream + .range(0, qualifiedNames.size()) + .mapToObj(index -> new RestResource(iterator.next(), context, index, qualifiedNames.size())) + .collect(toList()); } - private RestResource(final String restResourceName, + private RestResource(final String restResourceQualifiedName, + final CodeGenerationContext context, final int resourceIndex, final int numberOfResources) { - this.className = restResourceName; - this.objectName = CodeElementFormatter.simpleNameToAttribute(restResourceName); + this.className = CodeElementFormatter.simpleNameOf(restResourceQualifiedName); + this.objectName = CodeElementFormatter.qualifiedNameToAttribute(restResourceQualifiedName); + this.arguments = resolveArguments(restResourceQualifiedName, context); this.last = resourceIndex == numberOfResources - 1; } + private String resolveArguments(final String restResourceName, final CodeGenerationContext context) { + final Stream defaultArgument = Stream.of("grid"); + + final Optional queriesActorInstancePath = + resolveQueriesActorInstancePath(restResourceName, context); + + final Stream queriesArgument = + queriesActorInstancePath.isPresent() ? + Stream.of(queriesActorInstancePath.get()) : Stream.empty(); + + return Stream.of(defaultArgument, queriesArgument).flatMap(s -> s).collect(joining(", ")); + } + + private Optional resolveQueriesActorInstancePath(final String restResourceQualifiedName, final CodeGenerationContext context) { + final Optional queriesAttributeName = + AutoDispatchResourceHandlerDetail.findQueriesAttributeName(restResourceQualifiedName, context); + + if(!queriesAttributeName.isPresent()) { + return Optional.empty(); + } + + final String storeProviderClass = + TemplateStandard.STORE_PROVIDER.resolveClassname(with(STORAGE_TYPE, STATE_STORE).and(MODEL, QUERY)); + + final String path = + String.format("%s.%s", CodeElementFormatter.simpleNameToAttribute(storeProviderClass), queriesAttributeName.get()); + + return Optional.of(path); + } + public String getClassName() { return className; } @@ -54,6 +98,10 @@ public String getObjectName() { return objectName; } + public String getArguments() { + return arguments; + } + public boolean isLast() { return last; } diff --git a/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/StoreProvider.java b/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/StoreProvider.java index 3ade2760..83537996 100644 --- a/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/StoreProvider.java +++ b/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/StoreProvider.java @@ -8,6 +8,7 @@ package io.vlingo.xoom.codegen.template.bootstrap; +import io.vlingo.xoom.codegen.content.CodeElementFormatter; import io.vlingo.xoom.codegen.template.TemplateParameters; import io.vlingo.xoom.codegen.template.storage.Model; import io.vlingo.xoom.codegen.template.storage.StorageType; @@ -26,6 +27,7 @@ public class StoreProvider { private final String className; + private final String objectName; private final String arguments; public static List from(final StorageType storageType, @@ -50,6 +52,7 @@ private StoreProvider(final StorageType storageType, .and(MODEL, model); this.className = STORE_PROVIDER.resolveClassname(parameters); + this.objectName = CodeElementFormatter.simpleNameToAttribute(this.className); this.arguments = resolveArguments(model, storageType, useProjections, hasExchange); } @@ -83,6 +86,10 @@ public String getClassName() { return className; } + public String getObjectName() { + return objectName; + } + public String getArguments() { return arguments; } diff --git a/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/XoomInitializerTemplateData.java b/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/XoomInitializerTemplateData.java index 16e3976a..ff71390a 100644 --- a/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/XoomInitializerTemplateData.java +++ b/src/main/java/io/vlingo/xoom/codegen/template/bootstrap/XoomInitializerTemplateData.java @@ -32,11 +32,11 @@ protected void enrichParameters(final CodeGenerationContext context) { loadImports(context, contents); - parameters().and(TemplateParameter.BLOCKING_MESSAGING, blockingMessaging) + parameters().and(TemplateParameter.APPLICATION_NAME, appName) + .and(TemplateParameter.BLOCKING_MESSAGING, blockingMessaging) .and(TemplateParameter.XOOM_INITIALIZER_CLASS, xoomInitializerClass) .and(TemplateParameter.CUSTOM_INITIALIZATION, customInitialization) - .and(TemplateParameter.REST_RESOURCES, RestResource.from(contents)) - .and(TemplateParameter.APPLICATION_NAME, appName) + .and(TemplateParameter.REST_RESOURCES, RestResource.from(context)) .and(TemplateParameter.EXCHANGE_BOOTSTRAP_NAME, resolveExchangeBootstrapName(context)); } diff --git a/src/main/resources/codegen/java/RestResource.ftl b/src/main/resources/codegen/java/RestResource.ftl index 0cabeda1..25974868 100644 --- a/src/main/resources/codegen/java/RestResource.ftl +++ b/src/main/resources/codegen/java/RestResource.ftl @@ -42,17 +42,17 @@ public class ${resourceName} extends DynamicResourceHandler { private final ${queries.protocolName} $queries; - public ${resourceName}(final Grid grid) { - super(grid.world().stage()); - <#if useAutoDispatch> - this.$stage = grid; - this.$logger = super.logger(); - <#else> - this.grid = grid; - - <#if queries?has_content && !queries.empty> - this.$queries = ${storeProviderName}.instance().${queries.attributeName}; - + public ${resourceName}(final Grid grid<#if queries?has_content && !queries.empty>, final ${queries.protocolName} ${queries.attributeName}) { + super(grid.world().stage()); + <#if useAutoDispatch> + this.$stage = grid; + this.$logger = super.logger(); + <#else> + this.grid = grid; + + <#if queries?has_content && !queries.empty> + this.$queries = ${queries.attributeName}; + } <#list routeMethods as routeMethod> diff --git a/src/main/resources/codegen/java/XoomInitializer.ftl b/src/main/resources/codegen/java/XoomInitializer.ftl index 62525bbd..557af33d 100644 --- a/src/main/resources/codegen/java/XoomInitializer.ftl +++ b/src/main/resources/codegen/java/XoomInitializer.ftl @@ -54,11 +54,11 @@ public class XoomInitializer implements XoomInitializationAware { final ${registry.className} ${registry.objectName} = new ${registry.className}(grid.world()); <#list providers as provider> - ${provider.className}.using(${provider.arguments}); + final ${provider.className} ${provider.objectName} = ${provider.className}.using(${provider.arguments}); <#list restResources as restResource> - final ${restResource.className} ${restResource.objectName} = new ${restResource.className}(grid); + final ${restResource.className} ${restResource.objectName} = new ${restResource.className}(${restResource.arguments}); final Collection> sseResources = Loader.resourcesFrom(initializer.sseConfiguration()).values(); diff --git a/src/main/resources/codegen/kotlin/RestResource.ftl b/src/main/resources/codegen/kotlin/RestResource.ftl index 37e45e67..8f06905f 100644 --- a/src/main/resources/codegen/kotlin/RestResource.ftl +++ b/src/main/resources/codegen/kotlin/RestResource.ftl @@ -38,14 +38,14 @@ public class ${resourceName} : DynamicResourceHandler { val $queries: ${queries.protocolName} - public constructor(stage: Stage) : super(stage){ - <#if useAutoDispatch> - this.$stage = super.stage() - this.$logger = super.logger() - - <#if queries?has_content && !queries.empty> - this.$queries = ${storeProviderName}.instance().${queries.attributeName} - + public constructor(stage: Stage<#if queries?has_content && !queries.empty>, ${queries.attributeName}: ${queries.protocolName}) : super(stage){ + <#if useAutoDispatch> + this.$stage = super.stage() + this.$logger = super.logger() + + <#if queries?has_content && !queries.empty> + this.$queries = ${queries.attributeName} + } <#list routeMethods as routeMethod> diff --git a/src/main/resources/codegen/kotlin/XoomInitializer.ftl b/src/main/resources/codegen/kotlin/XoomInitializer.ftl index 12258e2e..293a7b3c 100644 --- a/src/main/resources/codegen/kotlin/XoomInitializer.ftl +++ b/src/main/resources/codegen/kotlin/XoomInitializer.ftl @@ -58,7 +58,7 @@ public class XoomInitializer : XoomInitializationAware { val ${registry.objectName}: ${registry.className} = ${registry.className}(world) <#list providers as provider> - ${provider.className}.using(${provider.arguments}) + val ${provider.objectName}: ${provider.className} = ${provider.className}.using(${provider.arguments}); <#list restResources as restResource> diff --git a/src/test/java/io/vlingo/xoom/codegen/template/bootstrap/BootstrapGenerationStepTest.java b/src/test/java/io/vlingo/xoom/codegen/template/bootstrap/BootstrapGenerationStepTest.java index c1f6b347..7c286abd 100644 --- a/src/test/java/io/vlingo/xoom/codegen/template/bootstrap/BootstrapGenerationStepTest.java +++ b/src/test/java/io/vlingo/xoom/codegen/template/bootstrap/BootstrapGenerationStepTest.java @@ -11,21 +11,21 @@ import io.vlingo.xoom.TextExpectation; import io.vlingo.xoom.codegen.CodeGenerationContext; import io.vlingo.xoom.codegen.content.Content; -import io.vlingo.xoom.codegen.content.TextBasedContent; -import io.vlingo.xoom.codegen.template.OutputFile; +import io.vlingo.xoom.codegen.parameter.CodeGenerationParameter; +import io.vlingo.xoom.codegen.parameter.Label; +import io.vlingo.xoom.codegen.template.*; import io.vlingo.xoom.codegen.template.projections.ProjectionType; -import org.apache.commons.lang3.StringUtils; +import io.vlingo.xoom.codegen.template.storage.Queries; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; import javax.annotation.processing.Filer; import javax.lang.model.element.Element; -import java.io.File; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Paths; +import static io.vlingo.xoom.codegen.language.Language.JAVA; import static io.vlingo.xoom.codegen.parameter.Label.*; import static io.vlingo.xoom.codegen.template.TemplateStandard.*; @@ -63,7 +63,6 @@ public void testThatAnnotatedBootstrapIsGenerated() throws IOException { Assert.assertTrue(bootstrap.contains(TextExpectation.onJava().read("annotated-bootstrap"))); } - @Test public void testThatXoomInitializerIsGenerated() throws IOException { final CodeGenerationContext context = @@ -74,11 +73,13 @@ public void testThatXoomInitializerIsGenerated() throws IOException { loadParameters(context, false); loadContents(context); + context.registerTemplateProcessing(JAVA, loadAutoDispatchResourceHanlderTemplateData(), ORDER_RESOURCE_HANDLER_CONTENT); + new BootstrapGenerationStep().process(context); final Content xoomInitializer = context.findContent(XOOM_INITIALIZER, "XoomInitializer"); - Assert.assertEquals(7, context.contents().size()); + Assert.assertEquals(8, context.contents().size()); Assert.assertTrue(xoomInitializer.contains(TextExpectation.onJava().read("xoom-initializer"))); } @@ -98,6 +99,32 @@ private void loadContents(final CodeGenerationContext context) { context.addContent(PROJECTION_DISPATCHER_PROVIDER, new OutputFile(PERSISTENCE_PACKAGE_PATH, "ProjectionDispatcherProvider.java"), PROJECTION_DISPATCHER_PROVIDER_CONTENT); } + private TemplateData loadAutoDispatchResourceHanlderTemplateData() { + final CodeGenerationParameter autoDispatchParameter = + CodeGenerationParameter.of(AUTO_DISPATCH_NAME, "OrderResourceHandler") + .relate(QUERIES_PROTOCOL, "io.vlingo.xoomapp.infrastructure.persistence.OrderQueries") + .relate(Label.QUERIES_ACTOR, "io.vlingo.xoomapp.infrastructure.persistence.OrderQueriesActor"); + + return new TemplateData() { + @Override + public TemplateParameters parameters() { + return TemplateParameters.with(TemplateParameter.REST_RESOURCE_NAME, "OrderResourceHandler") + .and(TemplateParameter.PACKAGE_NAME, "io.vlingo.xoomapp.resource") + .and(TemplateParameter.QUERIES, Queries.from(autoDispatchParameter)); + } + + @Override + public TemplateStandard standard() { + return AUTO_DISPATCH_RESOURCE_HANDLER; + } + + @Override + public String filename() { + return standard().resolveFilename("OrderResource", parameters()); + } + }; + } + private static final String HOME_DIRECTORY = OperatingSystem.detect().isWindows() ? "D:\\projects" : "/home"; private static final String PROJECT_PATH = Paths.get(HOME_DIRECTORY, "xoom-app").toString(); @@ -128,6 +155,12 @@ private void loadContents(final CodeGenerationContext context) { "... \\n" + "}"; + private static final String ORDER_RESOURCE_HANDLER_CONTENT = + "package io.vlingo.xoomapp.resource; \\n" + + "public class OrderResourceHandler { \\n" + + "... \\n" + + "}"; + private static final String EXCHANGE_BOOTSTRAP_CONTENT = "package io.vlingo.xoomapp.infrastructure.exchange; \\n" + "public class ExchangeBootstrap implements ExchangeInitializer { \\n" + diff --git a/src/test/java/io/vlingo/xoom/codegen/template/projectgenerationsettings/ProjectSettingsGenerationStepTest.java b/src/test/java/io/vlingo/xoom/codegen/template/projectgenerationsettings/ProjectSettingsGenerationStepTest.java index 14a03fa6..98f43ff1 100644 --- a/src/test/java/io/vlingo/xoom/codegen/template/projectgenerationsettings/ProjectSettingsGenerationStepTest.java +++ b/src/test/java/io/vlingo/xoom/codegen/template/projectgenerationsettings/ProjectSettingsGenerationStepTest.java @@ -38,7 +38,6 @@ public void testThatProjectSettingsIsGenerated() throws IOException { new ProjectSettingsGenerationStep().process(context); final Content projectSettings = context.findContent(TemplateStandard.PROJECT_SETTINGS, "xoom-app-generation-settings"); - Assert.assertTrue(projectSettings.contains(TextExpectation.onJava().read("project-settings"))); } diff --git a/src/test/resources/text-expectations/java/author-rest-resource.text b/src/test/resources/text-expectations/java/author-rest-resource.text index 146063e1..217dbfa8 100644 --- a/src/test/resources/text-expectations/java/author-rest-resource.text +++ b/src/test/resources/text-expectations/java/author-rest-resource.text @@ -28,10 +28,10 @@ public class AuthorResource extends DynamicResourceHandler { private final Grid grid; private final AuthorQueries $queries; - public AuthorResource(final Grid grid) { - super(grid.world().stage()); - this.grid = grid; - this.$queries = QueryModelStateStoreProvider.instance().authorQueries; + public AuthorResource(final Grid grid, final AuthorQueries authorQueries) { + super(grid.world().stage()); + this.grid = grid; + this.$queries = authorQueries; } public Completes withName(final AuthorData data) { diff --git a/src/test/resources/text-expectations/java/default-bootstrap.text b/src/test/resources/text-expectations/java/default-bootstrap.text index 17a0f35e..90fb0b60 100644 --- a/src/test/resources/text-expectations/java/default-bootstrap.text +++ b/src/test/resources/text-expectations/java/default-bootstrap.text @@ -32,12 +32,12 @@ public class Bootstrap { QueryModelStateStoreProvider.using(grid.world().stage(), statefulTypeRegistry); CommandModelStateStoreProvider.using(grid.world().stage(), statefulTypeRegistry, ProjectionDispatcherProvider.using(grid.world().stage()).storeDispatcher, exchangeInitializer.dispatcher()); - final AuthorResource authorResource = new AuthorResource(grid); final BookResource bookResource = new BookResource(grid); + final AuthorResource authorResource = new AuthorResource(grid); Resources allResources = Resources.are( - authorResource.routes(), - bookResource.routes() + bookResource.routes(), + authorResource.routes() ); server = Server.startWith(grid.world().stage(), allResources, Boot.serverPort(), Sizing.define(), Timing.define()); diff --git a/src/test/resources/text-expectations/java/xoom-initializer.text b/src/test/resources/text-expectations/java/xoom-initializer.text index a77ddf8a..2d6aa0d8 100644 --- a/src/test/resources/text-expectations/java/xoom-initializer.text +++ b/src/test/resources/text-expectations/java/xoom-initializer.text @@ -9,6 +9,7 @@ import io.vlingo.xoom.XoomInitializationAware; import io.vlingo.xoom.scooter.plugin.mailbox.blocking.BlockingMailboxPlugin; import io.vlingo.xoomapp.infrastructure.exchange.ExchangeBootstrap; +import io.vlingo.xoomapp.resource.OrderResourceHandler; import io.vlingo.xoomapp.resource.BookResource; import io.vlingo.xoomapp.resource.AuthorResource; import io.vlingo.xoomapp.infrastructure.persistence.ProjectionDispatcherProvider; @@ -45,18 +46,20 @@ public class XoomInitializer implements XoomInitializationAware { exchangeInitializer.init(grid); final StatefulTypeRegistry statefulTypeRegistry = new StatefulTypeRegistry(grid.world()); - QueryModelStateStoreProvider.using(grid.world().stage(), statefulTypeRegistry); - CommandModelStateStoreProvider.using(grid.world().stage(), statefulTypeRegistry, exchangeInitializer.dispatcher()); + final QueryModelStateStoreProvider queryModelStateStoreProvider = QueryModelStateStoreProvider.using(grid.world().stage(), statefulTypeRegistry); + final CommandModelStateStoreProvider commandModelStateStoreProvider = CommandModelStateStoreProvider.using(grid.world().stage(), statefulTypeRegistry, exchangeInitializer.dispatcher()); - final AuthorResource authorResource = new AuthorResource(grid); + final OrderResourceHandler orderResourceHandler = new OrderResourceHandler(grid, queryModelStateStoreProvider.orderQueries); final BookResource bookResource = new BookResource(grid); + final AuthorResource authorResource = new AuthorResource(grid); final Collection> sseResources = Loader.resourcesFrom(initializer.sseConfiguration()).values(); final Collection> feedResources = Loader.resourcesFrom(initializer.feedConfiguration()).values(); final Collection> staticResources = Loader.resourcesFrom(initializer.staticFilesConfiguration()).values(); final Collection> restResources = Arrays.asList( - authorResource.routes(), - bookResource.routes() + orderResourceHandler.routes(), + bookResource.routes(), + authorResource.routes() ); final Resource[] resources =