Skip to content

Add Jakarta migration to spring-boot-upgrade project #724

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;

@Disabled("Falky at CI hence disabling it")
public class BootUpgrade_27_30_IntegrationTest extends IntegrationTestBaseClass {

@Override
Expand All @@ -46,6 +45,7 @@ void migrateSimpleApplication() {
scanProject();

applyRecipe("boot-2.7-3.0-dependency-version-update");
applyRecipe("sbu30-migrate-to-jakarta-packages");

buildProject();
verifyParentPomVersion();
Expand All @@ -56,9 +56,32 @@ void migrateSimpleApplication() {
verifyAutoConfigurationIsRefactored();
verifyEhCacheVersionIsUpgraded();
verifyJohnzonCoreDependencyIsUpgraded();
verifyWireMockDependency();
verifyJavaxMigrationToJakarta();
// TODO: Verify if wiremock recipe is required for 3.0.0 migration
// verifyWireMockDependency();
verifySpringCloudDependency();
}
}

private void verifyJavaxMigrationToJakarta() {

String studentClass = loadJavaFile(
"org.springboot.example.upgrade",
"Student");

assertThat(studentClass).isEqualTo("""
package org.springboot.example.upgrade;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;

@Entity
public class Student {
@Id
private long id;
private String name;
}
""");
}

private void verifyWireMockDependency() {
Optional<Dependency> wireMock =
Expand Down Expand Up @@ -168,7 +191,7 @@ public interface StudentRepoReactiveSorting extends ReactiveSortingRepository<St
}
""");

String studentRepoRx= loadJavaFile("org.springboot.example.upgrade", "StudentRepoRxJava3Sorting");
String studentRepoRx = loadJavaFile("org.springboot.example.upgrade", "StudentRepoRxJava3Sorting");

assertThat(studentRepoRx).isEqualTo("""
package org.springboot.example.upgrade;
Expand Down Expand Up @@ -201,114 +224,116 @@ private void verifyConstructorBindingRemoval() {
private void verifyYamlConfigurationUpdate() {
String micrometerClass = loadFile(Path.of("src/main/resources/application.yaml"));
assertThat(micrometerClass).isEqualTo(
"spring:\n" +
" datasource:\n" +
" url: jdbc:h2:mem:testdb\n" +
" driverClassName: org.h2.Driver\n" +
" jpa:\n" +
" database-platform: org.hibernate.dialect.H2Dialect\n" +
" elasticsearch:\n" +
" connection-timeout: '1'\n" +
" password: testpassword\n" +
" socket-timeout: '2'\n" +
" restclient.sniffer.delay-after-failure: '3'\n" +
" restclient.sniffer.interval: '4'\n" +
" username: username\n" +
" security:\n" +
" saml2:\n" +
" relyingparty:\n" +
" registration:\n" +
" idpone:\n" +
" assertingparty:\n" +
" verification:\n" +
" credentials:\n" +
" certificate-location: classpath:saml/idpone.crt\n" +
" entity-id: https://idpone.com\n" +
" sso-url: https://idpone.com\n" +
" cassandra:\n" +
" keyspaceName: testKeySpace\n" +
" contactPoints: localhost\n" +
" port: 9042\n" +
" username: testusername\n" +
" schemaAction: NONE\n" +
" request:\n" +
" timeout: 10s\n" +
" connection:\n" +
" connectTimeout: 10s\n" +
" initQueryTimeout: 10s\n" +
" elasticsearch.connection-timeout: '1000'\n" +
" elasticsearch.webclient.max-in-memory-size: '122'\n" +
" elasticsearch.password: abc\n" +
" elasticsearch.socket-timeout: '100'\n" +
" elasticsearch.username: testUser\n" +
" sql.init.data-locations: testdata\n" +
" sql.init.password: password\n" +
" sql.init.username: sa\n" +
" sql.init.mode: mode1\n" +
" sql.init.platform: pls\n" +
" sql.init.schema-locations: table1\n" +
" sql.init.password: password\n" +
" sql.init.username: sa\n" +
" sql.init.separator: k\n" +
" sql.init.encoding: UTF-8\n" +
"server.reactive.session.cookie.same-site: 'true'\n");
"spring:\n" +
" datasource:\n" +
" url: jdbc:h2:mem:testdb\n" +
" driverClassName: org.h2.Driver\n" +
" jpa:\n" +
" database-platform: org.hibernate.dialect.H2Dialect\n" +
" elasticsearch:\n" +
" connection-timeout: '1'\n" +
" password: testpassword\n" +
" socket-timeout: '2'\n" +
" restclient.sniffer.delay-after-failure: '3'\n" +
" restclient.sniffer.interval: '4'\n" +
" username: username\n" +
" security:\n" +
" saml2:\n" +
" relyingparty:\n" +
" registration:\n" +
" idpone:\n" +
" assertingparty:\n" +
" verification:\n" +
" credentials:\n" +
" certificate-location: classpath:saml/idpone.crt\n" +
" entity-id: https://idpone.com\n" +
" sso-url: https://idpone.com\n" +
" cassandra:\n" +
" keyspaceName: testKeySpace\n" +
" contactPoints: localhost\n" +
" port: 9042\n" +
" username: testusername\n" +
" schemaAction: NONE\n" +
" request:\n" +
" timeout: 10s\n" +
" connection:\n" +
" connectTimeout: 10s\n" +
" initQueryTimeout: 10s\n" +
" elasticsearch.connection-timeout: '1000'\n" +
" elasticsearch.webclient.max-in-memory-size: '122'\n" +
" elasticsearch.password: abc\n" +
" elasticsearch.socket-timeout: '100'\n" +
" elasticsearch.username: testUser\n" +
" sql.init.data-locations: testdata\n" +
" sql.init.password: password\n" +
" sql.init.username: sa\n" +
" sql.init.mode: mode1\n" +
" sql.init.platform: pls\n" +
" sql.init.schema-locations: table1\n" +
" sql.init.password: password\n" +
" sql.init.username: sa\n" +
" sql.init.separator: k\n" +
" sql.init.encoding: UTF-8\n" +
"server.reactive.session.cookie.same-site: 'true'\n");
}


private void verifyPropertyConfigurationUpdate() {

String applicationProperties = loadFile(Path.of("src/main/resources/application.properties"));
assertThat(applicationProperties).isEqualTo(
"spring.elasticsearch.connection-timeout=1000\n" +
"spring.elasticsearch.webclient.max-in-memory-size=122\n" +
"spring.elasticsearch.password=abc\n" +
"spring.elasticsearch.socket-timeout=100\n" +
"spring.elasticsearch.username=testUser\n" +
"\n" +
"spring.sql.init.data-locations=testdata\n" +
"spring.sql.init.password=password\n" +
"spring.sql.init.username=username\n" +
"spring.sql.init.mode=mode1\n" +
"spring.sql.init.platform=pls\n" +
"spring.sql.init.schema-locations=table1\n" +
"spring.sql.init.password=password2\n" +
"spring.sql.init.username=username2\n" +
"spring.sql.init.separator=k\n" +
"spring.sql.init.encoding=UTF-8\n" +
"\n" +
"spring.elasticsearch.connection-timeout=1\n" +
"spring.elasticsearch.password=testpassword\n" +
"spring.elasticsearch.socket-timeout=2\n" +
"spring.elasticsearch.restclient.sniffer.delay-after-failure=3\n" +
"spring.elasticsearch.restclient.sniffer.interval=4\n" +
"spring.elasticsearch.username=username\n" +
"\n" +
"spring.security.saml2.relyingparty.registration.idpone.assertingparty.entity-id=https://idpone.com\n" +
"spring.security.saml2.relyingparty.registration.idpone.assertingparty.sso-url=https://idpone.com\n" +
"spring.security.saml2.relyingparty.registration.idpone.assertingparty.verification.credentials.certificate-location=classpath:saml/idpone.crt\n" +
"\n" +
"server.reactive.session.cookie.same-site=true\n" +
"\n" +
"spring.datasource.url=jdbc:h2:mem:testdb\n" +
"spring.datasource.driverClassName=org.h2.Driver\n" +
"spring.datasource.username=sa\n" +
"spring.datasource.password=password\n" +
"spring.jpa.database-platform=org.hibernate.dialect.H2Dialect\n" +
"\n" +
"spring.cassandra.keyspace-name=testKeySpace\n" +
"spring.cassandra.port=9042\n" +
"spring.cassandra.contact-points=localhost\n" +
"spring.cassandra.username=testusername\n" +
"spring.cassandra.schema-action=NONE\n" +
"spring.cassandra.request.timeout=10s\n" +
"spring.cassandra.connection.connect-timeout=10s\n" +
"spring.cassandra.connection.init-query-timeout=10s\n");
"spring.elasticsearch.connection-timeout=1000\n" +
"spring.elasticsearch.webclient.max-in-memory-size=122\n" +
"spring.elasticsearch.password=abc\n" +
"spring.elasticsearch.socket-timeout=100\n" +
"spring.elasticsearch.username=testUser\n" +
"\n" +
"spring.sql.init.data-locations=testdata\n" +
"spring.sql.init.password=password\n" +
"spring.sql.init.username=username\n" +
"spring.sql.init.mode=mode1\n" +
"spring.sql.init.platform=pls\n" +
"spring.sql.init.schema-locations=table1\n" +
"spring.sql.init.password=password2\n" +
"spring.sql.init.username=username2\n" +
"spring.sql.init.separator=k\n" +
"spring.sql.init.encoding=UTF-8\n" +
"\n" +
"spring.elasticsearch.connection-timeout=1\n" +
"spring.elasticsearch.password=testpassword\n" +
"spring.elasticsearch.socket-timeout=2\n" +
"spring.elasticsearch.restclient.sniffer.delay-after-failure=3\n" +
"spring.elasticsearch.restclient.sniffer.interval=4\n" +
"spring.elasticsearch.username=username\n" +
"\n" +
"spring.security.saml2.relyingparty.registration.idpone.assertingparty.entity-id=https://idpone.com\n" +
"spring.security.saml2.relyingparty.registration.idpone.assertingparty.sso-url=https://idpone.com\n" +
"spring.security.saml2.relyingparty.registration.idpone.assertingparty.verification.credentials.certificate-location=classpath:saml/idpone.crt\n" +
"\n" +
"server.reactive.session.cookie.same-site=true\n" +
"\n" +
"spring.datasource.url=jdbc:h2:mem:testdb\n" +
"spring.datasource.driverClassName=org.h2.Driver\n" +
"spring.datasource.username=sa\n" +
"spring.datasource.password=password\n" +
"spring.jpa.database-platform=org.hibernate.dialect.H2Dialect\n" +
"\n" +
"spring.cassandra.keyspace-name=testKeySpace\n" +
"spring.cassandra.port=9042\n" +
"spring.cassandra.contact-points=localhost\n" +
"spring.cassandra.username=testusername\n" +
"spring.cassandra.schema-action=NONE\n" +
"spring.cassandra.request.timeout=10s\n" +
"spring.cassandra.connection.connect-timeout=10s\n" +
"spring.cassandra.connection.init-query-timeout=10s\n" +
"logging.pattern.dateformat=yyyy-MM-dd HH:mm:ss.SSS\n" +
"management.endpoints.jmx.exposure.include=*\n");
}

