Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
47359d2
Bumped vertx to 5.0
And1sS Apr 8, 2026
00eff37
Replaced custom attempt to implement rolling window for circuit break…
And1sS Apr 8, 2026
28416bf
Removed circuit breaker wrapper entirely.
And1sS Apr 14, 2026
68d2154
Fixed modules.
And1sS May 28, 2026
2b2eeb1
Bumped vert.x to 5.1.0. Fixed functional test that has been failing d…
And1sS Jun 1, 2026
e3f97ea
WIP
And1sS Apr 20, 2026
173dd13
Fix logger conflict
Net-burst May 21, 2026
1753042
WIP
Net-burst Jun 1, 2026
43d5cd7
WIP
Jun 1, 2026
525d648
WIP
Jun 2, 2026
e93e6eb
Minor refactoring.
And1sS Jun 2, 2026
0615b5b
Refactored ContextRunner.
And1sS Jun 3, 2026
5fd97e0
Unit tests fixes.
And1sS Jun 3, 2026
12cf940
Another minor refactoring.
And1sS Jun 3, 2026
e750d10
Fix malformed Groovy files
Net-burst Jun 4, 2026
1dcf911
Revert logger control removal and fix remaining issues
Net-burst Jun 4, 2026
ca45f9b
Bump to Java 25
Net-burst Jun 4, 2026
bea17a3
Refactor Initializable interface to use Future (#4527)
Lightwood13 Jun 4, 2026
d8effa6
Merge branch 'dependencies-update-spring' into dependencies-update
And1sS Jun 4, 2026
335154a
Fix remaining (hopefully) functional test issues
Jun 4, 2026
a5be9bf
Merge branch 'dependencies-update-spring' into dependencies-update
And1sS Jun 8, 2026
a5fe57c
Reverted unnecessary formating changes.
And1sS Jun 8, 2026
7595087
Fixed couple of functional tests.
And1sS Jun 8, 2026
c1b3dbf
Another functional tests fixes.
And1sS Jun 8, 2026
dd461cb
Another round of functional tests fixes.
And1sS Jun 8, 2026
1bd591d
update analytics tests
osulzhenko Jun 9, 2026
fbc2197
Fixed optable npe.
And1sS Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# From https://github.com/microsoft/vscode-dev-containers/blob/master/containers/go/.devcontainer/Dockerfile
ARG VARIANT="21-jdk-bookworm"
ARG VARIANT="25-jdk-trixie"
FROM mcr.microsoft.com/vscode/devcontainers/java:${VARIANT}
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"dockerfile": "Dockerfile",
"args": {
// Update the VARIANT arg to pick a version of Java
"VARIANT": "21-jdk-bookworm",
"VARIANT": "25-jdk-trixie",
}
},
"containerEnv": {
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 21
java-version: 25

- name: Cache Maven packages
uses: actions/cache@v5
Expand All @@ -52,7 +52,7 @@ jobs:

- name: Build with Maven
if: matrix.build-mode == 'manual'
run: mvn -B package --file extra/pom.xml
run: mvn -B clean package -DskipUnitTests --file extra/pom.xml

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docker-image-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
java: [ 21 ]
java: [ 25 ]
dockerfile-path: [ Dockerfile, Dockerfile-modules ]
include:
- dockerfile-path: Dockerfile
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-functional-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:

strategy:
matrix:
java: [ 21 ]
java: [ 25 ]

steps:
- uses: actions/checkout@v5
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-java-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:

strategy:
matrix:
java: [ 21 ]
java: [ 25 ]

steps:
- uses: actions/checkout@v5
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-module-functional-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:

strategy:
matrix:
java: [ 21 ]
java: [ 25 ]

steps:
- uses: actions/checkout@v5
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-asset-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
java: [ 21 ]
java: [ 25 ]
steps:
- uses: actions/checkout@v5
- name: Set up JDK
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM amazoncorretto:21.0.8-al2023
FROM amazoncorretto:25.0.3-al2023-headless

WORKDIR /app/prebid-server

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile-modules
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM amazoncorretto:21.0.8-al2023
FROM amazoncorretto:25.0.3-al2023-headless

WORKDIR /app/prebid-server

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Follow next steps to create JAR file which can be deployed locally.

- Install prerequsites
- Java SDK: Oracle's or Corretto. Let us know if there's a distribution PBS-Java doesn't work with.
- Java SDK Version: 21
- Java SDK Version: 25
- Maven

- Clone the project:
Expand Down
6 changes: 3 additions & 3 deletions docs/config-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ This parameter exists to allow to change the location of the directory Vert.x wi
- `http-client.connect-timeout-ms` - set the connect timeout.
- `http-client.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make http client more robust.
- `http-client.circuit-breaker.opening-threshold` - the number of failures before opening the circuit.
- `http-client.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count reached.
- `http-client.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count reached. (Must be a multiple of 1000)
- `http-client.circuit-breaker.closing-interval-ms` - time spent in open state before attempting to re-try.
- `http-client.circuit-breaker.idle-expire-hours` - idle time to clean the circuit breaker up.
- `http-client.use-compression` - if equals to `true` httpclient compression is enabled for requests (see [also](https://vertx.io/docs/apidocs/io/vertx/core/http/HttpClientOptions.html#setTryUseCompression-boolean-))
Expand Down Expand Up @@ -338,7 +338,7 @@ For database data source available next options:
- `settings.database.stored-responses-query` - the SQL query to fetch stored responses.
- `settings.database.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make database client more robust.
- `settings.database.circuit-breaker.opening-threshold` - the number of failures before opening the circuit.
- `settings.database.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count reached.
- `settings.database.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count reached. (Must be a multiple of 1000)
- `settings.database.circuit-breaker.closing-interval-ms` - time spent in open state before attempting to re-try.

For HTTP data source available next options:
Expand Down Expand Up @@ -473,7 +473,7 @@ If not defined in config all other Health Checkers would be disabled and endpoin
- `geolocation.enabled` - if equals to `true` the geo location service will be used to determine the country for client request.
- `geolocation.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make geo location client more robust.
- `geolocation.circuit-breaker.opening-threshold` - the number of failures before opening the circuit.
- `geolocation.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count reached.
- `geolocation.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count reached. (Must be a multiple of 1000)
- `geolocation.circuit-breaker.closing-interval-ms` - time spent in open state before attempting to re-try.
- `geolocation.type` - set the geo location service provider, can be `maxmind` or custom provided by hosting company.
- `geolocation.maxmind` - section for [MaxMind](https://www.maxmind.com) configuration as geo location service provider.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,83 +55,50 @@ public void disableScan() {
}

public Future<BidsScanResult> submitBids(RedisBidsData bids) {
final Promise<BidsScanResult> scanResult = Promise.promise();

final RedisAPI readRedisNodeAPI = this.readRedisNode.getRedisAPI();
final boolean shouldSubmit = !isScanDisabled
&& readRedisNodeAPI != null && !bids.getBresps().isEmpty();

if (shouldSubmit) {
readRedisNodeAPI.get("function_submit_bids", submitHash -> {
final Object submitHashResult = submitHash.result();
if (submitHashResult != null) {
final List<String> readArgs = List.of(
submitHashResult.toString(),
"0",
toBidsAsJson(bids),
apiKey,
"true");

readRedisNodeAPI.evalsha(readArgs, response -> {
if (response.result() != null) {
final BidsScanResult parserResult = redisParser
.parseBidsScanResult(response.result().toString());
final boolean isAnyRoSkipped = parserResult.getBidScanResults()
.stream().anyMatch(BidScanResult::isRoSkipped);

if (isAnyRoSkipped) {
reSubmitBidsToWriteNode(readArgs, scanResult);
} else {
scanResult.complete(parserResult);
}
} else {
scanResult.complete(getEmptyScanResult());
}
});
} else {
scanResult.complete(getEmptyScanResult());
}
});

return scanResult.future();
if (isScanDisabled || readRedisNodeAPI == null || bids.getBresps().isEmpty()) {
return Future.succeededFuture(getEmptyScanResult());
}

return Future.succeededFuture(getEmptyScanResult());
return readRedisNodeAPI.get("function_submit_bids")
.map(Response::toString)
.map(response -> List.of(response, "0", toBidsAsJson(bids), apiKey, "true"))
.compose(args -> scanBids(args, readRedisNodeAPI))
.otherwise(ignored -> getEmptyScanResult());
}

private Future<BidsScanResult> scanBids(List<String> args, RedisAPI redisAPI) {
return redisAPI.evalsha(args)
.map(Response::toString)
.map(redisParser::parseBidsScanResult)
.compose(parsedResult -> parsedResult.getBidScanResults()
.stream().anyMatch(BidScanResult::isRoSkipped)
? reSubmitBidsToWriteNode(args)
: Future.succeededFuture(parsedResult));
}

private void reSubmitBidsToWriteNode(List<String> readArgs, Promise<BidsScanResult> scanResult) {
private Future<BidsScanResult> reSubmitBidsToWriteNode(List<String> readArgs) {
final RedisAPI writeRedisAPI = this.writeRedisNode.getRedisAPI();
if (writeRedisAPI != null) {
final List<String> writeArgs = readArgs.stream().limit(4).toList();
writeRedisAPI.evalsha(writeArgs, response -> {
if (response.result() != null) {
final BidsScanResult parserResult = redisParser
.parseBidsScanResult(response.result().toString());

scanResult.complete(parserResult);
} else {
scanResult.complete(getEmptyScanResult());
}
});
} else {
scanResult.complete(getEmptyScanResult());
if (writeRedisAPI == null) {
return Future.succeededFuture(getEmptyScanResult());
}

final List<String> writeArgs = readArgs.stream().limit(4).toList();
return writeRedisAPI.evalsha(writeArgs)
.map(Response::toString)
.map(redisParser::parseBidsScanResult);
}

public Future<Boolean> isScanDisabledFlag() {
final RedisAPI redisAPI = this.readRedisNode.getRedisAPI();
final Promise<Boolean> isDisabled = Promise.promise();

if (redisAPI != null) {
redisAPI.get("scan-disabled", scanDisabledValue -> {
final Response scanDisabled = scanDisabledValue.result();
isDisabled.complete(scanDisabled != null && "true".equals(scanDisabled.toString()));
});

return isDisabled.future();
if (redisAPI == null) {
return Future.succeededFuture(true);
}

return Future.succeededFuture(true);
return redisAPI.get("scan-disabled")
.map(Response::toString)
.map(Boolean::parseBoolean)
.otherwise(false);
}

private String toBidsAsJson(RedisBidsData bids) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public RedisAPI getRedisAPI() {
*/
private void createRedisClient(Handler<AsyncResult<RedisConnection>> handler, boolean isReconnect) {
Redis.createClient(vertx, options)
.connect(onConnect -> {
.connect()
.onComplete(onConnect -> {
if (onConnect.succeeded()) {
connection = onConnect.result();
connection.exceptionHandler(e -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.maxmind.geoip2.DatabaseReader;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.file.FileSystem;
import io.vertx.core.file.OpenOptions;
Expand Down Expand Up @@ -45,11 +44,10 @@ public DatabaseReaderFactory(GreenbidsRealTimeDataProperties properties, Vertx v
}

@Override
public void initialize(Promise<Void> initializePromise) {
downloadAndExtract()
public Future<Void> initialize() {
return downloadAndExtract()
.onSuccess(databaseReaderRef::set)
.<Void>mapEmpty()
.onComplete(initializePromise);
.mapEmpty();
}

private Future<DatabaseReader> downloadAndExtract() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.prebid.server.exception.InvalidRequestException;
import org.prebid.server.hooks.execution.v1.auction.AuctionResponsePayloadImpl;
import org.prebid.server.hooks.modules.optable.targeting.model.openrtb.Audience;
Expand Down Expand Up @@ -70,14 +71,15 @@ private ObjectNode targetingToObjectNode(List<Audience> targeting) {

for (Audience audience : targeting) {
final List<AudienceId> ids = audience.getIds();
if (CollectionUtils.isEmpty(ids)) {
final String keyspace = audience.getKeyspace();
if (CollectionUtils.isEmpty(ids) || StringUtils.isEmpty(keyspace)) {
continue;
}

final String joinedIds = ids.stream()
.map(AudienceId::getId)
.collect(Collectors.joining(","));
node.putIfAbsent(audience.getKeyspace(), TextNode.valueOf(joinedIds));
node.putIfAbsent(keyspace, TextNode.valueOf(joinedIds));
}

return node;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Future;
import io.vertx.core.MultiMap;
import io.vertx.core.http.impl.headers.HeadersMultiMap;
import io.vertx.core.http.HttpHeaders;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.prebid.server.execution.timeout.Timeout;
Expand Down Expand Up @@ -69,7 +69,7 @@ private String resolveEndpoint(String tenant, String origin) {
}

private static MultiMap headers(OptableTargetingProperties properties, List<String> ips, String userAgent) {
final MultiMap headers = HeadersMultiMap.headers()
final MultiMap headers = HttpHeaders.headers()
.add(HttpUtil.ACCEPT_HEADER, "application/json");

if (userAgent != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
import io.vertx.core.MultiMap;
import io.vertx.core.http.impl.headers.HeadersMultiMap;
import io.vertx.core.http.HttpHeaders;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpStatus;
import org.prebid.server.activity.infrastructure.ActivityInfrastructure;
Expand Down Expand Up @@ -193,7 +193,7 @@ protected Device givenDevice() {
}

protected HttpClientResponse givenSuccessHttpResponse(String fileName) {
final MultiMap headers = HeadersMultiMap.headers().add("Content-Type", "application/json");
final MultiMap headers = HttpHeaders.headers().add("Content-Type", "application/json");
return HttpClientResponse.of(HttpStatus.SC_OK, headers, givenBodyFromFile(fileName));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.prebid.server.hooks.modules.optable.targeting.v1.core;

import com.fasterxml.jackson.databind.node.ObjectNode;
import com.iab.openrtb.response.BidResponse;
import org.junit.jupiter.api.Test;
import org.prebid.server.hooks.execution.v1.auction.AuctionResponsePayloadImpl;
import org.prebid.server.hooks.modules.optable.targeting.model.openrtb.Audience;
Expand All @@ -10,10 +11,30 @@

import java.util.List;

import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;

public class BidResponseEnricherTest extends BaseOptableTest {

@Test
public void shouldSkipEmptyKeyspaceWhileEnrichingBidResponse() {
// given
final BidResponse bidResponse = givenBidResponse();
final AuctionResponsePayload auctionResponsePayload = AuctionResponsePayloadImpl.of(bidResponse);
final List<Audience> targeting = singletonList(new Audience(
"provider",
List.of(new AudienceId("audienceId"), new AudienceId("audienceId2")),
null,
1));
final BidResponseEnricher enricher = BidResponseEnricher.of(targeting, mapper, jsonMerger);

// when
final AuctionResponsePayload result = enricher.apply(auctionResponsePayload);

// then
assertThat(result.bidResponse()).isEqualTo(bidResponse);
}

@Test
public void shouldEnrichBidResponseByTargetingKeywords() {
// given
Expand Down
Loading
Loading