-
Notifications
You must be signed in to change notification settings - Fork 81
specification of asynchronous invokers #907
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
base: main
Are you sure you want to change the base?
Conversation
|
This is an initial proposal. I assume you will have some questions about certain aspects of the API. Here are my questions that I don't have a full answer to at the moment:
|
cd4199c to
3a3fd91
Compare
| ---- | ||
|
|
||
| An _async handler_ is a service provider of `jakarta.enterprise.invoke.AsyncHandler`. | ||
| An async handler must not declare a provider method; it must declare a provider constructor. |
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.
Provider constructor?
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.
This is specified in java.util.ServiceLoader.
I'm actually not sure if we need this provision, but if we allow provider methods, the service provider doesn't have to implement AsyncHandler and figuring out the async type would be harder. I'm not sure if anyone actually uses provider methods; the other parts of the CDI spec ignore them completely...
|
One other question I don't currently have an answer for: what if there's multiple async handlers for the same async type? |
I think we should allow
I think a recommendation makes sense. If you have a listener model, I don't think you can guarantee that.
It's straightforward when the return type represents the async task. If it's an argument, then I don't think there's an obvious answer as to whether it'll call the completion task or not. I would suggest that we say that cleanup will happen immediately if the invocation throws an exception, and that the container must tolerate the completion being run as well. |
Hmm, that makes sense. So something like the following? The first sentence is copied, the second is new.
|
I'm a bit concerned about this too since I think it's likely that multiple libraries will provide an async handler for common async types where support from the container is not required. We could say that any of them may be called, which is a bit unpredictable, but in theory fine as long as all the deployed async handlers are correct. We could say that it's a deployment error, though that might be difficult for the user to resolve if the handlers are provided by libraries. I'm also a bit concerned about the scope of an async handler in a multi-module scenario. If I deploy an async handler in a module, is it available for use by the whole application, or only invocations of beans in modules which have visibility of the async handler module. |
That looks good to me. Maybe tweak it slightly:
|
|
I was thinking about the issue with multiple async handlers existing for the same async type. As we discussed on the CDI call today, we could add
To avoid issue 2, we'd have to still require that async handlers are service providers of the There are some usability issues with this, but they are minor compared to the issue we'd solve. I'll think more about it. |
3a3fd91 to
e82ba10
Compare
|
I've adjusted this PR to what I described in my last comment. Async handlers must still be service providers, and they are selected explicitly using I'm not super happy about this proposal, but it isn't bad either. |
|
As I mentioned on the CDI call earlier this week, I wanted to list all possible issues the current design is supposed to prevent. Here goes.
If we removed the need to register async handlers (via the service loader mechanism), we would rely on users to never forget to call If we removed the [need to call the] All in all, I think there's no better alternative to the current design. |
e82ba10 to
793d97c
Compare
|
I slightly adjusted the API (renamed |
| * This method must be called when the target method is asynchronous and must not be called | ||
| * when the target method is not asynchronous. |
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.
Do we want to restrict calling withAsync(null) for non-asynchronous methods?
I would change this to "must not be called with a non-null value when the target method is not asynchronous"
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.
I would prefer to keep it, because it's nicely symmetric: if the target method is async, this method must be called, and if the target method is not async, this method may not be called.
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.
I get that, but it does mean that if another library is added which adds an async handler for your return type, you need to call withAsync(null) if that library is present and not call it if it isn't present.
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.
I think that situation is weird enough that users want to know about it instantly. (Also it seems quite unlikely. There's not that many async types out there.)
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.
It does seem unlikely, but I really dislike the idea of having to call the method if an unrelated library is present and not call it if the library is not present and having no way of writing the code that works in both situations.
I guess you could argue that the developer can use reflection to detect whether the other library is present, but that seems more nasty and error prone.
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.
I would say if you found yourself in such situation, you want know about it and you need to think about it. Using a null handler may be a valid solution, but I'd argue that more often than not, there will be other things you want to do.
| * This method must be called when the target method is asynchronous and must not be called | ||
| * when the target method is not asynchronous. | ||
| * | ||
| * @param asyncHandler the {@link AsyncHandler} to use; may be {@code null} |
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.
| * @param asyncHandler the {@link AsyncHandler} to use; may be {@code null} | |
| * @param asyncHandler the {@link AsyncHandler} to use, or {@code null} to indicate that the invoker is synchronous |
| ---- | ||
|
|
||
| If the target method is asynchronous, the `withAsync()` method on `InvokerBuilder` must be called, otherwise deployment problem occurs. | ||
| If the target method is not asynchronous, the `withAsync()` method on `InvokerBuilder` must not be called, otherwise deployment problem occurs. |
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.
| If the target method is not asynchronous, the `withAsync()` method on `InvokerBuilder` must not be called, otherwise deployment problem occurs. | |
| If the target method is not asynchronous, the `withAsync()` method on `InvokerBuilder` must not be called with a non-`null` value, otherwise deployment problem occurs. |
If we make the corresponding change in the Javadoc.
|
Need to think about (and likely mention in the spec) classloading restrictions:
|
I was thinking about this yesterday and I think we forgot to consider one other option: this could just be a deployment problem. The user can overcome this by explicitly configuring the class of System properties come to mind, but those are JVM-global, so probably not a good fit for multi-deployment application servers. (They would be fine for CDI SE though.) Using I think this would actually allow simplifying the API to the bare minimum (just the One possible problematic scenario would remain: user has an asynchronous method, but doesn't include an Ideas? |
793d97c to
f7c85fe
Compare
|
Rebased and pushed another commit (so it's easy to revert) that alters the specification per my previous comment. |
It could be, my initial concern is that it makes it harder to write portable libraries, but based on the discussion today, I'm thinking again about whether that might be unfounded, and maybe I don't hate it so much 😉 Here's the scenario I was imagining: I write a framework built on CDI which uses invokers (I'm thinking of something similar to the quarkus mcp server extension). I want my framework to support mutiny, so I allow methods to return I was imagining that my framework would:
However, based on the discussion today, maybe it would actually be built like this:
In the second case, staying portable is easier, but only because you're effectively saying the user has to provide your dependencies manually. However, it does also mean that the user is the one with the ability to fix the deployment problem. We do lose the ability for anyone to detect if the framework is deployed with the In general I think the framework has an interest in detecting this situation because it's them that will have to deal with users raising issues where the root cause is that the user didn't provide an
I do agree that keeping the API simpler is preferable. After weighing it up I don't mind losing the ability to force synchronous destruction for asynchronous methods and making the validation a little less strict if it keeps things simpler. |
|
Sorry for ignoring this topic for so long. I think your objections make sense and I'd be fine with removing the 2nd commit. But it also feels like it should be straightforward to add the ability for CDI-based frameworks to indicate that they require presence of |
Fixes #859