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

ConditionalOnBean not working well with JMS Connection Factory #39602

Closed
djechelon opened this issue Feb 16, 2024 · 1 comment
Closed

ConditionalOnBean not working well with JMS Connection Factory #39602

djechelon opened this issue Feb 16, 2024 · 1 comment
Labels
for: stackoverflow A question that's better suited to stackoverflow.com status: invalid An issue that we don't feel is valid

Comments

@djechelon
Copy link

djechelon commented Feb 16, 2024

Please see POC repository on Github

I am having a hard time defining a @ConditionalOnBean(jakarta.jms.ConnectionFactory.class) bean for some Spring Integration Flows that depend on the availability of a JMS connection. I have read the documentation (linked below) that @ConditionalOnBean must/should be used only on AutoConfiguration classes, but I insist on using it on regular beans like linked sources do.

I only need to define an IntegrationFlow as soon as the ConnectionFactory exists. If the application is started without a JMS broker (e.g. is not configured) then it should reduce its functionality and not initialize the relative IntegrationFlows. I have it over-simplified in my POC, as the real application contains around 120 integration flows and interacts with a handful of JMS channels.

Expectation: the provided JUnit test should invoke the flow using JMS and get the reply back. Commenting out the @ConditionalOnBean annotation fixes the test

I am opening an issue here instead of an SO question because ConditionalOnBean annotation is working perfectly in any other case, and I can't tell Spring to load my configuration class after JMS


In the POC repository, we define an example IntegrationFlow that:

  • Reads from entry JMS channel
  • Makes the word uppercase
  • Outputs the result to exit JMS channel

The IntegrationFlow depends on the jakarta.jms.ConnectionFactory to have been defined as a bean. In my business case, the Spring Boot application may start without JMS at all, so the IntegrationFlow must not be defined.
For that purpose, we use the @ConditionalOnBean annotation.

Per Spring documentation, that annotation should be used only on @AutoConfiguration classes, which makes things a little more complicated.
Half of the world uses the same annotation for a variety of purpose, and we are actively using it in our real application to instantiate a bean only if a collaborator is defined as a bean. None of these require that one uses @ConditionalOnBean on @AutoConfigurations only

To provide an example, we use a number of _RestService_s that depend on RestTemplate (bearing the root url and authentication info), as well as _SoapService_s depending on WebServiceTemplate, and they depend as such:

  • WebServiceTemplate bean depends on business property com.example.ws.{url|username|password}
  • SoapService component is annotated @ConditionalOnBean(value = WebServiceTemplate.class, name = "mySpecificWebServiceTemplateBecauseWeHavePlentiesOf")

All beautiful, except that it doesn't work with JMS ConnectionFactory. In this case, the connection factory exists only if spring.artemis.embedded=true (or another MQ broker is defined according to profile configuration).

In this POC, I can see that Spring refuses to instantiate the IntegrationFlow despite the ConnectionFactory exists.

Other considerations:

  • In the POC, I have simplified declaring the bean in the application class, but in my application we have dedicated @Configuration classes
  • Declaring that Configuration as an @AutoConfiguration(after = JmsAutoConfiguration.class) did not work
  • @DependsOn("jmsConnectionFactory") fails when there is no JMS ConnectionFactory
  • I can clearly see in the logs that MyJmsConfiguration does not match the Condition
  • Debugging into ArtemisAutoConfiguration, the ConnectionFactory eventually is created
  • Debugging the test with the BeanFactory, the ConnectionFactory bean is eventually available
  • @SpringIntegrationTest is currently not present in the POC but makes no difference (the real application shows it)
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 16, 2024
@bclozel
Copy link
Member

bclozel commented Feb 17, 2024

Thanks for getting in touch, but this issue tracker is dedicated to bugs an enhancement requests.

As you already know, ConditionalOnMissingBean and similar conditions should only be used on auto-configurations. While it might work in some cases, ordering is key: the condition is processed with your application and at that point the auto-configured bean might not be detected. You'll find many similar reports in our issue tracker over the years.

Half of the world uses the same annotation for a variety of purpose, and we are actively using it in our real application to instantiate a bean only if a collaborator is defined as a bean. None of these require that one uses @ConditionalOnBean on @AutoConfigurations only

All of those use cases are invalid. I've reported an issue to one of them, feel free to do so for other sources.

Declaring that Configuration as an @autoConfiguration(after = JmsAutoConfiguration.class) did not work

As for this case, it is probably because you've left your configuration to be scanned in your main application package. There is a note about this on our reference documentation:

Auto-configurations must be loaded only by being named in the imports file. Make sure that they are defined in a specific package space and that they are never the target of component scanning. Furthermore, auto-configuration classes should not enable component scanning to find additional components. Specific @import annotations should be used instead.

Moving your configuration class to a different package:

package org.zighinetto.autoconfigure;

import jakarta.jms.ConnectionFactory;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.jms.dsl.Jms;

@AutoConfiguration(after = JmsAutoConfiguration.class)
public class IntegrationFlowAutoConfiguration {

    @ConditionalOnBean(ConnectionFactory.class)
    @Bean
    public IntegrationFlow exampleIntegrationFlow(ConnectionFactory connectionFactory) {
        return IntegrationFlow.from(Jms.inboundAdapter(connectionFactory)
                        .destination("entry")
                )
                .transform(String.class, String::toUpperCase)
                .handle(Jms.outboundAdapter(connectionFactory)
                        .destination("exit")
                )
                .get();
    }
}

Declaring it in src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports with:

org.zighinetto.autoconfigure.IntegrationFlowAutoConfiguration

Yields the following in the auto-configuration report (when starting your application with the DEBUG property):

   IntegrationFlowAutoConfiguration#exampleIntegrationFlow matched:
      - @ConditionalOnBean (types: jakarta.jms.ConnectionFactory; SearchStrategy: all) found bean 'jmsConnectionFactory' (OnBeanCondition)

@bclozel bclozel closed this as not planned Won't fix, can't repro, duplicate, stale Feb 17, 2024
@bclozel bclozel added status: invalid An issue that we don't feel is valid for: stackoverflow A question that's better suited to stackoverflow.com and removed status: waiting-for-triage An issue we've not yet triaged labels Feb 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for: stackoverflow A question that's better suited to stackoverflow.com status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

3 participants