spring-data-couchbase: `MappingInstantiationException` for Abstract Classes using tasks ran on ForkJoinPool
Hi, our team has had a really tough problem we been dealing with for some time and been trying to get to root cause. We have an issue with a particular service where it begins failing with the following exception:
Exception: org.springframework.data.mapping.model.MappingInstantiationException
Message:
Failed to instantiate com.qvc.common.couchbase.model.cart.payment.AbstractPaymentMethod using constructor public com.qvc.common.couchbase.model.cart.payment.AbstractPaymentMethod() with arguments
Stacktrace:
org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator$MappingInstantiationExceptionEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:358)
org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:102)
org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter.read(MappingCouchbaseConverter.java:266)
org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter.read(MappingCouchbaseConverter.java:244)
org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter.readCollection(MappingCouchbaseConverter.java:773)
org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter.readValue(MappingCouchbaseConverter.java:856)
org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter.access$300(MappingCouchbaseConverter.java:86)
org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter$CouchbasePropertyValueProvider.getPropertyValue(MappingCouchbaseConverter.java:972)
org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter.getValueInternal(MappingCouchbaseConverter.java:309)
org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter$1.doWithPersistentProperty(MappingCouchbaseConverter.java:277)
org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter$1.doWithPersistentProperty(MappingCouchbaseConverter.java:269)
org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:368)
org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter.read(MappingCouchbaseConverter.java:269)
org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter.read(MappingCouchbaseConverter.java:244)
org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter.read(MappingCouchbaseConverter.java:201)
org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter.read(MappingCouchbaseConverter.java:86)
org.springframework.data.couchbase.core.CouchbaseTemplateSupport.decodeEntity(CouchbaseTemplateSupport.java:141)
org.springframework.data.couchbase.core.NonReactiveSupportWrapper.lambda$decodeEntity$1(NonReactiveSupportWrapper.java:45)
reactor.core.publisher.MonoSupplier.call(MonoSupplier.java:86)
reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:139)
reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onNext(FluxHide.java:137)
org.springframework.cloud.sleuth.instrument.reactor.ScopePassingSpanSubscriber.onNext(ScopePassingSpanSubscriber.java:89)
reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
reactor.core.publisher.FluxDoFinally$DoFinallySubscriber.onNext(FluxDoFinally.java:113)
org.springframework.cloud.sleuth.instrument.reactor.ScopePassingSpanSubscriber.onNext(ScopePassingSpanSubscriber.java:89)
reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1839)
com.couchbase.client.core.Reactor$SilentMonoCompletionStage.lambda$subscribe$0(Reactor.java:183)
java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:859)
java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:837)
java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2073)
com.couchbase.client.core.msg.BaseRequest.succeed(BaseRequest.java:161)
com.couchbase.client.core.io.netty.kv.KeyValueMessageHandler.decodeAndComplete(KeyValueMessageHandler.java:412)
com.couchbase.client.core.io.netty.kv.KeyValueMessageHandler.retryOrComplete(KeyValueMessageHandler.java:368)
com.couchbase.client.core.io.netty.kv.KeyValueMessageHandler.decode(KeyValueMessageHandler.java:351)
com.couchbase.client.core.io.netty.kv.KeyValueMessageHandler.channelRead(KeyValueMessageHandler.java:282)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
com.couchbase.client.core.io.netty.kv.MemcacheProtocolVerificationHandler.channelRead(MemcacheProtocolVerificationHandler.java:85)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
com.couchbase.client.core.deps.io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:336)
com.couchbase.client.core.deps.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:308)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
com.couchbase.client.core.deps.io.netty.handler.flush.FlushConsolidationHandler.channelRead(FlushConsolidationHandler.java:152)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
com.couchbase.client.core.deps.io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1373)
com.couchbase.client.core.deps.io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1236)
com.couchbase.client.core.deps.io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1285)
com.couchbase.client.core.deps.io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:519)
com.couchbase.client.core.deps.io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:458)
com.couchbase.client.core.deps.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:280)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
com.couchbase.client.core.deps.io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
com.couchbase.client.core.deps.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
com.couchbase.client.core.deps.io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
com.couchbase.client.core.deps.io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:800)
com.couchbase.client.core.deps.io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:499)
com.couchbase.client.core.deps.io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:397)
com.couchbase.client.core.deps.io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
com.couchbase.client.core.deps.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
com.couchbase.client.core.deps.io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
java.lang.Thread.run(Thread.java:829)
We have a standard @Document with a list of an Abstract object like so:
private List<AbstractPaymentMethod<?>> paymentMethods;
The definition of the abstract class is:
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public abstract class AbstractPaymentMethod<T> {
private String id;
private T data;
}
An example of a class implementing the abstract class is:
public class CreditPaymentMethod extends AbstractPaymentMethod<Credit> {
}
@Getter
@Setter
public class Credit {
private String id;
private String type;
private String description;
private BigDecimal amount;
}
We can’t quite determine when exactly it happen but some of the versions that we’re on are:
- Spring Boot 2.7.6
- spring data commons 2.7.6
- spring data couchbase 4.4.6
When app is initially deployed it runs fine for a while and then suddenly it begins throwing this error complaining specifically about our abstract class. Previous to these versions, we have been running fine up to 5 years so we’re trying to determine if there was any change of behavior in the spring data commons area where we have to configure abstract classes differently. Any help would be greatly appreciated.
We are not using converters (@ReadingConverter / @WritingConverter) for this but simply just have an overall @Document spring data couchbase entity class and use the standard PagingAndSortingRepository.
I imagine more data will be needed to help here but wanted to get convo started.
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 26 (2 by maintainers)
Commits related to this issue
- Use the ApplicationContext class loader for MappingCouchbaseConverter. Closes #1691. — committed to spring-projects/spring-data-couchbase by mikereiche a year ago
- Use ApplicationContext classloader for MappingCouchbaseConverter. Closes #1691. — committed to spring-projects/spring-data-couchbase by mikereiche a year ago
- Use ApplicationContext classloader for MappingCouchbaseConverter. (#1704) Closes #1691. — committed to spring-projects/spring-data-couchbase by mikereiche a year ago
- Use the ApplicationContext class loader for MappingCouchbaseConverter. (#1705) Closes #1691. — committed to spring-projects/spring-data-couchbase by mikereiche a year ago
- Use the ApplicationContext class loader for MappingCouchbaseConverter. (#1705) Closes #1691. — committed to spring-projects/spring-data-couchbase by mikereiche a year ago
The fix is going to be 4.4.11 and 5.0.5
Release date for those versions is April 14
The error message “Failed to instantiate com.qvc.common.couchbase.model.cart.payment.AbstractPaymentMethod” indicates that DefaultTypeMapper.readType() could not determine the concrete type (documentsTargetType) based on the ‘source’ and was therefore trying to use the abstract type (basicType) which was used to define the repository. The issue looks more like an issue determining what the documentsTargetType is (or determining that it isMoreConcreteCustomType than the abstract type). Which points to the (sub)document not having a _class property, or the code not finding it because it is using a different @TypeAlias than _class, or a custom TypeMapper (instead of the DefaultTypeMapper). (Or the aforementioned bug of not projecting _class, but that should be fixed in 4.4.8). The error message indicates it is trying to instantiate the wrong class - not any issue with instantiation of the correct class.
And thanks for helping and getting a fix in!
Thanks for mentioning
CompletableFuture.runAsync(…). This method runs typically on the ForkJoin pool that isn’t associated with a contextual class loader but rather uses the initial application class loader. Depending on your setup, the app class loader sees only the outer Spring Boot JAR and not the packaged inner jars.The issue can be fixed by providing the bean class loader to
CouchbaseTypeMapper.DefaultTypeMapperthat serves as base class for Couchbase’sTypeMapperisBeanClassLoaderAwareacceptingClassLoader. The easiest fix would be setting the class loader viaMappingCouchbaseConverter.setApplicationContext(…), see MongoDB’s usage for reference.If you set a break-point at line 231 in MappingCouchbaseConverter - you should see that
type=com.example.demo.AbstractPaymentMethod<Object>and the content of source contains _classCouchbaseDocument{id=null, exp=0, content={_class=com.example.demo.CreditPaymentMethod, data=CouchbaseDocument{id=null, exp=0, content={amount=10, description=Walmart_Purchase, id=visa_id, type=visa_type}}, id=123}}resulting in
typeToUse=com.example.demo.CreditPaymentMethod@cabbonizio - I’ll investigate your issue. There was an issue around the _class not being leveraged that was causing an issue for abstract entities - #1315 (and its duplicate #1364). But these were fixed before 4.4.6. (Meanwhile, can you please use the latest 4.x which is 4.4.9 so we are both looking using the same codebase? Thanks. Edit: 4.4.8 from spring-boot 2.7.9 is fine).