Skip to content

Commit 85632e2

Browse files
authored
Merge pull request #2855 from hongwei1/develop-obp
Thin-jar packaging, connector stability fixes, and portable/robust CI test tooling
2 parents a1c30b3 + 754b7cc commit 85632e2

23 files changed

Lines changed: 517 additions & 3274 deletions

.github/Dockerfile_PreBuild

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ FROM eclipse-temurin:25-jre-alpine
33
RUN addgroup -S obp && adduser -S -h /app -G obp obp
44

55
# Copy OBP source code
6-
# Copy build artifact (JAR file) from maven build
6+
# Copy build artifact (JAR file) and its runtime dependencies from maven build
7+
COPY /obp-api/target/lib /app/lib
78
COPY /obp-api/target/obp-api.jar /app/obp-api.jar
89
WORKDIR /app
910
USER obp

.github/workflows/build_container.yml

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@ jobs:
5151
# Test classes must be in target/test-classes for the test shards.
5252
# `clean` for a guaranteed-correct build. A no-clean Zinc incremental cache
5353
# (actions/cache of target/) was measured and saved only ~17s: compile time is
54-
# dominated by Maven startup + dependency resolution + packaging the ~297MB fat
55-
# jar + install — NOT by Scala compilation (a zero-source-change rebuild was
56-
# still 333s vs 350s). The stale-risk class of no-clean incremental builds is
57-
# not worth ~2.7% of wall-clock, so we keep a full clean build.
54+
# dominated by Maven startup + dependency resolution + install — NOT by Scala
55+
# compilation (a zero-source-change rebuild was still 333s vs 350s; this was
56+
# measured before the fat-jar-to-thin-jar switch, so the packaging share of
57+
# that number no longer applies and hasn't been re-measured). The stale-risk
58+
# class of no-clean incremental builds is not worth ~2.7% of wall-clock, so we
59+
# keep a full clean build.
5860
MAVEN_OPTS="-Xmx3G -Xss2m -XX:MaxMetaspaceSize=1G" \
5961
mvn clean install -T 4 -Pprod -DskipTests
6062
@@ -69,7 +71,10 @@ jobs:
6971
obp-commons/target/
7072
7173
- name: Save .jar artifact
72-
run: mkdir -p ./push && cp obp-api/target/obp-api.jar ./push/
74+
run: |
75+
mkdir -p ./push
76+
cp obp-api/target/obp-api.jar ./push/
77+
cp -r obp-api/target/lib ./push/lib
7378
7479
- uses: actions/upload-artifact@v4
7580
with:
@@ -409,8 +414,14 @@ jobs:
409414
maven-build-shard${{ matrix.shard }}.log | head -200 || true
410415
echo ""
411416
echo "=== FAILING TEST SCENARIOS (with 30 lines context) ==="
412-
if grep -C 30 -n "\*\*\* FAILED \*\*\*" maven-build-shard${{ matrix.shard }}.log; then
413-
echo "Failing tests detected in shard ${{ matrix.shard }}."
417+
# maven.test.failure.ignore=true (root pom) makes mvn exit 0 even when a suite
418+
# aborts entirely — "*** FAILED ***" alone misses that, since scalatest prints
419+
# "*** RUN ABORTED ***" / "*** SUITE ABORTED ***" instead for e.g. an
420+
# ExceptionInInitializerError, and neither pattern was being checked. That let
421+
# a genuinely aborted suite report CI green silently.
422+
if grep -C 30 -n -E "\*\*\* FAILED \*\*\*|\*\*\* RUN ABORTED \*\*\*|\*\*\* SUITE ABORTED \*\*\*" \
423+
maven-build-shard${{ matrix.shard }}.log; then
424+
echo "Failing/aborted tests detected in shard ${{ matrix.shard }}."
414425
exit 1
415426
else
416427
echo "No failing tests detected in shard ${{ matrix.shard }}."

