GoogleCloudPlatform / spring-cloud-gcp

New home for Spring Cloud GCP development starting with version 2.0.
Apache License 2.0
426 stars 317 forks source link

spring-cloud-gcp-starter-data-datastore latest (5.8.0) not compatible with Spring Boot 3.4.0 following removal of deprecated TransactionManagerCustomizers function #3390

Open csbiggar opened 1 day ago

csbiggar commented 1 day ago

Version compatibility request

This is a compatibility request rather than a bug. I understand the release cycle is not linked to Spring Boot, and that the current spring boot dependency version is 3.3.4, but users of spring-cloud-gcp-starter-data-datastore will be unable to upgrade Spring Boot from 3.3.x > 3.4.0 due to this incompatibility

Setup

My project is gradle/kotlin (which I don't think is relevant) .

Partial build.gradle.kts file:

plugins {
    id("org.springframework.boot") version "3.4.0"   // Just upgraded from 3.3.6, which works fine
    id("io.spring.dependency-management") version "1.1.6"
    kotlin("jvm") version "2.0.21"
    kotlin("plugin.spring") version "2.0.21"
}

dependencies {

    annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    implementation("org.springframework.boot:spring-boot-starter-aop")
    implementation("org.springframework.boot:spring-boot-starter-validation")

    implementation(platform("com.google.cloud:spring-cloud-gcp-dependencies:5.8.0"))
    implementation("com.google.cloud:spring-cloud-gcp-starter-data-datastore")

    .... plus other non-spring dependencies

}

Problem

org.springframework.boot version 3.3.6 runs fine with spring-cloud-gcp-dependencies:5.8.0 :+1:

On upgrading org.springframework.boot version to 3.4.0 I get the following on startup :disappointed:

***************************
APPLICATION FAILED TO START
***************************

Description:

An attempt was made to call a method that does not exist. The attempt was made from the following location:

    com.google.cloud.spring.autoconfigure.datastore.DatastoreTransactionManagerAutoConfiguration$DatastoreTransactionManagerConfiguration.datastoreTransactionManager(DatastoreTransactionManagerAutoConfiguration.java:66)

The following method did not exist:

    'void org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers.customize(org.springframework.transaction.PlatformTransactionManager)'

The calling method's class, com.google.cloud.spring.autoconfigure.datastore.DatastoreTransactionManagerAutoConfiguration$DatastoreTransactionManagerConfiguration, was loaded from the following location:

    jar:file:/some/location/.gradle/caches/modules-2/files-2.1/com.google.cloud/spring-cloud-gcp-autoconfigure/5.8.0/c1ed6ecc7fa5f569147ce33a3e326f5dbede7d8a/spring-cloud-gcp-autoconfigure-5.8.0.jar!/com/google/cloud/spring/autoconfigure/datastore/DatastoreTransactionManagerAutoConfiguration$DatastoreTransactionManagerConfiguration.class

The called method's class, org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers, is available from the following locations:

    jar:file:/somelocation/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-autoconfigure/3.4.0/2cf78b01be25952c96cb8ee60b4b0562b3a136f2/spring-boot-autoconfigure-3.4.0.jar!/org/springframework/boot/autoconfigure/transaction/TransactionManagerCustomizers.class

The called method's class hierarchy was loaded from the following locations:

    org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers: file:/somelocation/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-autoconfigure/3.4.0/2cf78b01be25952c96cb8ee60b4b0562b3a136f2/spring-boot-autoconfigure-3.4.0.jar

Action:

Correct the classpath of your application so that it contains compatible versions of the classes com.google.cloud.spring.autoconfigure.datastore.DatastoreTransactionManagerAutoConfiguration$DatastoreTransactionManagerConfiguration and org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers

spring-cloud-gcp-starter-data-datastore class DatastoreTransactionManagerAutoConfiguration calls org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers function customize, here

@Bean
    @ConditionalOnMissingBean
    public DatastoreTransactionManager datastoreTransactionManager() {
      DatastoreTransactionManager transactionManager =
          new DatastoreTransactionManager(this.datastore);
      if (this.transactionManagerCustomizers != null) {
        this.transactionManagerCustomizers.customize(transactionManager);
      }
      return transactionManager;
    }

DatastoreTransactionManager extends org.springframework.transaction.support.AbstractPlatformTransactionManager which implements PlatformTransactionManager ( here)

The overload of customize in TransactionManagerCustomizers which takes PlatformTransactionManager was deprecated in 3.2.0 and hence has been removed in 3.4.0 as per the release notes here , it can only take TransactionManager now:

    @Deprecated(
        since = "3.2.0",
        forRemoval = true
    )
    public void customize(PlatformTransactionManager platformTransactionManager) {  // This has been deleted in 3.4.0
        this.customize((TransactionManager)platformTransactionManager);
    }

    public void customize(TransactionManager transactionManager) {
        ((LambdaSafe.Callbacks)LambdaSafe.callbacks(TransactionManagerCustomizer.class, this.customizers, transactionManager, new Object[0]).withLogger(TransactionManagerCustomizers.class)).invoke((customizer) -> {
            customizer.customize(transactionManager);
        });
    }

Suggestion (not checked!)

All the removed function in TransactionManagerCustomizers did was cast PlatformTransactionManager to TransactionManager , and called the (remaining) overloaded function.

Maybe a quick fix to cast it in DatastoreTransactionManagerAutoConfiguration would work?? And should be backwards compatible?

this.transactionManagerCustomizers.customize( (TransactionManager) transactionManager);

Sample If possible, please provide a test case or sample application that reproduces the problem. This makes it much easier for us to diagnose the problem and to verify that we have fixed it.

sigand commented 5 hours ago

Our issue might be related, using implementation("com.google.cloud:spring-cloud-gcp-starter-secretmanager:5.8.0"), secret manager does not load secrets anymore. No error message, but secrets are resolved as their placeholder names.