From 79f70accbe1fdff74cf8f888b791e588415bc555 Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Fri, 26 Jun 2026 10:45:11 +0200 Subject: [PATCH 01/13] upgrade to spring boot 4 --- server/build.gradle | 47 +++++------- server/gradle/libs.versions.toml | 29 ++++--- server/settings.gradle | 37 +++++++++ .../eclipse/openvsx/RegistryApplication.java | 12 +-- .../eclipse/openvsx/RestTemplateConfig.java | 75 ++++++++----------- .../openvsx/UpstreamRegistryService.java | 2 +- .../eclipse/openvsx/cache/CacheConfig.java | 6 +- .../eclipse/openvsx/cache/CacheService.java | 4 +- .../openvsx/cache/jedis/JedisUtil.java | 6 +- .../mirror/DataMirrorJobRequestHandler.java | 2 +- .../config/RateLimitAutoConfiguration.java | 2 +- .../ratelimit/config/RateLimitConfig.java | 4 +- .../ExtensionScanPersistenceService.java | 11 +-- .../scanning/GitleaksRulesService.java | 4 +- .../openvsx/scanning/HttpClientExecutor.java | 11 +-- .../scanning/HttpResponseExtractor.java | 12 +-- .../eclipse/openvsx/search/SearchConfig.java | 6 +- .../openvsx/web/ServerErrorController.java | 10 +-- .../org/eclipse/openvsx/IntegrationTest.java | 2 +- .../org/eclipse/openvsx/RegistryAPITest.java | 4 +- .../java/org/eclipse/openvsx/UserAPITest.java | 4 +- .../openvsx/adapter/VSCodeAPITest.java | 4 +- .../eclipse/openvsx/admin/AdminAPITest.java | 4 +- .../openvsx/admin/FileDecisionAPITest.java | 4 +- .../eclipse/openvsx/admin/ScanAPITest.java | 4 +- .../ratelimit/RateLimitIntegrationTest.java | 2 +- .../scanning/ResponseExtractorTest.java | 2 +- .../openvsx/web/SitemapControllerTest.java | 4 +- 28 files changed, 155 insertions(+), 159 deletions(-) diff --git a/server/build.gradle b/server/build.gradle index 92f00fac0..6535ce63c 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -15,15 +15,13 @@ plugins { def jooqSrcDir = 'src/main/jooq-gen' -ext['junit-jupiter.version'] = libs.versions.junit.get() +//ext['junit-jupiter.version'] = libs.versions.junit.get() // override spring boot managed versions // see https://docs.spring.io/spring-boot/gradle-plugin/managing-dependencies.html // example: // ext['netty.version'] = '4.1.132.Final' -ext['postgresql.version'] = '42.7.11' -ext['netty.version'] = '4.1.135.Final' ext['opentelemetry.version'] = '1.63.0' java { @@ -52,24 +50,13 @@ configurations { // exclude commons-logging to avoid runtime warnings like these // Standard Commons Logging discovery in action with spring-jcl: // please remove commons-logging.jar from classpath in order to avoid potential conflicts - configureEach { - exclude group: "commons-logging", module: "commons-logging" - } - - // Gatling 3.15 requires Netty 4.2.x but Spring Boot's dependency management forces 4.1.x. - // Override all io.netty modules (except tcnative which has its own versioning) on the - // gatling configuration to keep its classpath internally consistent. - matching { it.name.startsWith('gatling') }.configureEach { - resolutionStrategy.eachDependency { details -> - if (details.requested.group == 'io.netty' && !details.requested.name.startsWith('netty-tcnative')) { - details.useVersion '4.2.13.Final' - } - } - } +// configureEach { +// exclude group: "commons-logging", module: "commons-logging" +// } } dependencies { - implementation "org.springframework.boot:spring-boot-starter-web" + implementation "org.springframework.boot:spring-boot-starter-webmvc" implementation "org.springframework.boot:spring-boot-starter-jetty" modules { module("org.springframework.boot:spring-boot-starter-tomcat") { @@ -84,13 +71,11 @@ dependencies { implementation "org.springframework.boot:spring-boot-starter-security" implementation "org.springframework.boot:spring-boot-starter-actuator" implementation "org.springframework.boot:spring-boot-starter-cache" - implementation "org.springframework.boot:spring-boot-starter-aop" implementation "org.springframework.boot:spring-boot-starter-mail" implementation "org.springframework.boot:spring-boot-starter-thymeleaf" implementation "org.springframework.security:spring-security-oauth2-client" implementation "org.springframework.security:spring-security-oauth2-jose" implementation "org.springframework.session:spring-session-jdbc" - implementation "org.springframework.retry:spring-retry" implementation "org.apache.httpcomponents.client5:httpclient5" implementation "com.github.ben-manes.caffeine:caffeine" @@ -119,13 +104,14 @@ dependencies { implementation libs.awssdk.sts implementation libs.springdoc - - implementation libs.jackson.core - implementation libs.jackson.annotations - implementation libs.jackson.databind - implementation libs.jackson.jaxb.annotations - implementation libs.jackson.dataformat.xml - implementation libs.jackson.dataformat.yaml + implementation libs.spring.retry + +// implementation libs.jackson.core +// implementation libs.jackson.annotations +// implementation libs.jackson.databind +// implementation libs.jackson.jaxb.annotations +// implementation libs.jackson.dataformat.xml +// implementation libs.jackson.dataformat.yaml implementation libs.jackson.dataformat.toml implementation libs.woodstox.core @@ -142,18 +128,21 @@ dependencies { runtimeOnly "org.postgresql:postgresql" jooqCodegen "org.postgresql:postgresql" + // to aid migration from 3.5 to 4.0 + runtimeOnly "org.springframework.boot:spring-boot-properties-migrator" + devRuntimeOnly "org.springframework.boot:spring-boot-devtools" testImplementation("org.springframework.boot:spring-boot-starter-test") { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } + testImplementation "org.springframework.boot:spring-boot-starter-webmvc-test" testImplementation "org.springframework.security:spring-security-test" - testImplementation libs.junit.jupiter.api + testImplementation libs.testcontainers.elasticsearch testImplementation libs.testcontainers.localstack testImplementation libs.testcontainers.junit.jupiter - testRuntimeOnly libs.junit.jupiter.engine testRuntimeOnly libs.testcontainers.postgresql gatling libs.gatling.core diff --git a/server/gradle/libs.versions.toml b/server/gradle/libs.versions.toml index d188262a5..216a65795 100644 --- a/server/gradle/libs.versions.toml +++ b/server/gradle/libs.versions.toml @@ -8,30 +8,28 @@ commons-lang3 = "3.20.0" flyway = "11.20.3" gatling = "3.15.0" gcloud = "2.62.1" -hibernate = "6.6.42.Final" ipaddress = "5.5.1" -jackson = "2.18.8" +jackson = "3.1.4" java = "25" jaxb-api = "2.3.1" jaxb-impl = "2.3.8" jedis = "7.4.1" -jobrunr = "7.5.3" +jobrunr = "8.6.1" jooq = "3.19.34" jsonpath = "2.9.0" -junit = "5.14.2" loki4j = "1.4.2" re2j = "1.7" rewrite-java = "3.26.0" -spring-boot = "3.5.14" +spring-boot = "4.0.7" +spring-retry = "2.0.13" springdoc = "2.8.13" -testcontainers = "1.21.4" tika = "3.3.1" woodstox = "6.4.0" [plugins] download = { id = "de.undercouch.download", version = "5.7.0" } gatling = { id = "io.gatling.gradle", version.ref = "gatling" } -hibernate-orm = { id = "org.hibernate.orm", version.ref = "hibernate" } +hibernate-orm = { id = "org.hibernate.orm" } jooq-codegen = { id = "org.jooq.jooq-codegen-gradle", version.ref = "jooq" } spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot"} spring-dependency-management = { id = "io.spring.dependency-management", version = "1.1.7" } @@ -51,28 +49,27 @@ flyway-database-postgresql = { module = "org.flywaydb:flyway-database-postgres gatling-app = { module = "io.gatling:gatling-app", version.ref = "gatling" } gatling-core = { module = "io.gatling:gatling-core", version.ref = "gatling" } google-cloud-storage = { module = "com.google.cloud:google-cloud-storage", version.ref = "gcloud" } -ipaddress = { module = "com.github.seancfoley:ipaddress", version.ref = "ipaddress"} +ipaddress = { module = "com.github.seancfoley:ipaddress", version.ref = "ipaddress" } jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations", version.ref = "jackson" } jackson-core = { module = "com.fasterxml.jackson.core:jackson-core", version.ref = "jackson" } jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" } -jackson-dataformat-toml = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-toml", version.ref = "jackson" } +jackson-dataformat-toml = { module = "tools.jackson.dataformat:jackson-dataformat-toml", version.ref = "jackson" } jackson-dataformat-xml = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-xml", version.ref = "jackson" } jackson-dataformat-yaml = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml", version.ref = "jackson" } jackson-jaxb-annotations = { module = "com.fasterxml.jackson.module:jackson-module-jaxb-annotations", version.ref = "jackson" } jaxb-api = { module = "javax.xml.bind:jaxb-api", version.ref = "jaxb-api" } jaxb-impl = { module = "com.sun.xml.bind:jaxb-impl", version.ref = "jaxb-impl" } jedis = { module = "redis.clients:jedis", version.ref = "jedis" } -jobrunr-spring = { module = "org.jobrunr:jobrunr-spring-boot-3-starter", version.ref = "jobrunr" } +jobrunr-spring = { module = "org.jobrunr:jobrunr-spring-boot-4-starter", version.ref = "jobrunr" } json-path = { module = "com.jayway.jsonpath:json-path", version.ref = "jsonpath" } -junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" } -junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" } loki-logback-appender = { module = "com.github.loki4j:loki-logback-appender", version.ref = "loki4j" } re2j = { module = "com.google.re2j:re2j", version.ref = "re2j" } rewrite-java = { module = "org.openrewrite.recipe:rewrite-migrate-java", version.ref = "rewrite-java"} springdoc = { module = "org.springdoc:springdoc-openapi-starter-webmvc-ui", version.ref = "springdoc" } -testcontainers-elasticsearch = { module = "org.testcontainers:elasticsearch", version.ref = "testcontainers" } -testcontainers-junit-jupiter = { module = "org.testcontainers:junit-jupiter", version.ref = "testcontainers" } -testcontainers-localstack = { module = "org.testcontainers:localstack", version.ref = "testcontainers" } -testcontainers-postgresql = { module = "org.testcontainers:postgresql", version.ref = "testcontainers" } +spring-retry = { module = "org.springframework.retry:spring-retry", version.ref = "spring-retry" } +testcontainers-elasticsearch = { module = "org.testcontainers:testcontainers-elasticsearch" } +testcontainers-junit-jupiter = { module = "org.testcontainers:testcontainers-junit-jupiter" } +testcontainers-localstack = { module = "org.testcontainers:testcontainers-localstack" } +testcontainers-postgresql = { module = "org.testcontainers:testcontainers-postgresql" } tika-core = { module = "org.apache.tika:tika-core", version.ref = "tika" } woodstox-core = { module = "com.fasterxml.woodstox:woodstox-core", version.ref = "woodstox" } diff --git a/server/settings.gradle b/server/settings.gradle index 1999fb519..99d9bd3d5 100644 --- a/server/settings.gradle +++ b/server/settings.gradle @@ -1 +1,38 @@ +import groovy.xml.XmlSlurper + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + } + resolutionStrategy { + eachPlugin { + // Supply the Hibernate ORM plugin version from Spring Boot's BOM so it is + // always kept in sync with the managed hibernate-core dependency. + if (requested.id.id == 'org.hibernate.orm') { + useVersion(gradle.ext.hibernateOrmVersion) + } + } + } +} + rootProject.name = 'openvsx-server' + +buildscript { + repositories { + mavenCentral() + } +} + +// Read the Spring Boot version from the version catalog file +def tomlText = file("gradle/libs.versions.toml").text +def springBootVersion = (tomlText =~ /(?m)^spring-boot\s*=\s*"([^"]+)"/).collect { it[1] }.first() + +// Resolve the Spring Boot BOM POM and extract the Hibernate ORM version it manages. +// The BOM uses Maven property placeholders (e.g. ${hibernate.version}) in +// , so we read the resolved value from directly. +def bomPomFile = buildscript.configurations.detachedConfiguration( + buildscript.dependencies.create("org.springframework.boot:spring-boot-dependencies:${springBootVersion}@pom") +).singleFile +def bomPom = new XmlSlurper().parse(bomPomFile) +gradle.ext.hibernateOrmVersion = bomPom.properties['hibernate.version'].text() diff --git a/server/src/main/java/org/eclipse/openvsx/RegistryApplication.java b/server/src/main/java/org/eclipse/openvsx/RegistryApplication.java index c867c643f..56cd187b9 100644 --- a/server/src/main/java/org/eclipse/openvsx/RegistryApplication.java +++ b/server/src/main/java/org/eclipse/openvsx/RegistryApplication.java @@ -18,9 +18,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration; -import org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration; -import org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration; +import org.springframework.boot.data.elasticsearch.autoconfigure.DataElasticsearchReactiveRepositoriesAutoConfiguration; +import org.springframework.boot.data.elasticsearch.autoconfigure.DataElasticsearchRepositoriesAutoConfiguration; +import org.springframework.boot.data.redis.autoconfigure.DataRedisRepositoriesAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; @@ -41,9 +41,9 @@ // exclude autoconfiguration for them to avoid unnecessary logging // messages due to existing jpa repositories // can be removed once such repositories are in use - ElasticsearchRepositoriesAutoConfiguration.class, - ReactiveElasticsearchRepositoriesAutoConfiguration.class, - RedisRepositoriesAutoConfiguration.class, + DataElasticsearchRepositoriesAutoConfiguration.class, + DataElasticsearchReactiveRepositoriesAutoConfiguration.class, + DataRedisRepositoriesAutoConfiguration.class, }) @EnableScheduling @EnableRetry diff --git a/server/src/main/java/org/eclipse/openvsx/RestTemplateConfig.java b/server/src/main/java/org/eclipse/openvsx/RestTemplateConfig.java index 19aa9d93e..34046b364 100644 --- a/server/src/main/java/org/eclipse/openvsx/RestTemplateConfig.java +++ b/server/src/main/java/org/eclipse/openvsx/RestTemplateConfig.java @@ -15,15 +15,15 @@ import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.core5.util.Timeout; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.converter.StringHttpMessageConverter; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.DefaultUriBuilderFactory; +import java.util.List; import java.util.concurrent.TimeUnit; @Configuration @@ -76,63 +76,50 @@ private HttpConnPoolConfig createHttpConnPoolConfig(int maxTotal, int defaultMax } @Bean - public RestTemplate restTemplate(RestTemplateBuilder builder, HttpConnPoolConfig foregroundHttpConnPool) { + public RestTemplate restTemplate(HttpConnPoolConfig foregroundHttpConnPool) { var httpClient = createHttpClientBuilder(foregroundHttpConnPool).build(); - return builder - .requestFactory(() -> { - HttpComponentsClientHttpRequestFactory f = new HttpComponentsClientHttpRequestFactory(); - f.setHttpClient(httpClient); - return f; - }) - .messageConverters( - new StringHttpMessageConverter(), - new MappingJackson2HttpMessageConverter()) - .build(); + var factory = new HttpComponentsClientHttpRequestFactory(); + factory.setHttpClient(httpClient); + var restTemplate = new RestTemplate(factory); + restTemplate.setMessageConverters(List.of( + new StringHttpMessageConverter(), + new JacksonJsonHttpMessageConverter())); + return restTemplate; } @Bean - public RestTemplate nonRedirectingRestTemplate(RestTemplateBuilder builder, HttpConnPoolConfig foregroundHttpConnPool) { + public RestTemplate nonRedirectingRestTemplate(HttpConnPoolConfig foregroundHttpConnPool) { var httpClient = createHttpClientBuilder(foregroundHttpConnPool).disableRedirectHandling().build(); - return builder - .requestFactory(() -> { - HttpComponentsClientHttpRequestFactory f = new HttpComponentsClientHttpRequestFactory(); - f.setHttpClient(httpClient); - return f; - }) - .build(); + var factory = new HttpComponentsClientHttpRequestFactory(); + factory.setHttpClient(httpClient); + return new RestTemplate(factory); } @Bean - public RestTemplate backgroundRestTemplate(RestTemplateBuilder builder, HttpConnPoolConfig backgroundHttpConnPool) { + public RestTemplate backgroundRestTemplate(HttpConnPoolConfig backgroundHttpConnPool) { var httpClient = createHttpClientBuilder(backgroundHttpConnPool).build(); - DefaultUriBuilderFactory defaultUriBuilderFactory = new DefaultUriBuilderFactory(); + var factory = new HttpComponentsClientHttpRequestFactory(); + factory.setHttpClient(httpClient); + var restTemplate = new RestTemplate(factory); + var defaultUriBuilderFactory = new DefaultUriBuilderFactory(); defaultUriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE); - return builder - .uriTemplateHandler(defaultUriBuilderFactory) - .messageConverters( - new StringHttpMessageConverter(), - new MappingJackson2HttpMessageConverter()) - .requestFactory(() -> { - HttpComponentsClientHttpRequestFactory f = new HttpComponentsClientHttpRequestFactory(); - f.setHttpClient(httpClient); - return f; - }) - .build(); + restTemplate.setUriTemplateHandler(defaultUriBuilderFactory); + restTemplate.setMessageConverters(List.of( + new StringHttpMessageConverter(), + new JacksonJsonHttpMessageConverter())); + return restTemplate; } @Bean - public RestTemplate backgroundNonRedirectingRestTemplate(RestTemplateBuilder builder, HttpConnPoolConfig backgroundHttpConnPool) { + public RestTemplate backgroundNonRedirectingRestTemplate(HttpConnPoolConfig backgroundHttpConnPool) { var httpClient = createHttpClientBuilder(backgroundHttpConnPool).disableRedirectHandling().build(); - DefaultUriBuilderFactory defaultUriBuilderFactory = new DefaultUriBuilderFactory(); + var factory = new HttpComponentsClientHttpRequestFactory(); + factory.setHttpClient(httpClient); + var restTemplate = new RestTemplate(factory); + var defaultUriBuilderFactory = new DefaultUriBuilderFactory(); defaultUriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE); - return builder - .uriTemplateHandler(defaultUriBuilderFactory) - .requestFactory(() -> { - HttpComponentsClientHttpRequestFactory f = new HttpComponentsClientHttpRequestFactory(); - f.setHttpClient(httpClient); - return f; - }) - .build(); + restTemplate.setUriTemplateHandler(defaultUriBuilderFactory); + return restTemplate; } @Bean diff --git a/server/src/main/java/org/eclipse/openvsx/UpstreamRegistryService.java b/server/src/main/java/org/eclipse/openvsx/UpstreamRegistryService.java index 586572d45..080c3a085 100644 --- a/server/src/main/java/org/eclipse/openvsx/UpstreamRegistryService.java +++ b/server/src/main/java/org/eclipse/openvsx/UpstreamRegistryService.java @@ -248,7 +248,7 @@ public ResponseEntity extractData(ClientHttpResponse resp var statusCode = response.getStatusCode(); if (statusCode.is2xxSuccessful()) { return ResponseEntity.status(HttpStatus.FOUND) - .location(UriComponentsBuilder.fromHttpUrl(urlTemplate).build(uriVariables)) + .location(UriComponentsBuilder.fromUriString(urlTemplate).build(uriVariables)) .build(); } if (statusCode.is3xxRedirection()) { diff --git a/server/src/main/java/org/eclipse/openvsx/cache/CacheConfig.java b/server/src/main/java/org/eclipse/openvsx/cache/CacheConfig.java index 0b09b73b5..69082a118 100644 --- a/server/src/main/java/org/eclipse/openvsx/cache/CacheConfig.java +++ b/server/src/main/java/org/eclipse/openvsx/cache/CacheConfig.java @@ -30,7 +30,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.boot.data.redis.autoconfigure.DataRedisProperties; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.caffeine.CaffeineCacheManager; @@ -140,7 +140,7 @@ public Cache settingCache( @Bean @ConditionalOnExpression("${bucket4j.enabled:false} && '${bucket4j.cache-to-use:}' == 'redis-jedis'") - public RedisClient redisClient(RedisProperties properties) { + public RedisClient redisClient(DataRedisProperties properties) { logger.info("Configure 'redis-jedis' bucket4j rate-limiting cache"); var builder = RedisClient.builder(); @@ -152,7 +152,7 @@ public RedisClient redisClient(RedisProperties properties) { @Bean @ConditionalOnExpression("${bucket4j.enabled:false} && '${bucket4j.cache-to-use:}' == 'redis-cluster-jedis'") - public RedisClusterClient redisClusterClient(RedisProperties properties) { + public RedisClusterClient redisClusterClient(DataRedisProperties properties) { logger.info("Configure 'redis-cluster-jedis' bucket4j rate-limiting cache"); var builder = RedisClusterClient.builder(); diff --git a/server/src/main/java/org/eclipse/openvsx/cache/CacheService.java b/server/src/main/java/org/eclipse/openvsx/cache/CacheService.java index 0f531fe5d..bee1a16e8 100644 --- a/server/src/main/java/org/eclipse/openvsx/cache/CacheService.java +++ b/server/src/main/java/org/eclipse/openvsx/cache/CacheService.java @@ -117,7 +117,7 @@ public void evictExtensionJsons(Extension extension) { // This uses the redis KEYS command that might take a while but considering the typical size of the EXTENSION_JSON // cache its acceptable. if (cache instanceof RedisCacheWriter redisCache) { - redisCache.clean(CACHE_EXTENSION_JSON, extensionJsonCacheKey.generateWildcard(extension).getBytes()); + redisCache.clear(CACHE_EXTENSION_JSON, extensionJsonCacheKey.generateWildcard(extension).getBytes()); return; } @@ -179,7 +179,7 @@ private void evictInternalLatestExtensionVersion(Extension extension) { // This uses the redis KEYS command that might take a while but considering the typical size of the EXTENSION_JSON // cache its acceptable. if (cache instanceof RedisCacheWriter redisCache) { - redisCache.clean(CACHE_LATEST_EXTENSION_VERSION, latestExtensionVersionCacheKey.generateWildcard(extension).getBytes()); + redisCache.clear(CACHE_LATEST_EXTENSION_VERSION, latestExtensionVersionCacheKey.generateWildcard(extension).getBytes()); return; } diff --git a/server/src/main/java/org/eclipse/openvsx/cache/jedis/JedisUtil.java b/server/src/main/java/org/eclipse/openvsx/cache/jedis/JedisUtil.java index eba4deabd..f08968325 100644 --- a/server/src/main/java/org/eclipse/openvsx/cache/jedis/JedisUtil.java +++ b/server/src/main/java/org/eclipse/openvsx/cache/jedis/JedisUtil.java @@ -13,7 +13,7 @@ package org.eclipse.openvsx.cache.jedis; import io.micrometer.common.util.StringUtils; -import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.boot.data.redis.autoconfigure.DataRedisProperties; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; @@ -25,7 +25,7 @@ public class JedisUtil { private JedisUtil() {} - public static JedisClientConfig getClientConfig(RedisProperties properties) { + public static JedisClientConfig getClientConfig(DataRedisProperties properties) { var configBuilder = DefaultJedisClientConfig.builder(); var username = properties.getUsername(); if (StringUtils.isNotEmpty(username)) { @@ -40,7 +40,7 @@ public static JedisClientConfig getClientConfig(RedisProperties properties) { return configBuilder.build(); } - public static Set getNodes(RedisProperties properties) { + public static Set getNodes(DataRedisProperties properties) { return properties.getCluster().getNodes().stream() .map(HostAndPort::from) .collect(Collectors.toSet()); diff --git a/server/src/main/java/org/eclipse/openvsx/mirror/DataMirrorJobRequestHandler.java b/server/src/main/java/org/eclipse/openvsx/mirror/DataMirrorJobRequestHandler.java index 73ddadda0..f673e3472 100644 --- a/server/src/main/java/org/eclipse/openvsx/mirror/DataMirrorJobRequestHandler.java +++ b/server/src/main/java/org/eclipse/openvsx/mirror/DataMirrorJobRequestHandler.java @@ -117,7 +117,7 @@ private List processUrls(NodeList urls, UserData mirrorUser) { logger.error("failed to mirror {}", extensionId, e); } extensionIds.add(extensionId); - progress.increaseByOne(); + progress.incrementSucceeded(); } return extensionIds; diff --git a/server/src/main/java/org/eclipse/openvsx/ratelimit/config/RateLimitAutoConfiguration.java b/server/src/main/java/org/eclipse/openvsx/ratelimit/config/RateLimitAutoConfiguration.java index c46026388..8258fe838 100644 --- a/server/src/main/java/org/eclipse/openvsx/ratelimit/config/RateLimitAutoConfiguration.java +++ b/server/src/main/java/org/eclipse/openvsx/ratelimit/config/RateLimitAutoConfiguration.java @@ -18,7 +18,7 @@ import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.web.server.WebServerFactoryCustomizer; -import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; +import org.springframework.boot.web.server.servlet.ConfigurableServletWebServerFactory; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.GenericApplicationContext; import org.springframework.util.StringUtils; diff --git a/server/src/main/java/org/eclipse/openvsx/ratelimit/config/RateLimitConfig.java b/server/src/main/java/org/eclipse/openvsx/ratelimit/config/RateLimitConfig.java index 57ecac824..0913b15bc 100644 --- a/server/src/main/java/org/eclipse/openvsx/ratelimit/config/RateLimitConfig.java +++ b/server/src/main/java/org/eclipse/openvsx/ratelimit/config/RateLimitConfig.java @@ -26,8 +26,8 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.data.redis.autoconfigure.DataRedisProperties; import org.springframework.cache.CacheManager; import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.context.annotation.Bean; @@ -59,7 +59,7 @@ public ExpressionParser expressionParser() { } @Bean - public RedisClusterClient redisClusterClient(RedisProperties properties) { + public RedisClusterClient redisClusterClient(DataRedisProperties properties) { logger.info("Configure jedis-cluster rate-limiting cache"); var builder = RedisClusterClient.builder(); diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanPersistenceService.java b/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanPersistenceService.java index 61983b836..af429830b 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanPersistenceService.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanPersistenceService.java @@ -12,9 +12,6 @@ ********************************************************************************/ package org.eclipse.openvsx.scanning; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.transaction.Transactional; import org.eclipse.openvsx.entities.*; import org.eclipse.openvsx.repositories.FileDecisionRepository; @@ -28,6 +25,10 @@ import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; +import tools.jackson.core.JacksonException; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; + import java.time.LocalDateTime; import java.util.Collections; import java.util.Map; @@ -502,7 +503,7 @@ public String serializeFileHashes(@Nullable Map fileHashes) { } try { return objectMapper.writeValueAsString(fileHashes); - } catch (JsonProcessingException e) { + } catch (JacksonException e) { logger.warn("Failed to serialize file hashes: {}", e.getMessage()); return null; } @@ -587,7 +588,7 @@ private Map parseFileHashes(String fileHashesJson) { } try { return objectMapper.readValue(fileHashesJson, new TypeReference>() {}); - } catch (JsonProcessingException e) { + } catch (JacksonException e) { logger.warn("Failed to parse file hashes JSON: {}", e.getMessage()); return Collections.emptyMap(); } diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java b/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java index d82eb618c..4ee1cf07c 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java @@ -13,8 +13,6 @@ package org.eclipse.openvsx.scanning; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.toml.TomlMapper; import org.eclipse.openvsx.cache.jedis.JedisClusterChannelListener; import org.eclipse.openvsx.migration.HandlerJobRequest; import org.jobrunr.jobs.annotations.Job; @@ -29,6 +27,8 @@ import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import redis.clients.jedis.RedisClusterClient; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.dataformat.toml.TomlMapper; import java.io.File; import java.io.IOException; diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java b/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java index bca9b216a..494e5ebae 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java @@ -21,7 +21,6 @@ import org.eclipse.openvsx.util.TempFile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.core.io.FileSystemResource; import org.springframework.http.*; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; @@ -123,13 +122,9 @@ private static RestTemplate createRestTemplate(RemoteScannerProperties.HttpConfi .setDefaultRequestConfig(requestConfig) .build(); - return new RestTemplateBuilder() - .requestFactory(() -> { - HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); - factory.setHttpClient(httpClient); - return factory; - }) - .build(); + var factory = new HttpComponentsClientHttpRequestFactory(); + factory.setHttpClient(httpClient); + return new RestTemplate(factory); } /** diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/HttpResponseExtractor.java b/server/src/main/java/org/eclipse/openvsx/scanning/HttpResponseExtractor.java index b0ef578e7..22acc688a 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/HttpResponseExtractor.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/HttpResponseExtractor.java @@ -12,11 +12,11 @@ ********************************************************************************/ package org.eclipse.openvsx.scanning; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Component; +import tools.jackson.core.JacksonException; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; import java.util.ArrayList; import java.util.List; @@ -69,7 +69,7 @@ public List> extractList(String response, String format, Str private JsonNode parseJson(String json) throws ScannerException { try { return objectMapper.readTree(json); - } catch (JsonProcessingException e) { + } catch (JacksonException e) { throw new ScannerException("Failed to parse JSON response", e); } } @@ -146,7 +146,7 @@ private List descend(JsonNode node, String segment) { if (node.isArray()) { node.forEach(result::add); } else if (node.isObject()) { - node.fields().forEachRemaining(entry -> result.add(entry.getValue())); + node.properties().iterator().forEachRemaining(entry -> result.add(entry.getValue())); } return result; } diff --git a/server/src/main/java/org/eclipse/openvsx/search/SearchConfig.java b/server/src/main/java/org/eclipse/openvsx/search/SearchConfig.java index 3edec867b..5d8c120aa 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/SearchConfig.java +++ b/server/src/main/java/org/eclipse/openvsx/search/SearchConfig.java @@ -10,7 +10,7 @@ package org.eclipse.openvsx.search; import org.apache.commons.lang3.StringUtils; -import org.apache.http.ssl.SSLContextBuilder; +import org.apache.hc.core5.ssl.SSLContextBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -82,13 +82,13 @@ private SSLContext sslContext() { try { sslContextBuilder.loadTrustMaterial(new File(trustStore), trustStorePassword.toCharArray()); } catch(NoSuchAlgorithmException | KeyStoreException | CertificateException | IOException e) { - logger.error("Unable to load password protected trust material " + trustStore, e); + logger.error("Unable to load password protected trust material {}", trustStore, e); } } else { try { sslContextBuilder.loadTrustMaterial(new File(trustStore)); } catch(NoSuchAlgorithmException | KeyStoreException | CertificateException | IOException e) { - logger.error("Unable to load trust material " + trustStore, e); + logger.error("Unable to load trust material {}", trustStore, e); } } try { diff --git a/server/src/main/java/org/eclipse/openvsx/web/ServerErrorController.java b/server/src/main/java/org/eclipse/openvsx/web/ServerErrorController.java index 93dc6b652..a520fe202 100644 --- a/server/src/main/java/org/eclipse/openvsx/web/ServerErrorController.java +++ b/server/src/main/java/org/eclipse/openvsx/web/ServerErrorController.java @@ -12,9 +12,9 @@ import org.eclipse.openvsx.util.UrlUtil; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController; -import org.springframework.boot.web.servlet.error.ErrorAttributes; +import org.springframework.boot.autoconfigure.web.WebProperties; +import org.springframework.boot.webmvc.autoconfigure.error.BasicErrorController; +import org.springframework.boot.webmvc.error.ErrorAttributes; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -26,8 +26,8 @@ public class ServerErrorController extends BasicErrorController { @Value("${ovsx.webui.url:}") String webuiUrl; - public ServerErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties) { - super(errorAttributes, serverProperties.getError()); + public ServerErrorController(ErrorAttributes errorAttributes, WebProperties webProperties) { + super(errorAttributes, webProperties.getError()); } @RequestMapping(value = "/server-error", produces = {MediaType.TEXT_HTML_VALUE}) diff --git a/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java b/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java index ca6e12f0f..2c4c3c9fa 100644 --- a/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java +++ b/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java @@ -17,7 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.resttestclient.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpStatus; import org.springframework.test.context.ActiveProfiles; diff --git a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java index dc16be628..36b5f395b 100644 --- a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java @@ -51,8 +51,7 @@ import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @@ -91,7 +90,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @WebMvcTest(RegistryAPI.class) -@AutoConfigureWebClient @MockitoBean(types = { ClientRegistrationRepository.class, UpstreamRegistryService.class, GoogleCloudStorageService.class, AzureBlobStorageService.class, AwsStorageService.class, VSCodeIdService.class, DownloadCountService.class, ExtensionDownloadMetrics.class, diff --git a/server/src/test/java/org/eclipse/openvsx/UserAPITest.java b/server/src/test/java/org/eclipse/openvsx/UserAPITest.java index 8934cbb4f..0b390b350 100644 --- a/server/src/test/java/org/eclipse/openvsx/UserAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/UserAPITest.java @@ -45,8 +45,7 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @@ -74,7 +73,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(UserAPI.class) -@AutoConfigureWebClient @MockitoBean(types = { EclipseService.class, ClientRegistrationRepository.class, StorageUtilService.class, CacheService.class, ExtensionValidator.class, SimpleMeterRegistry.class, SearchUtilService.class, PublishExtensionVersionHandler.class, diff --git a/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java b/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java index c34366321..f83b040e0 100644 --- a/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java @@ -38,8 +38,7 @@ import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @@ -70,7 +69,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @WebMvcTest(VSCodeAPI.class) -@AutoConfigureWebClient @MockitoBean( types = { ClientRegistrationRepository.class, GoogleCloudStorageService.class, AzureBlobStorageService.class, AwsStorageService.class, DownloadCountService.class, ExtensionDownloadMetrics.class, CacheService.class, UpstreamVSCodeService.class, diff --git a/server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java b/server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java index 0529bdc80..d4d5e0fba 100644 --- a/server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java @@ -93,8 +93,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @@ -115,7 +114,6 @@ import jakarta.persistence.EntityManager; @WebMvcTest(AdminAPI.class) -@AutoConfigureWebClient @MockitoBean(types = { ClientRegistrationRepository.class, UpstreamRegistryService.class, GoogleCloudStorageService.class, AzureBlobStorageService.class, AwsStorageService.class, VSCodeIdService.class, DownloadCountService.class, diff --git a/server/src/test/java/org/eclipse/openvsx/admin/FileDecisionAPITest.java b/server/src/test/java/org/eclipse/openvsx/admin/FileDecisionAPITest.java index ae00729b7..c84c9493c 100644 --- a/server/src/test/java/org/eclipse/openvsx/admin/FileDecisionAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/admin/FileDecisionAPITest.java @@ -16,8 +16,8 @@ import org.eclipse.openvsx.repositories.RepositoryService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.http.MediaType; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; diff --git a/server/src/test/java/org/eclipse/openvsx/admin/ScanAPITest.java b/server/src/test/java/org/eclipse/openvsx/admin/ScanAPITest.java index a0332e6a6..f3573d12b 100644 --- a/server/src/test/java/org/eclipse/openvsx/admin/ScanAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/admin/ScanAPITest.java @@ -24,8 +24,8 @@ import org.eclipse.openvsx.util.LogService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.util.Streamable; diff --git a/server/src/test/java/org/eclipse/openvsx/ratelimit/RateLimitIntegrationTest.java b/server/src/test/java/org/eclipse/openvsx/ratelimit/RateLimitIntegrationTest.java index 10d457520..5e320e5e4 100644 --- a/server/src/test/java/org/eclipse/openvsx/ratelimit/RateLimitIntegrationTest.java +++ b/server/src/test/java/org/eclipse/openvsx/ratelimit/RateLimitIntegrationTest.java @@ -21,7 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.resttestclient.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.test.context.bean.override.mockito.MockitoBean; import redis.clients.jedis.RedisClusterClient; diff --git a/server/src/test/java/org/eclipse/openvsx/scanning/ResponseExtractorTest.java b/server/src/test/java/org/eclipse/openvsx/scanning/ResponseExtractorTest.java index 4dd7771ea..95e924c1a 100644 --- a/server/src/test/java/org/eclipse/openvsx/scanning/ResponseExtractorTest.java +++ b/server/src/test/java/org/eclipse/openvsx/scanning/ResponseExtractorTest.java @@ -12,9 +12,9 @@ ********************************************************************************/ package org.eclipse.openvsx.scanning; -import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import tools.jackson.databind.ObjectMapper; import java.util.Map; diff --git a/server/src/test/java/org/eclipse/openvsx/web/SitemapControllerTest.java b/server/src/test/java/org/eclipse/openvsx/web/SitemapControllerTest.java index a46dda194..a4868893c 100644 --- a/server/src/test/java/org/eclipse/openvsx/web/SitemapControllerTest.java +++ b/server/src/test/java/org/eclipse/openvsx/web/SitemapControllerTest.java @@ -21,8 +21,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @@ -36,7 +35,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(SitemapController.class) -@AutoConfigureWebClient @MockitoBean(types = { EclipseService.class, SimpleMeterRegistry.class, UserService.class, EclipseTokenService.class, EntityManager.class }) From d3c5158b5233d2f20779d47b328639466e6049dc Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Fri, 26 Jun 2026 11:14:28 +0200 Subject: [PATCH 02/13] fix import for jackson 3 --- .../scanning/ExtensionScanPersistenceServiceTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/eclipse/openvsx/scanning/ExtensionScanPersistenceServiceTest.java b/server/src/test/java/org/eclipse/openvsx/scanning/ExtensionScanPersistenceServiceTest.java index fccd44932..f637f48bb 100644 --- a/server/src/test/java/org/eclipse/openvsx/scanning/ExtensionScanPersistenceServiceTest.java +++ b/server/src/test/java/org/eclipse/openvsx/scanning/ExtensionScanPersistenceServiceTest.java @@ -12,7 +12,6 @@ ********************************************************************************/ package org.eclipse.openvsx.scanning; -import com.fasterxml.jackson.databind.ObjectMapper; import org.eclipse.openvsx.entities.ExtensionScan; import org.eclipse.openvsx.entities.ScanStatus; import org.eclipse.openvsx.entities.ScannerJob; @@ -25,6 +24,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import tools.jackson.databind.ObjectMapper; import java.time.LocalDateTime; @@ -35,7 +35,8 @@ class ExtensionScanPersistenceServiceTest { @Mock RepositoryService repositories; - @Mock ObjectMapper objectMapper; + @Mock + ObjectMapper objectMapper; @Mock FileDecisionRepository fileDecisionRepository; @Mock ScannerJobRepository scannerJobRepository; @Mock ScanCheckResultRepository scanCheckResultRepository; From a9abf132ccebe8f7a48f25d2ce0b81601881af4d Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Fri, 26 Jun 2026 11:14:58 +0200 Subject: [PATCH 03/13] replace explicit spring-retry with builint spring boot features --- server/build.gradle | 9 +-------- server/gradle/libs.versions.toml | 11 +---------- .../eclipse/openvsx/RegistryApplication.java | 4 ++-- .../openvsx/migration/MigrationService.java | 2 +- .../PublishExtensionVersionHandler.java | 2 +- .../PublishExtensionVersionService.java | 2 +- .../openvsx/search/ElasticSearchService.java | 18 +++++++++--------- 7 files changed, 16 insertions(+), 32 deletions(-) diff --git a/server/build.gradle b/server/build.gradle index 6535ce63c..615ec8070 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -104,14 +104,7 @@ dependencies { implementation libs.awssdk.sts implementation libs.springdoc - implementation libs.spring.retry - -// implementation libs.jackson.core -// implementation libs.jackson.annotations -// implementation libs.jackson.databind -// implementation libs.jackson.jaxb.annotations -// implementation libs.jackson.dataformat.xml -// implementation libs.jackson.dataformat.yaml + implementation libs.jackson.dataformat.toml implementation libs.woodstox.core diff --git a/server/gradle/libs.versions.toml b/server/gradle/libs.versions.toml index 216a65795..408e452d5 100644 --- a/server/gradle/libs.versions.toml +++ b/server/gradle/libs.versions.toml @@ -9,7 +9,6 @@ flyway = "11.20.3" gatling = "3.15.0" gcloud = "2.62.1" ipaddress = "5.5.1" -jackson = "3.1.4" java = "25" jaxb-api = "2.3.1" jaxb-impl = "2.3.8" @@ -21,7 +20,6 @@ loki4j = "1.4.2" re2j = "1.7" rewrite-java = "3.26.0" spring-boot = "4.0.7" -spring-retry = "2.0.13" springdoc = "2.8.13" tika = "3.3.1" woodstox = "6.4.0" @@ -50,13 +48,7 @@ gatling-app = { module = "io.gatling:gatling-app", version.ref gatling-core = { module = "io.gatling:gatling-core", version.ref = "gatling" } google-cloud-storage = { module = "com.google.cloud:google-cloud-storage", version.ref = "gcloud" } ipaddress = { module = "com.github.seancfoley:ipaddress", version.ref = "ipaddress" } -jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations", version.ref = "jackson" } -jackson-core = { module = "com.fasterxml.jackson.core:jackson-core", version.ref = "jackson" } -jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" } -jackson-dataformat-toml = { module = "tools.jackson.dataformat:jackson-dataformat-toml", version.ref = "jackson" } -jackson-dataformat-xml = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-xml", version.ref = "jackson" } -jackson-dataformat-yaml = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml", version.ref = "jackson" } -jackson-jaxb-annotations = { module = "com.fasterxml.jackson.module:jackson-module-jaxb-annotations", version.ref = "jackson" } +jackson-dataformat-toml = { module = "tools.jackson.dataformat:jackson-dataformat-toml" } jaxb-api = { module = "javax.xml.bind:jaxb-api", version.ref = "jaxb-api" } jaxb-impl = { module = "com.sun.xml.bind:jaxb-impl", version.ref = "jaxb-impl" } jedis = { module = "redis.clients:jedis", version.ref = "jedis" } @@ -66,7 +58,6 @@ loki-logback-appender = { module = "com.github.loki4j:loki-logback-append re2j = { module = "com.google.re2j:re2j", version.ref = "re2j" } rewrite-java = { module = "org.openrewrite.recipe:rewrite-migrate-java", version.ref = "rewrite-java"} springdoc = { module = "org.springdoc:springdoc-openapi-starter-webmvc-ui", version.ref = "springdoc" } -spring-retry = { module = "org.springframework.retry:spring-retry", version.ref = "spring-retry" } testcontainers-elasticsearch = { module = "org.testcontainers:testcontainers-elasticsearch" } testcontainers-junit-jupiter = { module = "org.testcontainers:testcontainers-junit-jupiter" } testcontainers-localstack = { module = "org.testcontainers:testcontainers-localstack" } diff --git a/server/src/main/java/org/eclipse/openvsx/RegistryApplication.java b/server/src/main/java/org/eclipse/openvsx/RegistryApplication.java index 56cd187b9..d4919bd98 100644 --- a/server/src/main/java/org/eclipse/openvsx/RegistryApplication.java +++ b/server/src/main/java/org/eclipse/openvsx/RegistryApplication.java @@ -26,7 +26,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.core.Ordered; import org.springframework.data.web.config.EnableSpringDataWebSupport; -import org.springframework.retry.annotation.EnableRetry; +import org.springframework.resilience.annotation.EnableResilientMethods; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; @@ -46,7 +46,7 @@ DataRedisRepositoriesAutoConfiguration.class, }) @EnableScheduling -@EnableRetry +@EnableResilientMethods @EnableAsync @EnableConfigurationProperties(OAuth2AttributesConfig.class) // Need to enable serialization support for spring data's Page, see: diff --git a/server/src/main/java/org/eclipse/openvsx/migration/MigrationService.java b/server/src/main/java/org/eclipse/openvsx/migration/MigrationService.java index 3c9c4c6ee..fdedb7b81 100644 --- a/server/src/main/java/org/eclipse/openvsx/migration/MigrationService.java +++ b/server/src/main/java/org/eclipse/openvsx/migration/MigrationService.java @@ -22,7 +22,7 @@ import org.jobrunr.scheduling.JobRequestScheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.retry.annotation.Retryable; +import org.springframework.resilience.annotation.Retryable; import org.springframework.stereotype.Component; import java.io.IOException; diff --git a/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandler.java b/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandler.java index 14dd66e69..e0e90bc32 100644 --- a/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandler.java +++ b/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandler.java @@ -40,7 +40,7 @@ import org.jobrunr.scheduling.JobRequestScheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.retry.annotation.Retryable; +import org.springframework.resilience.annotation.Retryable; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerErrorException; diff --git a/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionService.java b/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionService.java index 7f062c061..63c59f8cf 100644 --- a/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionService.java +++ b/server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionService.java @@ -18,7 +18,7 @@ import org.eclipse.openvsx.storage.StorageUtilService; import org.eclipse.openvsx.util.TempFile; import org.springframework.cache.annotation.CacheEvict; -import org.springframework.retry.annotation.Retryable; +import org.springframework.resilience.annotation.Retryable; import org.springframework.stereotype.Component; import static org.eclipse.openvsx.cache.CacheService.CACHE_SITEMAP; diff --git a/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java b/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java index a0eb03eed..d9c81e219 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java @@ -42,7 +42,7 @@ import org.springframework.data.elasticsearch.core.query.DeleteQuery; import org.springframework.data.elasticsearch.core.query.IndexQuery; import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder; -import org.springframework.retry.annotation.Retryable; +import org.springframework.resilience.annotation.Retryable; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.util.StopWatch; @@ -96,7 +96,7 @@ public boolean isEnabled() { * not exist yet, it is created and initialized. Otherwise, nothing happens. */ @EventListener - @Retryable(retryFor = DataAccessResourceFailureException.class) + @Retryable(includes = DataAccessResourceFailureException.class) @CacheEvict(value = CACHE_AVERAGE_REVIEW_RATING, allEntries = true) public void initSearchIndex(ApplicationStartedEvent event) { if (!isEnabled()) { @@ -129,7 +129,7 @@ public void initSearchIndex(ApplicationStartedEvent event) { * consider the extension publishing timestamps in relation to the current * time or the extension rating. */ - @Retryable(retryFor = DataAccessResourceFailureException.class) + @Retryable(includes = DataAccessResourceFailureException.class) @CacheEvict(value = CACHE_AVERAGE_REVIEW_RATING, allEntries = true) public void updateSearchIndex() { if (!isEnabled()) { @@ -151,7 +151,7 @@ public void updateSearchIndex() { * In any case, this method scans all extensions in the database and indexes their * relevant metadata. */ - @Retryable(retryFor = DataAccessResourceFailureException.class) + @Retryable(includes = DataAccessResourceFailureException.class) public void updateSearchIndex(boolean clear) { var locked = false; try { @@ -196,12 +196,12 @@ public void updateSearchIndex(boolean clear) { } @Async - @Retryable(retryFor = DataAccessResourceFailureException.class) + @Retryable(includes = DataAccessResourceFailureException.class) public void updateSearchEntriesAsync(List extensions) { updateSearchEntries(extensions); } - @Retryable(retryFor = DataAccessResourceFailureException.class) + @Retryable(includes = DataAccessResourceFailureException.class) public void updateSearchEntries(List extensions) { if (!isEnabled() || extensions.isEmpty()) { return; @@ -220,7 +220,7 @@ public void updateSearchEntries(List extensions) { } } - @Retryable(retryFor = DataAccessResourceFailureException.class) + @Retryable(includes = DataAccessResourceFailureException.class) public void updateSearchEntry(Extension extension) { if (!isEnabled()) { return; @@ -251,7 +251,7 @@ public void updateSearchEntry(Extension extension) { } } - @Retryable(retryFor = DataAccessResourceFailureException.class) + @Retryable(includes = DataAccessResourceFailureException.class) public void removeSearchEntries(Collection ids) { if (!isEnabled()) { return; @@ -263,7 +263,7 @@ public void removeSearchEntries(Collection ids) { } - @Retryable(retryFor = DataAccessResourceFailureException.class) + @Retryable(includes = DataAccessResourceFailureException.class) public void removeSearchEntry(Extension extension) { if (!isEnabled()) { return; From e5b129fe2a30ddf96d921dde5ec453330806deb8 Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Fri, 26 Jun 2026 11:19:21 +0200 Subject: [PATCH 04/13] remove unnecessary workaround --- server/build.gradle | 7 ------- 1 file changed, 7 deletions(-) diff --git a/server/build.gradle b/server/build.gradle index 615ec8070..b88f3a5de 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -46,13 +46,6 @@ sourceSets { configurations { devImplementation.extendsFrom implementation devRuntimeOnly.extendsFrom runtimeOnly - - // exclude commons-logging to avoid runtime warnings like these - // Standard Commons Logging discovery in action with spring-jcl: - // please remove commons-logging.jar from classpath in order to avoid potential conflicts -// configureEach { -// exclude group: "commons-logging", module: "commons-logging" -// } } dependencies { From 7015334ce8bca0478e1ab90fbfca5aabdbf353ac Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Fri, 26 Jun 2026 13:28:00 +0200 Subject: [PATCH 05/13] fix tests --- server/build.gradle | 10 +++++++--- server/gradle/libs.versions.toml | 1 + .../openvsx/adapter/WebResourceService.java | 15 ++------------- .../openvsx/scanning/HttpClientExecutor.java | 13 +++++++++---- .../openvsx/storage/StorageUtilService.java | 6 +++--- .../storage/log/AzureDownloadCountHandler.java | 12 ++++++------ .../java/org/eclipse/openvsx/IntegrationTest.java | 11 +++++++++++ .../eclipse/openvsx/cache/CacheServiceTest.java | 9 +++++++++ .../ratelimit/RateLimitIntegrationTest.java | 11 +++++++++++ .../repositories/RepositoryServiceSmokeTest.java | 9 +++++++++ server/src/test/resources/application.yml | 6 ------ 11 files changed, 68 insertions(+), 35 deletions(-) diff --git a/server/build.gradle b/server/build.gradle index b88f3a5de..c68e045ad 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -23,6 +23,7 @@ def jooqSrcDir = 'src/main/jooq-gen' // ext['netty.version'] = '4.1.132.Final' ext['opentelemetry.version'] = '1.63.0' +ext['flyway.version'] = libs.versions.flyway java { sourceCompatibility = libs.versions.java.get() @@ -56,6 +57,7 @@ dependencies { replacedBy("org.springframework.boot:spring-boot-starter-jetty") } } + implementation "org.springframework.boot:spring-boot-starter-restclient" implementation "org.springframework.boot:spring-boot-starter-validation" implementation "org.springframework.boot:spring-boot-starter-jooq" implementation "org.springframework.boot:spring-boot-starter-data-jpa" @@ -65,10 +67,10 @@ dependencies { implementation "org.springframework.boot:spring-boot-starter-actuator" implementation "org.springframework.boot:spring-boot-starter-cache" implementation "org.springframework.boot:spring-boot-starter-mail" - implementation "org.springframework.boot:spring-boot-starter-thymeleaf" implementation "org.springframework.security:spring-security-oauth2-client" implementation "org.springframework.security:spring-security-oauth2-jose" implementation "org.springframework.session:spring-session-jdbc" + implementation "org.springframework.boot:spring-boot-thymeleaf" implementation "org.apache.httpcomponents.client5:httpclient5" implementation "com.github.ben-manes.caffeine:caffeine" @@ -88,6 +90,7 @@ dependencies { implementation libs.jobrunr.spring + implementation "org.springframework.boot:spring-boot-flyway" implementation libs.flyway.core implementation libs.flyway.database.postgresql @@ -98,6 +101,7 @@ dependencies { implementation libs.springdoc + implementation libs.jackson.dataformat.xml implementation libs.jackson.dataformat.toml implementation libs.woodstox.core @@ -125,11 +129,11 @@ dependencies { testImplementation "org.springframework.boot:spring-boot-starter-webmvc-test" testImplementation "org.springframework.security:spring-security-test" + testImplementation "org.springframework.boot:spring-boot-testcontainers" testImplementation libs.testcontainers.elasticsearch testImplementation libs.testcontainers.localstack testImplementation libs.testcontainers.junit.jupiter - - testRuntimeOnly libs.testcontainers.postgresql + testImplementation libs.testcontainers.postgresql gatling libs.gatling.core gatling libs.gatling.app diff --git a/server/gradle/libs.versions.toml b/server/gradle/libs.versions.toml index 408e452d5..9bb25f1de 100644 --- a/server/gradle/libs.versions.toml +++ b/server/gradle/libs.versions.toml @@ -48,6 +48,7 @@ gatling-app = { module = "io.gatling:gatling-app", version.ref gatling-core = { module = "io.gatling:gatling-core", version.ref = "gatling" } google-cloud-storage = { module = "com.google.cloud:google-cloud-storage", version.ref = "gcloud" } ipaddress = { module = "com.github.seancfoley:ipaddress", version.ref = "ipaddress" } +jackson-dataformat-xml = { module = "tools.jackson.dataformat:jackson-dataformat-xml" } jackson-dataformat-toml = { module = "tools.jackson.dataformat:jackson-dataformat-toml" } jaxb-api = { module = "javax.xml.bind:jaxb-api", version.ref = "jaxb-api" } jaxb-impl = { module = "com.sun.xml.bind:jaxb-impl", version.ref = "jaxb-impl" } diff --git a/server/src/main/java/org/eclipse/openvsx/adapter/WebResourceService.java b/server/src/main/java/org/eclipse/openvsx/adapter/WebResourceService.java index 68ae89892..b77a2b4b0 100644 --- a/server/src/main/java/org/eclipse/openvsx/adapter/WebResourceService.java +++ b/server/src/main/java/org/eclipse/openvsx/adapter/WebResourceService.java @@ -9,9 +9,6 @@ * ****************************************************************************** */ package org.eclipse.openvsx.adapter; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; import io.micrometer.observation.annotation.Observed; import org.eclipse.openvsx.cache.CacheService; import org.eclipse.openvsx.cache.FilesCacheKeyGenerator; @@ -27,6 +24,8 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.node.ArrayNode; import java.io.IOException; import java.io.UncheckedIOException; @@ -141,16 +140,6 @@ private void writeBinaryFile(Path file, ZipFile zip, ZipEntry fileEntry) { }); } - private void writeJsonFile(Path file, ObjectMapper mapper, JsonNode node) { - FileUtil.writeSync(file, p -> { - try { - mapper.writeValue(p.toFile(), node); - } catch(IOException e) { - throw new UncheckedIOException(e); - } - }); - } - private String getDirectoryName(String name) { return name.isEmpty() || name.endsWith("/") ? name : name + "/"; } diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java b/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java index 494e5ebae..3e7dd1472 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java @@ -21,6 +21,7 @@ import org.eclipse.openvsx.util.TempFile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.boot.restclient.RestTemplateBuilder; import org.springframework.core.io.FileSystemResource; import org.springframework.http.*; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; @@ -121,10 +122,14 @@ private static RestTemplate createRestTemplate(RemoteScannerProperties.HttpConfi .setConnectionManager(connectionManager) .setDefaultRequestConfig(requestConfig) .build(); - - var factory = new HttpComponentsClientHttpRequestFactory(); - factory.setHttpClient(httpClient); - return new RestTemplate(factory); + + return new RestTemplateBuilder() + .requestFactory(() -> { + HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); + factory.setHttpClient(httpClient); + return factory; + }) + .build(); } /** diff --git a/server/src/main/java/org/eclipse/openvsx/storage/StorageUtilService.java b/server/src/main/java/org/eclipse/openvsx/storage/StorageUtilService.java index e1347c157..a3de469a6 100644 --- a/server/src/main/java/org/eclipse/openvsx/storage/StorageUtilService.java +++ b/server/src/main/java/org/eclipse/openvsx/storage/StorageUtilService.java @@ -9,8 +9,6 @@ ********************************************************************************/ package org.eclipse.openvsx.storage; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.google.common.collect.Maps; import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; @@ -31,6 +29,8 @@ import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.node.ArrayNode; import java.io.IOException; import java.net.URI; @@ -332,7 +332,7 @@ public ResponseEntity getFileResponse(ArrayNode node) { var mapper = new ObjectMapper(); var value = mapper.createArrayNode(); for (var item : node) { - value.add(baseUrl + item.asText()); + value.add(baseUrl + item.asString()); } mapper.writeValue(outputStream, value); }); diff --git a/server/src/main/java/org/eclipse/openvsx/storage/log/AzureDownloadCountHandler.java b/server/src/main/java/org/eclipse/openvsx/storage/log/AzureDownloadCountHandler.java index afd6b923c..9e76df823 100644 --- a/server/src/main/java/org/eclipse/openvsx/storage/log/AzureDownloadCountHandler.java +++ b/server/src/main/java/org/eclipse/openvsx/storage/log/AzureDownloadCountHandler.java @@ -17,8 +17,6 @@ import com.azure.storage.blob.models.BlobListDetails; import com.azure.storage.blob.models.BlobStorageException; import com.azure.storage.blob.models.ListBlobsOptions; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; import org.eclipse.openvsx.entities.FileResource; import org.eclipse.openvsx.migration.HandlerJobRequest; @@ -33,6 +31,8 @@ import org.springframework.stereotype.Component; import org.springframework.util.StopWatch; import org.springframework.web.util.UriUtils; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; import java.io.IOException; import java.net.URI; @@ -213,7 +213,7 @@ private Map processBlobItem(String blobName) throws IOException var node = getObjectMapper().readTree(line); String[] pathParams = null; if (isGetBlobOperation(node) && isStatusOk(node) && isExtensionPackageUri(node) && isNotOpenVSXUserAgent(node)) { - var uri = node.get("uri").asText(); + var uri = node.get("uri").asString(); pathParams = uri.substring(storageServiceEndpoint.length()).split("/"); } if (pathParams != null && storageBlobContainer.equals(pathParams[1])) { @@ -226,7 +226,7 @@ private Map processBlobItem(String blobName) throws IOException } private boolean isGetBlobOperation(JsonNode node) { - return node.get("operationName").asText().equals("GetBlob"); + return node.get("operationName").asString().equals("GetBlob"); } private boolean isStatusOk(JsonNode node) { @@ -234,11 +234,11 @@ private boolean isStatusOk(JsonNode node) { } private boolean isExtensionPackageUri(JsonNode node) { - return node.get("uri").asText().endsWith(".vsix"); + return node.get("uri").asString().endsWith(".vsix"); } private boolean isNotOpenVSXUserAgent(JsonNode node) { - var userAgentHeader = node.path("properties").path("userAgentHeader").asText(); + var userAgentHeader = node.path("properties").path("userAgentHeader").asString(); return !AZURE_USER_AGENT.equals(userAgentHeader); } diff --git a/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java b/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java index 2c4c3c9fa..1f8a12b38 100644 --- a/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java +++ b/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java @@ -18,10 +18,15 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.resttestclient.TestRestTemplate; +import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.http.HttpStatus; import org.springframework.test.context.ActiveProfiles; import org.springframework.web.client.RestTemplate; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.postgresql.PostgreSQLContainer; import java.io.IOException; import java.net.URI; @@ -30,9 +35,15 @@ import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@AutoConfigureTestRestTemplate @ActiveProfiles("test") +@Testcontainers class IntegrationTest { + @Container + @ServiceConnection + static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:16.2"); + protected final Logger logger = LoggerFactory.getLogger(IntegrationTest.class); @LocalServerPort diff --git a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java index bc1f4866f..259b4645b 100644 --- a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java +++ b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.cache.CacheManager; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.security.authentication.TestingAuthenticationToken; @@ -34,6 +35,9 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.postgresql.PostgreSQLContainer; import java.io.IOException; import java.nio.file.Files; @@ -48,8 +52,13 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") +@Testcontainers class CacheServiceTest { + @Container + @ServiceConnection + static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:16.2"); + @Autowired CacheManager cache; diff --git a/server/src/test/java/org/eclipse/openvsx/ratelimit/RateLimitIntegrationTest.java b/server/src/test/java/org/eclipse/openvsx/ratelimit/RateLimitIntegrationTest.java index 5e320e5e4..a0514e089 100644 --- a/server/src/test/java/org/eclipse/openvsx/ratelimit/RateLimitIntegrationTest.java +++ b/server/src/test/java/org/eclipse/openvsx/ratelimit/RateLimitIntegrationTest.java @@ -22,8 +22,13 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.resttestclient.TestRestTemplate; +import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.postgresql.PostgreSQLContainer; import redis.clients.jedis.RedisClusterClient; import java.net.URI; @@ -37,8 +42,14 @@ "ovsx.rate-limit.filters[0].url=/(api|vscode)/.*", "ovsx.elasticsearch.enabled=false" }) +@AutoConfigureTestRestTemplate +@Testcontainers class RateLimitIntegrationTest { + @Container + @ServiceConnection + static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:16.2"); + @LocalServerPort int port; diff --git a/server/src/test/java/org/eclipse/openvsx/repositories/RepositoryServiceSmokeTest.java b/server/src/test/java/org/eclipse/openvsx/repositories/RepositoryServiceSmokeTest.java index 7246a41d3..656d503f2 100644 --- a/server/src/test/java/org/eclipse/openvsx/repositories/RepositoryServiceSmokeTest.java +++ b/server/src/test/java/org/eclipse/openvsx/repositories/RepositoryServiceSmokeTest.java @@ -45,9 +45,13 @@ import org.mockito.invocation.Invocation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.test.context.ActiveProfiles; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.postgresql.PostgreSQLContainer; import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; @@ -58,8 +62,13 @@ */ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") +@Testcontainers class RepositoryServiceSmokeTest { + @Container + @ServiceConnection + static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:16.2"); + private static final List STRING_LIST = List.of("id1", "id2"); private static final List LONG_LIST = List.of(1L, 2L); diff --git a/server/src/test/resources/application.yml b/server/src/test/resources/application.yml index 1cfd54e39..617cde02d 100644 --- a/server/src/test/resources/application.yml +++ b/server/src/test/resources/application.yml @@ -1,7 +1,4 @@ spring: - datasource: - driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver - url: jdbc:tc:postgresql:16.2:///test jpa: properties: hibernate: @@ -12,9 +9,6 @@ spring: store-type: jdbc jdbc: initialize-schema: never - thymeleaf: - # explicitly disable thymeleaf view resolution - enabled: false security: oauth2: From 3dcf42d46724f3da129356aa5e16d4ac67d521ba Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Fri, 26 Jun 2026 13:31:07 +0200 Subject: [PATCH 06/13] more fixes --- server/build.gradle | 1 + server/src/dev/resources/application.yml | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/server/build.gradle b/server/build.gradle index c68e045ad..1f4cc1e89 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -67,6 +67,7 @@ dependencies { implementation "org.springframework.boot:spring-boot-starter-actuator" implementation "org.springframework.boot:spring-boot-starter-cache" implementation "org.springframework.boot:spring-boot-starter-mail" + implementation "org.springframework.boot:spring-boot-starter-zipkin" implementation "org.springframework.security:spring-security-oauth2-client" implementation "org.springframework.security:spring-security-oauth2-jose" implementation "org.springframework.session:spring-session-jdbc" diff --git a/server/src/dev/resources/application.yml b/server/src/dev/resources/application.yml index a734e2ac5..a09031e6f 100644 --- a/server/src/dev/resources/application.yml +++ b/server/src/dev/resources/application.yml @@ -13,7 +13,7 @@ spring: name: openvsx-server autoconfigure: # don't send traces to Zipkin in development - exclude: org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinAutoConfiguration + exclude: org.springframework.boot.zipkin.autoconfigure.ZipkinAutoConfiguration profiles: include: ovsx # connect to redis cluster configured in docker-compose.yml @@ -39,9 +39,6 @@ spring: store-type: jdbc jdbc: initialize-schema: never - thymeleaf: - # explicitly disable thymeleaf view resolution - enabled: false # mail: # host: "localhost" From bef9e1ebf0897ecd0a2b6a0c0e66794f6e914174 Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Fri, 26 Jun 2026 13:42:20 +0200 Subject: [PATCH 07/13] revert to keep RestTemplateBuilder --- .../eclipse/openvsx/RestTemplateConfig.java | 104 +++++++++++------- 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/server/src/main/java/org/eclipse/openvsx/RestTemplateConfig.java b/server/src/main/java/org/eclipse/openvsx/RestTemplateConfig.java index 34046b364..e6356119d 100644 --- a/server/src/main/java/org/eclipse/openvsx/RestTemplateConfig.java +++ b/server/src/main/java/org/eclipse/openvsx/RestTemplateConfig.java @@ -9,12 +9,12 @@ * ****************************************************************************** */ package org.eclipse.openvsx; -import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.core5.util.Timeout; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.restclient.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; @@ -23,7 +23,6 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.util.DefaultUriBuilderFactory; -import java.util.List; import java.util.concurrent.TimeUnit; @Configuration @@ -60,66 +59,75 @@ public HttpConnPoolConfig backgroundHttpConnPool( } private HttpConnPoolConfig createHttpConnPoolConfig(int maxTotal, int defaultMaxPerRoute, int connectionRequestTimeout, int connectTimeout, int socketTimeout) { - var connectionConfig = ConnectionConfig.custom() - .setConnectTimeout(Timeout.of(connectTimeout, TimeUnit.MILLISECONDS)) - .setSocketTimeout(Timeout.of(socketTimeout, TimeUnit.MILLISECONDS)) - .build(); - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); connectionManager.setMaxTotal(maxTotal); connectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute); - connectionManager.setDefaultConnectionConfig(connectionConfig); return new HttpConnPoolConfig( connectionManager, - connectionRequestTimeout + connectionRequestTimeout, + connectTimeout, + socketTimeout ); } @Bean - public RestTemplate restTemplate(HttpConnPoolConfig foregroundHttpConnPool) { + public RestTemplate restTemplate(RestTemplateBuilder builder, HttpConnPoolConfig foregroundHttpConnPool) { var httpClient = createHttpClientBuilder(foregroundHttpConnPool).build(); - var factory = new HttpComponentsClientHttpRequestFactory(); - factory.setHttpClient(httpClient); - var restTemplate = new RestTemplate(factory); - restTemplate.setMessageConverters(List.of( - new StringHttpMessageConverter(), - new JacksonJsonHttpMessageConverter())); - return restTemplate; + return builder + .requestFactory(() -> { + HttpComponentsClientHttpRequestFactory f = new HttpComponentsClientHttpRequestFactory(); + f.setHttpClient(httpClient); + return f; + }) + .messageConverters( + new StringHttpMessageConverter(), + new JacksonJsonHttpMessageConverter()) + .build(); } @Bean - public RestTemplate nonRedirectingRestTemplate(HttpConnPoolConfig foregroundHttpConnPool) { + public RestTemplate nonRedirectingRestTemplate(RestTemplateBuilder builder, HttpConnPoolConfig foregroundHttpConnPool) { var httpClient = createHttpClientBuilder(foregroundHttpConnPool).disableRedirectHandling().build(); - var factory = new HttpComponentsClientHttpRequestFactory(); - factory.setHttpClient(httpClient); - return new RestTemplate(factory); + return builder + .requestFactory(() -> { + HttpComponentsClientHttpRequestFactory f = new HttpComponentsClientHttpRequestFactory(); + f.setHttpClient(httpClient); + return f; + }) + .build(); } @Bean - public RestTemplate backgroundRestTemplate(HttpConnPoolConfig backgroundHttpConnPool) { + public RestTemplate backgroundRestTemplate(RestTemplateBuilder builder, HttpConnPoolConfig backgroundHttpConnPool) { var httpClient = createHttpClientBuilder(backgroundHttpConnPool).build(); - var factory = new HttpComponentsClientHttpRequestFactory(); - factory.setHttpClient(httpClient); - var restTemplate = new RestTemplate(factory); - var defaultUriBuilderFactory = new DefaultUriBuilderFactory(); + DefaultUriBuilderFactory defaultUriBuilderFactory = new DefaultUriBuilderFactory(); defaultUriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE); - restTemplate.setUriTemplateHandler(defaultUriBuilderFactory); - restTemplate.setMessageConverters(List.of( - new StringHttpMessageConverter(), - new JacksonJsonHttpMessageConverter())); - return restTemplate; + return builder + .uriTemplateHandler(defaultUriBuilderFactory) + .messageConverters( + new StringHttpMessageConverter(), + new JacksonJsonHttpMessageConverter()) + .requestFactory(() -> { + HttpComponentsClientHttpRequestFactory f = new HttpComponentsClientHttpRequestFactory(); + f.setHttpClient(httpClient); + return f; + }) + .build(); } @Bean - public RestTemplate backgroundNonRedirectingRestTemplate(HttpConnPoolConfig backgroundHttpConnPool) { + public RestTemplate backgroundNonRedirectingRestTemplate(RestTemplateBuilder builder, HttpConnPoolConfig backgroundHttpConnPool) { var httpClient = createHttpClientBuilder(backgroundHttpConnPool).disableRedirectHandling().build(); - var factory = new HttpComponentsClientHttpRequestFactory(); - factory.setHttpClient(httpClient); - var restTemplate = new RestTemplate(factory); - var defaultUriBuilderFactory = new DefaultUriBuilderFactory(); + DefaultUriBuilderFactory defaultUriBuilderFactory = new DefaultUriBuilderFactory(); defaultUriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE); - restTemplate.setUriTemplateHandler(defaultUriBuilderFactory); - return restTemplate; + return builder + .uriTemplateHandler(defaultUriBuilderFactory) + .requestFactory(() -> { + HttpComponentsClientHttpRequestFactory f = new HttpComponentsClientHttpRequestFactory(); + f.setHttpClient(httpClient); + return f; + }) + .build(); } @Bean @@ -134,6 +142,7 @@ public RestTemplate vsCodeIdRestTemplate( private HttpClientBuilder createHttpClientBuilder(HttpConnPoolConfig httpConnPoolConfig) { var requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(Timeout.of(httpConnPoolConfig.getConnectionRequestTimeout(), TimeUnit.MILLISECONDS)) + .setConnectTimeout(Timeout.of(httpConnPoolConfig.getConnectTimeout(), TimeUnit.MILLISECONDS)) .build(); return HttpClientBuilder .create() @@ -145,10 +154,15 @@ public static class HttpConnPoolConfig { private final PoolingHttpClientConnectionManager connectionManager; private final int connectionRequestTimeout; + private final int connectTimeout; + private final int socketTimeout; - public HttpConnPoolConfig(PoolingHttpClientConnectionManager connectionManager, int connectionRequestTimeout) { + public HttpConnPoolConfig(PoolingHttpClientConnectionManager connectionManager, int connectionRequestTimeout, + int connectTimeout, int socketTimeout) { this.connectionManager = connectionManager; this.connectionRequestTimeout = connectionRequestTimeout; + this.connectTimeout = connectTimeout; + this.socketTimeout = socketTimeout; } public PoolingHttpClientConnectionManager getConnectionManager() { @@ -160,5 +174,17 @@ public PoolingHttpClientConnectionManager getConnectionManager() { public int getConnectionRequestTimeout() { return connectionRequestTimeout; } + /** + * the time to establish the connection with the remote host + */ + public int getConnectTimeout() { + return connectTimeout; + } + /** + * the time waiting for data – after establishing the connection; maximum time of inactivity between two data packets + */ + public int getSocketTimeout() { + return socketTimeout; + } } } From c082ac22d1370446bdfa345df987cc63c97e021b Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Fri, 26 Jun 2026 14:19:35 +0200 Subject: [PATCH 08/13] fix deprecation --- .../eclipse/openvsx/RestTemplateConfig.java | 54 +++++-------------- .../eclipse/openvsx/TestDatabaseConfig.java | 32 +++++++++++ 2 files changed, 45 insertions(+), 41 deletions(-) create mode 100644 server/src/test/java/org/eclipse/openvsx/TestDatabaseConfig.java diff --git a/server/src/main/java/org/eclipse/openvsx/RestTemplateConfig.java b/server/src/main/java/org/eclipse/openvsx/RestTemplateConfig.java index e6356119d..107994f2f 100644 --- a/server/src/main/java/org/eclipse/openvsx/RestTemplateConfig.java +++ b/server/src/main/java/org/eclipse/openvsx/RestTemplateConfig.java @@ -9,6 +9,7 @@ * ****************************************************************************** */ package org.eclipse.openvsx; +import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; @@ -30,7 +31,7 @@ public class RestTemplateConfig { /** * Use to serve requests to ensure that response is given within 30 seconds. - * VS Code does not wait more than it and will timeout a request. + * VS Code does not wait more than it and will time out a request. */ @Bean public HttpConnPoolConfig foregroundHttpConnPool( @@ -140,51 +141,22 @@ public RestTemplate vsCodeIdRestTemplate( } private HttpClientBuilder createHttpClientBuilder(HttpConnPoolConfig httpConnPoolConfig) { + var connectionConfig = ConnectionConfig.custom() + .setConnectTimeout(Timeout.of(httpConnPoolConfig.connectTimeout(), TimeUnit.MILLISECONDS)) + .build(); + httpConnPoolConfig.connectionManager().setDefaultConnectionConfig(connectionConfig); var requestConfig = RequestConfig.custom() - .setConnectionRequestTimeout(Timeout.of(httpConnPoolConfig.getConnectionRequestTimeout(), TimeUnit.MILLISECONDS)) - .setConnectTimeout(Timeout.of(httpConnPoolConfig.getConnectTimeout(), TimeUnit.MILLISECONDS)) + .setConnectionRequestTimeout(Timeout.of(httpConnPoolConfig.connectionRequestTimeout(), TimeUnit.MILLISECONDS)) .build(); return HttpClientBuilder .create() - .setConnectionManager(httpConnPoolConfig.getConnectionManager()) + .setConnectionManager(httpConnPoolConfig.connectionManager()) .setDefaultRequestConfig(requestConfig); } - public static class HttpConnPoolConfig { - - private final PoolingHttpClientConnectionManager connectionManager; - private final int connectionRequestTimeout; - private final int connectTimeout; - private final int socketTimeout; - - public HttpConnPoolConfig(PoolingHttpClientConnectionManager connectionManager, int connectionRequestTimeout, - int connectTimeout, int socketTimeout) { - this.connectionManager = connectionManager; - this.connectionRequestTimeout = connectionRequestTimeout; - this.connectTimeout = connectTimeout; - this.socketTimeout = socketTimeout; - } - - public PoolingHttpClientConnectionManager getConnectionManager() { - return connectionManager; - } - /** - * the time to wait for a connection from the connection manager/pool - */ - public int getConnectionRequestTimeout() { - return connectionRequestTimeout; - } - /** - * the time to establish the connection with the remote host - */ - public int getConnectTimeout() { - return connectTimeout; - } - /** - * the time waiting for data – after establishing the connection; maximum time of inactivity between two data packets - */ - public int getSocketTimeout() { - return socketTimeout; - } - } + public record HttpConnPoolConfig( + PoolingHttpClientConnectionManager connectionManager, + int connectionRequestTimeout, + int connectTimeout, + int socketTimeout) {} } diff --git a/server/src/test/java/org/eclipse/openvsx/TestDatabaseConfig.java b/server/src/test/java/org/eclipse/openvsx/TestDatabaseConfig.java new file mode 100644 index 000000000..26c260043 --- /dev/null +++ b/server/src/test/java/org/eclipse/openvsx/TestDatabaseConfig.java @@ -0,0 +1,32 @@ +/******************************************************************************** + * Copyright (c) 2020 TypeFox and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ +package org.eclipse.openvsx; + +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.testcontainers.elasticsearch.ElasticsearchContainer; +import org.testcontainers.utility.DockerImageName; + +@Configuration +@Profile("test") +public class TestDatabaseConfig { + + @Bean + @ServiceConnection + ElasticsearchContainer elasticsearchContainer() { + return new ElasticsearchContainer( + DockerImageName.parse("elasticsearch:8.17.3") + .asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch")) + .withEnv("discovery.type", "single-node") + .withEnv("xpack.security.enabled", "false"); + } +} From afb014e03ed269a20fb3fbe68ab7a90ff5c5d4ba Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Fri, 26 Jun 2026 14:43:17 +0200 Subject: [PATCH 09/13] fix tests --- .../org/eclipse/openvsx/IntegrationTest.java | 13 ++-------- .../org/eclipse/openvsx/RegistryAPITest.java | 8 +++--- .../eclipse/openvsx/TestDatabaseConfig.java | 26 +++++++++---------- .../org/eclipse/openvsx/TestSearchConfig.java | 23 ++++++++-------- .../openvsx/cache/CacheServiceTest.java | 15 +++-------- .../ratelimit/RateLimitIntegrationTest.java | 11 ++------ .../RepositoryServiceSmokeTest.java | 15 +++-------- 7 files changed, 40 insertions(+), 71 deletions(-) diff --git a/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java b/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java index 1f8a12b38..089960008 100644 --- a/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java +++ b/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java @@ -20,13 +20,9 @@ import org.springframework.boot.resttestclient.TestRestTemplate; import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.http.HttpStatus; import org.springframework.test.context.ActiveProfiles; import org.springframework.web.client.RestTemplate; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.postgresql.PostgreSQLContainer; import java.io.IOException; import java.net.URI; @@ -36,14 +32,9 @@ @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @AutoConfigureTestRestTemplate -@ActiveProfiles("test") -@Testcontainers +@ActiveProfiles({"test", "test_db", "test_search"}) class IntegrationTest { - @Container - @ServiceConnection - static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:16.2"); - protected final Logger logger = LoggerFactory.getLogger(IntegrationTest.class); @LocalServerPort @@ -214,7 +205,7 @@ private void searchExtension() { var response = restTemplate.getForEntity(apiCall("/api/-/search?query=editorconfig"), SearchResultJson.class); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(response.getBody().getExtensions().size()).isEqualTo(1); - assertThat(response.getBody().getExtensions().get(0).getDescription()) + assertThat(response.getBody().getExtensions().getFirst().getDescription()) .isEqualTo("EditorConfig Support for Visual Studio Code"); } diff --git a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java index 36b5f395b..f616b85c4 100644 --- a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java @@ -746,14 +746,14 @@ void testSearchInactive() throws Exception { extVersionsList.forEach(extVersion -> { var extension = extVersion.getExtension(); extension.setActive(false); - extension.getVersions().get(0).setActive(false); + extension.getVersions().getFirst().setActive(false); }); Mockito.when(repositories.findLatestVersions(extVersionsList.stream().map(ExtensionVersion::getExtension).map(Extension::getId).toList())) .thenReturn(Collections.emptyList()); mockMvc.perform(get("/api/-/search?query={query}&size={size}&offset={offset}", "foo", "10", "0")) .andExpect(status().isOk()) - .andExpect(content().string("{\"offset\":0,\"totalSize\":1,\"extensions\":[]}")); + .andExpect(content().json("{\"offset\":0,\"totalSize\":1,\"extensions\":[]}")); } @Test @@ -2125,9 +2125,9 @@ private List mockExtensionVersions(String targetPlatform, List Mockito.when(repositories.findActiveExtensionVersions(Set.of(extension.getId()), null)) .thenReturn(versions); Mockito.when(repositories.findLatestVersionsIsPreview(Set.of(extension.getId()))) - .thenReturn(Map.of(extension.getId(), versions.get(0).isPreview())); + .thenReturn(Map.of(extension.getId(), versions.getFirst().isPreview())); Mockito.when(repositories.findActiveVersionStringsSorted(Set.of(extension.getId()), null)) - .thenReturn(versions.stream().collect(Collectors.groupingBy(ev -> ev.getExtension().getId(), Collectors.mapping(ev -> ev.getVersion(), Collectors.toList())))); + .thenReturn(versions.stream().collect(Collectors.groupingBy(ev -> ev.getExtension().getId(), Collectors.mapping(ExtensionVersion::getVersion, Collectors.toList())))); Mockito.when(repositories.findVersionStringsSorted(extension, targetPlatform, true)) .thenReturn(versions.stream().map(ExtensionVersion::getVersion).collect(Collectors.toList())); diff --git a/server/src/test/java/org/eclipse/openvsx/TestDatabaseConfig.java b/server/src/test/java/org/eclipse/openvsx/TestDatabaseConfig.java index 26c260043..12d20ab5a 100644 --- a/server/src/test/java/org/eclipse/openvsx/TestDatabaseConfig.java +++ b/server/src/test/java/org/eclipse/openvsx/TestDatabaseConfig.java @@ -1,32 +1,30 @@ -/******************************************************************************** - * Copyright (c) 2020 TypeFox and others +/****************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. * * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0. * * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ + *****************************************************************************/ package org.eclipse.openvsx; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; -import org.testcontainers.elasticsearch.ElasticsearchContainer; -import org.testcontainers.utility.DockerImageName; +import org.testcontainers.postgresql.PostgreSQLContainer; @Configuration -@Profile("test") +@Profile("test_db") public class TestDatabaseConfig { @Bean @ServiceConnection - ElasticsearchContainer elasticsearchContainer() { - return new ElasticsearchContainer( - DockerImageName.parse("elasticsearch:8.17.3") - .asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch")) - .withEnv("discovery.type", "single-node") - .withEnv("xpack.security.enabled", "false"); + PostgreSQLContainer postgreSQLContainer() { + return new PostgreSQLContainer("postgres:16.2"); } } diff --git a/server/src/test/java/org/eclipse/openvsx/TestSearchConfig.java b/server/src/test/java/org/eclipse/openvsx/TestSearchConfig.java index aec136ee1..6d3136c75 100644 --- a/server/src/test/java/org/eclipse/openvsx/TestSearchConfig.java +++ b/server/src/test/java/org/eclipse/openvsx/TestSearchConfig.java @@ -9,23 +9,24 @@ ********************************************************************************/ package org.eclipse.openvsx; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; -import org.springframework.data.elasticsearch.client.ClientConfiguration; -import org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration; import org.testcontainers.elasticsearch.ElasticsearchContainer; +import org.testcontainers.utility.DockerImageName; @Configuration -@Profile("test") -public class TestSearchConfig extends ElasticsearchConfiguration { +@Profile("test_search") +public class TestSearchConfig { - @Override - public ClientConfiguration clientConfiguration() { - var container = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.7.1") + @Bean + @ServiceConnection + ElasticsearchContainer elasticsearchContainer() { + return new ElasticsearchContainer( + DockerImageName.parse("elasticsearch:8.17.3") + .asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch")) .withEnv("discovery.type", "single-node") .withEnv("xpack.security.enabled", "false"); - - container.start(); - return ClientConfiguration.create(container.getHttpHostAddress()); } -} \ No newline at end of file +} diff --git a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java index 259b4645b..cd102ecd5 100644 --- a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java +++ b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java @@ -26,7 +26,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.cache.CacheManager; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.security.authentication.TestingAuthenticationToken; @@ -35,9 +34,6 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.postgresql.PostgreSQLContainer; import java.io.IOException; import java.nio.file.Files; @@ -50,15 +46,12 @@ import static org.eclipse.openvsx.entities.FileResource.STORAGE_LOCAL; import static org.junit.jupiter.api.Assertions.*; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@ActiveProfiles("test") -@Testcontainers +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { + "ovsx.elasticsearch.enabled=false" +}) +@ActiveProfiles({"test", "test_db"}) class CacheServiceTest { - @Container - @ServiceConnection - static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:16.2"); - @Autowired CacheManager cache; diff --git a/server/src/test/java/org/eclipse/openvsx/ratelimit/RateLimitIntegrationTest.java b/server/src/test/java/org/eclipse/openvsx/ratelimit/RateLimitIntegrationTest.java index a0514e089..750f190d7 100644 --- a/server/src/test/java/org/eclipse/openvsx/ratelimit/RateLimitIntegrationTest.java +++ b/server/src/test/java/org/eclipse/openvsx/ratelimit/RateLimitIntegrationTest.java @@ -24,11 +24,8 @@ import org.springframework.boot.resttestclient.TestRestTemplate; import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.bean.override.mockito.MockitoBean; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.postgresql.PostgreSQLContainer; import redis.clients.jedis.RedisClusterClient; import java.net.URI; @@ -43,13 +40,9 @@ "ovsx.elasticsearch.enabled=false" }) @AutoConfigureTestRestTemplate -@Testcontainers +@ActiveProfiles("test_db") class RateLimitIntegrationTest { - @Container - @ServiceConnection - static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:16.2"); - @LocalServerPort int port; diff --git a/server/src/test/java/org/eclipse/openvsx/repositories/RepositoryServiceSmokeTest.java b/server/src/test/java/org/eclipse/openvsx/repositories/RepositoryServiceSmokeTest.java index 656d503f2..0c84c46df 100644 --- a/server/src/test/java/org/eclipse/openvsx/repositories/RepositoryServiceSmokeTest.java +++ b/server/src/test/java/org/eclipse/openvsx/repositories/RepositoryServiceSmokeTest.java @@ -45,13 +45,9 @@ import org.mockito.invocation.Invocation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.test.context.ActiveProfiles; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.postgresql.PostgreSQLContainer; import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; @@ -60,15 +56,12 @@ * Run the DB queries and assert no DB error, just to ensure that the queries * are consistent with the schema. */ -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@ActiveProfiles("test") -@Testcontainers +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { + "ovsx.elasticsearch.enabled=false" +}) +@ActiveProfiles("test_db") class RepositoryServiceSmokeTest { - @Container - @ServiceConnection - static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:16.2"); - private static final List STRING_LIST = List.of("id1", "id2"); private static final List LONG_LIST = List.of(1L, 2L); From 0c9a0d491aae206418775710376361723622a63e Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Fri, 26 Jun 2026 16:06:36 +0200 Subject: [PATCH 10/13] fix tests --- server/build.gradle | 6 ++++++ .../eclipse/openvsx/admin/FileDecisionAPITest.java | 3 ++- .../java/org/eclipse/openvsx/admin/ScanAPITest.java | 3 ++- .../org/eclipse/openvsx/cache/CacheServiceTest.java | 11 +++++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/server/build.gradle b/server/build.gradle index 1f4cc1e89..4f1998a64 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -47,6 +47,8 @@ sourceSets { configurations { devImplementation.extendsFrom implementation devRuntimeOnly.extendsFrom runtimeOnly + + testImplementation.exclude group: 'com.vaadin.external.google', module: 'android-json' } dependencies { @@ -68,9 +70,13 @@ dependencies { implementation "org.springframework.boot:spring-boot-starter-cache" implementation "org.springframework.boot:spring-boot-starter-mail" implementation "org.springframework.boot:spring-boot-starter-zipkin" + implementation "org.springframework.boot:spring-boot-security-oauth2-client" + implementation "org.springframework.security:spring-security-oauth2-client" implementation "org.springframework.security:spring-security-oauth2-jose" + implementation "org.springframework.session:spring-session-jdbc" + implementation "org.springframework.boot:spring-boot-thymeleaf" implementation "org.apache.httpcomponents.client5:httpclient5" diff --git a/server/src/test/java/org/eclipse/openvsx/admin/FileDecisionAPITest.java b/server/src/test/java/org/eclipse/openvsx/admin/FileDecisionAPITest.java index c84c9493c..5036b6580 100644 --- a/server/src/test/java/org/eclipse/openvsx/admin/FileDecisionAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/admin/FileDecisionAPITest.java @@ -16,6 +16,7 @@ import org.eclipse.openvsx.repositories.RepositoryService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.security.oauth2.client.autoconfigure.servlet.OAuth2ClientWebSecurityAutoConfiguration; import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.http.MediaType; @@ -26,7 +27,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -@WebMvcTest(FileDecisionAPI.class) +@WebMvcTest(value = FileDecisionAPI.class, excludeAutoConfiguration = {OAuth2ClientWebSecurityAutoConfiguration.class}) @AutoConfigureMockMvc(addFilters = false) class FileDecisionAPITest { diff --git a/server/src/test/java/org/eclipse/openvsx/admin/ScanAPITest.java b/server/src/test/java/org/eclipse/openvsx/admin/ScanAPITest.java index f3573d12b..15559141f 100644 --- a/server/src/test/java/org/eclipse/openvsx/admin/ScanAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/admin/ScanAPITest.java @@ -24,6 +24,7 @@ import org.eclipse.openvsx.util.LogService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.security.oauth2.client.autoconfigure.servlet.OAuth2ClientWebSecurityAutoConfiguration; import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.data.domain.PageImpl; @@ -45,7 +46,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@WebMvcTest(ScanAPI.class) +@WebMvcTest(value = ScanAPI.class, excludeAutoConfiguration = {OAuth2ClientWebSecurityAutoConfiguration.class}) @AutoConfigureMockMvc(addFilters = false) class ScanAPITest { diff --git a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java index cd102ecd5..60e01466f 100644 --- a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java +++ b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java @@ -161,6 +161,8 @@ void testPostReview() throws IOException { registry.postReview(review, namespace.getName(), extension.getName()); assertNull(cache.getCache(CACHE_EXTENSION_JSON).get(cacheKey, ExtensionJson.class)); + entityManager.flush(); + json = registry.getExtension(namespace.getName(), extension.getName(), extVersion.getTargetPlatform(), extVersion.getVersion()); assertEquals(Long.valueOf(1), json.getReviewCount()); assertEquals(Double.valueOf(3), json.getAverageRating()); @@ -192,6 +194,9 @@ void testDeleteReview() throws IOException { review.setTimestamp("2000-01-01T10:00Z"); registry.postReview(review, namespace.getName(), extension.getName()); + + entityManager.flush(); + var json = registry.getExtension(namespace.getName(), extension.getName(), extVersion.getTargetPlatform(), extVersion.getVersion()); assertEquals(Long.valueOf(1), json.getReviewCount()); assertEquals(Double.valueOf(3), json.getAverageRating()); @@ -199,6 +204,8 @@ void testDeleteReview() throws IOException { registry.deleteReview(namespace.getName(), extension.getName()); assertNull(cache.getCache(CACHE_EXTENSION_JSON).get(cacheKey, ExtensionJson.class)); + entityManager.flush(); + json = registry.getExtension(namespace.getName(), extension.getName(), extVersion.getTargetPlatform(), extVersion.getVersion()); assertEquals(Long.valueOf(0), json.getReviewCount()); assertNull(json.getAverageRating()); @@ -250,6 +257,8 @@ void testDeleteExtensionVersion() throws IOException { admins.deleteExtension(namespace.getName(), extension.getName(), extVersion.getTargetPlatform(), newVersion, admin); assertNull(cache.getCache(CACHE_EXTENSION_JSON).get(cacheKey, ExtensionJson.class)); + entityManager.flush(); + json = registry.getExtension(namespace.getName(), extension.getName(), extVersion.getTargetPlatform(), extVersion.getVersion()); assertFalse(json.getAllVersions().containsKey(newVersion)); assertTrue(json.getAllVersions().containsKey(oldVersion)); @@ -280,6 +289,8 @@ void testUpdateExtension() throws IOException { extensions.updateExtension(extension); assertNull(cache.getCache(CACHE_EXTENSION_JSON).get(cacheKey, ExtensionJson.class)); + entityManager.flush(); + var json = registry.getExtension(namespace.getName(), extension.getName(), extVersion.getTargetPlatform(), oldVersion); assertTrue(json.getAllVersions().containsKey(oldVersion)); assertTrue(json.getAllVersions().containsKey(newVersion)); From c525065cf433961246ef2dc6b855863e9b990d79 Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Fri, 26 Jun 2026 16:56:39 +0200 Subject: [PATCH 11/13] fix integration test --- .../src/test/java/org/eclipse/openvsx/IntegrationTest.java | 2 +- .../src/test/java/org/eclipse/openvsx/TestSearchConfig.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java b/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java index 089960008..95a2b18d7 100644 --- a/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java +++ b/server/src/test/java/org/eclipse/openvsx/IntegrationTest.java @@ -9,7 +9,6 @@ ********************************************************************************/ package org.eclipse.openvsx; -import com.fasterxml.jackson.databind.JsonNode; import org.eclipse.openvsx.json.*; import org.junit.jupiter.api.Test; import org.slf4j.Logger; @@ -23,6 +22,7 @@ import org.springframework.http.HttpStatus; import org.springframework.test.context.ActiveProfiles; import org.springframework.web.client.RestTemplate; +import tools.jackson.databind.JsonNode; import java.io.IOException; import java.net.URI; diff --git a/server/src/test/java/org/eclipse/openvsx/TestSearchConfig.java b/server/src/test/java/org/eclipse/openvsx/TestSearchConfig.java index 6d3136c75..959d8ca39 100644 --- a/server/src/test/java/org/eclipse/openvsx/TestSearchConfig.java +++ b/server/src/test/java/org/eclipse/openvsx/TestSearchConfig.java @@ -24,9 +24,10 @@ public class TestSearchConfig { @ServiceConnection ElasticsearchContainer elasticsearchContainer() { return new ElasticsearchContainer( - DockerImageName.parse("elasticsearch:8.17.3") + DockerImageName.parse("elasticsearch:9.2.8") .asCompatibleSubstituteFor("docker.elastic.co/elasticsearch/elasticsearch")) .withEnv("discovery.type", "single-node") - .withEnv("xpack.security.enabled", "false"); + .withEnv("xpack.security.http.ssl.enabled", "false") + .withPassword("test-password"); } } From 2875d99177e415b1b6a212e9825ed37a594df6bb Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Fri, 26 Jun 2026 21:40:27 +0200 Subject: [PATCH 12/13] migrate from jakarta to jspecify annotations --- server/build.gradle | 2 +- server/gradle/libs.versions.toml | 4 +- .../eclipse/openvsx/ExtensionProcessor.java | 2 +- .../eclipse/openvsx/LocalRegistryService.java | 2 +- .../accesstoken/AccessTokenConfig.java | 12 ++-- .../adapter/UpstreamVSCodeService.java | 2 +- .../org/eclipse/openvsx/admin/ScanAPI.java | 2 +- .../openvsx/cache/ExpiredFileListener.java | 6 +- .../eclipse/openvsx/entities/UserData.java | 3 +- .../eclipse/openvsx/json/AccessTokenJson.java | 3 +- .../eclipse/openvsx/json/CheckResultJson.java | 2 +- .../openvsx/json/RateLimitTokenJson.java | 2 +- .../eclipse/openvsx/json/ScannerJobJson.java | 2 +- .../mirror/DataMirrorJobRequestHandler.java | 2 +- .../openvsx/ratelimit/CustomerService.java | 2 +- .../openvsx/ratelimit/RateLimitService.java | 6 +- .../openvsx/ratelimit/ResolvedIdentity.java | 8 +-- .../openvsx/ratelimit/UsageStatsService.java | 4 +- .../jobs/CalculateDailyUsageStatsHandler.java | 2 +- .../jobs/CollectUsageStatsHandler.java | 2 +- .../AdminScanDecisionRepository.java | 3 +- .../repositories/ExtensionScanRepository.java | 3 +- .../repositories/RepositoryService.java | 7 +- .../openvsx/scanning/EntropyCalculator.java | 3 +- .../ExtensionScanPersistenceService.java | 68 +++++++++---------- .../scanning/ExtensionScanService.java | 22 +++--- .../scanning/GitleaksRulesService.java | 2 +- .../openvsx/scanning/HttpAuthHandler.java | 4 +- .../openvsx/scanning/HttpClientExecutor.java | 8 +-- .../openvsx/scanning/PublishCheck.java | 8 +-- .../openvsx/scanning/PublishCheckRunner.java | 42 ++++++------ .../openvsx/scanning/RemoteScanner.java | 35 +++++----- .../scanning/RemoteScannerProperties.java | 2 +- .../org/eclipse/openvsx/scanning/Scanner.java | 60 ++++++++-------- .../openvsx/scanning/ScannerException.java | 8 +-- .../openvsx/scanning/ScannerFileProvider.java | 4 +- .../scanning/ScannerInvocationRequest.java | 7 +- .../openvsx/scanning/ScannerRegistry.java | 21 +++--- .../openvsx/scanning/SecretDetector.java | 3 +- .../scanning/SecretDetectorFactory.java | 12 ++-- .../eclipse/openvsx/scanning/SecretRule.java | 2 +- .../openvsx/scanning/SecretRuleLoader.java | 2 +- .../openvsx/search/ElasticSearchService.java | 2 +- .../openvsx/search/ExtensionSearch.java | 2 +- .../openvsx/search/RelevanceService.java | 2 +- .../search/SimilarityCheckService.java | 4 +- .../openvsx/search/SimilarityService.java | 4 +- .../openvsx/settings/SettingsService.java | 2 +- .../openvsx/storage/AwsStorageService.java | 2 +- .../openvsx/storage/CdnServiceConfig.java | 2 +- .../openvsx/storage/IStorageService.java | 2 +- .../storage/log/FastlyLogFileParser.java | 2 +- .../openvsx/storage/log/LogFileParser.java | 2 +- .../openvsx/storage/log/LogRecord.java | 4 +- .../org/eclipse/openvsx/util/ArchiveUtil.java | 2 +- .../eclipse/openvsx/util/HttpHeadersUtil.java | 7 +- .../org/eclipse/openvsx/util/LogService.java | 4 +- .../org/eclipse/openvsx/util/UrlUtil.java | 5 +- .../org/eclipse/openvsx/util/XmlUtil.java | 2 +- .../openvsx/web/ServerExceptionResolver.java | 5 +- .../eclipse/openvsx/scanning/ScannerTest.java | 5 +- 61 files changed, 222 insertions(+), 234 deletions(-) diff --git a/server/build.gradle b/server/build.gradle index 4f1998a64..5659facca 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -188,7 +188,7 @@ jooq { } rewrite { - activeRecipe("org.openrewrite.java.migrate.jakarta.JavaxAnnotationMigrationToJakartaAnnotation") + activeRecipe("org.openrewrite.java.jspecify.MigrateFromJakartaAnnotationApi") setExportDatatables(true) } diff --git a/server/gradle/libs.versions.toml b/server/gradle/libs.versions.toml index 9bb25f1de..f1e0ee253 100644 --- a/server/gradle/libs.versions.toml +++ b/server/gradle/libs.versions.toml @@ -18,7 +18,7 @@ jooq = "3.19.34" jsonpath = "2.9.0" loki4j = "1.4.2" re2j = "1.7" -rewrite-java = "3.26.0" +rewrite-java = "3.38.0" spring-boot = "4.0.7" springdoc = "2.8.13" tika = "3.3.1" @@ -32,7 +32,7 @@ jooq-codegen = { id = "org.jooq.jooq-codegen-gradle", version.re spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot"} spring-dependency-management = { id = "io.spring.dependency-management", version = "1.1.7" } test-logger = { id = "com.adarshr.test-logger", version = "4.0.0" } -rewrite = { id = "org.openrewrite.rewrite", version = "7.25.0" } +rewrite = { id = "org.openrewrite.rewrite", version = "7.35.0" } [libraries] awssdk-s3 = { module = "software.amazon.awssdk:s3", version.ref = "aws" } diff --git a/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java b/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java index e2e4e2d88..b4e0ae534 100644 --- a/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java +++ b/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java @@ -14,13 +14,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.MissingNode; import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import jakarta.annotation.Nullable; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.eclipse.openvsx.adapter.ExtensionQueryResult; import org.eclipse.openvsx.entities.ExtensionVersion; import org.eclipse.openvsx.entities.FileResource; import org.eclipse.openvsx.util.*; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.server.ServerErrorException; diff --git a/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java b/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java index 170f72c32..b8c749836 100644 --- a/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java +++ b/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java @@ -26,8 +26,8 @@ import org.eclipse.openvsx.search.SearchUtilService; import org.eclipse.openvsx.search.SimilarityCheckService; import org.eclipse.openvsx.storage.StorageUtilService; -import jakarta.annotation.Nullable; import org.eclipse.openvsx.util.*; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; diff --git a/server/src/main/java/org/eclipse/openvsx/accesstoken/AccessTokenConfig.java b/server/src/main/java/org/eclipse/openvsx/accesstoken/AccessTokenConfig.java index f16f570ba..3c0ef28d3 100644 --- a/server/src/main/java/org/eclipse/openvsx/accesstoken/AccessTokenConfig.java +++ b/server/src/main/java/org/eclipse/openvsx/accesstoken/AccessTokenConfig.java @@ -12,8 +12,8 @@ *****************************************************************************/ package org.eclipse.openvsx.accesstoken; -import jakarta.annotation.Nonnull; import jakarta.annotation.PostConstruct; +import org.jspecify.annotations.NonNull; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; @@ -95,7 +95,7 @@ public class AccessTokenConfig { @Value("${ovsx.data.mirror.enabled:false}") private boolean mirrorEnabled; - public @Nonnull String getPrefix() { + public @NonNull String getPrefix() { return this.prefix; } @@ -103,7 +103,7 @@ public boolean isTokenExpiryEnabled() { return this.expiration.isPositive(); } - public @Nonnull Duration getExpiration() { + public @NonNull Duration getExpiration() { return this.expiration; } @@ -111,7 +111,7 @@ public boolean isTokenExpiryNotificationEnabled() { return this.notification.isPositive(); } - public @Nonnull Duration getNotification() { + public @NonNull Duration getNotification() { return this.notification; } @@ -127,7 +127,7 @@ public boolean hasExpirationSchedule() { return StringUtils.hasText(this.expirationSchedule); } - public @Nonnull String getExpirationSchedule() { + public @NonNull String getExpirationSchedule() { return this.expirationSchedule; } @@ -135,7 +135,7 @@ public boolean hasNotificationSchedule() { return StringUtils.hasText(this.notificationSchedule); } - public @Nonnull String getNotificationSchedule() { + public @NonNull String getNotificationSchedule() { return this.notificationSchedule; } diff --git a/server/src/main/java/org/eclipse/openvsx/adapter/UpstreamVSCodeService.java b/server/src/main/java/org/eclipse/openvsx/adapter/UpstreamVSCodeService.java index 94ca0eadd..d9a65d468 100644 --- a/server/src/main/java/org/eclipse/openvsx/adapter/UpstreamVSCodeService.java +++ b/server/src/main/java/org/eclipse/openvsx/adapter/UpstreamVSCodeService.java @@ -10,7 +10,6 @@ package org.eclipse.openvsx.adapter; import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.annotation.Nullable; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.eclipse.openvsx.ExtensionValidator; @@ -19,6 +18,7 @@ import org.eclipse.openvsx.util.HttpHeadersUtil; import org.eclipse.openvsx.util.NotFoundException; import org.eclipse.openvsx.util.TempFile; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.*; diff --git a/server/src/main/java/org/eclipse/openvsx/admin/ScanAPI.java b/server/src/main/java/org/eclipse/openvsx/admin/ScanAPI.java index d46f89c9a..ceb366b50 100644 --- a/server/src/main/java/org/eclipse/openvsx/admin/ScanAPI.java +++ b/server/src/main/java/org/eclipse/openvsx/admin/ScanAPI.java @@ -20,7 +20,6 @@ import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; -import jakarta.annotation.Nullable; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import org.eclipse.openvsx.entities.*; @@ -32,6 +31,7 @@ import org.eclipse.openvsx.util.LogService; import org.eclipse.openvsx.util.TimeUtil; import org.eclipse.openvsx.util.UrlUtil; +import org.jspecify.annotations.Nullable; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; diff --git a/server/src/main/java/org/eclipse/openvsx/cache/ExpiredFileListener.java b/server/src/main/java/org/eclipse/openvsx/cache/ExpiredFileListener.java index 835aa73e6..fd83e3f97 100644 --- a/server/src/main/java/org/eclipse/openvsx/cache/ExpiredFileListener.java +++ b/server/src/main/java/org/eclipse/openvsx/cache/ExpiredFileListener.java @@ -11,11 +11,11 @@ import com.github.benmanes.caffeine.cache.RemovalCause; import com.github.benmanes.caffeine.cache.RemovalListener; -import jakarta.annotation.Nonnull; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import jakarta.annotation.Nullable; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -24,7 +24,7 @@ public class ExpiredFileListener implements RemovalListener { protected final Logger logger = LoggerFactory.getLogger(ExpiredFileListener.class); @Override - public void onRemoval(@Nullable Object key, @Nullable Object value, @Nonnull RemovalCause cause) { + public void onRemoval(@Nullable Object key, @Nullable Object value, @NonNull RemovalCause cause) { logger.debug("File removal cache event: {} | key: {} | value: {}", cause, key, value); if (!(value instanceof Path path)) { return; diff --git a/server/src/main/java/org/eclipse/openvsx/entities/UserData.java b/server/src/main/java/org/eclipse/openvsx/entities/UserData.java index 9a0eec10d..ccbfc65e7 100644 --- a/server/src/main/java/org/eclipse/openvsx/entities/UserData.java +++ b/server/src/main/java/org/eclipse/openvsx/entities/UserData.java @@ -15,9 +15,8 @@ import java.util.Objects; import java.util.Optional; -import jakarta.annotation.Nullable; import org.eclipse.openvsx.json.UserJson; - +import org.jspecify.annotations.Nullable; import jakarta.persistence.Column; import jakarta.persistence.Convert; import jakarta.persistence.Entity; diff --git a/server/src/main/java/org/eclipse/openvsx/json/AccessTokenJson.java b/server/src/main/java/org/eclipse/openvsx/json/AccessTokenJson.java index d3b0a6ef9..ce3ea7366 100644 --- a/server/src/main/java/org/eclipse/openvsx/json/AccessTokenJson.java +++ b/server/src/main/java/org/eclipse/openvsx/json/AccessTokenJson.java @@ -9,10 +9,9 @@ ********************************************************************************/ package org.eclipse.openvsx.json; -import jakarta.annotation.Nullable; - import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import org.jspecify.annotations.Nullable; @JsonInclude(Include.NON_NULL) public class AccessTokenJson extends ResultJson { diff --git a/server/src/main/java/org/eclipse/openvsx/json/CheckResultJson.java b/server/src/main/java/org/eclipse/openvsx/json/CheckResultJson.java index f7568a251..fcce119c4 100644 --- a/server/src/main/java/org/eclipse/openvsx/json/CheckResultJson.java +++ b/server/src/main/java/org/eclipse/openvsx/json/CheckResultJson.java @@ -15,7 +15,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.annotation.Nullable; +import org.jspecify.annotations.Nullable; /** * JSON representation of a scan check execution result. diff --git a/server/src/main/java/org/eclipse/openvsx/json/RateLimitTokenJson.java b/server/src/main/java/org/eclipse/openvsx/json/RateLimitTokenJson.java index 23c771def..63f27a641 100644 --- a/server/src/main/java/org/eclipse/openvsx/json/RateLimitTokenJson.java +++ b/server/src/main/java/org/eclipse/openvsx/json/RateLimitTokenJson.java @@ -14,7 +14,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; -import jakarta.annotation.Nullable; +import org.jspecify.annotations.Nullable; @JsonInclude(Include.NON_NULL) public class RateLimitTokenJson extends ResultJson { diff --git a/server/src/main/java/org/eclipse/openvsx/json/ScannerJobJson.java b/server/src/main/java/org/eclipse/openvsx/json/ScannerJobJson.java index d38715ac1..95b95884a 100644 --- a/server/src/main/java/org/eclipse/openvsx/json/ScannerJobJson.java +++ b/server/src/main/java/org/eclipse/openvsx/json/ScannerJobJson.java @@ -15,7 +15,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.annotation.Nullable; +import org.jspecify.annotations.Nullable; /** * JSON representation of a scanner job, exposing its current lifecycle state. diff --git a/server/src/main/java/org/eclipse/openvsx/mirror/DataMirrorJobRequestHandler.java b/server/src/main/java/org/eclipse/openvsx/mirror/DataMirrorJobRequestHandler.java index f673e3472..ce7c82244 100644 --- a/server/src/main/java/org/eclipse/openvsx/mirror/DataMirrorJobRequestHandler.java +++ b/server/src/main/java/org/eclipse/openvsx/mirror/DataMirrorJobRequestHandler.java @@ -9,7 +9,6 @@ * ****************************************************************************** */ package org.eclipse.openvsx.mirror; -import jakarta.annotation.Nullable; import org.eclipse.openvsx.UrlConfigService; import org.eclipse.openvsx.admin.AdminService; import org.eclipse.openvsx.entities.UserData; @@ -19,6 +18,7 @@ import org.eclipse.openvsx.util.XmlUtil; import org.jobrunr.jobs.annotations.Job; import org.jobrunr.jobs.lambdas.JobRequestHandler; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; diff --git a/server/src/main/java/org/eclipse/openvsx/ratelimit/CustomerService.java b/server/src/main/java/org/eclipse/openvsx/ratelimit/CustomerService.java index f3daac6e0..0f58d884f 100644 --- a/server/src/main/java/org/eclipse/openvsx/ratelimit/CustomerService.java +++ b/server/src/main/java/org/eclipse/openvsx/ratelimit/CustomerService.java @@ -14,7 +14,6 @@ import inet.ipaddr.IPAddressString; import inet.ipaddr.ipv4.IPv4AddressAssociativeTrie; -import jakarta.annotation.Nullable; import jakarta.annotation.PostConstruct; import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; @@ -27,6 +26,7 @@ import org.eclipse.openvsx.repositories.RepositoryService; import org.eclipse.openvsx.util.ErrorResultException; import org.eclipse.openvsx.util.TimeUtil; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.annotation.Cacheable; diff --git a/server/src/main/java/org/eclipse/openvsx/ratelimit/RateLimitService.java b/server/src/main/java/org/eclipse/openvsx/ratelimit/RateLimitService.java index 4573f5102..d1f13a65f 100644 --- a/server/src/main/java/org/eclipse/openvsx/ratelimit/RateLimitService.java +++ b/server/src/main/java/org/eclipse/openvsx/ratelimit/RateLimitService.java @@ -17,14 +17,14 @@ import io.github.bucket4j.BucketConfiguration; import io.github.bucket4j.TokensInheritanceStrategy; import io.github.bucket4j.distributed.proxy.ProxyManager; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; import org.eclipse.openvsx.entities.Customer; import org.eclipse.openvsx.entities.EnforcementState; import org.eclipse.openvsx.entities.RefillStrategy; import org.eclipse.openvsx.entities.Tier; import org.eclipse.openvsx.ratelimit.cache.ConfigurationChanged; import org.eclipse.openvsx.ratelimit.config.RateLimitConfig; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -72,7 +72,7 @@ public static BucketPair empty() { return new BucketPair(null, null); } - public static BucketPair of(@Nonnull Bucket bucket, MinimumBandwidth minimumBandwidth) { + public static BucketPair of(@NonNull Bucket bucket, MinimumBandwidth minimumBandwidth) { return new BucketPair(bucket, minimumBandwidth); } } diff --git a/server/src/main/java/org/eclipse/openvsx/ratelimit/ResolvedIdentity.java b/server/src/main/java/org/eclipse/openvsx/ratelimit/ResolvedIdentity.java index 03860205a..6d60471a7 100644 --- a/server/src/main/java/org/eclipse/openvsx/ratelimit/ResolvedIdentity.java +++ b/server/src/main/java/org/eclipse/openvsx/ratelimit/ResolvedIdentity.java @@ -12,15 +12,15 @@ *****************************************************************************/ package org.eclipse.openvsx.ratelimit; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; import jakarta.validation.constraints.NotNull; import org.eclipse.openvsx.entities.Customer; import org.eclipse.openvsx.entities.Tier; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; public record ResolvedIdentity( - @Nonnull String ipAddress, - @Nonnull String cacheKey, + @NonNull String ipAddress, + @NonNull String cacheKey, @Nullable Customer customer, @Nullable Tier freeTier, @Nullable Tier safetyTier diff --git a/server/src/main/java/org/eclipse/openvsx/ratelimit/UsageStatsService.java b/server/src/main/java/org/eclipse/openvsx/ratelimit/UsageStatsService.java index dd44380ea..4954a7fa4 100644 --- a/server/src/main/java/org/eclipse/openvsx/ratelimit/UsageStatsService.java +++ b/server/src/main/java/org/eclipse/openvsx/ratelimit/UsageStatsService.java @@ -13,13 +13,13 @@ package org.eclipse.openvsx.ratelimit; import com.github.benmanes.caffeine.cache.Cache; -import jakarta.annotation.Nonnull; import org.eclipse.openvsx.entities.Customer; import org.eclipse.openvsx.entities.DailyUsageStats; import org.eclipse.openvsx.entities.UsageStats; import org.eclipse.openvsx.ratelimit.config.RateLimitConfig; import org.eclipse.openvsx.repositories.RepositoryService; import org.eclipse.openvsx.util.TimeUtil; +import org.jspecify.annotations.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -174,7 +174,7 @@ public void calculateDailyUsageStats() { } } - long getDailyPercentile(@Nonnull List input, double percentile) { + long getDailyPercentile(@NonNull List input, double percentile) { if (percentile < 0 || percentile > 100) { throw new IllegalArgumentException("Percentile must be between 0 and 100 inclusive."); } diff --git a/server/src/main/java/org/eclipse/openvsx/ratelimit/jobs/CalculateDailyUsageStatsHandler.java b/server/src/main/java/org/eclipse/openvsx/ratelimit/jobs/CalculateDailyUsageStatsHandler.java index d1a7a9373..dab38aa57 100644 --- a/server/src/main/java/org/eclipse/openvsx/ratelimit/jobs/CalculateDailyUsageStatsHandler.java +++ b/server/src/main/java/org/eclipse/openvsx/ratelimit/jobs/CalculateDailyUsageStatsHandler.java @@ -12,12 +12,12 @@ *****************************************************************************/ package org.eclipse.openvsx.ratelimit.jobs; -import jakarta.annotation.Nullable; import org.eclipse.openvsx.migration.HandlerJobRequest; import org.eclipse.openvsx.ratelimit.UsageStatsService; import org.eclipse.openvsx.settings.SettingsService; import org.jobrunr.jobs.annotations.Job; import org.jobrunr.jobs.lambdas.JobRequestHandler; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; diff --git a/server/src/main/java/org/eclipse/openvsx/ratelimit/jobs/CollectUsageStatsHandler.java b/server/src/main/java/org/eclipse/openvsx/ratelimit/jobs/CollectUsageStatsHandler.java index 823d9cd87..6340a3ff0 100644 --- a/server/src/main/java/org/eclipse/openvsx/ratelimit/jobs/CollectUsageStatsHandler.java +++ b/server/src/main/java/org/eclipse/openvsx/ratelimit/jobs/CollectUsageStatsHandler.java @@ -12,12 +12,12 @@ *****************************************************************************/ package org.eclipse.openvsx.ratelimit.jobs; -import jakarta.annotation.Nullable; import org.eclipse.openvsx.migration.HandlerJobRequest; import org.eclipse.openvsx.ratelimit.UsageStatsService; import org.eclipse.openvsx.settings.SettingsService; import org.jobrunr.jobs.annotations.Job; import org.jobrunr.jobs.lambdas.JobRequestHandler; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; diff --git a/server/src/main/java/org/eclipse/openvsx/repositories/AdminScanDecisionRepository.java b/server/src/main/java/org/eclipse/openvsx/repositories/AdminScanDecisionRepository.java index 53ab746a0..91219192c 100644 --- a/server/src/main/java/org/eclipse/openvsx/repositories/AdminScanDecisionRepository.java +++ b/server/src/main/java/org/eclipse/openvsx/repositories/AdminScanDecisionRepository.java @@ -14,13 +14,12 @@ import org.eclipse.openvsx.entities.AdminScanDecision; import org.eclipse.openvsx.entities.ExtensionScan; +import org.jspecify.annotations.Nullable; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; import org.springframework.data.repository.query.Param; import org.springframework.data.util.Streamable; -import jakarta.annotation.Nullable; - import java.time.LocalDateTime; import java.util.Collection; diff --git a/server/src/main/java/org/eclipse/openvsx/repositories/ExtensionScanRepository.java b/server/src/main/java/org/eclipse/openvsx/repositories/ExtensionScanRepository.java index 27cef2a4a..830e5da41 100644 --- a/server/src/main/java/org/eclipse/openvsx/repositories/ExtensionScanRepository.java +++ b/server/src/main/java/org/eclipse/openvsx/repositories/ExtensionScanRepository.java @@ -14,6 +14,7 @@ import org.eclipse.openvsx.entities.ExtensionScan; import org.eclipse.openvsx.entities.ScanStatus; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.Query; @@ -21,8 +22,6 @@ import org.springframework.data.repository.query.Param; import org.springframework.data.util.Streamable; -import jakarta.annotation.Nullable; - import java.time.LocalDateTime; import java.util.Collection; import java.util.List; diff --git a/server/src/main/java/org/eclipse/openvsx/repositories/RepositoryService.java b/server/src/main/java/org/eclipse/openvsx/repositories/RepositoryService.java index db8e81026..483306258 100644 --- a/server/src/main/java/org/eclipse/openvsx/repositories/RepositoryService.java +++ b/server/src/main/java/org/eclipse/openvsx/repositories/RepositoryService.java @@ -52,6 +52,7 @@ import org.eclipse.openvsx.util.ExtensionId; import org.eclipse.openvsx.util.NamingUtil; import org.eclipse.openvsx.web.SitemapRow; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -60,8 +61,6 @@ import org.springframework.data.util.Streamable; import org.springframework.stereotype.Component; -import jakarta.annotation.Nullable; - @Component public class RepositoryService { @@ -927,7 +926,7 @@ public org.springframework.data.domain.Page findScansFullyFiltere @Nullable Collection checkTypes, @Nullable Collection scannerNames, @Nullable Boolean enforcedOnly, - @Nullable org.eclipse.openvsx.admin.ScanAPI.AdminDecisionFilterValues adminDecisionFilter, + org.eclipse.openvsx.admin.ScanAPI.@Nullable AdminDecisionFilterValues adminDecisionFilter, boolean includeCheckErrors, org.springframework.data.domain.Pageable pageable ) { @@ -970,7 +969,7 @@ public long countScansFullyFiltered( @Nullable Collection checkTypes, @Nullable Collection scannerNames, @Nullable Boolean enforcedOnly, - @Nullable org.eclipse.openvsx.admin.ScanAPI.AdminDecisionFilterValues adminDecisionFilter, + org.eclipse.openvsx.admin.ScanAPI.@Nullable AdminDecisionFilterValues adminDecisionFilter, boolean includeCheckErrors ) { // Convert enums to strings for native query diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/EntropyCalculator.java b/server/src/main/java/org/eclipse/openvsx/scanning/EntropyCalculator.java index 6bb764dd5..3fa001a53 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/EntropyCalculator.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/EntropyCalculator.java @@ -12,7 +12,8 @@ ********************************************************************************/ package org.eclipse.openvsx.scanning; -import jakarta.annotation.Nullable; +import org.jspecify.annotations.Nullable; + import java.util.HashMap; import java.util.Map; diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanPersistenceService.java b/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanPersistenceService.java index af429830b..20f11b099 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanPersistenceService.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanPersistenceService.java @@ -19,12 +19,12 @@ import org.eclipse.openvsx.repositories.ScanCheckResultRepository; import org.eclipse.openvsx.repositories.ScannerJobRepository; import org.eclipse.openvsx.util.TimeUtil; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; import tools.jackson.core.JacksonException; import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; @@ -81,14 +81,14 @@ public ExtensionScanPersistenceService( * Creates and persists a new scan record BEFORE an extension version exists. */ @Transactional(TxType.REQUIRES_NEW) - @Nonnull + @NonNull public ExtensionScan initializeScan( - @Nonnull String namespaceName, - @Nonnull String extensionName, - @Nonnull String version, + @NonNull String namespaceName, + @NonNull String extensionName, + @NonNull String version, @Nullable String targetPlatform, @Nullable String displayName, - @Nonnull UserData user + @NonNull UserData user ) { var isUniversal = targetPlatform == null || "universal".equals(targetPlatform); if (displayName == null || displayName.isBlank()) { @@ -147,7 +147,7 @@ private ExtensionScan initializeScanInternal( * Persists a status change. The caller is responsible for validating the transition. */ @Transactional(TxType.REQUIRES_NEW) - public void updateStatus(@Nonnull ExtensionScan scan, @Nonnull ScanStatus newStatus) { + public void updateStatus(@NonNull ExtensionScan scan, @NonNull ScanStatus newStatus) { scan.setStatus(newStatus); repositories.saveExtensionScan(scan); } @@ -156,7 +156,7 @@ public void updateStatus(@Nonnull ExtensionScan scan, @Nonnull ScanStatus newSta * Persists a terminal status change with completion timestamp. */ @Transactional(TxType.REQUIRES_NEW) - public void completeWithStatus(@Nonnull ExtensionScan scan, @Nonnull ScanStatus newStatus) { + public void completeWithStatus(@NonNull ExtensionScan scan, @NonNull ScanStatus newStatus) { scan.setStatus(newStatus); scan.setCompletedAt(TimeUtil.getCurrentUTC()); repositories.saveExtensionScan(scan); @@ -166,7 +166,7 @@ public void completeWithStatus(@Nonnull ExtensionScan scan, @Nonnull ScanStatus * Persists an error status with message. */ @Transactional(TxType.REQUIRES_NEW) - public void markAsErrored(@Nonnull ExtensionScan scan, @Nullable String errorMessage) { + public void markAsErrored(@NonNull ExtensionScan scan, @Nullable String errorMessage) { scan.setStatus(ScanStatus.ERRORED); scan.setErrorMessage(errorMessage); scan.setCompletedAt(TimeUtil.getCurrentUTC()); @@ -177,7 +177,7 @@ public void markAsErrored(@Nonnull ExtensionScan scan, @Nullable String errorMes * Removes a scan. */ @Transactional(TxType.REQUIRES_NEW) - public void removeScan(@Nonnull ExtensionScan scan) { + public void removeScan(@NonNull ExtensionScan scan) { repositories.deleteExtensionScan(scan); } @@ -189,7 +189,7 @@ public void removeScan(@Nonnull ExtensionScan scan) { * {@code scan}, that the job is FAILED, and that the scan is terminal. */ @Transactional(TxType.REQUIRES_NEW) - public void resetJobForRetry(@Nonnull ExtensionScan scan, @Nonnull ScannerJob job) { + public void resetJobForRetry(@NonNull ExtensionScan scan, @NonNull ScannerJob job) { // Drop the stale check result so the retry's outcome is the only record the UI shows scanCheckResultRepository.deleteByScannerJobId(job.getId()); @@ -205,9 +205,9 @@ public void resetJobForRetry(@Nonnull ExtensionScan scan, @Nonnull ScannerJob jo */ @Transactional(TxType.REQUIRES_NEW) public void recordValidationFailure( - @Nonnull ExtensionScan scan, - @Nonnull String checkType, - @Nonnull String ruleName, + @NonNull ExtensionScan scan, + @NonNull String checkType, + @NonNull String ruleName, @Nullable String reason, boolean enforced ) { @@ -225,12 +225,12 @@ public void recordValidationFailure( */ @Transactional(TxType.REQUIRES_NEW) public void recordCheckResult( - @Nonnull ExtensionScan scan, - @Nonnull String checkType, - @Nonnull ScanCheckResult.CheckCategory category, - @Nonnull ScanCheckResult.CheckResult result, - @Nonnull LocalDateTime startedAt, - @Nonnull LocalDateTime completedAt, + @NonNull ExtensionScan scan, + @NonNull String checkType, + ScanCheckResult.@NonNull CheckCategory category, + ScanCheckResult.@NonNull CheckResult result, + @NonNull LocalDateTime startedAt, + @NonNull LocalDateTime completedAt, @Nullable Integer filesScanned, int findingsCount, @Nullable String summary, @@ -267,10 +267,10 @@ public void recordCheckResult( */ @Transactional(TxType.REQUIRES_NEW) public void recordScannerJobResult( - @Nonnull String scanId, - @Nonnull ScannerJob job, - @Nonnull ScanCheckResult.CheckResult result, - @Nonnull LocalDateTime startedAt, + @NonNull String scanId, + @NonNull ScannerJob job, + ScanCheckResult.@NonNull CheckResult result, + @NonNull LocalDateTime startedAt, @Nullable Integer filesScanned, int findingsCount, @Nullable String summary, @@ -340,7 +340,7 @@ public static ThreatSaveResult clean() { * - If enforced threats exist → check FOUND (blocks publication) */ @Transactional(TxType.REQUIRES_NEW) - public ThreatSaveResult saveThreats(@Nonnull ScannerJob job, @Nonnull Scanner.Result result, boolean scannerEnforced) { + public ThreatSaveResult saveThreats(@NonNull ScannerJob job, Scanner.@NonNull Result result, boolean scannerEnforced) { if (result.isClean()) { logger.debug("No threats to save for scanner job {}", job.getId()); return ThreatSaveResult.clean(); @@ -412,10 +412,10 @@ public record CompletedScanResult( */ @Transactional(TxType.REQUIRES_NEW) public CompletedScanResult processCompletedScan( - @Nonnull ScannerJob job, - @Nonnull Scanner.Result result, + @NonNull ScannerJob job, + Scanner.@NonNull Result result, boolean scannerEnforced, - @Nonnull LocalDateTime startedAt + @NonNull LocalDateTime startedAt ) { int threatCount = 0; String summary; @@ -469,14 +469,14 @@ public CompletedScanResult processCompletedScan( */ @Transactional(TxType.REQUIRES_NEW) public void recordThreat( - @Nonnull ExtensionScan scan, - @Nonnull String fileName, + @NonNull ExtensionScan scan, + @NonNull String fileName, @Nullable String fileHash, @Nullable String fileExtension, - @Nonnull String scannerType, - @Nonnull String ruleName, + @NonNull String scannerType, + @NonNull String ruleName, @Nullable String reason, - @Nonnull String severity, + @NonNull String severity, boolean enforced ) { var threat = ExtensionThreat.create( diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanService.java b/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanService.java index 02c36ec9e..d17cbd236 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanService.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanService.java @@ -20,13 +20,13 @@ import org.eclipse.openvsx.util.TempFile; import org.eclipse.openvsx.util.TimeUtil; import org.jobrunr.scheduling.JobRequestScheduler; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; import java.util.List; import java.util.stream.Collectors; @@ -76,8 +76,8 @@ public boolean isEnabled() { * Creates a scan record from ExtensionProcessor metadata. * Use this when validation runs BEFORE extension creation. */ - @Nonnull - public ExtensionScan initializeScan(@Nonnull ExtensionProcessor processor, @Nonnull UserData user) { + @NonNull + public ExtensionScan initializeScan(@NonNull ExtensionProcessor processor, @NonNull UserData user) { return initializeScan( processor.getNamespace(), processor.getExtensionName(), @@ -115,9 +115,9 @@ private ExtensionScan initializeScan( * then records findings and manages state transitions. */ public void runValidation( - @Nonnull ExtensionScan scan, - @Nonnull TempFile extensionFile, - @Nonnull UserData user + @NonNull ExtensionScan scan, + @NonNull TempFile extensionFile, + @NonNull UserData user ) { transitionTo(scan, ScanStatus.VALIDATING); @@ -187,7 +187,7 @@ public void runValidation( * and try to enqueue a JobRunr request. On enqueue failure we only log since the watchdog * will re-enqueue them for retry. */ - public boolean submitScannerJobs(@Nonnull ExtensionScan scan, @Nonnull ExtensionVersion extVersion) { + public boolean submitScannerJobs(@NonNull ExtensionScan scan, @NonNull ExtensionVersion extVersion) { if (!config.isEnabled()) { logger.debug("Scanning is disabled, skipping scanner jobs for: {}", NamingUtil.toLogFormat(extVersion)); @@ -266,7 +266,7 @@ public boolean submitScannerJobs(@Nonnull ExtensionScan scan, @Nonnull Extension * @throws ErrorResultException if the scan is not in a terminal state, the * job does not belong to the scan, or the job is not in FAILED status. */ - public void retryFailedJob(@Nonnull ExtensionScan scan, @Nonnull ScannerJob job) { + public void retryFailedJob(@NonNull ExtensionScan scan, @NonNull ScannerJob job) { if (job.getStatus().isActive()) { throw new ErrorResultException( "Cannot retry: this job is currently in an active state (current: " + job.getStatus() + ")" @@ -308,7 +308,7 @@ public void retryFailedJob(@Nonnull ExtensionScan scan, @Nonnull ScannerJob job) * @throws ErrorResultException if the scan is not terminal, has no jobs, * or has no failed jobs to retry. */ - public ExtensionScan retryFailedJobs(@Nonnull ExtensionScan scan) { + public ExtensionScan retryFailedJobs(@NonNull ExtensionScan scan) { if (!scan.getStatus().isCompleted()) { throw new ErrorResultException( String.format( @@ -424,7 +424,7 @@ private boolean isValidTransition(ScanStatus from, ScanStatus to) { }; } - public void removeScan(@Nonnull ExtensionScan scan) { + public void removeScan(@NonNull ExtensionScan scan) { persistenceService.removeScan(scan); } diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java b/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java index 4ee1cf07c..c507ec764 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java @@ -17,13 +17,13 @@ import org.eclipse.openvsx.migration.HandlerJobRequest; import org.jobrunr.jobs.annotations.Job; import org.jobrunr.jobs.lambdas.JobRequestHandler; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; -import jakarta.annotation.Nullable; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import redis.clients.jedis.RedisClusterClient; diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/HttpAuthHandler.java b/server/src/main/java/org/eclipse/openvsx/scanning/HttpAuthHandler.java index 62c769c51..dd56d1604 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/HttpAuthHandler.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/HttpAuthHandler.java @@ -12,6 +12,7 @@ ********************************************************************************/ package org.eclipse.openvsx.scanning; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; @@ -20,7 +21,6 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; -import jakarta.annotation.Nullable; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.Map; @@ -47,7 +47,7 @@ public class HttpAuthHandler { */ public HttpAuthHandler( String scannerName, - @Nullable RemoteScannerProperties.AuthConfig authConfig, + RemoteScannerProperties.@Nullable AuthConfig authConfig, RestTemplate restTemplate ) { this.scannerName = scannerName; diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java b/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java index 3e7dd1472..168a84c62 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java @@ -12,13 +12,14 @@ ********************************************************************************/ package org.eclipse.openvsx.scanning; -import jakarta.annotation.Nonnull; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.core5.util.Timeout; import org.eclipse.openvsx.util.TempFile; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.restclient.RestTemplateBuilder; @@ -31,7 +32,6 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; -import jakarta.annotation.Nullable; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -63,7 +63,7 @@ private HttpClientExecutor(RestTemplate restTemplate, @Nullable HttpAuthHandler */ public static HttpClientExecutor create( RemoteScannerProperties.HttpConfig httpConfig, - @Nullable RemoteScannerProperties.AuthConfig authConfig, + RemoteScannerProperties.@Nullable AuthConfig authConfig, String scannerName ) { logger.debug("Creating HTTP client for scanner '{}': socketTimeout={}ms, connectTimeout={}ms, auth={}", @@ -329,7 +329,7 @@ public TempFileResource(TempFile tempFile) { } @Override - public @Nonnull String getFilename() { + public @NonNull String getFilename() { return tempFile.getResource() != null ? tempFile.getResource().getName() : tempFile.getPath().toFile().getName(); } } diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/PublishCheck.java b/server/src/main/java/org/eclipse/openvsx/scanning/PublishCheck.java index 9e7231245..89250c32b 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/PublishCheck.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/PublishCheck.java @@ -15,8 +15,8 @@ import org.eclipse.openvsx.entities.ExtensionScan; import org.eclipse.openvsx.entities.UserData; import org.eclipse.openvsx.util.TempFile; +import org.jspecify.annotations.NonNull; -import jakarta.annotation.Nonnull; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -97,9 +97,9 @@ default String getUserFacingMessage(List failures) { * etc. to access extension metadata. */ record Context( - @Nonnull ExtensionScan scan, - @Nonnull TempFile extensionFile, - @Nonnull UserData user + @NonNull ExtensionScan scan, + @NonNull TempFile extensionFile, + @NonNull UserData user ) {} /** diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/PublishCheckRunner.java b/server/src/main/java/org/eclipse/openvsx/scanning/PublishCheckRunner.java index 74796f2e0..93a6acb20 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/PublishCheckRunner.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/PublishCheckRunner.java @@ -17,12 +17,12 @@ import org.eclipse.openvsx.entities.UserData; import org.eclipse.openvsx.util.TempFile; import org.eclipse.openvsx.util.TimeUtil; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -67,11 +67,11 @@ public int getCheckCount() { * This method does not persist anything. It only executes checks and returns findings. * The caller is responsible for recording failures and managing state. */ - @Nonnull + @NonNull public Result runChecks( - @Nonnull ExtensionScan scan, - @Nonnull TempFile extensionFile, - @Nonnull UserData user + @NonNull ExtensionScan scan, + @NonNull TempFile extensionFile, + @NonNull UserData user ) { var context = new PublishCheck.Context(scan, extensionFile, user); var allFindings = new ArrayList(); @@ -190,9 +190,9 @@ public Result runChecks( * Result of running all publish checks. */ public record Result( - @Nonnull List findings, - @Nonnull List checkExecutions, - @Nonnull List errors, + @NonNull List findings, + @NonNull List checkExecutions, + @NonNull List errors, boolean hasEnforcedFailure, boolean hasRequiredCheckError ) { @@ -215,7 +215,7 @@ public boolean hasError() { /** * Get errors from required checks (these block publishing). */ - @Nonnull + @NonNull public List getRequiredErrors() { return errors.stream() .filter(CheckError::required) @@ -225,7 +225,7 @@ public List getRequiredErrors() { /** * Get errors from non-required checks (these don't block publishing). */ - @Nonnull + @NonNull public List getNonRequiredErrors() { return errors.stream() .filter(e -> !e.required()) @@ -269,7 +269,7 @@ public String getErrorMessage() { /** * Get findings that are enforced (would block publication). */ - @Nonnull + @NonNull public List getEnforcedFindings() { return findings.stream() .filter(Finding::enforced) @@ -279,7 +279,7 @@ public List getEnforcedFindings() { /** * Get findings that are not enforced (warnings only). */ - @Nonnull + @NonNull public List getWarningFindings() { return findings.stream() .filter(f -> !f.enforced()) @@ -291,8 +291,8 @@ public List getWarningFindings() { * An error that occurred during a check execution. */ public record CheckError( - @Nonnull String checkType, - @Nonnull Exception exception, + @NonNull String checkType, + @NonNull Exception exception, boolean required ) {} @@ -301,8 +301,8 @@ public record CheckError( * Contains all info needed for the service to record the failure. */ public record Finding( - @Nonnull String checkType, - @Nonnull String ruleName, + @NonNull String checkType, + @NonNull String ruleName, @Nullable String reason, boolean enforced, @Nullable String userFacingMessage @@ -313,10 +313,10 @@ public record Finding( * Used to record all checks that were run for audit trail. */ public record CheckExecution( - @Nonnull String checkType, - @Nonnull LocalDateTime startedAt, - @Nonnull LocalDateTime completedAt, - @Nonnull ScanCheckResult.CheckResult result, + @NonNull String checkType, + @NonNull LocalDateTime startedAt, + @NonNull LocalDateTime completedAt, + ScanCheckResult.@NonNull CheckResult result, int findingsCount, @Nullable String errorMessage, @Nullable String summary, diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/RemoteScanner.java b/server/src/main/java/org/eclipse/openvsx/scanning/RemoteScanner.java index c50bb1ed9..4a7045380 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/RemoteScanner.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/RemoteScanner.java @@ -11,12 +11,11 @@ * SPDX-License-Identifier: EPL-2.0 ********************************************************************************/ package org.eclipse.openvsx.scanning; - +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; import java.io.File; import java.util.ArrayList; import java.util.HashMap; @@ -43,12 +42,12 @@ public class RemoteScanner implements Scanner { private final ScannerFileProvider scanFileService; public RemoteScanner( - @Nonnull String scannerName, - @Nonnull RemoteScannerProperties.ScannerConfig config, - @Nonnull HttpTemplateEngine templateEngine, - @Nonnull HttpClientExecutor httpExecutor, - @Nonnull HttpResponseExtractor responseExtractor, - @Nonnull ScannerFileProvider scanFileService + @NonNull String scannerName, + RemoteScannerProperties.@NonNull ScannerConfig config, + @NonNull HttpTemplateEngine templateEngine, + @NonNull HttpClientExecutor httpExecutor, + @NonNull HttpResponseExtractor responseExtractor, + @NonNull ScannerFileProvider scanFileService ) { this.scannerName = scannerName; this.config = config; @@ -59,7 +58,7 @@ public RemoteScanner( } @Override - @Nonnull + @NonNull public String getScannerType() { return config.getType(); } @@ -109,7 +108,7 @@ public String buildExternalUrl(@Nullable String externalJobId) { } return template.replace("{jobId}", externalJobId); } - + /** * Start a scan by sending the entire .vsix file to the scanner. *

@@ -117,8 +116,7 @@ public String buildExternalUrl(@Nullable String externalJobId) { * For async scanners: Extracts job ID from start response */ @Override - @Nonnull - public Scanner.Invocation startScan(@Nonnull Command command) throws ScannerException { + public Scanner.@NonNull Invocation startScan(@NonNull Command command) throws ScannerException { logger.debug("Starting {} scan for extension version {}", scannerName, command.extensionVersionId()); @@ -178,8 +176,8 @@ private Scanner.Invocation parseStartResponse( * to PollStatus. */ @Override - @Nonnull - public PollStatus pollStatus(@Nonnull Submission submission) throws ScannerException { + @NonNull + public PollStatus pollStatus(@NonNull Submission submission) throws ScannerException { if (!config.isAsync()) { throw new UnsupportedOperationException("Scanner is not async: " + scannerName); } @@ -216,15 +214,14 @@ public PollStatus pollStatus(@Nonnull Submission submission) throws ScannerExcep throw new ScannerException("Failed to poll scan status: " + e.getMessage(), e); } } - + /** * Retrieve results from a completed async scan. *

* Executes the configured result operation and parses threats. */ @Override - @Nonnull - public Scanner.Result fetchResults(@Nonnull Submission submission) throws ScannerException { + public Scanner.@NonNull Result fetchResults(@NonNull Submission submission) throws ScannerException { if (!config.isAsync()) { throw new UnsupportedOperationException("Scanner is not async: " + scannerName); } @@ -482,7 +479,7 @@ private String extractThreatField(Map threatObj, String path) { * Extract threat severity using path or expression. * Always returns a non-null value (defaults to "MEDIUM"). */ - @Nonnull + @NonNull private String extractThreatSeverity( Map threatObj, RemoteScannerProperties.ThreatMapping threatMapping diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/RemoteScannerProperties.java b/server/src/main/java/org/eclipse/openvsx/scanning/RemoteScannerProperties.java index d8359d7b4..422d57973 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/RemoteScannerProperties.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/RemoteScannerProperties.java @@ -12,11 +12,11 @@ ********************************************************************************/ package org.eclipse.openvsx.scanning; -import jakarta.annotation.Nullable; import jakarta.validation.Valid; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import org.jspecify.annotations.Nullable; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.validation.annotation.Validated; diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/Scanner.java b/server/src/main/java/org/eclipse/openvsx/scanning/Scanner.java index 01ea7ed45..aab5b2c7b 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/Scanner.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/Scanner.java @@ -12,8 +12,9 @@ ********************************************************************************/ package org.eclipse.openvsx.scanning; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -32,17 +33,17 @@ public interface Scanner { * Command to start a scan. Contains metadata about what to scan. * Scanners retrieve the actual file via extensionVersionId using ScannerFileService. */ - record Command(long extensionVersionId, @Nonnull String scanId) {} + record Command(long extensionVersionId, @NonNull String scanId) {} /** * Represents a scan that has been submitted to an external service. */ - record Submission(@Nonnull String externalJobId, @Nullable Map fileHashes) { - public Submission(@Nonnull String externalJobId) { + record Submission(@NonNull String externalJobId, @Nullable Map fileHashes) { + public Submission(@NonNull String externalJobId) { this(externalJobId, null); } - @Nonnull + @NonNull public Map fileHashes() { return fileHashes != null ? fileHashes : Collections.emptyMap(); } @@ -76,23 +77,23 @@ private Result(boolean clean, List threats, String summary) { this.summary = summary; } - @Nonnull + @NonNull public static Result clean() { return new Result(true, Collections.emptyList(), null); } - @Nonnull + @NonNull public static Result clean(@Nullable String summary) { return new Result(true, Collections.emptyList(), summary); } - @Nonnull - public static Result withThreats(@Nonnull List threats) { + @NonNull + public static Result withThreats(@NonNull List threats) { return new Result(false, threats, null); } - @Nonnull - public static Result withThreats(@Nonnull List threats, @Nullable String summary) { + @NonNull + public static Result withThreats(@NonNull List threats, @Nullable String summary) { return new Result(false, threats, summary); } @@ -100,7 +101,7 @@ public boolean isClean() { return clean; } - @Nonnull + @NonNull public List getThreats() { return Collections.unmodifiableList(threats); } @@ -122,15 +123,15 @@ class Threat { private final String filePath; private final String fileHash; - public Threat(@Nonnull String name, @Nullable String description, @Nonnull String severity) { + public Threat(@NonNull String name, @Nullable String description, @NonNull String severity) { this(name, description, severity, null, null); } - public Threat(@Nonnull String name, @Nullable String description, @Nonnull String severity, @Nullable String filePath) { + public Threat(@NonNull String name, @Nullable String description, @NonNull String severity, @Nullable String filePath) { this(name, description, severity, filePath, null); } - public Threat(@Nonnull String name, @Nullable String description, @Nonnull String severity, @Nullable String filePath, @Nullable String fileHash) { + public Threat(@NonNull String name, @Nullable String description, @NonNull String severity, @Nullable String filePath, @Nullable String fileHash) { this.name = name; this.description = description; this.severity = severity; @@ -138,9 +139,9 @@ public Threat(@Nonnull String name, @Nullable String description, @Nonnull Strin this.fileHash = fileHash; } - @Nonnull public String getName() { return name; } + @NonNull public String getName() { return name; } @Nullable public String getDescription() { return description; } - @Nonnull public String getSeverity() { return severity; } + @NonNull public String getSeverity() { return severity; } @Nullable public String getFilePath() { return filePath; } @Nullable public String getFileHash() { return fileHash; } } @@ -153,14 +154,14 @@ public Threat(@Nonnull String name, @Nullable String description, @Nonnull Strin * - Submitted: Async scan that requires polling */ sealed interface Invocation { - record Completed(@Nonnull Result result) implements Invocation {} - record Submitted(@Nonnull Submission submission) implements Invocation {} + record Completed(@NonNull Result result) implements Invocation {} + record Submitted(@NonNull Submission submission) implements Invocation {} } /** * Returns the unique type identifier for this scanner. */ - @Nonnull + @NonNull String getScannerType(); /** @@ -220,27 +221,26 @@ default String buildExternalUrl(@Nullable String externalJobId) { * Only relevant when maxConcurrency > 0. */ default int getMaxQueueWaitMinutes() { return 120; } - + /** * Get the polling configuration for this async scanner. * Returns null to use defaults. */ - @Nullable - default RemoteScannerProperties.PollConfig getPollConfig() { + default RemoteScannerProperties.@Nullable PollConfig getPollConfig() { return null; } /** * Start a scan and return the invocation result. */ - @Nonnull - Invocation startScan(@Nonnull Command command) throws ScannerException; + @NonNull + Invocation startScan(@NonNull Command command) throws ScannerException; /** * Poll status of an async scan job. */ - @Nonnull - default PollStatus pollStatus(@Nonnull Submission submission) throws ScannerException { + @NonNull + default PollStatus pollStatus(@NonNull Submission submission) throws ScannerException { throw new UnsupportedOperationException( "Scanner " + getScannerType() + " does not support polling" ); @@ -249,8 +249,8 @@ default PollStatus pollStatus(@Nonnull Submission submission) throws ScannerExce /** * Retrieve final results from an async scan job. */ - @Nonnull - default Result fetchResults(@Nonnull Submission submission) throws ScannerException { + @NonNull + default Result fetchResults(@NonNull Submission submission) throws ScannerException { throw new UnsupportedOperationException( "Scanner " + getScannerType() + " does not support result retrieval" ); diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/ScannerException.java b/server/src/main/java/org/eclipse/openvsx/scanning/ScannerException.java index 63e9e1766..24874cbea 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/ScannerException.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/ScannerException.java @@ -13,19 +13,19 @@ package org.eclipse.openvsx.scanning; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; /** * Exception thrown when a scan operation fails. */ public class ScannerException extends Exception { - public ScannerException(@Nonnull String message) { + public ScannerException(@NonNull String message) { super(message); } - public ScannerException(@Nonnull String message, @Nullable Throwable cause) { + public ScannerException(@NonNull String message, @Nullable Throwable cause) { super(message, cause); } } diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/ScannerFileProvider.java b/server/src/main/java/org/eclipse/openvsx/scanning/ScannerFileProvider.java index 15851a9d5..a0cf5c161 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/ScannerFileProvider.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/ScannerFileProvider.java @@ -19,11 +19,11 @@ import org.eclipse.openvsx.storage.StorageUtilService; import org.eclipse.openvsx.util.NamingUtil; import org.eclipse.openvsx.util.TempFile; +import org.jspecify.annotations.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; -import jakarta.annotation.Nonnull; import java.io.IOException; /** @@ -57,7 +57,7 @@ public ScannerFileProvider( * Downloads the .vsix file to a temp location. Use in try-with-resources * for automatic cleanup. */ - @Nonnull + @NonNull public TempFile getExtensionFile(long extensionVersionId) throws ScannerException { try { // Find the extension version using EntityManager diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/ScannerInvocationRequest.java b/server/src/main/java/org/eclipse/openvsx/scanning/ScannerInvocationRequest.java index d5f54a5b8..689eda0e0 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/ScannerInvocationRequest.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/ScannerInvocationRequest.java @@ -13,9 +13,8 @@ package org.eclipse.openvsx.scanning; -import jakarta.annotation.Nonnull; - import org.jobrunr.jobs.lambdas.JobRequest; +import org.jspecify.annotations.NonNull; /** * JobRunr request to invoke a scanner for an extension version. @@ -42,9 +41,9 @@ public ScannerInvocationRequest() {} * Create a scanner invocation request. */ public ScannerInvocationRequest( - @Nonnull String scannerType, + @NonNull String scannerType, long extensionVersionId, - @Nonnull String scanId + @NonNull String scanId ) { this.scannerType = scannerType; this.extensionVersionId = extensionVersionId; diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/ScannerRegistry.java b/server/src/main/java/org/eclipse/openvsx/scanning/ScannerRegistry.java index d3fc271e1..c385b2309 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/ScannerRegistry.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/ScannerRegistry.java @@ -11,11 +11,10 @@ * SPDX-License-Identifier: EPL-2.0 ********************************************************************************/ package org.eclipse.openvsx.scanning; - +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.springframework.stereotype.Component; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; import java.util.List; import java.util.Map; import java.util.Set; @@ -33,7 +32,7 @@ public class ScannerRegistry { /** * Register a scanner in the registry. */ - public void registerScanner(@Nonnull Scanner scanner) { + public void registerScanner(@NonNull Scanner scanner) { String type = scanner.getScannerType(); // Check for duplicate scanner types @@ -50,7 +49,7 @@ public void registerScanner(@Nonnull Scanner scanner) { * Unregister a scanner from the registry. */ @Nullable - public Scanner unregisterScanner(@Nonnull String scannerType) { + public Scanner unregisterScanner(@NonNull String scannerType) { return scanners.remove(scannerType); } @@ -59,7 +58,7 @@ public Scanner unregisterScanner(@Nonnull String scannerType) { * Used when scanner configuration changes at runtime. */ @Nullable - public Scanner replaceScanner(@Nonnull Scanner scanner) { + public Scanner replaceScanner(@NonNull Scanner scanner) { return scanners.put(scanner.getScannerType(), scanner); } @@ -67,21 +66,21 @@ public Scanner replaceScanner(@Nonnull Scanner scanner) { * Get a scanner by its type. */ @Nullable - public Scanner getScanner(@Nonnull String scannerType) { + public Scanner getScanner(@NonNull String scannerType) { return scanners.get(scannerType); } /** * Check if a scanner type is registered. */ - public boolean hasScanner(@Nonnull String scannerType) { + public boolean hasScanner(@NonNull String scannerType) { return scanners.containsKey(scannerType); } /** * Get all registered scanner types. */ - @Nonnull + @NonNull public Set getRegisteredTypes() { return Set.copyOf(scanners.keySet()); } @@ -89,7 +88,7 @@ public Set getRegisteredTypes() { /** * Get all registered scanners. */ - @Nonnull + @NonNull public List getAllScanners() { return List.copyOf(scanners.values()); } @@ -97,7 +96,7 @@ public List getAllScanners() { /** * Get all registered async scanners (those that require polling). */ - @Nonnull + @NonNull public List getAsyncScanners() { return scanners.values().stream() .filter(Scanner::isAsync) diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetector.java b/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetector.java index 7dec1fd2c..1a1a52410 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetector.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetector.java @@ -11,7 +11,7 @@ * SPDX-License-Identifier: EPL-2.0 ********************************************************************************/ package org.eclipse.openvsx.scanning; - +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.commons.lang3.StringUtils; @@ -19,7 +19,6 @@ import org.eclipse.openvsx.util.ArchiveUtil; import org.eclipse.openvsx.util.SizeLimitInputStream; import jakarta.validation.constraints.NotNull; -import jakarta.annotation.Nullable; import java.io.BufferedReader; import java.io.BufferedInputStream; diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetectorFactory.java b/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetectorFactory.java index 41aa2673b..306697b23 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetectorFactory.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetectorFactory.java @@ -15,7 +15,7 @@ import com.google.re2j.Pattern; import jakarta.annotation.PostConstruct; import jakarta.validation.constraints.NotNull; -import jakarta.annotation.Nullable; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -206,7 +206,7 @@ private AhoCorasick buildMatcher(java.util.Collection keywords) { * Combines extensions from the global allowlist in the YAML with extensions from config. */ private List getGlobalExcludedExtensions( - @Nullable SecretRuleLoader.GlobalAllowlist globalAllowlist, + SecretRuleLoader.@Nullable GlobalAllowlist globalAllowlist, @NotNull SecretDetectorConfig config) { List result = new ArrayList<>(); @@ -225,7 +225,7 @@ private List getGlobalExcludedExtensions( * Combines paths from the global allowlist in the YAML with paths from config. */ private List getGlobalExcludedPathPatterns( - @Nullable SecretRuleLoader.GlobalAllowlist globalAllowlist, + SecretRuleLoader.@Nullable GlobalAllowlist globalAllowlist, @NotNull SecretDetectorConfig config) { List result = new ArrayList<>(); @@ -243,7 +243,7 @@ private List getGlobalExcludedPathPatterns( * Uses regex patterns from the global allowlist in the YAML, falling back to config. */ private List getGlobalAllowlistPatterns( - @Nullable SecretRuleLoader.GlobalAllowlist globalAllowlist, + SecretRuleLoader.@Nullable GlobalAllowlist globalAllowlist, @NotNull SecretDetectorConfig config) { List result = new ArrayList<>(); @@ -261,7 +261,7 @@ private List getGlobalAllowlistPatterns( * Uses stopwords from the global allowlist in the YAML, falling back to config. */ private List getGlobalStopwords( - @Nullable SecretRuleLoader.GlobalAllowlist globalAllowlist, + SecretRuleLoader.@Nullable GlobalAllowlist globalAllowlist, @NotNull SecretDetectorConfig config) { List result = new ArrayList<>(); @@ -290,7 +290,7 @@ private List getSuppressionMarkers(@NotNull SecretDetectorConfig config) * Uses case-insensitive matching. */ private List getSkipMimeTypePatterns( - @Nullable SecretRuleLoader.GlobalAllowlist globalAllowlist) { + SecretRuleLoader.@Nullable GlobalAllowlist globalAllowlist) { List result = new ArrayList<>(); if (globalAllowlist != null && globalAllowlist.skipMimeTypes != null) { diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/SecretRule.java b/server/src/main/java/org/eclipse/openvsx/scanning/SecretRule.java index 466989f20..a06eefc24 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/SecretRule.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/SecretRule.java @@ -14,7 +14,7 @@ import com.google.re2j.Pattern; import jakarta.validation.constraints.NotNull; -import jakarta.annotation.Nullable; +import org.jspecify.annotations.Nullable; import java.util.List; diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/SecretRuleLoader.java b/server/src/main/java/org/eclipse/openvsx/scanning/SecretRuleLoader.java index 08e7d94e5..bd7f6725b 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/SecretRuleLoader.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/SecretRuleLoader.java @@ -16,7 +16,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import jakarta.validation.constraints.NotNull; -import jakarta.annotation.Nullable; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; diff --git a/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java b/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java index d9c81e219..aee21b284 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/ElasticSearchService.java @@ -15,7 +15,6 @@ import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery; import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders; import co.elastic.clients.util.ObjectBuilder; -import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.eclipse.openvsx.entities.Extension; import org.eclipse.openvsx.migration.HandlerJobRequest; @@ -26,6 +25,7 @@ import org.eclipse.openvsx.util.TargetPlatform; import org.jobrunr.scheduling.JobRequestScheduler; import org.jobrunr.scheduling.cron.Cron; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; diff --git a/server/src/main/java/org/eclipse/openvsx/search/ExtensionSearch.java b/server/src/main/java/org/eclipse/openvsx/search/ExtensionSearch.java index 6f6651f5f..9c46ce682 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/ExtensionSearch.java +++ b/server/src/main/java/org/eclipse/openvsx/search/ExtensionSearch.java @@ -9,11 +9,11 @@ ********************************************************************************/ package org.eclipse.openvsx.search; +import org.jspecify.annotations.Nullable; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; -import jakarta.annotation.Nullable; import java.util.List; import java.util.Objects; diff --git a/server/src/main/java/org/eclipse/openvsx/search/RelevanceService.java b/server/src/main/java/org/eclipse/openvsx/search/RelevanceService.java index 5be4378b0..9300c5b3d 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/RelevanceService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/RelevanceService.java @@ -12,12 +12,12 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.annotation.Nullable; import org.eclipse.openvsx.entities.Extension; import org.eclipse.openvsx.entities.ExtensionVersion; import org.eclipse.openvsx.repositories.RepositoryService; import org.eclipse.openvsx.util.NamingUtil; import org.eclipse.openvsx.util.TimeUtil; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; diff --git a/server/src/main/java/org/eclipse/openvsx/search/SimilarityCheckService.java b/server/src/main/java/org/eclipse/openvsx/search/SimilarityCheckService.java index 2fe021646..cb4fd64da 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/SimilarityCheckService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/SimilarityCheckService.java @@ -18,12 +18,12 @@ import org.eclipse.openvsx.entities.NamespaceMembership; import org.eclipse.openvsx.entities.UserData; import org.eclipse.openvsx.repositories.RepositoryService; -import org.eclipse.openvsx.scanning.PublishCheck; +import org.eclipse.openvsx.scanning.PublishCheck; +import org.jspecify.annotations.Nullable; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Service; -import jakarta.annotation.Nullable; import java.util.List; /** diff --git a/server/src/main/java/org/eclipse/openvsx/search/SimilarityService.java b/server/src/main/java/org/eclipse/openvsx/search/SimilarityService.java index dda437d55..47d26f620 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/SimilarityService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/SimilarityService.java @@ -16,13 +16,13 @@ import org.eclipse.openvsx.entities.Extension; import org.eclipse.openvsx.entities.Namespace; import org.eclipse.openvsx.repositories.RepositoryService; -import org.eclipse.openvsx.util.ErrorResultException; +import org.eclipse.openvsx.util.ErrorResultException; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.List; -import jakarta.annotation.Nullable; @Service public class SimilarityService { diff --git a/server/src/main/java/org/eclipse/openvsx/settings/SettingsService.java b/server/src/main/java/org/eclipse/openvsx/settings/SettingsService.java index 9677b097c..e2d23c09f 100644 --- a/server/src/main/java/org/eclipse/openvsx/settings/SettingsService.java +++ b/server/src/main/java/org/eclipse/openvsx/settings/SettingsService.java @@ -12,12 +12,12 @@ *****************************************************************************/ package org.eclipse.openvsx.settings; -import jakarta.annotation.Nullable; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import org.apache.logging.log4j.util.Strings; import org.eclipse.openvsx.cache.jedis.JedisClusterChannelListener; import org.eclipse.openvsx.json.SettingsJson; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; diff --git a/server/src/main/java/org/eclipse/openvsx/storage/AwsStorageService.java b/server/src/main/java/org/eclipse/openvsx/storage/AwsStorageService.java index 3bd2a6b16..a0cfcdc70 100644 --- a/server/src/main/java/org/eclipse/openvsx/storage/AwsStorageService.java +++ b/server/src/main/java/org/eclipse/openvsx/storage/AwsStorageService.java @@ -9,7 +9,6 @@ ********************************************************************************/ package org.eclipse.openvsx.storage; -import jakarta.annotation.Nullable; import jakarta.annotation.PreDestroy; import org.apache.commons.lang3.StringUtils; import org.eclipse.openvsx.cache.FilesCacheKeyGenerator; @@ -18,6 +17,7 @@ import org.eclipse.openvsx.util.FileUtil; import org.eclipse.openvsx.util.HttpHeadersUtil; import org.eclipse.openvsx.util.TempFile; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.util.Pair; diff --git a/server/src/main/java/org/eclipse/openvsx/storage/CdnServiceConfig.java b/server/src/main/java/org/eclipse/openvsx/storage/CdnServiceConfig.java index 74fda788e..ffc5b010e 100644 --- a/server/src/main/java/org/eclipse/openvsx/storage/CdnServiceConfig.java +++ b/server/src/main/java/org/eclipse/openvsx/storage/CdnServiceConfig.java @@ -10,10 +10,10 @@ package org.eclipse.openvsx.storage; import com.google.common.collect.ImmutableMap; +import org.jspecify.annotations.Nullable; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; -import jakarta.annotation.Nullable; import java.util.HashMap; import java.util.Map; diff --git a/server/src/main/java/org/eclipse/openvsx/storage/IStorageService.java b/server/src/main/java/org/eclipse/openvsx/storage/IStorageService.java index f0e4a2879..32f15088a 100644 --- a/server/src/main/java/org/eclipse/openvsx/storage/IStorageService.java +++ b/server/src/main/java/org/eclipse/openvsx/storage/IStorageService.java @@ -14,9 +14,9 @@ import org.eclipse.openvsx.entities.Namespace; import org.eclipse.openvsx.util.TempFile; import org.eclipse.openvsx.util.UrlUtil; +import org.jspecify.annotations.Nullable; import org.springframework.data.util.Pair; -import jakarta.annotation.Nullable; import java.io.IOException; import java.net.URI; import java.nio.file.Path; diff --git a/server/src/main/java/org/eclipse/openvsx/storage/log/FastlyLogFileParser.java b/server/src/main/java/org/eclipse/openvsx/storage/log/FastlyLogFileParser.java index f7d1e135d..100114f03 100644 --- a/server/src/main/java/org/eclipse/openvsx/storage/log/FastlyLogFileParser.java +++ b/server/src/main/java/org/eclipse/openvsx/storage/log/FastlyLogFileParser.java @@ -19,10 +19,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import jakarta.annotation.Nullable; import java.io.IOException; class FastlyLogFileParser implements LogFileParser { diff --git a/server/src/main/java/org/eclipse/openvsx/storage/log/LogFileParser.java b/server/src/main/java/org/eclipse/openvsx/storage/log/LogFileParser.java index 4a31da619..68d8bf462 100644 --- a/server/src/main/java/org/eclipse/openvsx/storage/log/LogFileParser.java +++ b/server/src/main/java/org/eclipse/openvsx/storage/log/LogFileParser.java @@ -12,7 +12,7 @@ *****************************************************************************/ package org.eclipse.openvsx.storage.log; -import jakarta.annotation.Nullable; +import org.jspecify.annotations.Nullable; public interface LogFileParser { @Nullable LogRecord parse(String line); diff --git a/server/src/main/java/org/eclipse/openvsx/storage/log/LogRecord.java b/server/src/main/java/org/eclipse/openvsx/storage/log/LogRecord.java index 18e4febe8..39cbe7fc1 100644 --- a/server/src/main/java/org/eclipse/openvsx/storage/log/LogRecord.java +++ b/server/src/main/java/org/eclipse/openvsx/storage/log/LogRecord.java @@ -12,6 +12,6 @@ *****************************************************************************/ package org.eclipse.openvsx.storage.log; -import jakarta.annotation.Nonnull; +import org.jspecify.annotations.NonNull; -public record LogRecord(@Nonnull String method, int status, @Nonnull String url) {} +public record LogRecord(@NonNull String method, int status, @NonNull String url) {} diff --git a/server/src/main/java/org/eclipse/openvsx/util/ArchiveUtil.java b/server/src/main/java/org/eclipse/openvsx/util/ArchiveUtil.java index e885e848f..c838acf1d 100644 --- a/server/src/main/java/org/eclipse/openvsx/util/ArchiveUtil.java +++ b/server/src/main/java/org/eclipse/openvsx/util/ArchiveUtil.java @@ -9,9 +9,9 @@ ********************************************************************************/ package org.eclipse.openvsx.util; -import jakarta.annotation.Nullable; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.jspecify.annotations.Nullable; import java.io.IOException; import java.nio.file.Files; diff --git a/server/src/main/java/org/eclipse/openvsx/util/HttpHeadersUtil.java b/server/src/main/java/org/eclipse/openvsx/util/HttpHeadersUtil.java index 7a026fd3f..1e9275eab 100644 --- a/server/src/main/java/org/eclipse/openvsx/util/HttpHeadersUtil.java +++ b/server/src/main/java/org/eclipse/openvsx/util/HttpHeadersUtil.java @@ -12,11 +12,10 @@ *****************************************************************************/ package org.eclipse.openvsx.util; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; import org.apache.tika.Tika; import org.eclipse.openvsx.storage.StorageUtil; import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.springframework.http.ContentDisposition; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -82,12 +81,12 @@ public static HttpHeaders getAcceptJsonHeaders() { return headers; } - public static HttpHeaders createFileResponseHeaders(@Nonnull Path file) { + public static HttpHeaders createFileResponseHeaders(@NonNull Path file) { var fileName = file.getFileName().toString(); return createFileResponseHeaders(file, fileName); } - public static HttpHeaders createFileResponseHeaders(@Nonnull Path file, @Nullable String fileName) { + public static HttpHeaders createFileResponseHeaders(@NonNull Path file, @Nullable String fileName) { try (var inputStream = Files.newInputStream(file)) { return createFileResponseHeaders(inputStream, fileName); } catch (IOException ex) { diff --git a/server/src/main/java/org/eclipse/openvsx/util/LogService.java b/server/src/main/java/org/eclipse/openvsx/util/LogService.java index be023edb5..aa4a13509 100644 --- a/server/src/main/java/org/eclipse/openvsx/util/LogService.java +++ b/server/src/main/java/org/eclipse/openvsx/util/LogService.java @@ -12,12 +12,12 @@ *****************************************************************************/ package org.eclipse.openvsx.util; -import jakarta.annotation.Nonnull; import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; import org.eclipse.openvsx.entities.PersistedLog; import org.eclipse.openvsx.entities.UserData; import org.eclipse.openvsx.json.ResultJson; +import org.jspecify.annotations.NonNull; import org.springframework.stereotype.Service; import java.util.Objects; @@ -32,7 +32,7 @@ public LogService(EntityManager entityManager) { } @Transactional - public void logAction(@Nonnull UserData user, @Nonnull ResultJson result) { + public void logAction(@NonNull UserData user, @NonNull ResultJson result) { Objects.requireNonNull(user); Objects.requireNonNull(result); diff --git a/server/src/main/java/org/eclipse/openvsx/util/UrlUtil.java b/server/src/main/java/org/eclipse/openvsx/util/UrlUtil.java index a36b8c20a..15f3b5edc 100644 --- a/server/src/main/java/org/eclipse/openvsx/util/UrlUtil.java +++ b/server/src/main/java/org/eclipse/openvsx/util/UrlUtil.java @@ -9,12 +9,12 @@ ********************************************************************************/ package org.eclipse.openvsx.util; -import jakarta.annotation.Nonnull; import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.eclipse.openvsx.entities.ExtensionVersion; import org.eclipse.openvsx.json.ExtensionJson; +import org.jspecify.annotations.NonNull; import org.springframework.data.util.Pair; import org.springframework.util.AntPathMatcher; import org.springframework.web.context.request.RequestContextHolder; @@ -22,7 +22,6 @@ import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.util.UriUtils; -import jakarta.annotation.Nullable; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Arrays; @@ -95,7 +94,7 @@ private static String[] createApiVersionSegments(String namespaceName, String ex /** * Create a URL pointing to an API path. */ - public static @Nonnull String createApiUrl(String baseUrl, String... segments) { + public static @NonNull String createApiUrl(String baseUrl, String... segments) { if (Arrays.stream(segments).anyMatch(Objects::isNull)) { throw new IllegalArgumentException("Argument to createApiUrl has been null"); } diff --git a/server/src/main/java/org/eclipse/openvsx/util/XmlUtil.java b/server/src/main/java/org/eclipse/openvsx/util/XmlUtil.java index f3c893198..987f79da0 100644 --- a/server/src/main/java/org/eclipse/openvsx/util/XmlUtil.java +++ b/server/src/main/java/org/eclipse/openvsx/util/XmlUtil.java @@ -12,7 +12,7 @@ *****************************************************************************/ package org.eclipse.openvsx.util; -import jakarta.annotation.Nullable; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; diff --git a/server/src/main/java/org/eclipse/openvsx/web/ServerExceptionResolver.java b/server/src/main/java/org/eclipse/openvsx/web/ServerExceptionResolver.java index d2cdf3952..86edf8ff3 100644 --- a/server/src/main/java/org/eclipse/openvsx/web/ServerExceptionResolver.java +++ b/server/src/main/java/org/eclipse/openvsx/web/ServerExceptionResolver.java @@ -13,13 +13,12 @@ package org.eclipse.openvsx.web; import jakarta.servlet.http.HttpServletRequest; +import org.jspecify.annotations.NonNull; import org.springframework.core.Ordered; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; -import jakarta.annotation.Nonnull; - @RestControllerAdvice public class ServerExceptionResolver extends DefaultHandlerExceptionResolver { @@ -29,7 +28,7 @@ public ServerExceptionResolver() { } @Override - protected void logException(@Nonnull Exception ex, @Nonnull HttpServletRequest request) { + protected void logException(@NonNull Exception ex, @NonNull HttpServletRequest request) { // do not log HttpMediaTypeNotSupportedException, see https://github.com/eclipse/openvsx/issues/1505 // this just pollutes the server logs but bringing no added value if (!(ex instanceof HttpMediaTypeNotSupportedException)) { diff --git a/server/src/test/java/org/eclipse/openvsx/scanning/ScannerTest.java b/server/src/test/java/org/eclipse/openvsx/scanning/ScannerTest.java index c8c23181a..e242f213e 100644 --- a/server/src/test/java/org/eclipse/openvsx/scanning/ScannerTest.java +++ b/server/src/test/java/org/eclipse/openvsx/scanning/ScannerTest.java @@ -12,6 +12,7 @@ ********************************************************************************/ package org.eclipse.openvsx.scanning; +import org.jspecify.annotations.NonNull; import org.junit.jupiter.api.Test; import java.util.List; @@ -125,9 +126,9 @@ void scanner_buildExternalUrlDefaultsToNull() { // checks, self-hosted services without a dashboard). They simply don't // contribute a "View in scanner" deep link. Scanner scanner = new Scanner() { - @Override public @jakarta.annotation.Nonnull String getScannerType() { return "TEST"; } + @Override public @NonNull String getScannerType() { return "TEST"; } @Override public boolean isAsync() { return false; } - @Override public @jakarta.annotation.Nonnull Scanner.Invocation startScan(@jakarta.annotation.Nonnull Command command) { throw new UnsupportedOperationException(); } + @Override public Scanner.@NonNull Invocation startScan(@NonNull Command command) { throw new UnsupportedOperationException(); } }; assertNull(scanner.buildExternalUrl("any-job-id")); From 753fc004c773b2cb6a6e465104001d5b45198daf Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Fri, 26 Jun 2026 23:40:25 +0200 Subject: [PATCH 13/13] convert to jackson 3, jspecify --- server/build.gradle | 1 + server/gradle/libs.versions.toml | 1 + .../eclipse/openvsx/ExtensionProcessor.java | 96 +++++++++---------- .../eclipse/openvsx/ExtensionValidator.java | 9 +- .../eclipse/openvsx/UpstreamProxyService.java | 19 ++-- .../ScheduleTokenExpirationJobs.java | 2 - .../adapter/UpstreamVSCodeService.java | 2 +- .../eclipse/openvsx/adapter/VSCodeAPI.java | 13 ++- .../eclipse/openvsx/cache/CacheConfig.java | 23 +++-- .../bucket4j/JedisClusterCacheListener.java | 8 +- .../bucket4j/JedisClusterCacheManager.java | 10 +- .../openvsx/eclipse/EclipseProfile.java | 24 ++--- .../openvsx/eclipse/EclipseService.java | 36 ++++--- .../openvsx/eclipse/EclipseTokenService.java | 13 ++- .../eclipse/PublisherAgreementResponse.java | 2 +- .../eclipse/PublisherComplianceChecker.java | 3 +- .../openvsx/eclipse/SignAgreementParam.java | 2 +- .../openvsx/entities/AuthTokenConverter.java | 21 ++-- .../ExtensionControlJobRequestHandler.java | 10 +- .../ExtensionControlService.java | 20 ++-- .../RegistryObservationConvention.java | 9 +- .../ExtensionVersionIntegrityService.java | 4 +- .../openvsx/ratelimit/ResolvedIdentity.java | 3 +- .../eclipse/openvsx/scanning/AhoCorasick.java | 15 +-- .../openvsx/scanning/EntropyCalculator.java | 1 - .../ExtensionScanCompletionService.java | 1 - .../scanning/ExtensionScanService.java | 2 +- .../scanning/GitleaksRulesService.java | 12 +-- .../openvsx/scanning/HttpClientExecutor.java | 1 - .../scanning/HttpResponseExtractor.java | 21 ++-- .../openvsx/scanning/HttpTemplateEngine.java | 1 - .../openvsx/scanning/PublishCheckRunner.java | 2 +- .../openvsx/scanning/RemoteScanner.java | 1 + .../openvsx/scanning/ScannerException.java | 1 - .../openvsx/scanning/ScannerFileProvider.java | 1 - .../scanning/ScannerInvocationRequest.java | 2 - .../openvsx/scanning/ScannerPollHandler.java | 1 - .../openvsx/scanning/ScannerPollRequest.java | 1 - .../openvsx/scanning/ScannerRegistry.java | 1 + .../openvsx/scanning/SecretCheckService.java | 5 +- .../openvsx/scanning/SecretDetector.java | 89 +++++++++-------- .../scanning/SecretDetectorConfig.java | 10 +- .../scanning/SecretDetectorFactory.java | 32 +++---- .../eclipse/openvsx/scanning/SecretRule.java | 24 ++--- .../openvsx/scanning/SecretRuleLoader.java | 58 +++-------- .../openvsx/search/DatabaseSearchService.java | 1 - .../openvsx/search/RelevanceService.java | 18 ++-- .../eclipse/openvsx/search/SearchConfig.java | 2 +- .../eclipse/openvsx/search/SearchResult.java | 4 +- .../openvsx/search/SearchUtilService.java | 6 +- .../search/SimilarityCheckService.java | 17 ++-- .../openvsx/search/SimilarityConfig.java | 21 ++-- .../openvsx/search/SimilarityService.java | 15 ++- .../storage/log/FastlyLogFileParser.java | 39 +++----- .../org/eclipse/openvsx/RegistryAPITest.java | 22 ++--- .../java/org/eclipse/openvsx/UserAPITest.java | 20 ++-- .../eclipse/openvsx/admin/AdminAPITest.java | 19 ++-- .../scanning/ResponseExtractorTest.java | 4 +- .../scanning/SecretDetectorFactoryTest.java | 3 +- .../scanning/SecretRuleLoaderTest.java | 54 +++++------ 60 files changed, 391 insertions(+), 467 deletions(-) diff --git a/server/build.gradle b/server/build.gradle index 5659facca..38a98f8be 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -108,6 +108,7 @@ dependencies { implementation libs.springdoc + implementation libs.jackson.dataformat.yaml implementation libs.jackson.dataformat.xml implementation libs.jackson.dataformat.toml diff --git a/server/gradle/libs.versions.toml b/server/gradle/libs.versions.toml index f1e0ee253..902cf7a0b 100644 --- a/server/gradle/libs.versions.toml +++ b/server/gradle/libs.versions.toml @@ -48,6 +48,7 @@ gatling-app = { module = "io.gatling:gatling-app", version.ref gatling-core = { module = "io.gatling:gatling-core", version.ref = "gatling" } google-cloud-storage = { module = "com.google.cloud:google-cloud-storage", version.ref = "gcloud" } ipaddress = { module = "com.github.seancfoley:ipaddress", version.ref = "ipaddress" } +jackson-dataformat-yaml = { module = "tools.jackson.dataformat:jackson-dataformat-yaml" } jackson-dataformat-xml = { module = "tools.jackson.dataformat:jackson-dataformat-xml" } jackson-dataformat-toml = { module = "tools.jackson.dataformat:jackson-dataformat-toml" } jaxb-api = { module = "javax.xml.bind:jaxb-api", version.ref = "jaxb-api" } diff --git a/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java b/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java index b4e0ae534..eb8792a34 100644 --- a/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java +++ b/server/src/main/java/org/eclipse/openvsx/ExtensionProcessor.java @@ -9,11 +9,11 @@ ********************************************************************************/ package org.eclipse.openvsx; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.MissingNode; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.node.MissingNode; +import tools.jackson.dataformat.xml.XmlMapper; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.eclipse.openvsx.adapter.ExtensionQueryResult; @@ -99,9 +99,10 @@ private void loadPackageJson() { throw new ErrorResultException(entryNotFoundMessage(PACKAGE_JSON)); } - var mapper = new ObjectMapper(); - packageJson = mapper.readTree(entryFile.getPath().toFile()); - } catch (JsonParseException exc) { + try (var stream = Files.newInputStream(entryFile.getPath())) { + packageJson = new JsonMapper().readTree(stream); + } + } catch (JacksonException exc) { throw new ErrorResultException("Invalid JSON format in " + PACKAGE_JSON + ": " + exc.getMessage()); } catch (IOException exc) { @@ -122,9 +123,10 @@ private void loadVsixManifest() { throw new ErrorResultException(entryNotFoundMessage(VSIX_MANIFEST)); } - var mapper = new XmlMapper(); - vsixManifest = mapper.readTree(entryFile.getPath().toFile()); - } catch (JsonParseException exc) { + try (var stream = Files.newInputStream(entryFile.getPath())) { + vsixManifest = new XmlMapper().readTree(stream); + } + } catch (JacksonException exc) { throw new ErrorResultException("Invalid JSON format in " + VSIX_MANIFEST + ": " + exc.getMessage()); } catch (IOException exc) { @@ -139,7 +141,7 @@ private String entryNotFoundMessage(String file) { private JsonNode findByIdInArray(Iterable iter, String id) { for(JsonNode node : iter){ var idNode = node.get("Id"); - if(idNode != null && idNode.asText().equals(id)){ + if(idNode != null && idNode.asString().equals(id)){ return node; } } @@ -148,72 +150,72 @@ private JsonNode findByIdInArray(Iterable iter, String id) { public String getExtensionName() { loadVsixManifest(); - return vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_IDENTITY).path("Id").asText(); + return vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_IDENTITY).path("Id").asString(); } public String getNamespace() { loadVsixManifest(); - return vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_IDENTITY).path("Publisher").asText(); + return vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_IDENTITY).path("Publisher").asString(); } public List getExtensionDependencies() { loadVsixManifest(); var extDepenNode = findByIdInArray(vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_PROPERTIES).path(MANIFEST_PROPERTY), "Microsoft.VisualStudio.Code.ExtensionDependencies"); - return asStringList(extDepenNode.path(MANIFEST_VALUE).asText(), ","); + return asStringList(extDepenNode.path(MANIFEST_VALUE).asString(), ","); } public List getBundledExtensions() { loadVsixManifest(); var extPackNode = findByIdInArray(vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_PROPERTIES).path(MANIFEST_PROPERTY), "Microsoft.VisualStudio.Code.ExtensionPack"); - return asStringList(extPackNode.path(MANIFEST_VALUE).asText(), ","); + return asStringList(extPackNode.path(MANIFEST_VALUE).asString(), ","); } public List getExtensionKinds() { loadVsixManifest(); var extKindNode = findByIdInArray(vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_PROPERTIES).path(MANIFEST_PROPERTY), "Microsoft.VisualStudio.Code.ExtensionKind"); - return asStringList(extKindNode.path(MANIFEST_VALUE).asText(), ","); + return asStringList(extKindNode.path(MANIFEST_VALUE).asString(), ","); } public String getHomepage() { loadVsixManifest(); var extKindNode = findByIdInArray(vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_PROPERTIES).path(MANIFEST_PROPERTY), "Microsoft.VisualStudio.Services.Links.Learn"); - return extKindNode.path(MANIFEST_VALUE).asText(); + return extKindNode.path(MANIFEST_VALUE).asString(); } public String getRepository() { loadVsixManifest(); var sourceNode = findByIdInArray(vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_PROPERTIES).path(MANIFEST_PROPERTY), "Microsoft.VisualStudio.Services.Links.Source"); - return sourceNode.path(MANIFEST_VALUE).asText(); + return sourceNode.path(MANIFEST_VALUE).asString(); } public String getBugs() { loadVsixManifest(); var supportNode = findByIdInArray(vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_PROPERTIES).path(MANIFEST_PROPERTY), "Microsoft.VisualStudio.Services.Links.Support"); - return supportNode.path(MANIFEST_VALUE).asText(); + return supportNode.path(MANIFEST_VALUE).asString(); } public String getGalleryColor() { loadVsixManifest(); var colorNode = findByIdInArray(vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_PROPERTIES).path(MANIFEST_PROPERTY), "Microsoft.VisualStudio.Services.Branding.Color"); - return colorNode.path(MANIFEST_VALUE).asText(); + return colorNode.path(MANIFEST_VALUE).asString(); } public String getGalleryTheme() { loadVsixManifest(); var themeNode = findByIdInArray(vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_PROPERTIES).path(MANIFEST_PROPERTY), "Microsoft.VisualStudio.Services.Branding.Theme"); - return themeNode.path(MANIFEST_VALUE).asText(); + return themeNode.path(MANIFEST_VALUE).asString(); } public List getLocalizedLanguages() { loadVsixManifest(); var languagesNode = findByIdInArray(vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_PROPERTIES).path(MANIFEST_PROPERTY), "Microsoft.VisualStudio.Code.LocalizedLanguages"); - return asStringList(languagesNode.path(MANIFEST_VALUE).asText(), ","); + return asStringList(languagesNode.path(MANIFEST_VALUE).asString(), ","); } public boolean isPreview() { loadVsixManifest(); var galleryFlags = vsixManifest.path(MANIFEST_METADATA).path("GalleryFlags"); - return asStringList(galleryFlags.asText(), " ").contains("Preview"); + return asStringList(galleryFlags.asString(), " ").contains("Preview"); } public boolean isPreRelease() { @@ -225,7 +227,7 @@ public boolean isPreRelease() { public String getSponsorLink() { loadVsixManifest(); var sponsorLinkNode = findByIdInArray(vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_PROPERTIES).path(MANIFEST_PROPERTY), "Microsoft.VisualStudio.Code.SponsorLink"); - return sponsorLinkNode.path(MANIFEST_VALUE).asText(); + return sponsorLinkNode.path(MANIFEST_VALUE).asString(); } public ExtensionVersion getMetadata() { @@ -236,25 +238,25 @@ public ExtensionVersion getMetadata() { extVersion.setTargetPlatform(getTargetPlatform()); extVersion.setPreview(isPreview()); extVersion.setPreRelease(isPreRelease()); - var displayName = vsixManifest.path(MANIFEST_METADATA).path("DisplayName").asText(); + var displayName = vsixManifest.path(MANIFEST_METADATA).path("DisplayName").asString(); if(StringUtils.isNotBlank(displayName)) { extVersion.setDisplayName(displayName); } - extVersion.setDescription(vsixManifest.path(MANIFEST_METADATA).path("Description").path("").asText()); + extVersion.setDescription(vsixManifest.path(MANIFEST_METADATA).path("Description").path("").asString()); extVersion.setEngines(getEngines(packageJson.path("engines"))); - extVersion.setCategories(asStringList(vsixManifest.path(MANIFEST_METADATA).path("Categories").asText(), ",")); + extVersion.setCategories(asStringList(vsixManifest.path(MANIFEST_METADATA).path("Categories").asString(), ",")); extVersion.setExtensionKind(getExtensionKinds()); extVersion.setTags(getTags()); - extVersion.setLicense(packageJson.path("license").textValue()); + extVersion.setLicense(packageJson.path("license").stringValue(null)); extVersion.setHomepage(getHomepage()); extVersion.setRepository(getRepository()); extVersion.setSponsorLink(getSponsorLink()); extVersion.setBugs(getBugs()); - extVersion.setMarkdown(packageJson.path("markdown").textValue()); + extVersion.setMarkdown(packageJson.path("markdown").stringValue(null)); extVersion.setGalleryColor(getGalleryColor()); extVersion.setGalleryTheme(getGalleryTheme()); extVersion.setLocalizedLanguages(getLocalizedLanguages()); - extVersion.setQna(packageJson.path("qna").textValue()); + extVersion.setQna(packageJson.path("qna").stringValue(null)); return extVersion; } @@ -262,20 +264,20 @@ public ExtensionVersion getMetadata() { public PackageMetadata getPackageMetadata() { loadPackageJson(); return new PackageMetadata( - packageJson.path("publisher").asText(), - packageJson.path("name").asText(), - packageJson.path("version").asText(), - packageJson.path("displayName").asText() + packageJson.path("publisher").asString(), + packageJson.path("name").asString(), + packageJson.path("version").asString(), + packageJson.path("displayName").asString() ); } public String getVersion() { - return vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_IDENTITY).path("Version").asText(); + return vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_IDENTITY).path("Version").asString(); } public String getTargetPlatform() { loadVsixManifest(); - var targetPlatform = vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_IDENTITY).path("TargetPlatform").asText(); + var targetPlatform = vsixManifest.path(MANIFEST_METADATA).path(MANIFEST_IDENTITY).path("TargetPlatform").asString(); if (targetPlatform.isEmpty()) { targetPlatform = TargetPlatform.NAME_UNIVERSAL; } @@ -285,7 +287,7 @@ public String getTargetPlatform() { public String getDisplayName() { loadVsixManifest(); - var displayName = vsixManifest.path(MANIFEST_METADATA).path("DisplayName").asText(); + var displayName = vsixManifest.path(MANIFEST_METADATA).path("DisplayName").asString(); if (StringUtils.isBlank(displayName)) { return getExtensionName(); } @@ -293,7 +295,7 @@ public String getDisplayName() { } private List getTags() { - var tags = vsixManifest.path(MANIFEST_METADATA).path("Tags").asText(); + var tags = vsixManifest.path(MANIFEST_METADATA).path("Tags").asString(); return asStringList(tags, ",").stream() .collect(Collectors.groupingBy(String::toLowerCase)) .entrySet().stream() @@ -313,10 +315,8 @@ private List asStringList(String value, String sep){ private List getEngines(JsonNode node) { if (node.isObject()) { var result = new ArrayList(); - var fieldIter = node.fields(); - while (fieldIter.hasNext()) { - var entry = fieldIter.next(); - result.add(entry.getKey() + "@" + entry.getValue().textValue()); + for (Map.Entry entry : node.properties()) { + result.add(entry.getKey() + "@" + entry.getValue().stringValue()); } return result; } @@ -464,7 +464,7 @@ private TempFile readFromAlternateNames(String[] names) throws IOException { private String tryGetLicensePath() { loadVsixManifest(); - var licensePath = vsixManifest.path(MANIFEST_METADATA).path("License").asText(); + var licensePath = vsixManifest.path(MANIFEST_METADATA).path("License").asString(); return licensePath.isEmpty() ? tryGetAssetPath(ExtensionQueryResult.ExtensionFile.FILE_LICENSE) : licensePath; @@ -473,9 +473,9 @@ private String tryGetLicensePath() { private String tryGetAssetPath(String type) { loadVsixManifest(); for(var asset : vsixManifest.path("Assets").path("Asset")) { - if(Optional.ofNullable(asset.findValue("Type")).map(JsonNode::asText).orElse("").equals(type)) { + if(Optional.ofNullable(asset.findValue("Type")).map(JsonNode::asString).orElse("").equals(type)) { return Optional.ofNullable(asset.findValue("Path")) - .map(JsonNode::asText) + .map(JsonNode::asString) .orElse(null); } } @@ -489,8 +489,8 @@ private String tryGetAssetPath(String type) { if (StringUtils.isEmpty(iconPath)) { loadPackageJson(); var iconPathNode = packageJson.get("icon"); - iconPath = iconPathNode != null && iconPathNode.isTextual() - ? iconPathNode.asText().replace('\\', '/') + iconPath = iconPathNode != null && iconPathNode.isString() + ? iconPathNode.asString().replace('\\', '/') : null; if (StringUtils.isEmpty(iconPath)) { diff --git a/server/src/main/java/org/eclipse/openvsx/ExtensionValidator.java b/server/src/main/java/org/eclipse/openvsx/ExtensionValidator.java index c317097cc..5e35f1f07 100644 --- a/server/src/main/java/org/eclipse/openvsx/ExtensionValidator.java +++ b/server/src/main/java/org/eclipse/openvsx/ExtensionValidator.java @@ -18,7 +18,8 @@ import org.springframework.stereotype.Component; import java.net.MalformedURLException; -import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import java.util.*; import java.util.function.Predicate; import java.util.regex.Pattern; @@ -105,7 +106,7 @@ public Optional validateExtensionVersion(String version) { checkVersion(version, issues); return issues.isEmpty() ? Optional.empty() - : Optional.of(issues.get(0)); + : Optional.of(issues.getFirst()); } public List validateMetadata(ExtensionVersion extVersion) { @@ -240,9 +241,9 @@ private boolean isInvalidURL(String value) { value = value.substring(4); try { - var url = new URL(value); + var url = new URI(value).toURL(); return url.getProtocol().matches("http(s)?") && StringUtils.isEmpty(url.getHost()); - } catch (MalformedURLException exc) { + } catch (URISyntaxException | MalformedURLException exc) { return true; } } diff --git a/server/src/main/java/org/eclipse/openvsx/UpstreamProxyService.java b/server/src/main/java/org/eclipse/openvsx/UpstreamProxyService.java index 45c02df44..6cc1e3cc0 100644 --- a/server/src/main/java/org/eclipse/openvsx/UpstreamProxyService.java +++ b/server/src/main/java/org/eclipse/openvsx/UpstreamProxyService.java @@ -9,9 +9,6 @@ ********************************************************************************/ package org.eclipse.openvsx; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; import org.apache.commons.lang3.StringUtils; import org.eclipse.openvsx.adapter.ExtensionQueryResult; import org.eclipse.openvsx.json.*; @@ -20,6 +17,10 @@ import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.node.ArrayNode; import java.net.URI; import java.net.URISyntaxException; @@ -36,10 +37,10 @@ public class UpstreamProxyService { public NamespaceJson rewriteUrls(NamespaceJson json) { rewriteUrlMap(json.getExtensions()); - if(!StringUtils.isEmpty(json.getMembersUrl())) { + if (!StringUtils.isEmpty(json.getMembersUrl())) { json.setMembersUrl(rewriteUrl(json.getMembersUrl())); } - if(!StringUtils.isEmpty(json.getRoleUrl())) { + if (!StringUtils.isEmpty(json.getRoleUrl())) { json.setRoleUrl(rewriteUrl(json.getRoleUrl())); } @@ -131,10 +132,10 @@ public VersionReferencesJson rewriteUrls(VersionReferencesJson json) { } public JsonNode rewriteUrls(JsonNode json) { - if(json.isArray()) { - var list = new ObjectMapper().createArrayNode(); + if (json.isArray()) { + var list = JsonMapper.shared().createArrayNode(); var array = (ArrayNode) json; - array.forEach(url -> list.add(rewriteUrl(url.asText()))); + array.forEach(url -> list.add(rewriteUrl(url.asString()))); json = list; } @@ -169,7 +170,7 @@ private List rewriteUrlList(List jsonList, Function mapper) { } private void rewriteUrlMap(Map map) { - if(map != null) { + if (map != null) { map.replaceAll((k, v) -> rewriteUrl(v)); } } diff --git a/server/src/main/java/org/eclipse/openvsx/accesstoken/ScheduleTokenExpirationJobs.java b/server/src/main/java/org/eclipse/openvsx/accesstoken/ScheduleTokenExpirationJobs.java index 84e96b0e5..495144b87 100644 --- a/server/src/main/java/org/eclipse/openvsx/accesstoken/ScheduleTokenExpirationJobs.java +++ b/server/src/main/java/org/eclipse/openvsx/accesstoken/ScheduleTokenExpirationJobs.java @@ -19,8 +19,6 @@ import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - @Component public class ScheduleTokenExpirationJobs { diff --git a/server/src/main/java/org/eclipse/openvsx/adapter/UpstreamVSCodeService.java b/server/src/main/java/org/eclipse/openvsx/adapter/UpstreamVSCodeService.java index d9a65d468..5ebd7dee2 100644 --- a/server/src/main/java/org/eclipse/openvsx/adapter/UpstreamVSCodeService.java +++ b/server/src/main/java/org/eclipse/openvsx/adapter/UpstreamVSCodeService.java @@ -9,7 +9,6 @@ * ****************************************************************************** */ package org.eclipse.openvsx.adapter; -import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.eclipse.openvsx.ExtensionValidator; @@ -30,6 +29,7 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import org.springframework.web.util.UriComponentsBuilder; +import tools.jackson.databind.ObjectMapper; import java.io.IOException; import java.net.URI; diff --git a/server/src/main/java/org/eclipse/openvsx/adapter/VSCodeAPI.java b/server/src/main/java/org/eclipse/openvsx/adapter/VSCodeAPI.java index 7d8992d0d..f8bef2638 100644 --- a/server/src/main/java/org/eclipse/openvsx/adapter/VSCodeAPI.java +++ b/server/src/main/java/org/eclipse/openvsx/adapter/VSCodeAPI.java @@ -9,9 +9,6 @@ ********************************************************************************/ package org.eclipse.openvsx.adapter; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -34,6 +31,8 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.json.JsonMapper; import java.net.URI; import java.util.List; @@ -51,7 +50,7 @@ public class VSCodeAPI { private final UpstreamVSCodeService upstream; private final List registries; private final IExtensionQueryRequestHandler extensionQueryRequestHandler; - private final ObjectMapper objectMapper; + private final JsonMapper jsonMapper; public VSCodeAPI( LocalVSCodeService local, @@ -62,7 +61,7 @@ public VSCodeAPI( this.upstream = upstream; this.registries = setupRegistries(); this.extensionQueryRequestHandler = extensionQueryRequestHandler; - this.objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + this.jsonMapper = new JsonMapper(); } private List setupRegistries() { @@ -114,8 +113,8 @@ public ResponseEntity extensionQueryAsGet(@RequestParam(va ExtensionQueryParam param; try { - param = objectMapper.readValue(query, ExtensionQueryParam.class); - } catch (JsonProcessingException ex) { + param = jsonMapper.readValue(query, ExtensionQueryParam.class); + } catch (JacksonException ex) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid extension query"); } diff --git a/server/src/main/java/org/eclipse/openvsx/cache/CacheConfig.java b/server/src/main/java/org/eclipse/openvsx/cache/CacheConfig.java index 69082a118..d6e2409e4 100644 --- a/server/src/main/java/org/eclipse/openvsx/cache/CacheConfig.java +++ b/server/src/main/java/org/eclipse/openvsx/cache/CacheConfig.java @@ -10,8 +10,7 @@ package org.eclipse.openvsx.cache; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import tools.jackson.databind.json.JsonMapper; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Scheduler; @@ -250,38 +249,38 @@ public CacheManager redisCacheManager( ) { logger.info("Configure Redis cache manager"); var extensionVersionMapper = JsonMapper.builder() - .addModule(new JavaTimeModule()) - .serializationInclusion(JsonInclude.Include.NON_NULL) + .changeDefaultPropertyInclusion(incl -> incl.withContentInclusion(JsonInclude.Include.NON_NULL). + withValueInclusion(JsonInclude.Include.NON_NULL)) .build(); return RedisCacheManager.builder(redisConnectionFactory) .withCacheConfiguration( CACHE_AVERAGE_REVIEW_RATING, - redisCacheConfig(new GenericJackson2JsonRedisSerializer(), averageReviewRatingTtl) + redisCacheConfig(new GenericJacksonJsonRedisSerializer(JsonMapper.shared()), averageReviewRatingTtl) ) .withCacheConfiguration( CACHE_NAMESPACE_DETAILS_JSON, - redisCacheConfig(new Jackson2JsonRedisSerializer<>(NamespaceDetailsJson.class), namespaceDetailsJsonTtl) + redisCacheConfig(new JacksonJsonRedisSerializer<>(NamespaceDetailsJson.class), namespaceDetailsJsonTtl) ) .withCacheConfiguration( CACHE_DATABASE_SEARCH, - redisCacheConfig(new Jackson2JsonRedisSerializer<>(SearchResult.class), databaseSearchTtl) + redisCacheConfig(new JacksonJsonRedisSerializer<>(SearchResult.class), databaseSearchTtl) ) .withCacheConfiguration( CACHE_EXTENSION_JSON, - redisCacheConfig(new Jackson2JsonRedisSerializer<>(ExtensionJson.class), extensionJsonTtl) + redisCacheConfig(new JacksonJsonRedisSerializer<>(ExtensionJson.class), extensionJsonTtl) ) .withCacheConfiguration( CACHE_LATEST_EXTENSION_VERSION_VSCODE, - redisCacheConfig(new Jackson2JsonRedisSerializer<>(ExtensionQueryResult.Extension.class), latestExtensionVersionVscodeTtl) + redisCacheConfig(new JacksonJsonRedisSerializer<>(ExtensionQueryResult.Extension.class), latestExtensionVersionVscodeTtl) ) .withCacheConfiguration( CACHE_LATEST_EXTENSION_VERSION, - redisCacheConfig(new Jackson2JsonRedisSerializer<>(extensionVersionMapper, ExtensionVersion.class), latestExtensionVersionTtl) + redisCacheConfig(new JacksonJsonRedisSerializer<>(extensionVersionMapper, ExtensionVersion.class), latestExtensionVersionTtl) ) .withCacheConfiguration( CACHE_LATEST_EXTENSION_VERSIONS_BY_PLATFORM, redisCacheConfig( - new Jackson2JsonRedisSerializer<>( + new JacksonJsonRedisSerializer<>( extensionVersionMapper, extensionVersionMapper.getTypeFactory().constructParametricType(List.class, ExtensionVersion.class) ), @@ -294,7 +293,7 @@ public CacheManager redisCacheManager( ) .withCacheConfiguration( CACHE_MALICIOUS_EXTENSIONS, - redisCacheConfig(new GenericJackson2JsonRedisSerializer(), maliciousExtensionsTtl) + redisCacheConfig(new GenericJacksonJsonRedisSerializer(JsonMapper.shared()), maliciousExtensionsTtl) ) .build(); } diff --git a/server/src/main/java/org/eclipse/openvsx/cache/bucket4j/JedisClusterCacheListener.java b/server/src/main/java/org/eclipse/openvsx/cache/bucket4j/JedisClusterCacheListener.java index 8360c883e..a8255d2db 100644 --- a/server/src/main/java/org/eclipse/openvsx/cache/bucket4j/JedisClusterCacheListener.java +++ b/server/src/main/java/org/eclipse/openvsx/cache/bucket4j/JedisClusterCacheListener.java @@ -12,9 +12,6 @@ *****************************************************************************/ package org.eclipse.openvsx.cache.bucket4j; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.ObjectMapper; import com.giffing.bucket4j.spring.boot.starter.config.cache.CacheUpdateEvent; import io.micrometer.core.instrument.util.NamedThreadFactory; import org.slf4j.Logger; @@ -22,6 +19,9 @@ import org.springframework.context.ApplicationEventPublisher; import redis.clients.jedis.JedisPubSub; import redis.clients.jedis.RedisClusterClient; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.JavaType; +import tools.jackson.databind.ObjectMapper; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -109,7 +109,7 @@ private void onCacheUpdateEvent(String message) { try { CacheUpdateEvent event = objectMapper.readValue(message, deserializeType); this.eventPublisher.publishEvent(event); - } catch (JsonProcessingException e) { + } catch (JacksonException e) { throw new RuntimeException(e); } } diff --git a/server/src/main/java/org/eclipse/openvsx/cache/bucket4j/JedisClusterCacheManager.java b/server/src/main/java/org/eclipse/openvsx/cache/bucket4j/JedisClusterCacheManager.java index 021ea9645..949f3180e 100644 --- a/server/src/main/java/org/eclipse/openvsx/cache/bucket4j/JedisClusterCacheManager.java +++ b/server/src/main/java/org/eclipse/openvsx/cache/bucket4j/JedisClusterCacheManager.java @@ -12,13 +12,13 @@ *****************************************************************************/ package org.eclipse.openvsx.cache.bucket4j; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import com.giffing.bucket4j.spring.boot.starter.config.cache.CacheManager; import com.giffing.bucket4j.spring.boot.starter.config.cache.CacheUpdateEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.RedisClusterClient; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; public class JedisClusterCacheManager implements CacheManager { @@ -50,7 +50,7 @@ public V getValue(K key) { try { String serializedValue = redisClusterClient.hget(cacheName, objectMapper.writeValueAsString(key)); return serializedValue != null ? objectMapper.readValue(serializedValue, this.valueType) : null; - } catch (JsonProcessingException e) { + } catch (JacksonException e) { LOGGER.warn("Exception occurred while retrieving key '{}' from cache '{}'. Message: {}", key, cacheName, e.getMessage()); return null; } @@ -65,12 +65,12 @@ public void setValue(K key, V value) { String serializedValue = objectMapper.writeValueAsString(value); redisClusterClient.hset(this.cacheName, serializedKey, serializedValue); - //publish an update event if the key already existed + // publish an update event if the key already existed if(oldValue != null){ CacheUpdateEvent updateEvent = new CacheUpdateEvent<>(key, oldValue, value); redisClusterClient.publish(this.updateChannel, objectMapper.writeValueAsString(updateEvent)); } - } catch (JsonProcessingException e) { + } catch (JacksonException e) { LOGGER.warn("Exception occurred while setting key '{}' in cache '{}'. Message: {}", key, cacheName, e.getMessage()); throw new RuntimeException(e); } diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseProfile.java b/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseProfile.java index 996f72b5d..7ceec8e24 100644 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseProfile.java +++ b/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseProfile.java @@ -10,14 +10,14 @@ package org.eclipse.openvsx.eclipse; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import java.io.IOException; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.core.JsonToken; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.annotation.JsonDeserialize; + import java.util.List; import java.util.Optional; @@ -151,20 +151,20 @@ public void setOpenVsx(PublisherAgreement openVsx) { this.openVsx = openVsx; } - public static class Deserializer extends JsonDeserializer { + public static class Deserializer extends ValueDeserializer { private static final TypeReference> TYPE_LIST_AGREEMENT = new TypeReference<>() {}; @Override - public PublisherAgreements deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + public PublisherAgreements deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException { if (p.currentToken() == JsonToken.START_ARRAY) { - var list = p.getCodec().readValue(p, TYPE_LIST_AGREEMENT); + var list = ctxt.readValue(p, TYPE_LIST_AGREEMENT); var result = new PublisherAgreements(); if (!list.isEmpty()) result.openVsx = list.getFirst(); return result; } - return p.getCodec().readValue(p, PublisherAgreements.class); + return ctxt.readValue(p, PublisherAgreements.class); } } diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseService.java b/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseService.java index 3eaf04fdb..b990392d8 100644 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseService.java +++ b/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseService.java @@ -9,10 +9,6 @@ ********************************************************************************/ package org.eclipse.openvsx.eclipse; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Lists; import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; @@ -28,12 +24,15 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; -import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import org.springframework.web.client.HttpStatusCodeException; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +import tools.jackson.core.JacksonException; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.json.JsonMapper; import java.net.URI; import java.time.LocalDateTime; @@ -66,7 +65,7 @@ public class EclipseService { private final ExtensionService extensions; private final EntityManager entityManager; private final RestTemplate restTemplate; - private final ObjectMapper objectMapper; + private final JsonMapper jsonMapper; @Value("${ovsx.eclipse.base-url:}") String eclipseApiUrl; @@ -87,8 +86,7 @@ public EclipseService( this.extensions = extensions; this.entityManager = entityManager; this.restTemplate = restTemplate; - this.objectMapper = new ObjectMapper(); - this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + this.jsonMapper = new JsonMapper(); } public boolean isActive() { @@ -304,20 +302,20 @@ private EclipseProfile parseEclipseProfile(ResponseEntity response) { try { if (json.startsWith("[\"")) { - var error = objectMapper.readValue(json, TYPE_LIST_STRING); + var error = jsonMapper.readValue(json, TYPE_LIST_STRING); logger.error("Profile request failed:\n{}", json); throw new ErrorResultException("Request to the Eclipse Foundation server failed: " + error, HttpStatus.INTERNAL_SERVER_ERROR); } else if (json.startsWith("[")) { - var profileList = objectMapper.readValue(json, TYPE_LIST_PROFILE); + var profileList = jsonMapper.readValue(json, TYPE_LIST_PROFILE); if (profileList.isEmpty()) { throw new ErrorResultException("No Eclipse user profile available.", HttpStatus.INTERNAL_SERVER_ERROR); } return profileList.getFirst(); } else { - return objectMapper.readValue(json, EclipseProfile.class); + return jsonMapper.readValue(json, EclipseProfile.class); } - } catch (JsonProcessingException exc) { + } catch (JacksonException exc) { logger.error("Failed to parse JSON response ({}):\n{}", response.getStatusCode(), json, exc); throw new ErrorResultException("Parsing Eclipse user profile failed: " + exc.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); @@ -402,8 +400,8 @@ public PublisherAgreement signPublisherAgreement(UserData user) { String payload; try { - payload = objectMapper.writeValueAsString(data); - } catch (JsonProcessingException exc2) { + payload = jsonMapper.writeValueAsString(data); + } catch (JacksonException exc2) { payload = "<" + exc2.getMessage() + ">"; } logger.error("Post request failed with URL: {} Payload: {}", requestUrl, payload, exc); @@ -420,18 +418,18 @@ private PublisherAgreement parseAgreementResponse(ResponseEntity respons try { PublisherAgreementResponse agreementResponse; if (json.startsWith("[\"")) { - var error = objectMapper.readValue(json, TYPE_LIST_STRING); + var error = jsonMapper.readValue(json, TYPE_LIST_STRING); logger.error("Publisher agreement request failed:\n{}", json); throw new ErrorResultException("Request to the Eclipse Foundation server failed: " + error, HttpStatus.INTERNAL_SERVER_ERROR); } else if (json.startsWith("[")) { - var profileList = objectMapper.readValue(json, TYPE_LIST_AGREEMENT); + var profileList = jsonMapper.readValue(json, TYPE_LIST_AGREEMENT); if (profileList.isEmpty()) { throw new ErrorResultException("No publisher agreement available.", HttpStatus.INTERNAL_SERVER_ERROR); } agreementResponse = profileList.getFirst(); } else { - agreementResponse = objectMapper.readValue(json, PublisherAgreementResponse.class); + agreementResponse = jsonMapper.readValue(json, PublisherAgreementResponse.class); } var timestamp = parseDate(agreementResponse.effectiveDate); @@ -441,7 +439,7 @@ private PublisherAgreement parseAgreementResponse(ResponseEntity respons agreementResponse.version, timestamp ); - } catch (JsonProcessingException exc) { + } catch (JacksonException exc) { logger.error("Failed to parse JSON response ({}):\n{}", response.getStatusCode(), json, exc); throw new ErrorResultException("Parsing publisher agreement response failed: " + exc.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); @@ -514,4 +512,4 @@ private void checkEclipseData(UserData user) { + user.getProvider() + "/" + user.getLoginName()); } } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseTokenService.java b/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseTokenService.java index 94abed9b3..e59420b60 100644 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseTokenService.java +++ b/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseTokenService.java @@ -9,8 +9,6 @@ ********************************************************************************/ package org.eclipse.openvsx.eclipse; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.persistence.EntityManager; import org.eclipse.openvsx.entities.AuthToken; import org.eclipse.openvsx.entities.UserData; @@ -31,6 +29,8 @@ import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; import java.time.Instant; import java.util.List; @@ -136,8 +136,8 @@ private Pair refreshEclipseToken(AuthToke var restTemplate = new RestTemplate(); var response = restTemplate.postForObject(tokenUri, request, String.class); var root = objectMapper.readTree(response); - var newTokenValue = root.get("access_token").asText(); - var newRefreshTokenValue = root.get("refresh_token").asText(); + var newTokenValue = root.get("access_token").asString(); + var newRefreshTokenValue = root.get("refresh_token").asString(); var expires_in = root.get("expires_in").asLong(); var issuedAt = Instant.now(); @@ -151,10 +151,9 @@ private Pair refreshEclipseToken(AuthToke logger.warn("Eclipse token could not be refreshed: {}", exc.getMessage()); } catch (RestClientException exc) { logger.error("Post request failed with URL: {}", tokenUri, exc); - } catch (JsonProcessingException exc) { + } catch (JacksonException exc) { logger.error("Invalid JSON data received from URL: {}", tokenUri, exc); } return null; } - -} \ No newline at end of file +} diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherAgreementResponse.java b/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherAgreementResponse.java index 29469e3b4..5b6d4e46e 100644 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherAgreementResponse.java +++ b/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherAgreementResponse.java @@ -51,4 +51,4 @@ class PublisherAgreementResponse { /** Comment about the document being posted. */ @JsonProperty("Comments") String comments; -} \ No newline at end of file +} diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherComplianceChecker.java b/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherComplianceChecker.java index 717cd2df5..85c9fa443 100644 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherComplianceChecker.java +++ b/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherComplianceChecker.java @@ -118,5 +118,4 @@ private void deactivateExtensions(List accessTokens) { entityManager.merge(extension); } } - -} \ No newline at end of file +} diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/SignAgreementParam.java b/server/src/main/java/org/eclipse/openvsx/eclipse/SignAgreementParam.java index 9505c0cf3..d7d71915b 100644 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/SignAgreementParam.java +++ b/server/src/main/java/org/eclipse/openvsx/eclipse/SignAgreementParam.java @@ -50,4 +50,4 @@ public String getGithubHandle() { public void setGithubHandle(String githubHandle) { this.githubHandle = githubHandle; } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/eclipse/openvsx/entities/AuthTokenConverter.java b/server/src/main/java/org/eclipse/openvsx/entities/AuthTokenConverter.java index e2fe784d7..783076d84 100644 --- a/server/src/main/java/org/eclipse/openvsx/entities/AuthTokenConverter.java +++ b/server/src/main/java/org/eclipse/openvsx/entities/AuthTokenConverter.java @@ -12,26 +12,22 @@ import jakarta.persistence.AttributeConverter; import jakarta.persistence.Converter; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.json.JsonMapper; import org.springframework.web.server.ServerErrorException; @Converter public class AuthTokenConverter implements AttributeConverter { - private final ObjectMapper objectMapper = new ObjectMapper(); - - public AuthTokenConverter() { - objectMapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule()); - } + private final JsonMapper jsonMapper = new JsonMapper(); @Override public String convertToDatabaseColumn(AuthToken data) { if (data == null) return null; try { - return objectMapper.writeValueAsString(data); - } catch (JsonProcessingException exc) { + return jsonMapper.writeValueAsString(data); + } catch (JacksonException exc) { throw new ServerErrorException("Failed to serialize AuthToken to DB column.", exc); } } @@ -41,10 +37,9 @@ public AuthToken convertToEntityAttribute(String raw) { if (raw == null) return null; try { - return objectMapper.readValue(raw, AuthToken.class); - } catch (JsonProcessingException exc) { + return jsonMapper.readValue(raw, AuthToken.class); + } catch (JacksonException exc) { throw new ServerErrorException("Failed to parse AuthToken from DB column.", exc); } } - -} \ No newline at end of file +} diff --git a/server/src/main/java/org/eclipse/openvsx/extension_control/ExtensionControlJobRequestHandler.java b/server/src/main/java/org/eclipse/openvsx/extension_control/ExtensionControlJobRequestHandler.java index 181301086..62b1af682 100644 --- a/server/src/main/java/org/eclipse/openvsx/extension_control/ExtensionControlJobRequestHandler.java +++ b/server/src/main/java/org/eclipse/openvsx/extension_control/ExtensionControlJobRequestHandler.java @@ -9,7 +9,6 @@ * ****************************************************************************** */ package org.eclipse.openvsx.extension_control; -import com.fasterxml.jackson.databind.JsonNode; import org.eclipse.openvsx.admin.AdminService; import org.eclipse.openvsx.migration.HandlerJobRequest; import org.eclipse.openvsx.repositories.RepositoryService; @@ -20,6 +19,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; +import tools.jackson.databind.JsonNode; @Component public class ExtensionControlJobRequestHandler implements JobRequestHandler> { @@ -69,10 +69,10 @@ private void processMaliciousExtensions(JsonNode json) { for(var item : node) { logger.atInfo() .setMessage("malicious: {}") - .addArgument(item::asText) + .addArgument(item::asString) .log(); - var extensionId = NamingUtil.fromExtensionId(item.asText()); + var extensionId = NamingUtil.fromExtensionId(item.asString()); if(extensionId != null && repositories.hasExtension(extensionId.namespace(), extensionId.extension())) { logger.info("delete malicious extension"); admin.deleteExtensionAndDependencies(extensionId.namespace(), extensionId.extension(), adminUser); @@ -88,7 +88,7 @@ private void processDeprecatedExtensions(JsonNode json) { return; } - node.fields().forEachRemaining(field -> { + node.properties().iterator().forEachRemaining(field -> { logger.info("deprecated: {}", field.getKey()); var extensionId = NamingUtil.fromExtensionId(field.getKey()); if(extensionId == null) { @@ -101,7 +101,7 @@ private void processDeprecatedExtensions(JsonNode json) { } else if(value.isObject()) { var replacement = value.get("extension"); var replacementId = replacement != null && replacement.isObject() - ? NamingUtil.fromExtensionId(replacement.get("id").asText()) + ? NamingUtil.fromExtensionId(replacement.get("id").asString()) : null; var disallowInstall = value.has("disallowInstall") && value.get("disallowInstall").asBoolean(false); diff --git a/server/src/main/java/org/eclipse/openvsx/extension_control/ExtensionControlService.java b/server/src/main/java/org/eclipse/openvsx/extension_control/ExtensionControlService.java index 3480b7dec..d94825900 100644 --- a/server/src/main/java/org/eclipse/openvsx/extension_control/ExtensionControlService.java +++ b/server/src/main/java/org/eclipse/openvsx/extension_control/ExtensionControlService.java @@ -9,8 +9,6 @@ * ****************************************************************************** */ package org.eclipse.openvsx.extension_control; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; import org.eclipse.openvsx.cache.CacheService; @@ -29,6 +27,8 @@ import org.springframework.cache.annotation.Cacheable; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; import java.io.IOException; import java.net.URI; @@ -78,10 +78,10 @@ public ExtensionControlService( @EventListener public void applicationStarted(ApplicationStartedEvent event) { - if(!enabled || mirrorEnabled) { + if (!enabled || mirrorEnabled) { return; } - if(updateOnStart) { + if (updateOnStart) { scheduler.schedule(TimeUtil.getCurrentUTC().plusSeconds(delay), new HandlerJobRequest<>(ExtensionControlJobRequestHandler.class)); } @@ -104,18 +104,18 @@ public UserData createExtensionControlUser() { @Transactional public void updateExtension(ExtensionId extensionId, boolean deprecated, ExtensionId replacementId, boolean downloadable) { var extension = repositories.findExtension(extensionId.extension(), extensionId.namespace()); - if(extension == null) { + if (extension == null) { return; } var wasDeprecated = extension.isDeprecated(); extension.setDeprecated(deprecated); extension.setDownloadable(downloadable); - if(replacementId != null) { + if (replacementId != null) { var replacement = repositories.findExtension(replacementId.extension(), replacementId.namespace()); extension.setReplacement(replacement); } - if(deprecated != wasDeprecated) { + if (deprecated != wasDeprecated) { cache.evictNamespaceDetails(extension); cache.evictLatestExtensionVersion(extension); cache.evictExtensionJsons(extension); @@ -125,7 +125,9 @@ public void updateExtension(ExtensionId extensionId, boolean deprecated, Extensi public JsonNode getExtensionControlJson() throws IOException { var url = URI.create("https://github.com/open-vsx/publish-extensions/raw/master/extension-control/extensions.json").toURL(); - return new ObjectMapper().readValue(url, JsonNode.class); + try (var inputStream = url.openStream()) { + return new ObjectMapper().readValue(inputStream, JsonNode.class); + } } @Cacheable(CACHE_MALICIOUS_EXTENSIONS) @@ -142,7 +144,7 @@ public List getMaliciousExtensionIds() throws IOException { } var list = new ArrayList(); - malicious.forEach(node -> list.add(node.asText())); + malicious.forEach(node -> list.add(node.asString())); return list; } } diff --git a/server/src/main/java/org/eclipse/openvsx/metrics/RegistryObservationConvention.java b/server/src/main/java/org/eclipse/openvsx/metrics/RegistryObservationConvention.java index 1f9cb6c67..1a885e26e 100644 --- a/server/src/main/java/org/eclipse/openvsx/metrics/RegistryObservationConvention.java +++ b/server/src/main/java/org/eclipse/openvsx/metrics/RegistryObservationConvention.java @@ -9,18 +9,17 @@ * ****************************************************************************** */ package org.eclipse.openvsx.metrics; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.micrometer.common.KeyValue; import io.micrometer.common.KeyValues; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationConvention; import io.micrometer.observation.aop.ObservedAspect; import org.aspectj.lang.reflect.MethodSignature; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; public class RegistryObservationConvention implements ObservationConvention { - private ObjectMapper mapper; + private final ObjectMapper mapper; public RegistryObservationConvention() { this.mapper = new ObjectMapper(); @@ -50,7 +49,7 @@ private String convertObjectToString(Object arg) { } else { try { return mapper.writeValueAsString(arg); - } catch (JsonProcessingException e) { + } catch (JacksonException e) { return ""; } } diff --git a/server/src/main/java/org/eclipse/openvsx/publish/ExtensionVersionIntegrityService.java b/server/src/main/java/org/eclipse/openvsx/publish/ExtensionVersionIntegrityService.java index b7818654f..1373fcf54 100644 --- a/server/src/main/java/org/eclipse/openvsx/publish/ExtensionVersionIntegrityService.java +++ b/server/src/main/java/org/eclipse/openvsx/publish/ExtensionVersionIntegrityService.java @@ -9,8 +9,6 @@ * ****************************************************************************** */ package org.eclipse.openvsx.publish; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; import org.apache.commons.codec.binary.Base64; @@ -31,6 +29,8 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; import java.io.IOException; import java.io.InputStream; diff --git a/server/src/main/java/org/eclipse/openvsx/ratelimit/ResolvedIdentity.java b/server/src/main/java/org/eclipse/openvsx/ratelimit/ResolvedIdentity.java index 6d60471a7..da754ca6f 100644 --- a/server/src/main/java/org/eclipse/openvsx/ratelimit/ResolvedIdentity.java +++ b/server/src/main/java/org/eclipse/openvsx/ratelimit/ResolvedIdentity.java @@ -12,7 +12,6 @@ *****************************************************************************/ package org.eclipse.openvsx.ratelimit; -import jakarta.validation.constraints.NotNull; import org.eclipse.openvsx.entities.Customer; import org.eclipse.openvsx.entities.Tier; import org.jspecify.annotations.NonNull; @@ -29,7 +28,7 @@ public boolean isCustomer() { return customer != null; } - public @NotNull Customer getCustomer() { + public @NonNull Customer getCustomer() { if (isCustomer()) { return customer; } else { diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/AhoCorasick.java b/server/src/main/java/org/eclipse/openvsx/scanning/AhoCorasick.java index c89a93695..496ca9a5b 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/AhoCorasick.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/AhoCorasick.java @@ -12,7 +12,8 @@ ********************************************************************************/ package org.eclipse.openvsx.scanning; -import jakarta.validation.constraints.NotNull; +import org.jspecify.annotations.NonNull; + import java.util.*; /** @@ -77,9 +78,9 @@ private Builder() {} * @param keyword The keyword to search for (should be lowercase for case-insensitive matching) * @return this builder for chaining */ - public Builder addKeyword(@NotNull String keyword) { + public Builder addKeyword(@NonNull String keyword) { checkNotBuilt(); - if (keyword != null && !keyword.isEmpty()) { + if (!keyword.isEmpty()) { keywords.add(keyword); } return this; @@ -90,7 +91,7 @@ public Builder addKeyword(@NotNull String keyword) { * @param keywords Collection of keywords to search for * @return this builder for chaining */ - public Builder addKeywords(@NotNull Collection keywords) { + public Builder addKeywords(@NonNull Collection keywords) { checkNotBuilt(); for (String keyword : keywords) { if (keyword != null && !keyword.isEmpty()) { @@ -195,7 +196,7 @@ private static void buildFailureLinks(TrieNode root) { * @param text Text to search in (should be lowercase for case-insensitive matching) * @return List of all keyword matches found */ - public @NotNull List search(@NotNull String text) { + public @NonNull List search(@NonNull String text) { List matches = new ArrayList<>(); TrieNode current = root; @@ -231,13 +232,13 @@ public static class Match { private final int startPos; private final int endPos; - public Match(@NotNull String keyword, int startPos, int endPos) { + public Match(@NonNull String keyword, int startPos, int endPos) { this.keyword = keyword; this.startPos = startPos; this.endPos = endPos; } - public @NotNull String getKeyword() { + public @NonNull String getKeyword() { return keyword; } diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/EntropyCalculator.java b/server/src/main/java/org/eclipse/openvsx/scanning/EntropyCalculator.java index 3fa001a53..e7b9455f5 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/EntropyCalculator.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/EntropyCalculator.java @@ -47,4 +47,3 @@ public double calculate(@Nullable String input) { return entropy; } } - diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanCompletionService.java b/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanCompletionService.java index 738754c7d..69ae7840d 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanCompletionService.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanCompletionService.java @@ -718,4 +718,3 @@ private String formatExtensionId(ExtensionScan scan) { return scan.getNamespaceName() + "." + scan.getExtensionName() + " " + scan.getExtensionVersion(); } } - diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanService.java b/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanService.java index d17cbd236..8b6012e29 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanService.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanService.java @@ -459,4 +459,4 @@ private String getUserFacingErrorMessage(List findin return "Extension publication blocked: " + String.join(", ", parts) + ". For details on " + "publishing extensions, see: https://github.com/eclipse/openvsx/wiki/Publishing-Extensions"; } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java b/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java index c507ec764..439f23371 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/GitleaksRulesService.java @@ -27,7 +27,7 @@ import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import redis.clients.jedis.RedisClusterClient; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; import tools.jackson.dataformat.toml.TomlMapper; import java.io.File; @@ -45,12 +45,12 @@ /** * Manages gitleaks secret detection rules: generation, scheduled refresh, and Redis sync. - * + *

* This service combines three responsibilities: * 1. Generates rules from gitleaks.toml at startup (if auto-fetch enabled) * 2. Refreshes rules on a schedule (if scheduled-refresh enabled) * 3. Syncs rules across pods via Redis (if Redis enabled) - * + *

* Only loaded when gitleaks.auto-fetch is enabled. */ @Service @@ -349,8 +349,8 @@ private String downloadGitleaksToml() throws IOException, InterruptedException { } } - private GitleaksToml parseToml(String tomlContent) throws IOException { - return new TomlMapper().readValue(tomlContent, GitleaksToml.class); + private GitleaksToml parseToml(String tomlContent) { + return TomlMapper.shared().readValue(tomlContent, GitleaksToml.class); } private List buildRules(List rawRules) { @@ -461,7 +461,7 @@ private void writeYamlList(StringBuilder yaml, String prefix, List items private String quote(String value) { if (value == null) return "\"\""; try { - return new ObjectMapper().writeValueAsString(value); + return JsonMapper.shared().writeValueAsString(value); } catch (Exception e) { return "\"" + value.replace("\\", "\\\\").replace("\"", "\\\"") + "\""; } diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java b/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java index 168a84c62..4c04b03d6 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/HttpClientExecutor.java @@ -334,4 +334,3 @@ public TempFileResource(TempFile tempFile) { } } } - diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/HttpResponseExtractor.java b/server/src/main/java/org/eclipse/openvsx/scanning/HttpResponseExtractor.java index 22acc688a..eb7e33b3a 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/HttpResponseExtractor.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/HttpResponseExtractor.java @@ -16,7 +16,7 @@ import tools.jackson.core.JacksonException; import tools.jackson.core.type.TypeReference; import tools.jackson.databind.JsonNode; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; import java.util.ArrayList; import java.util.List; @@ -28,10 +28,10 @@ @Component public class HttpResponseExtractor { - private final ObjectMapper objectMapper; + private final JsonMapper jsonMapper; - public HttpResponseExtractor(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; + public HttpResponseExtractor(JsonMapper jsonMapper) { + this.jsonMapper = jsonMapper; } /** @@ -68,7 +68,7 @@ public List> extractList(String response, String format, Str private JsonNode parseJson(String json) throws ScannerException { try { - return objectMapper.readTree(json); + return jsonMapper.readTree(json); } catch (JacksonException e) { throw new ScannerException("Failed to parse JSON response", e); } @@ -76,7 +76,7 @@ private JsonNode parseJson(String json) throws ScannerException { private String extractJsonString(String json, String jsonPath) throws ScannerException { JsonNode node = pickFirst(parseJson(json), jsonPath); - return node != null && !node.isMissingNode() ? node.asText(null) : null; + return node != null && !node.isMissingNode() ? node.asString(null) : null; } private List> extractJsonList(String json, String jsonPath) throws ScannerException { @@ -86,23 +86,23 @@ private List> extractJsonList(String json, String jsonPath) List> result = new ArrayList<>(); for (JsonNode node : nodes) { if (node.isObject()) { - Map map = objectMapper.convertValue( + Map map = jsonMapper.convertValue( node, new TypeReference>() { }); result.add(map); } else if (node.isArray()) { for (JsonNode child : node) { if (child.isObject()) { - Map map = objectMapper.convertValue( + Map map = jsonMapper.convertValue( child, new TypeReference>() { }); result.add(map); } else { - result.add(Map.of("value", child.asText(null))); + result.add(Map.of("value", child.asString(null))); } } } else { - result.add(Map.of("value", node.asText(null))); + result.add(Map.of("value", node.asString(null))); } } @@ -318,4 +318,3 @@ public String evaluateExpression(Map object, String expression) return expression; } } - diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/HttpTemplateEngine.java b/server/src/main/java/org/eclipse/openvsx/scanning/HttpTemplateEngine.java index 9c9987c05..1ec732791 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/HttpTemplateEngine.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/HttpTemplateEngine.java @@ -88,4 +88,3 @@ public Map processMap(Map map, Map getJobRequestHandler() { return ScannerPollHandler.class; } } - diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/ScannerRegistry.java b/server/src/main/java/org/eclipse/openvsx/scanning/ScannerRegistry.java index c385b2309..9c135229e 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/ScannerRegistry.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/ScannerRegistry.java @@ -11,6 +11,7 @@ * SPDX-License-Identifier: EPL-2.0 ********************************************************************************/ package org.eclipse.openvsx.scanning; + import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.springframework.stereotype.Component; diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/SecretCheckService.java b/server/src/main/java/org/eclipse/openvsx/scanning/SecretCheckService.java index 5a69c48d0..5472bea36 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/SecretCheckService.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/SecretCheckService.java @@ -14,6 +14,7 @@ import org.eclipse.openvsx.util.TempFile; import org.eclipse.openvsx.util.ArchiveUtil; +import org.jspecify.annotations.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; @@ -21,7 +22,6 @@ import org.springframework.core.annotation.Order; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.stereotype.Service; -import jakarta.validation.constraints.NotNull; import java.util.ArrayList; import java.util.Collections; @@ -120,7 +120,7 @@ public PublishCheck.Result check(PublishCheck.Context context) { *

* Callers should check {@link #isEnabled()} before invoking this method. */ - private SecretDetector.Result scanForSecrets(@NotNull TempFile extensionFile) { + private SecretDetector.Result scanForSecrets(@NonNull TempFile extensionFile) { // Thread-safe collection for parallel processing List findings = Collections.synchronizedList(new ArrayList<>()); AtomicInteger findingsCount = new AtomicInteger(0); @@ -271,4 +271,3 @@ class SecretScanningException extends RuntimeException { super(message, cause); } } - diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetector.java b/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetector.java index 1a1a52410..eade44ce6 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetector.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetector.java @@ -11,14 +11,14 @@ * SPDX-License-Identifier: EPL-2.0 ********************************************************************************/ package org.eclipse.openvsx.scanning; + +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.commons.lang3.StringUtils; import org.apache.tika.Tika; -import org.eclipse.openvsx.util.ArchiveUtil; import org.eclipse.openvsx.util.SizeLimitInputStream; -import jakarta.validation.constraints.NotNull; import java.io.BufferedReader; import java.io.BufferedInputStream; @@ -42,7 +42,7 @@ class SecretDetector { @FunctionalInterface interface FindingRecorder { - boolean record(@NotNull List findings, @NotNull AtomicInteger count, @NotNull Finding finding); + boolean record(@NonNull List findings, @NonNull AtomicInteger count, @NonNull Finding finding); } private static final Logger logger = LoggerFactory.getLogger(SecretDetector.class); @@ -67,16 +67,16 @@ interface FindingRecorder { private final int keywordContextChars; private final int logAllowlistedPreviewLength; - SecretDetector(@NotNull AhoCorasick keywordMatcher, - @NotNull Map> keywordToRules, - @NotNull List rules, + SecretDetector(@NonNull AhoCorasick keywordMatcher, + @NonNull Map> keywordToRules, + @NonNull List rules, @Nullable List allowlistPatterns, @Nullable List excludedPathPatterns, @Nullable AhoCorasick stopwordMatcher, @Nullable AhoCorasick excludedExtensionMatcher, @Nullable AhoCorasick inlineSuppressionMatcher, @Nullable List skipMimeTypePatterns, - @NotNull EntropyCalculator entropyCalculator, + @NonNull EntropyCalculator entropyCalculator, long maxFileSizeBytes, int maxLineLength, int timeoutCheckEveryNLines, @@ -101,13 +101,13 @@ interface FindingRecorder { this.logAllowlistedPreviewLength = logAllowlistedPreviewLength; } - boolean scanFile(@NotNull ZipFile zipFile, - @NotNull ZipEntry entry, - @NotNull List findings, + boolean scanFile(@NonNull ZipFile zipFile, + @NonNull ZipEntry entry, + @NonNull List findings, long startTime, long timeoutMillis, - @NotNull AtomicInteger findingsCount, - @NotNull FindingRecorder recorder) throws IOException { + @NonNull AtomicInteger findingsCount, + @NonNull FindingRecorder recorder) throws IOException { String filePath = entry.getName(); if (Thread.currentThread().isInterrupted()) { @@ -183,13 +183,13 @@ boolean scanFile(@NotNull ZipFile zipFile, * Scan the line with keyword matching. * This is a performance optimization to skip the regex matching for lines that don't contain any keywords. */ - private void scanLineWithKeywordMatching(@NotNull String line, - @NotNull String lowerLine, - @NotNull String filePath, + private void scanLineWithKeywordMatching(@NonNull String line, + @NonNull String lowerLine, + @NonNull String filePath, int lineNumber, - @NotNull List findings, - @NotNull AtomicInteger findingsCount, - @NotNull FindingRecorder recorder) { + @NonNull List findings, + @NonNull AtomicInteger findingsCount, + @NonNull FindingRecorder recorder) { if (Thread.currentThread().isInterrupted()) { return; } @@ -230,13 +230,13 @@ private void scanLineWithKeywordMatching(@NotNull String line, /** * Scan the line with the given rule's regex pattern */ - private void scanLineWithRule(@NotNull SecretRule rule, - @NotNull String chunk, - @NotNull String filePath, + private void scanLineWithRule(@NonNull SecretRule rule, + @NonNull String chunk, + @NonNull String filePath, int lineNumber, - @NotNull List findings, - @NotNull AtomicInteger findingsCount, - @NotNull FindingRecorder recorder) { + @NonNull List findings, + @NonNull AtomicInteger findingsCount, + @NonNull FindingRecorder recorder) { if (Thread.currentThread().isInterrupted()) { return; } @@ -313,7 +313,7 @@ private void scanLineWithRule(@NotNull SecretRule rule, /** * Check if the line has any inline suppressions */ - private boolean hasInlineSuppression(@NotNull String line) { + private boolean hasInlineSuppression(@NonNull String line) { if (inlineSuppressionMatcher == null) { return false; } @@ -324,7 +324,7 @@ private boolean hasInlineSuppression(@NotNull String line) { /** * Check if the file path is excluded by any of the global excluded path patterns */ - private boolean shouldExcludeFile(@NotNull String filePath) { + private boolean shouldExcludeFile(@NonNull String filePath) { for (Pattern pattern : globalExcludedPathPatterns) { if (pattern.matcher(filePath).find()) { return true; @@ -337,7 +337,7 @@ private boolean shouldExcludeFile(@NotNull String filePath) { /** * Check if the file type is excluded by any of the global excluded file extensions */ - private boolean isExcludedFileType(@NotNull String filePath) { + private boolean isExcludedFileType(@NonNull String filePath) { String lowerPath = filePath.toLowerCase(); if (globalExcludedExtensionMatcher != null) { @@ -354,7 +354,7 @@ private boolean isExcludedFileType(@NotNull String filePath) { /** * Check if the secret value is allowlisted by any of the global allowlist patterns */ - private boolean isAllowlistedContent(@NotNull String secretValue) { + private boolean isAllowlistedContent(@NonNull String secretValue) { // Stopwords are exact strings that should never be flagged as secrets if (globalStopwordMatcher != null && !globalStopwordMatcher.search(secretValue.toLowerCase()).isEmpty()) { return true; @@ -382,7 +382,7 @@ private boolean isAllowlistedContent(@NotNull String secretValue) { * * @return true if file should be skipped, false if it should be scanned */ - private boolean shouldExcludeByMimeType(@NotNull ZipFile zipFile, @NotNull ZipEntry entry) { + private boolean shouldExcludeByMimeType(@NonNull ZipFile zipFile, @NonNull ZipEntry entry) { // If no skip patterns configured, don't skip any files based on MIME type if (skipMimeTypePatterns.isEmpty()) { return false; @@ -422,15 +422,15 @@ private boolean shouldExcludeByMimeType(@NotNull ZipFile zipFile, @NotNull ZipEn static final class Result { private final boolean secretsFound; private final boolean timedOut; - private final @NotNull List findings; + private final @NonNull List findings; - private Result(boolean secretsFound, boolean timedOut, @NotNull List findings) { + private Result(boolean secretsFound, boolean timedOut, @NonNull List findings) { this.secretsFound = secretsFound; this.timedOut = timedOut; this.findings = List.copyOf(findings); } - public static Result secretsFound(@NotNull List findings) { + public static Result secretsFound(@NonNull List findings) { if (findings.isEmpty()) { throw new IllegalArgumentException("Cannot create secretsFound result with empty findings"); } @@ -442,7 +442,7 @@ public static Result secretsFound(@NotNull List findings) { * Only use this when there are actual findings to report. * If no findings were found before timeout, throw an exception instead. */ - public static Result timedOut(@NotNull List partialFindings) { + public static Result timedOut(@NonNull List partialFindings) { if (partialFindings.isEmpty()) { throw new IllegalArgumentException("Cannot create timedOut result with empty findings - throw exception instead"); } @@ -459,9 +459,9 @@ public static Result skipped() { public boolean isSecretsFound() { return secretsFound; } public boolean isTimedOut() { return timedOut; } - public @NotNull List getFindings() { return findings; } + public @NonNull List getFindings() { return findings; } - public @NotNull String getSummaryMessage() { + public @NonNull String getSummaryMessage() { String prefix = timedOut ? "(PARTIAL - timed out) " : ""; if (!secretsFound) { return prefix + "No secrets detected"; @@ -475,14 +475,14 @@ public static Result skipped() { * A single secret finding. Secrets are redacted immediately. */ static final class Finding { - private final @NotNull String filePath; + private final @NonNull String filePath; private final int lineNumber; private final double entropy; - private final @NotNull String redactedSecret; - private final @NotNull String ruleId; + private final @NonNull String redactedSecret; + private final @NonNull String ruleId; - Finding(@NotNull String filePath, int lineNumber, double entropy, - @Nullable String secretValue, @NotNull String ruleId) { + Finding(@NonNull String filePath, int lineNumber, double entropy, + @Nullable String secretValue, @NonNull String ruleId) { this.filePath = filePath; this.lineNumber = lineNumber; this.entropy = entropy; @@ -490,11 +490,11 @@ static final class Finding { this.ruleId = ruleId; } - public @NotNull String getFilePath() { return filePath; } + public @NonNull String getFilePath() { return filePath; } public int getLineNumber() { return lineNumber; } public double getEntropy() { return entropy; } - public @NotNull String getSecretValue() { return redactedSecret; } - public @NotNull String getRuleId() { return ruleId; } + public @NonNull String getSecretValue() { return redactedSecret; } + public @NonNull String getRuleId() { return ruleId; } @Override public String toString() { @@ -502,7 +502,7 @@ public String toString() { filePath, lineNumber, ruleId, entropy, redactedSecret); } - private static @NotNull String redactSecret(@Nullable String secret) { + private static @NonNull String redactSecret(@Nullable String secret) { if (secret == null || secret.length() <= 6) { return "***"; } @@ -512,4 +512,3 @@ public String toString() { } } } - diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetectorConfig.java b/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetectorConfig.java index dc29a652e..b1b52a118 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetectorConfig.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetectorConfig.java @@ -13,12 +13,13 @@ package org.eclipse.openvsx.scanning; import jakarta.annotation.PostConstruct; -import jakarta.validation.constraints.NotNull; +import org.jspecify.annotations.NonNull; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import java.util.Arrays; import java.util.List; +import java.util.Set; /** * Configuration for secret detection in extension packages. @@ -247,7 +248,7 @@ public int getMinifiedLineThreshold() { /** * Suppression markers that skip an entire line from scanning. */ - public @NotNull List getSuppressionMarkers() { + public @NonNull List getSuppressionMarkers() { if (suppressionMarkers == null || suppressionMarkers.trim().isEmpty()) { return List.of(); } @@ -270,7 +271,7 @@ public int getMaxFindings() { * Get YAML rule paths, supporting comma-separated inputs. * Empty input yields an empty list so callers can fail fast. */ - public @NotNull List getRulePaths() { + public @NonNull List getRulePaths() { if (rulesPath == null || rulesPath.trim().isEmpty()) { return List.of(); } @@ -320,7 +321,7 @@ public String getGitleaksRefreshCron() { * Get gitleaks rule IDs to skip when generating rules. * Returns a set for efficient lookup. */ - public @NotNull java.util.Set getGitleaksSkipRuleIds() { + public @NonNull Set getGitleaksSkipRuleIds() { if (gitleaksSkipRuleIds == null || gitleaksSkipRuleIds.trim().isEmpty()) { return java.util.Set.of(); } @@ -368,4 +369,3 @@ public void validate() { } } } - diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetectorFactory.java b/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetectorFactory.java index 306697b23..2871c5776 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetectorFactory.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/SecretDetectorFactory.java @@ -14,7 +14,7 @@ import com.google.re2j.Pattern; import jakarta.annotation.PostConstruct; -import jakarta.validation.constraints.NotNull; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,9 +57,9 @@ public class SecretDetectorFactory { private volatile SecretDetector scanner; public SecretDetectorFactory( - @NotNull SecretRuleLoader ruleLoader, - @NotNull SecretDetectorConfig config, - @NotNull ExtensionScanConfig scanConfig, + @NonNull SecretRuleLoader ruleLoader, + @NonNull SecretDetectorConfig config, + @NonNull ExtensionScanConfig scanConfig, @Nullable GitleaksRulesService gitleaksService) { this.ruleLoader = ruleLoader; this.config = config; @@ -80,8 +80,8 @@ public void initialize() { // Load all rules from the rule paths SecretRuleLoader.LoadedRules loaded = ruleLoader.loadAll(rulePaths); - List loadedRules = loaded.getRules(); - SecretRuleLoader.GlobalAllowlist globalAllowlist = loaded.getGlobalAllowlist(); + List loadedRules = loaded.rules(); + SecretRuleLoader.GlobalAllowlist globalAllowlist = loaded.globalAllowlist(); // Build the keyword index for efficient keyword matching based on all loaded rules Set allKeywords = new HashSet<>(); @@ -148,19 +148,19 @@ public synchronized void reinitialize() { return scanner; } - public @NotNull List getRules() { + public @NonNull List getRules() { return rules; } - public @NotNull Map> getKeywordToRules() { + public @NonNull Map> getKeywordToRules() { return keywordToRules; } - public @NotNull AhoCorasick getKeywordMatcher() { + public @NonNull AhoCorasick getKeywordMatcher() { return keywordMatcher; } - private Map> buildKeywordIndex(@NotNull List sourceRules, @NotNull Set allKeywords) { + private Map> buildKeywordIndex(@NonNull List sourceRules, @NonNull Set allKeywords) { Map> index = new HashMap<>(); for (SecretRule rule : sourceRules) { @@ -207,7 +207,7 @@ private AhoCorasick buildMatcher(java.util.Collection keywords) { */ private List getGlobalExcludedExtensions( SecretRuleLoader.@Nullable GlobalAllowlist globalAllowlist, - @NotNull SecretDetectorConfig config) { + @NonNull SecretDetectorConfig config) { List result = new ArrayList<>(); if (globalAllowlist != null && globalAllowlist.fileExtensions != null) { @@ -226,7 +226,7 @@ private List getGlobalExcludedExtensions( */ private List getGlobalExcludedPathPatterns( SecretRuleLoader.@Nullable GlobalAllowlist globalAllowlist, - @NotNull SecretDetectorConfig config) { + @NonNull SecretDetectorConfig config) { List result = new ArrayList<>(); if (globalAllowlist != null && globalAllowlist.paths != null) { @@ -244,7 +244,7 @@ private List getGlobalExcludedPathPatterns( */ private List getGlobalAllowlistPatterns( SecretRuleLoader.@Nullable GlobalAllowlist globalAllowlist, - @NotNull SecretDetectorConfig config) { + @NonNull SecretDetectorConfig config) { List result = new ArrayList<>(); if (globalAllowlist != null && globalAllowlist.regexes != null && !globalAllowlist.regexes.isEmpty()) { @@ -262,7 +262,7 @@ private List getGlobalAllowlistPatterns( */ private List getGlobalStopwords( SecretRuleLoader.@Nullable GlobalAllowlist globalAllowlist, - @NotNull SecretDetectorConfig config) { + @NonNull SecretDetectorConfig config) { List result = new ArrayList<>(); if (globalAllowlist != null && globalAllowlist.stopwords != null) { @@ -278,7 +278,7 @@ private List getGlobalStopwords( * Get suppression markers from config. * Returns lowercase versions for case-insensitive matching. */ - private List getSuppressionMarkers(@NotNull SecretDetectorConfig config) { + private List getSuppressionMarkers(@NonNull SecretDetectorConfig config) { return config.getSuppressionMarkers().stream() .map(String::toLowerCase) .toList(); @@ -333,5 +333,3 @@ private List buildRulePaths() { return paths; } } - - diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/SecretRule.java b/server/src/main/java/org/eclipse/openvsx/scanning/SecretRule.java index a06eefc24..e7388de27 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/SecretRule.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/SecretRule.java @@ -13,7 +13,7 @@ package org.eclipse.openvsx.scanning; import com.google.re2j.Pattern; -import jakarta.validation.constraints.NotNull; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import java.util.List; @@ -34,27 +34,27 @@ */ public class SecretRule { /** Unique identifier for this rule (e.g., "github-pat", "aws-access-key") */ - private final @NotNull String id; + private final @NonNull String id; /** Human-readable description of what this rule detects */ - private final @NotNull String description; + private final @NonNull String description; /** Compiled regex pattern to match secrets (case-insensitive) */ - private final @NotNull Pattern pattern; + private final @NonNull Pattern pattern; /** Minimum Shannon entropy threshold (0.0-8.0). If set, matches below this are filtered out. */ private final @Nullable Double entropy; /** Keywords that must appear in a line before applying the regex (lowercase, for performance) */ - private final @NotNull List keywords; + private final @NonNull List keywords; /** Compiled allowlist patterns to exclude known safe matches from this rule */ - private final @NotNull List allowlistPatterns; + private final @NonNull List allowlistPatterns; /** Optional capture group index to extract the secret (default: 1 if available, else 0) */ private final @Nullable Integer secretGroup; - private SecretRule(@NotNull Builder builder) { + private SecretRule(@NonNull Builder builder) { this.id = builder.id; this.description = builder.description; this.pattern = Pattern.compile(builder.regex, Pattern.CASE_INSENSITIVE); @@ -81,15 +81,15 @@ private SecretRule(@NotNull Builder builder) { } } - public @NotNull String getId() { + public @NonNull String getId() { return id; } - public @NotNull String getDescription() { + public @NonNull String getDescription() { return description; } - public @NotNull Pattern getPattern() { + public @NonNull Pattern getPattern() { return pattern; } @@ -97,11 +97,11 @@ private SecretRule(@NotNull Builder builder) { return entropy; } - public @NotNull List getKeywords() { + public @NonNull List getKeywords() { return keywords; } - public @NotNull List getAllowlistPatterns() { + public @NonNull List getAllowlistPatterns() { return allowlistPatterns; } diff --git a/server/src/main/java/org/eclipse/openvsx/scanning/SecretRuleLoader.java b/server/src/main/java/org/eclipse/openvsx/scanning/SecretRuleLoader.java index bd7f6725b..45e8c27fa 100644 --- a/server/src/main/java/org/eclipse/openvsx/scanning/SecretRuleLoader.java +++ b/server/src/main/java/org/eclipse/openvsx/scanning/SecretRuleLoader.java @@ -13,15 +13,15 @@ package org.eclipse.openvsx.scanning; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import jakarta.validation.constraints.NotNull; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Component; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.dataformat.yaml.YAMLFactory; import java.io.File; import java.io.FileInputStream; @@ -47,41 +47,16 @@ public class SecretRuleLoader { * Container for loaded rules and global allowlist configuration. *

* This is returned by {@link #loadAll(List)} and contains: - * Compiled secret detection rules from all YAML files (deduplicated by ID) - * Global allowlist configuration from the last YAML file + * Compiled secret detection rules from all YAML files (deduplicated by ID) + * Global allowlist configuration from the last YAML file */ - public static class LoadedRules { - private final List rules; - private final GlobalAllowlist globalAllowlist; - - /** - * Create a container for loaded rules and global allowlist. - */ - public LoadedRules(@NotNull List rules, @Nullable GlobalAllowlist globalAllowlist) { - this.rules = rules; - this.globalAllowlist = globalAllowlist; - } - - /** - * Get the compiled secret detection rules. - */ - public @NotNull List getRules() { - return rules; - } - - /** - * Get the global allowlist configuration. - */ - public @Nullable GlobalAllowlist getGlobalAllowlist() { - return globalAllowlist; - } - } + public record LoadedRules(@NonNull List rules, @Nullable GlobalAllowlist globalAllowlist) {} /** * Load rules from a single path. Supports classpath: prefixed resources or absolute/relative file paths. */ - public List load(@NotNull String path) { - return loadAll(List.of(path)).getRules(); + public List load(@NonNull String path) { + return loadAll(List.of(path)).rules(); } /** @@ -89,7 +64,7 @@ public List load(@NotNull String path) { * Later files override earlier ones by rule id. * Global allowlists are merged from all files. */ - public LoadedRules loadAll(@NotNull List paths) { + public LoadedRules loadAll(@NonNull List paths) { if (paths.isEmpty()) { logger.warn("Secret detection rules path list is empty"); return new LoadedRules(List.of(), null); @@ -147,17 +122,9 @@ public LoadedRules loadAll(@NotNull List paths) { /** * Internal container for data loaded from a single YAML file. */ - private static class RuleFileData { - final List rules; - final GlobalAllowlist globalAllowlist; + private record RuleFileData(List rules, GlobalAllowlist globalAllowlist) {} - RuleFileData(List rules, GlobalAllowlist globalAllowlist) { - this.rules = rules; - this.globalAllowlist = globalAllowlist; - } - } - - private RuleFileData loadSingle(@NotNull String path) { + private RuleFileData loadSingle(@NonNull String path) { // Fail when we cannot read rules and scanning is enabled. if (path.isBlank()) { var message = "Secret detection rules path is empty"; @@ -234,7 +201,7 @@ private RuleFileData loadSingle(@NotNull String path) { } } - private @Nullable InputStream openStream(@NotNull String path) throws IOException { + private @Nullable InputStream openStream(@NonNull String path) throws IOException { if (path.startsWith("classpath:")) { String cp = path.substring("classpath:".length()); ClassPathResource resource = new ClassPathResource(cp); @@ -351,4 +318,3 @@ public static class GlobalAllowlist { public List skipMimeTypes; } } - diff --git a/server/src/main/java/org/eclipse/openvsx/search/DatabaseSearchService.java b/server/src/main/java/org/eclipse/openvsx/search/DatabaseSearchService.java index c7d327987..a0352ecd8 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/DatabaseSearchService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/DatabaseSearchService.java @@ -7,7 +7,6 @@ * * SPDX-License-Identifier: EPL-2.0 ********************************************************************************/ - package org.eclipse.openvsx.search; import jakarta.transaction.Transactional; diff --git a/server/src/main/java/org/eclipse/openvsx/search/RelevanceService.java b/server/src/main/java/org/eclipse/openvsx/search/RelevanceService.java index 9300c5b3d..56ff0af47 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/RelevanceService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/RelevanceService.java @@ -7,11 +7,8 @@ * * SPDX-License-Identifier: EPL-2.0 ********************************************************************************/ - package org.eclipse.openvsx.search; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import org.eclipse.openvsx.entities.Extension; import org.eclipse.openvsx.entities.ExtensionVersion; import org.eclipse.openvsx.repositories.RepositoryService; @@ -21,7 +18,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; import java.time.Duration; import java.time.LocalDateTime; @@ -30,7 +29,7 @@ /** * Provides relevance for a given extension */ -@Component +@Service public class RelevanceService { protected final Logger logger = LoggerFactory.getLogger(RelevanceService.class); @@ -112,7 +111,7 @@ private double calculateRelevance(Extension extension, ExtensionVersion latest, var message = "Invalid relevance for entry " + NamingUtil.toExtensionId(entry); try { message += " " + new ObjectMapper().writeValueAsString(stats); - } catch (JsonProcessingException exc) { + } catch (JacksonException exc) { // Ignore exception } logger.error(message); @@ -124,12 +123,7 @@ private double calculateRelevance(Extension extension, ExtensionVersion latest, } private double limit(double value) { - if (value < 0.0) - return 0.0; - else if (value > 1.0) - return 1.0; - else - return value; + return Math.clamp(value, 0.0, 1.0); } private double saturate(double value, double factor) { diff --git a/server/src/main/java/org/eclipse/openvsx/search/SearchConfig.java b/server/src/main/java/org/eclipse/openvsx/search/SearchConfig.java index 5d8c120aa..fc0ad7eec 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/SearchConfig.java +++ b/server/src/main/java/org/eclipse/openvsx/search/SearchConfig.java @@ -103,4 +103,4 @@ private SSLContext sslContext() { throw new RuntimeException("Error while getting default SSLContext", e); } } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/eclipse/openvsx/search/SearchResult.java b/server/src/main/java/org/eclipse/openvsx/search/SearchResult.java index 204c48c62..a0033751f 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/SearchResult.java +++ b/server/src/main/java/org/eclipse/openvsx/search/SearchResult.java @@ -9,7 +9,7 @@ * ****************************************************************************** */ package org.eclipse.openvsx.search; -import jakarta.validation.constraints.NotNull; +import org.jspecify.annotations.NonNull; import java.util.Collections; import java.util.List; @@ -24,7 +24,7 @@ public SearchResult() { hits = Collections.emptyList(); } - public SearchResult(long totalHits, @NotNull List hits) { + public SearchResult(long totalHits, @NonNull List hits) { this.totalHits = totalHits; this.hits = hits; } diff --git a/server/src/main/java/org/eclipse/openvsx/search/SearchUtilService.java b/server/src/main/java/org/eclipse/openvsx/search/SearchUtilService.java index 2c384a648..38264b2b3 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/SearchUtilService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/SearchUtilService.java @@ -7,11 +7,10 @@ * * SPDX-License-Identifier: EPL-2.0 ********************************************************************************/ - package org.eclipse.openvsx.search; import org.eclipse.openvsx.entities.Extension; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import java.util.Collection; import java.util.List; @@ -20,7 +19,7 @@ * Wrap all available implementations and redirect to the implementation pickup * from configuration */ -@Component +@Service public class SearchUtilService implements ISearchService { private final DatabaseSearchService databaseSearchService; @@ -88,5 +87,4 @@ public void updateSearchEntry(Extension extension) { public void removeSearchEntry(Extension extension) { getImplementation().removeSearchEntry(extension); } - } diff --git a/server/src/main/java/org/eclipse/openvsx/search/SimilarityCheckService.java b/server/src/main/java/org/eclipse/openvsx/search/SimilarityCheckService.java index cb4fd64da..a1e175c80 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/SimilarityCheckService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/SimilarityCheckService.java @@ -12,14 +12,14 @@ ********************************************************************************/ package org.eclipse.openvsx.search; -import jakarta.validation.constraints.NotNull; import org.eclipse.openvsx.entities.Extension; import org.eclipse.openvsx.entities.Namespace; import org.eclipse.openvsx.entities.NamespaceMembership; import org.eclipse.openvsx.entities.UserData; import org.eclipse.openvsx.repositories.RepositoryService; -import org.eclipse.openvsx.scanning.PublishCheck; -import org.jspecify.annotations.Nullable; +import org.eclipse.openvsx.scanning.PublishCheck; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Service; @@ -104,7 +104,7 @@ public PublishCheck.Result check(PublishCheck.Context context) { return PublishCheck.Result.pass(); } - var similarExt = similarExtensions.get(0); + var similarExt = similarExtensions.getFirst(); var latestVersion = repositories.findLatestVersion(similarExt, null, false, true); String similarDisplayName = latestVersion != null ? latestVersion.getDisplayName() : null; @@ -129,7 +129,7 @@ public List findSimilarExtensionsForPublishing( @Nullable String extensionName, @Nullable String namespaceName, @Nullable String displayName, - @NotNull UserData publishingUser + @NonNull UserData publishingUser ) { return similarityService.findSimilarExtensions( extensionName, @@ -147,8 +147,8 @@ public List findSimilarExtensionsForPublishing( * Callers should check {@link #isEnabled()} before invoking this method. */ public List findSimilarNamespacesForCreation( - @NotNull String namespaceName, - @NotNull UserData publishingUser + @NonNull String namespaceName, + @NonNull UserData publishingUser ) { return similarityService.findSimilarNamespaces( namespaceName, @@ -164,7 +164,7 @@ public List findSimilarNamespacesForCreation( * When configured, excludes namespaces where the user is a member (owner OR contributor). * This prevents false positives when uploading to namespaces the user legitimately controls. */ - private List getExcludedNamespaces(@NotNull UserData user) { + private List getExcludedNamespaces(@NonNull UserData user) { if (!config.isAllowSimilarityToOwnNames()) { return List.of(); } @@ -177,4 +177,3 @@ private List getExcludedNamespaces(@NotNull UserData user) { } } - diff --git a/server/src/main/java/org/eclipse/openvsx/search/SimilarityConfig.java b/server/src/main/java/org/eclipse/openvsx/search/SimilarityConfig.java index 19d2dd727..b7f8412ad 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/SimilarityConfig.java +++ b/server/src/main/java/org/eclipse/openvsx/search/SimilarityConfig.java @@ -18,8 +18,7 @@ /** * Configuration for extension similarity checking (name squatting protection). - * - * + *

* Configuration example: * ovsx: * similarity: @@ -35,7 +34,7 @@ public class SimilarityConfig { /** * If enabled, run similarity checks during extension publishing. - * + *

* Property: {@code ovsx.similarity.enabled} * Default: {@code true} */ @@ -45,7 +44,7 @@ public class SimilarityConfig { /** * If enabled, only run similarity checks for the very first upload of an extension. * This means updates to an already existing extension (new versions) are not blocked by similarity. - * + *

* Property: {@code ovsx.similarity.only-check-new-extensions} * Default: {@code false} */ @@ -54,11 +53,11 @@ public class SimilarityConfig { /** * Whether similarity failures are enforced (i.e. block publishing) when a similarity match is found. - * + *

* Why this exists: * - We sometimes want to run the check and store the audit trail (scan + failures) * without rejecting publication (monitor-only mode). - * + *

* Default is true to preserve the historic behavior: when the similarity check is enabled, * it blocks publishing on matches unless explicitly configured otherwise. */ @@ -81,7 +80,7 @@ public class SimilarityConfig { * Threshold used to decide whether two extension identifiers are "too similar". * The check compares the edit distance against a fraction of the identifier length. * Larger values are stricter. For example {@code 0.2} requires at least ~20% difference. - * + *

* Property: {@code ovsx.similarity.similarity-threshold} * Default: {@code 0.2}
* Valid range: {@code 0.0} - {@code 0.3} (validated at startup) @@ -92,7 +91,7 @@ public class SimilarityConfig { /** * If enabled, do not run similarity checks for verified publishers. * This reduces friction for trusted publishers while keeping checks for unverified ones. - * + *

* Property: {@code ovsx.similarity.skip-if-publisher-verified} * Default: {@code false} */ @@ -102,7 +101,7 @@ public class SimilarityConfig { /** * If enabled, compare new extensions only against extensions from verified publishers. * This reduces noise by focusing on protecting well-known publishers. - * + *

* Property: {@code ovsx.similarity.only-protect-verified-names} * Default: {@code false} */ @@ -113,7 +112,7 @@ public class SimilarityConfig { * If enabled, exclude namespaces where the publishing user is a member (owner or contributor) * from similarity checks. This prevents false positives when a user uploads to namespaces * they legitimately have access to. - * + *

* Property: {@code ovsx.similarity.allow-similarity-to-own-names} * Default: {@code true} */ @@ -159,4 +158,4 @@ public void validate() { "ovsx.similarity.similarity-threshold must be between 0.0 and 0.3, got: " + similarityThreshold); } } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/eclipse/openvsx/search/SimilarityService.java b/server/src/main/java/org/eclipse/openvsx/search/SimilarityService.java index 47d26f620..c8e49fe3f 100644 --- a/server/src/main/java/org/eclipse/openvsx/search/SimilarityService.java +++ b/server/src/main/java/org/eclipse/openvsx/search/SimilarityService.java @@ -12,12 +12,12 @@ ********************************************************************************/ package org.eclipse.openvsx.search; -import jakarta.validation.constraints.NotNull; import org.eclipse.openvsx.entities.Extension; import org.eclipse.openvsx.entities.Namespace; import org.eclipse.openvsx.repositories.RepositoryService; -import org.eclipse.openvsx.util.ErrorResultException; -import org.jspecify.annotations.Nullable; +import org.eclipse.openvsx.util.ErrorResultException; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -41,7 +41,7 @@ public List findSimilarExtensions( @Nullable String extensionName, @Nullable String namespaceName, @Nullable String displayName, - @NotNull List excludeNamespaces, + @NonNull List excludeNamespaces, double threshold, boolean verifiedOnly, int limit) { @@ -76,8 +76,8 @@ public List findSimilarExtensions( * Find namespaces similar to the given namespace name using Levenshtein distance. */ public List findSimilarNamespaces( - @NotNull String namespaceName, - @NotNull List excludeNamespaces, + @NonNull String namespaceName, + @NonNull List excludeNamespaces, double threshold, boolean verifiedOnly, int limit) { @@ -104,5 +104,4 @@ public List findSimilarNamespaces( ); } } - -} \ No newline at end of file +} diff --git a/server/src/main/java/org/eclipse/openvsx/storage/log/FastlyLogFileParser.java b/server/src/main/java/org/eclipse/openvsx/storage/log/FastlyLogFileParser.java index 100114f03..8b0bf0239 100644 --- a/server/src/main/java/org/eclipse/openvsx/storage/log/FastlyLogFileParser.java +++ b/server/src/main/java/org/eclipse/openvsx/storage/log/FastlyLogFileParser.java @@ -12,30 +12,26 @@ *****************************************************************************/ package org.eclipse.openvsx.storage.log; -import com.fasterxml.jackson.core.JacksonException; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.fasterxml.jackson.databind.module.SimpleModule; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.deser.std.StdDeserializer; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; - class FastlyLogFileParser implements LogFileParser { private final Logger logger = LoggerFactory.getLogger(FastlyLogFileParser.class); - private final ObjectMapper mapper; + private final JsonMapper mapper; public FastlyLogFileParser() { - this.mapper = new ObjectMapper(); - - SimpleModule module = new SimpleModule(); + var module = new SimpleModule(); module.addDeserializer(LogRecord.class, new LogRecordDeserializer()); - mapper.registerModule(module); + this.mapper = JsonMapper.builder().addModule(module).build(); } @Override @@ -57,20 +53,15 @@ public FastlyLogFileParser() { class LogRecordDeserializer extends StdDeserializer { public LogRecordDeserializer() { - this(null); - } - - public LogRecordDeserializer(Class vc) { - super(vc); + super(LogRecord.class); } @Override - public LogRecord deserialize(JsonParser jp, DeserializationContext ctxt) - throws IOException { - JsonNode node = jp.getCodec().readTree(jp); - String operation = node.get("request_method").asText(); + public LogRecord deserialize(JsonParser jp, DeserializationContext ctxt) throws JacksonException { + JsonNode node = ctxt.readTree(jp); + String operation = node.get("request_method").asString(); int status = (Integer) node.get("response_status").numberValue(); - String url = node.get("url").asText(); + String url = node.get("url").asString(); return new LogRecord(operation, status, url); } } diff --git a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java index f616b85c4..ab155bbb6 100644 --- a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java @@ -9,8 +9,6 @@ ********************************************************************************/ package org.eclipse.openvsx; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import jakarta.persistence.EntityManager; import org.apache.commons.lang3.ArrayUtils; @@ -66,6 +64,8 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.transaction.support.TransactionTemplate; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -2027,7 +2027,7 @@ private Namespace mockNamespace() { return namespace; } - private String namespaceJson(Consumer content) throws JsonProcessingException { + private String namespaceJson(Consumer content) throws JacksonException { var json = new NamespaceJson(); content.accept(json); return new ObjectMapper().writeValueAsString(json); @@ -2266,13 +2266,13 @@ private ExtensionVersion mockExtension(String targetPlatform, boolean withSignat return extVersion; } - private String extensionJson(Consumer content) throws JsonProcessingException { + private String extensionJson(Consumer content) throws JacksonException { var json = new ExtensionJson(); content.accept(json); return new ObjectMapper().writeValueAsString(json); } - private String queryResultJson(Consumer... contents) throws JsonProcessingException { + private String queryResultJson(Consumer... contents) throws JacksonException { var extensionJsons = new ArrayList(); for(var content : contents) { extensionJsons.add(extensionJson(content)); @@ -2388,7 +2388,7 @@ private void mockReviews() { .thenReturn(Streamable.of(review1, review2)); } - private String reviewsJson(Consumer content) throws JsonProcessingException { + private String reviewsJson(Consumer content) throws JacksonException { var json = new ReviewListJson(); json.setReviews(new ArrayList<>()); content.accept(json); @@ -2423,7 +2423,7 @@ private List mockSearch() { return List.of(extVersion); } - private String searchJson(Consumer content) throws JsonProcessingException { + private String searchJson(Consumer content) throws JacksonException { var json = new SearchResultJson(); json.setExtensions(new ArrayList<>()); content.accept(json); @@ -2557,7 +2557,7 @@ private void mockForPublish(String mode) { .then((Answer) invocation -> invocation.getArgument(0, Extension.class)); } - private String reviewJson(Consumer content) throws JsonProcessingException { + private String reviewJson(Consumer content) throws JacksonException { var json = new ReviewJson(); content.accept(json); return new ObjectMapper().writeValueAsString(json); @@ -2572,17 +2572,17 @@ private UserData mockUserData() { return userData; } - private String successJson(String message) throws JsonProcessingException { + private String successJson(String message) throws JacksonException { var json = ResultJson.success(message); return new ObjectMapper().writeValueAsString(json); } - private String errorJson(String message) throws JsonProcessingException { + private String errorJson(String message) throws JacksonException { var json = ResultJson.error(message); return new ObjectMapper().writeValueAsString(json); } - private String warningJson(String message) throws JsonProcessingException { + private String warningJson(String message) throws JacksonException { var json = ResultJson.warning(message); return new ObjectMapper().writeValueAsString(json); } diff --git a/server/src/test/java/org/eclipse/openvsx/UserAPITest.java b/server/src/test/java/org/eclipse/openvsx/UserAPITest.java index 0b390b350..3da1683e7 100644 --- a/server/src/test/java/org/eclipse/openvsx/UserAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/UserAPITest.java @@ -9,8 +9,6 @@ ********************************************************************************/ package org.eclipse.openvsx; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import jakarta.persistence.EntityManager; import org.eclipse.openvsx.accesstoken.AccessTokenConfig; @@ -56,6 +54,8 @@ import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.springframework.test.web.servlet.MockMvc; import org.springframework.transaction.support.TransactionTemplate; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; import java.time.LocalDateTime; import java.util.ArrayList; @@ -572,7 +572,7 @@ private UserData mockUserData() { return userData; } - private String userJson(Consumer content) throws JsonProcessingException { + private String userJson(Consumer content) throws JacksonException { var json = new UserJson(); content.accept(json); return new ObjectMapper().writeValueAsString(json); @@ -602,13 +602,13 @@ private void mockAccessTokens() { .thenReturn(Streamable.of(token1, token3)); } - private String accessTokenJson(Consumer content) throws JsonProcessingException { + private String accessTokenJson(Consumer content) throws JacksonException { var json = new AccessTokenJson(); content.accept(json); return new ObjectMapper().writeValueAsString(json); } - private String accessTokensJson(Consumer> content) throws JsonProcessingException { + private String accessTokensJson(Consumer> content) throws JacksonException { var json = new ArrayList(); content.accept(json); return new ObjectMapper().writeValueAsString(json); @@ -636,13 +636,13 @@ private void mockOwnMemberships() { .thenReturn(Streamable.of(membership1, membership2)); } - private String namespacesJson(Consumer> content) throws JsonProcessingException { + private String namespacesJson(Consumer> content) throws JacksonException { var json = new ArrayList(); content.accept(json); return new ObjectMapper().writeValueAsString(json); } - private String extensionJson(Consumer> content) throws JsonProcessingException { + private String extensionJson(Consumer> content) throws JacksonException { var json = new ArrayList(); content.accept(json); return new ObjectMapper().writeValueAsString(json); @@ -669,19 +669,19 @@ private void mockNamespaceMemberships(String userRole) { .thenReturn(userRole.equals(NamespaceMembership.ROLE_OWNER) ? List.of(membership1, membership2) : Collections.emptyList()); } - private String membershipsJson(Consumer content) throws JsonProcessingException { + private String membershipsJson(Consumer content) throws JacksonException { var json = new NamespaceMembershipListJson(); json.setNamespaceMemberships(new ArrayList<>()); content.accept(json); return new ObjectMapper().writeValueAsString(json); } - private String successJson(String message) throws JsonProcessingException { + private String successJson(String message) throws JacksonException { var json = ResultJson.success(message); return new ObjectMapper().writeValueAsString(json); } - private String errorJson(String message) throws JsonProcessingException { + private String errorJson(String message) throws JacksonException { var json = ResultJson.error(message); return new ObjectMapper().writeValueAsString(json); } diff --git a/server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java b/server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java index d4d5e0fba..6d3e7ee42 100644 --- a/server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java @@ -107,11 +107,10 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.transaction.support.TransactionTemplate; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import jakarta.persistence.EntityManager; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; @WebMvcTest(AdminAPI.class) @MockitoBean(types = { @@ -1495,13 +1494,13 @@ private Namespace mockNamespace(int numberOfMembers) { return namespace; } - private String namespaceJson(Consumer content) throws JsonProcessingException { + private String namespaceJson(Consumer content) throws JacksonException { var json = new NamespaceJson(); content.accept(json); return new ObjectMapper().writeValueAsString(json); } - private String namespaceMemberJson(Consumer content) throws JsonProcessingException { + private String namespaceMemberJson(Consumer content) throws JacksonException { var json = new NamespaceMembershipListJson(); content.accept(json); return new ObjectMapper().writeValueAsString(json); @@ -1638,30 +1637,30 @@ private String createVersion(int major) { return major + ".0.0"; } - private String adminStatisticsJson(Consumer content) throws JsonProcessingException { + private String adminStatisticsJson(Consumer content) throws JacksonException { var json = new AdminStatisticsJson(); content.accept(json); return new ObjectMapper().writeValueAsString(json); } - private String extensionJson(Consumer content) throws JsonProcessingException { + private String extensionJson(Consumer content) throws JacksonException { var json = new ExtensionJson(); content.accept(json); return new ObjectMapper().writeValueAsString(json); } - private String publishInfoJson(Consumer content) throws JsonProcessingException { + private String publishInfoJson(Consumer content) throws JacksonException { var json = new UserPublishInfoJson(); content.accept(json); return new ObjectMapper().writeValueAsString(json); } - private String successJson(String message) throws JsonProcessingException { + private String successJson(String message) throws JacksonException { var json = ResultJson.success(message); return new ObjectMapper().writeValueAsString(json); } - private String errorJson(String message) throws JsonProcessingException { + private String errorJson(String message) throws JacksonException { var json = ResultJson.error(message); return new ObjectMapper().writeValueAsString(json); } diff --git a/server/src/test/java/org/eclipse/openvsx/scanning/ResponseExtractorTest.java b/server/src/test/java/org/eclipse/openvsx/scanning/ResponseExtractorTest.java index 95e924c1a..26770dc38 100644 --- a/server/src/test/java/org/eclipse/openvsx/scanning/ResponseExtractorTest.java +++ b/server/src/test/java/org/eclipse/openvsx/scanning/ResponseExtractorTest.java @@ -14,7 +14,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; import java.util.Map; @@ -29,7 +29,7 @@ class ResponseExtractorTest { @BeforeEach void setUp() { - extractor = new HttpResponseExtractor(new ObjectMapper()); + extractor = new HttpResponseExtractor(new JsonMapper()); } // === String extraction tests === diff --git a/server/src/test/java/org/eclipse/openvsx/scanning/SecretDetectorFactoryTest.java b/server/src/test/java/org/eclipse/openvsx/scanning/SecretDetectorFactoryTest.java index 7a8024c50..ddf24fb50 100644 --- a/server/src/test/java/org/eclipse/openvsx/scanning/SecretDetectorFactoryTest.java +++ b/server/src/test/java/org/eclipse/openvsx/scanning/SecretDetectorFactoryTest.java @@ -12,6 +12,7 @@ ********************************************************************************/ package org.eclipse.openvsx.scanning; +import org.jspecify.annotations.NonNull; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; @@ -142,7 +143,7 @@ private static final class TrackingRuleLoader extends SecretRuleLoader { boolean wasCalled = false; @Override - public LoadedRules loadAll(List paths) { + public LoadedRules loadAll(@NonNull List paths) { wasCalled = true; // Delegate to the real loader so the behavior stays realistic. return super.loadAll(paths); diff --git a/server/src/test/java/org/eclipse/openvsx/scanning/SecretRuleLoaderTest.java b/server/src/test/java/org/eclipse/openvsx/scanning/SecretRuleLoaderTest.java index 4ba06d8d8..0756c522e 100644 --- a/server/src/test/java/org/eclipse/openvsx/scanning/SecretRuleLoaderTest.java +++ b/server/src/test/java/org/eclipse/openvsx/scanning/SecretRuleLoaderTest.java @@ -33,7 +33,7 @@ void loadAll_overridesRulesByIdAndPreservesOrder() { List rules = loader.loadAll(List.of( "classpath:org/eclipse/openvsx/scanning/secret-rules-a.yaml", "classpath:org/eclipse/openvsx/scanning/secret-rules-b.yaml" - )).getRules(); + )).rules(); assertEquals(2, rules.size()); @@ -54,8 +54,8 @@ void loadAll_overridesRulesByIdAndPreservesOrder() { void loadAll_returnsEmptyWhenPathsEmpty() { // Empty path lists should return empty result with a warning (not throw) SecretRuleLoader.LoadedRules result = loader.loadAll(List.of()); - assertTrue(result.getRules().isEmpty(), "Should return empty rules list"); - assertNull(result.getGlobalAllowlist(), "Should return null global allowlist"); + assertTrue(result.rules().isEmpty(), "Should return empty rules list"); + assertNull(result.globalAllowlist(), "Should return null global allowlist"); } @Test @@ -85,7 +85,7 @@ void loadAll_parsesGlobalAllowlistPaths() { "classpath:org/eclipse/openvsx/scanning/secret-rules-with-allowlist.yaml" )); - SecretRuleLoader.GlobalAllowlist allowlist = Objects.requireNonNull(result.getGlobalAllowlist()); + SecretRuleLoader.GlobalAllowlist allowlist = Objects.requireNonNull(result.globalAllowlist()); assertNotNull(allowlist.paths); assertEquals(3, allowlist.paths.size()); assertTrue(allowlist.paths.contains("node_modules/")); @@ -99,7 +99,7 @@ void loadAll_parsesGlobalAllowlistRegexes() { "classpath:org/eclipse/openvsx/scanning/secret-rules-with-allowlist.yaml" )); - SecretRuleLoader.GlobalAllowlist allowlist = Objects.requireNonNull(result.getGlobalAllowlist()); + SecretRuleLoader.GlobalAllowlist allowlist = Objects.requireNonNull(result.globalAllowlist()); assertNotNull(allowlist.regexes); assertEquals(3, allowlist.regexes.size()); assertTrue(allowlist.regexes.contains("^test$")); @@ -113,7 +113,7 @@ void loadAll_parsesGlobalAllowlistStopwords() { "classpath:org/eclipse/openvsx/scanning/secret-rules-with-allowlist.yaml" )); - SecretRuleLoader.GlobalAllowlist allowlist = Objects.requireNonNull(result.getGlobalAllowlist()); + SecretRuleLoader.GlobalAllowlist allowlist = Objects.requireNonNull(result.globalAllowlist()); assertNotNull(allowlist.stopwords); assertEquals(3, allowlist.stopwords.size()); assertTrue(allowlist.stopwords.contains("example")); @@ -127,7 +127,7 @@ void loadAll_parsesGlobalAllowlistFileExtensions() { "classpath:org/eclipse/openvsx/scanning/secret-rules-with-allowlist.yaml" )); - SecretRuleLoader.GlobalAllowlist allowlist = Objects.requireNonNull(result.getGlobalAllowlist()); + SecretRuleLoader.GlobalAllowlist allowlist = Objects.requireNonNull(result.globalAllowlist()); assertNotNull(allowlist.fileExtensions); assertEquals(3, allowlist.fileExtensions.size()); assertTrue(allowlist.fileExtensions.contains(".png")); @@ -142,9 +142,9 @@ void loadAll_handlesYamlWithoutGlobalAllowlist() { )); // Should return null when no global allowlist is present - assertNull(result.getGlobalAllowlist()); + assertNull(result.globalAllowlist()); // But rules should still be loaded - assertEquals(1, result.getRules().size()); + assertEquals(1, result.rules().size()); } @Test @@ -155,7 +155,7 @@ void loadAll_mergesGlobalAllowlistsFromAllFiles() { "classpath:org/eclipse/openvsx/scanning/secret-rules-allowlist-2.yaml" // has different allowlist )); - SecretRuleLoader.GlobalAllowlist allowlist = result.getGlobalAllowlist(); + SecretRuleLoader.GlobalAllowlist allowlist = result.globalAllowlist(); assertNotNull(allowlist); // Paths should be merged (3 from first file + 2 from second = 5) @@ -194,7 +194,7 @@ void loadAll_mergesGlobalAllowlistsFromAllFiles() { assertTrue(allowlist.fileExtensions.contains(".gif")); // Both rules should be present (merged) - assertEquals(2, result.getRules().size()); + assertEquals(2, result.rules().size()); } @Test @@ -204,11 +204,11 @@ void loadAll_returnsRulesAndGlobalAllowlistTogether() { )); // Verify both components are present - assertNotNull(result.getRules()); - assertEquals(1, result.getRules().size()); - assertEquals("test-rule-1", result.getRules().getFirst().getId()); + assertNotNull(result.rules()); + assertEquals(1, result.rules().size()); + assertEquals("test-rule-1", result.rules().getFirst().getId()); - SecretRuleLoader.GlobalAllowlist allowlist = result.getGlobalAllowlist(); + SecretRuleLoader.GlobalAllowlist allowlist = result.globalAllowlist(); assertNotNull(allowlist); assertNotNull(allowlist.paths); assertNotNull(allowlist.regexes); @@ -224,7 +224,7 @@ void loadAll_mergesPartialAllowlists() { "classpath:org/eclipse/openvsx/scanning/secret-rules-allowlist-3.yaml" // partial (only paths and stopwords) )); - SecretRuleLoader.GlobalAllowlist allowlist = result.getGlobalAllowlist(); + SecretRuleLoader.GlobalAllowlist allowlist = result.globalAllowlist(); assertNotNull(allowlist); // Paths: 3 from first + 1 from second = 4 @@ -242,7 +242,7 @@ void loadAll_mergesPartialAllowlists() { assertEquals(3, allowlist.fileExtensions.size()); // Rules should be merged (1 + 1 = 2) - assertEquals(2, result.getRules().size()); + assertEquals(2, result.rules().size()); } @Test @@ -253,7 +253,7 @@ void loadAll_mergesAllowlistWhenFirstFileHasNone() { "classpath:org/eclipse/openvsx/scanning/secret-rules-with-allowlist.yaml" // has allowlist )); - SecretRuleLoader.GlobalAllowlist allowlist = result.getGlobalAllowlist(); + SecretRuleLoader.GlobalAllowlist allowlist = result.globalAllowlist(); assertNotNull(allowlist); assertEquals(3, allowlist.paths.size()); assertEquals(3, allowlist.regexes.size()); @@ -261,7 +261,7 @@ void loadAll_mergesAllowlistWhenFirstFileHasNone() { assertEquals(3, allowlist.fileExtensions.size()); // Both rules should be present - assertEquals(2, result.getRules().size()); + assertEquals(2, result.rules().size()); } @Test @@ -273,7 +273,7 @@ void loadAll_mergesThreeFilesWithMixedAllowlists() { "classpath:org/eclipse/openvsx/scanning/secret-rules-allowlist-2.yaml" // full )); - SecretRuleLoader.GlobalAllowlist allowlist = result.getGlobalAllowlist(); + SecretRuleLoader.GlobalAllowlist allowlist = result.globalAllowlist(); assertNotNull(allowlist); // Should have items from file 1 and file 3 (file 2 has no allowlist) @@ -283,10 +283,10 @@ void loadAll_mergesThreeFilesWithMixedAllowlists() { assertEquals(5, allowlist.fileExtensions.size()); // 3 + 2 // All three rules should be present, but rule-a is overridden by file 2 - assertEquals(3, result.getRules().size()); + assertEquals(3, result.rules().size()); // Verify rule-a was overridden (should have the description from file A, not the original) - SecretRule ruleA = result.getRules().stream() + SecretRule ruleA = result.rules().stream() .filter(r -> r.getId().equals("rule-a")) .findFirst() .orElseThrow(); @@ -306,16 +306,16 @@ void loadAll_overridesRulesButMergesAllowlists() { )); // All three unique rules should be present - assertEquals(3, result.getRules().size()); + assertEquals(3, result.rules().size()); - List ruleIds = result.getRules().stream() + List ruleIds = result.rules().stream() .map(SecretRule::getId) .sorted() .toList(); assertEquals(List.of("test-rule-1", "test-rule-2", "test-rule-3"), ruleIds); // All allowlists should be merged - SecretRuleLoader.GlobalAllowlist allowlist = result.getGlobalAllowlist(); + SecretRuleLoader.GlobalAllowlist allowlist = result.globalAllowlist(); assertNotNull(allowlist); // Paths: 3 + 2 + 1 = 6 @@ -340,7 +340,7 @@ void loadAll_allowsDuplicateAllowlistItems() { "classpath:org/eclipse/openvsx/scanning/secret-rules-with-allowlist.yaml" // same file twice )); - SecretRuleLoader.GlobalAllowlist allowlist = result.getGlobalAllowlist(); + SecretRuleLoader.GlobalAllowlist allowlist = result.globalAllowlist(); assertNotNull(allowlist); // Items should be duplicated (3 + 3 = 6) @@ -350,7 +350,7 @@ void loadAll_allowsDuplicateAllowlistItems() { assertEquals(6, allowlist.fileExtensions.size()); // But rules should not be duplicated (same ID) - assertEquals(1, result.getRules().size()); + assertEquals(1, result.rules().size()); } }