.github/workflows/build_pull_request.yml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,10 @@ jobs:
6565
obp-commons/target/
6666
6767
- name: Save .jar artifact
68-
run: mkdir -p ./pull && cp obp-api/target/obp-api.jar ./pull/
68+
run: |
69+
mkdir -p ./pull
70+
cp obp-api/target/obp-api.jar ./pull/
71+
cp -r obp-api/target/lib ./pull/lib
6972
7073
- uses: actions/upload-artifact@v4
7174
with:
@@ -404,8 +407,14 @@ jobs:
404407
maven-build-shard${{ matrix.shard }}.log | head -200 || true
405408
echo ""
406409
echo "=== FAILING TEST SCENARIOS (with 30 lines context) ==="
407-
if grep -C 30 -n "\*\*\* FAILED \*\*\*" maven-build-shard${{ matrix.shard }}.log; then
408-
echo "Failing tests detected in shard ${{ matrix.shard }}."
410+
# maven.test.failure.ignore=true (root pom) makes mvn exit 0 even when a suite
411+
# aborts entirely — "*** FAILED ***" alone misses that, since scalatest prints
412+
# "*** RUN ABORTED ***" / "*** SUITE ABORTED ***" instead for e.g. an
413+
# ExceptionInInitializerError, and neither pattern was being checked. That let
414+
# a genuinely aborted suite report CI green silently.
415+
if grep -C 30 -n -E "\*\*\* FAILED \*\*\*|\*\*\* RUN ABORTED \*\*\*|\*\*\* SUITE ABORTED \*\*\*" \
416+
maven-build-shard${{ matrix.shard }}.log; then
417+
echo "Failing/aborted tests detected in shard ${{ matrix.shard }}."
409418
exit 1
410419
else
411420
echo "No failing tests detected in shard ${{ matrix.shard }}."

README.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,29 @@ java -jar obp-api/target/obp-api.jar
7676

7777
The http4s server binds to `hostname` / `dev.port` as configured in your props file (defaults are `127.0.0.1` and `8080`).
7878

79-
No `--add-opens` flags are needed on the command line: the executable jar's manifest carries
79+
`obp-api.jar` is a thin jar: it contains only this module's classes and resources. Its
80+
runtime dependencies are copied to the sibling `obp-api/target/lib/` directory by the build,
81+
and the jar's manifest carries a `Class-Path` entry pointing at `lib/`, so the jar and `lib/`
82+
must stay next to each other — copying the jar alone is not enough to run it.
83+
84+
No `--add-opens` flags are needed on the command line: the jar's manifest also carries
8085
an `Add-Opens` attribute (JEP 261) with all modules the runtime needs (CGLib proxy generation,
8186
Kryo serialization, Pekko remoting, Scala runtime reflection). Only when launching via a
82-
custom classpath (`java -cp ... bootstrap.http4s.Http4sServer`) do the flags need to be passed
83-
explicitly, since the manifest is only honored by `java -jar`.
87+
custom classpath does the manifest not apply, since it is only honored by `java -jar`; the
88+
equivalent form needs the flags passed explicitly:
89+
90+
```sh
91+
java --add-opens java.base/java.lang=ALL-UNNAMED \
92+
--add-opens java.base/java.lang.reflect=ALL-UNNAMED \
93+
--add-opens java.base/java.util=ALL-UNNAMED \
94+
--add-opens java.base/java.lang.invoke=ALL-UNNAMED \
95+
--add-opens java.base/java.util.jar=ALL-UNNAMED \
96+
--add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED \
97+
--add-opens java.base/java.io=ALL-UNNAMED \
98+
--add-opens java.base/java.util.concurrent=ALL-UNNAMED \
99+
--add-opens java.base/java.security=ALL-UNNAMED \
100+
-cp "obp-api/target/obp-api.jar:obp-api/target/lib/*" bootstrap.http4s.Http4sServer
101+
```
84102

85103
[Note: How to run via IntelliJ IDEA](obp-api/src/main/docs/glossary/Run_via_IntelliJ_IDEA.md)
86104

branding.md

Lines changed: 0 additions & 49 deletions
This file was deleted.

development/docker/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ RUN --mount=type=cache,target=$HOME/.m2 MAVEN_OPTS="-Xmx3G -Xss2m" mvn install -
99
RUN --mount=type=cache,target=$HOME/.m2 MAVEN_OPTS="-Xmx3G -Xss2m" mvn install -DskipTests -pl obp-api
1010

1111
FROM eclipse-temurin:25-jre-alpine
12+
COPY --from=maven /usr/src/OBP-API/obp-api/target/lib /app/lib
1213
COPY --from=maven /usr/src/OBP-API/obp-api/target/obp-api.jar /app/obp-api.jar
1314
ENTRYPOINT ["java", "-jar", "/app/obp-api.jar"]

development/docker/Dockerfile.dev

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,12 @@ WORKDIR /app
44

55
# Copy Maven configuration files
66
COPY pom.xml .
7-
COPY build.sbt .
87

