Compare commits
258 Commits
Author | SHA1 | Date |
---|---|---|
|
2196f6da73 | |
|
c21293bb24 | |
|
33038d3e1e | |
|
b66509b4e1 | |
|
e20e2393c9 | |
|
a2d4ecf8ac | |
|
05172cf77b | |
|
4420c92173 | |
|
da8eac3483 | |
|
cb51a44e2b | |
|
5e04d98260 | |
|
61acc52137 | |
|
f1a3971148 | |
|
dd353034de | |
|
c2e838a603 | |
|
6f1bcc4bfa | |
|
2542430e5b | |
|
43b06ca9b9 | |
|
533544d8fc | |
|
5ec993bb8c | |
|
1ebc557bd5 | |
|
2eaad0585c | |
|
46022d2a4e | |
|
a5dbc5bb22 | |
|
16718f676d | |
|
c1924f2fcd | |
|
473fc1b19e | |
|
a79d0e0d10 | |
|
44099d3d21 | |
|
2440667aab | |
|
7db43ef820 | |
|
d97539bc46 | |
|
8c25063273 | |
|
489336255a | |
|
fde0ae4ac9 | |
|
643f6112c7 | |
|
4bf6fcefab | |
|
9d85c379bd | |
|
9d63a74bbc | |
|
f059a953d3 | |
|
1f293cc653 | |
|
49c7546662 | |
|
c92617a015 | |
|
a9bf744b68 | |
|
b503ad3a9a | |
|
a350ead232 | |
|
55a97aa7ad | |
|
06dd6aec3c | |
|
a1038a5e64 | |
|
99f8c4cd0d | |
|
2edd8f9d62 | |
|
3353090bc6 | |
|
ad22bc033f | |
|
dee42bfe2d | |
|
307dc1c477 | |
|
64aad6d060 | |
|
0f41e906b9 | |
|
c65a259637 | |
|
1b44bafda1 | |
|
e4a58a53d0 | |
|
34a8e1bd82 | |
|
af941e0a9a | |
|
7f225a8df5 | |
|
cae3fb578c | |
|
f175d132b3 | |
|
2ca6703f55 | |
|
f59fc4020a | |
|
80c384e92c | |
|
0634c11eee | |
|
bf075ec89c | |
|
f74ed943e5 | |
|
80436abbb6 | |
|
09311ff44e | |
|
9f1e033caf | |
|
4854fec275 | |
|
e83ad75d3e | |
|
657c537cc2 | |
|
0753e7dbf2 | |
|
1d4b9d372f | |
|
e878db0abf | |
|
58e2f9c872 | |
|
1023ed65a6 | |
|
266f9c44e0 | |
|
3fdc1db7ce | |
|
a111f1f68f | |
|
b10262c9bc | |
|
485180df4f | |
|
94fce6f689 | |
|
761302452b | |
|
3f2e1b2466 | |
|
74daedff76 | |
|
169b1b10b0 | |
|
652414155c | |
|
1e4cc948f2 | |
|
4fd9626854 | |
|
8a9528ebdc | |
|
1ed8eb08e0 | |
|
49ba8aa668 | |
|
53cda6a6a1 | |
|
d5717b71ab | |
|
79ad5e6999 | |
|
871f82d116 | |
|
0187b77761 | |
|
04038101d2 | |
|
45bf83d737 | |
|
f7e7664a5a | |
|
6ee519ec57 | |
|
92fe4c55f7 | |
|
911578e560 | |
|
fbcc1fdec6 | |
|
c280fdefe8 | |
|
319e462cba | |
|
aa06ab1e25 | |
|
09967b1a5d | |
|
5c0d2ee180 | |
|
bb19585601 | |
|
f2c2d19a9c | |
|
3db8b5fb26 | |
|
e313624263 | |
|
af2436de84 | |
|
4a62872d63 | |
|
f5d6bea934 | |
|
419d78b359 | |
|
b6950f8fb0 | |
|
f8ce17c751 | |
|
4a30228978 | |
|
f3df45ace1 | |
|
3aaf4b7b0a | |
|
4af0d6d747 | |
|
3c3726b89e | |
|
92ee73df30 | |
|
418e057afc | |
|
1fda419b17 | |
|
4c8cabcb3a | |
|
dec46b45d7 | |
|
57992512b7 | |
|
b566b73be6 | |
|
532ea9abc1 | |
|
ea05f6df15 | |
|
23c9b6510b | |
|
9bb62a074b | |
|
fe975214cd | |
|
c099462e6f | |
|
ee2730ca8d | |
|
b28d390c69 | |
|
e7ffa33d65 | |
|
dadfd9a35d | |
|
2d312ed5f5 | |
|
ee72caf7dc | |
|
a30e832915 | |
|
0ae23de2b5 | |
|
2a521ce4e9 | |
|
3cced515ce | |
|
279de9e807 | |
|
97c89b480f | |
|
82795f9966 | |
|
7c8a15629b | |
|
ef37765625 | |
|
bba56ffc8b | |
|
6f909114e7 | |
|
79091f926d | |
|
155e3bd5e6 | |
|
8008076e04 | |
|
4b2d358384 | |
|
17e655b7e5 | |
|
2b3a8ff647 | |
|
36732bd74a | |
|
352925bbe8 | |
|
f8f6e59d1a | |
|
08a641c1a2 | |
|
26364c27e2 | |
|
787840735b | |
|
e2ba4dad2a | |
|
7a595f1b83 | |
|
922592938a | |
|
b23d7fe203 | |
|
f9d4589259 | |
|
d4fce9a068 | |
|
cc20b3ec3b | |
|
f28bea16c5 | |
|
4ba14c139e | |
|
ad3a18d086 | |
|
98b4afe527 | |
|
a8caeed578 | |
|
bc968fc671 | |
|
1c1a35f6be | |
|
eb307b91ec | |
|
14791f0593 | |
|
474c9cdeb1 | |
|
cbdd7cf23b | |
|
569fa5fd7d | |
|
5a2f90d1f1 | |
|
5301c8d905 | |
|
4f6bbac13e | |
|
54ffc42309 | |
|
8c5a25ee5c | |
|
30d3889079 | |
|
e8b6ab14bc | |
|
5344fb218a | |
|
bdb05639bc | |
|
db10e3d1ed | |
|
ae86895953 | |
|
93c9413ac0 | |
|
f93c43cb8a | |
|
c3aca4b126 | |
|
b3db525f0f | |
|
52c55788a2 | |
|
990644919a | |
|
2756424035 | |
|
9c727be8f2 | |
|
e4e3988433 | |
|
0959271061 | |
|
8c7e0c675f | |
|
0b601118bd | |
|
5348880b69 | |
|
7979a51f65 | |
|
27715dee20 | |
|
d27aedf92a | |
|
b3c28f1ef3 | |
|
f3b44031a6 | |
|
f6da5e7a5c | |
|
8d63043d9e | |
|
b43d2f2088 | |
|
0b0b742758 | |
|
b61e38bef8 | |
|
6490b749db | |
|
9a1d9f677b | |
|
1cd14c96a5 | |
|
51b606e941 | |
|
c5e28c9133 | |
|
c4cace3f74 | |
|
b7695200a9 | |
|
5cec09dd02 | |
|
cae5a811ad | |
|
0afd40fa40 | |
|
3265a2672d | |
|
b6a1c29763 | |
|
9a3a3b1b5e | |
|
6354d802c0 | |
|
bc46bb2a24 | |
|
3b2c8abf50 | |
|
dc341edfdd | |
|
3a9ab15696 | |
|
8d87586395 | |
|
d3bab5f82d | |
|
862db41134 | |
|
42cc7775f4 | |
|
5c95ee798c | |
|
14703cbafd | |
|
3b98af30f5 | |
|
8d000e008f | |
|
335a0b76b5 | |
|
4a1de8534f | |
|
630bb335ea | |
|
c1d51f8687 | |
|
255ea92a57 | |
|
b01dc92233 | |
|
2cf854c5b6 |
|
@ -23,7 +23,7 @@ inputs:
|
||||||
java-version:
|
java-version:
|
||||||
description: 'Java version to use for the build'
|
description: 'Java version to use for the build'
|
||||||
required: false
|
required: false
|
||||||
default: '24'
|
default: '25'
|
||||||
runs:
|
runs:
|
||||||
using: composite
|
using: composite
|
||||||
steps:
|
steps:
|
||||||
|
@ -42,12 +42,12 @@ runs:
|
||||||
${{ inputs.java-toolchain == 'true' && '24' || '' }}
|
${{ inputs.java-toolchain == 'true' && '24' || '' }}
|
||||||
- name: Set Up Gradle With Read/Write Cache
|
- name: Set Up Gradle With Read/Write Cache
|
||||||
if: ${{ inputs.cache-read-only == 'false' }}
|
if: ${{ inputs.cache-read-only == 'false' }}
|
||||||
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
|
uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4
|
||||||
with:
|
with:
|
||||||
cache-read-only: false
|
cache-read-only: false
|
||||||
develocity-access-key: ${{ inputs.develocity-access-key }}
|
develocity-access-key: ${{ inputs.develocity-access-key }}
|
||||||
- name: Set Up Gradle
|
- name: Set Up Gradle
|
||||||
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
|
uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4
|
||||||
with:
|
with:
|
||||||
develocity-access-key: ${{ inputs.develocity-access-key }}
|
develocity-access-key: ${{ inputs.develocity-access-key }}
|
||||||
develocity-token-expiry: 4
|
develocity-token-expiry: 4
|
||||||
|
|
|
@ -21,7 +21,7 @@ runs:
|
||||||
using: composite
|
using: composite
|
||||||
steps:
|
steps:
|
||||||
- name: Set Up JFrog CLI
|
- name: Set Up JFrog CLI
|
||||||
uses: jfrog/setup-jfrog-cli@88e9eba31c07e31beefa4cef5c0e93d1af9535d7 # v4.6.1
|
uses: jfrog/setup-jfrog-cli@c32bf10843e4071112c4ea3abf622d3b27cd8c17 # v4.7.0
|
||||||
env:
|
env:
|
||||||
JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }}
|
JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }}
|
||||||
- name: Download Artifacts
|
- name: Download Artifacts
|
||||||
|
|
|
@ -17,7 +17,7 @@ runs:
|
||||||
using: composite
|
using: composite
|
||||||
steps:
|
steps:
|
||||||
- name: Set Up JFrog CLI
|
- name: Set Up JFrog CLI
|
||||||
uses: jfrog/setup-jfrog-cli@88e9eba31c07e31beefa4cef5c0e93d1af9535d7 # v4.6.1
|
uses: jfrog/setup-jfrog-cli@c32bf10843e4071112c4ea3abf622d3b27cd8c17 # v4.7.0
|
||||||
env:
|
env:
|
||||||
JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }}
|
JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }}
|
||||||
- name: Download Release Artifacts
|
- name: Download Release Artifacts
|
||||||
|
|
|
@ -23,16 +23,13 @@ jobs:
|
||||||
toolchain: true
|
toolchain: true
|
||||||
- version: 21
|
- version: 21
|
||||||
toolchain: true
|
toolchain: true
|
||||||
- version: 24
|
|
||||||
toolchain: false
|
|
||||||
- version: 25
|
- version: 25
|
||||||
early-access: true
|
toolchain: false
|
||||||
toolchain: true
|
|
||||||
exclude:
|
exclude:
|
||||||
- os:
|
- os:
|
||||||
name: Linux
|
name: Linux
|
||||||
java:
|
java:
|
||||||
version: 24
|
version: 25
|
||||||
- os:
|
- os:
|
||||||
name: ${{ github.repository == 'spring-projects/spring-boot-commercial' && 'Windows' }}
|
name: ${{ github.repository == 'spring-projects/spring-boot-commercial' && 'Windows' }}
|
||||||
steps:
|
steps:
|
||||||
|
|
|
@ -75,7 +75,7 @@ jobs:
|
||||||
runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }}
|
runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Set up JFrog CLI
|
- name: Set up JFrog CLI
|
||||||
uses: jfrog/setup-jfrog-cli@88e9eba31c07e31beefa4cef5c0e93d1af9535d7 # v4.6.1
|
uses: jfrog/setup-jfrog-cli@c32bf10843e4071112c4ea3abf622d3b27cd8c17 # v4.7.0
|
||||||
env:
|
env:
|
||||||
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
|
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
|
||||||
- name: Promote build
|
- name: Promote build
|
||||||
|
|
|
@ -86,7 +86,7 @@ jobs:
|
||||||
runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }}
|
runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Set up JFrog CLI
|
- name: Set up JFrog CLI
|
||||||
uses: jfrog/setup-jfrog-cli@88e9eba31c07e31beefa4cef5c0e93d1af9535d7 # v4.6.1
|
uses: jfrog/setup-jfrog-cli@c32bf10843e4071112c4ea3abf622d3b27cd8c17 # v4.7.0
|
||||||
env:
|
env:
|
||||||
JF_ENV_SPRING: ${{ vars.COMMERCIAL && secrets.COMMERCIAL_JF_ARTIFACTORY_SPRING || secrets.JF_ARTIFACTORY_SPRING }}
|
JF_ENV_SPRING: ${{ vars.COMMERCIAL && secrets.COMMERCIAL_JF_ARTIFACTORY_SPRING || secrets.JF_ARTIFACTORY_SPRING }}
|
||||||
- name: Promote open source build
|
- name: Promote open source build
|
||||||
|
|
|
@ -59,7 +59,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
stable: true
|
stable: true
|
||||||
- name: Set Up Gradle
|
- name: Set Up Gradle
|
||||||
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
|
uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4
|
||||||
with:
|
with:
|
||||||
cache-read-only: false
|
cache-read-only: false
|
||||||
- name: Configure Gradle Properties
|
- name: Configure Gradle Properties
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
# Enable auto-env through the sdkman_auto_env config
|
# Enable auto-env through the sdkman_auto_env config
|
||||||
# Add key=value pairs of SDKs to use below
|
# Add key=value pairs of SDKs to use below
|
||||||
java=24.0.2-librca
|
java=25-librca
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
"@springio/antora-xref-extension": "1.0.0-alpha.4",
|
"@springio/antora-xref-extension": "1.0.0-alpha.4",
|
||||||
"@springio/antora-zip-contents-collector-extension": "1.0.0-alpha.8",
|
"@springio/antora-zip-contents-collector-extension": "1.0.0-alpha.8",
|
||||||
"@springio/asciidoctor-extensions": "1.0.0-alpha.17",
|
"@springio/asciidoctor-extensions": "1.0.0-alpha.17",
|
||||||
"patch-package": "^8.0.0"
|
"patch-package": "^8.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@antora/asciidoc-loader": {
|
"node_modules/@antora/asciidoc-loader": {
|
||||||
|
@ -575,14 +575,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz",
|
||||||
"integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ=="
|
"integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ=="
|
||||||
},
|
},
|
||||||
"node_modules/at-least-node": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/atomic-sleep": {
|
"node_modules/atomic-sleep": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
||||||
|
@ -1406,17 +1398,17 @@
|
||||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||||
},
|
},
|
||||||
"node_modules/fs-extra": {
|
"node_modules/fs-extra": {
|
||||||
"version": "9.1.0",
|
"version": "10.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
||||||
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
|
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"at-least-node": "^1.0.0",
|
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^6.0.1",
|
"jsonfile": "^6.0.1",
|
||||||
"universalify": "^2.0.0"
|
"universalify": "^2.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fs-mkdirp-stream": {
|
"node_modules/fs-mkdirp-stream": {
|
||||||
|
@ -1993,9 +1985,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsonfile": {
|
"node_modules/jsonfile": {
|
||||||
"version": "6.1.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
||||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"universalify": "^2.0.0"
|
"universalify": "^2.0.0"
|
||||||
},
|
},
|
||||||
|
@ -2331,38 +2324,30 @@
|
||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/os-tmpdir": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pako": {
|
"node_modules/pako": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||||
},
|
},
|
||||||
"node_modules/patch-package": {
|
"node_modules/patch-package": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz",
|
||||||
"integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==",
|
"integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@yarnpkg/lockfile": "^1.1.0",
|
"@yarnpkg/lockfile": "^1.1.0",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"ci-info": "^3.7.0",
|
"ci-info": "^3.7.0",
|
||||||
"cross-spawn": "^7.0.3",
|
"cross-spawn": "^7.0.3",
|
||||||
"find-yarn-workspace-root": "^2.0.0",
|
"find-yarn-workspace-root": "^2.0.0",
|
||||||
"fs-extra": "^9.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"json-stable-stringify": "^1.0.2",
|
"json-stable-stringify": "^1.0.2",
|
||||||
"klaw-sync": "^6.0.0",
|
"klaw-sync": "^6.0.0",
|
||||||
"minimist": "^1.2.6",
|
"minimist": "^1.2.6",
|
||||||
"open": "^7.4.2",
|
"open": "^7.4.2",
|
||||||
"rimraf": "^2.6.3",
|
|
||||||
"semver": "^7.5.3",
|
"semver": "^7.5.3",
|
||||||
"slash": "^2.0.0",
|
"slash": "^2.0.0",
|
||||||
"tmp": "^0.0.33",
|
"tmp": "^0.2.4",
|
||||||
"yaml": "^2.2.2"
|
"yaml": "^2.2.2"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -2750,18 +2735,6 @@
|
||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rimraf": {
|
|
||||||
"version": "2.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
|
||||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
|
||||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
|
||||||
"dependencies": {
|
|
||||||
"glob": "^7.1.3"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"rimraf": "bin.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/safe-buffer": {
|
"node_modules/safe-buffer": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
@ -3118,14 +3091,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tmp": {
|
"node_modules/tmp": {
|
||||||
"version": "0.0.33",
|
"version": "0.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
|
||||||
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
|
"integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
|
||||||
"dependencies": {
|
"license": "MIT",
|
||||||
"os-tmpdir": "~1.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6.0"
|
"node": ">=14.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/to-absolute-glob": {
|
"node_modules/to-absolute-glob": {
|
||||||
|
@ -3238,6 +3209,7 @@
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"@springio/antora-zip-contents-collector-extension": "1.0.0-alpha.8",
|
"@springio/antora-zip-contents-collector-extension": "1.0.0-alpha.8",
|
||||||
"@asciidoctor/tabs": "1.0.0-beta.6",
|
"@asciidoctor/tabs": "1.0.0-beta.6",
|
||||||
"@springio/asciidoctor-extensions": "1.0.0-alpha.17",
|
"@springio/asciidoctor-extensions": "1.0.0-alpha.17",
|
||||||
"patch-package": "^8.0.0"
|
"patch-package": "^8.0.1"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"ui-bundle-url": "https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.18/ui-bundle.zip"
|
"ui-bundle-url": "https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.18/ui-bundle.zip"
|
||||||
|
|
|
@ -80,7 +80,6 @@ tasks.register("integrationTest") {
|
||||||
ant.propertyref(name: "ivy.class.path")
|
ant.propertyref(name: "ivy.class.path")
|
||||||
}
|
}
|
||||||
plainlistener()
|
plainlistener()
|
||||||
file(layout.buildDirectory.dir("test-results/integrationTest")).mkdirs()
|
|
||||||
xmllistener(toDir: resultsDir)
|
xmllistener(toDir: resultsDir)
|
||||||
fileset(dir: layout.buildDirectory.dir("it").get().asFile.toString(), includes: "**/build.xml")
|
fileset(dir: layout.buildDirectory.dir("it").get().asFile.toString(), includes: "**/build.xml")
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,9 +141,13 @@ final class ApplicationPluginAction implements PluginApplicationAction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private void configureFileMode(CopySpec copySpec, int mode) {
|
private void configureFileMode(CopySpec copySpec, int mode) {
|
||||||
copySpec.setFileMode(mode);
|
try {
|
||||||
|
copySpec.getClass().getMethod("setFileMode", Integer.class).invoke(copySpec, Integer.valueOf(mode));
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new RuntimeException("Failed to set file mode on CopySpec", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package org.springframework.boot.gradle.plugin;
|
package org.springframework.boot.gradle.plugin;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
|
|
||||||
|
@ -33,7 +32,7 @@ import org.gradle.api.specs.Spec;
|
||||||
*/
|
*/
|
||||||
class JarTypeFileSpec implements Spec<File> {
|
class JarTypeFileSpec implements Spec<File> {
|
||||||
|
|
||||||
private static final Set<String> EXCLUDED_JAR_TYPES = Collections.singleton("dependencies-starter");
|
private static final Set<String> EXCLUDED_JAR_TYPES = Set.of("dependencies-starter", "development-tool");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSatisfiedBy(File file) {
|
public boolean isSatisfiedBy(File file) {
|
||||||
|
|
|
@ -285,7 +285,6 @@ final class JavaPluginAction implements PluginApplicationAction {
|
||||||
private void configureProductionRuntimeClasspathConfiguration(Project project) {
|
private void configureProductionRuntimeClasspathConfiguration(Project project) {
|
||||||
Configuration productionRuntimeClasspath = project.getConfigurations()
|
Configuration productionRuntimeClasspath = project.getConfigurations()
|
||||||
.create(SpringBootPlugin.PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
.create(SpringBootPlugin.PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||||
productionRuntimeClasspath.setVisible(false);
|
|
||||||
Configuration runtimeClasspath = project.getConfigurations()
|
Configuration runtimeClasspath = project.getConfigurations()
|
||||||
.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||||
productionRuntimeClasspath.attributes((attributes) -> {
|
productionRuntimeClasspath.attributes((attributes) -> {
|
||||||
|
|
|
@ -104,7 +104,6 @@ class WarPluginAction implements PluginApplicationAction {
|
||||||
.set(project.provider(() -> javaPluginExtension(project).getTargetCompatibility()));
|
.set(project.provider(() -> javaPluginExtension(project).getTargetCompatibility()));
|
||||||
bootWar.resolvedArtifacts(runtimeClasspath.getIncoming().getArtifacts().getResolvedArtifacts());
|
bootWar.resolvedArtifacts(runtimeClasspath.getIncoming().getArtifacts().getResolvedArtifacts());
|
||||||
});
|
});
|
||||||
bootWarProvider.map(War::getClasspath);
|
|
||||||
return bootWarProvider;
|
return bootWarProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ import org.springframework.util.Assert;
|
||||||
* @since 3.0.0
|
* @since 3.0.0
|
||||||
*/
|
*/
|
||||||
@CacheableTask
|
@CacheableTask
|
||||||
public class ProcessTestAot extends AbstractAot {
|
public abstract class ProcessTestAot extends AbstractAot {
|
||||||
|
|
||||||
private @Nullable FileCollection classpathRoots;
|
private @Nullable FileCollection classpathRoots;
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,6 @@ import org.gradle.api.tasks.Nested;
|
||||||
import org.gradle.api.tasks.Optional;
|
import org.gradle.api.tasks.Optional;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Spring Boot "fat" archive task.
|
* A Spring Boot "fat" archive task.
|
||||||
*
|
*
|
||||||
|
@ -137,15 +135,6 @@ public interface BootArchive extends Task {
|
||||||
*/
|
*/
|
||||||
void resolvedArtifacts(Provider<Set<ResolvedArtifactResult>> resolvedArtifacts);
|
void resolvedArtifacts(Provider<Set<ResolvedArtifactResult>> resolvedArtifacts);
|
||||||
|
|
||||||
/**
|
|
||||||
* The loader implementation that should be used with the archive.
|
|
||||||
* @return the loader implementation
|
|
||||||
* @since 3.2.0
|
|
||||||
*/
|
|
||||||
@Input
|
|
||||||
@Optional
|
|
||||||
Property<LoaderImplementation> getLoaderImplementation();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the JAR tools should be included as a dependency in the layered
|
* Returns whether the JAR tools should be included as a dependency in the layered
|
||||||
* archive.
|
* archive.
|
||||||
|
|
|
@ -22,32 +22,24 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.gradle.api.file.ConfigurableFilePermissions;
|
import org.gradle.api.file.ConfigurableFilePermissions;
|
||||||
import org.gradle.api.file.CopySpec;
|
import org.gradle.api.file.CopySpec;
|
||||||
import org.gradle.api.file.FileCopyDetails;
|
import org.gradle.api.file.FileCopyDetails;
|
||||||
import org.gradle.api.file.FileTreeElement;
|
import org.gradle.api.file.FileTreeElement;
|
||||||
import org.gradle.api.file.RelativePath;
|
|
||||||
import org.gradle.api.internal.file.copy.CopyAction;
|
import org.gradle.api.internal.file.copy.CopyAction;
|
||||||
import org.gradle.api.internal.file.copy.CopyActionProcessingStream;
|
|
||||||
import org.gradle.api.internal.file.copy.FileCopyDetailsInternal;
|
|
||||||
import org.gradle.api.java.archives.Attributes;
|
import org.gradle.api.java.archives.Attributes;
|
||||||
import org.gradle.api.java.archives.Manifest;
|
import org.gradle.api.java.archives.Manifest;
|
||||||
import org.gradle.api.provider.Property;
|
import org.gradle.api.provider.Property;
|
||||||
import org.gradle.api.specs.Spec;
|
import org.gradle.api.specs.Spec;
|
||||||
import org.gradle.api.specs.Specs;
|
import org.gradle.api.specs.Specs;
|
||||||
import org.gradle.api.tasks.WorkResult;
|
|
||||||
import org.gradle.api.tasks.bundling.Jar;
|
import org.gradle.api.tasks.bundling.Jar;
|
||||||
import org.gradle.api.tasks.util.PatternSet;
|
import org.gradle.api.tasks.util.PatternSet;
|
||||||
import org.gradle.util.GradleVersion;
|
import org.gradle.util.GradleVersion;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Support class for implementations of {@link BootArchive}.
|
* Support class for implementations of {@link BootArchive}.
|
||||||
*
|
*
|
||||||
|
@ -123,13 +115,11 @@ class BootArchiveSupport {
|
||||||
return (version != null) ? version : "unknown";
|
return (version != null) ? version : "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies,
|
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies, boolean supportsSignatureFile) {
|
||||||
LoaderImplementation loaderImplementation, boolean supportsSignatureFile) {
|
return createCopyAction(jar, resolvedDependencies, supportsSignatureFile, null, null);
|
||||||
return createCopyAction(jar, resolvedDependencies, loaderImplementation, supportsSignatureFile, null, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies,
|
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies, boolean supportsSignatureFile,
|
||||||
LoaderImplementation loaderImplementation, boolean supportsSignatureFile,
|
|
||||||
@Nullable LayerResolver layerResolver, @Nullable String jarmodeToolsLocation) {
|
@Nullable LayerResolver layerResolver, @Nullable String jarmodeToolsLocation) {
|
||||||
File output = jar.getArchiveFile().get().getAsFile();
|
File output = jar.getArchiveFile().get().getAsFile();
|
||||||
Manifest manifest = jar.getManifest();
|
Manifest manifest = jar.getManifest();
|
||||||
|
@ -145,9 +135,8 @@ class BootArchiveSupport {
|
||||||
String encoding = jar.getMetadataCharset();
|
String encoding = jar.getMetadataCharset();
|
||||||
CopyAction action = new BootZipCopyAction(output, manifest, preserveFileTimestamps, dirPermissions,
|
CopyAction action = new BootZipCopyAction(output, manifest, preserveFileTimestamps, dirPermissions,
|
||||||
filePermissions, includeDefaultLoader, jarmodeToolsLocation, requiresUnpack, exclusions, launchScript,
|
filePermissions, includeDefaultLoader, jarmodeToolsLocation, requiresUnpack, exclusions, launchScript,
|
||||||
librarySpec, compressionResolver, encoding, resolvedDependencies, supportsSignatureFile, layerResolver,
|
librarySpec, compressionResolver, encoding, resolvedDependencies, supportsSignatureFile, layerResolver);
|
||||||
loaderImplementation);
|
return action;
|
||||||
return jar.isReproducibleFileOrder() ? new ReproducibleOrderingCopyAction(action) : action;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private @Nullable Integer getUnixNumericDirPermissions(CopySpec copySpec) {
|
private @Nullable Integer getUnixNumericDirPermissions(CopySpec copySpec) {
|
||||||
|
@ -164,14 +153,22 @@ class BootArchiveSupport {
|
||||||
return permissions.isPresent() ? permissions.get().toUnixNumeric() : null;
|
return permissions.isPresent() ? permissions.get().toUnixNumeric() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private @Nullable Integer getDirMode(CopySpec copySpec) {
|
private @Nullable Integer getDirMode(CopySpec copySpec) {
|
||||||
return copySpec.getDirMode();
|
try {
|
||||||
|
return (Integer) copySpec.getClass().getMethod("getDirMode").invoke(copySpec);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new RuntimeException("Failed to get dir mode from CopySpec", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private @Nullable Integer getFileMode(CopySpec copySpec) {
|
private @Nullable Integer getFileMode(CopySpec copySpec) {
|
||||||
return copySpec.getFileMode();
|
try {
|
||||||
|
return (Integer) copySpec.getClass().getMethod("getFileMode").invoke(copySpec);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new RuntimeException("Failed to get file mode from CopySpec", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isUsingDefaultLoader(Jar jar) {
|
private boolean isUsingDefaultLoader(Jar jar) {
|
||||||
|
@ -234,26 +231,4 @@ class BootArchiveSupport {
|
||||||
details.setRelativePath(details.getRelativeSourcePath());
|
details.setRelativePath(details.getRelativeSourcePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link CopyAction} variant that sorts entries to ensure reproducible ordering.
|
|
||||||
*/
|
|
||||||
private static final class ReproducibleOrderingCopyAction implements CopyAction {
|
|
||||||
|
|
||||||
private final CopyAction delegate;
|
|
||||||
|
|
||||||
private ReproducibleOrderingCopyAction(CopyAction delegate) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WorkResult execute(CopyActionProcessingStream stream) {
|
|
||||||
return this.delegate.execute((action) -> {
|
|
||||||
Map<RelativePath, FileCopyDetailsInternal> detailsByPath = new TreeMap<>();
|
|
||||||
stream.process((details) -> detailsByPath.put(details.getRelativePath(), details));
|
|
||||||
detailsByPath.values().forEach(action::processFile);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,6 @@ import org.gradle.api.tasks.bundling.Jar;
|
||||||
import org.gradle.work.DisableCachingByDefault;
|
import org.gradle.work.DisableCachingByDefault;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom {@link Jar} task that produces a Spring Boot executable jar.
|
* A custom {@link Jar} task that produces a Spring Boot executable jar.
|
||||||
*
|
*
|
||||||
|
@ -145,13 +143,12 @@ public abstract class BootJar extends Jar implements BootArchive {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CopyAction createCopyAction() {
|
protected CopyAction createCopyAction() {
|
||||||
LoaderImplementation loaderImplementation = getLoaderImplementation().getOrElse(LoaderImplementation.DEFAULT);
|
|
||||||
LayerResolver layerResolver = null;
|
LayerResolver layerResolver = null;
|
||||||
if (!isLayeredDisabled()) {
|
if (!isLayeredDisabled()) {
|
||||||
layerResolver = new LayerResolver(this.resolvedDependencies, this.layered, this::isLibrary);
|
layerResolver = new LayerResolver(this.resolvedDependencies, this.layered, this::isLibrary);
|
||||||
}
|
}
|
||||||
String jarmodeToolsLocation = isIncludeJarmodeTools() ? LIB_DIRECTORY : null;
|
String jarmodeToolsLocation = isIncludeJarmodeTools() ? LIB_DIRECTORY : null;
|
||||||
return this.support.createCopyAction(this, this.resolvedDependencies, loaderImplementation, true, layerResolver,
|
return this.support.createCopyAction(this, this.resolvedDependencies, true, layerResolver,
|
||||||
jarmodeToolsLocation);
|
jarmodeToolsLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,6 @@ import org.gradle.api.tasks.bundling.War;
|
||||||
import org.gradle.work.DisableCachingByDefault;
|
import org.gradle.work.DisableCachingByDefault;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom {@link War} task that produces a Spring Boot executable war.
|
* A custom {@link War} task that produces a Spring Boot executable war.
|
||||||
*
|
*
|
||||||
|
@ -119,14 +117,13 @@ public abstract class BootWar extends War implements BootArchive {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CopyAction createCopyAction() {
|
protected CopyAction createCopyAction() {
|
||||||
LoaderImplementation loaderImplementation = getLoaderImplementation().getOrElse(LoaderImplementation.DEFAULT);
|
|
||||||
LayerResolver layerResolver = null;
|
LayerResolver layerResolver = null;
|
||||||
if (!isLayeredDisabled()) {
|
if (!isLayeredDisabled()) {
|
||||||
layerResolver = new LayerResolver(this.resolvedDependencies, this.layered, this::isLibrary);
|
layerResolver = new LayerResolver(this.resolvedDependencies, this.layered, this::isLibrary);
|
||||||
}
|
}
|
||||||
String jarmodeToolsLocation = isIncludeJarmodeTools() ? LIB_DIRECTORY : null;
|
String jarmodeToolsLocation = isIncludeJarmodeTools() ? LIB_DIRECTORY : null;
|
||||||
return this.support.createCopyAction(this, this.resolvedDependencies, loaderImplementation, false,
|
return this.support.createCopyAction(this, this.resolvedDependencies, false, layerResolver,
|
||||||
layerResolver, jarmodeToolsLocation);
|
jarmodeToolsLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isIncludeJarmodeTools() {
|
private boolean isIncludeJarmodeTools() {
|
||||||
|
|
|
@ -23,13 +23,10 @@ import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HexFormat;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -63,7 +60,6 @@ import org.springframework.boot.loader.tools.JarModeLibrary;
|
||||||
import org.springframework.boot.loader.tools.Layer;
|
import org.springframework.boot.loader.tools.Layer;
|
||||||
import org.springframework.boot.loader.tools.LayersIndex;
|
import org.springframework.boot.loader.tools.LayersIndex;
|
||||||
import org.springframework.boot.loader.tools.LibraryCoordinates;
|
import org.springframework.boot.loader.tools.LibraryCoordinates;
|
||||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
|
||||||
import org.springframework.boot.loader.tools.NativeImageArgFile;
|
import org.springframework.boot.loader.tools.NativeImageArgFile;
|
||||||
import org.springframework.boot.loader.tools.ReachabilityMetadataProperties;
|
import org.springframework.boot.loader.tools.ReachabilityMetadataProperties;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
@ -120,15 +116,13 @@ class BootZipCopyAction implements CopyAction {
|
||||||
|
|
||||||
private final @Nullable LayerResolver layerResolver;
|
private final @Nullable LayerResolver layerResolver;
|
||||||
|
|
||||||
private final LoaderImplementation loaderImplementation;
|
|
||||||
|
|
||||||
BootZipCopyAction(File output, Manifest manifest, boolean preserveFileTimestamps, @Nullable Integer dirMode,
|
BootZipCopyAction(File output, Manifest manifest, boolean preserveFileTimestamps, @Nullable Integer dirMode,
|
||||||
@Nullable Integer fileMode, boolean includeDefaultLoader, @Nullable String jarmodeToolsLocation,
|
@Nullable Integer fileMode, boolean includeDefaultLoader, @Nullable String jarmodeToolsLocation,
|
||||||
Spec<FileTreeElement> requiresUnpack, Spec<FileTreeElement> exclusions,
|
Spec<FileTreeElement> requiresUnpack, Spec<FileTreeElement> exclusions,
|
||||||
@Nullable LaunchScriptConfiguration launchScript, Spec<FileCopyDetails> librarySpec,
|
@Nullable LaunchScriptConfiguration launchScript, Spec<FileCopyDetails> librarySpec,
|
||||||
Function<FileCopyDetails, ZipCompression> compressionResolver, @Nullable String encoding,
|
Function<FileCopyDetails, ZipCompression> compressionResolver, @Nullable String encoding,
|
||||||
ResolvedDependencies resolvedDependencies, boolean supportsSignatureFile,
|
ResolvedDependencies resolvedDependencies, boolean supportsSignatureFile,
|
||||||
@Nullable LayerResolver layerResolver, LoaderImplementation loaderImplementation) {
|
@Nullable LayerResolver layerResolver) {
|
||||||
this.output = output;
|
this.output = output;
|
||||||
this.manifest = manifest;
|
this.manifest = manifest;
|
||||||
this.preserveFileTimestamps = preserveFileTimestamps;
|
this.preserveFileTimestamps = preserveFileTimestamps;
|
||||||
|
@ -145,7 +139,6 @@ class BootZipCopyAction implements CopyAction {
|
||||||
this.resolvedDependencies = resolvedDependencies;
|
this.resolvedDependencies = resolvedDependencies;
|
||||||
this.supportsSignatureFile = supportsSignatureFile;
|
this.supportsSignatureFile = supportsSignatureFile;
|
||||||
this.layerResolver = layerResolver;
|
this.layerResolver = layerResolver;
|
||||||
this.loaderImplementation = loaderImplementation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -329,8 +322,7 @@ class BootZipCopyAction implements CopyAction {
|
||||||
// Always write loader entries after META-INF directory (see gh-16698)
|
// Always write loader entries after META-INF directory (see gh-16698)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LoaderZipEntries loaderEntries = new LoaderZipEntries(getTime(), getDirMode(), getFileMode(),
|
LoaderZipEntries loaderEntries = new LoaderZipEntries(getTime(), getDirMode(), getFileMode());
|
||||||
BootZipCopyAction.this.loaderImplementation);
|
|
||||||
this.writtenLoaderEntries = loaderEntries.writeTo(this.out);
|
this.writtenLoaderEntries = loaderEntries.writeTo(this.out);
|
||||||
if (BootZipCopyAction.this.layerResolver != null) {
|
if (BootZipCopyAction.this.layerResolver != null) {
|
||||||
for (String name : this.writtenLoaderEntries.getFiles()) {
|
for (String name : this.writtenLoaderEntries.getFiles()) {
|
||||||
|
@ -512,9 +504,13 @@ class BootZipCopyAction implements CopyAction {
|
||||||
? details.getPermissions().toUnixNumeric() : getMode(details);
|
? details.getPermissions().toUnixNumeric() : getMode(details);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private int getMode(FileCopyDetails details) {
|
private int getMode(FileCopyDetails details) {
|
||||||
return details.getMode();
|
try {
|
||||||
|
return (int) details.getClass().getMethod("getMode").invoke(details);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new RuntimeException("Failed to get mode from FileCopyDetails", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -590,36 +586,24 @@ class BootZipCopyAction implements CopyAction {
|
||||||
|
|
||||||
private static final int BUFFER_SIZE = 32 * 1024;
|
private static final int BUFFER_SIZE = 32 * 1024;
|
||||||
|
|
||||||
private final @Nullable MessageDigest messageDigest;
|
private final boolean unpack;
|
||||||
|
|
||||||
private final CRC32 crc = new CRC32();
|
private final CRC32 crc = new CRC32();
|
||||||
|
|
||||||
private long size;
|
private long size;
|
||||||
|
|
||||||
StoredEntryPreparator(InputStream inputStream, boolean unpack) throws IOException {
|
StoredEntryPreparator(InputStream inputStream, boolean unpack) throws IOException {
|
||||||
this.messageDigest = (unpack) ? sha1Digest() : null;
|
this.unpack = unpack;
|
||||||
try (inputStream) {
|
try (inputStream) {
|
||||||
load(inputStream);
|
load(inputStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MessageDigest sha1Digest() {
|
|
||||||
try {
|
|
||||||
return MessageDigest.getInstance("SHA-1");
|
|
||||||
}
|
|
||||||
catch (NoSuchAlgorithmException ex) {
|
|
||||||
throw new IllegalStateException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void load(InputStream inputStream) throws IOException {
|
private void load(InputStream inputStream) throws IOException {
|
||||||
byte[] buffer = new byte[BUFFER_SIZE];
|
byte[] buffer = new byte[BUFFER_SIZE];
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||||
this.crc.update(buffer, 0, bytesRead);
|
this.crc.update(buffer, 0, bytesRead);
|
||||||
if (this.messageDigest != null) {
|
|
||||||
this.messageDigest.update(buffer, 0, bytesRead);
|
|
||||||
}
|
|
||||||
this.size += bytesRead;
|
this.size += bytesRead;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -629,8 +613,8 @@ class BootZipCopyAction implements CopyAction {
|
||||||
entry.setCompressedSize(this.size);
|
entry.setCompressedSize(this.size);
|
||||||
entry.setCrc(this.crc.getValue());
|
entry.setCrc(this.crc.getValue());
|
||||||
entry.setMethod(ZipEntry.STORED);
|
entry.setMethod(ZipEntry.STORED);
|
||||||
if (this.messageDigest != null) {
|
if (this.unpack) {
|
||||||
entry.setComment("UNPACK:" + HexFormat.of().formatHex(this.messageDigest.digest()));
|
entry.setComment("UNPACK");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
||||||
import org.gradle.api.file.FileTreeElement;
|
import org.gradle.api.file.FileTreeElement;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StreamUtils;
|
import org.springframework.util.StreamUtils;
|
||||||
|
|
||||||
|
@ -42,27 +41,22 @@ import org.springframework.util.StreamUtils;
|
||||||
*/
|
*/
|
||||||
class LoaderZipEntries {
|
class LoaderZipEntries {
|
||||||
|
|
||||||
private final LoaderImplementation loaderImplementation;
|
|
||||||
|
|
||||||
private final @Nullable Long entryTime;
|
private final @Nullable Long entryTime;
|
||||||
|
|
||||||
private final int dirMode;
|
private final int dirMode;
|
||||||
|
|
||||||
private final int fileMode;
|
private final int fileMode;
|
||||||
|
|
||||||
LoaderZipEntries(@Nullable Long entryTime, int dirMode, int fileMode,
|
LoaderZipEntries(@Nullable Long entryTime, int dirMode, int fileMode) {
|
||||||
@Nullable LoaderImplementation loaderImplementation) {
|
|
||||||
this.entryTime = entryTime;
|
this.entryTime = entryTime;
|
||||||
this.dirMode = dirMode;
|
this.dirMode = dirMode;
|
||||||
this.fileMode = fileMode;
|
this.fileMode = fileMode;
|
||||||
this.loaderImplementation = (loaderImplementation != null) ? loaderImplementation
|
|
||||||
: LoaderImplementation.DEFAULT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WrittenEntries writeTo(ZipArchiveOutputStream out) throws IOException {
|
WrittenEntries writeTo(ZipArchiveOutputStream out) throws IOException {
|
||||||
WrittenEntries written = new WrittenEntries();
|
WrittenEntries written = new WrittenEntries();
|
||||||
try (ZipInputStream loaderJar = new ZipInputStream(
|
try (ZipInputStream loaderJar = new ZipInputStream(
|
||||||
getResourceAsStream("/" + this.loaderImplementation.getJarResourceName()))) {
|
getResourceAsStream("/META-INF/loader/spring-boot-loader.jar"))) {
|
||||||
java.util.zip.ZipEntry entry = loaderJar.getNextEntry();
|
java.util.zip.ZipEntry entry = loaderJar.getNextEntry();
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.isDirectory() && !entry.getName().equals("META-INF/")) {
|
if (entry.isDirectory() && !entry.getName().equals("META-INF/")) {
|
||||||
|
|
|
@ -124,7 +124,7 @@ class PackagingDocumentationTests {
|
||||||
try (JarFile jar = new JarFile(file)) {
|
try (JarFile jar = new JarFile(file)) {
|
||||||
JarEntry entry = jar.getJarEntry("BOOT-INF/lib/jruby-complete-1.7.25.jar");
|
JarEntry entry = jar.getJarEntry("BOOT-INF/lib/jruby-complete-1.7.25.jar");
|
||||||
assertThat(entry).isNotNull();
|
assertThat(entry).isNotNull();
|
||||||
assertThat(entry.getComment()).startsWith("UNPACK:");
|
assertThat(entry.getComment()).isEqualTo("UNPACK");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,6 @@ import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import org.gradle.api.Project;
|
import org.gradle.api.Project;
|
||||||
import org.gradle.api.internal.project.ProjectInternal;
|
|
||||||
import org.gradle.initialization.GradlePropertiesController;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
@ -173,11 +171,7 @@ class BuildInfoTests {
|
||||||
|
|
||||||
private Project createProject(String projectName) {
|
private Project createProject(String projectName) {
|
||||||
File projectDir = new File(this.temp, projectName);
|
File projectDir = new File(this.temp, projectName);
|
||||||
Project project = GradleProjectBuilder.builder().withProjectDir(projectDir).withName(projectName).build();
|
return GradleProjectBuilder.builder().withProjectDir(projectDir).withName(projectName).build();
|
||||||
((ProjectInternal) project).getServices()
|
|
||||||
.get(GradlePropertiesController.class)
|
|
||||||
.loadGradlePropertiesFrom(projectDir, false);
|
|
||||||
return project;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BuildInfo createTask(Project project) {
|
private BuildInfo createTask(Project project) {
|
||||||
|
|
|
@ -108,16 +108,6 @@ abstract class AbstractBootArchiveIntegrationTests {
|
||||||
assertThat(firstHash).isEqualTo(secondHash);
|
assertThat(firstHash).isEqualTo(secondHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestTemplate
|
|
||||||
void classicLoader() throws IOException {
|
|
||||||
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
|
|
||||||
.isEqualTo(TaskOutcome.SUCCESS);
|
|
||||||
File jar = new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0];
|
|
||||||
try (JarFile jarFile = new JarFile(jar)) {
|
|
||||||
assertThat(jarFile.getEntry("org/springframework/boot/loader/LaunchedURLClassLoader.class")).isNotNull();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
void upToDateWhenBuiltTwice() {
|
void upToDateWhenBuiltTwice() {
|
||||||
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
|
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
|
||||||
|
@ -243,15 +233,9 @@ abstract class AbstractBootArchiveIntegrationTests {
|
||||||
.filter((entry) -> !entry.isDirectory())
|
.filter((entry) -> !entry.isDirectory())
|
||||||
.map(JarEntry::getName)
|
.map(JarEntry::getName)
|
||||||
.filter((name) -> name.startsWith(this.libPath));
|
.filter((name) -> name.startsWith(this.libPath));
|
||||||
if (this.gradleBuild.gradleVersionIsLessThan("9.0.0-rc-1")) {
|
|
||||||
assertThat(libEntryNames).containsExactly(this.libPath + "two-1.0.jar",
|
assertThat(libEntryNames).containsExactly(this.libPath + "two-1.0.jar",
|
||||||
this.libPath + "commons-io-2.19.0.jar");
|
this.libPath + "commons-io-2.19.0.jar");
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
assertThat(libEntryNames).containsExactly(this.libPath + "commons-io-2.19.0.jar",
|
|
||||||
this.libPath + "two-1.0.jar");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
|
@ -293,6 +277,7 @@ abstract class AbstractBootArchiveIntegrationTests {
|
||||||
void jarTypeFilteringIsApplied() throws IOException {
|
void jarTypeFilteringIsApplied() throws IOException {
|
||||||
File flatDirRepository = new File(this.gradleBuild.getProjectDir(), "repository");
|
File flatDirRepository = new File(this.gradleBuild.getProjectDir(), "repository");
|
||||||
createDependenciesStarterJar(new File(flatDirRepository, "starter.jar"));
|
createDependenciesStarterJar(new File(flatDirRepository, "starter.jar"));
|
||||||
|
createDependenciesDeveloperToolsJar(new File(flatDirRepository, "devonly.jar"));
|
||||||
createStandardJar(new File(flatDirRepository, "standard.jar"));
|
createStandardJar(new File(flatDirRepository, "standard.jar"));
|
||||||
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
|
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
|
||||||
.isEqualTo(TaskOutcome.SUCCESS);
|
.isEqualTo(TaskOutcome.SUCCESS);
|
||||||
|
@ -669,6 +654,10 @@ abstract class AbstractBootArchiveIntegrationTests {
|
||||||
createJar(location, (attributes) -> attributes.putValue("Spring-Boot-Jar-Type", "dependencies-starter"));
|
createJar(location, (attributes) -> attributes.putValue("Spring-Boot-Jar-Type", "dependencies-starter"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createDependenciesDeveloperToolsJar(File location) throws IOException {
|
||||||
|
createJar(location, (attributes) -> attributes.putValue("Spring-Boot-Jar-Type", "development-tool"));
|
||||||
|
}
|
||||||
|
|
||||||
private void createJar(File location, Consumer<Attributes> attributesConfigurer) throws IOException {
|
private void createJar(File location, Consumer<Attributes> attributesConfigurer) throws IOException {
|
||||||
location.getParentFile().mkdirs();
|
location.getParentFile().mkdirs();
|
||||||
Manifest manifest = new Manifest();
|
Manifest manifest = new Manifest();
|
||||||
|
|
|
@ -27,6 +27,8 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.nio.file.attribute.PosixFilePermission;
|
import java.nio.file.attribute.PosixFilePermission;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -35,6 +37,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
import java.util.jar.JarOutputStream;
|
import java.util.jar.JarOutputStream;
|
||||||
|
@ -65,7 +68,6 @@ import org.junit.jupiter.api.io.TempDir;
|
||||||
import org.springframework.boot.gradle.junit.GradleProjectBuilder;
|
import org.springframework.boot.gradle.junit.GradleProjectBuilder;
|
||||||
import org.springframework.boot.loader.tools.DefaultLaunchScript;
|
import org.springframework.boot.loader.tools.DefaultLaunchScript;
|
||||||
import org.springframework.boot.loader.tools.JarModeLibrary;
|
import org.springframework.boot.loader.tools.JarModeLibrary;
|
||||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
|
||||||
import org.springframework.util.FileCopyUtils;
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -283,17 +285,6 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void loaderIsWrittenToTheRootOfTheJarWhenUsingClassicLoader() throws IOException {
|
|
||||||
this.task.getMainClass().set("com.example.Main");
|
|
||||||
this.task.getLoaderImplementation().set(LoaderImplementation.CLASSIC);
|
|
||||||
executeTask();
|
|
||||||
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
|
||||||
assertThat(jarFile.getEntry("org/springframework/boot/loader/LaunchedURLClassLoader.class")).isNotNull();
|
|
||||||
assertThat(jarFile.getEntry("org/springframework/boot/loader/")).isNotNull();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void unpackCommentIsAddedToEntryIdentifiedByAPattern() throws IOException {
|
void unpackCommentIsAddedToEntryIdentifiedByAPattern() throws IOException {
|
||||||
this.task.getMainClass().set("com.example.Main");
|
this.task.getMainClass().set("com.example.Main");
|
||||||
|
@ -301,7 +292,7 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
||||||
this.task.requiresUnpack("**/one.jar");
|
this.task.requiresUnpack("**/one.jar");
|
||||||
executeTask();
|
executeTask();
|
||||||
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
||||||
assertThat(jarFile.getEntry(this.libPath + "one.jar").getComment()).startsWith("UNPACK:");
|
assertThat(jarFile.getEntry(this.libPath + "one.jar").getComment()).isEqualTo("UNPACK");
|
||||||
assertThat(jarFile.getEntry(this.libPath + "two.jar").getComment()).isNull();
|
assertThat(jarFile.getEntry(this.libPath + "two.jar").getComment()).isNull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,7 +304,7 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
||||||
this.task.requiresUnpack((element) -> element.getName().endsWith("two.jar"));
|
this.task.requiresUnpack((element) -> element.getName().endsWith("two.jar"));
|
||||||
executeTask();
|
executeTask();
|
||||||
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
||||||
assertThat(jarFile.getEntry(this.libPath + "two.jar").getComment()).startsWith("UNPACK:");
|
assertThat(jarFile.getEntry(this.libPath + "two.jar").getComment()).isEqualTo("UNPACK");
|
||||||
assertThat(jarFile.getEntry(this.libPath + "one.jar").getComment()).isNull();
|
assertThat(jarFile.getEntry(this.libPath + "one.jar").getComment()).isNull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -419,23 +410,46 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void reproducibleOrderingCanBeEnabled() throws IOException {
|
void archiveIsReproducibleByDefault() throws IOException {
|
||||||
this.task.getMainClass().set("com.example.Main");
|
this.task.getMainClass().set("com.example.Main");
|
||||||
this.task.from(newFile("bravo.txt"), newFile("alpha.txt"), newFile("charlie.txt"));
|
this.task.from(newFiles("files/b/bravo.txt", "files/a/alpha.txt", "files/c/charlie.txt"));
|
||||||
this.task.setReproducibleFileOrder(true);
|
|
||||||
executeTask();
|
executeTask();
|
||||||
assertThat(this.task.getArchiveFile().get().getAsFile()).exists();
|
assertThat(this.task.getArchiveFile().get().getAsFile()).exists();
|
||||||
List<String> textFiles = new ArrayList<>();
|
List<String> files = new ArrayList<>();
|
||||||
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
||||||
Enumeration<JarEntry> entries = jarFile.entries();
|
Enumeration<JarEntry> entries = jarFile.entries();
|
||||||
while (entries.hasMoreElements()) {
|
while (entries.hasMoreElements()) {
|
||||||
JarEntry entry = entries.nextElement();
|
JarEntry entry = entries.nextElement();
|
||||||
if (entry.getName().endsWith(".txt")) {
|
assertThat(entry.getLastModifiedTime().toMillis())
|
||||||
textFiles.add(entry.getName());
|
.isEqualTo(ZipEntryConstants.CONSTANT_TIME_FOR_ZIP_ENTRIES);
|
||||||
|
if (entry.getName().startsWith("files/")) {
|
||||||
|
files.add(entry.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertThat(files).containsExactly("files/", "files/a/", "files/a/alpha.txt", "files/b/", "files/b/bravo.txt",
|
||||||
|
"files/c/", "files/c/charlie.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void archiveReproducibilityCanBeDisabled() throws IOException {
|
||||||
|
this.task.getMainClass().set("com.example.Main");
|
||||||
|
this.task.from(newFiles("files/b/bravo.txt", "files/a/alpha.txt", "files/c/charlie.txt"));
|
||||||
|
this.task.setPreserveFileTimestamps(true);
|
||||||
|
this.task.setReproducibleFileOrder(false);
|
||||||
|
executeTask();
|
||||||
|
assertThat(this.task.getArchiveFile().get().getAsFile()).exists();
|
||||||
|
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
||||||
|
Enumeration<JarEntry> entries = jarFile.entries();
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
JarEntry entry = entries.nextElement();
|
||||||
|
if (entry.getName().endsWith(".txt") || entry.getName().startsWith("BOOT-INF/lib/")) {
|
||||||
|
OffsetDateTime lastModifiedTime = entry.getLastModifiedTime().toInstant().atOffset(ZoneOffset.UTC);
|
||||||
|
assertThat(lastModifiedTime)
|
||||||
|
.isNotEqualTo(OffsetDateTime.of(1980, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertThat(textFiles).containsExactly("alpha.txt", "bravo.txt", "charlie.txt");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -675,6 +689,19 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
||||||
return entryNames;
|
return entryNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected File newFiles(String... names) throws IOException {
|
||||||
|
File dir = new File(this.temp, UUID.randomUUID().toString());
|
||||||
|
dir.mkdir();
|
||||||
|
List<File> files = new ArrayList<>();
|
||||||
|
for (String name : names) {
|
||||||
|
File file = new File(dir, name);
|
||||||
|
file.getParentFile().mkdirs();
|
||||||
|
file.createNewFile();
|
||||||
|
files.add(file);
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
protected File newFile(String name) throws IOException {
|
protected File newFile(String name) throws IOException {
|
||||||
File file = new File(this.temp, name);
|
File file = new File(this.temp, name);
|
||||||
file.createNewFile();
|
file.createNewFile();
|
||||||
|
|
|
@ -30,8 +30,8 @@ import org.junit.jupiter.api.io.TempDir;
|
||||||
import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
||||||
import org.springframework.boot.buildpack.platform.build.BuildpackReference;
|
import org.springframework.boot.buildpack.platform.build.BuildpackReference;
|
||||||
import org.springframework.boot.buildpack.platform.build.PullPolicy;
|
import org.springframework.boot.buildpack.platform.build.PullPolicy;
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||||
import org.springframework.boot.gradle.junit.GradleProjectBuilder;
|
import org.springframework.boot.gradle.junit.GradleProjectBuilder;
|
||||||
|
|
||||||
|
|
|
@ -66,18 +66,10 @@ class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests {
|
||||||
copyClasspathApplication();
|
copyClasspathApplication();
|
||||||
BuildResult result = this.gradleBuild.build("launch");
|
BuildResult result = this.gradleBuild.build("launch");
|
||||||
String output = result.getOutput();
|
String output = result.getOutput();
|
||||||
if (this.gradleBuild.gradleVersionIsLessThan("9.0.0-rc-1")) {
|
|
||||||
assertThat(output).containsPattern("1\\. .*classes");
|
assertThat(output).containsPattern("1\\. .*classes");
|
||||||
assertThat(output).containsPattern("2\\. .*library-1.0-SNAPSHOT.jar");
|
assertThat(output).containsPattern("2\\. .*library-1.0-SNAPSHOT.jar");
|
||||||
assertThat(output).containsPattern("3\\. .*commons-lang3-3.9.jar");
|
assertThat(output).containsPattern("3\\. .*commons-lang3-3.9.jar");
|
||||||
assertThat(output).containsPattern("4\\. .*spring-boot-jarmode-tools.*.jar");
|
assertThat(output).containsPattern("4\\. .*spring-boot-jarmode-tools.*.jar");
|
||||||
}
|
|
||||||
else {
|
|
||||||
assertThat(output).containsPattern("1\\. .*classes");
|
|
||||||
assertThat(output).containsPattern("2\\. .*commons-lang3-3.9.jar");
|
|
||||||
assertThat(output).containsPattern("3\\. .*library-1.0-SNAPSHOT.jar");
|
|
||||||
assertThat(output).containsPattern("4\\. .*spring-boot-jarmode-tools.*.jar");
|
|
||||||
}
|
|
||||||
assertThat(output).doesNotContain("5. ");
|
assertThat(output).doesNotContain("5. ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,18 +78,10 @@ class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests {
|
||||||
copyClasspathApplication();
|
copyClasspathApplication();
|
||||||
BuildResult result = this.gradleBuild.build("launch");
|
BuildResult result = this.gradleBuild.build("launch");
|
||||||
String output = result.getOutput();
|
String output = result.getOutput();
|
||||||
if (this.gradleBuild.gradleVersionIsLessThan("9.0.0-rc-1")) {
|
|
||||||
assertThat(output).containsPattern("1\\. .*classes");
|
assertThat(output).containsPattern("1\\. .*classes");
|
||||||
assertThat(output).containsPattern("2\\. .*spring-boot-jarmode-tools.*.jar");
|
assertThat(output).containsPattern("2\\. .*spring-boot-jarmode-tools.*.jar");
|
||||||
assertThat(output).containsPattern("3\\. .*library-1.0-SNAPSHOT.jar");
|
assertThat(output).containsPattern("3\\. .*library-1.0-SNAPSHOT.jar");
|
||||||
assertThat(output).containsPattern("4\\. .*commons-lang3-3.9.jar");
|
assertThat(output).containsPattern("4\\. .*commons-lang3-3.9.jar");
|
||||||
}
|
|
||||||
else {
|
|
||||||
assertThat(output).containsPattern("1\\. .*classes");
|
|
||||||
assertThat(output).containsPattern("2\\. .*spring-boot-jarmode-tools.*.jar");
|
|
||||||
assertThat(output).containsPattern("3\\. .*commons-lang3-3.9.jar");
|
|
||||||
assertThat(output).containsPattern("4\\. .*library-1.0-SNAPSHOT.jar");
|
|
||||||
}
|
|
||||||
assertThat(output).doesNotContain("5. ");
|
assertThat(output).doesNotContain("5. ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ plugins {
|
||||||
|
|
||||||
bootJar {
|
bootJar {
|
||||||
mainClass = 'com.example.Application'
|
mainClass = 'com.example.Application'
|
||||||
|
if (GradleVersion.current().compareTo(GradleVersion.version("9.0.0-rc-1")) < 0) {
|
||||||
preserveFileTimestamps = false
|
preserveFileTimestamps = false
|
||||||
reproducibleFileOrder = true
|
reproducibleFileOrder = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ plugins {
|
||||||
|
|
||||||
bootWar {
|
bootWar {
|
||||||
mainClass = 'com.example.Application'
|
mainClass = 'com.example.Application'
|
||||||
|
if (GradleVersion.current().compareTo(GradleVersion.version("9.0.0-rc-1")) < 0) {
|
||||||
preserveFileTimestamps = false
|
preserveFileTimestamps = false
|
||||||
reproducibleFileOrder = true
|
reproducibleFileOrder = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<!-- tag::different-versions[] -->
|
<!-- tag::different-versions[] -->
|
||||||
<properties>
|
<properties>
|
||||||
<slf4j.version>1.7.30</slf4j.version>
|
<slf4j.version>1.7.30</slf4j.version>
|
||||||
<spring-data-releasetrain.version>Moore-SR6</spring-data-releasetrain.version>
|
<spring-data-bom.version>2024.1.10</spring-data-bom.version>
|
||||||
</properties>
|
</properties>
|
||||||
<!-- end::different-versions[] -->
|
<!-- end::different-versions[] -->
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,8 @@
|
||||||
<!-- Override Spring Data release train provided by Spring Boot -->
|
<!-- Override Spring Data release train provided by Spring Boot -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.data</groupId>
|
<groupId>org.springframework.data</groupId>
|
||||||
<artifactId>spring-data-releasetrain</artifactId>
|
<artifactId>spring-data-bom</artifactId>
|
||||||
<version>2020.0.0-SR1</version>
|
<version>2024.1.10</version>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
|
@ -67,6 +67,9 @@ include::example$using/different-versions-pom.xml[tags=different-versions]
|
||||||
|
|
||||||
Browse the xref:appendix:dependency-versions/properties.adoc[Dependency Versions Properties] section in the Spring Boot reference for a complete list of dependency version properties.
|
Browse the xref:appendix:dependency-versions/properties.adoc[Dependency Versions Properties] section in the Spring Boot reference for a complete list of dependency version properties.
|
||||||
|
|
||||||
|
WARNING: Each Spring Boot release is designed and tested against a specific set of third-party dependencies.
|
||||||
|
Overriding versions may cause compatibility issues and should be done with care.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[using.import]]
|
[[using.import]]
|
||||||
|
|
|
@ -159,7 +159,7 @@ abstract class AbstractArchiveIntegrationTests {
|
||||||
Optional<JarEntry> match = entries.filter((entry) -> entry.getName().startsWith(prefix))
|
Optional<JarEntry> match = entries.filter((entry) -> entry.getName().startsWith(prefix))
|
||||||
.findFirst();
|
.findFirst();
|
||||||
assertThat(match).as("Name starting with %s", prefix)
|
assertThat(match).as("Name starting with %s", prefix)
|
||||||
.hasValueSatisfying((entry) -> assertThat(entry.getComment()).startsWith("UNPACK:"));
|
.hasValueSatisfying((entry) -> assertThat(entry.getComment()).isEqualTo("UNPACK"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -76,26 +76,6 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestTemplate
|
|
||||||
void whenJarWithClassicLoaderIsRepackagedInPlaceOnlyRepackagedJarIsInstalled(MavenBuild mavenBuild) {
|
|
||||||
mavenBuild.project("jar-with-classic-loader").goals("install").execute((project) -> {
|
|
||||||
File original = new File(project, "target/jar-with-classic-loader-0.0.1.BUILD-SNAPSHOT.jar.original");
|
|
||||||
assertThat(original).isFile();
|
|
||||||
File repackaged = new File(project, "target/jar-with-classic-loader-0.0.1.BUILD-SNAPSHOT.jar");
|
|
||||||
assertThat(launchScript(repackaged)).isEmpty();
|
|
||||||
assertThat(jar(repackaged)).manifest((manifest) -> {
|
|
||||||
manifest.hasMainClass("org.springframework.boot.loader.launch.JarLauncher");
|
|
||||||
manifest.hasStartClass("some.random.Main");
|
|
||||||
manifest.hasAttribute("Not-Used", "Foo");
|
|
||||||
}).hasEntryWithName("org/springframework/boot/loader/launch/JarLauncher.class");
|
|
||||||
assertThat(buildLog(project))
|
|
||||||
.contains("Replacing main artifact " + repackaged + " with repackaged archive,")
|
|
||||||
.contains("The original artifact has been renamed to " + original)
|
|
||||||
.contains("Installing " + repackaged + " to")
|
|
||||||
.doesNotContain("Installing " + original + " to");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
void whenAttachIsDisabledOnlyTheOriginalJarIsInstalled(MavenBuild mavenBuild) {
|
void whenAttachIsDisabledOnlyTheOriginalJarIsInstalled(MavenBuild mavenBuild) {
|
||||||
mavenBuild.project("jar-attach-disabled").goals("install").execute((project) -> {
|
mavenBuild.project("jar-attach-disabled").goals("install").execute((project) -> {
|
||||||
|
@ -196,7 +176,40 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
||||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
||||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/commons-logging")
|
.hasEntryWithNameStartingWith("BOOT-INF/lib/commons-logging")
|
||||||
.doesNotHaveEntryWithName("BOOT-INF/lib/servlet-api-2.5.jar");
|
.doesNotHaveEntryWithNameStartingWith("BOOT-INF/lib/servlet-api-");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestTemplate
|
||||||
|
void whenAnEntryIsOptionalByDefaultDoesNotAppearInTheRepackagedJar(MavenBuild mavenBuild) {
|
||||||
|
mavenBuild.project("jar-optional-default").goals("install").execute((project) -> {
|
||||||
|
File repackaged = new File(project, "target/jar-optional-default-0.0.1.BUILD-SNAPSHOT.jar");
|
||||||
|
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
|
||||||
|
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||||
|
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
||||||
|
.doesNotHaveEntryWithNameStartingWith("BOOT-INF/lib/log4j-api-");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestTemplate
|
||||||
|
void whenAnEntryIsOptionalAndOptionalsIncludedAppearsInTheRepackagedJar(MavenBuild mavenBuild) {
|
||||||
|
mavenBuild.project("jar-optional-include").goals("install").execute((project) -> {
|
||||||
|
File repackaged = new File(project, "target/jar-optional-include-0.0.1.BUILD-SNAPSHOT.jar");
|
||||||
|
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
|
||||||
|
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||||
|
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
||||||
|
.hasEntryWithNameStartingWith("BOOT-INF/lib/log4j-api-");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestTemplate
|
||||||
|
void whenAnEntryIsOptionalAndOptionalsExcludedDoesNotAppearInTheRepackagedJar(MavenBuild mavenBuild) {
|
||||||
|
mavenBuild.project("jar-optional-exclude").goals("install").execute((project) -> {
|
||||||
|
File repackaged = new File(project, "target/jar-optional-exclude-0.0.1.BUILD-SNAPSHOT.jar");
|
||||||
|
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
|
||||||
|
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||||
|
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
||||||
|
.doesNotHaveEntryWithNameStartingWith("BOOT-INF/lib/log4j-api-");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,9 +261,8 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
||||||
File repackaged = new File(project, "target/jar-exclude-group-0.0.1.BUILD-SNAPSHOT.jar");
|
File repackaged = new File(project, "target/jar-exclude-group-0.0.1.BUILD-SNAPSHOT.jar");
|
||||||
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
|
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
|
||||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
|
||||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/commons-logging")
|
.hasEntryWithNameStartingWith("BOOT-INF/lib/commons-logging")
|
||||||
.doesNotHaveEntryWithName("BOOT-INF/lib/log4j-api-2.4.1.jar");
|
.doesNotHaveEntryWithName("BOOT-INF/lib/log4j-api-");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,7 +400,7 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
||||||
assertThat(layerIndex.get("application")).contains("BOOT-INF/lib/jar-release-0.0.1.RELEASE.jar",
|
assertThat(layerIndex.get("application")).contains("BOOT-INF/lib/jar-release-0.0.1.RELEASE.jar",
|
||||||
"BOOT-INF/lib/jar-snapshot-0.0.1.BUILD-SNAPSHOT.jar");
|
"BOOT-INF/lib/jar-snapshot-0.0.1.BUILD-SNAPSHOT.jar");
|
||||||
assertThat(layerIndex.get("dependencies"))
|
assertThat(layerIndex.get("dependencies"))
|
||||||
.anyMatch((dependency) -> dependency.startsWith("BOOT-INF/lib/log4j-api-2"));
|
.anyMatch((dependency) -> dependency.startsWith("BOOT-INF/lib/log4j-api-"));
|
||||||
}
|
}
|
||||||
catch (IOException ex) {
|
catch (IOException ex) {
|
||||||
// Ignore
|
// Ignore
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>org.springframework.boot.maven.it</groupId>
|
||||||
|
<artifactId>jar-optional-default</artifactId>
|
||||||
|
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>@java.version@</maven.compiler.source>
|
||||||
|
<maven.compiler.target>@java.version@</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>@project.groupId@</groupId>
|
||||||
|
<artifactId>@project.artifactId@</artifactId>
|
||||||
|
<version>@project.version@</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>@maven-jar-plugin.version@</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-context</artifactId>
|
||||||
|
<version>@spring-framework.version@</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-api</artifactId>
|
||||||
|
<version>@log4j2.version@</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -3,7 +3,7 @@
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.springframework.boot.maven.it</groupId>
|
<groupId>org.springframework.boot.maven.it</groupId>
|
||||||
<artifactId>jar-with-classic-loader</artifactId>
|
<artifactId>jar-optional-exclude</artifactId>
|
||||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
<goal>repackage</goal>
|
<goal>repackage</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<loaderImplementation>CLASSIC</loaderImplementation>
|
<includeOptional>false</includeOptional>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
|
@ -31,16 +31,6 @@
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
<version>@maven-jar-plugin.version@</version>
|
<version>@maven-jar-plugin.version@</version>
|
||||||
<configuration>
|
|
||||||
<archive>
|
|
||||||
<manifest>
|
|
||||||
<mainClass>some.random.Main</mainClass>
|
|
||||||
</manifest>
|
|
||||||
<manifestEntries>
|
|
||||||
<Not-Used>Foo</Not-Used>
|
|
||||||
</manifestEntries>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
@ -51,10 +41,10 @@
|
||||||
<version>@spring-framework.version@</version>
|
<version>@spring-framework.version@</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>jakarta.servlet</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>jakarta.servlet-api</artifactId>
|
<artifactId>log4j-api</artifactId>
|
||||||
<version>@jakarta-servlet.version@</version>
|
<version>@log4j2.version@</version>
|
||||||
<scope>provided</scope>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
|
@ -14,7 +14,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
package org.test;
|
||||||
* Utilities used by Spring Boot's JAR loading.
|
|
||||||
*/
|
public class SampleApplication {
|
||||||
package org.springframework.boot.loader.util;
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>org.springframework.boot.maven.it</groupId>
|
||||||
|
<artifactId>jar-optional-include</artifactId>
|
||||||
|
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>@java.version@</maven.compiler.source>
|
||||||
|
<maven.compiler.target>@java.version@</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>@project.groupId@</groupId>
|
||||||
|
<artifactId>@project.artifactId@</artifactId>
|
||||||
|
<version>@project.version@</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<includeOptional>true</includeOptional>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>@maven-jar-plugin.version@</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-context</artifactId>
|
||||||
|
<version>@spring-framework.version@</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-api</artifactId>
|
||||||
|
<version>@log4j2.version@</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -14,7 +14,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
package org.test;
|
||||||
* Support for loading and manipulating JAR/WAR files.
|
|
||||||
*/
|
public class SampleApplication {
|
||||||
package org.springframework.boot.loader.jar;
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -47,7 +47,6 @@ import org.springframework.boot.loader.tools.Layouts.Jar;
|
||||||
import org.springframework.boot.loader.tools.Layouts.None;
|
import org.springframework.boot.loader.tools.Layouts.None;
|
||||||
import org.springframework.boot.loader.tools.Layouts.War;
|
import org.springframework.boot.loader.tools.Layouts.War;
|
||||||
import org.springframework.boot.loader.tools.Libraries;
|
import org.springframework.boot.loader.tools.Libraries;
|
||||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
|
||||||
import org.springframework.boot.loader.tools.Packager;
|
import org.springframework.boot.loader.tools.Packager;
|
||||||
import org.springframework.boot.loader.tools.layer.CustomLayers;
|
import org.springframework.boot.loader.tools.layer.CustomLayers;
|
||||||
|
|
||||||
|
@ -114,6 +113,13 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
||||||
@Parameter(defaultValue = "false")
|
@Parameter(defaultValue = "false")
|
||||||
public boolean includeSystemScope;
|
public boolean includeSystemScope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include optional dependencies.
|
||||||
|
* @since 3.5.7
|
||||||
|
*/
|
||||||
|
@Parameter(defaultValue = "false")
|
||||||
|
public boolean includeOptional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Include JAR tools.
|
* Include JAR tools.
|
||||||
* @since 3.3.0
|
* @since 3.3.0
|
||||||
|
@ -142,15 +148,6 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the loader implementation that should be used.
|
|
||||||
* @return the loader implementation or {@code null}
|
|
||||||
* @since 3.2.0
|
|
||||||
*/
|
|
||||||
protected @Nullable LoaderImplementation getLoaderImplementation() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the layout factory that will be used to determine the {@link LayoutType} if
|
* Return the layout factory that will be used to determine the {@link LayoutType} if
|
||||||
* no explicit layout is set.
|
* no explicit layout is set.
|
||||||
|
@ -168,7 +165,6 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
||||||
*/
|
*/
|
||||||
protected <P extends Packager> P getConfiguredPackager(Supplier<P> supplier) {
|
protected <P extends Packager> P getConfiguredPackager(Supplier<P> supplier) {
|
||||||
P packager = supplier.get();
|
P packager = supplier.get();
|
||||||
packager.setLoaderImplementation(getLoaderImplementation());
|
|
||||||
packager.setLayoutFactory(getLayoutFactory());
|
packager.setLayoutFactory(getLayoutFactory());
|
||||||
packager.addMainClassTimeoutWarningListener(new LoggingMainClassTimeoutWarningListener(this::getLog));
|
packager.addMainClassTimeoutWarningListener(new LoggingMainClassTimeoutWarningListener(this::getLog));
|
||||||
packager.setMainClass(this.mainClass);
|
packager.setMainClass(this.mainClass);
|
||||||
|
@ -231,6 +227,9 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
||||||
if (!this.includeSystemScope) {
|
if (!this.includeSystemScope) {
|
||||||
filters.add(new ScopeFilter(null, Artifact.SCOPE_SYSTEM));
|
filters.add(new ScopeFilter(null, Artifact.SCOPE_SYSTEM));
|
||||||
}
|
}
|
||||||
|
if (!this.includeOptional) {
|
||||||
|
filters.add(DependencyFilter.exclude(Artifact::isOptional));
|
||||||
|
}
|
||||||
return filters.toArray(new ArtifactsFilter[0]);
|
return filters.toArray(new ArtifactsFilter[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,6 @@ import org.springframework.boot.loader.tools.EntryWriter;
|
||||||
import org.springframework.boot.loader.tools.ImagePackager;
|
import org.springframework.boot.loader.tools.ImagePackager;
|
||||||
import org.springframework.boot.loader.tools.LayoutFactory;
|
import org.springframework.boot.loader.tools.LayoutFactory;
|
||||||
import org.springframework.boot.loader.tools.Libraries;
|
import org.springframework.boot.loader.tools.Libraries;
|
||||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
@ -208,13 +207,6 @@ public abstract class BuildImageMojo extends AbstractPackagerMojo {
|
||||||
@Parameter
|
@Parameter
|
||||||
private @Nullable LayoutType layout;
|
private @Nullable LayoutType layout;
|
||||||
|
|
||||||
/**
|
|
||||||
* The loader implementation that should be used.
|
|
||||||
* @since 3.2.0
|
|
||||||
*/
|
|
||||||
@Parameter
|
|
||||||
private @Nullable LoaderImplementation loaderImplementation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The layout factory that will be used to create the executable archive if no
|
* The layout factory that will be used to create the executable archive if no
|
||||||
* explicit layout is set. Alternative layouts implementations can be provided by 3rd
|
* explicit layout is set. Alternative layouts implementations can be provided by 3rd
|
||||||
|
@ -238,11 +230,6 @@ public abstract class BuildImageMojo extends AbstractPackagerMojo {
|
||||||
return this.layout;
|
return this.layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected @Nullable LoaderImplementation getLoaderImplementation() {
|
|
||||||
return this.loaderImplementation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the layout factory that will be used to determine the
|
* Return the layout factory that will be used to determine the
|
||||||
* {@link AbstractPackagerMojo.LayoutType} if no explicit layout is set.
|
* {@link AbstractPackagerMojo.LayoutType} if no explicit layout is set.
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
|
|
||||||
package org.springframework.boot.maven;
|
package org.springframework.boot.maven;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.apache.maven.artifact.Artifact;
|
import org.apache.maven.artifact.Artifact;
|
||||||
import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactsFilter;
|
import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactsFilter;
|
||||||
|
@ -81,4 +83,22 @@ public abstract class DependencyFilter extends AbstractArtifactsFilter {
|
||||||
return this.filters;
|
return this.filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new {@link DependencyFilter} the excludes artifacts based on the given
|
||||||
|
* predicate.
|
||||||
|
* @param filter the predicate used to filter the artifacts.
|
||||||
|
* @return a new {@link DependencyFilter} instance
|
||||||
|
* @since 3.5.7
|
||||||
|
*/
|
||||||
|
public static DependencyFilter exclude(Predicate<Artifact> filter) {
|
||||||
|
return new DependencyFilter(Collections.emptyList()) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean filter(Artifact artifact) {
|
||||||
|
return filter.test(artifact);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,8 +34,8 @@ import org.apache.maven.artifact.Artifact;
|
||||||
*/
|
*/
|
||||||
class JarTypeFilter extends DependencyFilter {
|
class JarTypeFilter extends DependencyFilter {
|
||||||
|
|
||||||
private static final Set<String> EXCLUDED_JAR_TYPES = Collections
|
private static final Set<String> EXCLUDED_JAR_TYPES = Collections.unmodifiableSet(
|
||||||
.unmodifiableSet(new HashSet<>(Arrays.asList("annotation-processor", "dependencies-starter")));
|
new HashSet<>(Arrays.asList("annotation-processor", "dependencies-starter", "development-tool")));
|
||||||
|
|
||||||
JarTypeFilter() {
|
JarTypeFilter() {
|
||||||
super(Collections.emptyList());
|
super(Collections.emptyList());
|
||||||
|
|
|
@ -40,7 +40,6 @@ import org.springframework.boot.loader.tools.DefaultLaunchScript;
|
||||||
import org.springframework.boot.loader.tools.LaunchScript;
|
import org.springframework.boot.loader.tools.LaunchScript;
|
||||||
import org.springframework.boot.loader.tools.LayoutFactory;
|
import org.springframework.boot.loader.tools.LayoutFactory;
|
||||||
import org.springframework.boot.loader.tools.Libraries;
|
import org.springframework.boot.loader.tools.Libraries;
|
||||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
|
||||||
import org.springframework.boot.loader.tools.Repackager;
|
import org.springframework.boot.loader.tools.Repackager;
|
||||||
import org.springframework.lang.Contract;
|
import org.springframework.lang.Contract;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
@ -170,13 +169,6 @@ public class RepackageMojo extends AbstractPackagerMojo {
|
||||||
@Parameter(property = "spring-boot.repackage.layout")
|
@Parameter(property = "spring-boot.repackage.layout")
|
||||||
private @Nullable LayoutType layout;
|
private @Nullable LayoutType layout;
|
||||||
|
|
||||||
/**
|
|
||||||
* The loader implementation that should be used.
|
|
||||||
* @since 3.2.0
|
|
||||||
*/
|
|
||||||
@Parameter
|
|
||||||
private @Nullable LoaderImplementation loaderImplementation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The layout factory that will be used to create the executable archive if no
|
* The layout factory that will be used to create the executable archive if no
|
||||||
* explicit layout is set. Alternative layouts implementations can be provided by 3rd
|
* explicit layout is set. Alternative layouts implementations can be provided by 3rd
|
||||||
|
@ -201,11 +193,6 @@ public class RepackageMojo extends AbstractPackagerMojo {
|
||||||
return this.layout;
|
return this.layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected @Nullable LoaderImplementation getLoaderImplementation() {
|
|
||||||
return this.loaderImplementation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the layout factory that will be used to determine the
|
* Return the layout factory that will be used to determine the
|
||||||
* {@link AbstractPackagerMojo.LayoutType} if no explicit layout is set.
|
* {@link AbstractPackagerMojo.LayoutType} if no explicit layout is set.
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.maven;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.maven.artifact.Artifact;
|
||||||
|
import org.apache.maven.artifact.DefaultArtifact;
|
||||||
|
import org.apache.maven.artifact.handler.ArtifactHandler;
|
||||||
|
import org.apache.maven.artifact.handler.DefaultArtifactHandler;
|
||||||
|
import org.apache.maven.artifact.versioning.VersionRange;
|
||||||
|
import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link DependencyFilter}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
class DependencyFilterTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void excludeFiltersBasedOnPredicate() throws ArtifactFilterException {
|
||||||
|
DependencyFilter filter = DependencyFilter.exclude(Artifact::isOptional);
|
||||||
|
ArtifactHandler ah = new DefaultArtifactHandler();
|
||||||
|
VersionRange v = VersionRange.createFromVersion("1.0.0");
|
||||||
|
DefaultArtifact a1 = new DefaultArtifact("com.example", "a1", v, "compile", "jar", null, ah, false);
|
||||||
|
DefaultArtifact a2 = new DefaultArtifact("com.example", "a2", v, "compile", "jar", null, ah, true);
|
||||||
|
DefaultArtifact a3 = new DefaultArtifact("com.example", "a3", v, "compile", "jar", null, ah, false);
|
||||||
|
Set<Artifact> filtered = filter.filter(Set.of(a1, a2, a3));
|
||||||
|
assertThat(filtered).containsExactlyInAnyOrder(a1, a3);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -31,8 +31,8 @@ import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
||||||
import org.springframework.boot.buildpack.platform.build.BuildpackReference;
|
import org.springframework.boot.buildpack.platform.build.BuildpackReference;
|
||||||
import org.springframework.boot.buildpack.platform.build.Cache;
|
import org.springframework.boot.buildpack.platform.build.Cache;
|
||||||
import org.springframework.boot.buildpack.platform.build.PullPolicy;
|
import org.springframework.boot.buildpack.platform.build.PullPolicy;
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||||
|
|
|
@ -60,6 +60,11 @@ class JarTypeFilterTests {
|
||||||
assertThat(new JarTypeFilter().filter(createArtifact("annotation-processor"))).isTrue();
|
assertThat(new JarTypeFilter().filter(createArtifact("annotation-processor"))).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenArtifactHasDevelopmentToolJarTypeThenItIsExcluded() {
|
||||||
|
assertThat(new JarTypeFilter().filter(createArtifact("development-tool"))).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void whenArtifactHasNoManifestFileThenItIsIncluded() {
|
void whenArtifactHasNoManifestFileThenItIsIncluded() {
|
||||||
assertThat(new JarTypeFilter().filter(createArtifactWithNoManifest())).isFalse();
|
assertThat(new JarTypeFilter().filter(createArtifactWithNoManifest())).isFalse();
|
||||||
|
|
|
@ -52,7 +52,7 @@ dependencies {
|
||||||
implementation("commons-codec:commons-codec:${commonsCodecVersion}")
|
implementation("commons-codec:commons-codec:${commonsCodecVersion}")
|
||||||
implementation("de.undercouch.download:de.undercouch.download.gradle.plugin:5.5.0")
|
implementation("de.undercouch.download:de.undercouch.download.gradle.plugin:5.5.0")
|
||||||
implementation("dev.adamko.dokkatoo:dokkatoo-plugin:2.3.1")
|
implementation("dev.adamko.dokkatoo:dokkatoo-plugin:2.3.1")
|
||||||
implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.8")
|
implementation("dev.detekt:detekt-gradle-plugin:2.0.0-alpha.0")
|
||||||
implementation("io.spring.gradle.antora:spring-antora-plugin:0.0.1")
|
implementation("io.spring.gradle.antora:spring-antora-plugin:0.0.1")
|
||||||
implementation("io.spring.javaformat:spring-javaformat-gradle-plugin:${javaFormatVersion}")
|
implementation("io.spring.javaformat:spring-javaformat-gradle-plugin:${javaFormatVersion}")
|
||||||
implementation("io.spring.nohttp:nohttp-gradle:0.0.11")
|
implementation("io.spring.nohttp:nohttp-gradle:0.0.11")
|
||||||
|
@ -133,10 +133,6 @@ gradlePlugin {
|
||||||
id = "org.springframework.boot.integration-test"
|
id = "org.springframework.boot.integration-test"
|
||||||
implementationClass = "org.springframework.boot.build.test.IntegrationTestPlugin"
|
implementationClass = "org.springframework.boot.build.test.IntegrationTestPlugin"
|
||||||
}
|
}
|
||||||
systemTestPlugin {
|
|
||||||
id = "org.springframework.boot.system-test"
|
|
||||||
implementationClass = "org.springframework.boot.build.test.SystemTestPlugin"
|
|
||||||
}
|
|
||||||
mavenPluginPlugin {
|
mavenPluginPlugin {
|
||||||
id = "org.springframework.boot.maven-plugin"
|
id = "org.springframework.boot.maven-plugin"
|
||||||
implementationClass = "org.springframework.boot.build.mavenplugin.MavenPluginPlugin"
|
implementationClass = "org.springframework.boot.build.mavenplugin.MavenPluginPlugin"
|
||||||
|
@ -153,10 +149,22 @@ gradlePlugin {
|
||||||
id = "org.springframework.boot.starter"
|
id = "org.springframework.boot.starter"
|
||||||
implementationClass = "org.springframework.boot.build.starters.StarterPlugin"
|
implementationClass = "org.springframework.boot.build.starters.StarterPlugin"
|
||||||
}
|
}
|
||||||
|
systemTestPlugin {
|
||||||
|
id = "org.springframework.boot.system-test"
|
||||||
|
implementationClass = "org.springframework.boot.build.test.SystemTestPlugin"
|
||||||
|
}
|
||||||
|
testAutoConfigurationPlugin {
|
||||||
|
id = "org.springframework.boot.test-auto-configuration"
|
||||||
|
implementationClass = "org.springframework.boot.build.test.autoconfigure.TestAutoConfigurationPlugin"
|
||||||
|
}
|
||||||
testFailuresPlugin {
|
testFailuresPlugin {
|
||||||
id = "org.springframework.boot.test-failures"
|
id = "org.springframework.boot.test-failures"
|
||||||
implementationClass = "org.springframework.boot.build.testing.TestFailuresPlugin"
|
implementationClass = "org.springframework.boot.build.testing.TestFailuresPlugin"
|
||||||
}
|
}
|
||||||
|
testSlicePlugin {
|
||||||
|
id = "org.springframework.boot.test-slice"
|
||||||
|
implementationClass = "org.springframework.boot.build.test.autoconfigure.TestSlicePlugin"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,9 @@ import java.net.URI;
|
||||||
|
|
||||||
import dev.adamko.dokkatoo.DokkatooExtension;
|
import dev.adamko.dokkatoo.DokkatooExtension;
|
||||||
import dev.adamko.dokkatoo.formats.DokkatooHtmlPlugin;
|
import dev.adamko.dokkatoo.formats.DokkatooHtmlPlugin;
|
||||||
import io.gitlab.arturbosch.detekt.Detekt;
|
import dev.detekt.gradle.Detekt;
|
||||||
import io.gitlab.arturbosch.detekt.DetektPlugin;
|
import dev.detekt.gradle.extensions.DetektExtension;
|
||||||
import io.gitlab.arturbosch.detekt.extensions.DetektExtension;
|
import dev.detekt.gradle.plugin.DetektPlugin;
|
||||||
import org.gradle.api.Project;
|
import org.gradle.api.Project;
|
||||||
import org.gradle.api.tasks.SourceSet;
|
import org.gradle.api.tasks.SourceSet;
|
||||||
import org.gradle.api.tasks.SourceSetContainer;
|
import org.gradle.api.tasks.SourceSetContainer;
|
||||||
|
@ -76,6 +76,7 @@ class KotlinConventions {
|
||||||
|
|
||||||
private void configureDokkatoo(Project project) {
|
private void configureDokkatoo(Project project) {
|
||||||
DokkatooExtension dokkatoo = project.getExtensions().getByType(DokkatooExtension.class);
|
DokkatooExtension dokkatoo = project.getExtensions().getByType(DokkatooExtension.class);
|
||||||
|
dokkatoo.getVersions().getJetbrainsDokka().set("2.1.0-Beta");
|
||||||
dokkatoo.getDokkatooSourceSets().configureEach((sourceSet) -> {
|
dokkatoo.getDokkatooSourceSets().configureEach((sourceSet) -> {
|
||||||
if (SourceSet.MAIN_SOURCE_SET_NAME.equals(sourceSet.getName())) {
|
if (SourceSet.MAIN_SOURCE_SET_NAME.equals(sourceSet.getName())) {
|
||||||
sourceSet.getSourceRoots().setFrom(project.file("src/main/kotlin"));
|
sourceSet.getSourceRoots().setFrom(project.file("src/main/kotlin"));
|
||||||
|
@ -103,7 +104,9 @@ class KotlinConventions {
|
||||||
project.getPlugins().apply(DetektPlugin.class);
|
project.getPlugins().apply(DetektPlugin.class);
|
||||||
DetektExtension detekt = project.getExtensions().getByType(DetektExtension.class);
|
DetektExtension detekt = project.getExtensions().getByType(DetektExtension.class);
|
||||||
detekt.getConfig().setFrom(project.getRootProject().file("config/detekt/config.yml"));
|
detekt.getConfig().setFrom(project.getRootProject().file("config/detekt/config.yml"));
|
||||||
project.getTasks().withType(Detekt.class).configureEach((task) -> task.setJvmTarget(JVM_TARGET.getTarget()));
|
project.getTasks()
|
||||||
|
.withType(Detekt.class)
|
||||||
|
.configureEach((task) -> task.getJvmTarget().set(JVM_TARGET.getTarget()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,13 @@ final class ArchitectureRules {
|
||||||
|
|
||||||
private static final String AUTOCONFIGURATION_ANNOTATION = "org.springframework.boot.autoconfigure.AutoConfiguration";
|
private static final String AUTOCONFIGURATION_ANNOTATION = "org.springframework.boot.autoconfigure.AutoConfiguration";
|
||||||
|
|
||||||
|
private static final String TEST_AUTOCONFIGURATION_ANNOTATION = "org.springframework.boot.test.autoconfigure.TestAutoConfiguration";
|
||||||
|
|
||||||
|
private static final Predicate<JavaPackage> NULL_MARKED_PACKAGE_FILTER = (candidate) -> !List
|
||||||
|
.of("org.springframework.boot.cli.json", "org.springframework.boot.configurationmetadata.json",
|
||||||
|
"org.springframework.boot.configurationprocessor.json")
|
||||||
|
.contains(candidate.getName());
|
||||||
|
|
||||||
private ArchitectureRules() {
|
private ArchitectureRules() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,6 +119,7 @@ final class ArchitectureRules {
|
||||||
rules.add(allConfigurationPropertiesBindingBeanMethodsShouldBeStatic());
|
rules.add(allConfigurationPropertiesBindingBeanMethodsShouldBeStatic());
|
||||||
rules.add(autoConfigurationClassesShouldBePublicAndFinal());
|
rules.add(autoConfigurationClassesShouldBePublicAndFinal());
|
||||||
rules.add(autoConfigurationClassesShouldHaveNoPublicMembers());
|
rules.add(autoConfigurationClassesShouldHaveNoPublicMembers());
|
||||||
|
rules.add(testAutoConfigurationClassesShouldBePackagePrivateAndFinal());
|
||||||
return List.copyOf(rules);
|
return List.copyOf(rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +138,12 @@ final class ArchitectureRules {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ArchRule allPackagesShouldBeFreeOfTangles() {
|
private static ArchRule allPackagesShouldBeFreeOfTangles() {
|
||||||
return SlicesRuleDefinition.slices().matching("(**)").should().beFreeOfCycles();
|
return SlicesRuleDefinition.slices()
|
||||||
|
.matching("(**)")
|
||||||
|
.should()
|
||||||
|
.beFreeOfCycles()
|
||||||
|
.ignoreDependency("org.springframework.boot.env.EnvironmentPostProcessor",
|
||||||
|
"org.springframework.boot.SpringApplication");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ArchRule allBeanPostProcessorBeanMethodsShouldBeStaticAndNotCausePrematureInitialization() {
|
private static ArchRule allBeanPostProcessorBeanMethodsShouldBeStaticAndNotCausePrematureInitialization() {
|
||||||
|
@ -250,7 +263,9 @@ final class ArchitectureRules {
|
||||||
}
|
}
|
||||||
|
|
||||||
static ArchRule packagesShouldBeAnnotatedWithNullMarked() {
|
static ArchRule packagesShouldBeAnnotatedWithNullMarked() {
|
||||||
return ArchRuleDefinition.all(packages()).should(beAnnotatedWithNullMarked()).allowEmptyShould(true);
|
return ArchRuleDefinition.all(packages(NULL_MARKED_PACKAGE_FILTER))
|
||||||
|
.should(beAnnotatedWithNullMarked())
|
||||||
|
.allowEmptyShould(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ArchCondition<? super JavaMethod> notSpecifyOnlyATypeThatIsTheSameAsTheMethodReturnType() {
|
private static ArchCondition<? super JavaMethod> notSpecifyOnlyATypeThatIsTheSameAsTheMethodReturnType() {
|
||||||
|
@ -353,8 +368,7 @@ final class ArchitectureRules {
|
||||||
|
|
||||||
private static ArchRule autoConfigurationClassesShouldBePublicAndFinal() {
|
private static ArchRule autoConfigurationClassesShouldBePublicAndFinal() {
|
||||||
return ArchRuleDefinition.classes()
|
return ArchRuleDefinition.classes()
|
||||||
.that()
|
.that(areRegularAutoConfiguration())
|
||||||
.areAnnotatedWith(AUTOCONFIGURATION_ANNOTATION)
|
|
||||||
.should()
|
.should()
|
||||||
.bePublic()
|
.bePublic()
|
||||||
.andShould()
|
.andShould()
|
||||||
|
@ -365,8 +379,7 @@ final class ArchitectureRules {
|
||||||
private static ArchRule autoConfigurationClassesShouldHaveNoPublicMembers() {
|
private static ArchRule autoConfigurationClassesShouldHaveNoPublicMembers() {
|
||||||
return ArchRuleDefinition.members()
|
return ArchRuleDefinition.members()
|
||||||
.that()
|
.that()
|
||||||
.areDeclaredInClassesThat()
|
.areDeclaredInClassesThat(areRegularAutoConfiguration())
|
||||||
.areAnnotatedWith(AUTOCONFIGURATION_ANNOTATION)
|
|
||||||
.and(areNotDefaultConstructors())
|
.and(areNotDefaultConstructors())
|
||||||
.and(areNotConstants())
|
.and(areNotConstants())
|
||||||
.and(dontOverridePublicMethods())
|
.and(dontOverridePublicMethods())
|
||||||
|
@ -375,6 +388,24 @@ final class ArchitectureRules {
|
||||||
.allowEmptyShould(true);
|
.allowEmptyShould(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ArchRule testAutoConfigurationClassesShouldBePackagePrivateAndFinal() {
|
||||||
|
return ArchRuleDefinition.classes()
|
||||||
|
.that()
|
||||||
|
.areAnnotatedWith(TEST_AUTOCONFIGURATION_ANNOTATION)
|
||||||
|
.should()
|
||||||
|
.bePackagePrivate()
|
||||||
|
.andShould()
|
||||||
|
.haveModifier(JavaModifier.FINAL)
|
||||||
|
.allowEmptyShould(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DescribedPredicate<JavaClass> areRegularAutoConfiguration() {
|
||||||
|
return DescribedPredicate.describe("Regular @AutoConfiguration",
|
||||||
|
(javaClass) -> javaClass.isMetaAnnotatedWith(AUTOCONFIGURATION_ANNOTATION)
|
||||||
|
&& !javaClass.isMetaAnnotatedWith(TEST_AUTOCONFIGURATION_ANNOTATION)
|
||||||
|
&& !javaClass.isAnnotation());
|
||||||
|
}
|
||||||
|
|
||||||
private static DescribedPredicate<? super JavaMember> dontOverridePublicMethods() {
|
private static DescribedPredicate<? super JavaMember> dontOverridePublicMethods() {
|
||||||
OverridesPublicMethod<JavaMember> predicate = new OverridesPublicMethod<>();
|
OverridesPublicMethod<JavaMember> predicate = new OverridesPublicMethod<>();
|
||||||
return DescribedPredicate.describe("don't override public methods", (member) -> !predicate.test(member));
|
return DescribedPredicate.describe("don't override public methods", (member) -> !predicate.test(member));
|
||||||
|
@ -480,11 +511,11 @@ final class ArchitectureRules {
|
||||||
return string + " should be used instead";
|
return string + " should be used instead";
|
||||||
}
|
}
|
||||||
|
|
||||||
static ClassesTransformer<JavaPackage> packages() {
|
static ClassesTransformer<JavaPackage> packages(Predicate<JavaPackage> filter) {
|
||||||
return new AbstractClassesTransformer<>("packages") {
|
return new AbstractClassesTransformer<>("packages") {
|
||||||
@Override
|
@Override
|
||||||
public Iterable<JavaPackage> doTransform(JavaClasses collection) {
|
public Iterable<JavaPackage> doTransform(JavaClasses collection) {
|
||||||
return collection.stream().map(JavaClass::getPackage).collect(Collectors.toSet());
|
return collection.stream().map(JavaClass::getPackage).filter(filter).collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.springframework.boot.build.autoconfigure;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -54,8 +55,8 @@ public record AutoConfigurationClass(String name, List<String> before, List<Stri
|
||||||
attributes.getOrDefault("afterName", Collections.emptyList()));
|
attributes.getOrDefault("afterName", Collections.emptyList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static AutoConfigurationClass of(File classFile) {
|
public static AutoConfigurationClass of(InputStream input) {
|
||||||
try (FileInputStream input = new FileInputStream(classFile)) {
|
try {
|
||||||
ClassReader classReader = new ClassReader(input);
|
ClassReader classReader = new ClassReader(input);
|
||||||
AutoConfigurationClassVisitor visitor = new AutoConfigurationClassVisitor();
|
AutoConfigurationClassVisitor visitor = new AutoConfigurationClassVisitor();
|
||||||
classReader.accept(visitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
|
classReader.accept(visitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
|
||||||
|
@ -66,6 +67,15 @@ public record AutoConfigurationClass(String name, List<String> before, List<Stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AutoConfigurationClass of(File classFile) {
|
||||||
|
try (InputStream input = new FileInputStream(classFile)) {
|
||||||
|
return of(input);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new UncheckedIOException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class AutoConfigurationClassVisitor extends ClassVisitor {
|
private static final class AutoConfigurationClassVisitor extends ClassVisitor {
|
||||||
|
|
||||||
private AutoConfigurationClass autoConfigurationClass;
|
private AutoConfigurationClass autoConfigurationClass;
|
||||||
|
|
|
@ -38,7 +38,10 @@ import org.gradle.api.tasks.SkipWhenEmpty;
|
||||||
*/
|
*/
|
||||||
public abstract class AutoConfigurationImportsTask extends DefaultTask {
|
public abstract class AutoConfigurationImportsTask extends DefaultTask {
|
||||||
|
|
||||||
static final String IMPORTS_FILE = "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports";
|
/**
|
||||||
|
* The path of the {@code AutoConfiguration.imports} file.
|
||||||
|
*/
|
||||||
|
public static final String IMPORTS_FILE = "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports";
|
||||||
|
|
||||||
private FileCollection sourceFiles = getProject().getObjects().fileCollection();
|
private FileCollection sourceFiles = getProject().getObjects().fileCollection();
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class ConfigurationPropertiesPlugin implements Plugin<Project> {
|
||||||
public static final String CHECK_ADDITIONAL_SPRING_CONFIGURATION_METADATA_TASK_NAME = "checkAdditionalSpringConfigurationMetadata";
|
public static final String CHECK_ADDITIONAL_SPRING_CONFIGURATION_METADATA_TASK_NAME = "checkAdditionalSpringConfigurationMetadata";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the {@link CheckAdditionalSpringConfigurationMetadata} task.
|
* Name of the {@link CheckSpringConfigurationMetadata} task.
|
||||||
*/
|
*/
|
||||||
public static final String CHECK_SPRING_CONFIGURATION_METADATA_TASK_NAME = "checkSpringConfigurationMetadata";
|
public static final String CHECK_SPRING_CONFIGURATION_METADATA_TASK_NAME = "checkSpringConfigurationMetadata";
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class OptionalDependenciesPlugin implements Plugin<Project> {
|
||||||
@Override
|
@Override
|
||||||
public void apply(Project project) {
|
public void apply(Project project) {
|
||||||
Configuration optional = project.getConfigurations().create("optional");
|
Configuration optional = project.getConfigurations().create("optional");
|
||||||
optional.setCanBeConsumed(false);
|
optional.setCanBeConsumed(true);
|
||||||
optional.setCanBeResolved(false);
|
optional.setCanBeResolved(false);
|
||||||
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
||||||
SourceSetContainer sourceSets = project.getExtensions()
|
SourceSetContainer sourceSets = project.getExtensions()
|
||||||
|
|
|
@ -0,0 +1,233 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.build.test.autoconfigure;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.file.DirectoryProperty;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.api.file.FileTree;
|
||||||
|
import org.gradle.api.tasks.Classpath;
|
||||||
|
import org.gradle.api.tasks.InputFiles;
|
||||||
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
|
import org.gradle.api.tasks.PathSensitive;
|
||||||
|
import org.gradle.api.tasks.PathSensitivity;
|
||||||
|
import org.gradle.api.tasks.SkipWhenEmpty;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
import org.gradle.api.tasks.VerificationException;
|
||||||
|
import org.gradle.language.base.plugins.LifecycleBasePlugin;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.autoconfigure.AutoConfigurationClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task to check the contents of a project's
|
||||||
|
* {@code META-INF/spring/*.AutoConfigure*.imports} files.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public abstract class CheckAutoConfigureImports extends DefaultTask {
|
||||||
|
|
||||||
|
private FileCollection sourceFiles = getProject().getObjects().fileCollection();
|
||||||
|
|
||||||
|
private FileCollection classpath = getProject().getObjects().fileCollection();
|
||||||
|
|
||||||
|
public CheckAutoConfigureImports() {
|
||||||
|
getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName()));
|
||||||
|
setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
@SkipWhenEmpty
|
||||||
|
@PathSensitive(PathSensitivity.RELATIVE)
|
||||||
|
public FileTree getSource() {
|
||||||
|
return this.sourceFiles.getAsFileTree()
|
||||||
|
.matching((filter) -> filter.include("META-INF/spring/*.AutoConfigure*.imports"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSource(Object source) {
|
||||||
|
this.sourceFiles = getProject().getObjects().fileCollection().from(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Classpath
|
||||||
|
public FileCollection getClasspath() {
|
||||||
|
return this.classpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClasspath(Object classpath) {
|
||||||
|
this.classpath = getProject().getObjects().fileCollection().from(classpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputDirectory
|
||||||
|
public abstract DirectoryProperty getOutputDirectory();
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void execute() {
|
||||||
|
Map<String, List<String>> allProblems = new TreeMap<>();
|
||||||
|
for (AutoConfigureImports autoConfigureImports : loadImports()) {
|
||||||
|
List<String> problems = new ArrayList<>();
|
||||||
|
if (!find(autoConfigureImports.annotationName)) {
|
||||||
|
problems.add("Annotation '%s' was not found".formatted(autoConfigureImports.annotationName));
|
||||||
|
}
|
||||||
|
for (String imported : autoConfigureImports.imports) {
|
||||||
|
String importedClassName = imported;
|
||||||
|
if (importedClassName.startsWith("optional:")) {
|
||||||
|
importedClassName = importedClassName.substring("optional:".length());
|
||||||
|
}
|
||||||
|
boolean found = find(importedClassName, (input) -> {
|
||||||
|
if (!correctlyAnnotated(input)) {
|
||||||
|
problems.add("Imported auto-configuration '%s' is not annotated with @AutoConfiguration"
|
||||||
|
.formatted(imported));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!found) {
|
||||||
|
problems.add("Imported auto-configuration '%s' was not found".formatted(importedClassName));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
List<String> sortedValues = new ArrayList<>(autoConfigureImports.imports);
|
||||||
|
Collections.sort(sortedValues, (i1, i2) -> {
|
||||||
|
boolean imported1 = i1.startsWith("optional:");
|
||||||
|
boolean imported2 = i2.startsWith("optional:");
|
||||||
|
int comparison = Boolean.compare(imported1, imported2);
|
||||||
|
if (comparison != 0) {
|
||||||
|
return comparison;
|
||||||
|
}
|
||||||
|
return i1.compareTo(i2);
|
||||||
|
});
|
||||||
|
if (!sortedValues.equals(autoConfigureImports.imports)) {
|
||||||
|
File sortedOutputFile = getOutputDirectory().file("sorted-" + autoConfigureImports.fileName)
|
||||||
|
.get()
|
||||||
|
.getAsFile();
|
||||||
|
writeString(sortedOutputFile, sortedValues.stream().collect(Collectors.joining(System.lineSeparator()))
|
||||||
|
+ System.lineSeparator());
|
||||||
|
problems.add(
|
||||||
|
"Entries should be required then optional, each sorted alphabetically (expected content written to '%s')"
|
||||||
|
.formatted(sortedOutputFile.getAbsolutePath()));
|
||||||
|
}
|
||||||
|
if (!problems.isEmpty()) {
|
||||||
|
allProblems.computeIfAbsent(autoConfigureImports.fileName, (unused) -> new ArrayList<>())
|
||||||
|
.addAll(problems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile();
|
||||||
|
writeReport(allProblems, outputFile);
|
||||||
|
if (!allProblems.isEmpty()) {
|
||||||
|
throw new VerificationException(
|
||||||
|
"AutoConfigure….imports checks failed. See '%s' for details".formatted(outputFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AutoConfigureImports> loadImports() {
|
||||||
|
return getSource().getFiles().stream().map((file) -> {
|
||||||
|
String fileName = file.getName();
|
||||||
|
String annotationName = fileName.substring(0, fileName.length() - ".imports".length());
|
||||||
|
return new AutoConfigureImports(annotationName, loadImports(file), fileName);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> loadImports(File importsFile) {
|
||||||
|
try {
|
||||||
|
return Files.readAllLines(importsFile.toPath());
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new UncheckedIOException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean find(String className) {
|
||||||
|
return find(className, (input) -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean find(String className, Consumer<InputStream> handler) {
|
||||||
|
for (File root : this.classpath.getFiles()) {
|
||||||
|
String classFilePath = className.replace(".", "/") + ".class";
|
||||||
|
if (root.isDirectory()) {
|
||||||
|
File classFile = new File(root, classFilePath);
|
||||||
|
if (classFile.isFile()) {
|
||||||
|
try (InputStream input = new FileInputStream(classFile)) {
|
||||||
|
handler.accept(input);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new UncheckedIOException(ex);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try (JarFile jar = new JarFile(root)) {
|
||||||
|
ZipEntry entry = jar.getEntry(classFilePath);
|
||||||
|
if (entry != null) {
|
||||||
|
try (InputStream input = jar.getInputStream(entry)) {
|
||||||
|
handler.accept(input);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new UncheckedIOException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean correctlyAnnotated(InputStream classFile) {
|
||||||
|
return AutoConfigurationClass.of(classFile) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeReport(Map<String, List<String>> allProblems, File outputFile) {
|
||||||
|
outputFile.getParentFile().mkdirs();
|
||||||
|
StringBuilder report = new StringBuilder();
|
||||||
|
if (!allProblems.isEmpty()) {
|
||||||
|
allProblems.forEach((fileName, problems) -> {
|
||||||
|
report.append("Found problems in '%s':%n".formatted(fileName));
|
||||||
|
problems.forEach((problem) -> report.append(" - %s%n".formatted(problem)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
writeString(outputFile, report.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeString(File file, String content) {
|
||||||
|
try {
|
||||||
|
Files.writeString(file.toPath(), content);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new UncheckedIOException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record AutoConfigureImports(String annotationName, List<String> imports, String fileName) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,17 +17,14 @@
|
||||||
package org.springframework.boot.build.test.autoconfigure;
|
package org.springframework.boot.build.test.autoconfigure;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.Reader;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Enumeration;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.TreeMap;
|
||||||
import java.util.SortedSet;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
|
|
||||||
import org.gradle.api.DefaultTask;
|
import org.gradle.api.DefaultTask;
|
||||||
import org.gradle.api.Task;
|
import org.gradle.api.Task;
|
||||||
|
@ -38,9 +35,9 @@ import org.gradle.api.tasks.OutputFile;
|
||||||
import org.gradle.api.tasks.PathSensitive;
|
import org.gradle.api.tasks.PathSensitive;
|
||||||
import org.gradle.api.tasks.PathSensitivity;
|
import org.gradle.api.tasks.PathSensitivity;
|
||||||
import org.gradle.api.tasks.TaskAction;
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
import tools.jackson.databind.json.JsonMapper;
|
||||||
|
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.boot.build.test.autoconfigure.TestSliceMetadata.TestSlice;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Task} used to document test slices.
|
* {@link Task} used to document test slices.
|
||||||
|
@ -49,16 +46,16 @@ import org.springframework.util.StringUtils;
|
||||||
*/
|
*/
|
||||||
public abstract class DocumentTestSlices extends DefaultTask {
|
public abstract class DocumentTestSlices extends DefaultTask {
|
||||||
|
|
||||||
private FileCollection testSlices;
|
private FileCollection testSliceMetadata;
|
||||||
|
|
||||||
@InputFiles
|
@InputFiles
|
||||||
@PathSensitive(PathSensitivity.RELATIVE)
|
@PathSensitive(PathSensitivity.RELATIVE)
|
||||||
public FileCollection getTestSlices() {
|
public FileCollection getTestSlices() {
|
||||||
return this.testSlices;
|
return this.testSliceMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTestSlices(FileCollection testSlices) {
|
public void setTestSlices(FileCollection testSlices) {
|
||||||
this.testSlices = testSlices;
|
this.testSliceMetadata = testSlices;
|
||||||
}
|
}
|
||||||
|
|
||||||
@OutputFile
|
@OutputFile
|
||||||
|
@ -66,61 +63,42 @@ public abstract class DocumentTestSlices extends DefaultTask {
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
void documentTestSlices() throws IOException {
|
void documentTestSlices() throws IOException {
|
||||||
Set<TestSlice> testSlices = readTestSlices();
|
Map<String, List<TestSlice>> testSlices = readTestSlices();
|
||||||
writeTable(testSlices);
|
writeTable(testSlices);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
private Map<String, List<TestSlice>> readTestSlices() {
|
||||||
private Set<TestSlice> readTestSlices() throws IOException {
|
Map<String, List<TestSlice>> testSlices = new TreeMap<>();
|
||||||
Set<TestSlice> testSlices = new TreeSet<>();
|
for (File metadataFile : this.testSliceMetadata) {
|
||||||
for (File metadataFile : this.testSlices) {
|
JsonMapper mapper = JsonMapper.builder().build();
|
||||||
Properties metadata = new Properties();
|
TestSliceMetadata metadata = mapper.readValue(metadataFile, TestSliceMetadata.class);
|
||||||
try (Reader reader = new FileReader(metadataFile)) {
|
List<TestSlice> slices = new ArrayList<>(metadata.testSlices());
|
||||||
metadata.load(reader);
|
Collections.sort(slices, (s1, s2) -> s1.annotation().compareTo(s2.annotation()));
|
||||||
}
|
testSlices.put(metadata.module(), slices);
|
||||||
for (String name : Collections.list((Enumeration<String>) metadata.propertyNames())) {
|
|
||||||
testSlices.add(new TestSlice(name,
|
|
||||||
new TreeSet<>(StringUtils.commaDelimitedListToSet(metadata.getProperty(name)))));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return testSlices;
|
return testSlices;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeTable(Set<TestSlice> testSlices) throws IOException {
|
private void writeTable(Map<String, List<TestSlice>> testSlicesByModule) throws IOException {
|
||||||
File outputFile = getOutputFile().getAsFile().get();
|
File outputFile = getOutputFile().getAsFile().get();
|
||||||
outputFile.getParentFile().mkdirs();
|
outputFile.getParentFile().mkdirs();
|
||||||
try (PrintWriter writer = new PrintWriter(new FileWriter(outputFile))) {
|
try (PrintWriter writer = new PrintWriter(new FileWriter(outputFile))) {
|
||||||
writer.println("[cols=\"d,a\"]");
|
writer.println("[cols=\"d,d,a\"]");
|
||||||
writer.println("|===");
|
writer.println("|===");
|
||||||
writer.println("| Test slice | Imported auto-configuration");
|
writer.println("|Module | Test slice | Imported auto-configuration");
|
||||||
for (TestSlice testSlice : testSlices) {
|
testSlicesByModule.forEach((module, testSlices) -> {
|
||||||
|
testSlices.forEach((testSlice) -> {
|
||||||
writer.println();
|
writer.println();
|
||||||
writer.printf("| `@%s`%n", testSlice.className);
|
writer.printf("| `%s`%n", module);
|
||||||
|
writer.printf("| javadoc:%s[format=annotation]%n", testSlice.annotation());
|
||||||
writer.println("| ");
|
writer.println("| ");
|
||||||
for (String importedAutoConfiguration : testSlice.importedAutoConfigurations) {
|
for (String importedAutoConfiguration : testSlice.importedAutoConfigurations()) {
|
||||||
writer.printf("`%s`%n", importedAutoConfiguration);
|
writer.printf("`%s`%n", importedAutoConfiguration);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
});
|
||||||
writer.println("|===");
|
writer.println("|===");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class TestSlice implements Comparable<TestSlice> {
|
|
||||||
|
|
||||||
private final String className;
|
|
||||||
|
|
||||||
private final SortedSet<String> importedAutoConfigurations;
|
|
||||||
|
|
||||||
private TestSlice(String className, SortedSet<String> importedAutoConfigurations) {
|
|
||||||
this.className = ClassUtils.getShortName(className);
|
|
||||||
this.importedAutoConfigurations = importedAutoConfigurations;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(TestSlice other) {
|
|
||||||
return this.className.compareTo(other.className);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,238 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.build.test.autoconfigure;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.api.file.RegularFileProperty;
|
||||||
|
import org.gradle.api.model.ObjectFactory;
|
||||||
|
import org.gradle.api.tasks.Classpath;
|
||||||
|
import org.gradle.api.tasks.InputFiles;
|
||||||
|
import org.gradle.api.tasks.OutputFile;
|
||||||
|
import org.gradle.api.tasks.PathSensitive;
|
||||||
|
import org.gradle.api.tasks.PathSensitivity;
|
||||||
|
import org.gradle.api.tasks.SourceSet;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.test.autoconfigure.TestSliceMetadata.TestSlice;
|
||||||
|
import org.springframework.core.io.FileSystemResource;
|
||||||
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
|
import org.springframework.core.type.classreading.MetadataReader;
|
||||||
|
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||||
|
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Task} for generating metadata describing a project's test slices.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public abstract class GenerateTestSliceMetadata extends DefaultTask {
|
||||||
|
|
||||||
|
private final ObjectFactory objectFactory;
|
||||||
|
|
||||||
|
private FileCollection classpath;
|
||||||
|
|
||||||
|
private FileCollection importsFiles;
|
||||||
|
|
||||||
|
private FileCollection classesDirs;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public GenerateTestSliceMetadata(ObjectFactory objectFactory) {
|
||||||
|
this.objectFactory = objectFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSourceSet(SourceSet sourceSet) {
|
||||||
|
this.classpath = sourceSet.getRuntimeClasspath();
|
||||||
|
this.importsFiles = this.objectFactory.fileTree()
|
||||||
|
.from(new File(sourceSet.getOutput().getResourcesDir(), "META-INF/spring"));
|
||||||
|
this.importsFiles.filter((file) -> file.getName().endsWith(".imports"));
|
||||||
|
getSpringFactories().set(new File(sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories"));
|
||||||
|
this.classesDirs = sourceSet.getOutput().getClassesDirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputFile
|
||||||
|
public abstract RegularFileProperty getOutputFile();
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
@PathSensitive(PathSensitivity.RELATIVE)
|
||||||
|
abstract RegularFileProperty getSpringFactories();
|
||||||
|
|
||||||
|
@Classpath
|
||||||
|
FileCollection getClasspath() {
|
||||||
|
return this.classpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
@PathSensitive(PathSensitivity.RELATIVE)
|
||||||
|
FileCollection getImportFiles() {
|
||||||
|
return this.importsFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Classpath
|
||||||
|
FileCollection getClassesDirs() {
|
||||||
|
return this.classesDirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void generateTestSliceMetadata() throws IOException {
|
||||||
|
TestSliceMetadata metadata = readTestSlices();
|
||||||
|
File outputFile = getOutputFile().getAsFile().get();
|
||||||
|
outputFile.getParentFile().mkdirs();
|
||||||
|
metadata.writeTo(outputFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TestSliceMetadata readTestSlices() throws IOException {
|
||||||
|
List<TestSlice> testSlices = new ArrayList<>();
|
||||||
|
try (URLClassLoader classLoader = new URLClassLoader(
|
||||||
|
StreamSupport.stream(this.classpath.spliterator(), false).map(this::toURL).toArray(URL[]::new))) {
|
||||||
|
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(classLoader);
|
||||||
|
Properties springFactories = readSpringFactories(getSpringFactories().getAsFile().getOrNull());
|
||||||
|
readImportsFiles(springFactories, this.importsFiles);
|
||||||
|
for (File classesDir : this.classesDirs) {
|
||||||
|
testSlices.addAll(readTestSlices(classesDir, metadataReaderFactory, springFactories));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new TestSliceMetadata(getProject().getName(), testSlices);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the given imports files and puts them in springFactories. The key is the file
|
||||||
|
* name, the value is the file contents, split by line, delimited with a comma. This
|
||||||
|
* is done to mimic the spring.factories structure.
|
||||||
|
* @param springFactories spring.factories parsed as properties
|
||||||
|
* @param importsFiles the imports files to read
|
||||||
|
*/
|
||||||
|
private void readImportsFiles(Properties springFactories, FileCollection importsFiles) {
|
||||||
|
for (File file : importsFiles.getFiles()) {
|
||||||
|
try {
|
||||||
|
List<String> lines = removeComments(Files.readAllLines(file.toPath()));
|
||||||
|
String fileNameWithoutExtension = file.getName()
|
||||||
|
.substring(0, file.getName().length() - ".imports".length());
|
||||||
|
springFactories.setProperty(fileNameWithoutExtension,
|
||||||
|
StringUtils.collectionToCommaDelimitedString(lines));
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new UncheckedIOException("Failed to read file " + file, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> removeComments(List<String> lines) {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
for (String line : lines) {
|
||||||
|
int commentIndex = line.indexOf('#');
|
||||||
|
if (commentIndex > -1) {
|
||||||
|
line = line.substring(0, commentIndex);
|
||||||
|
}
|
||||||
|
line = line.trim();
|
||||||
|
if (!line.isEmpty()) {
|
||||||
|
result.add(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private URL toURL(File file) {
|
||||||
|
try {
|
||||||
|
return file.toURI().toURL();
|
||||||
|
}
|
||||||
|
catch (MalformedURLException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Properties readSpringFactories(File file) throws IOException {
|
||||||
|
Properties springFactories = new Properties();
|
||||||
|
if (file.isFile()) {
|
||||||
|
try (Reader in = new FileReader(file)) {
|
||||||
|
springFactories.load(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return springFactories;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TestSlice> readTestSlices(File classesDir, MetadataReaderFactory metadataReaderFactory,
|
||||||
|
Properties springFactories) throws IOException {
|
||||||
|
try (Stream<Path> classes = Files.walk(classesDir.toPath())) {
|
||||||
|
return classes.filter((path) -> path.toString().endsWith("Test.class"))
|
||||||
|
.map((path) -> getMetadataReader(path, metadataReaderFactory))
|
||||||
|
.filter((metadataReader) -> metadataReader.getClassMetadata().isAnnotation())
|
||||||
|
.map((metadataReader) -> readTestSlice(metadataReader, springFactories))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private MetadataReader getMetadataReader(Path path, MetadataReaderFactory metadataReaderFactory) {
|
||||||
|
try {
|
||||||
|
return metadataReaderFactory.getMetadataReader(new FileSystemResource(path));
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TestSlice readTestSlice(MetadataReader metadataReader, Properties springFactories) {
|
||||||
|
String annotationName = metadataReader.getClassMetadata().getClassName();
|
||||||
|
List<String> importedAutoConfiguration = getImportedAutoConfiguration(springFactories,
|
||||||
|
metadataReader.getAnnotationMetadata());
|
||||||
|
return new TestSlice(annotationName, importedAutoConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getImportedAutoConfiguration(Properties springFactories,
|
||||||
|
AnnotationMetadata annotationMetadata) {
|
||||||
|
Stream<String> importers = findMetaImporters(annotationMetadata);
|
||||||
|
if (annotationMetadata.isAnnotated("org.springframework.boot.autoconfigure.ImportAutoConfiguration")) {
|
||||||
|
importers = Stream.concat(importers, Stream.of(annotationMetadata.getClassName()));
|
||||||
|
}
|
||||||
|
return importers
|
||||||
|
.flatMap((importer) -> StringUtils.commaDelimitedListToSet(springFactories.getProperty(importer)).stream())
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<String> findMetaImporters(AnnotationMetadata annotationMetadata) {
|
||||||
|
return annotationMetadata.getAnnotationTypes()
|
||||||
|
.stream()
|
||||||
|
.filter((annotationType) -> isAutoConfigurationImporter(annotationType, annotationMetadata));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAutoConfigurationImporter(String annotationType, AnnotationMetadata metadata) {
|
||||||
|
return metadata.getMetaAnnotationTypes(annotationType)
|
||||||
|
.contains("org.springframework.boot.autoconfigure.ImportAutoConfiguration");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.build.test.autoconfigure;
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.file.ConfigurableFileCollection;
|
||||||
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
|
import org.gradle.api.plugins.JavaPluginExtension;
|
||||||
|
import org.gradle.api.tasks.SourceSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Plugin} for projects that define test auto-configuration. When the
|
||||||
|
* {@link JavaPlugin} is applied it:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Add checks to ensure AutoConfigure*.import files and related annotations are
|
||||||
|
* correct</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class TestAutoConfigurationPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project target) {
|
||||||
|
target.getPlugins().withType(JavaPlugin.class, (plugin) -> {
|
||||||
|
target.getTasks().register("checkAutoConfigureImports", CheckAutoConfigureImports.class, (task) -> {
|
||||||
|
SourceSet mainSourceSet = target.getExtensions()
|
||||||
|
.getByType(JavaPluginExtension.class)
|
||||||
|
.getSourceSets()
|
||||||
|
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||||
|
task.setSource(mainSourceSet.getResources());
|
||||||
|
ConfigurableFileCollection classpath = target.files(mainSourceSet.getRuntimeClasspath(),
|
||||||
|
target.getConfigurations().getByName(mainSourceSet.getRuntimeClasspathConfigurationName()));
|
||||||
|
task.setClasspath(classpath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,229 +17,30 @@
|
||||||
package org.springframework.boot.build.test.autoconfigure;
|
package org.springframework.boot.build.test.autoconfigure;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.UncheckedIOException;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.SortedSet;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import java.util.stream.StreamSupport;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import tools.jackson.databind.SerializationFeature;
|
||||||
|
import tools.jackson.databind.json.JsonMapper;
|
||||||
import org.gradle.api.DefaultTask;
|
|
||||||
import org.gradle.api.Task;
|
|
||||||
import org.gradle.api.artifacts.Configuration;
|
|
||||||
import org.gradle.api.file.FileCollection;
|
|
||||||
import org.gradle.api.file.RegularFileProperty;
|
|
||||||
import org.gradle.api.model.ObjectFactory;
|
|
||||||
import org.gradle.api.tasks.Classpath;
|
|
||||||
import org.gradle.api.tasks.InputFile;
|
|
||||||
import org.gradle.api.tasks.InputFiles;
|
|
||||||
import org.gradle.api.tasks.OutputFile;
|
|
||||||
import org.gradle.api.tasks.PathSensitive;
|
|
||||||
import org.gradle.api.tasks.PathSensitivity;
|
|
||||||
import org.gradle.api.tasks.SourceSet;
|
|
||||||
import org.gradle.api.tasks.TaskAction;
|
|
||||||
|
|
||||||
import org.springframework.core.CollectionFactory;
|
|
||||||
import org.springframework.core.io.FileSystemResource;
|
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
|
||||||
import org.springframework.core.type.classreading.MetadataReader;
|
|
||||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
|
||||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link Task} for generating metadata describing a project's test slices.
|
* Metadata describing a module's test slices.
|
||||||
*
|
*
|
||||||
|
* @param module the module's name
|
||||||
|
* @param testSlices the module's test slices
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
*/
|
*/
|
||||||
public abstract class TestSliceMetadata extends DefaultTask {
|
record TestSliceMetadata(String module, List<TestSlice> testSlices) {
|
||||||
|
|
||||||
private final ObjectFactory objectFactory;
|
static TestSliceMetadata readFrom(File file) {
|
||||||
|
return JsonMapper.builder().build().readValue(file, TestSliceMetadata.class);
|
||||||
private FileCollection classpath;
|
|
||||||
|
|
||||||
private FileCollection importsFiles;
|
|
||||||
|
|
||||||
private FileCollection classesDirs;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public TestSliceMetadata(ObjectFactory objectFactory) {
|
|
||||||
this.objectFactory = objectFactory;
|
|
||||||
Configuration testSliceMetadata = getProject().getConfigurations().maybeCreate("testSliceMetadata");
|
|
||||||
getProject().afterEvaluate((evaluated) -> evaluated.getArtifacts()
|
|
||||||
.add(testSliceMetadata.getName(), getOutputFile(), (artifact) -> artifact.builtBy(this)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSourceSet(SourceSet sourceSet) {
|
void writeTo(File file) {
|
||||||
this.classpath = sourceSet.getRuntimeClasspath();
|
JsonMapper.builder().enable(SerializationFeature.INDENT_OUTPUT).build().writeValue(file, this);
|
||||||
this.importsFiles = this.objectFactory.fileTree()
|
|
||||||
.from(new File(sourceSet.getOutput().getResourcesDir(), "META-INF/spring"));
|
|
||||||
this.importsFiles.filter((file) -> file.getName().endsWith(".imports"));
|
|
||||||
getSpringFactories().set(new File(sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories"));
|
|
||||||
this.classesDirs = sourceSet.getOutput().getClassesDirs();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OutputFile
|
record TestSlice(String annotation, List<String> importedAutoConfigurations) {
|
||||||
public abstract RegularFileProperty getOutputFile();
|
|
||||||
|
|
||||||
@InputFile
|
|
||||||
@PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
abstract RegularFileProperty getSpringFactories();
|
|
||||||
|
|
||||||
@Classpath
|
|
||||||
FileCollection getClasspath() {
|
|
||||||
return this.classpath;
|
|
||||||
}
|
|
||||||
|
|
||||||
@InputFiles
|
|
||||||
@PathSensitive(PathSensitivity.RELATIVE)
|
|
||||||
FileCollection getImportFiles() {
|
|
||||||
return this.importsFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Classpath
|
|
||||||
FileCollection getClassesDirs() {
|
|
||||||
return this.classesDirs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@TaskAction
|
|
||||||
void documentTestSlices() throws IOException {
|
|
||||||
Properties testSlices = readTestSlices();
|
|
||||||
File outputFile = getOutputFile().getAsFile().get();
|
|
||||||
outputFile.getParentFile().mkdirs();
|
|
||||||
try (FileWriter writer = new FileWriter(outputFile)) {
|
|
||||||
testSlices.store(writer, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Properties readTestSlices() throws IOException {
|
|
||||||
Properties testSlices = CollectionFactory.createSortedProperties(true);
|
|
||||||
try (URLClassLoader classLoader = new URLClassLoader(
|
|
||||||
StreamSupport.stream(this.classpath.spliterator(), false).map(this::toURL).toArray(URL[]::new))) {
|
|
||||||
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(classLoader);
|
|
||||||
Properties springFactories = readSpringFactories(getSpringFactories().getAsFile().get());
|
|
||||||
readImportsFiles(springFactories, this.importsFiles);
|
|
||||||
for (File classesDir : this.classesDirs) {
|
|
||||||
addTestSlices(testSlices, classesDir, metadataReaderFactory, springFactories);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return testSlices;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the given imports files and puts them in springFactories. The key is the file
|
|
||||||
* name, the value is the file contents, split by line, delimited with a comma. This
|
|
||||||
* is done to mimic the spring.factories structure.
|
|
||||||
* @param springFactories spring.factories parsed as properties
|
|
||||||
* @param importsFiles the imports files to read
|
|
||||||
*/
|
|
||||||
private void readImportsFiles(Properties springFactories, FileCollection importsFiles) {
|
|
||||||
for (File file : importsFiles.getFiles()) {
|
|
||||||
try {
|
|
||||||
List<String> lines = removeComments(Files.readAllLines(file.toPath()));
|
|
||||||
String fileNameWithoutExtension = file.getName()
|
|
||||||
.substring(0, file.getName().length() - ".imports".length());
|
|
||||||
springFactories.setProperty(fileNameWithoutExtension,
|
|
||||||
StringUtils.collectionToCommaDelimitedString(lines));
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new UncheckedIOException("Failed to read file " + file, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> removeComments(List<String> lines) {
|
|
||||||
List<String> result = new ArrayList<>();
|
|
||||||
for (String line : lines) {
|
|
||||||
int commentIndex = line.indexOf('#');
|
|
||||||
if (commentIndex > -1) {
|
|
||||||
line = line.substring(0, commentIndex);
|
|
||||||
}
|
|
||||||
line = line.trim();
|
|
||||||
if (!line.isEmpty()) {
|
|
||||||
result.add(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private URL toURL(File file) {
|
|
||||||
try {
|
|
||||||
return file.toURI().toURL();
|
|
||||||
}
|
|
||||||
catch (MalformedURLException ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Properties readSpringFactories(File file) throws IOException {
|
|
||||||
Properties springFactories = new Properties();
|
|
||||||
try (Reader in = new FileReader(file)) {
|
|
||||||
springFactories.load(in);
|
|
||||||
}
|
|
||||||
return springFactories;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addTestSlices(Properties testSlices, File classesDir, MetadataReaderFactory metadataReaderFactory,
|
|
||||||
Properties springFactories) throws IOException {
|
|
||||||
try (Stream<Path> classes = Files.walk(classesDir.toPath())) {
|
|
||||||
classes.filter((path) -> path.toString().endsWith("Test.class"))
|
|
||||||
.map((path) -> getMetadataReader(path, metadataReaderFactory))
|
|
||||||
.filter((metadataReader) -> metadataReader.getClassMetadata().isAnnotation())
|
|
||||||
.forEach((metadataReader) -> addTestSlice(testSlices, springFactories, metadataReader));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MetadataReader getMetadataReader(Path path, MetadataReaderFactory metadataReaderFactory) {
|
|
||||||
try {
|
|
||||||
return metadataReaderFactory.getMetadataReader(new FileSystemResource(path));
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addTestSlice(Properties testSlices, Properties springFactories, MetadataReader metadataReader) {
|
|
||||||
testSlices.setProperty(metadataReader.getClassMetadata().getClassName(),
|
|
||||||
StringUtils.collectionToCommaDelimitedString(
|
|
||||||
getImportedAutoConfiguration(springFactories, metadataReader.getAnnotationMetadata())));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SortedSet<String> getImportedAutoConfiguration(Properties springFactories,
|
|
||||||
AnnotationMetadata annotationMetadata) {
|
|
||||||
Stream<String> importers = findMetaImporters(annotationMetadata);
|
|
||||||
if (annotationMetadata.isAnnotated("org.springframework.boot.autoconfigure.ImportAutoConfiguration")) {
|
|
||||||
importers = Stream.concat(importers, Stream.of(annotationMetadata.getClassName()));
|
|
||||||
}
|
|
||||||
return importers
|
|
||||||
.flatMap((importer) -> StringUtils.commaDelimitedListToSet(springFactories.getProperty(importer)).stream())
|
|
||||||
.collect(Collectors.toCollection(TreeSet::new));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Stream<String> findMetaImporters(AnnotationMetadata annotationMetadata) {
|
|
||||||
return annotationMetadata.getAnnotationTypes()
|
|
||||||
.stream()
|
|
||||||
.filter((annotationType) -> isAutoConfigurationImporter(annotationType, annotationMetadata));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isAutoConfigurationImporter(String annotationType, AnnotationMetadata metadata) {
|
|
||||||
return metadata.getMetaAnnotationTypes(annotationType)
|
|
||||||
.contains("org.springframework.boot.autoconfigure.ImportAutoConfiguration");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.build.test.autoconfigure;
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.attributes.Category;
|
||||||
|
import org.gradle.api.attributes.Usage;
|
||||||
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
|
import org.gradle.api.plugins.JavaPluginExtension;
|
||||||
|
import org.gradle.api.plugins.PluginContainer;
|
||||||
|
import org.gradle.api.tasks.SourceSet;
|
||||||
|
import org.gradle.api.tasks.TaskProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Plugin} for projects that define one or more test slices. When applied, it:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Applies the {@link TestAutoConfigurationPlugin}
|
||||||
|
* </ul>
|
||||||
|
* Additionally, when the {@link JavaPlugin} is applied it:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Defines a task that produces metadata describing the test slices. The metadata is
|
||||||
|
* made available as an artifact in the {@code testSliceMetadata} configuration
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class TestSlicePlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
private static final String TEST_SLICE_METADATA_CONFIGURATION_NAME = "testSliceMetadata";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Project target) {
|
||||||
|
PluginContainer plugins = target.getPlugins();
|
||||||
|
plugins.apply(TestAutoConfigurationPlugin.class);
|
||||||
|
plugins.withType(JavaPlugin.class, (plugin) -> {
|
||||||
|
TaskProvider<GenerateTestSliceMetadata> generateTestSliceMetadata = target.getTasks()
|
||||||
|
.register("generateTestSliceMetadata", GenerateTestSliceMetadata.class, (task) -> {
|
||||||
|
SourceSet mainSourceSet = target.getExtensions()
|
||||||
|
.getByType(JavaPluginExtension.class)
|
||||||
|
.getSourceSets()
|
||||||
|
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||||
|
task.setSourceSet(mainSourceSet);
|
||||||
|
task.getOutputFile().set(target.getLayout().getBuildDirectory().file("test-slice-metadata.json"));
|
||||||
|
});
|
||||||
|
addMetadataArtifact(target, generateTestSliceMetadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addMetadataArtifact(Project project, TaskProvider<GenerateTestSliceMetadata> task) {
|
||||||
|
project.getConfigurations().consumable(TEST_SLICE_METADATA_CONFIGURATION_NAME, (configuration) -> {
|
||||||
|
configuration.attributes((attributes) -> {
|
||||||
|
attributes.attribute(Category.CATEGORY_ATTRIBUTE,
|
||||||
|
project.getObjects().named(Category.class, Category.DOCUMENTATION));
|
||||||
|
attributes.attribute(Usage.USAGE_ATTRIBUTE,
|
||||||
|
project.getObjects().named(Usage.class, "test-slice-metadata"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
project.getArtifacts().add(TEST_SLICE_METADATA_CONFIGURATION_NAME, task);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -116,6 +116,18 @@ apiref-openjdk=https://docs.oracle.com/en/java/javase/17/docs/api
|
||||||
# === Code Links ===
|
# === Code Links ===
|
||||||
|
|
||||||
code-spring-boot=https://github.com/{github-repo}/tree/{github-ref}
|
code-spring-boot=https://github.com/{github-repo}/tree/{github-ref}
|
||||||
code-spring-boot-autoconfigure-src={code-spring-boot}/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure
|
code-spring-boot-autoconfigure-src={code-spring-boot}/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure
|
||||||
|
code-spring-boot-batch-jdbc-src={code-spring-boot}/module/spring-boot-batch-jdbc/src/main/java/org/springframework/boot/batch/jdbc
|
||||||
|
code-spring-boot-batch-src={code-spring-boot}/module/spring-boot-batch/src/main/java/org/springframework/boot/batch
|
||||||
|
code-spring-boot-freemarker-src={code-spring-boot}/module/spring-boot-freemarker/src/main/java/org/springframework/boot/freemarker
|
||||||
|
code-spring-boot-groovy-templates-src={code-spring-boot}/module/spring-boot-groovy-templates/src/main/java/org/springframework/boot/groovy/template
|
||||||
|
code-spring-boot-hibernate-src={code-spring-boot}/module/spring-boot-hibernate/src/main/java/org/springframework/boot/hibernate
|
||||||
|
code-spring-boot-integration-src={code-spring-boot}/module/spring-boot-integration/src/main/java/org/springframework/boot/integration
|
||||||
|
code-spring-boot-jdbc-src={code-spring-boot}/module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc
|
||||||
|
code-spring-boot-jooq-src={code-spring-boot}/module/spring-boot-jooq/src/main/java/org/springframework/boot/jooq
|
||||||
|
code-spring-boot-jpa-src={code-spring-boot}/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa
|
||||||
code-spring-boot-latest=https://github.com/{github-repo}/tree/main
|
code-spring-boot-latest=https://github.com/{github-repo}/tree/main
|
||||||
|
code-spring-boot-servlet-src={code-spring-boot}/module/spring-boot-servlet/src/main/java/org/springframework/boot/servlet
|
||||||
|
code-spring-boot-thymeleaf-src={code-spring-boot}/module/spring-boot-thymeleaf/src/main/java/org/springframework/boot/thymeleaf
|
||||||
|
code-spring-boot-webmvc-src={code-spring-boot}/module/spring-boot-webmvc/src/main/java/org/springframework/boot/webmvc
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,9 @@ class ConventionsPluginTests {
|
||||||
out.println(" id 'org.springframework.boot.conventions'");
|
out.println(" id 'org.springframework.boot.conventions'");
|
||||||
out.println("}");
|
out.println("}");
|
||||||
out.println("version = '1.2.3'");
|
out.println("version = '1.2.3'");
|
||||||
|
out.println("java {");
|
||||||
out.println(" sourceCompatibility = '17'");
|
out.println(" sourceCompatibility = '17'");
|
||||||
|
out.println("}");
|
||||||
out.println("description 'Test project for manifest customization'");
|
out.println("description 'Test project for manifest customization'");
|
||||||
out.println("jar.archiveFileName = 'test.jar'");
|
out.println("jar.archiveFileName = 'test.jar'");
|
||||||
}
|
}
|
||||||
|
@ -107,7 +109,9 @@ class ConventionsPluginTests {
|
||||||
out.println(" id 'org.springframework.boot.conventions'");
|
out.println(" id 'org.springframework.boot.conventions'");
|
||||||
out.println("}");
|
out.println("}");
|
||||||
out.println("version = '1.2.3'");
|
out.println("version = '1.2.3'");
|
||||||
|
out.println("java {");
|
||||||
out.println(" sourceCompatibility = '17'");
|
out.println(" sourceCompatibility = '17'");
|
||||||
|
out.println("}");
|
||||||
out.println("description 'Test'");
|
out.println("description 'Test'");
|
||||||
}
|
}
|
||||||
runGradle("assemble");
|
runGradle("assemble");
|
||||||
|
@ -136,7 +140,9 @@ class ConventionsPluginTests {
|
||||||
out.println(" id 'org.springframework.boot.conventions'");
|
out.println(" id 'org.springframework.boot.conventions'");
|
||||||
out.println("}");
|
out.println("}");
|
||||||
out.println("version = '1.2.3'");
|
out.println("version = '1.2.3'");
|
||||||
|
out.println("java {");
|
||||||
out.println(" sourceCompatibility = '17'");
|
out.println(" sourceCompatibility = '17'");
|
||||||
|
out.println("}");
|
||||||
out.println("description 'Test'");
|
out.println("description 'Test'");
|
||||||
}
|
}
|
||||||
runGradle("assemble");
|
runGradle("assemble");
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.ArgumentsProvider;
|
import org.junit.jupiter.params.provider.ArgumentsProvider;
|
||||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||||
|
import org.junit.jupiter.params.support.ParameterDeclarations;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@ -244,7 +245,8 @@ class DependencyVersionUpgradeTests {
|
||||||
static class InputProvider implements ArgumentsProvider {
|
static class InputProvider implements ArgumentsProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
|
public Stream<? extends Arguments> provideArguments(ParameterDeclarations parameterDeclarations,
|
||||||
|
ExtensionContext context) {
|
||||||
Method testMethod = context.getRequiredTestMethod();
|
Method testMethod = context.getRequiredTestMethod();
|
||||||
Stream<Arguments> artifactVersions = artifactVersions(testMethod)
|
Stream<Arguments> artifactVersions = artifactVersions(testMethod)
|
||||||
.map((artifactVersion) -> Arguments.of(VersionType.ARTIFACT_VERSION.parse(artifactVersion.current()),
|
.map((artifactVersion) -> Arguments.of(VersionType.ARTIFACT_VERSION.parse(artifactVersion.current()),
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.build.test.autoconfigure;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import org.springframework.boot.build.test.autoconfigure.TestSliceMetadata.TestSlice;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link TestSliceMetadata}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class TestSliceMetadataTests {
|
||||||
|
|
||||||
|
@TempDir
|
||||||
|
private File temp;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void roundtripJson() {
|
||||||
|
TestSliceMetadata source = new TestSliceMetadata("example",
|
||||||
|
List.of(new TestSlice("ExampleOneTest", List.of("com.example.OneAutoConfiguration")),
|
||||||
|
new TestSlice("ExampleTwoTest", List.of("com.example.TwoAutoConfiguration"))));
|
||||||
|
File metadataFile = new File(this.temp, "metadata.json");
|
||||||
|
source.writeTo(metadataFile);
|
||||||
|
TestSliceMetadata readBack = TestSliceMetadata.readFrom(metadataFile);
|
||||||
|
assertThat(source).isEqualTo(readBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -166,6 +166,7 @@ class TestFailuresPluginIntegrationTests {
|
||||||
writer.println("dependencies {");
|
writer.println("dependencies {");
|
||||||
writer.println(" testImplementation 'org.junit.jupiter:junit-jupiter:5.6.0'");
|
writer.println(" testImplementation 'org.junit.jupiter:junit-jupiter:5.6.0'");
|
||||||
writer.println(" testImplementation 'org.assertj:assertj-core:3.11.1'");
|
writer.println(" testImplementation 'org.assertj:assertj-core:3.11.1'");
|
||||||
|
writer.println(" testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.6.0'");
|
||||||
writer.println("}");
|
writer.println("}");
|
||||||
writer.println();
|
writer.println();
|
||||||
writer.println("test {");
|
writer.println("test {");
|
||||||
|
|
|
@ -21,11 +21,11 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ import java.util.stream.IntStream;
|
||||||
|
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
import org.springframework.boot.buildpack.platform.docker.ApiVersion;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,11 +21,11 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,8 @@ import java.util.function.Function;
|
||||||
|
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
||||||
import org.springframework.boot.buildpack.platform.docker.DockerLog;
|
import org.springframework.boot.buildpack.platform.docker.DockerLog;
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressPullListener;
|
import org.springframework.boot.buildpack.platform.docker.TotalProgressPullListener;
|
||||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressPushListener;
|
import org.springframework.boot.buildpack.platform.docker.TotalProgressPushListener;
|
||||||
|
@ -34,7 +35,6 @@ import org.springframework.boot.buildpack.platform.docker.transport.DockerEngine
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||||
import org.springframework.boot.buildpack.platform.io.IOBiConsumer;
|
import org.springframework.boot.buildpack.platform.io.IOBiConsumer;
|
||||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||||
|
|
|
@ -28,10 +28,10 @@ import com.sun.jna.Platform;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.boot.buildpack.platform.build.Cache.Bind;
|
import org.springframework.boot.buildpack.platform.build.Cache.Bind;
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.ApiVersion;
|
||||||
import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
||||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost;
|
import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.boot.buildpack.platform.docker.type;
|
package org.springframework.boot.buildpack.platform.docker;
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
@ -28,7 +28,7 @@ import org.springframework.util.Assert;
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Scott Frederick
|
* @author Scott Frederick
|
||||||
* @since 3.4.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
public final class ApiVersion {
|
public final class ApiVersion {
|
||||||
|
|
|
@ -34,14 +34,12 @@ import org.springframework.boot.buildpack.platform.docker.PushImageUpdateEvent.E
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConnectionConfiguration;
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConnectionConfiguration;
|
||||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport;
|
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport;
|
||||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
|
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||||
import org.springframework.boot.buildpack.platform.io.IOBiConsumer;
|
import org.springframework.boot.buildpack.platform.io.IOBiConsumer;
|
||||||
|
|
|
@ -14,19 +14,20 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.boot.buildpack.platform.docker.type;
|
package org.springframework.boot.buildpack.platform.docker;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A platform specification for a Docker image.
|
* A platform specification for a Docker image.
|
||||||
*
|
*
|
||||||
* @author Scott Frederick
|
* @author Scott Frederick
|
||||||
* @since 3.4.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
public class ImagePlatform {
|
public class ImagePlatform {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import java.util.stream.IntStream;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
import org.springframework.boot.buildpack.platform.docker.ApiVersion;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||||
|
|
|
@ -37,9 +37,9 @@ import org.junit.jupiter.api.io.TempDir;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageName;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageName;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.springframework.boot.buildpack.platform.docker.DockerApi.ContainerApi
|
||||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.ImageApi;
|
import org.springframework.boot.buildpack.platform.docker.DockerApi.ImageApi;
|
||||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
||||||
import org.springframework.boot.buildpack.platform.docker.DockerLog;
|
import org.springframework.boot.buildpack.platform.docker.DockerLog;
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressPullListener;
|
import org.springframework.boot.buildpack.platform.docker.TotalProgressPullListener;
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryAuthentication;
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryAuthentication;
|
||||||
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
|
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
|
||||||
|
@ -39,7 +40,6 @@ import org.springframework.boot.buildpack.platform.docker.type.ContainerReferenc
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
||||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.ContainerApi;
|
import org.springframework.boot.buildpack.platform.docker.DockerApi.ContainerApi;
|
||||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.ImageApi;
|
import org.springframework.boot.buildpack.platform.docker.DockerApi.ImageApi;
|
||||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConnectionConfiguration;
|
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConnectionConfiguration;
|
||||||
import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost;
|
import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||||
|
@ -48,7 +49,6 @@ import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||||
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
||||||
|
|
|
@ -25,10 +25,10 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||||
import org.springframework.util.FileCopyUtils;
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.boot.buildpack.platform.docker.type;
|
package org.springframework.boot.buildpack.platform.docker;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
|
@ -45,14 +45,12 @@ import org.springframework.boot.buildpack.platform.docker.DockerApi.SystemApi;
|
||||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
||||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport;
|
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport;
|
||||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
|
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||||
import org.springframework.boot.buildpack.platform.io.Content;
|
import org.springframework.boot.buildpack.platform.io.Content;
|
||||||
|
|
|
@ -14,12 +14,13 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.boot.buildpack.platform.docker.type;
|
package org.springframework.boot.buildpack.platform.docker;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||||
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
|
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -64,7 +65,7 @@ class ImagePlatformTests extends AbstractJsonTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Image getImage() throws IOException {
|
private Image getImage() throws IOException {
|
||||||
return Image.of(getContent("image.json"));
|
return Image.of(getContent("type/image.json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -79,4 +79,7 @@
|
||||||
<suppress files="EntityManagerFactoryBuilder\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
<suppress files="EntityManagerFactoryBuilder\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
||||||
<suppress files="DockerApi\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
<suppress files="DockerApi\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
||||||
<suppress files="InvocationContext\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
<suppress files="InvocationContext\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
||||||
|
<!-- https://github.com/apache/logging-log4j2/issues/2769#issuecomment-3049020222 -->
|
||||||
|
<suppress files="SpringProfileArbiter\.java" checks="SpringMethodVisibility"/>
|
||||||
|
<suppress files="StructuredLogLayout\.java" checks="SpringMethodVisibility"/>
|
||||||
</suppressions>
|
</suppressions>
|
||||||
|
|
|
@ -6,7 +6,10 @@
|
||||||
<module name="SuppressionFilter">
|
<module name="SuppressionFilter">
|
||||||
<property name="file" value="${config_loc}/checkstyle-suppressions.xml"/>
|
<property name="file" value="${config_loc}/checkstyle-suppressions.xml"/>
|
||||||
</module>
|
</module>
|
||||||
<module name="io.spring.javaformat.checkstyle.SpringChecks" />
|
<module name="io.spring.javaformat.checkstyle.SpringChecks">
|
||||||
|
<property name="avoidStaticImportExcludes"
|
||||||
|
value="org.springframework.boot.autoconfigure.AutoConfigurationImportedCondition.importedAutoConfiguration" />
|
||||||
|
</module>
|
||||||
<module name="io.spring.javaformat.checkstyle.check.SpringHeaderCheck">
|
<module name="io.spring.javaformat.checkstyle.check.SpringHeaderCheck">
|
||||||
<property name="headerFile" value="${config_loc}/checkstyle-header.txt"/>
|
<property name="headerFile" value="${config_loc}/checkstyle-header.txt"/>
|
||||||
</module>
|
</module>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<subpackage name="ansi">
|
<subpackage name="ansi">
|
||||||
<disallow pkg="org.springframework.boot" exact-match="true"/>
|
<disallow pkg="org.springframework.boot" exact-match="true"/>
|
||||||
</subpackage>
|
</subpackage>
|
||||||
<subpackage name="avilability">
|
<subpackage name="availability">
|
||||||
<disallow pkg="org.springframework.boot" exact-match="true"/>
|
<disallow pkg="org.springframework.boot" exact-match="true"/>
|
||||||
</subpackage>
|
</subpackage>
|
||||||
<subpackage name="bootstrap">
|
<subpackage name="bootstrap">
|
||||||
|
@ -91,8 +91,6 @@
|
||||||
<disallow pkg="jakarta.servlet" />
|
<disallow pkg="jakarta.servlet" />
|
||||||
|
|
||||||
<!-- Common -->
|
<!-- Common -->
|
||||||
<subpackage name="client">
|
|
||||||
</subpackage>
|
|
||||||
<subpackage name="context">
|
<subpackage name="context">
|
||||||
<allow pkg="org.springframework.context" />
|
<allow pkg="org.springframework.context" />
|
||||||
<subpackage name="servlet">
|
<subpackage name="servlet">
|
||||||
|
@ -111,6 +109,10 @@
|
||||||
<allow pkg="org.springframework.boot.web.servlet" />
|
<allow pkg="org.springframework.boot.web.servlet" />
|
||||||
</subpackage>
|
</subpackage>
|
||||||
</subpackage>
|
</subpackage>
|
||||||
|
<subpackage name="client">
|
||||||
|
<allow pkg="jakarta.servlet" />
|
||||||
|
<allow pkg="org.springframework.context" />
|
||||||
|
</subpackage>
|
||||||
<subpackage name="context">
|
<subpackage name="context">
|
||||||
<allow pkg="org.springframework.context" />
|
<allow pkg="org.springframework.context" />
|
||||||
</subpackage>
|
</subpackage>
|
||||||
|
|
|
@ -19,7 +19,7 @@ style:
|
||||||
active: true
|
active: true
|
||||||
NewLineAtEndOfFile:
|
NewLineAtEndOfFile:
|
||||||
active: true
|
active: true
|
||||||
UnusedImports:
|
UnusedImport:
|
||||||
active: true
|
active: true
|
||||||
WildcardImport:
|
WildcardImport:
|
||||||
active: true
|
active: true
|
||||||
|
|
|
@ -136,6 +136,7 @@ def dependenciesOf(String version) {
|
||||||
modules += [
|
modules += [
|
||||||
"spring-boot-jersey"
|
"spring-boot-jersey"
|
||||||
]
|
]
|
||||||
|
}
|
||||||
if (version.equals("4.0.0-M1")) {
|
if (version.equals("4.0.0-M1")) {
|
||||||
modules += [
|
modules += [
|
||||||
"spring-boot-metrics",
|
"spring-boot-metrics",
|
||||||
|
@ -150,6 +151,10 @@ def dependenciesOf(String version) {
|
||||||
"spring-boot-micrometer-tracing"
|
"spring-boot-micrometer-tracing"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
if (version.equals("4.0.0-RC1")) {
|
||||||
|
modules += [
|
||||||
|
"spring-boot-batch-jdbc"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
return modules
|
return modules
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,3 +56,7 @@ dependencies {
|
||||||
test {
|
test {
|
||||||
jvmArgs += "--add-opens=java.base/java.net=ALL-UNNAMED"
|
jvmArgs += "--add-opens=java.base/java.net=ALL-UNNAMED"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.named("compileTestJava") {
|
||||||
|
options.nullability.checking = "tests"
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ class AutoConfigurationExcludeFilterTests {
|
||||||
|
|
||||||
private static final Class<?> FILTERED = ExampleFilteredAutoConfiguration.class;
|
private static final Class<?> FILTERED = ExampleFilteredAutoConfiguration.class;
|
||||||
|
|
||||||
private AnnotationConfigApplicationContext context;
|
private @Nullable AnnotationConfigApplicationContext context;
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
void cleanUp() {
|
void cleanUp() {
|
||||||
|
@ -56,7 +57,10 @@ class AutoConfigurationExcludeFilterTests {
|
||||||
this.context = new AnnotationConfigApplicationContext(Config.class);
|
this.context = new AnnotationConfigApplicationContext(Config.class);
|
||||||
assertThat(this.context.getBeansOfType(String.class)).hasSize(1);
|
assertThat(this.context.getBeansOfType(String.class)).hasSize(1);
|
||||||
assertThat(this.context.getBean(String.class)).isEqualTo("test");
|
assertThat(this.context.getBean(String.class)).isEqualTo("test");
|
||||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() -> this.context.getBean(FILTERED));
|
assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() -> {
|
||||||
|
assertThat(this.context).isNotNull();
|
||||||
|
this.context.getBean(FILTERED);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
|
|
@ -28,6 +28,7 @@ import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -83,39 +84,35 @@ class AutoConfigurationImportSelectorTests {
|
||||||
void importsAreSelectedWhenUsingEnableAutoConfiguration() {
|
void importsAreSelectedWhenUsingEnableAutoConfiguration() {
|
||||||
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
||||||
assertThat(imports).hasSameSizeAs(getAutoConfigurationClassNames());
|
assertThat(imports).hasSameSizeAs(getAutoConfigurationClassNames());
|
||||||
assertThat(this.importSelector.getLastEvent().getExclusions()).isEmpty();
|
assertThat(getLastEvent().getExclusions()).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void classExclusionsAreApplied() {
|
void classExclusionsAreApplied() {
|
||||||
String[] imports = selectImports(EnableAutoConfigurationWithClassExclusions.class);
|
String[] imports = selectImports(EnableAutoConfigurationWithClassExclusions.class);
|
||||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||||
assertThat(this.importSelector.getLastEvent().getExclusions())
|
assertThat(getLastEvent().getExclusions()).contains(SeventhAutoConfiguration.class.getName());
|
||||||
.contains(SeventhAutoConfiguration.class.getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void classExclusionsAreAppliedWhenUsingSpringBootApplication() {
|
void classExclusionsAreAppliedWhenUsingSpringBootApplication() {
|
||||||
String[] imports = selectImports(SpringBootApplicationWithClassExclusions.class);
|
String[] imports = selectImports(SpringBootApplicationWithClassExclusions.class);
|
||||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||||
assertThat(this.importSelector.getLastEvent().getExclusions())
|
assertThat(getLastEvent().getExclusions()).contains(SeventhAutoConfiguration.class.getName());
|
||||||
.contains(SeventhAutoConfiguration.class.getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void classNamesExclusionsAreApplied() {
|
void classNamesExclusionsAreApplied() {
|
||||||
String[] imports = selectImports(EnableAutoConfigurationWithClassNameExclusions.class);
|
String[] imports = selectImports(EnableAutoConfigurationWithClassNameExclusions.class);
|
||||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||||
assertThat(this.importSelector.getLastEvent().getExclusions())
|
assertThat(getLastEvent().getExclusions()).contains("com.example.one.FirstAutoConfiguration");
|
||||||
.contains("com.example.one.FirstAutoConfiguration");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void classNamesExclusionsAreAppliedWhenUsingSpringBootApplication() {
|
void classNamesExclusionsAreAppliedWhenUsingSpringBootApplication() {
|
||||||
String[] imports = selectImports(SpringBootApplicationWithClassNameExclusions.class);
|
String[] imports = selectImports(SpringBootApplicationWithClassNameExclusions.class);
|
||||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||||
assertThat(this.importSelector.getLastEvent().getExclusions())
|
assertThat(getLastEvent().getExclusions()).contains("com.example.three.ThirdAutoConfiguration");
|
||||||
.contains("com.example.three.ThirdAutoConfiguration");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -123,8 +120,7 @@ class AutoConfigurationImportSelectorTests {
|
||||||
this.environment.setProperty("spring.autoconfigure.exclude", "com.example.three.ThirdAutoConfiguration");
|
this.environment.setProperty("spring.autoconfigure.exclude", "com.example.three.ThirdAutoConfiguration");
|
||||||
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
||||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||||
assertThat(this.importSelector.getLastEvent().getExclusions())
|
assertThat(getLastEvent().getExclusions()).contains("com.example.three.ThirdAutoConfiguration");
|
||||||
.contains("com.example.three.ThirdAutoConfiguration");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -151,8 +147,8 @@ class AutoConfigurationImportSelectorTests {
|
||||||
private void testSeveralPropertyExclusionsAreApplied() {
|
private void testSeveralPropertyExclusionsAreApplied() {
|
||||||
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
||||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 2);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 2);
|
||||||
assertThat(this.importSelector.getLastEvent().getExclusions())
|
assertThat(getLastEvent().getExclusions()).contains("com.example.two.SecondAutoConfiguration",
|
||||||
.contains("com.example.two.SecondAutoConfiguration", "com.example.four.FourthAutoConfiguration");
|
"com.example.four.FourthAutoConfiguration");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -160,9 +156,8 @@ class AutoConfigurationImportSelectorTests {
|
||||||
this.environment.setProperty("spring.autoconfigure.exclude", "com.example.one.FirstAutoConfiguration");
|
this.environment.setProperty("spring.autoconfigure.exclude", "com.example.one.FirstAutoConfiguration");
|
||||||
String[] imports = selectImports(EnableAutoConfigurationWithClassAndClassNameExclusions.class);
|
String[] imports = selectImports(EnableAutoConfigurationWithClassAndClassNameExclusions.class);
|
||||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 3);
|
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 3);
|
||||||
assertThat(this.importSelector.getLastEvent().getExclusions()).contains(
|
assertThat(getLastEvent().getExclusions()).contains("com.example.one.FirstAutoConfiguration",
|
||||||
"com.example.one.FirstAutoConfiguration", "com.example.five.FifthAutoConfiguration",
|
"com.example.five.FifthAutoConfiguration", SeventhAutoConfiguration.class.getName());
|
||||||
SeventhAutoConfiguration.class.getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -202,7 +197,7 @@ class AutoConfigurationImportSelectorTests {
|
||||||
this.environment.setProperty("spring.autoconfigure.exclude",
|
this.environment.setProperty("spring.autoconfigure.exclude",
|
||||||
"org.springframework.boot.autoconfigure.DoesNotExist2");
|
"org.springframework.boot.autoconfigure.DoesNotExist2");
|
||||||
selectImports(EnableAutoConfigurationWithAbsentClassNameExclude.class);
|
selectImports(EnableAutoConfigurationWithAbsentClassNameExclude.class);
|
||||||
assertThat(this.importSelector.getLastEvent().getExclusions()).containsExactlyInAnyOrder(
|
assertThat(getLastEvent().getExclusions()).containsExactlyInAnyOrder(
|
||||||
"org.springframework.boot.autoconfigure.DoesNotExist1",
|
"org.springframework.boot.autoconfigure.DoesNotExist1",
|
||||||
"org.springframework.boot.autoconfigure.DoesNotExist2");
|
"org.springframework.boot.autoconfigure.DoesNotExist2");
|
||||||
}
|
}
|
||||||
|
@ -269,11 +264,17 @@ class AutoConfigurationImportSelectorTests {
|
||||||
importSelector.setBeanClassLoader(Thread.currentThread().getContextClassLoader());
|
importSelector.setBeanClassLoader(Thread.currentThread().getContextClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AutoConfigurationImportEvent getLastEvent() {
|
||||||
|
AutoConfigurationImportEvent result = this.importSelector.getLastEvent();
|
||||||
|
assertThat(result).isNotNull();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private final class TestAutoConfigurationImportSelector extends AutoConfigurationImportSelector {
|
private final class TestAutoConfigurationImportSelector extends AutoConfigurationImportSelector {
|
||||||
|
|
||||||
private AutoConfigurationImportEvent lastEvent;
|
private @Nullable AutoConfigurationImportEvent lastEvent;
|
||||||
|
|
||||||
TestAutoConfigurationImportSelector(Class<?> autoConfigurationAnnotation) {
|
TestAutoConfigurationImportSelector(@Nullable Class<?> autoConfigurationAnnotation) {
|
||||||
super(autoConfigurationAnnotation);
|
super(autoConfigurationAnnotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +288,7 @@ class AutoConfigurationImportSelectorTests {
|
||||||
return Collections.singletonList((event) -> this.lastEvent = event);
|
return Collections.singletonList((event) -> this.lastEvent = event);
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoConfigurationImportEvent getLastEvent() {
|
@Nullable AutoConfigurationImportEvent getLastEvent() {
|
||||||
return this.lastEvent;
|
return this.lastEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,6 +298,7 @@ class AutoConfigurationImportSelectorTests {
|
||||||
|
|
||||||
private final Set<String> nonMatching = new HashSet<>();
|
private final Set<String> nonMatching = new HashSet<>();
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private BeanFactory beanFactory;
|
private BeanFactory beanFactory;
|
||||||
|
|
||||||
TestAutoConfigurationImportFilter(String[] configurations, int... nonMatching) {
|
TestAutoConfigurationImportFilter(String[] configurations, int... nonMatching) {
|
||||||
|
@ -306,7 +308,8 @@ class AutoConfigurationImportSelectorTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
|
public boolean[] match(@Nullable String[] autoConfigurationClasses,
|
||||||
|
AutoConfigurationMetadata autoConfigurationMetadata) {
|
||||||
boolean[] result = new boolean[autoConfigurationClasses.length];
|
boolean[] result = new boolean[autoConfigurationClasses.length];
|
||||||
for (int i = 0; i < result.length; i++) {
|
for (int i = 0; i < result.length; i++) {
|
||||||
result[i] = !this.nonMatching.contains(autoConfigurationClasses[i]);
|
result[i] = !this.nonMatching.contains(autoConfigurationClasses[i]);
|
||||||
|
|
|
@ -26,6 +26,7 @@ import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.UnaryOperator;
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -251,10 +252,14 @@ class AutoConfigurationSorterTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAutoConfigureAfter(Properties properties, String className, AnnotationMetadata annotationMetadata) {
|
private void addAutoConfigureAfter(Properties properties, String className, AnnotationMetadata annotationMetadata) {
|
||||||
Map<String, Object> autoConfigureAfter = annotationMetadata
|
Map<String, @Nullable Object> autoConfigureAfter = annotationMetadata
|
||||||
.getAnnotationAttributes(AutoConfigureAfter.class.getName(), true);
|
.getAnnotationAttributes(AutoConfigureAfter.class.getName(), true);
|
||||||
if (autoConfigureAfter != null) {
|
if (autoConfigureAfter != null) {
|
||||||
String value = merge((String[]) autoConfigureAfter.get("value"), (String[]) autoConfigureAfter.get("name"));
|
String[] valueAttribute = (String[]) autoConfigureAfter.get("value");
|
||||||
|
String[] nameAttribute = (String[]) autoConfigureAfter.get("name");
|
||||||
|
assertThat(valueAttribute).isNotNull();
|
||||||
|
assertThat(nameAttribute).isNotNull();
|
||||||
|
String value = merge(valueAttribute, nameAttribute);
|
||||||
if (!value.isEmpty()) {
|
if (!value.isEmpty()) {
|
||||||
properties.put(className + ".AutoConfigureAfter", value);
|
properties.put(className + ".AutoConfigureAfter", value);
|
||||||
}
|
}
|
||||||
|
@ -263,11 +268,14 @@ class AutoConfigurationSorterTests {
|
||||||
|
|
||||||
private void addAutoConfigureBefore(Properties properties, String className,
|
private void addAutoConfigureBefore(Properties properties, String className,
|
||||||
AnnotationMetadata annotationMetadata) {
|
AnnotationMetadata annotationMetadata) {
|
||||||
Map<String, Object> autoConfigureBefore = annotationMetadata
|
Map<String, @Nullable Object> autoConfigureBefore = annotationMetadata
|
||||||
.getAnnotationAttributes(AutoConfigureBefore.class.getName(), true);
|
.getAnnotationAttributes(AutoConfigureBefore.class.getName(), true);
|
||||||
if (autoConfigureBefore != null) {
|
if (autoConfigureBefore != null) {
|
||||||
String value = merge((String[]) autoConfigureBefore.get("value"),
|
String[] valueAttribute = (String[]) autoConfigureBefore.get("value");
|
||||||
(String[]) autoConfigureBefore.get("name"));
|
String[] nameAttribute = (String[]) autoConfigureBefore.get("name");
|
||||||
|
assertThat(valueAttribute).isNotNull();
|
||||||
|
assertThat(nameAttribute).isNotNull();
|
||||||
|
String value = merge(valueAttribute, nameAttribute);
|
||||||
if (!value.isEmpty()) {
|
if (!value.isEmpty()) {
|
||||||
properties.put(className + ".AutoConfigureBefore", value);
|
properties.put(className + ".AutoConfigureBefore", value);
|
||||||
}
|
}
|
||||||
|
@ -275,7 +283,7 @@ class AutoConfigurationSorterTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAutoConfigureOrder(Properties properties, String className, AnnotationMetadata annotationMetadata) {
|
private void addAutoConfigureOrder(Properties properties, String className, AnnotationMetadata annotationMetadata) {
|
||||||
Map<String, Object> autoConfigureOrder = annotationMetadata
|
Map<String, @Nullable Object> autoConfigureOrder = annotationMetadata
|
||||||
.getAnnotationAttributes(AutoConfigureOrder.class.getName());
|
.getAnnotationAttributes(AutoConfigureOrder.class.getName());
|
||||||
if (autoConfigureOrder != null) {
|
if (autoConfigureOrder != null) {
|
||||||
Integer order = (Integer) autoConfigureOrder.get("order");
|
Integer order = (Integer) autoConfigureOrder.get("order");
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
|
|
||||||
package org.springframework.boot.autoconfigure;
|
package org.springframework.boot.autoconfigure;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
|
|
||||||
public class EarlyInitFactoryBean implements FactoryBean<String> {
|
public class EarlyInitFactoryBean implements FactoryBean<String> {
|
||||||
|
|
||||||
private String propertyFromConfig;
|
private @Nullable String propertyFromConfig;
|
||||||
|
|
||||||
public void setPropertyFromConfig(String propertyFromConfig) {
|
public void setPropertyFromConfig(String propertyFromConfig) {
|
||||||
this.propertyFromConfig = propertyFromConfig;
|
this.propertyFromConfig = propertyFromConfig;
|
||||||
|
@ -32,12 +34,12 @@ public class EarlyInitFactoryBean implements FactoryBean<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> getObjectType() {
|
public @Nullable Class<?> getObjectType() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getObject() throws Exception {
|
public @Nullable String getObject() throws Exception {
|
||||||
return this.propertyFromConfig;
|
return this.propertyFromConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,7 @@ class SharedMetadataReaderFactoryContextInitializerTests {
|
||||||
.getField(application, "initializers");
|
.getField(application, "initializers");
|
||||||
// Simulate what would happen if an initializer was added using spring.factories
|
// Simulate what would happen if an initializer was added using spring.factories
|
||||||
// and happened to be loaded first
|
// and happened to be loaded first
|
||||||
|
assertThat(initializers).isNotNull();
|
||||||
initializers.add(0, new Initializer());
|
initializers.add(0, new Initializer());
|
||||||
GenericApplicationContext context = (GenericApplicationContext) application.run();
|
GenericApplicationContext context = (GenericApplicationContext) application.run();
|
||||||
BeanDefinition definition = context.getBeanDefinition(SharedMetadataReaderFactoryContextInitializer.BEAN_NAME);
|
BeanDefinition definition = context.getBeanDefinition(SharedMetadataReaderFactoryContextInitializer.BEAN_NAME);
|
||||||
|
|
|
@ -44,7 +44,9 @@ class AbstractNestedConditionTests {
|
||||||
void invalidMemberPhase() {
|
void invalidMemberPhase() {
|
||||||
this.contextRunner.withUserConfiguration(InvalidConfig.class).run((context) -> {
|
this.contextRunner.withUserConfiguration(InvalidConfig.class).run((context) -> {
|
||||||
assertThat(context).hasFailed();
|
assertThat(context).hasFailed();
|
||||||
assertThat(context.getStartupFailure().getCause()).isInstanceOf(IllegalStateException.class)
|
Throwable startupFailure = context.getStartupFailure();
|
||||||
|
assertThat(startupFailure).isNotNull();
|
||||||
|
assertThat(startupFailure.getCause()).isInstanceOf(IllegalStateException.class)
|
||||||
.hasMessageContaining("Nested condition " + InvalidNestedCondition.class.getName()
|
.hasMessageContaining("Nested condition " + InvalidNestedCondition.class.getName()
|
||||||
+ " uses a configuration phase that is inappropriate for class "
|
+ " uses a configuration phase that is inappropriate for class "
|
||||||
+ OnBeanCondition.class.getName());
|
+ OnBeanCondition.class.getName());
|
||||||
|
@ -55,7 +57,9 @@ class AbstractNestedConditionTests {
|
||||||
void invalidNestedMemberPhase() {
|
void invalidNestedMemberPhase() {
|
||||||
this.contextRunner.withUserConfiguration(DoubleNestedConfig.class).run((context) -> {
|
this.contextRunner.withUserConfiguration(DoubleNestedConfig.class).run((context) -> {
|
||||||
assertThat(context).hasFailed();
|
assertThat(context).hasFailed();
|
||||||
assertThat(context.getStartupFailure().getCause()).isInstanceOf(IllegalStateException.class)
|
Throwable startupFailure = context.getStartupFailure();
|
||||||
|
assertThat(startupFailure).isNotNull();
|
||||||
|
assertThat(startupFailure.getCause()).isInstanceOf(IllegalStateException.class)
|
||||||
.hasMessageContaining("Nested condition " + DoubleNestedCondition.class.getName()
|
.hasMessageContaining("Nested condition " + DoubleNestedCondition.class.getName()
|
||||||
+ " uses a configuration phase that is inappropriate for class "
|
+ " uses a configuration phase that is inappropriate for class "
|
||||||
+ ValidNestedCondition.class.getName());
|
+ ValidNestedCondition.class.getName());
|
||||||
|
|
|
@ -20,12 +20,14 @@ import java.time.Duration;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
@ -61,19 +63,22 @@ class ConditionEvaluationReportTests {
|
||||||
private ConditionEvaluationReport report;
|
private ConditionEvaluationReport report;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private Condition condition1;
|
private Condition condition1;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private Condition condition2;
|
private Condition condition2;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
private Condition condition3;
|
private Condition condition3;
|
||||||
|
|
||||||
private ConditionOutcome outcome1;
|
private @Nullable ConditionOutcome outcome1;
|
||||||
|
|
||||||
private ConditionOutcome outcome2;
|
private @Nullable ConditionOutcome outcome2;
|
||||||
|
|
||||||
private ConditionOutcome outcome3;
|
private @Nullable ConditionOutcome outcome3;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
|
@ -90,21 +95,25 @@ class ConditionEvaluationReportTests {
|
||||||
@Test
|
@Test
|
||||||
void parent() {
|
void parent() {
|
||||||
this.beanFactory.setParentBeanFactory(new DefaultListableBeanFactory());
|
this.beanFactory.setParentBeanFactory(new DefaultListableBeanFactory());
|
||||||
ConditionEvaluationReport.get((ConfigurableListableBeanFactory) this.beanFactory.getParentBeanFactory());
|
BeanFactory parentBeanFactory = this.beanFactory.getParentBeanFactory();
|
||||||
|
assertThat(parentBeanFactory).isNotNull();
|
||||||
|
ConditionEvaluationReport.get((ConfigurableListableBeanFactory) parentBeanFactory);
|
||||||
assertThat(this.report).isSameAs(ConditionEvaluationReport.get(this.beanFactory));
|
assertThat(this.report).isSameAs(ConditionEvaluationReport.get(this.beanFactory));
|
||||||
assertThat(this.report).isNotNull();
|
assertThat(this.report).isNotNull();
|
||||||
assertThat(this.report.getParent()).isNotNull();
|
assertThat(this.report.getParent()).isNotNull();
|
||||||
ConditionEvaluationReport.get((ConfigurableListableBeanFactory) this.beanFactory.getParentBeanFactory());
|
ConditionEvaluationReport.get((ConfigurableListableBeanFactory) parentBeanFactory);
|
||||||
assertThat(this.report).isSameAs(ConditionEvaluationReport.get(this.beanFactory));
|
assertThat(this.report).isSameAs(ConditionEvaluationReport.get(this.beanFactory));
|
||||||
assertThat(this.report.getParent()).isSameAs(ConditionEvaluationReport
|
assertThat(this.report.getParent())
|
||||||
.get((ConfigurableListableBeanFactory) this.beanFactory.getParentBeanFactory()));
|
.isSameAs(ConditionEvaluationReport.get((ConfigurableListableBeanFactory) parentBeanFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void parentBottomUp() {
|
void parentBottomUp() {
|
||||||
this.beanFactory = new DefaultListableBeanFactory(); // NB: overrides setup
|
this.beanFactory = new DefaultListableBeanFactory(); // NB: overrides setup
|
||||||
this.beanFactory.setParentBeanFactory(new DefaultListableBeanFactory());
|
this.beanFactory.setParentBeanFactory(new DefaultListableBeanFactory());
|
||||||
ConditionEvaluationReport.get((ConfigurableListableBeanFactory) this.beanFactory.getParentBeanFactory());
|
BeanFactory parentBeanFactory = this.beanFactory.getParentBeanFactory();
|
||||||
|
assertThat(parentBeanFactory).isNotNull();
|
||||||
|
ConditionEvaluationReport.get((ConfigurableListableBeanFactory) parentBeanFactory);
|
||||||
this.report = ConditionEvaluationReport.get(this.beanFactory);
|
this.report = ConditionEvaluationReport.get(this.beanFactory);
|
||||||
assertThat(this.report).isNotNull();
|
assertThat(this.report).isNotNull();
|
||||||
assertThat(this.report).isNotSameAs(this.report.getParent());
|
assertThat(this.report).isNotSameAs(this.report.getParent());
|
||||||
|
@ -122,7 +131,9 @@ class ConditionEvaluationReportTests {
|
||||||
this.report.recordConditionEvaluation("b", this.condition3, this.outcome3);
|
this.report.recordConditionEvaluation("b", this.condition3, this.outcome3);
|
||||||
Map<String, ConditionAndOutcomes> map = this.report.getConditionAndOutcomesBySource();
|
Map<String, ConditionAndOutcomes> map = this.report.getConditionAndOutcomesBySource();
|
||||||
assertThat(map).hasSize(2);
|
assertThat(map).hasSize(2);
|
||||||
Iterator<ConditionAndOutcome> iterator = map.get("a").iterator();
|
ConditionAndOutcomes a = map.get("a");
|
||||||
|
assertThat(a).isNotNull();
|
||||||
|
Iterator<ConditionAndOutcome> iterator = a.iterator();
|
||||||
ConditionAndOutcome conditionAndOutcome = iterator.next();
|
ConditionAndOutcome conditionAndOutcome = iterator.next();
|
||||||
assertThat(conditionAndOutcome.getCondition()).isEqualTo(this.condition1);
|
assertThat(conditionAndOutcome.getCondition()).isEqualTo(this.condition1);
|
||||||
assertThat(conditionAndOutcome.getOutcome()).isEqualTo(this.outcome1);
|
assertThat(conditionAndOutcome.getOutcome()).isEqualTo(this.outcome1);
|
||||||
|
@ -130,7 +141,9 @@ class ConditionEvaluationReportTests {
|
||||||
assertThat(conditionAndOutcome.getCondition()).isEqualTo(this.condition2);
|
assertThat(conditionAndOutcome.getCondition()).isEqualTo(this.condition2);
|
||||||
assertThat(conditionAndOutcome.getOutcome()).isEqualTo(this.outcome2);
|
assertThat(conditionAndOutcome.getOutcome()).isEqualTo(this.outcome2);
|
||||||
assertThat(iterator.hasNext()).isFalse();
|
assertThat(iterator.hasNext()).isFalse();
|
||||||
iterator = map.get("b").iterator();
|
ConditionAndOutcomes b = map.get("b");
|
||||||
|
assertThat(b).isNotNull();
|
||||||
|
iterator = b.iterator();
|
||||||
conditionAndOutcome = iterator.next();
|
conditionAndOutcome = iterator.next();
|
||||||
assertThat(conditionAndOutcome.getCondition()).isEqualTo(this.condition3);
|
assertThat(conditionAndOutcome.getCondition()).isEqualTo(this.condition3);
|
||||||
assertThat(conditionAndOutcome.getOutcome()).isEqualTo(this.outcome3);
|
assertThat(conditionAndOutcome.getOutcome()).isEqualTo(this.outcome3);
|
||||||
|
@ -140,13 +153,17 @@ class ConditionEvaluationReportTests {
|
||||||
@Test
|
@Test
|
||||||
void fullMatch() {
|
void fullMatch() {
|
||||||
prepareMatches(true, true, true);
|
prepareMatches(true, true, true);
|
||||||
assertThat(this.report.getConditionAndOutcomesBySource().get("a").isFullMatch()).isTrue();
|
ConditionAndOutcomes a = this.report.getConditionAndOutcomesBySource().get("a");
|
||||||
|
assertThat(a).isNotNull();
|
||||||
|
assertThat(a.isFullMatch()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void notFullMatch() {
|
void notFullMatch() {
|
||||||
prepareMatches(true, false, true);
|
prepareMatches(true, false, true);
|
||||||
assertThat(this.report.getConditionAndOutcomesBySource().get("a").isFullMatch()).isFalse();
|
ConditionAndOutcomes a = this.report.getConditionAndOutcomesBySource().get("a");
|
||||||
|
assertThat(a).isNotNull();
|
||||||
|
assertThat(a.isFullMatch()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareMatches(boolean m1, boolean m2, boolean m3) {
|
private void prepareMatches(boolean m1, boolean m2, boolean m3) {
|
||||||
|
@ -203,9 +220,13 @@ class ConditionEvaluationReportTests {
|
||||||
Map<String, ConditionAndOutcomes> sourceOutcomes = report.getConditionAndOutcomesBySource();
|
Map<String, ConditionAndOutcomes> sourceOutcomes = report.getConditionAndOutcomesBySource();
|
||||||
assertThat(context.containsBean("negativeOuterPositiveInnerBean")).isFalse();
|
assertThat(context.containsBean("negativeOuterPositiveInnerBean")).isFalse();
|
||||||
String negativeConfig = NegativeOuterConfig.class.getName();
|
String negativeConfig = NegativeOuterConfig.class.getName();
|
||||||
assertThat(sourceOutcomes.get(negativeConfig).isFullMatch()).isFalse();
|
ConditionAndOutcomes negativeOutcome = sourceOutcomes.get(negativeConfig);
|
||||||
|
assertThat(negativeOutcome).isNotNull();
|
||||||
|
assertThat(negativeOutcome.isFullMatch()).isFalse();
|
||||||
String positiveConfig = NegativeOuterConfig.PositiveInnerConfig.class.getName();
|
String positiveConfig = NegativeOuterConfig.PositiveInnerConfig.class.getName();
|
||||||
assertThat(sourceOutcomes.get(positiveConfig).isFullMatch()).isFalse();
|
ConditionAndOutcomes positiveOutcome = sourceOutcomes.get(positiveConfig);
|
||||||
|
assertThat(positiveOutcome).isNotNull();
|
||||||
|
assertThat(positiveOutcome.isFullMatch()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.condition;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||||
*/
|
*/
|
||||||
class ConditionalOnBooleanPropertyTests {
|
class ConditionalOnBooleanPropertyTests {
|
||||||
|
|
||||||
private ConfigurableApplicationContext context;
|
private @Nullable ConfigurableApplicationContext context;
|
||||||
|
|
||||||
private final ConfigurableEnvironment environment = new StandardEnvironment();
|
private final ConfigurableEnvironment environment = new StandardEnvironment();
|
||||||
|
|
||||||
|
@ -56,97 +57,97 @@ class ConditionalOnBooleanPropertyTests {
|
||||||
@Test
|
@Test
|
||||||
void defaultsWhenTrue() {
|
void defaultsWhenTrue() {
|
||||||
load(Defaults.class, "test=true");
|
load(Defaults.class, "test=true");
|
||||||
assertThat(this.context.containsBean("foo")).isTrue();
|
assertThat(containsBean()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void defaultsWhenFalse() {
|
void defaultsWhenFalse() {
|
||||||
load(Defaults.class, "test=false");
|
load(Defaults.class, "test=false");
|
||||||
assertThat(this.context.containsBean("foo")).isFalse();
|
assertThat(containsBean()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void defaultsWhenMissing() {
|
void defaultsWhenMissing() {
|
||||||
load(Defaults.class);
|
load(Defaults.class);
|
||||||
assertThat(this.context.containsBean("foo")).isFalse();
|
assertThat(containsBean()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void havingValueTrueMatchIfMissingFalseWhenTrue() {
|
void havingValueTrueMatchIfMissingFalseWhenTrue() {
|
||||||
load(HavingValueTrueMatchIfMissingFalse.class, "test=true");
|
load(HavingValueTrueMatchIfMissingFalse.class, "test=true");
|
||||||
assertThat(this.context.containsBean("foo")).isTrue();
|
assertThat(containsBean()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void havingValueTrueMatchIfMissingFalseWhenFalse() {
|
void havingValueTrueMatchIfMissingFalseWhenFalse() {
|
||||||
load(HavingValueTrueMatchIfMissingFalse.class, "test=false");
|
load(HavingValueTrueMatchIfMissingFalse.class, "test=false");
|
||||||
assertThat(this.context.containsBean("foo")).isFalse();
|
assertThat(containsBean()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void havingValueTrueMatchIfMissingFalseWhenMissing() {
|
void havingValueTrueMatchIfMissingFalseWhenMissing() {
|
||||||
load(HavingValueTrueMatchIfMissingFalse.class);
|
load(HavingValueTrueMatchIfMissingFalse.class);
|
||||||
assertThat(this.context.containsBean("foo")).isFalse();
|
assertThat(containsBean()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void havingValueTrueMatchIfMissingTrueWhenTrue() {
|
void havingValueTrueMatchIfMissingTrueWhenTrue() {
|
||||||
load(HavingValueTrueMatchIfMissingTrue.class, "test=true");
|
load(HavingValueTrueMatchIfMissingTrue.class, "test=true");
|
||||||
assertThat(this.context.containsBean("foo")).isTrue();
|
assertThat(containsBean()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void havingValueTrueMatchIfMissingTrueWhenFalse() {
|
void havingValueTrueMatchIfMissingTrueWhenFalse() {
|
||||||
load(HavingValueTrueMatchIfMissingTrue.class, "test=false");
|
load(HavingValueTrueMatchIfMissingTrue.class, "test=false");
|
||||||
assertThat(this.context.containsBean("foo")).isFalse();
|
assertThat(containsBean()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void havingValueTrueMatchIfMissingTrueWhenMissing() {
|
void havingValueTrueMatchIfMissingTrueWhenMissing() {
|
||||||
load(HavingValueTrueMatchIfMissingTrue.class);
|
load(HavingValueTrueMatchIfMissingTrue.class);
|
||||||
assertThat(this.context.containsBean("foo")).isTrue();
|
assertThat(containsBean()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void havingValueFalseMatchIfMissingFalseWhenTrue() {
|
void havingValueFalseMatchIfMissingFalseWhenTrue() {
|
||||||
load(HavingValueFalseMatchIfMissingFalse.class, "test=true");
|
load(HavingValueFalseMatchIfMissingFalse.class, "test=true");
|
||||||
assertThat(this.context.containsBean("foo")).isFalse();
|
assertThat(containsBean()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void havingValueFalseMatchIfMissingFalseWhenFalse() {
|
void havingValueFalseMatchIfMissingFalseWhenFalse() {
|
||||||
load(HavingValueFalseMatchIfMissingFalse.class, "test=false");
|
load(HavingValueFalseMatchIfMissingFalse.class, "test=false");
|
||||||
assertThat(this.context.containsBean("foo")).isTrue();
|
assertThat(containsBean()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void havingValueFalseMatchIfMissingFalseWhenMissing() {
|
void havingValueFalseMatchIfMissingFalseWhenMissing() {
|
||||||
load(HavingValueFalseMatchIfMissingFalse.class);
|
load(HavingValueFalseMatchIfMissingFalse.class);
|
||||||
assertThat(this.context.containsBean("foo")).isFalse();
|
assertThat(containsBean()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void havingValueFalseMatchIfMissingTrueWhenTrue() {
|
void havingValueFalseMatchIfMissingTrueWhenTrue() {
|
||||||
load(HavingValueFalseMatchIfMissingTrue.class, "test=true");
|
load(HavingValueFalseMatchIfMissingTrue.class, "test=true");
|
||||||
assertThat(this.context.containsBean("foo")).isFalse();
|
assertThat(containsBean()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void havingValueFalseMatchIfMissingTrueWhenFalse() {
|
void havingValueFalseMatchIfMissingTrueWhenFalse() {
|
||||||
load(HavingValueFalseMatchIfMissingTrue.class, "test=false");
|
load(HavingValueFalseMatchIfMissingTrue.class, "test=false");
|
||||||
assertThat(this.context.containsBean("foo")).isTrue();
|
assertThat(containsBean()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void havingValueFalseMatchIfMissingTrueWhenMissing() {
|
void havingValueFalseMatchIfMissingTrueWhenMissing() {
|
||||||
load(HavingValueFalseMatchIfMissingTrue.class);
|
load(HavingValueFalseMatchIfMissingTrue.class);
|
||||||
assertThat(this.context.containsBean("foo")).isTrue();
|
assertThat(containsBean()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void withPrefix() {
|
void withPrefix() {
|
||||||
load(HavingValueFalseMatchIfMissingTrue.class, "foo.test=true");
|
load(HavingValueFalseMatchIfMissingTrue.class, "foo.test=true");
|
||||||
assertThat(this.context.containsBean("foo")).isTrue();
|
assertThat(containsBean()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -166,14 +167,14 @@ class ConditionalOnBooleanPropertyTests {
|
||||||
@Test
|
@Test
|
||||||
void conditionReportWhenMatched() {
|
void conditionReportWhenMatched() {
|
||||||
load(Defaults.class, "test=true");
|
load(Defaults.class, "test=true");
|
||||||
assertThat(this.context.containsBean("foo")).isTrue();
|
assertThat(containsBean()).isTrue();
|
||||||
assertThat(getConditionEvaluationReport()).contains("@ConditionalOnBooleanProperty (test=true) matched");
|
assertThat(getConditionEvaluationReport()).contains("@ConditionalOnBooleanProperty (test=true) matched");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void conditionReportWhenDoesNotMatch() {
|
void conditionReportWhenDoesNotMatch() {
|
||||||
load(Defaults.class, "test=false");
|
load(Defaults.class, "test=false");
|
||||||
assertThat(this.context.containsBean("foo")).isFalse();
|
assertThat(containsBean()).isFalse();
|
||||||
assertThat(getConditionEvaluationReport())
|
assertThat(getConditionEvaluationReport())
|
||||||
.contains("@ConditionalOnBooleanProperty (test=true) found different value in property 'test'");
|
.contains("@ConditionalOnBooleanProperty (test=true) found different value in property 'test'");
|
||||||
}
|
}
|
||||||
|
@ -181,7 +182,7 @@ class ConditionalOnBooleanPropertyTests {
|
||||||
@Test
|
@Test
|
||||||
void repeatablePropertiesConditionReportWhenMatched() {
|
void repeatablePropertiesConditionReportWhenMatched() {
|
||||||
load(RepeatablePropertiesRequiredConfiguration.class, "property1=true", "property2=true");
|
load(RepeatablePropertiesRequiredConfiguration.class, "property1=true", "property2=true");
|
||||||
assertThat(this.context.containsBean("foo")).isTrue();
|
assertThat(containsBean()).isTrue();
|
||||||
String report = getConditionEvaluationReport();
|
String report = getConditionEvaluationReport();
|
||||||
assertThat(report).contains("@ConditionalOnBooleanProperty (property1=true) matched");
|
assertThat(report).contains("@ConditionalOnBooleanProperty (property1=true) matched");
|
||||||
assertThat(report).contains("@ConditionalOnBooleanProperty (property2=true) matched");
|
assertThat(report).contains("@ConditionalOnBooleanProperty (property2=true) matched");
|
||||||
|
@ -199,6 +200,7 @@ class ConditionalOnBooleanPropertyTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getConditionEvaluationReport() {
|
private String getConditionEvaluationReport() {
|
||||||
|
assertThat(this.context).isNotNull();
|
||||||
return ConditionEvaluationReport.get(this.context.getBeanFactory())
|
return ConditionEvaluationReport.get(this.context.getBeanFactory())
|
||||||
.getConditionAndOutcomesBySource()
|
.getConditionAndOutcomesBySource()
|
||||||
.values()
|
.values()
|
||||||
|
@ -215,6 +217,11 @@ class ConditionalOnBooleanPropertyTests {
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean containsBean() {
|
||||||
|
assertThat(this.context).isNotNull();
|
||||||
|
return this.context.containsBean("foo");
|
||||||
|
}
|
||||||
|
|
||||||
abstract static class BeanConfiguration {
|
abstract static class BeanConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|
|
@ -105,8 +105,10 @@ class ConditionalOnJavaTests {
|
||||||
FilteredClassLoader classLoader = new FilteredClassLoader(hiddenClasses);
|
FilteredClassLoader classLoader = new FilteredClassLoader(hiddenClasses);
|
||||||
Class<?> javaVersionClass = Class.forName(JavaVersion.class.getName(), false, classLoader);
|
Class<?> javaVersionClass = Class.forName(JavaVersion.class.getName(), false, classLoader);
|
||||||
Method getJavaVersionMethod = ReflectionUtils.findMethod(javaVersionClass, "getJavaVersion");
|
Method getJavaVersionMethod = ReflectionUtils.findMethod(javaVersionClass, "getJavaVersion");
|
||||||
|
assertThat(getJavaVersionMethod).isNotNull();
|
||||||
Object javaVersion = ReflectionUtils.invokeMethod(getJavaVersionMethod, null);
|
Object javaVersion = ReflectionUtils.invokeMethod(getJavaVersionMethod, null);
|
||||||
classLoader.close();
|
classLoader.close();
|
||||||
|
assertThat(javaVersion).isNotNull();
|
||||||
return javaVersion.toString();
|
return javaVersion.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Map;
|
||||||
|
|
||||||
import javax.naming.Context;
|
import javax.naming.Context;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -29,6 +30,7 @@ import org.springframework.boot.autoconfigure.jndi.JndiPropertiesHidingClassLoad
|
||||||
import org.springframework.boot.autoconfigure.jndi.TestableInitialContextFactory;
|
import org.springframework.boot.autoconfigure.jndi.TestableInitialContextFactory;
|
||||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.ConditionContext;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
|
|
||||||
|
@ -47,7 +49,7 @@ class ConditionalOnJndiTests {
|
||||||
|
|
||||||
private ClassLoader threadContextClassLoader;
|
private ClassLoader threadContextClassLoader;
|
||||||
|
|
||||||
private String initialContextFactory;
|
private @Nullable String initialContextFactory;
|
||||||
|
|
||||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
|
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
|
||||||
|
|
||||||
|
@ -101,14 +103,16 @@ class ConditionalOnJndiTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void jndiLocationNotFound() {
|
void jndiLocationNotFound() {
|
||||||
ConditionOutcome outcome = this.condition.getMatchOutcome(null, mockMetadata("java:/a"));
|
ConditionOutcome outcome = this.condition.getMatchOutcome(mock(ConditionContext.class),
|
||||||
|
mockMetadata("java:/a"));
|
||||||
assertThat(outcome.isMatch()).isFalse();
|
assertThat(outcome.isMatch()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void jndiLocationFound() {
|
void jndiLocationFound() {
|
||||||
this.condition.setFoundLocation("java:/b");
|
this.condition.setFoundLocation("java:/b");
|
||||||
ConditionOutcome outcome = this.condition.getMatchOutcome(null, mockMetadata("java:/a", "java:/b"));
|
ConditionOutcome outcome = this.condition.getMatchOutcome(mock(ConditionContext.class),
|
||||||
|
mockMetadata("java:/a", "java:/b"));
|
||||||
assertThat(outcome.isMatch()).isTrue();
|
assertThat(outcome.isMatch()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +155,7 @@ class ConditionalOnJndiTests {
|
||||||
|
|
||||||
private final boolean jndiAvailable = true;
|
private final boolean jndiAvailable = true;
|
||||||
|
|
||||||
private String foundLocation;
|
private @Nullable String foundLocation;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isJndiAvailable() {
|
protected boolean isJndiAvailable() {
|
||||||
|
@ -162,7 +166,7 @@ class ConditionalOnJndiTests {
|
||||||
protected JndiLocator getJndiLocator(String[] locations) {
|
protected JndiLocator getJndiLocator(String[] locations) {
|
||||||
return new JndiLocator(locations) {
|
return new JndiLocator(locations) {
|
||||||
@Override
|
@Override
|
||||||
public String lookupFirstLocation() {
|
public @Nullable String lookupFirstLocation() {
|
||||||
return MockableOnJndi.this.foundLocation;
|
return MockableOnJndi.this.foundLocation;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue