-
Notifications
You must be signed in to change notification settings - Fork 77
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
RequestContextController spec impossible to implement #477
Comments
What exactly is broken with it? I think it was deliberately designed so that both, I don't really see an issue with that, especially the approach you showed under
From the specification part you quoted,
Why would finally block be too early? I suppose I am missing the use case you are referring to, could you try to lay out the scenario in which current |
I assume the problem with case b) is that you can potentially call boolean activated = rcc.activate();
try {
...
} finally {
if (activated) {
rcc.deactivate();
}
} That is, IIUC, the recommended way to use RCC. |
@Ladicek there are 2 recommended ways. What you wrote is the option a) in my original ticket. But there is also usage option b.) with the finally block. My problem is that there is no clear rule that activate/deactivate have to be called symmetrically or not. Which means either we close to early or miss a cleanup. That problem does not appear when using the interceptor, but only if using the I think I have an idea how to implement it different than I originally did. It will solve a few more problems, but still not all. |
Recommended where? The spec doc doesn't show this, nor does the javadoc of RCC. |
I'd say that even the I believe that for the simple cases, it would be a lot better if we provided something like // probably should have 2 variants: for Runnable and for Callable
rcc.withRequestContext(() -> {
...
}); but there still are the more complex cases where the request context must be activated in one method and deactivated in another one. Those should be hopefully rare, only in infrastructure code. People doing that should understand that, indeed, activation/deactivation must go in pair, otherwise lifecycle can't be guaranteed, memory leaks may happen and in general it's not a nice place to be at. |
the original design proposed in the discussions was always with the boolean gotActivated flag. That's the reason the boolean return value was introduced in the first place. Otherwise you don't need it, isn't? |
Most of these cases can likely be covered using
+1
Yea, the spec is written so that the boolean lets you know whether you activated the context or not. Like Ladislav said, you could/should use that even in |
C'mon, that's not a serious answer, isn't? :) Where in the spec does it say that it must get called symmetrically? |
What I mean is that this is an API (and not the only one) that carries some potential risk of bad usage and requires user awareness. However, at the same time provides a functionality that justifies the risk. What's wrong with that?
If this issue is all about adding "if you activate the context, you are responsible for deactivating it" into RCC, then sure, we can add that. Otherwise, I still don't see what you are getting at. |
+1 to fix this, it is just fully broken right now and there is no way to implement it due to conflicting activate/deactivate javadoc. I guess we should get a quick maintenance release (outside EE train) to enhance the controller deprecating these two method and making activate returning a Deactivation callback (a Runnable like) instead of a boolean:
caller would then do
Not sure we can keep activate name or need another controller API but currently spec can not be implemented at the risk of thread safety or/and mem leaks (+ functional issues since predestroy are not called). Indeed, at least fixing the javadoc is also needed by saying that calling deactiavte if activate returns false will not be a defined case (ie dont do it). |
It doesn't have to be -- but if it isn't, you get yourself into all kinds of troubles that follow from the fact that context objects for normal scopes are essentially global state. Which IMHO naturally follows from the fact that they are associated with threads, as the spec says. I would totally be for adding some clarification to the javadoc, to improve user friendliness, but when it comes to normative text in the spec, IMHO, all the necessary provisions are already there.
If you don't call the deactivation callback, or call it too soon / too late, you have the exact same problem. So how does that improve the situation? |
@Ladicek no issue with the callback since nested calls will return a noop callback, this is how start/stop context are generally implemented in servers. I'm not sure what you meant with "all the necessary provisions are already there" but since activate/deactivate javadoc are not consistent, the behavior is not so we'll break applications/usage by clarifying it so thought a new API can make it less ambiguous but if you think we should reuse the one we have and enforce deactivate to not be called when false is returned it can work too with some risk (and to be honest i'm not sure how low or high it is). |
Your nested calls returning a noop callback directly correspond to current Now, I'd really like to understand how the
I think that makes sense. I can't see a contradiction there, could you please point that out? Or is there a contradiction between the javadoc and the spec? I can't see that either. (Also, I think someone mentioned thread safety in this thread. The spec explicitly says that |
@Ladicek ok,
Makes this working. Since the enable/disable calls are not always symmetric you can't have a counter or alike, since the calls can be symmetric you can't get a boolean (need to disable at last deactivate call), since there is no enclosing cleanup you can't cleanup lazily (this API works in SE where request context is not automatically cleaned up). What I envision is that the spec tend to say you must lookup a new instance each time you use it but it is a shame in terms of usage - requires to NOT use @Inject and to store controller instance - so I think spec should be refined to be consistent and usable by application - as used today, ie with inject. |
I assume these are 3 different examples to be considered in isolation. Further, I assume that the request context is not active at the beginning of each example. Then: // RC not active
c.activate(); // returns true
// RC active
c.deactivate();
// RC not active // RC not active
c.activate(); // returns true
// RC active
c.deactivate();
// RC not active
c.deactivate(); // throws
// unreachable // RC not active
c.activate(); // returns true
// RC active
boolean enabled2 = c.activate(); // returns false
// RC active
if (enabled2) {
// unreachable, enabled2 is false
c.deactivate();
}
// RC active
c.deactivate();
// RC not active I don't quite see the problem here. I guess I'm missing something, but I don't know what. |
@Ladicek In reality the activation is nested over different independent classes which obviously are not required to know about each other. |
Please provide a realistic example that exhibits problems, then :-) |
I mean -- I believe that at this point, we conclusively established that RCC is possible to implement (contrary to this issue's title). We also established that there are code patterns that make RCC usage safe. We're just debating problematic scenarios that stem from incorrect usage of RCC. That incorrectness stems from the nature of global state and has nothing to do with the CDI specification. There are certainly ways how to improve the RCC API to make correct RCC usage easier. If we want to debate that, sure -- I'm all for it. |
@Ladicek main issue is to use it right in 90% of applications, you must So guess minimum is to make the spec more explicit about the usage (with a sample maybe?) and we should think to a way to avoid the provider probably? |
I can see how it can work. But it's not so obvious to users. I think it would be wise to add an example. Most people are probably not aware that they need to use a |
@struberg just wondering, is it still impossible to implement, or did you manage to do it in OWB 4? |
Yes, it's mostly a documentation issue. But it's really tricky to do right. We should document this better. Search for the class on github and you'll find tons of wrong usage There is a lot of different usage. And most are missing parts. or It's really a pitty, even people really into CDI fall into this trap. The problem is that correct usage requires to use BOTH the flag AND finally. Without the finally block any Exception will leave the RequestContext open. And without the flag all nested usage is broken. So to be more clear: this never was a real implementation issue, but the API design and description did imo imply that no special care is needed. But that's misleading, and we should point this out in the spec. CC-ing @manovotn for improving this in the next spec iteration. |
The
It is true that using the flag (return value of Anyway, thanks for confirming that there are no implementation issues. I'll leave this issue open for a few more days and then close if noone objects. |
I think we should update the example in the Javadoc to include checking the return value from |
Agreed. |
Sorry but isn't that totally unrelated? The real question is whether the state of the RequestContext is shared. And that SURELY is the case on the very same thread. Regardless how many context controller instances there are - they all control the very same RequestContext. So if you have multiple cases where the RequestContext gets activated in this very thread (even downstream in userland code) you will end up with broken state. |
That is correct.
But that is not. If an RCC didn't activate the request context -- because it was already active --, calling |
That would mean that the RCC needs an additional ThreadLocal with this information. As the RCC could be used in concurrent situations. And then what happens to the ThreadLocal if the TCC just gets thrown away? This just cries 'memleak' quit loudly and there is no finally block to prevent it. |
@struberg not another one cause you can use the same but another state yes. |
I'm not really happy with the RequestContextController. And it appears to me that the spec is broken.
Here is the important part from the spec:
The problem is that there are 2 ways to use that part
a.)
b.)
The problematic part is nesting.
In case a) we got maybe 7 calls to activate() but only 1 to deactivate.
In case b) we got 7 calls to activate and 7 to deactivate();
There is simply no way to implement this in a clean way. A simple boolean flag does not help because of concurrency. A ThreadLocal does not help much either. If we use a ThreadLocal we potentially leak memory in case of a). If we close immediately we potentially close way too early in case b).
The text was updated successfully, but these errors were encountered: