Skip to content

TransactionManager is not of required type #2067

@johanhaleby

Description

@johanhaleby

Hi!

Describe the bug

After upgrading to Spring Cloud 2021.0.0 (congratulations on the new release btw :)) in one of our projects (which uses sleuth 3.1.0 if I'm not mistaken), we run into an issue due to an incompatible TransactionManager:

org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'mongoTransactionManager' is expected to be of type 'org.springframework.data.mongodb.MongoTransactionManager' but was actually of type 'org.springframework.cloud.sleuth.instrument.tx.TracePlatformTransactionManager'

Here's the full stracktrace:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'occurrentEventStoreConfig' defined in class path resource [org/occurrent/springboot/mongo/blocking/OccurrentMongoAutoConfiguration.class]: Unsatisfied dependency expressed through method 'occurrentEventStoreConfig' parameter 0; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'mongoTransactionManager' is expected to be of type 'org.springframework.data.mongodb.MongoTransactionManager' but was actually of type 'org.springframework.cloud.sleuth.instrument.tx.TracePlatformTransactionManager'
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:541)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1380)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
        at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
        ... 115 more
    Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'mongoTransactionManager' is expected to be of type 'org.springframework.data.mongodb.MongoTransactionManager' but was actually of type 'org.springframework.cloud.sleuth.instrument.tx.TracePlatformTransactionManager'
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1390)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
        at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
        ... 129 more

Sample

I'm developing an open-source project called Occurrent and it has a spring starter module that is defined like this (here's the code on github):

@Configuration
@ConditionalOnClass({SpringMongoEventStore.class, SpringMongoSubscriptionModel.class})
@EnableConfigurationProperties(OccurrentProperties.class)
@AutoConfigureAfter(MongoAutoConfiguration.class)
@Import(MongoDataAutoConfiguration.class)
public class OccurrentMongoAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(MongoTransactionManager.class)
    public MongoTransactionManager mongoTransactionManager(MongoDatabaseFactory dbFactory) {
        return new MongoTransactionManager(dbFactory, TransactionOptions.builder().readConcern(ReadConcern.MAJORITY).writeConcern(WriteConcern.MAJORITY).build());
    }

    @Bean
    @ConditionalOnMissingBean(EventStoreConfig.class)
    public EventStoreConfig occurrentEventStoreConfig(MongoTransactionManager transactionManager, OccurrentProperties occurrentProperties) {
        EventStoreProperties eventStoreProperties = occurrentProperties.getEventStore();
        return new EventStoreConfig.Builder().eventStoreCollectionName(eventStoreProperties.getCollection()).transactionConfig(transactionManager).timeRepresentation(eventStoreProperties.getTimeRepresentation()).build();
    }

    @Bean
    @ConditionalOnMissingBean(SpringMongoEventStore.class)
    public SpringMongoEventStore occurrentSpringMongoEventStore(MongoTemplate template, EventStoreConfig eventStoreConfig) {
        return new SpringMongoEventStore(template, eventStoreConfig);
    }

    @Bean
    @ConditionalOnMissingBean(SubscriptionPositionStorage.class)
    public SubscriptionPositionStorage occurrentSubscriptionPositionStorage(MongoTemplate mongoTemplate, OccurrentProperties occurrentProperties) {
        return new SpringMongoSubscriptionPositionStorage(mongoTemplate, occurrentProperties.getSubscription().getCollection());
    }

    @Bean
    @ConditionalOnMissingBean(SpringMongoLeaseCompetingConsumerStrategy.class)
    public SpringMongoLeaseCompetingConsumerStrategy occurrentCompetingConsumerStrategy(MongoTemplate mongoTemplate, List<CompetingConsumerListener> competingConsumerListeners) {
        SpringMongoLeaseCompetingConsumerStrategy strategy = SpringMongoLeaseCompetingConsumerStrategy.withDefaults(mongoTemplate);
        competingConsumerListeners.forEach(strategy::addListener);
        return strategy;
    }

    @Bean
    @ConditionalOnMissingBean(SubscriptionModel.class)
    public SubscriptionModel occurrentCompetingDurableSubscriptionModel(MongoTemplate mongoTemplate, SpringMongoLeaseCompetingConsumerStrategy competingConsumerStrategy, SubscriptionPositionStorage storage, OccurrentProperties occurrentProperties) {
        EventStoreProperties eventStoreProperties = occurrentProperties.getEventStore();
        SpringMongoSubscriptionModel mongoSubscriptionModel = new SpringMongoSubscriptionModel(mongoTemplate, withConfig(eventStoreProperties.getCollection(), eventStoreProperties.getTimeRepresentation())
                .restartSubscriptionsOnChangeStreamHistoryLost(occurrentProperties.getSubscription().isRestartOnChangeStreamHistoryLost()));
        DurableSubscriptionModel durableSubscriptionModel = new DurableSubscriptionModel(mongoSubscriptionModel, storage);
        return new CompetingConsumerSubscriptionModel(durableSubscriptionModel, competingConsumerStrategy);
    }

    @Bean
    @ConditionalOnMissingBean(CloudEventConverter.class)
    public <T> CloudEventConverter<?> occurrentCloudEventConverter(Optional<ObjectMapper> objectMapper, OccurrentProperties occurrentProperties, CloudEventTypeMapper<T> cloudEventTypeMapper) {
        ObjectMapper mapper = objectMapper.orElseGet(ObjectMapper::new);
        return new JacksonCloudEventConverter.Builder<T>(mapper, occurrentProperties.getCloudEventConverter().getCloudEventSource())
                .typeMapper(cloudEventTypeMapper)
                .build();
    }

    @Bean
    @ConditionalOnMissingBean(CloudEventTypeMapper.class)
    public CloudEventTypeMapper<?> occurrentTypeMapper() {
        return ReflectionCloudEventTypeMapper.qualified();
    }

    @Bean
    @ConditionalOnMissingBean(Subscriptions.class)
    public <T> Subscriptions<?> occurrentSubscriptionDsl(Subscribable subscribable, CloudEventConverter<T> cloudEventConverter) {
        return new Subscriptions<>(subscribable, cloudEventConverter);
    }

    @Bean
    @ConditionalOnMissingBean(DomainEventQueries.class)
    public <T> DomainEventQueries<?> occurrentDomainEventQueries(EventStoreQueries eventStoreQueries, CloudEventConverter<T> cloudEventConverter) {
        return new DomainEventQueries<>(eventStoreQueries, cloudEventConverter);
    }

    @Bean
    @ConditionalOnMissingBean(ApplicationService.class)
    public ApplicationService<?> occurrentApplicationService(EventStore eventStore, CloudEventConverter<?> cloudEventConverter, OccurrentProperties occurrentProperties) {
        boolean enableDefaultRetryStrategy = occurrentProperties.getApplicationService().isEnableDefaultRetryStrategy();
        return enableDefaultRetryStrategy ? new GenericApplicationService<>(eventStore, cloudEventConverter) : new GenericApplicationService<>(eventStore, cloudEventConverter, RetryStrategy.none());
    }
}

In our application, we depend on this starter module from Maven. When booting this service we get the error presented above. We didn't get this when using spring-cloud-dependencies 2020.0.4.

Maybe it's a bad practice to depend on a specific implementation of the transaction manager like occurrentEventStoreConfig does? But I want to make sure that this bean receives a MongoTransactionManager and not the wrong type of PlatformTransactionManager (such as JPA). If I'm not doing things correctly, I would be happy to be corrected :)

Thanks for a great project!

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions