micronaut-views
micronaut-views copied to clipboard
Feature: Reactive Soy (v2)
This changeset amends the Soy view renderer, to support a new interface called ReactiveViewRenderer. The new interface generates a Flowable instead of a Writable, so that reactive chunked responses can be generated when the renderer is waiting on Future values to resolve.
Caveat: see discussion on #26 (v1 of Reactive Soy), in which @graemerocher and I discuss the ability to dynamically switch to chunked responses, which hasn't landed yet. Until that time, this PR will block forever/fail on unresolved futures in the render context. Workarounds for this might include:
- always sending a chunked response, for now (i.e. opting out of dynamic switching, at the cost of some client efficiency)
- always sending a synchronous response, for now, i.e. resolving futures before rendering (at the cost of some server efficiency)
- working upstream to enable dynamic decision making for chunked responses (this is more invasive, of course, but also higher impact, and probably exceeds the scope of my abilities with Micronaut)
@graemerocher / @jameskleeh your feedback on the above would be very welcome. particularly, the ideas in the description about workarounds for the chunking issue.
i'm hoping to get this PR in even if the chunking issue isn't quite ready yet, if you are open to it (we are increasingly using micronaut in prod and this would help on a number of fronts)
all of the above respectfully, of course, and thank you again for your time
also, this PR supersedes the last one, it's based off the latest master and cleans up my previous approach in a few ways. the SoyContext feature was not in yet, that one is new to this PR.
@sgammon Sorry for the late reply. I debugged this and it is essentially writing the view as a single chunk to the response which is not ideal. I'm not sure that is desirable?
I think we should prioritize fixing this problem as it won't work as designed. Could you raise an issue in Micronaut core to fix the handling of stream response detection?
@graemerocher sure thing, i'll raise a bug on core (thank you for your response!)
update: filed as micronaut-projects/micronaut-core#2902
Depends on #37
@sgammon can you please rebase master branch and fix the conflicts, please?
@ilopmar afaik we are waiting on upstream changes here still (specifically, the ability to switch the chunked serving on or off depending on chunk count)
@graemerocher can you advise?
@sgammon the upstream changes went into Micronaut 2.0 so the PR needs rebasing on Micronaut 2.0 and it should be good to go
@graemerocher understood. i'll do that this week or over the weekend, that's great news!
to use the switched chunk response, do i just return the proper response type based on what needs to happen?
This PR looks very promising.
@sgammon @graemerocher Any updates?
@lpellegr this needs to be cleaned up and converged with our internal forks. it's working in production and over the past few months has successfully served well over 1m requests. with precompiled templates, it's insanely fast.
if you are interested in adopting, let me know and i can move that up on my plate. we use those forks from @sgammon/elide, which is a meta-framework that wraps Micronaut.
@sgammon Thanks for your answer. I would really love to adopt this. Precompiled templates are also something I started to look at but the current micronaut-views documentation looks quite obscure regarding this point.
@lpellegr i hope it would be okay to start with a code sample in Bazel, that would be much easier. i haven't yet approached build tooling for precompiling soy templates in, say, Gradle or Maven, but it's theoretically the same as any other route (i.e. the Micronaut/Soy layer just expects templates to exist on the classpath - they are compiled Java classes at a computable package path based on the template namespace)
the micronaut/soy layer is in charge of converting a @View-annotated controller method into a Soy render routine. it captures the return type of the method, and provides it as Soy render context. so, in your method, you return a map of data, which is converted into Soy properties and provided to the template mentioned in your @View annotation.
as an example, in Kotlin (this would be an Elide controller, but in this case it's functionally the same as Micronaut):
some_page.soy
{namespace some.soy.path}
{template templateName}
{@param name: string /} // Name for the salutation.
<b>Hello, {$name}!</b>
{/template}
SomeController.kt
@Controller
class SomeController {
@View("some.soy.path.templateName")
@Get("/", produces = ["text/html;charset=UTF-8"])
fun home(request: HttpRequest<*>): Map<String, String> {
// do something to calculate context
return mapOf("name" to "World")
}
}
curl http://localhost:8080/
<b>Hello, World!</b>
I do see some Soy plugins for Gradle and Maven, or, if you are using Bazel (or willing to learn it, it's awesome), you can use the compiler directly via rules_closure.
@sgammon Thanks for your help (and happy new year!). I was able to generate compiled templates using Gradle and to use them. However, using compiled templates requires to override SoyFileSetProvider#provideCompiledTemplates with something like:
@Override
public SoySauce provideCompiledTemplates() {
return new SoySauceBuilder().build();
}
Adding classes in the classpath is not enough. Also, as soon as the previous method is overridden, if classes for compiled templates are not found in the classpath, then rendering always fails, and live compilation is not triggered. I don't know if the current behavior is normal but, personally, I found the documentation misleading:
If compiled templates can’t be located by the SoyFileSetProvider, templates are pre-compiled into bytecode at server startup
Another thing I struggled with and required me to use a customized version of micronaut-views is Soy ij data. A dedicated issue has been opened. I would love to have your thought.
@lpellegr ah drat, it looks like there are some crossed wires here - on our internal fork, we have a fix for the IJ data and I can also dig up a sample for how we load templates. let me get back to you on this, and happy new year back! :)
@sgammon Any news?
This has breaking changes. Thus, it will need to wait until the next mayor version of Micronaut Views module.
@sdelamo I just noticed the first milestone of Micronaut 3.0. Any chance to get this PR sorted?
@lpellegr this was only ever waiting on the ability to dynamically switch between chunked responses and unary (buffered) responses, based on encountering a Future (or descendent) during the render flow.
if there's a way to trigger that now, we can amend this code to do that and merge -- otherwise, we could simply decide to treat it as an enhancement later. IIRC, right now, it will simply block rendering and wait for the future to complete, which is sub-optimal in many circumstances
we have been using this in production for some time now, as i know you are as well. so i think the code is very stable, it just needs a rebase, quick cleanup, and the mechanism to return chunked bytes instead of a single response.
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.