Skip to content
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

Development: Update Spring Boot to 3.4.0 #9852

Merged
merged 10 commits into from
Dec 2, 2024
54 changes: 20 additions & 34 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -212,17 +212,16 @@ repositories {
maven {
url "https://build.shibboleth.net/maven/releases"
}
// required for latest jgit 7.0.0 dependency
// TODO: remove this when jgit is available in the official maven repository
// TODO: remove this when spring cloud is available in the official maven repository
maven {
url "https://repo.eclipse.org/content/repositories/jgit-releases"
url "https://repo.spring.io/milestone"
}
}

ext["jackson.version"] = fasterxml_version
ext["junit-jupiter.version"] = junit_version

ext { qDoxVersionReusable = "com.thoughtworks.qdox:qdox:2.1.0" }
ext { qDoxVersionReusable = "com.thoughtworks.qdox:qdox:2.2.0" }
ext { springBootStarterWeb = "org.springframework.boot:spring-boot-starter-web:${spring_boot_version}" }

dependencies {
Expand All @@ -231,9 +230,8 @@ dependencies {
// implementation "com.offbytwo.jenkins:jenkins-client:0.3.8"
implementation files("libs/jenkins-client-0.4.1.jar")
// The following 4 dependencies are explicitly integrated as transitive dependencies of jenkins-client-0.4.0.jar
// NOTE: we cannot upgrade to the latest version for org.apache.httpcomponents because of exceptions in Docker Java
implementation "org.apache.httpcomponents.client5:httpclient5:5.3.1" // also used by Docker Java
implementation "org.apache.httpcomponents.core5:httpcore5:5.2.5"
implementation "org.apache.httpcomponents.client5:httpclient5:5.4.1"
implementation "org.apache.httpcomponents.core5:httpcore5:5.3.1"
implementation "org.apache.httpcomponents:httpmime:4.5.14"
implementation("org.dom4j:dom4j:2.1.4") {
// Note: avoid org.xml.sax.SAXNotRecognizedException: unrecognized feature http://xml.org/sax/features/external-general-entities
Expand All @@ -246,7 +244,7 @@ dependencies {
exclude module: "jaxb-api"
}

implementation "org.gitlab4j:gitlab4j-api:6.0.0-rc.6"
implementation "org.gitlab4j:gitlab4j-api:6.0.0-rc.7"

implementation "de.jplag:jplag:${jplag_version}"

Expand All @@ -268,7 +266,7 @@ dependencies {
implementation "org.apache.lucene:lucene-queryparser:${lucene_version}"
implementation "org.apache.lucene:lucene-core:${lucene_version}"
implementation "org.apache.lucene:lucene-analyzers-common:${lucene_version}"
implementation "com.google.protobuf:protobuf-java:4.28.3"
implementation "com.google.protobuf:protobuf-java:4.29.0"

// we have to override those values to use the latest version
implementation "org.slf4j:jcl-over-slf4j:${slf4j_version}"
Expand All @@ -279,7 +277,7 @@ dependencies {
}
}

implementation "org.apache.logging.log4j:log4j-to-slf4j:2.24.1"
implementation "org.apache.logging.log4j:log4j-to-slf4j:2.24.2"

// Note: spring-security-lti13 does not work with jakarta yet, so we built our own custom version and declare its transitive dependencies below
// implementation "uk.ac.ox.ctl:spring-security-lti13:0.1.11"
Expand Down Expand Up @@ -327,24 +325,26 @@ dependencies {
// required by Saml2
implementation "org.apache.santuario:xmlsec:4.0.3"

implementation "org.jsoup:jsoup:1.18.1"
implementation "org.jsoup:jsoup:1.18.2"
implementation "commons-codec:commons-codec:1.17.1" // needed for spring security saml2

// TODO: decide if we want to use OpenAPI and Swagger v3
// implementation 'io.swagger.core.v3:swagger-annotations:2.2.23'
// implementation "org.springdoc:springdoc-openapi-ui:1.8.0"

// use the latest version to avoid security vulnerabilities
implementation "org.springframework:spring-webmvc:6.1.14"
implementation "org.springframework:spring-webmvc:${spring_framework_version}"

implementation "com.vdurmont:semver4j:3.1.0"

implementation "com.github.docker-java:docker-java-core:${docker_java_version}"
implementation "com.github.docker-java:docker-java-transport-httpclient5:${docker_java_version}"
// Note: we explicitly use docker-java-transport-zerodep, because docker-java-transport-httpclient5 uses an outdated http5 version which is not compatible with Spring Boot >= 3.4.0
implementation "com.github.docker-java:docker-java-transport-zerodep:${docker_java_version}"

// use newest version of commons-compress to avoid security issues through outdated dependencies
implementation "org.apache.commons:commons-compress:1.27.1"


// import JHipster dependencies BOM
implementation platform("tech.jhipster:jhipster-dependencies:${jhipster_dependencies_version}")

Expand Down Expand Up @@ -403,32 +403,26 @@ dependencies {
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:${spring_boot_version}"

implementation "org.springframework.ldap:spring-ldap-core:3.2.8"
implementation "org.springframework.data:spring-data-ldap:3.3.5"
implementation "org.springframework.data:spring-data-ldap:3.4.0"

implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:4.1.3") {
implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:${spring_cloud_version}") {
// NOTE: these modules contain security vulnerabilities and are not needed
exclude module: "commons-jxpath"
exclude module: "woodstox-core"
}
implementation "org.springframework.cloud:spring-cloud-starter-config:4.1.3"
implementation "org.springframework.cloud:spring-cloud-commons:4.1.4"
implementation "org.springframework.cloud:spring-cloud-starter-config:${spring_cloud_version}"
implementation "org.springframework.cloud:spring-cloud-commons:${spring_cloud_version}"
krusche marked this conversation as resolved.
Show resolved Hide resolved

implementation "io.netty:netty-all:4.1.115.Final"
implementation "io.projectreactor.netty:reactor-netty:1.2.0"
implementation("io.netty:netty-common") {
version {
strictly netty_version
}
}

implementation "org.springframework:spring-messaging:6.1.14"
implementation "org.springframework:spring-messaging:${spring_framework_version}"
krusche marked this conversation as resolved.
Show resolved Hide resolved
implementation "org.springframework.retry:spring-retry:2.0.10"

implementation "org.springframework.security:spring-security-config:${spring_security_version}"
implementation "org.springframework.security:spring-security-data:${spring_security_version}"
implementation "org.springframework.security:spring-security-core:${spring_security_version}"
implementation "org.springframework.security:spring-security-oauth2-core:${spring_security_version}"
implementation "org.springframework.security:spring-security-oauth2-client:${spring_security_version}"
implementation "org.springframework.security:spring-security-oauth2-resource-server:${spring_security_version}"
// use newest version of nimbus-jose-jwt to avoid security issues through outdated dependencies
implementation "com.nimbusds:nimbus-jose-jwt:9.47"

Expand Down Expand Up @@ -547,18 +541,10 @@ dependencies {
testImplementation "org.gradle:gradle-tooling-api:8.11.1"
testImplementation "org.apache.maven.surefire:surefire-report-parser:3.5.2"
testImplementation "com.opencsv:opencsv:5.9"
testImplementation("io.zonky.test:embedded-database-spring-test:2.5.1") {
testImplementation("io.zonky.test:embedded-database-spring-test:2.6.0") {
exclude group: "org.testcontainers", module: "mariadb"
exclude group: "org.testcontainers", module: "mssqlserver"
}
testImplementation "org.testcontainers:testcontainers:${testcontainer_version}"
testImplementation "org.testcontainers:mysql:${testcontainer_version}"
testImplementation "org.testcontainers:postgresql:${testcontainer_version}"
testImplementation "org.testcontainers:testcontainers:${testcontainer_version}"
testImplementation "org.testcontainers:junit-jupiter:${testcontainer_version}"
testImplementation "org.testcontainers:jdbc:${testcontainer_version}"
testImplementation "org.testcontainers:database-commons:${testcontainer_version}"

testImplementation "com.tngtech.archunit:archunit:1.3.0"
testImplementation("org.skyscreamer:jsonassert:1.5.3") {
exclude module: "android-json"
Expand Down
14 changes: 7 additions & 7 deletions docs/dev/guidelines/server-tests.rst
Original file line number Diff line number Diff line change
Expand Up @@ -151,19 +151,19 @@ Follow these tips to write performant tests:
* Limit object creation in tests and the test setup.


6. Avoid using @MockBean
=========================
6. Avoid using @MockitoBean
===========================

Do not use the ``@SpyBean`` or ``@MockBean`` annotation unless absolutely necessary or possibly in an abstract Superclass. `Here <https://www.baeldung.com/spring-tests>`__ you can see why in more detail.
Whenever``@MockBean`` appears in a class, the application context cache gets marked as dirty, meaning the runner will clean the cache after finishing the test class. The application context is restarted, which leads to an additional server start with runtime overhead.
Do not use the ``@MockitoSpyBean`` or ``@MockitoBean`` annotation unless absolutely necessary or possibly in an abstract Superclass. `Here <https://www.baeldung.com/spring-tests>`__ you can see why in more detail.
Whenever``@MockitoBean`` appears in a class, the application context cache gets marked as dirty, meaning the runner will clean the cache after finishing the test class. The application context is restarted, which leads to an additional server start with runtime overhead.
We want to keep the number of server starts minimal.

Below is an example of how to replace a ``@SpyBean``. To test an edge case where an ``IOException`` is thrown, we mocked the service method so it threw an Exception.
Below is an example of how to replace a ``@MockitoSpyBean``. To test an edge case where an ``IOException`` is thrown, we mocked the service method so it threw an Exception.

.. code-block:: java

class TestExport extends AbstractSpringIntegrationIndependentTest {
@SpyBean
@MockitoSpyBean
private FileUploadSubmissionExportService fileUploadSubmissionExportService;

@Test
Expand All @@ -174,7 +174,7 @@ Below is an example of how to replace a ``@SpyBean``. To test an edge case where
}
}

To avoid new SpyBeans, we now use `static mocks <https://asolntsev.github.io/en/2020/07/11/mockito-static-methods/>`__. Upon examining the ``export()`` method, we find a ``File.newOutputStream(..)`` call.
To avoid new MockitoSpyBeans, we now use `static mocks <https://asolntsev.github.io/en/2020/07/11/mockito-static-methods/>`__. Upon examining the ``export()`` method, we find a ``File.newOutputStream(..)`` call.
Now, instead of mocking the whole service, we can mock the static method:

.. code-block:: java
Expand Down
8 changes: 6 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ npm_version=10.8.0
jhipster_dependencies_version=8.7.2
spring_boot_version=3.3.6
spring_security_version=6.3.5
spring_boot_version=3.4.0
spring_framework_version=6.2.0
spring_cloud_version=4.2.0-RC1
krusche marked this conversation as resolved.
Show resolved Hide resolved
spring_security_version=6.4.1
krusche marked this conversation as resolved.
Show resolved Hide resolved
# TODO: upgrading to 6.6.0 currently leads to issues due to internal changes in Hibernate and potentially wrong use in Artemis server code
hibernate_version=6.4.10.Final
# TODO: can we update to 5.x?
opensaml_version=4.3.2
jwt_version=0.12.6
jaxb_runtime_version=4.0.5
hazelcast_version=5.5.0
fasterxml_version=2.18.1
fasterxml_version=2.18.2
jgit_version=7.0.0.202409031743-r
sshd_version=2.14.0
checkstyle_version=10.20.1
Expand All @@ -25,7 +29,7 @@ jplag_version=5.1.0
# NOTE: we do not need to use the latest version 9.x here as long as Stanford CoreNLP does not reference it
lucene_version=8.11.4
slf4j_version=2.0.16
sentry_version=7.18.0
sentry_version=7.18.1
liquibase_version=4.30.0
docker_java_version=3.4.0
logback_version=1.5.12
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import com.github.dockerjava.transport.DockerHttpClient;
import com.github.dockerjava.zerodep.ZerodepDockerHttpClient;
import com.google.common.util.concurrent.ThreadFactoryBuilder;

import de.tum.cit.aet.artemis.core.config.ProgrammingLanguageConfiguration;
Expand Down Expand Up @@ -151,7 +151,7 @@ public ExecutorService localCIBuildExecutorService() {
public DockerClient dockerClient() {
log.debug("Create bean dockerClient");
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().withDockerHost(dockerConnectionUri).build();
DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder().dockerHost(config.getDockerHost()).sslConfig(config.getSSLConfig()).build();
DockerHttpClient httpClient = new ZerodepDockerHttpClient.Builder().dockerHost(config.getDockerHost()).sslConfig(config.getSSLConfig()).build();
DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient);

log.debug("Docker client created with connection URI: {}", dockerConnectionUri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,56 @@

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.configuration.SSLContextFactory;
import org.springframework.cloud.configuration.TlsProperties;
import org.springframework.cloud.netflix.eureka.RestClientTimeoutProperties;
import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier;
import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactories;
import org.springframework.cloud.netflix.eureka.http.RestClientDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactories;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.web.client.RestClient;

/**
* This class is necessary to avoid using Jersey (which has an issue deserializing Eureka responses) after the spring boot upgrade.
* It provides the RestTemplateTransportClientFactories and RestTemplateDiscoveryClientOptionalArgs that would normally not be instantiated
* It provides the RestClientTransportClientFactories and RestClientDiscoveryClientOptionalArgs that would normally not be instantiated
* when Jersey is found by Eureka.
*/
@Profile({ PROFILE_CORE, PROFILE_BUILDAGENT })
@Configuration
public class EurekaClientRestTemplateConfiguration {
public class EurekaClientConfiguration {

private static final Logger log = LoggerFactory.getLogger(EurekaClientRestTemplateConfiguration.class);
private static final Logger log = LoggerFactory.getLogger(EurekaClientConfiguration.class);

/**
* Configures and returns {@link RestTemplateDiscoveryClientOptionalArgs} for Eureka client communication,
* Configures and returns {@link RestClientDiscoveryClientOptionalArgs} for Eureka client communication,
* with optional TLS/SSL setup based on provided configuration.
* <p>
* This method leverages the {@link EurekaClientHttpRequestFactorySupplier} to configure the RestTemplate
* This method leverages the {@link EurekaClientHttpRequestFactorySupplier} to configure the RestClient
* specifically for Eureka client interactions. If TLS is enabled in the provided {@link TlsProperties},
* a custom SSLContext is set up to ensure secure communication.
* </p>
*
* @param tlsProperties The TLS configuration properties, used to check if TLS is enabled and to configure it accordingly.
* @param eurekaClientHttpRequestFactorySupplier Supplies the HTTP request factory for the Eureka client RestTemplate.
* @return A configured instance of {@link RestTemplateDiscoveryClientOptionalArgs} for Eureka client,
* @param tlsProperties The TLS configuration properties, used to check if TLS is enabled and to configure it accordingly.
* @param restClientBuilderProvider The provider for the {@link RestClient.Builder} instance, if available.
* @return A configured instance of {@link RestClientDiscoveryClientOptionalArgs} for Eureka client,
* potentially with SSL/TLS enabled if specified in the {@code tlsProperties}.
* @throws GeneralSecurityException If there's an issue with setting up the SSL/TLS context.
* @throws IOException If there's an I/O error during the setup.
* @see TlsProperties
* @see EurekaClientHttpRequestFactorySupplier
*/
@Bean
public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs(TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier) throws GeneralSecurityException, IOException {
log.debug("Using RestTemplate for the Eureka client.");
public RestClientDiscoveryClientOptionalArgs restClientDiscoveryClientOptionalArgs(TlsProperties tlsProperties, ObjectProvider<RestClient.Builder> restClientBuilderProvider)
throws GeneralSecurityException, IOException {
log.debug("Using RestClient for the Eureka client.");
// The Eureka DiscoveryClientOptionalArgsConfiguration invokes a private method setupTLS.
// This code is taken from that method.
var args = new RestTemplateDiscoveryClientOptionalArgs(eurekaClientHttpRequestFactorySupplier);
var supplier = new DefaultEurekaClientHttpRequestFactorySupplier(new RestClientTimeoutProperties());
var args = new RestClientDiscoveryClientOptionalArgs(supplier, () -> restClientBuilderProvider.getIfAvailable(RestClient::builder));
if (tlsProperties.isEnabled()) {
SSLContextFactory factory = new SSLContextFactory(tlsProperties);
args.setSSLContext(factory.createSSLContext());
Expand All @@ -61,7 +66,7 @@ public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOption
}

@Bean
public RestTemplateTransportClientFactories restTemplateTransportClientFactories(RestTemplateDiscoveryClientOptionalArgs optionalArgs) {
return new RestTemplateTransportClientFactories(optionalArgs);
public RestClientTransportClientFactories restClientTransportClientFactories(RestClientDiscoveryClientOptionalArgs optionalArgs) {
return new RestClientTransportClientFactories(optionalArgs);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ public SpringLiquibase liquibase(@LiquibaseDataSource ObjectProvider<DataSource>
SpringLiquibase liquibase = SpringLiquibaseUtil.createSpringLiquibase(liquibaseDataSource.getIfAvailable(), liquibaseProperties, dataSource, dataSourceProperties);
Scope.setScopeManager(new SingletonScopeManager());
liquibase.setChangeLog("classpath:config/liquibase/master.xml");
liquibase.setContexts(liquibaseProperties.getContexts());
liquibase.setContexts(liquibaseProperties.getContexts() != null ? liquibaseProperties.getContexts().getFirst() : null);
liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
liquibase.setLiquibaseSchema(liquibaseProperties.getLiquibaseSchema());
liquibase.setLiquibaseTablespace(liquibaseProperties.getLiquibaseTablespace());
liquibase.setDatabaseChangeLogLockTable(liquibaseProperties.getDatabaseChangeLogLockTable());
liquibase.setDatabaseChangeLogTable(liquibaseProperties.getDatabaseChangeLogTable());
liquibase.setDropFirst(liquibaseProperties.isDropFirst());
liquibase.setLabelFilter(liquibaseProperties.getLabelFilter());
liquibase.setLabelFilter(liquibaseProperties.getLabelFilter() != null ? liquibaseProperties.getLabelFilter().getFirst() : null);
liquibase.setChangeLogParameters(liquibaseProperties.getParameters());
liquibase.setRollbackFile(liquibaseProperties.getRollbackFile());
liquibase.setTestRollbackOnUpdate(liquibaseProperties.isTestRollbackOnUpdate());
Expand Down
Loading
Loading