Skip to content

Commit 1008e43

Browse files
authored
Merge pull request #2851 from hongwei1/fix/issue-9-remove-dispatch
build: replace dispatch-core/async-http-client with OkHttp3; upgrade runtime to JDK 25
2 parents 7e76469 + 8a12d24 commit 1008e43

72 files changed

Lines changed: 1070 additions & 3597 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/Dockerfile_PreBuild

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
1-
FROM gcr.io/distroless/java:11
1+
FROM eclipse-temurin:25-jre-alpine
2+
3+
RUN addgroup -S obp && adduser -S -h /app -G obp obp
24

35
# Copy OBP source code
46
# Copy build artifact (JAR file) from maven build
57
COPY /obp-api/target/obp-api.jar /app/obp-api.jar
68
WORKDIR /app
7-
CMD ["obp-api.jar"]
9+
USER obp
10+
ENTRYPOINT ["java", \
11+
"--add-opens", "java.base/java.lang=ALL-UNNAMED", \
12+
"--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", \
13+
"--add-opens", "java.base/java.io=ALL-UNNAMED", \
14+
"--add-opens", "java.base/java.util=ALL-UNNAMED", \
15+
"--add-opens", "java.base/java.util.concurrent=ALL-UNNAMED", \
16+
"-jar", "/app/obp-api.jar"]

.github/workflows/build_container.yml

Lines changed: 96 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ env:
1010

1111
# ---------------------------------------------------------------------------
1212
# compile — compiles everything once, packages the JAR, uploads classes
13-
# test — 4-way matrix downloads compiled output and runs a shard of tests
13+
# test — 9-way matrix downloads compiled output and runs a shard of tests
1414
# docker — downloads compiled output, builds and pushes the container image
1515
#
1616
# Wall-clock target:
1717
# compile ~10 min (parallel with setup of test shards)
18-
# tests ~8 min (4 shards in parallel after compile finishes)
18+
# tests ~8 min (9 shards in parallel after compile finishes)
1919
# docker ~3 min (after all shards pass)
2020
# total ~21 min (vs ~30 min single-job)
2121
# ---------------------------------------------------------------------------
@@ -30,18 +30,21 @@ jobs:
3030
steps:
3131
- uses: actions/checkout@v4
3232

33-
- name: Set up JDK 11
33+
- name: Set up JDK 25
3434
uses: actions/setup-java@v4
3535
with:
36-
java-version: "11"
37-
distribution: "adopt"
36+
java-version: "25"
37+
distribution: "temurin"
3838
cache: maven # caches ~/.m2/repository keyed on pom.xml hash
3939

4040
- name: Setup production props
4141
run: |
4242
cp obp-api/src/main/resources/props/sample.props.template \
4343
obp-api/src/main/resources/props/production.default.props
4444
45+
- name: Lint — test-isolation (no setPropsValues at class/feature body)
46+
run: python3 .github/scripts/check_test_isolation.py
47+
4548
- name: Compile and install (skip test execution)
4649
run: |
4750
# -DskipTests — compile test sources but do NOT run them
@@ -74,24 +77,36 @@ jobs:
7477
path: push/
7578

