bug: No qualifying bean of type 'com.fasterxml.jackson.databind.ObjectMapper' available
ps-tunnelsoft opened this issue · 6 comments
Expected behavior
When
DGS Version: 8.5.5
spring-graphql: 1.2.6
graphql-dgs-spring-graphql-starter: 8.5.5
graphql-dgs-subscriptions-websockets-autoconfigure: 8.5.5
Autoconfiguration for graphql-dgs-spring-graphql-starter includes bean for ObjectMapper with qualifier "dgsObjectMapper".
Actual behavior
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webSocketHandler' defined in class path resource [com/netflix/graphql/dgs/subscriptions/websockets/DgsWebSocketAutoConfig.class]: Unsatisfied dependency expressed through method 'webSocketHandler' parameter 2: No qualifying bean of type 'com.fasterxml.jackson.databind.ObjectMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier("dgsObjectMapper")}
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:795)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:542)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1335)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1165)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1443)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:782)
... 101 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.fasterxml.jackson.databind.ObjectMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier("dgsObjectMapper")}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1880)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1406)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:782)
... 115 common frames omitted
If I provide my own bean this problem does not exists.
@Bean(name = "dgsObjectMapper")
@ConditionalOnMissingBean(name = "dgsObjectMapper")
public ObjectMapper dgsObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
return objectMapper;
}
Steps to reproduce
run dgs-examples-java with graphql-dgs-spring-graphql-starter instead of graphql-dgs-spring-boot-starter.
Thanks for reporting. We'll look into it. The examples do need some updates so will fix those and post an update.
I think, I have a follow up issue. But it may just be connected to my workaround providing my own ObjectMapper to dgs.
If I want to test a simple subscription, I get this errror
ExceptionWebSocketHandlerDecorator - Closing session due to exception for StandardWebSocketSession[id=0abecb5b-e29e-64be-ba95-40e723869dfa, uri=ws://localhost:8080/subscriptions?token=TOKEN] - tryCloseWithError
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.netflix.graphql.types.subscription.websockets.Message$SubscribeMessage` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"id":"ebc576a5-3d3d-48fc-8c59-271bbf62904f","type":"subscribe","payload":{"variables":{},"extensions":{},"operationName":"test","query":"subscription test {\n test\n}"}}"; line: 1, column: 53]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1915)
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:414)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1360)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1434)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:352)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:220)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:187)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:170)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:136)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:263)
at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:74)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4825)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3772)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3740)
at com.netflix.graphql.dgs.subscriptions.websockets.WebsocketGraphQLTransportWSProtocolHandler.handleTextMessage(WebsocketGraphQLTransportWSProtocolHandler.kt:109)
at com.netflix.graphql.dgs.subscriptions.websockets.DgsWebSocketHandler.handleTextMessage(DgsWebSocketHandler.kt:109)
at org.springframework.web.socket.handler.AbstractWebSocketHandler.handleMessage(AbstractWebSocketHandler.java:43)
at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75)
at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56)
at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:58)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:113)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:84)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:81)
at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:390)
at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:130)
at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:484)
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:284)
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:130)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:85)
at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:184)
at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:164)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:152)
at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:60)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:57)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
at java.base/java.lang.Thread.run(Thread.java:840)
So actually one thing that needs to be called out here is the usage of implementation("com.netflix.graphql.dgs:graphql-dgs-subscriptions-websockets-autoconfigure")
that needs to be replaced by implementation("org.springframework.boot:spring-boot-starter-websocket")
With the spring-graphql integration, we rely on spring-graphql's implementation of websocket based subscriptions and therefore the DGS module won't work here. This will eliminate the need to provide a dgsObjectMapper as well.
Similarly, for multipart file uploads we need to explicitly add implementation("name.nkonev.multipart-spring-graphql:multipart-spring-graphql:1.1.4
instead of relying on the framework's implementation.
I will update our docs to reflect the websocket part.
ok thanks. I will wait for the documentation update.
Replacing implementation("com.netflix.graphql.dgs:graphql-dgs-subscriptions-websockets-autoconfigure")
with implementation("org.springframework.boot:spring-boot-starter-websocket")
does not work for me. Maybe I am just missing some configuration now
WARN o.s.web.servlet.PageNotFound - No mapping for GET /subscriptions - noHandlerFound
WARN o.s.web.servlet.PageNotFound - No endpoint GET /subscriptions. - handleNoHandlerFoundException
Yes it does as documented here: https://github.com/spring-projects/spring-graphql/blob/1.0.x/samples/webflux-websocket/src/main/resources/application.properties#L1
You need to add spring.graphql.websocket.path: /graphql
I will add this to the docs.
Thanks for helping me out. I wasn't familiar with the spring graphql setup process.
The GraphQL WebSocket endpoint is off by default. To enable it:
- For a Servlet application, add the WebSocket starter spring-boot-starter-websocket
- For a WebFlux application, no additional dependency is required
- For both, the spring.graphql.websocket.path application property must be set