-
Notifications
You must be signed in to change notification settings - Fork 43
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
[improvement][undertow] Add EndpointHandlerWrapper for filter-like features #406
[improvement][undertow] Add EndpointHandlerWrapper for filter-like features #406
Conversation
conjure-undertow-lib/src/main/java/com/palantir/conjure/java/undertow/lib/EndpointWrapper.java
Outdated
Show resolved
Hide resolved
@carterkozak I probably have to add tests but what do you think of the refactor? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs tests for Endpoints
and UndertowServices
utility functions, especially since UndertowServices
isn't otherwise used.
...ndertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/ConjureHandler.java
Outdated
Show resolved
Hide resolved
...ndertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/ConjureHandler.java
Outdated
Show resolved
Hide resolved
...runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/EndpointHandlerWrapper.java
Outdated
Show resolved
Hide resolved
.add(wrapper) | ||
.build(); | ||
return this; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ConjureHandler is meant to be immutable, configuration should occur on the builder
Maybe call the builder method initialNonBlockingEndpointHandlerWrappers(EndpointHandlerWrapper)
with strongly worded documentation warning against performing any blocking operations in this wrapper. We should also call out the order we expect them to be invoked in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added doc, but kept the name.
...ava-undertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/Endpoints.java
Outdated
Show resolved
Hide resolved
...ava-undertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/Endpoints.java
Outdated
Show resolved
Hide resolved
...ertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/UndertowServices.java
Outdated
Show resolved
Hide resolved
...ndertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/ConjureHandler.java
Outdated
Show resolved
Hide resolved
...ndertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/ConjureHandler.java
Show resolved
Hide resolved
…re/java/undertow/runtime/UndertowServices.java Co-Authored-By: Carter Kozak <ckozak@ckozak.net>
…re/java/undertow/runtime/Endpoints.java Co-Authored-By: Carter Kozak <ckozak@ckozak.net>
would it be possible to get a top-line description of why we want this functionality, either on the PR, in docs or in code? |
@markelliot updated the title and description |
@rubenfiszel thanks! The description sounds pretty great, but isn't quite what the code gets us right now. Could we do something like this?
Somewhat inspired by https://github.com/palantir/tritium/blob/develop/tritium-api/src/main/java/com/palantir/tritium/event/InvocationEventHandler.java. The idea is that it makes it impossible to mess up/not call the original handler, still gives options to code an aspect. If we're after filters and want the ability to stop processing, may want to have something that's more explicitly a predicate |
@markelliot I'm somewhat skeptical of that approach because it doesn't allow us to try/finally around execution, which can lead to leaks. While I agree that it may be cleaner for some functionality, it has drawbacks for others. I want to avoid over-engineering a new api unless it's a strict improvement or would solve problems we have encountered. A few benefits of the current proposal:
Given that, I would propose that we build the aop extension in a way that it can produce a |
The try/finally semantics are exactly why one would do this — and it’s otherwise not possible to guarantee that all implementers will correctly perform that kind of wrapping unless we’re a little more structured. The current proposal also seems way more likely to leak things like Endpoint (my sample was directional, drop the args you don’t want to have), and things like selective application can easily be solved with an additional predicate. I’m not a fan of the big hammer of being able to wholesale replace an Endpoint, and don’t think this is the appropriate way to allow developers to add cross-cut functionality. |
@markelliot I share the concerns of @carterkozak .The experience we have had with WC is that the Undertow model of HttpHandler/HandlerWrapper is pleasant to write in, concise/readable and give us sufficient flexibility to achieve a diverse set of features so far without having us to go through any escape hatch. One of the goal of having your proposed interface is to make it "impossible to mess up/not call the original handler". However, in some cases, the control that you would want to apply is precisely to not call the original handler. That being said, I agree that onSuccess, onFailure is a better try/catch replacement and that your proposal bring more safety and structure. I think in an ideal world, what we would want is to be able to use the PR's EndpointHandlerWrapper for WC's internals and core libraries, but to force other final consumers to go through your proposed interface of EndpointHandlerWrapper if they desire to implement their own filter-like logic. It is possible to achieve some of this desired goal by taking advantage of the fact that your proposed EndpointHandlerWrapper can extend the PR's one and that we can strongly recommend final consumers to extend your more structured interface when implementing their custom filter logic. |
Okay, perhaps we could make clear the possible downsides of the approach proposed here:
|
@markelliot @carterkozak @iamdanfox |
...ava-undertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/Endpoints.java
Outdated
Show resolved
Hide resolved
...undertow-lib/src/main/java/com/palantir/conjure/java/undertow/lib/UndertowConjureFilter.java
Outdated
Show resolved
Hide resolved
...runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/EndpointHandlerWrapper.java
Outdated
Show resolved
Hide resolved
...ndertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/ConjureHandler.java
Outdated
Show resolved
Hide resolved
* If you call this multiple time, the last wrapper will be applied first, meaning it will be wrapped the | ||
* previously added {@link EndpointHandlerWrapper}s. | ||
*/ | ||
public Builder addEndpointHandlerWrapperBeforeBlocking(EndpointHandlerWrapper wrapper) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mind adding a test for this?
...ndertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/ConjureHandler.java
Outdated
Show resolved
Hide resolved
...ndertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/ConjureHandler.java
Outdated
Show resolved
Hide resolved
...tow-runtime/src/test/java/com/palantir/conjure/java/undertow/runtime/ConjureHandlerTest.java
Outdated
Show resolved
Hide resolved
Refactor ConjureHandler.build for readability. All handler wrapers are created together in a single list so it's easier to quickly grok the order of operations.
Pushed commit 00820bb |
@carterkozak LGTM. I did a small aesthetic change of using |
I'm just worried that it's less clear in this structure that the pre-defined ConjureHandler |
👍 |
Changes in this PR
This PR enables features that are similar to the concept of Jersey filters. Undertow is meant to be used by chaining
HttpHandler
s. The firstHttpHandler
applies logic and then pass it to the next handler. With this structure, handlers always have control over the control flow and can apply logic, before and after processing by the next handlers. This is similar to the desired behavior of jersey Filters.After that PR, it is possible add arbitrary behaviours pre and post processing of the underlying resource by leveraging a concept similar to Undertow's
HandlerWrapper
: theEndpointHandlerWrapper
. It wraps the handler of an endpoint, taking potentially advantage of all informations contained in anEndpoint
.Below an example of an EndpointHandlerWrapper with pre and post processing.
EndpointHandlerWrapper
as a functional interface of the formHttpHandler wrap(Endpoint endpoint)
. WarningEndpointHandlerWrapper
s are meant to be used only by library writers and webserver maintainers.addEndpointHandlerWrapperBeforeBlocking(EndpointHandlerWrapper wrapper)
to theConjureHandler
class to give the possibility of adding non-blockingEndpointHandlerWrapper
to all registeredUndertowService
s.The utilities to easily wrap an
UndertowService
with anEndpointHandlerWrapper
have been purposefully not included as this is a behavior that we do not want to encourage.One can still use the following snipper to achieve this behavior.
Possible downsides?
None