7679
# --------------------------------------------------------------------------
77-
# Job 2: test (4-way matrix)
80+
# Job 2: test (9-way matrix, mirrors build_pull_request.yml shard layout)
7881
#
79-
# Shard assignment (based on actual clean-run timings):
80-
# Shard 1 ~258s v4_0_0(258)
81-
# Shard 2 ~267s v6_0_0(122) v5_0_0(42) v3_0_0(39) v2_1_0(35) v2_2_0(12) …
82-
# Shard 3 ~252s v1_2_1(137) ResourceDocs(67) berlin(34) util(12) …
83-
# Shard 4 ~232s v5_1_0(79) v3_1_0(65) http4sbridge(52) v7_0_0(45) … + catch-all
82+
# Shard assignment (wall-clock on GitHub-hosted ubuntu-latest runners):
83+
# Shard 1 ~157s v4_0_0 non-Dynamic (explicit class list, ~58 classes)
84+
# Shard 2 ~257s v1_2_1 (single 6604-line suite, isolated)
85+
# Shard 3 ~155s v6_0_0 only (isolated after v2_x moved to shard 7)
86+
# Shard 4 ~183s v5_1_0 v5_0_0 v3_0_0
87+
# Shard 5 ~193s ResourceDocs v3_1_0 v1_4_0 v1_3_0
88+
# Shard 6 ~168s v7_0_0 http4sbridge UKOpenBanking
89+
# Shard 7 ~280s model + views + customer + util + berlin + v2_x
90+
# Shard 8 ~240s connector + auth + login + mgmt + metrics + catch-all
91+
# Shard 9 ~110s v4_0_0 Dynamic* (6 heavy test classes)
8492
# --------------------------------------------------------------------------
8593
test:
8694
needs: compile
8795
runs-on: ubuntu-latest
96+
timeout-minutes: 35
8897
strategy:
8998
fail-fast: false
9099
matrix:
91100
include:
92101
- shard: 1
93-
name: "v4 only (bottleneck pkg)"
94-
# ~258s — single largest package, kept on its own shard
102+
name: "v4 non-Dynamic"
103+
# v4_0_0 split: non-Dynamic classes only. Dynamic* on shard 9.
104+
# test_filter is a marker, not a literal wildcardSuites value: the "Run tests"
105+
# step below discovers code.api.v4_0_0.* classes at runtime and excludes
106+
# Dynamic* — a static class list here would silently drop any class added
107+
# after the list was written (the shard-8 catch-all treats the whole
108+
# code.api.v4_0_0 package as covered by shard 1, so an omitted class would
109+
# never run on ANY shard). Runtime discovery keeps shard 1 self-updating.
95110
test_filter: >-
96111
code.api.v4_0_0
97112
- shard: 2
@@ -102,12 +117,11 @@ jobs:
102117
test_filter: >-
103118
code.api.v1_2_1
104119
- shard: 3
105-
name: "v6 + v2_x"
120+
name: "v6 only"
121+
# v6_0_0 isolated: previously bundled with v2_x causing 700s+ runs;
122+
# v2_x moved to shard 7 which had headroom.
106123
test_filter: >-
107124
code.api.v6_0_0
108-
code.api.v2_1_0
109-
code.api.v2_2_0
110-
code.api.v2_0_0
111125
- shard: 4
112126
name: "v5_1 + v5_0 + v3_0"
113127
test_filter: >-
@@ -128,7 +142,8 @@ jobs:
128142
code.api.http4sbridge
129143
code.api.UKOpenBanking
130144
- shard: 7
131-
name: "model + views + customer + util + small data + berlin"
145+
name: "model + views + customer + util + small data + berlin + v2_x"
146+
# v2_0_0/v2_1_0/v2_2_0 moved here from shard 3 to rebalance after v6_0_0 was isolated.
132147
test_filter: >-
133148
code.model
134149
code.views
@@ -142,9 +157,12 @@ jobs:
142157
code.crm
143158
code.accountHolder
144159
code.api.berlin
160+
code.api.v2_1_0
161+
code.api.v2_2_0
162+
code.api.v2_0_0
145163
- shard: 8
146164
name: "connector + auth + login + mgmt + metrics + remaining (catch-all)"
147-
# catch-all shard: appends any test package not assigned to shards 1-7
165+
# catch-all shard: appends any test package not assigned to shards 1-7 and 9
148166
# Root-level code.api tests use class-name prefix matching (lowercase classes).
149167
# NOTE: classes that sit DIRECTLY in package code.api must be listed here by
150168
# FQN-prefix — the catch-all marks the parent package code.api as "covered" once
@@ -165,6 +183,15 @@ jobs:
165183
code.container
166184
code.management
167185
code.metrics
186+
code.concurrency
187+
- shard: 9
188+
name: "v4 Dynamic tests"
189+
# v4_0_0 Dynamic* split: 6 heavy test classes (DynamicEndpointHelperTest 4206 lines,
190+
# DynamicEndpointsTest 2548, DynamicEntityTest 1974, plus 3 smaller ones).
191+
# Prefix code.api.v4_0_0.Dynamic matches all 6 classes; shard 1's runtime
192+
# discovery excludes this same prefix so no test runs in both shards.
193+
test_filter: >-
194+
code.api.v4_0_0.Dynamic
168195
169196
services:
170197
redis:
@@ -180,11 +207,11 @@ jobs:
180207
steps:
181208
- uses: actions/checkout@v4
182209

183-
- name: Set up JDK 11
210+
- name: Set up JDK 25
184211
uses: actions/setup-java@v4
185212
with:
186-
java-version: "11"
187-
distribution: "adopt"
213+
java-version: "25"
214+
distribution: "temurin"
188215
cache: maven
189216