98
# Copy source code and necessary project files
109
COPY obp-api/ ./obp-api/
1110
COPY obp-commons/ ./obp-commons/
12-
COPY project/ ./project/
1311

14-
# Copy other necessary files for the build
15-
COPY jitpack.yml .
16-
COPY web-app_2_3.dtd .
12+
1713

1814
EXPOSE 8080
1915

flushall_build_and_run.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ fi
124124

125125
echo ""
126126
echo "✓ Build completed successfully"
127-
echo "✓ JAR created: obp-api/target/obp-api.jar"
127+
echo "✓ JAR created: obp-api/target/obp-api.jar (thin jar — runtime deps in obp-api/target/lib/)"
128128
echo "✓ Build log saved to: build.log"
129129
echo ""
130130

@@ -147,7 +147,10 @@ JAVA_OPTS="--add-opens java.base/java.lang=ALL-UNNAMED \
147147
--add-opens java.base/java.util=ALL-UNNAMED \
148148
--add-opens java.base/java.lang.invoke=ALL-UNNAMED \
149149
--add-opens java.base/java.util.jar=ALL-UNNAMED \
150-
--add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED"
150+
--add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED \
151+
--add-opens java.base/java.io=ALL-UNNAMED \
152+
--add-opens java.base/java.util.concurrent=ALL-UNNAMED \
153+
--add-opens java.base/java.security=ALL-UNNAMED"
151154

152155
RUNTIME_LOG=/tmp/obp-api.log
153156

flushall_fast_build_and_run.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ fi
292292

293293
echo ""
294294
echo "✓ Fast build completed successfully"
295-
echo "✓ JAR created: obp-api/target/obp-api.jar"
295+
echo "✓ JAR created: obp-api/target/obp-api.jar (thin jar — runtime deps in obp-api/target/lib/)"
296296
echo "✓ Build log saved to: fast_build.log"
297297
echo ""
298298

@@ -315,7 +315,10 @@ JAVA_OPTS="--add-opens java.base/java.lang=ALL-UNNAMED \
315315
--add-opens java.base/java.util=ALL-UNNAMED \
316316
--add-opens java.base/java.lang.invoke=ALL-UNNAMED \
317317
--add-opens java.base/java.util.jar=ALL-UNNAMED \
318-
--add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED"
318+
--add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED \
319+
--add-opens java.base/java.io=ALL-UNNAMED \
320+
--add-opens java.base/java.util.concurrent=ALL-UNNAMED \
321+
--add-opens java.base/java.security=ALL-UNNAMED"
319322

320323
if [ "$RUN_BACKGROUND" = true ]; then
321324
# Run in background with output to log file

obp-api/pom.xml

Lines changed: 45 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@
568568
</dependencies>
569569

