-
Notifications
You must be signed in to change notification settings - Fork 926
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
Introduce a way to unwrap RequestContext
s
#4308
Conversation
Codecov Report
@@ Coverage Diff @@
## master #4308 +/- ##
=========================================
Coverage 73.51% 73.51%
- Complexity 17663 17673 +10
=========================================
Files 1504 1504
Lines 66100 66112 +12
Branches 8338 8341 +3
=========================================
+ Hits 48594 48603 +9
- Misses 13292 13294 +2
- Partials 4214 4215 +1
Help us with your feedback. Take ten seconds to tell us how you rate us. |
9ed815f
to
b6b53b6
Compare
b6b53b6
to
a4da669
Compare
core/src/main/java/com/linecorp/armeria/internal/client/ClientRequestContextExtension.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/common/RequestContextWrapper.java
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/internal/common/RequestContextExtension.java
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/internal/client/ClientThreadLocalState.java
Show resolved
Hide resolved
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.
Looks great! 😄
brave/src/main/java/com/linecorp/armeria/server/brave/BraveService.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/internal/common/RequestContextExtension.java
Show resolved
Hide resolved
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.
Thanks, @jrhee17! 💯
@@ -464,6 +465,11 @@ default ByteBufAllocator alloc() { | |||
@MustBeClosed | |||
SafeCloseable push(); | |||
|
|||
@Override | |||
default RequestContext unwrap() { |
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.
master
branch has been merged. Let's do the same work for unwrapAll()
. 😉
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.
Let's do the same work for unwrapAll()
Do you mean override the return type for unwrapAll
?
I thought it's difficult to do this with the current constraints provided by Unwrappable
😅
ref: #4338 (comment)
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.
Couldn't we safely cast the return type to RequestContext
?
@Override
default RequestContext unwrapAll() {
return (RequestContext) Unwrappable.super.unwrapAll();
}
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.
Sorry, I guess I should've made my comment on the other PR clearer 😅
To be precise, as you said we can override the return type as RequestContext
for objects that are "unwrappable".
However, I'm unsure how the API should be designed for objects that wrap an Unwrappable
(i.e. via AbstractUnwrappable
)
- If
AbstractUnwrappable#unwrapAll
returns anObject
:
In this caseAbstractUnwrappable#unwrapAll
returns an Object whereasRequestContext#unwrapAll
returns aRequestContext
, which causes a return type collision.
e.g.
'unwrapAll()' in 'com.linecorp.armeria.common.util.AbstractUnwrappable' clashes with 'unwrapAll()' in 'com.linecorp.armeria.common.RequestContext'; attempting to use incompatible return type
- If
AbstractUnwrappable#unwrapAll
returns T:
I'm not sure if there's type safe way to know the innermost type of a wrappable.
final Foo foo = new Foo();
final Bar<Foo> bar = new Bar<>(foo);
final Qux<Bar<Foo>> qux = new Qux<>(bar);
I guess it's not too late to modify to a type unsafe API though 😅
e.g.
<U extends Unwrappable> U unwrapAll()
ref: #4338 (comment)
Let me know if you have a better idea though 😅
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.
'unwrapAll()' in 'com.linecorp.armeria.common.util.AbstractUnwrappable' clashes with 'unwrapAll()' in 'com.linecorp.armeria.common.RequestContext'; attempting to use incompatible return type
The error seems to happen in RequestContextWrapper
. How about overriding the return type of unwrapAll()
in RequestContextWrapper
?
I don't see that we need to change the return type of AbstractUnwrappable#unwrapAll
.
The subtype of AbstractUnwrappable
or Unwrappable
freely overrides the return type if necessary.
public abstract class RequestContextWrapper<T extends RequestContext>
extends AbstractUnwrappable<T> implements RequestContext {
@Override
public RequestContext unwrapAll() {
return (RequestContext) super.unwrapAll();
}
}
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.
Ah, I think you're right. I guess we can override the return type for wrapper types only. Done!
Thanks, @jrhee17! You paid off our technical debt. 😆 |
Motivation: Armeria has some core logic which assumes that the concrete default implementations of `[Client|Service]RequestContext` are `Default[Client|Service]RequestContext`. e.g. If the `ClientRequestContext` isn't an instance of `DefaultClientRequestContext`, then the response timeout may not be respected. https://github.com/line/armeria/blob/5edaef039abfd047f152a63cde38b46094c8353f/core/src/main/java/com/linecorp/armeria/client/HttpResponseDecoder.java#L401-L407 This usage pattern mostly stems from the fact that `[Client|Service]RequestContext` are public APIs that users are exposed to a lot (especially in decorators). However, Armeria developers are skeptical of adding public methods that are either 1) very specific and used only in core logic 2) have the potential to break behavior if used incorrectly. Up to now, we have added such methods to the default implementation and checked with `instanceof` when invoking such methods. With the introduction of line#4232, we are now planning on introducing and extensively utilizing wrapper classes for `[Client|Service]RequestContext`. However, using wrapper classes means previous assumptions of `instanceof Default[Client|Service]RequestContext` will be invalid. In this pull request, I propose that: 1) `RequestContext` now extends `Unwrappable`, allowing users to peel `RequestContext`s to the intended type. 2) We expose another interface `[Client]RequestContextExtension`, which contains "extension" methods for `ClientRequestContext`, `RequestContext`. In this interface, we can add methods that Armeria uses internally without worrying about public API exposure. 3) `Default[Client|Service]RequestContext` doesn't need to be exposed as a public API. We may simply expose the interface to users and hide the detailed implementation like we do for the `Http[Response|Request]` series. - Note: This move was handled in this PR mainly due to `CancellationScheduler`. While extracting `ClientRequestContextExtension`, `CancellationScheduler` which is an internal class was exposed as the return value of `#responseCancellationScheduler`. I had the choice of exposing `CancellationScheduler`, or hiding `DefaultClientRequestContext`. I chose the latter and also moved `DefaultServiceRequestContext` for consistency. Modifications: - `RequestContext` now extends `Unwrappable`. `RequestContextWrapper` now extends `AbstractUnwrappable` - Convert usages of `instanceof DefaultClientRequestContext` to unwrap instead - e.g. `ctx instanceof DefaultClientRequestContext` -> `ctx.as(ClientRequestContextExtension.class)` - `RequestContextExtension`, `ClientRequestContextExtension` has been added to an internal package - `DefaultClientRequestContext`, `DefaultServiceRequestContext`, `NonWrappingRequestContext` have been moved to an internal package - `pathWithQuery` is moved to `ClientUtil` and made public - `ClientThreadLocalState` is moved to an internal package and made public - Class hierarchy has been modified for `RequestContext` - Now there is an extra layer between `ClientRequestContext` and `DefaultClientRequestContext`: - `ClientRequestContext` -> `ClientRequestContextExtension` -> `DefaultClientRequestContext` - Now there is an extra layer between `RequestContext` and `NonWrappingRequestContext` - `RequestContext` -> `RequestContextExtension` -> `NonWrappingRequestContext` Before: <img width="300" alt="Screen Shot 2022-06-24 at 12 00 49 PM" src="https://user-images.githubusercontent.com/8510579/175453672-15dbdf09-4c2e-4afa-a186-a2147e97afa2.png"> After: <img width="300" alt="Screen Shot 2022-06-24 at 12 01 15 PM" src="https://user-images.githubusercontent.com/8510579/175453689-3c00edde-7209-4d1c-9480-e776c58537e0.png"> Result: - Developers may add more utility functions to `RequestContext` without worrying about public API exposure - Armeria tries to unwrap `RequestContext` instead of simply checking the type.
Motivation: RequestContextWrapper$delegate is deprecated since #4308 Modifications: - replaced all occurrences of delegate() with unwrap()
Motivation:
Armeria has some core logic which assumes that the concrete default implementations of
[Client|Service]RequestContext
areDefault[Client|Service]RequestContext
.e.g. If the
ClientRequestContext
isn't an instance ofDefaultClientRequestContext
, then the response timeout may not be respected.armeria/core/src/main/java/com/linecorp/armeria/client/HttpResponseDecoder.java
Lines 401 to 407 in 5edaef0
This usage pattern mostly stems from the fact that
[Client|Service]RequestContext
are public APIs that users are exposed to a lot (especially in decorators). However, Armeria developers are skeptical of adding public methods that are either 1) very specific and used only in core logic 2) have the potential to break behavior if used incorrectly.Up to now, we have added such methods to the default implementation and checked with
instanceof
when invoking such methods.With the introduction of #4232, we are now planning on introducing and extensively utilizing wrapper classes for
[Client|Service]RequestContext
. However, using wrapper classes means previous assumptions ofinstanceof Default[Client|Service]RequestContext
will be invalid.In this pull request, I propose that:
RequestContext
now extendsUnwrappable
, allowing users to peelRequestContext
s to the intended type.[Client]RequestContextExtension
, which contains "extension" methods forClientRequestContext
,RequestContext
. In this interface, we can add methods that Armeria uses internally without worrying about public API exposure.Default[Client|Service]RequestContext
doesn't need to be exposed as a public API. We may simply expose the interface to users and hide the detailed implementation like we do for theHttp[Response|Request]
series.CancellationScheduler
. While extractingClientRequestContextExtension
,CancellationScheduler
which is an internal class was exposed as the return value of#responseCancellationScheduler
. I had the choice of exposingCancellationScheduler
, or hidingDefaultClientRequestContext
. I chose the latter and also movedDefaultServiceRequestContext
for consistency.Modifications:
RequestContext
now extendsUnwrappable
.RequestContextWrapper
now extendsAbstractUnwrappable
instanceof DefaultClientRequestContext
to unwrap insteadctx instanceof DefaultClientRequestContext
->ctx.as(ClientRequestContextExtension.class)
RequestContextExtension
,ClientRequestContextExtension
has been added to an internal packageDefaultClientRequestContext
,DefaultServiceRequestContext
,NonWrappingRequestContext
have been moved to an internal packagepathWithQuery
is moved toClientUtil
and made publicClientThreadLocalState
is moved to an internal package and made publicRequestContext
ClientRequestContext
andDefaultClientRequestContext
:ClientRequestContext
->ClientRequestContextExtension
->DefaultClientRequestContext
RequestContext
andNonWrappingRequestContext
RequestContext
->RequestContextExtension
->NonWrappingRequestContext
Before:
After:
Result:
RequestContext
without worrying about public API exposureRequestContext
instead of simply checking the type.