Skip to content
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

fix(CDI) Auto-detect beans on Rest services Refs: #30187 #30417

Merged
merged 24 commits into from
Oct 29, 2024

Conversation

fabrizzio-dotCMS
Copy link
Contributor

@fabrizzio-dotCMS fabrizzio-dotCMS commented Oct 21, 2024

Proposed Changes

  • Jersey 2.28 works with CDI 2.0
  • beans.xml is set to use CDI 2.0
  • A Few "Producer" classes were removed as they were generating conflicts trying to decide what implementation had to be chosen
  • Updated the Resources where there were previous attempts to introduce injection
  • Some classes were located in different projects but had identical names like GenericBundleActivatorTest Weld didn't like that so I had to rename one of them.
  • CDI Utils has a new method that throws an exception as some people complained about the one returning an Optional
  • We have support for Integration tests through these two runners (JUnit4WeldRunner,DataProviderWeldRunner)

This PR fixes: #30187

@fabrizzio-dotCMS fabrizzio-dotCMS linked an issue Oct 23, 2024 that may be closed by this pull request
@fabrizzio-dotCMS fabrizzio-dotCMS marked this pull request as ready for review October 25, 2024 18:01
@spbolton
Copy link
Contributor

@fabrizzio-dotCMS Some extra info and context. The Producer methods are only useful to create a bean if there is not already another annotated bean class, having both will create the conflicts. The default behaviour for a bean is to use the local class name as the name of the bean. As found, if there is more than one class that is being initialised as a bean with the same local name it will cause conflicts. It is possible to overide the default bean name using the @nAmed annotation e.g. @nAmed("cart"). To match this up at the point of injection you would have to use the same annotation on the injection point. Changing the class name is a cleaner fix to prevent confusion if possible, and usually using a custom Qualifier annotation is better than using @nAmed as there is no chance of typos.

I am thinking we are getting conflicts here with classes not annotated with a bean defining annotation e.g. @ApplicationScoped. This may be due to a change in the default behavior of the bean discovery mode between the versions. With the downgrade we have changed to version="1.1" bean-discovery-mode="all" rather than bean-discovery-mode="annotated". It is better to use annotated as it will not load classes as beans that are not intended to be injected which can produce unintended conflicts and decrease performance due to having to scan all classes. We may be restricted here due to the behavior of version 1.1 of cdi we currently need but need to be aware of the difference and should be working towards using "annotated" always.

@fabrizzio-dotCMS
Copy link
Contributor Author

@fabrizzio-dotCMS Some extra info and context. The Producer methods are only useful to create a bean if there is not already another annotated bean class, having both will create the conflicts. The default behaviour for a bean is to use the local class name as the name of the bean. As found, if there is more than one class that is being initialised as a bean with the same local name it will cause conflicts. It is possible to overide the default bean name using the @nAmed annotation e.g. @nAmed("cart"). To match this up at the point of injection you would have to use the same annotation on the injection point. Changing the class name is a cleaner fix to prevent confusion if possible, and usually using a custom Qualifier annotation is better than using @nAmed as there is no chance of typos.

I am thinking we are getting conflicts here with classes not annotated with a bean defining annotation e.g. @ApplicationScoped. This may be due to a change in the default behavior of the bean discovery mode between the versions. With the downgrade we have changed to version="1.1" bean-discovery-mode="all" rather than bean-discovery-mode="annotated". It is better to use annotated as it will not load classes as beans that are not intended to be injected which can produce unintended conflicts and decrease performance due to having to scan all classes. We may be restricted here due to the behavior of version 1.1 of cdi we currently need but need to be aware of the difference and should be working towards using "annotated" always.

@fabrizzio-dotCMS Some extra info and context. The Producer methods are only useful to create a bean if there is not already another annotated bean class, having both will create the conflicts. The default behaviour for a bean is to use the local class name as the name of the bean. As found, if there is more than one class that is being initialised as a bean with the same local name it will cause conflicts. It is possible to overide the default bean name using the @nAmed annotation e.g. @nAmed("cart"). To match this up at the point of injection you would have to use the same annotation on the injection point. Changing the class name is a cleaner fix to prevent confusion if possible, and usually using a custom Qualifier annotation is better than using @nAmed as there is no chance of typos.