private void verifyParentPomVersion() {
Xml.Document mavenAsXMLDocument = getRootBuildFile();

Xml.Tag parentTag =mavenAsXMLDocument
Xml.Tag parentTag = mavenAsXMLDocument
.getRoot()
.getChildren("parent").get(0);

Expand All @@ -317,7 +342,7 @@ private void verifyParentPomVersion() {
String groupId = parentTag.getChildValue("groupId").get();
String artifactId = parentTag.getChildValue("artifactId").get();

assertThat(version).isEqualTo("3.0.0-M3");
assertThat(version).isEqualTo("3.0.0");
assertThat(groupId).isEqualTo("org.springframework.boot");
assertThat(artifactId).isEqualTo("spring-boot-starter-parent");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- name: sbu30-migrate-javax-to-jakarta
- name: sbu30-migrate-to-jakarta-packages
description: Spring boot 3.0 Upgrade - Migrate javax packages to new jakarta packages
condition:
type: org.springframework.sbm.common.migration.conditions.TrueCondition
Expand All @@ -9,4 +9,4 @@
type: org.springframework.sbm.boot.common.conditions.IsSpringBootProject
versionPattern: "3\\.0\\..*"
description: Replace javax with new jakarta packages
openRewriteRecipeName: org.openrewrite.java.migrate.JavaxMigrationToJakarta
openRewriteRecipeName: org.openrewrite.java.migrate.JavaxMigrationToJakarta
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,22 @@
contributors:
- "Fabian Krüger[@fabapp2]"

- title: Upgrade to Jakarta EE 10
helper: org.springframework.sbm.boot.upgrade_27_30.report.helper.UpgradeSpringBootVersionHelper
change: |-
Jakarta EE Upgrade, usage of dependency coordinate changes from `javax.*` to `jakarta.*`
affected: |-
Upgrade application to support Jakarta EE upgrade
remediation:
description: |-
Whenever Spring Boot depends on a Jakarta EE specification, Spring Boot 3.0 has upgraded to the version that is included in Jakarta EE 10. For example, Spring Boot 3.0 uses the Servlet 6.0 and JPA 3.1 specifications.
If you are managing your own dependencies, and aren’t relying on our starter POMs, you should ensure that you have updated your Maven or Gradle file appropriately. You need to be especially careful that older Java EE dependencies are no longer directly or transitively used in your build. For example, if you should always be using jakarta.servlet:jakarta.servlet-api and not javax.servlet:javax.servlet-api.
As well as dependency coordinate changes, Jakarta EE now uses jakarta packages rather than javax. Once you’ve update your dependencies you may find that import statements in your project need to be updated.
recipe: sbu30-migrate-to-jakarta-packages
githubIssue: 723
contributors:
- "Fabian Krüger[@fabapp2]"
- "Sandeep Nagaraj[@sanagaraj-pivotal]"

- title: Actuator Endpoints Sanitization
helper: org.springframework.sbm.boot.upgrade_27_30.report.helper.ActuatorEndpointsSanitizationHelper
Expand Down Expand Up @@ -1678,4 +1694,4 @@
footer: |-
We want to say thank you to all Contributors:

Generated by Spring Boot Migrator (experimental)
Generated by Spring Boot Migrator (experimental)
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,6 @@
description: Remove redundant @ConstructorBinding annotations when applicable
recipeList:
- org.openrewrite.java.spring.boot3.RemoveConstructorBindingAnnotation
- type: org.springframework.sbm.engine.recipe.OpenRewriteNamedRecipeAdapter
condition:
type: org.springframework.sbm.boot.common.conditions.IsSpringBootProject
versionPattern: "3\\.0\\..*"
description: Replace javax with new jakarta packages
openRewriteRecipeName: org.openrewrite.java.migrate.JavaxMigrationToJakarta

- type: org.springframework.sbm.engine.recipe.OpenRewriteDeclarativeRecipeAdapter
condition:
Expand Down Expand Up @@ -668,4 +662,4 @@
- type: org.springframework.sbm.boot.upgrade_27_30.actions.Boot_27_30_JmxEndpointExposureAction
description: "Sets JMX endpoint exposure include to *"
condition:
type: org.springframework.sbm.boot.upgrade_27_30.conditions.JmxEndpointExposureCondition
type: org.springframework.sbm.boot.upgrade_27_30.conditions.JmxEndpointExposureCondition