570570
<build>
571+
<finalName>obp-api</finalName>
571572
<plugins>
572573
<plugin>
573574
<groupId>org.apache.maven.plugins</groupId>
@@ -586,8 +587,8 @@
586587
<forkMode>once</forkMode>
587588
<junitxml>.</junitxml>
588589
<filereports>WDF TestSuite.txt</filereports>
589-
<!-- Keep this module list identical to the shaded-jar manifest's Add-Opens
590-
(below, near maven-shade-plugin's ManifestResourceTransformer): a module
590+
<!-- Keep this module list identical to the thin-jar manifest's Add-Opens
591+
(below, near maven-jar-plugin's manifestEntries): a module
591592
opened for the test JVM but not production (or vice versa) causes an
592593
InaccessibleObjectException that only reproduces in whichever environment
593594
is missing it. -->
@@ -738,57 +739,57 @@
738739
<release>${java.version}</release>
739740
</configuration>
740741
</plugin>
741-
<!-- maven-shade-plugin: Create executable fat JAR with all dependencies -->
742+
<!-- Thin JAR: project classes/resources only. Runnable via `java -jar` because the
743+
manifest carries Main-Class + Class-Path (relative lib/) + Add-Opens.
744+
JEP 261: the `java -jar` launcher honors Add-Opens from the manifest.
745+
Required on JDK 17+ for CGLib (StarConnector proxy generation via
746+
ClassLoader.defineClass), Kryo/chill serialization, Pekko remoting and
747+
Scala runtime reflection. development/docker/Dockerfile (local dev
748+
image) relies on this and launches with a plain `java -jar`.
749+
.github/Dockerfile_PreBuild (the production image) instead keeps its
750+
own explicit "add-opens" ENTRYPOINT flags — that is the combination
751+
actually verified against the K8s deployment, so don't remove it in
752+
favor of this manifest alone without re-verifying in that environment.
753+
Keep this module list identical to the scalatest-maven-plugin argLine
754+
above (surefire test JVM): a module opened here but not there (or vice
755+
versa) causes an InaccessibleObjectException that only reproduces in
756+
whichever environment is missing it. -->
742757
<plugin>
743758
<groupId>org.apache.maven.plugins</groupId>
744-
<artifactId>maven-shade-plugin</artifactId>
745-
<version>3.5.1</version>
759+
<artifactId>maven-jar-plugin</artifactId>
760+
<version>3.4.2</version>
746761
<configuration>
747-
<shadedArtifactAttached>false</shadedArtifactAttached>
748-
<createDependencyReducedPom>false</createDependencyReducedPom>
749-
<transformers>
750-
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
762+
<archive>
763+
<manifest>
751764
<mainClass>bootstrap.http4s.Http4sServer</mainClass>
752-
<!-- JEP 261: the `java -jar` launcher honors Add-Opens from the manifest.
753-
Required on JDK 17+ for CGLib (StarConnector proxy generation via
754-
ClassLoader.defineClass), Kryo/chill serialization, Pekko remoting and
755-
Scala runtime reflection. development/docker/Dockerfile (local dev
756-
image) relies on this and launches with a plain `java -jar`.
757-
.github/Dockerfile_PreBuild (the production image) instead keeps its
758-
own explicit "add-opens" ENTRYPOINT flags — that is the combination
759-
actually verified against the K8s deployment, so don't remove it in
760-
favor of this manifest alone without re-verifying in that environment.
761-
Keep this module list identical to the scalatest-maven-plugin argLine
762-
above (surefire test JVM): a module opened here but not there (or vice
763-
versa) causes an InaccessibleObjectException that only reproduces in
764-
whichever environment is missing it. -->
765-
<manifestEntries>
766-
<Add-Opens>java.base/java.lang java.base/java.lang.reflect java.base/java.util java.base/java.lang.invoke java.base/java.util.jar java.base/sun.reflect.generics.reflectiveObjects java.base/java.io java.base/java.util.concurrent java.base/java.security</Add-Opens>
767-
</manifestEntries>
768-
</transformer>
769-
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
770-
<resource>reference.conf</resource>
771-
</transformer>
772-
</transformers>
773-
<filters>
774-
<filter>
775-
<artifact>*:*</artifact>
776-
<excludes>
777-
<exclude>META-INF/*.SF</exclude>
778-
<exclude>META-INF/*.DSA</exclude>
779-
<exclude>META-INF/*.RSA</exclude>
780-
</excludes>
781-
</filter>
782-
</filters>
783-
<finalName>obp-api</finalName>
765+
<addClasspath>true</addClasspath>
766+
<classpathPrefix>lib/</classpathPrefix>
767+
</manifest>
768+
<manifestEntries>
769+
<Add-Opens>java.base/java.lang java.base/java.lang.reflect java.base/java.util java.base/java.lang.invoke java.base/java.util.jar java.base/sun.reflect.generics.reflectiveObjects java.base/java.io java.base/java.util.concurrent java.base/java.security</Add-Opens>
770+
</manifestEntries>
771+
</archive>
784772
</configuration>
773+
</plugin>
774+
775+
<!-- Copy runtime dependencies to target/lib/ (sibling of obp-api.jar) -->
776+
<plugin>
777+
<groupId>org.apache.maven.plugins</groupId>
778+
<artifactId>maven-dependency-plugin</artifactId>
779+
<version>3.8.1</version>
785780
<executions>
786781
<execution>
787-
<id>make-fat-jar</id>
788-
<phase>package</phase>
782+
<id>copy-runtime-deps</id>
783+
<phase>prepare-package</phase>
789784
<goals>
790-
<goal>shade</goal>
785+
<goal>copy-dependencies</goal>
791786
</goals>
787+
<configuration>
788+
<outputDirectory>${project.build.directory}/lib</outputDirectory>
789+
<includeScope>runtime</includeScope>
790+
<overWriteReleases>false</overWriteReleases>
791+
<overWriteSnapshots>true</overWriteSnapshots>
792+
</configuration>
792793
</execution>
793794
</executions>
794795
</plugin>

0 commit comments

Comments
 (0)