Reputation: 18811
I'm trying to use two libraries together, GraphQL and Jmix.
I created the Jmix project using Intellij's new project wizard (with the Jmix plugin installed) then I added GraphQL to Gradle using the standard graphql-spring-boot-starter. Then I wrote a schema and the resolver beans.
But during startup, an exception is thrown because the WebSocket endpoint /subscriptions is being registered twice on Tomcat. (I tried changing the endpoint by using the application property graphql.servlet.subscriptions.websocket.path, but that is not the issue.)
After some digging, I found that the classes GraphQLWebsocketAutoConfiguration from graphql-spring-boot-autoconfigure and VaadinAutoConfiguration from jmix-ui-starter are both registering a ServerEndpointExporter bean, which is not supposed to happen.
Here is graphql's code:
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(ServerContainer.class)
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
and here Jmix's:
@Bean
public ServerEndpointExporter websocketEndpointDeployer() {
return new VaadinWebsocketEndpointExporter();
}
GraphQL's is marked as ConditionalOnMissingBean, but is registered before the other, so the conditional is not triggered.
How can I disable one of these two beans, or set their priority?
I managed to work around the issue by disabling GraphQL's websocket service entirely:
graphql.servlet.websocket.enabled = false
But I would like to know how to solve this kind of problem in general.
Upvotes: 0
Views: 744
Reputation: 42461
Unfortunately, It looks like the configurations are buggy, so there is no something you can do "idiomatically" in a Spring-ish way.
All the solutions that I can think of are rather workarounds, and probably the best way to solve this is opening a defect on the corresponding teams so that they could introduce a property which will allow to disable the Configuration in both ways. Well, maybe they've already done that, who knows.
As for the possible solutions:
Use the property: graphql.servlet.websocket.enabled=false
It will disable the graphql configuration. And you'll be able to redefine the beans you need by yourself. For example, define only: ServerEndpointRegistration
and ServerEndpointExporter
bean if you need them. Caveat - I haven't checked the source files of graphql library, maybe this property is used elsewhere.
Try to redefine the BeanDefinition of the bean you want/don't want to load. This can be done with BeanFactoryPostProcessor:
@Component
public class SampleBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// find the bean definition of the bean you need and try to:
// - set "primary" on it
String bdName = ... here put the name of the bean you want to fine-tune
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(bdName);
beanDefinition.setPrimary(true);
// ... or ...
// - remove it altogether from the application context (bean factory)
((BeanDefinitionRegistry)beanFactory).removeBeanDefinition(bdName);
Just to clarify - bean factory post processors are called by spring during the application startup when the bean definitions are already resolved but before the actual injection takes place.
Upvotes: 1