190217
- name: Download compiled output
@@ -258,6 +285,12 @@ jobs:
258285
echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props
259286
echo hikari.maximumPoolSize=20 >> obp-api/src/main/resources/props/test.default.props
260287
echo write_metrics=false >> obp-api/src/main/resources/props/test.default.props
288+
# Log emails instead of opening a real SMTP socket: without this,
289+
# LocalMappedConnector.sendCustomerNotification's EMAIL branch calls
290+
# CommonsEmailWrapper.sendTextEmail which throws ConnectException because
291+
# there's no mail server in CI. That surfaces as 500 in any test that
292+
# hits an endpoint triggering the notification (v5 consent flows, etc.).
293+
echo mail.test.mode=true >> obp-api/src/main/resources/props/test.default.props
261294
# Permissions granted to runtime-compiled dynamic-endpoint code inside the security sandbox
262295
# (mirrors default.props / production.default.props). Required so dynamic resource-doc bodies
263296
# can do JSON extraction (reflection) and read OBP props (getenv); without it the sandbox
@@ -270,19 +303,38 @@ jobs:
270303
# The YAML >- scalar collapses newlines to spaces, so we convert here.
271304
FILTER=$(echo "${{ matrix.test_filter }}" | tr ' ' ',')
272305
306+
# Shard 1 (v4 non-Dynamic): discover code.api.v4_0_0 classes at runtime and
307+
# exclude Dynamic* (shard 9's domain), instead of a hand-maintained class list.
308+
# This is what makes the shard-8 catch-all's "code.api.v4_0_0 is covered by
309+
# shard 1" assumption actually true — a class list here could drift from the
310+
# real directory contents and leave a new test running on no shard at all.
311+
if [ "${{ matrix.shard }}" = "1" ]; then
312+
FILTER=$(grep -l '^class.*extends' obp-api/src/test/scala/code/api/v4_0_0/*.scala \
313+
| xargs -I{} grep -hoP '^class \K[A-Z][A-Za-z0-9_]+' {} \
314+
| grep -v '^Dynamic' \
315+
| sed 's/^/code.api.v4_0_0./' \
316+
| paste -sd, -)
317+
fi
318+
273319
# Shard 8 is the catch-all: append any test package not explicitly
274-
# assigned to shards 1–7, so new packages are never silently skipped.
320+
# assigned to shards 1–7 and 9, so new packages are never silently skipped.
275321
if [ "${{ matrix.shard }}" = "8" ]; then
322+
# Shard 1 discovers v4 non-Dynamic classes at runtime (see above); shard 9
323+
# covers Dynamic*. Use code.api.v4_0_0 as the assigned prefix so the
324+
# catch-all treats the whole v4_0_0 package as covered — true as long as
325+
# shard 1's discovery and shard 9's Dynamic prefix together span it.
276326
SHARD1="code.api.v4_0_0"
277327
SHARD2="code.api.v1_2_1"
278-
SHARD3="code.api.v6_0_0 code.api.v2_1_0 code.api.v2_2_0 code.api.v2_0_0"
328+
SHARD3="code.api.v6_0_0"
279329
SHARD4="code.api.v5_1_0 code.api.v5_0_0 code.api.v3_0_0"
280330
SHARD5="code.api.ResourceDocs1_4_0 code.api.v3_1_0 code.api.v1_4_0 code.api.v1_3_0"
281331
SHARD6="code.api.v7_0_0 code.api.http4sbridge code.api.UKOpenBanking"
282332
SHARD7="code.model code.views code.customer code.usercustomerlinks \
283333
code.api.util code.errormessages code.atms code.branches \
284-
code.products code.crm code.accountHolder code.api.berlin"
285-
ASSIGNED="$SHARD1 $SHARD2 $SHARD3 $SHARD4 $SHARD5 $SHARD6 $SHARD7 ${{ matrix.test_filter }}"
334+
code.products code.crm code.accountHolder code.api.berlin \
335+
code.api.v2_1_0 code.api.v2_2_0 code.api.v2_0_0"
336+
SHARD9="code.api.v4_0_0.Dynamic"
337+
ASSIGNED="$SHARD1 $SHARD2 $SHARD3 $SHARD4 $SHARD5 $SHARD6 $SHARD7 $SHARD9 ${{ matrix.test_filter }}"
286338
287339
# Discover all packages that contain at least one .scala test file
288340
ALL_PKGS=$(find obp-api/src/test/scala obp-commons/src/test/scala \
@@ -315,10 +367,27 @@ jobs:
315367
# -pl obp-commons,obp-api: obp-commons' own 5 util suites run on whichever
316368
# shard's filter matches com.openbankproject.* (the catch-all shard); on every
317369
# other shard the filter matches nothing in obp-commons → 0 tests there.
370+
# timeout 1500: hard-kill after 25 min to prevent Pekko non-daemon threads
371+
# (ConsentScheduler etc.) from keeping the JVM alive after tests complete.
372+
# Exit code 124 (timeout) is treated as success — tests are done, JVM just hung.
373+
# set +e: GitHub Actions uses -eo pipefail by default; without it, a 124 exit from
374+
# timeout would abort the step before the rc check below can run.
375+
set +e
318376
MAVEN_OPTS="-Xmx3G -Xss2m -XX:MaxMetaspaceSize=1G" \
319-
mvn process-resources scalatest:test -pl obp-commons,obp-api -DfailIfNoTests=false \
377+
timeout 1500 mvn process-resources scalatest:test -pl obp-commons,obp-api -DfailIfNoTests=false \
320378
-DwildcardSuites="$FILTER" \
321379
> maven-build-shard${{ matrix.shard }}.log 2>&1
380+
rc=$?
381+
set -e
382+
# timeout returns 124 when the JVM was killed. That is only benign when the tests had
383+
# successfully finished but Pekko non-daemon threads kept the JVM alive. We must
384+
# require proof from the log instead of blindly converting 124 to success.
385+
if [ $rc -eq 124 ]; then
386+
if grep -q "BUILD SUCCESS" maven-build-shard${{ matrix.shard }}.log; then
387+
rc=0
388+
fi
389+
fi
390+
exit $rc
322391
323392
- name: Report failing tests — shard ${{ matrix.shard }}
324393
if: always()

0 commit comments

Comments
 (0)