I am thinking we are getting conflicts here with classes not annotated with a bean defining annotation e.g. @ApplicationScoped. This may be due to a change in the default behavior of the bean discovery mode between the versions. With the downgrade we have changed to version="1.1" bean-discovery-mode="all" rather than bean-discovery-mode="annotated". It is better to use annotated as it will not load classes as beans that are not intended to be injected which can produce unintended conflicts and decrease performance due to having to scan all classes. We may be restricted here due to the behavior of version 1.1 of cdi we currently need but need to be aware of the difference and should be working towards using "annotated" always.

Indeed, @spbolton , using the @nAmed annotation is a much cleaner approach. However, @jgambarios explained that the requirement involves making the producer dynamic. Based on a parameter read from the config class, it should decide whether the queue implementation is Postgres, Redis, or another option. In this case, I had to veto the Bean to exclude it accordingly. Additionally, I couldn’t get the discovery strategy for annotated Beans to work; apparently, CDI-1 only allows for full discovery of all Beans. So, that’s essentially the status of this pull request.

@spbolton
Copy link
Contributor

@fabrizzio-dotCMS Yes it seems that annotation option is only for cdi 1.2 and above. I see the reason for the Producer and the need to exclude the base class in this case as it requires the dynamic behavior.

Still bugging me that we should be able to get CDI 2 working even with Tomcat 9, but we have spent too many cycles on this and can cover this better when we can upgrade tomcat. We will have to pay the possible perf penalty in the short term by not having the "annotated" option though.

Copy link
Contributor

@spbolton spbolton left a comment

Choose a reason for hiding this comment

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

Just a sec there may be an issue here with the way the beans are loaded

Looking at what happens when the beans.xml references cdi 2.0 and using annotation scanning I saw that it is trying to inject CubeJSClientFactory and failing because it is not annotated as a bean. In cdi 1.0 with "all". option it will automatically create this object as a bean. This may make things appear to be working, but in reality what is going to happen is that CDI is going to create an instance of the bean, and may in fact create a different instance of CubeJSClientFactoryImpl for each bean it is used. This will not be the same instance that is created and used from the FactoryLocator. The CubeJSClientFactory is injected in ContentAnalyticsFactoryImpl, and Application Scoped bean which is annotated.

This is the case where to properly handle this behavior both in the cdi context and non cdi context we either need to annotate the CubeJSClientFactoryImpl as @ApplicationScoped, and then modify the FactoryLocater to return the instance from the CDI context instead of creating its own, or we would annotate the getCubeJSClientFactory() method in FactoryLocator with @produces and @ApplicationScoped, to make CDI use the same instance as the Factory class. The first option would make an easier transition later.

In CDI 1.0 it is implicitly creating the bean which although seems to work on the surface may cause issues and confusion due to the multiple instances of the same class being used in the system.

It looks as if we have already done the former approach for the CONTENT_ANALYTICS_FACTORY and would just need to do the same for CUBEJS_CLIENT_FACTORY as well as add the @ApplicationScoped annotation to the implementation class.
case CONTENT_ANALYTICS_FACTORY: CDIUtils.getBean(ContentAnalyticsFactory.class).orElseThrow(() -> new DotRuntimeException("ContentAnalyticsFactory not found"));

Eventually analyticsHelper and analyticsAPI in CubeJSClientFactoryImpl would themselves be injected beans but in the short term this is not required.

@fabrizzio-dotCMS fabrizzio-dotCMS added this pull request to the merge queue Oct 29, 2024
Merged via the queue into main with commit fe2a36c Oct 29, 2024
35 checks passed
@fabrizzio-dotCMS fabrizzio-dotCMS deleted the issue-30187-fixing-cdi-auto-detect branch October 29, 2024 15:18
spbolton pushed a commit that referenced this pull request Nov 11, 2024
### Proposed Changes
* Jersey 2.28 works with CDI1.x 
* beans.xml were downgraded to use CDI 1.x
* A Few "Producer" classes were removed as they were generating
conflicts trying to decide what implementation had to be chosen
* Updated the Resources where there were previous attempts to introduce
injection
* Some classes were located in different projects but had identical
names like GenericBundleActivatorTest Weld didn't like that so I had to
rename one of them.
* CDI Utils has a new method that throws an exception as some people
complained about the one returning an Optional
* We have support for Integration tests through these two runners
(JUnit4WeldRunner,DataProviderWeldRunner)
* If we want to use CDI2.x we will have to wait until we move to tomcat
10+
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.

Weld Container should auto detect any annotated bean
4 participants