Skip to content

Remove need to instantiate Application classes twice for OpenAPI support#2829

Merged
tjquinno merged 4 commits intohelidon-io:masterfrom
tjquinno:openapi-single
Mar 22, 2021
Merged

Remove need to instantiate Application classes twice for OpenAPI support#2829
tjquinno merged 4 commits intohelidon-io:masterfrom
tjquinno:openapi-single

Conversation

@tjquinno
Copy link
Copy Markdown
Member

@tjquinno tjquinno commented Mar 4, 2021

Resolves #2799

If multiple Application classes exist in the user's app, we build separate OpenAPI models for each one. As part of that, we need to invoke each Application's getClasses and getSingletons methods. The OpenAPI CDI extension needs to act before the server's CDI extension actually creates the Application instances because the OpenAPI extension needs to adjust the routing to include the OpenAPI endpoint. But at that time, the server had not created the Application instances. So to invoke getClasses and getSingletons the OpenAPI CDI extension would use CDI to create unmanaged instances of each Application, invoke those methods, and then dispose of the unmanaged instances.

This caused @PostConstruct methods to be invoked twice, once for the unmanaged instances and again when the server created the "live" instances. Developers found this disconcerting.

Avoiding the double-instantiation involves splitting the OpenAPI CDI extension's work into two steps: first, creating the OpenAPISupport object so its routing can be plugged in correctly, and second later on when the Application instances are available for use in building the in-memory OpenAPI model.

To do that involved refactoring the model-building logic from the Builder class to the OpenAPISupport class.

Allowing the MP CDI extension to prepare the model during start-up (rather than lazily upon first request for the OpenAPI document), but without exposing a prepareModel() method as public involved creating SE and MP variants of the OpenAPISupport class. The now-abstract superclass OpenAPISupport exposes a protected prepareModel() method which the MP variant overrides, also as protected, so the MP CDI extension in the same package can invoke it.

And doing that in a type-safe way requires using type parameters on the OpenAPISupport class and its builder, which brought along another set of changes.

As a result, there is a seemingly large number of changes required for a conceptually simple improvement in user experience, but doing it this way avoids expanding the public surface area more than we want.

Signed-off-by: tim.quinn@oracle.com tim.quinn@oracle.com

Signed-off-by: tim.quinn@oracle.com <tim.quinn@oracle.com>
@tjquinno tjquinno self-assigned this Mar 4, 2021
@tjquinno tjquinno changed the title Remove need to instantiate Application classes twice Remove need to instantiate Application classes twice for OpenAPI support Mar 4, 2021
@tjquinno tjquinno requested a review from tomas-langer March 4, 2021 00:40
Copy link
Copy Markdown
Member

@tomas-langer tomas-langer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few type parameter related issues.

* @param <B> concrete builder subclass which extends OpenAPISupport.Builder
*/
public class OpenAPISupport implements Service {
public abstract class OpenAPISupport<T extends OpenAPISupport<T, B>, B extends OpenAPISupport.Builder<T, B>> implements Service {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see the type B used anywhere. (it also should not be).

Copy link
Copy Markdown
Member Author

@tjquinno tjquinno Mar 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type B is used as the return type of Builder#me() and all the public builder methods defined on the abstract class. Otherwise there is no way for the builder methods that are implemented on the abstract class to return this correctly typed to the builder subclass without using unchecked casts. Or at least I have not seen one. If there is I would love to do away with me().

Returning the abstract Builder type instead of the specific subclass type B would lead to problems chaining calls of methods that are defined only on the subclass.

private final OpenApiStaticFile openApiStaticFile;
private final Supplier<List<? extends IndexView>> indexViewsSupplier;

protected OpenAPISupport(Builder builder) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be Builder<?, ?> so we do not have a Raw use of class with type parameters

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this should not use the raw type. For the moment I've changed it to Builder<T, B> to strongly type the parameter.

Comment thread openapi/src/main/java/io/helidon/openapi/OpenAPISupport.java Outdated
Comment thread openapi/src/main/java/io/helidon/openapi/OpenAPISupport.java Outdated
@tjquinno tjquinno requested a review from tomas-langer March 11, 2021 12:45
Copy link
Copy Markdown
Member

@tomas-langer tomas-langer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@tjquinno tjquinno merged commit 3fa4242 into helidon-io:master Mar 22, 2021
@tjquinno tjquinno deleted the openapi-single branch March 22, 2021 12:47
paulparkinson pushed a commit that referenced this pull request Mar 29, 2021
…ort (#2829)

* Remove need to instantiate Application classes twice

Signed-off-by: tim.quinn@oracle.com <tim.quinn@oracle.com>
aseovic pushed a commit to aseovic/helidon that referenced this pull request Apr 26, 2021
…ort (helidon-io#2829)

* Remove need to instantiate Application classes twice

Signed-off-by: tim.quinn@oracle.com <tim.quinn@oracle.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@PostConstruct in RestApplication invoked twice when using openapi

2 participants