Skip to content

Improve FreeMarker macro support in spring-webflux #23105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1 task
sbrannen opened this issue Jun 10, 2019 · 5 comments
Closed
1 task

Improve FreeMarker macro support in spring-webflux #23105

sbrannen opened this issue Jun 10, 2019 · 5 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Milestone

Comments

@sbrannen
Copy link
Member

Overview

This issue is a followup to gh-23002.

The documentation for spring.ftl in spring-webflux states the following.

To take advantage of these macros, the "requestContextAttribute" property of the FreeMarkerView class must be set to "springMacroRequestContext". This will expose a RequestContext under the name "springMacroRequestContext", as needed by the macros in this library.

Although this technically works, it is not as user-friendly as the analogous support in spring-webmvc. Specifically, AbstractTemplateView (in spring-webmvc) provides a dedicated exposeSpringMacroHelpers flag for automatically exposing the RequestContext as a model attribute for consumption in FreeMarker templates. Since this boolean flag defaults to true, users do not have to configure anything special in FreeMarkerView to use all supported macros.

In contrast, users of FreeMarkerView in spring-webflux cannot use all supported macros unless they explicitly set the requestContextAttribute property of the FreeMarkerView to "springMacroRequestContext".

I think this is cumbersome and should be improved.

Deliverables

  • Consider introducing a dedicated exposeSpringMacroHelpers flag in template-based views in spring-webflux analogous to the support in spring-webmvc.
@sbrannen sbrannen added in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement labels Jun 10, 2019
@sbrannen sbrannen added this to the 5.2 M3 milestone Jun 10, 2019
@sbrannen sbrannen added the status: pending-design-work Needs design work before any code can be developed label Jun 10, 2019
@sbrannen
Copy link
Member Author

@rstoyanchev, I would appreciate your input on this issue before I begin working on it.

@sbrannen sbrannen changed the title Improve FreeMarker support in spring-webflux Improve FreeMarker macro support in spring-webflux Jun 10, 2019
@rstoyanchev
Copy link
Contributor

Sounds reasonable.

@sbrannen sbrannen self-assigned this Jun 11, 2019
@sbrannen sbrannen removed the status: pending-design-work Needs design work before any code can be developed label Jun 11, 2019
@philwebb philwebb reopened this Jun 12, 2019
@philwebb
Copy link
Member

Unfortunately this has broken a test in Spring Boot:

java.lang.NullPointerException
	at org.springframework.web.reactive.result.view.freemarker.FreeMarkerView.getModelAttributes(FreeMarkerView.java:226)
	at org.springframework.web.reactive.result.view.AbstractView.render(AbstractView.java:203)
	at org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfigurationReactiveIntegrationTests.lambda$7(FreeMarkerAutoConfigurationReactiveIntegrationTests.java:120)
	at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:141)
	at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:53)
	at reactor.core.publisher.Mono.block(Mono.java:1519)
	at org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfigurationReactiveIntegrationTests.render(FreeMarkerAutoConfigurationReactiveIntegrationTests.java:120)
	at org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfigurationReactiveIntegrationTests.lambda$1(FreeMarkerAutoConfigurationReactiveIntegrationTests.java:62)
	at org.springframework.boot.test.context.runner.AbstractApplicationContextRunner.accept(AbstractApplicationContextRunner.java:424)
	at org.springframework.boot.test.context.runner.AbstractApplicationContextRunner.lambda$1(AbstractApplicationContextRunner.java:359)
	at org.springframework.boot.test.util.TestPropertyValues.applyToSystemProperties(TestPropertyValues.java:129)
	at org.springframework.boot.test.context.runner.AbstractApplicationContextRunner.lambda$0(AbstractApplicationContextRunner.java:357)
	at org.springframework.boot.test.context.runner.AbstractApplicationContextRunner.withContextClassLoader(AbstractApplicationContextRunner.java:369)
	at org.springframework.boot.test.context.runner.AbstractApplicationContextRunner.run(AbstractApplicationContextRunner.java:356)
	at org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfigurationReactiveIntegrationTests.defaultViewResolution(FreeMarkerAutoConfigurationReactiveIntegrationTests.java:61)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:628)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:117)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:184)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:180)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:127)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at java.util.ArrayList.forEach(ArrayList.java:1249)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at java.util.ArrayList.forEach(ArrayList.java:1249)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
	at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
	at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:137)
	at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:89)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
	Suppressed: java.lang.Exception: #block terminated with an error
		at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:133)
		at reactor.core.publisher.Mono.block(Mono.java:1520)
		... 62 more

I think the model attribute should be @Nullable and an additional guard is needed.

@sbrannen
Copy link
Member Author

Thanks for bringing this to our attention, @philwebb!

Please let me know if the fix in eef9bc8 solves the problem.

@philwebb
Copy link
Member

Looks good from a glance, I'm just trying to rerun the tests. I'll report back if there's still any problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

3 participants